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