1 /* portability.c - code to workaround the deficiencies of various platforms.
2 *
3 * Copyright 2012 Rob Landley <[email protected]>
4 * Copyright 2012 Georgi Chorbadzhiyski <[email protected]>
5 */
6
7 #include "toys.h"
8
9 // We can't fork() on nommu systems, and vfork() requires an exec() or exit()
10 // before resuming the parent (because they share a heap until then). And no,
11 // we can't implement our own clone() call that does the equivalent of fork()
12 // because nommu heaps use physical addresses so if we copy the heap all our
13 // pointers are wrong. (You need an mmu in order to map two heaps to the same
14 // address range without interfering with each other.) In the absence of
15 // a portable way to tell malloc() to start a new heap without freeing the old
16 // one, you pretty much need the exec().)
17
18 // So we exec ourselves (via /proc/self/exe, if anybody knows a way to
19 // re-exec self without depending on the filesystem, I'm all ears),
20 // and use the arguments to signal reentry.
21
22 #if CFG_TOYBOX_FORK
xfork(void)23 pid_t xfork(void)
24 {
25 pid_t pid = fork();
26
27 if (pid < 0) perror_exit("fork");
28
29 return pid;
30 }
31 #endif
32
xgetrandom(void * buf,unsigned buflen)33 void xgetrandom(void *buf, unsigned buflen)
34 {
35 int fd;
36
37 // Linux keeps getrandom() in <sys/random.h> and getentropy() in <unistd.h>
38 // BSD/macOS only has getentropy(), but it's in <sys/random.h> (to be fair,
39 // they were there first). getrandom() and getentropy() both went into glibc
40 // in the same release (2.25 in 2017), so this test still works.
41 #if __has_include(<sys/random.h>)
42 while (buflen) {
43 if (getentropy(buf, fd = buflen>256 ? 256 : buflen)) break;
44 buflen -= fd;
45 buf += fd;
46 }
47 if (!buflen) return;
48 if (errno!=ENOSYS) perror_exit("getrandom");
49 #endif
50 xreadall(fd = xopen("/dev/urandom", O_RDONLY), buf, buflen);
51 close(fd);
52 }
53
54 // Get list of mounted filesystems, including stat and statvfs info.
55 // Returns a reversed list, which is good for finding overmounts and such.
56
57 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
58
59 #include <sys/mount.h>
60
xgetmountlist(char * path)61 struct mtab_list *xgetmountlist(char *path)
62 {
63 struct mtab_list *mtlist = 0, *mt;
64 struct statfs *entries;
65 int i, count;
66
67 if (path) error_exit("xgetmountlist");
68 if (!(count = getmntinfo(&entries, MNT_WAIT))) perror_exit("getmntinfo");
69
70 // The "test" part of the loop is done before the first time through and
71 // again after each "increment", so putting the actual load there avoids
72 // duplicating it. If the load was NULL, the loop stops.
73
74 for (i = 0; i < count; ++i) {
75 struct statfs *me = &entries[i];
76
77 mt = xzalloc(sizeof(struct mtab_list) + strlen(me->f_fstypename) +
78 strlen(me->f_mntonname) + strlen(me->f_mntfromname) + strlen("") + 4);
79 dlist_add_nomalloc((void *)&mtlist, (void *)mt);
80
81 // Collect details about mounted filesystem.
82 // Don't report errors, just leave data zeroed.
83 stat(me->f_mntonname, &(mt->stat));
84 statvfs(me->f_mntonname, &(mt->statvfs));
85
86 // Remember information from struct statfs.
87 mt->dir = stpcpy(mt->type, me->f_fstypename)+1;
88 mt->device = stpcpy(mt->dir, me->f_mntonname)+1;
89 mt->opts = stpcpy(mt->device, me->f_mntfromname)+1;
90 strcpy(mt->opts, ""); /* TODO: reverse from f_flags? */
91 }
92
93 return mtlist;
94 }
95
96 #else
97
98 #include <mntent.h>
99
100 // Check if this type matches list.
101 // Odd syntax: typelist all yes = if any, typelist all no = if none.
102
mountlist_istype(struct mtab_list * ml,char * typelist)103 int mountlist_istype(struct mtab_list *ml, char *typelist)
104 {
105 int len, skip;
106 char *t;
107
108 if (!typelist) return 1;
109
110 // leading "no" indicates whether entire list is inverted
111 skip = strncmp(typelist, "no", 2);
112
113 for (;;) {
114 if (!(t = comma_iterate(&typelist, &len))) break;
115 if (!skip) {
116 // later "no" after first are ignored
117 strstart(&t, "no");
118 if (!strncmp(t, ml->type, len-2)) {
119 skip = 1;
120 break;
121 }
122 } else if (!strncmp(t, ml->type, len) && !ml->type[len]) {
123 skip = 0;
124 break;
125 }
126 }
127
128 return !skip;
129 }
130
xgetmountlist(char * path)131 struct mtab_list *xgetmountlist(char *path)
132 {
133 struct mtab_list *mtlist = 0, *mt;
134 struct mntent *me;
135 FILE *fp;
136 char *p = path ? path : "/proc/mounts";
137
138 if (!(fp = setmntent(p, "r"))) perror_exit("bad %s", p);
139
140 // The "test" part of the loop is done before the first time through and
141 // again after each "increment", so putting the actual load there avoids
142 // duplicating it. If the load was NULL, the loop stops.
143
144 while ((me = getmntent(fp))) {
145 mt = xzalloc(sizeof(struct mtab_list) + strlen(me->mnt_fsname) +
146 strlen(me->mnt_dir) + strlen(me->mnt_type) + strlen(me->mnt_opts) + 4);
147 dlist_add_nomalloc((void *)&mtlist, (void *)mt);
148
149 // Collect details about mounted filesystem
150 // Don't report errors, just leave data zeroed
151 if (!path) {
152 stat(me->mnt_dir, &(mt->stat));
153 statvfs(me->mnt_dir, &(mt->statvfs));
154 }
155
156 // Remember information from /proc/mounts
157 mt->dir = stpcpy(mt->type, me->mnt_type)+1;
158 mt->device = stpcpy(mt->dir, me->mnt_dir)+1;
159 mt->opts = stpcpy(mt->device, me->mnt_fsname)+1;
160 strcpy(mt->opts, me->mnt_opts);
161
162 octal_deslash(mt->dir);
163 octal_deslash(mt->device);
164 }
165 endmntent(fp);
166
167 return mtlist;
168 }
169
170 #endif
171
172 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
173
174 #include <sys/event.h>
175
xnotify_init(int max)176 struct xnotify *xnotify_init(int max)
177 {
178 struct xnotify *not = xzalloc(sizeof(struct xnotify));
179
180 not->max = max;
181 if ((not->kq = kqueue()) == -1) perror_exit("kqueue");
182 not->paths = xmalloc(max * sizeof(char *));
183 not->fds = xmalloc(max * sizeof(int));
184
185 return not;
186 }
187
xnotify_add(struct xnotify * not,int fd,char * path)188 int xnotify_add(struct xnotify *not, int fd, char *path)
189 {
190 struct kevent event;
191
192 if (not->count == not->max) error_exit("xnotify_add overflow");
193 EV_SET(&event, fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE, 0, NULL);
194 if (kevent(not->kq, &event, 1, NULL, 0, NULL) == -1 || event.flags & EV_ERROR)
195 error_exit("xnotify_add failed on %s", path);
196 not->paths[not->count] = path;
197 not->fds[not->count++] = fd;
198
199 return 0;
200 }
201
xnotify_wait(struct xnotify * not,char ** path)202 int xnotify_wait(struct xnotify *not, char **path)
203 {
204 struct kevent event;
205 int i;
206
207 for (;;) {
208 if (kevent(not->kq, NULL, 0, &event, 1, NULL) != -1) {
209 // We get the fd for free, but still have to search for the path.
210 for (i = 0; i<not->count; i++) if (not->fds[i]==event.ident) {
211 *path = not->paths[i];
212
213 return event.ident;
214 }
215 }
216 }
217 }
218
219 #else
220
221 #include <sys/inotify.h>
222
xnotify_init(int max)223 struct xnotify *xnotify_init(int max)
224 {
225 struct xnotify *not = xzalloc(sizeof(struct xnotify));
226
227 not->max = max;
228 if ((not->kq = inotify_init()) < 0) perror_exit("inotify_init");
229 not->paths = xmalloc(max * sizeof(char *));
230 not->fds = xmalloc(max * 2 * sizeof(int));
231
232 return not;
233 }
234
xnotify_add(struct xnotify * not,int fd,char * path)235 int xnotify_add(struct xnotify *not, int fd, char *path)
236 {
237 int i = 2*not->count;
238
239 if (not->max == not->count) error_exit("xnotify_add overflow");
240 if ((not->fds[i] = inotify_add_watch(not->kq, path, IN_MODIFY))==-1)
241 perror_exit("xnotify_add failed on %s", path);
242 not->fds[i+1] = fd;
243 not->paths[not->count++] = path;
244
245 return 0;
246 }
247
xnotify_wait(struct xnotify * not,char ** path)248 int xnotify_wait(struct xnotify *not, char **path)
249 {
250 struct inotify_event ev;
251 int i;
252
253 for (;;) {
254 if (sizeof(ev)!=read(not->kq, &ev, sizeof(ev))) perror_exit("inotify");
255
256 for (i = 0; i<not->count; i++) if (ev.wd==not->fds[2*i]) {
257 *path = not->paths[i];
258
259 return not->fds[2*i+1];
260 }
261 }
262 }
263
264 #endif
265
266 #ifdef __APPLE__
267
xattr_get(const char * path,const char * name,void * value,size_t size)268 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
269 {
270 return getxattr(path, name, value, size, 0, 0);
271 }
272
xattr_lget(const char * path,const char * name,void * value,size_t size)273 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
274 {
275 return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
276 }
277
xattr_fget(int fd,const char * name,void * value,size_t size)278 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
279 {
280 return fgetxattr(fd, name, value, size, 0, 0);
281 }
282
xattr_list(const char * path,char * list,size_t size)283 ssize_t xattr_list(const char *path, char *list, size_t size)
284 {
285 return listxattr(path, list, size, 0);
286 }
287
xattr_llist(const char * path,char * list,size_t size)288 ssize_t xattr_llist(const char *path, char *list, size_t size)
289 {
290 return listxattr(path, list, size, XATTR_NOFOLLOW);
291 }
292
xattr_flist(int fd,char * list,size_t size)293 ssize_t xattr_flist(int fd, char *list, size_t size)
294 {
295 return flistxattr(fd, list, size, 0);
296 }
297
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)298 ssize_t xattr_set(const char* path, const char* name,
299 const void* value, size_t size, int flags)
300 {
301 return setxattr(path, name, value, size, 0, flags);
302 }
303
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)304 ssize_t xattr_lset(const char* path, const char* name,
305 const void* value, size_t size, int flags)
306 {
307 return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
308 }
309
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)310 ssize_t xattr_fset(int fd, const char* name,
311 const void* value, size_t size, int flags)
312 {
313 return fsetxattr(fd, name, value, size, 0, flags);
314 }
315
316 #elif !defined(__FreeBSD__) && !defined(__OpenBSD__)
317
xattr_get(const char * path,const char * name,void * value,size_t size)318 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
319 {
320 return getxattr(path, name, value, size);
321 }
322
xattr_lget(const char * path,const char * name,void * value,size_t size)323 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
324 {
325 return lgetxattr(path, name, value, size);
326 }
327
xattr_fget(int fd,const char * name,void * value,size_t size)328 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
329 {
330 return fgetxattr(fd, name, value, size);
331 }
332
xattr_list(const char * path,char * list,size_t size)333 ssize_t xattr_list(const char *path, char *list, size_t size)
334 {
335 return listxattr(path, list, size);
336 }
337
xattr_llist(const char * path,char * list,size_t size)338 ssize_t xattr_llist(const char *path, char *list, size_t size)
339 {
340 return llistxattr(path, list, size);
341 }
342
xattr_flist(int fd,char * list,size_t size)343 ssize_t xattr_flist(int fd, char *list, size_t size)
344 {
345 return flistxattr(fd, list, size);
346 }
347
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)348 ssize_t xattr_set(const char* path, const char* name,
349 const void* value, size_t size, int flags)
350 {
351 return setxattr(path, name, value, size, flags);
352 }
353
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)354 ssize_t xattr_lset(const char* path, const char* name,
355 const void* value, size_t size, int flags)
356 {
357 return lsetxattr(path, name, value, size, flags);
358 }
359
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)360 ssize_t xattr_fset(int fd, const char* name,
361 const void* value, size_t size, int flags)
362 {
363 return fsetxattr(fd, name, value, size, flags);
364 }
365
366
367 #endif
368
369 #ifdef __APPLE__
370 // In the absence of a mknodat system call, fchdir to dirfd and back
371 // around a regular mknod call...
mknodat(int dirfd,const char * path,mode_t mode,dev_t dev)372 int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev)
373 {
374 int old_dirfd = open(".", O_RDONLY), result;
375
376 if (old_dirfd == -1 || fchdir(dirfd) == -1) return -1;
377 result = mknod(path, mode, dev);
378 if (fchdir(old_dirfd) == -1) perror_exit("mknodat couldn't return");
379 return result;
380 }
381
382 // As of 10.15, macOS offers an fcntl F_PREALLOCATE rather than fallocate()
383 // or posix_fallocate() calls. The fcntl only (as the name implies)
384 // pre-allocates, so we also need to ftruncate() afterwards.
posix_fallocate(int fd,off_t offset,off_t length)385 int posix_fallocate(int fd, off_t offset, off_t length)
386 {
387 int e = errno, result = 0;
388 fstore_t f;
389
390 f.fst_flags = F_ALLOCATEALL;
391 f.fst_posmode = F_PEOFPOSMODE;
392 f.fst_offset = 0;
393 f.fst_length = offset + length;
394 if (fcntl(fd, F_PREALLOCATE, &f) == -1) result = errno;
395 else if (ftruncate(fd, maxof(offset+length, fdlength(fd)))) result = errno;
396 errno = e;
397 return result;
398 }
399 #endif
400
401 // Signals required by POSIX 2008:
402 // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
403
404 #define SIGNIFY(x) {SIG##x, #x}
405
406 static const struct signame signames[] = {
407 {0, "0"},
408 // POSIX
409 SIGNIFY(ABRT), SIGNIFY(ALRM), SIGNIFY(BUS),
410 SIGNIFY(FPE), SIGNIFY(HUP), SIGNIFY(ILL), SIGNIFY(INT), SIGNIFY(KILL),
411 SIGNIFY(PIPE), SIGNIFY(QUIT), SIGNIFY(SEGV), SIGNIFY(TERM),
412 SIGNIFY(USR1), SIGNIFY(USR2), SIGNIFY(SYS), SIGNIFY(TRAP),
413 SIGNIFY(VTALRM), SIGNIFY(XCPU), SIGNIFY(XFSZ),
414 // Non-POSIX signals that cause termination
415 SIGNIFY(PROF), SIGNIFY(IO),
416 // signals only present/absent on some targets (mips and macos)
417 #ifdef SIGEMT
418 SIGNIFY(EMT),
419 #endif
420 #ifdef SIGINFO
421 SIGNIFY(INFO),
422 #endif
423 #ifdef SIGPOLL
424 SIGNIFY(POLL),
425 #endif
426 #ifdef SIGPWR
427 SIGNIFY(PWR),
428 #endif
429 #ifdef SIGSTKFLT
430 SIGNIFY(STKFLT),
431 #endif
432
433 // Note: sigatexit relies on all the signals with a default disposition that
434 // terminates the process coming *before* SIGCHLD.
435
436 // POSIX signals that don't cause termination
437 SIGNIFY(CHLD), SIGNIFY(CONT), SIGNIFY(STOP), SIGNIFY(TSTP),
438 SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(URG),
439 // Non-POSIX signals that don't cause termination
440 SIGNIFY(WINCH),
441 };
442
443 #undef SIGNIFY
444
xsignal_all_killers(void * handler)445 void xsignal_all_killers(void *handler)
446 {
447 int i;
448
449 for (i = 1; signames[i].num != SIGCHLD; i++)
450 if (signames[i].num != SIGKILL) xsignal(signames[i].num, handler);
451 }
452
453 // Convert a string like "9", "KILL", "SIGHUP", or "SIGRTMIN+2" to a number.
sig_to_num(char * sigstr)454 int sig_to_num(char *sigstr)
455 {
456 int i, offset;
457 char *s;
458
459 // Numeric?
460 offset = estrtol(sigstr, &s, 10);
461 if (!errno && !*s) return offset;
462
463 // Skip leading "SIG".
464 strcasestart(&sigstr, "sig");
465
466 // Named signal?
467 for (i=0; i<ARRAY_LEN(signames); i++)
468 if (!strcasecmp(sigstr, signames[i].name)) return signames[i].num;
469
470 // Real-time signal?
471 #ifdef SIGRTMIN
472 if (strcasestart(&sigstr, "rtmin")) i = SIGRTMIN;
473 else if (strcasestart(&sigstr, "rtmax")) i = SIGRTMAX;
474 else return -1;
475
476 // No offset?
477 if (!*sigstr) return i;
478
479 // We allow any offset that's still a real-time signal: SIGRTMIN+20 is fine.
480 // Others are more restrictive, only accepting what they show with -l.
481 offset = estrtol(sigstr, &s, 10);
482 if (errno || *s) return -1;
483 i += offset;
484 if (i >= SIGRTMIN && i <= SIGRTMAX) return i;
485 #endif
486
487 return -1;
488 }
489
num_to_sig(int sig)490 char *num_to_sig(int sig)
491 {
492 int i;
493
494 // A named signal?
495 for (i=0; i<ARRAY_LEN(signames); i++)
496 if (signames[i].num == sig) return signames[i].name;
497
498 // A real-time signal?
499 #ifdef SIGRTMIN
500 if (sig == SIGRTMIN) return "RTMIN";
501 if (sig == SIGRTMAX) return "RTMAX";
502 if (sig > SIGRTMIN && sig < SIGRTMAX) {
503 if (sig-SIGRTMIN <= SIGRTMAX-sig) sprintf(libbuf, "RTMIN+%d", sig-SIGRTMIN);
504 else sprintf(libbuf, "RTMAX-%d", SIGRTMAX-sig);
505 return libbuf;
506 }
507 #endif
508
509 return NULL;
510 }
511
dev_minor(int dev)512 int dev_minor(int dev)
513 {
514 #if defined(__linux__)
515 return ((dev&0xfff00000)>>12)|(dev&0xff);
516 #elif defined(__APPLE__)
517 return dev&0xffffff;
518 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
519 return minor(dev);
520 #else
521 #error
522 #endif
523 }
524
dev_major(int dev)525 int dev_major(int dev)
526 {
527 #if defined(__linux__)
528 return (dev&0xfff00)>>8;
529 #elif defined(__APPLE__)
530 return (dev>>24)&0xff;
531 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
532 return major(dev);
533 #else
534 #error
535 #endif
536 }
537
dev_makedev(int major,int minor)538 int dev_makedev(int major, int minor)
539 {
540 #if defined(__linux__)
541 return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12);
542 #elif defined(__APPLE__)
543 return (minor&0xffffff)|((major&0xff)<<24);
544 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
545 return makedev(major, minor);
546 #else
547 #error
548 #endif
549 }
550
fs_type_name(struct statfs * statfs)551 char *fs_type_name(struct statfs *statfs)
552 {
553 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
554 // macOS has an `f_type` field, but assigns values dynamically as filesystems
555 // are registered. They do give you the name directly though, so use that.
556 return statfs->f_fstypename;
557 #else
558 char *s = NULL;
559 struct {unsigned num; char *name;} nn[] = {
560 {0xADF5, "adfs"}, {0xADFF, "affs"}, {0x5346414F, "afs"}, {0x187, "autofs"},
561 {0x1BADFACE, "bfs"}, {0x6C6F6F70, "binder"}, {0x9123683E, "btrfs"},
562 {0xFF534D42, "cifs"}, {0x27E0EB, "cgroup"}, {0x63677270, "cgroup2"},
563 {0x73757245, "coda"}, {0x28cd3d45, "cramfs"}, {0x1CD1, "devpts"},
564 {0xF15F, "ecryptfs"}, {0x414A53, "efs"}, {0xE0F5E1E2, "erofs"},
565 {0x2011BAB0, "exfat"}, {0x137D, "ext"}, {0xEF51, "ext2"},
566 {0xEF53, "ext3/4"}, {0xF2F52010, "f2fs"}, {0xBAD1DEA, "futexfs"},
567 {0x00C0FFEE, "hostfs"}, {0xF995E849, "hpfs"},
568 {0x9660, "isofs"}, {0x72B6, "jffs2"}, {0x3153464a, "jfs"},
569 {0x137F, "minix"}, {0x2468, "minix2"}, {0x4D5A, "minix3"},
570 {0x4D44, "vfat"}, {0x6969, "nfs"}, {0x3434, "nilfs2"},
571 {0x5346544E, "ntfs"}, {0x7461636F, "ocfs2"}, {0x9FA1, "openpromfs"},
572 {0x794C7630, "overlay"}, {0x9FA0, "proc"}, {0x002f, "qnx4"},
573 {0x68191122, "qnx6"}, {0x7275, "romfs"}, {0x7655821, "resctrl"},
574 {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"},
575 {0x01021994, "tmpfs"}, {0x15013346, "udf"}, {0x43415d53, "smackfs"},
576 {0x73717368, "squashfs"}, {0xabba1974, "xenfs"}, {0x58465342, "xfs"}
577 };
578 int i;
579
580 for (i=0; i<ARRAY_LEN(nn); i++)
581 if (nn[i].num == statfs->f_type) s = nn[i].name;
582 if (!s) sprintf(s = libbuf, "0x%x", (unsigned)statfs->f_type);
583 return s;
584 #endif
585 }
586
587 #if defined(__APPLE__)
588 #include <sys/disk.h>
get_block_device_size(int fd,unsigned long long * size)589 int get_block_device_size(int fd, unsigned long long* size)
590 {
591 unsigned long block_size, block_count;
592
593 if (!ioctl(fd, DKIOCGETBLOCKSIZE, &block_size) &&
594 !ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count)) {
595 *size = block_count * block_size;
596 return 1;
597 }
598 return 0;
599 }
600 #elif defined(__linux__)
get_block_device_size(int fd,unsigned long long * size)601 int get_block_device_size(int fd, unsigned long long* size)
602 {
603 return (ioctl(fd, BLKGETSIZE64, size) >= 0);
604 }
605 #elif defined(__OpenBSD__)
606 #include <sys/dkio.h>
607 #include <sys/disklabel.h>
get_block_device_size(int fd,unsigned long long * size)608 int get_block_device_size(int fd, unsigned long long* size)
609 {
610 struct disklabel lab;
611 int status = (ioctl(fd, DIOCGDINFO, &lab) >= 0);
612 *size = lab.d_secsize * lab.d_nsectors;
613 return status;
614 }
615 #else
get_block_device_size(int fd,unsigned long long * size)616 int get_block_device_size(int fd, unsigned long long* size)
617 {
618 return 0;
619 }
620 #endif
621
622 #if defined(__ANDROID__)
android_api_level(void)623 static int android_api_level(void)
624 {
625 // Cached so we don't do a system property lookup on every call.
626 static int api_level;
627
628 if (!api_level) api_level = android_get_device_api_level();
629 return api_level;
630 }
631 #endif
632
check_copy_file_range(void)633 static int check_copy_file_range(void)
634 {
635 #if defined(__ANDROID__)
636 // Android's had the constant for years, but seccomp means you'll get
637 // SIGSYS if you try the system call before 2023's Android U.
638 return (android_api_level() >= __ANDROID_API_U__) ? __NR_copy_file_range : 0;
639 #elif defined(__NR_copy_file_range)
640 // glibc added this constant in git at the end of 2017, shipped 2018-02.
641 return __NR_copy_file_range;
642 #else
643 return 0;
644 #endif
645 }
646
647 // Return bytes copied from in to out. If bytes <0 copy all of in to out.
648 // If consumed isn't null, amount read saved there (return is written or error)
sendfile_len(int in,int out,long long bytes,long long * consumed)649 long long sendfile_len(int in, int out, long long bytes, long long *consumed)
650 {
651 long long total = 0, len, ww;
652 int try_cfr = check_copy_file_range();
653
654 if (consumed) *consumed = 0;
655 if (in>=0) while (bytes != total) {
656 ww = 0;
657 len = bytes-total;
658
659 errno = 0;
660 if (try_cfr) {
661 if (bytes<0 || len>(1<<30)) len = (1<<30);
662 len = syscall(try_cfr, in, 0, out, 0, len, 0);
663 if (len < 0) {
664 try_cfr = 0;
665
666 continue;
667 }
668 } else {
669 if (bytes<0 || len>sizeof(libbuf)) len = sizeof(libbuf);
670 ww = len = read(in, libbuf, len);
671 }
672 if (len<1 && errno==EAGAIN) continue;
673 if (len<1) break;
674 if (consumed) *consumed += len;
675 if (ww && writeall(out, libbuf, len) != len) return -1;
676 total += len;
677 }
678
679 return total;
680 }
681
682 #ifdef __APPLE__
683 // The absolute minimum POSIX timer implementation to build timeout(1).
684 // Note that although timeout(1) uses POSIX timers to get the monotonic clock,
685 // that doesn't seem to be an option on macOS (without using other libraries),
686 // so we just mangle that back into a regular setitimer(ITIMER_REAL) call.
timer_create(clock_t c,struct sigevent * se,timer_t * t)687 int timer_create(clock_t c, struct sigevent *se, timer_t *t)
688 {
689 if (se->sigev_notify != SIGEV_SIGNAL || se->sigev_signo != SIGALRM)
690 error_exit("unimplemented");
691 *t = 1;
692 return 0;
693 }
694
timer_settime(timer_t t,int flags,struct itimerspec * new,void * old)695 int timer_settime(timer_t t, int flags, struct itimerspec *new, void *old)
696 {
697 struct itimerval mangled;
698
699 if (flags != 0 || old != 0) error_exit("unimplemented");
700 memset(&mangled, 0, sizeof(mangled));
701 mangled.it_value.tv_sec = new->it_value.tv_sec;
702 mangled.it_value.tv_usec = new->it_value.tv_nsec / 1000;
703 return setitimer(ITIMER_REAL, &mangled, NULL);
704 }
705 // glibc requires -lrt for linux syscalls, which pulls in libgcc_eh.a for
706 // static linking, and gcc 9.3 leaks pthread calls from that breaking the build
707 // These are both just linux syscalls: wrap them ourselves
708 #elif defined(__GLIBC__)
timer_create_wrap(clockid_t c,struct sigevent * se,timer_t * t)709 int timer_create_wrap(clockid_t c, struct sigevent *se, timer_t *t)
710 {
711 // convert overengineered structure to what kernel actually uses
712 struct ksigevent { void *sv; int signo, notify, tid; } kk = {
713 0, se->sigev_signo, se->sigev_notify, 0
714 };
715 int timer;
716
717 if (syscall(SYS_timer_create, c, &kk, &timer)<0) return -1;
718 *t = (timer_t)(long)timer;
719
720 return 0;
721 }
722
723 #if !defined(SYS_timer_settime) && defined(SYS_timer_settime64)
724 // glibc does not define defines SYS_timer_settime on 32-bit systems
725 // with 64-bit time_t defaults e.g. riscv32
726 #define SYS_timer_settime SYS_timer_settime64
727 #endif
728
timer_settime_wrap(timer_t t,int flags,struct itimerspec * val,struct itimerspec * old)729 int timer_settime_wrap(timer_t t, int flags, struct itimerspec *val,
730 struct itimerspec *old)
731 {
732 return syscall(SYS_timer_settime, t, flags, val, old);
733 }
734 #endif
735
736 // Atomically swap two files
rename_exchange(char * file1,char * file2)737 int rename_exchange(char *file1, char *file2)
738 {
739 #if defined(__linux__)
740 // 2 is RENAME_EXCHANGE
741 return syscall(SYS_renameat2, AT_FDCWD, file1, AT_FDCWD, file2, 2);
742 #else
743 return ENOSYS;
744 #endif
745 }
746