xref: /aosp_15_r20/external/toybox/lib/portability.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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