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 #if defined(__FreeBSD__) || defined(__DragonFly__)
10 #include <sys/types.h>
11 #endif
12 
13 // We can't fork() on nommu systems, and vfork() requires an exec() or exit()
14 // before resuming the parent (because they share a heap until then). And no,
15 // we can't implement our own clone() call that does the equivalent of fork()
16 // because nommu heaps use physical addresses so if we copy the heap all our
17 // pointers are wrong. (You need an mmu in order to map two heaps to the same
18 // address range without interfering with each other.) In the absence of
19 // a portable way to tell malloc() to start a new heap without freeing the old
20 // one, you pretty much need the exec().)
21 
22 // So we exec ourselves (via /proc/self/exe, if anybody knows a way to
23 // re-exec self without depending on the filesystem, I'm all ears),
24 // and use the arguments to signal reentry.
25 
26 #if CFG_TOYBOX_FORK
xfork(void)27 pid_t xfork(void)
28 {
29   pid_t pid = fork();
30 
31   if (pid < 0) perror_exit("fork");
32 
33   return pid;
34 }
35 #endif
36 
xgetrandom(void * buf,unsigned buflen,unsigned flags)37 int xgetrandom(void *buf, unsigned buflen, unsigned flags)
38 {
39   int fd;
40 
41 #if CFG_TOYBOX_GETRANDOM
42   if (buflen == getrandom(buf, buflen, flags&~WARN_ONLY)) return 1;
43   if (errno!=ENOSYS && !(flags&WARN_ONLY)) perror_exit("getrandom");
44 #endif
45   fd = xopen(flags ? "/dev/random" : "/dev/urandom",O_RDONLY|(flags&WARN_ONLY));
46   if (fd == -1) return 0;
47   xreadall(fd, buf, buflen);
48   close(fd);
49 
50   return 1;
51 }
52 
53 // Get list of mounted filesystems, including stat and statvfs info.
54 // Returns a reversed list, which is good for finding overmounts and such.
55 
56 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
57     defined(__DragonFly__)
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, 0)) == 0) 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 
octal_deslash(char * s)100 static void octal_deslash(char *s)
101 {
102   char *o = s;
103 
104   while (*s) {
105     if (*s == '\\') {
106       int i, oct = 0;
107 
108       for (i = 1; i < 4; i++) {
109         if (!isdigit(s[i])) break;
110         oct = (oct<<3)+s[i]-'0';
111       }
112       if (i == 4) {
113         *o++ = oct;
114         s += i;
115         continue;
116       }
117     }
118     *o++ = *s++;
119   }
120 
121   *o = 0;
122 }
123 
124 // Check if this type matches list.
125 // Odd syntax: typelist all yes = if any, typelist all no = if none.
126 
mountlist_istype(struct mtab_list * ml,char * typelist)127 int mountlist_istype(struct mtab_list *ml, char *typelist)
128 {
129   int len, skip;
130   char *t;
131 
132   if (!typelist) return 1;
133 
134   // leading "no" indicates whether entire list is inverted
135   skip = strncmp(typelist, "no", 2);
136 
137   for (;;) {
138     if (!(t = comma_iterate(&typelist, &len))) break;
139     if (!skip) {
140       // later "no" after first are ignored
141       strstart(&t, "no");
142       if (!strncmp(t, ml->type, len-2)) {
143         skip = 1;
144         break;
145       }
146     } else if (!strncmp(t, ml->type, len) && !ml->type[len]) {
147       skip = 0;
148       break;
149     }
150   }
151 
152   return !skip;
153 }
154 
xgetmountlist(char * path)155 struct mtab_list *xgetmountlist(char *path)
156 {
157   struct mtab_list *mtlist = 0, *mt;
158   struct mntent *me;
159   FILE *fp;
160   char *p = path ? path : "/proc/mounts";
161 
162   if (!(fp = setmntent(p, "r"))) perror_exit("bad %s", p);
163 
164   // The "test" part of the loop is done before the first time through and
165   // again after each "increment", so putting the actual load there avoids
166   // duplicating it. If the load was NULL, the loop stops.
167 
168   while ((me = getmntent(fp))) {
169     mt = xzalloc(sizeof(struct mtab_list) + strlen(me->mnt_fsname) +
170       strlen(me->mnt_dir) + strlen(me->mnt_type) + strlen(me->mnt_opts) + 4);
171     dlist_add_nomalloc((void *)&mtlist, (void *)mt);
172 
173     // Collect details about mounted filesystem
174     // Don't report errors, just leave data zeroed
175     if (!path) {
176       stat(me->mnt_dir, &(mt->stat));
177       statvfs(me->mnt_dir, &(mt->statvfs));
178     }
179 
180     // Remember information from /proc/mounts
181     mt->dir = stpcpy(mt->type, me->mnt_type)+1;
182     mt->device = stpcpy(mt->dir, me->mnt_dir)+1;
183     mt->opts = stpcpy(mt->device, me->mnt_fsname)+1;
184     strcpy(mt->opts, me->mnt_opts);
185 
186     octal_deslash(mt->dir);
187     octal_deslash(mt->device);
188   }
189   endmntent(fp);
190 
191   return mtlist;
192 }
193 
194 #endif
195 
196 #if defined(__APPLE__) || defined(__OpenBSD__)
197 
198 #include <sys/event.h>
199 
xnotify_init(int max)200 struct xnotify *xnotify_init(int max)
201 {
202   struct xnotify *not = xzalloc(sizeof(struct xnotify));
203 
204   not->max = max;
205   if ((not->kq = kqueue()) == -1) perror_exit("kqueue");
206   not->paths = xmalloc(max * sizeof(char *));
207   not->fds = xmalloc(max * sizeof(int));
208 
209   return not;
210 }
211 
xnotify_add(struct xnotify * not,int fd,char * path)212 int xnotify_add(struct xnotify *not, int fd, char *path)
213 {
214   struct kevent event;
215 
216   if (not->count == not->max) error_exit("xnotify_add overflow");
217   EV_SET(&event, fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE, 0, NULL);
218   if (kevent(not->kq, &event, 1, NULL, 0, NULL) == -1 || event.flags & EV_ERROR)
219     return -1;
220   not->paths[not->count] = path;
221   not->fds[not->count++] = fd;
222 
223   return 0;
224 }
225 
xnotify_wait(struct xnotify * not,char ** path)226 int xnotify_wait(struct xnotify *not, char **path)
227 {
228   struct kevent event;
229   int i;
230 
231   for (;;) {
232     if (kevent(not->kq, NULL, 0, &event, 1, NULL) != -1) {
233       // We get the fd for free, but still have to search for the path.
234       for (i = 0; i<not->count; i++) if (not->fds[i]==event.ident) {
235         *path = not->paths[i];
236 
237         return event.ident;
238       }
239     }
240   }
241 }
242 
243 #else
244 
245 #include "/usr/local/include/sys/inotify.h"
246 
xnotify_init(int max)247 struct xnotify *xnotify_init(int max)
248 {
249   struct xnotify *not = xzalloc(sizeof(struct xnotify));
250 
251   not->max = max;
252   if ((not->kq = inotify_init()) < 0) perror_exit("inotify_init");
253   not->paths = xmalloc(max * sizeof(char *));
254   not->fds = xmalloc(max * 2 * sizeof(int));
255 
256   return not;
257 }
258 
xnotify_add(struct xnotify * not,int fd,char * path)259 int xnotify_add(struct xnotify *not, int fd, char *path)
260 {
261   int i = 2*not->count;
262 
263   if (not->max == not->count) error_exit("xnotify_add overflow");
264   if ((not->fds[i] = inotify_add_watch(not->kq, path, IN_MODIFY))==-1)
265     return -1;
266   not->fds[i+1] = fd;
267   not->paths[not->count++] = path;
268 
269   return 0;
270 }
271 
xnotify_wait(struct xnotify * not,char ** path)272 int xnotify_wait(struct xnotify *not, char **path)
273 {
274   struct inotify_event ev;
275   int i;
276 
277   for (;;) {
278     if (sizeof(ev)!=read(not->kq, &ev, sizeof(ev))) perror_exit("inotify");
279 
280     for (i = 0; i<not->count; i++) if (ev.wd==not->fds[2*i]) {
281       *path = not->paths[i];
282 
283       return not->fds[2*i+1];
284     }
285   }
286 }
287 
288 #endif
289 
290 #ifdef __APPLE__
291 
xattr_get(const char * path,const char * name,void * value,size_t size)292 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
293 {
294   return getxattr(path, name, value, size, 0, 0);
295 }
296 
xattr_lget(const char * path,const char * name,void * value,size_t size)297 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
298 {
299   return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
300 }
301 
xattr_fget(int fd,const char * name,void * value,size_t size)302 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
303 {
304   return fgetxattr(fd, name, value, size, 0, 0);
305 }
306 
xattr_list(const char * path,char * list,size_t size)307 ssize_t xattr_list(const char *path, char *list, size_t size)
308 {
309   return listxattr(path, list, size, 0);
310 }
311 
xattr_llist(const char * path,char * list,size_t size)312 ssize_t xattr_llist(const char *path, char *list, size_t size)
313 {
314   return listxattr(path, list, size, XATTR_NOFOLLOW);
315 }
316 
xattr_flist(int fd,char * list,size_t size)317 ssize_t xattr_flist(int fd, char *list, size_t size)
318 {
319   return flistxattr(fd, list, size, 0);
320 }
321 
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)322 ssize_t xattr_set(const char* path, const char* name,
323                   const void* value, size_t size, int flags)
324 {
325   return setxattr(path, name, value, size, 0, flags);
326 }
327 
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)328 ssize_t xattr_lset(const char* path, const char* name,
329                    const void* value, size_t size, int flags)
330 {
331   return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
332 }
333 
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)334 ssize_t xattr_fset(int fd, const char* name,
335                    const void* value, size_t size, int flags)
336 {
337   return fsetxattr(fd, name, value, size, 0, flags);
338 }
339 
340 #elif !defined(__OpenBSD__)
341 
342 #ifndef __DragonFly__
xattr_get(const char * path,const char * name,void * value,size_t size)343 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
344 {
345   return getxattr(path, name, value, size);
346 }
347 
xattr_lget(const char * path,const char * name,void * value,size_t size)348 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
349 {
350   return lgetxattr(path, name, value, size);
351 }
352 
xattr_fget(int fd,const char * name,void * value,size_t size)353 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
354 {
355   return fgetxattr(fd, name, value, size);
356 }
357 
xattr_list(const char * path,char * list,size_t size)358 ssize_t xattr_list(const char *path, char *list, size_t size)
359 {
360   return listxattr(path, list, size);
361 }
362 
xattr_llist(const char * path,char * list,size_t size)363 ssize_t xattr_llist(const char *path, char *list, size_t size)
364 {
365   return llistxattr(path, list, size);
366 }
367 
xattr_flist(int fd,char * list,size_t size)368 ssize_t xattr_flist(int fd, char *list, size_t size)
369 {
370   return flistxattr(fd, list, size);
371 }
372 
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)373 ssize_t xattr_set(const char* path, const char* name,
374                   const void* value, size_t size, int flags)
375 {
376   return setxattr(path, name, value, size, flags);
377 }
378 
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)379 ssize_t xattr_lset(const char* path, const char* name,
380                    const void* value, size_t size, int flags)
381 {
382   return lsetxattr(path, name, value, size, flags);
383 }
384 
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)385 ssize_t xattr_fset(int fd, const char* name,
386                    const void* value, size_t size, int flags)
387 {
388   return fsetxattr(fd, name, value, size, flags);
389 }
390 #endif
391 
392 #endif
393 
394 #ifdef __APPLE__
395 // In the absence of a mknodat system call, fchdir to dirfd and back
396 // around a regular mknod call...
mknodat(int dirfd,const char * path,mode_t mode,dev_t dev)397 int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev)
398 {
399   int old_dirfd = open(".", O_RDONLY), result;
400 
401   if (old_dirfd == -1 || fchdir(dirfd) == -1) return -1;
402   result = mknod(path, mode, dev);
403   if (fchdir(old_dirfd) == -1) perror_exit("mknodat couldn't return");
404   return result;
405 }
406 
407 // As of 10.15, macOS offers an fcntl F_PREALLOCATE rather than fallocate()
408 // or posix_fallocate() calls.
posix_fallocate(int fd,off_t offset,off_t length)409 int posix_fallocate(int fd, off_t offset, off_t length)
410 {
411   int e = errno, result;
412   fstore_t f;
413 
414   f.fst_flags = F_ALLOCATEALL;
415   f.fst_posmode = F_PEOFPOSMODE;
416   f.fst_offset = offset;
417   f.fst_length = length;
418   if (fcntl(fd, F_PREALLOCATE, &f) == -1) result = errno;
419   else result = ftruncate(fd, length);
420   errno = e;
421   return result;
422 }
423 #endif
424 
425 // Signals required by POSIX 2008:
426 // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
427 
428 #define SIGNIFY(x) {SIG##x, #x}
429 
430 static const struct signame signames[] = {
431   // POSIX
432   SIGNIFY(ABRT), SIGNIFY(ALRM), SIGNIFY(BUS),
433   SIGNIFY(FPE), SIGNIFY(HUP), SIGNIFY(ILL), SIGNIFY(INT), SIGNIFY(KILL),
434   SIGNIFY(PIPE), SIGNIFY(QUIT), SIGNIFY(SEGV), SIGNIFY(TERM),
435   SIGNIFY(USR1), SIGNIFY(USR2), SIGNIFY(SYS), SIGNIFY(TRAP),
436   SIGNIFY(VTALRM), SIGNIFY(XCPU), SIGNIFY(XFSZ),
437   // Non-POSIX signals that cause termination
438   SIGNIFY(PROF), SIGNIFY(IO),
439   // signals only present/absent on some targets (mips and macos)
440 #ifdef SIGEMT
441   SIGNIFY(EMT),
442 #endif
443 #ifdef SIGINFO
444   SIGNIFY(INFO),
445 #endif
446 #ifdef SIGPOLL
447   SIGNIFY(POLL),
448 #endif
449 #ifdef SIGPWR
450   SIGNIFY(PWR),
451 #endif
452 #ifdef SIGSTKFLT
453   SIGNIFY(STKFLT),
454 #endif
455 
456   // Note: sigatexit relies on all the signals with a default disposition that
457   // terminates the process coming *before* SIGCHLD.
458 
459   // POSIX signals that don't cause termination
460   SIGNIFY(CHLD), SIGNIFY(CONT), SIGNIFY(STOP), SIGNIFY(TSTP),
461   SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(URG),
462   // Non-POSIX signals that don't cause termination
463   SIGNIFY(WINCH),
464 };
465 int signames_len = ARRAY_LEN(signames);
466 
467 #undef SIGNIFY
468 
xsignal_all_killers(void * handler)469 void xsignal_all_killers(void *handler)
470 {
471   int i;
472 
473   if (!handler) handler = SIG_DFL;
474   for (i = 0; signames[i].num != SIGCHLD; i++)
475     if (signames[i].num != SIGKILL) xsignal(signames[i].num, handler);
476 }
477 
478 // Convert a string like "9", "KILL", "SIGHUP", or "SIGRTMIN+2" to a number.
sig_to_num(char * sigstr)479 int sig_to_num(char *sigstr)
480 {
481   int i, offset;
482   char *s;
483 
484   // Numeric?
485   i = estrtol(sigstr, &s, 10);
486   if (!errno && !*s) return i;
487 
488   // Skip leading "SIG".
489   strcasestart(&sigstr, "sig");
490 
491   // Named signal?
492   for (i=0; i<ARRAY_LEN(signames); i++)
493     if (!strcasecmp(sigstr, signames[i].name)) return signames[i].num;
494 
495   // Real-time signal?
496 #ifdef SIGRTMIN
497   if (strcasestart(&sigstr, "rtmin")) i = SIGRTMIN;
498   else if (strcasestart(&sigstr, "rtmax")) i = SIGRTMAX;
499   else return -1;
500 
501   // No offset?
502   if (!*sigstr) return i;
503 
504   // We allow any offset that's still a real-time signal: SIGRTMIN+20 is fine.
505   // Others are more restrictive, only accepting what they show with -l.
506   offset = estrtol(sigstr, &s, 10);
507   if (errno || *s) return -1;
508   i += offset;
509   if (i >= SIGRTMIN && i <= SIGRTMAX) return i;
510 #endif
511 
512   return -1;
513 }
514 
num_to_sig(int sig)515 char *num_to_sig(int sig)
516 {
517   int i;
518 
519   // A named signal?
520   for (i=0; i<signames_len; i++)
521     if (signames[i].num == sig) return signames[i].name;
522 
523   // A real-time signal?
524 #ifdef SIGRTMIN
525   if (sig == SIGRTMIN) return "RTMIN";
526   if (sig == SIGRTMAX) return "RTMAX";
527   if (sig > SIGRTMIN && sig < SIGRTMAX) {
528     if (sig-SIGRTMIN <= SIGRTMAX-sig) sprintf(libbuf, "RTMIN+%d", sig-SIGRTMIN);
529     else sprintf(libbuf, "RTMAX-%d", SIGRTMAX-sig);
530     return libbuf;
531   }
532 #endif
533 
534   return NULL;
535 }
536 
dev_minor(int dev)537 int dev_minor(int dev)
538 {
539 #if defined(__linux__)
540   return ((dev&0xfff00000)>>12)|(dev&0xff);
541 #elif defined(__APPLE__)
542   return dev&0xffffff;
543 #elif defined(__OpenBSD__)
544   return minor(dev);
545 #elif defined(__FreeBSD__) || defined(__DragonFly__)
546   return minor(dev);
547 #else
548 #error
549 #endif
550 }
551 
dev_major(int dev)552 int dev_major(int dev)
553 {
554 #if defined(__linux__)
555   return (dev&0xfff00)>>8;
556 #elif defined(__APPLE__)
557   return (dev>>24)&0xff;
558 #elif defined(__OpenBSD__)
559   return major(dev);
560 #elif defined(__FreeBSD__) || defined(__DragonFly__)
561   return major(dev);
562 #else
563 #error
564 #endif
565 }
566 
dev_makedev(int major,int minor)567 int dev_makedev(int major, int minor)
568 {
569 #if defined(__linux__)
570   return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12);
571 #elif defined(__APPLE__)
572   return (minor&0xffffff)|((major&0xff)<<24);
573 #elif defined(__OpenBSD__)
574   return makedev(major, minor);
575 #elif defined(__FreeBSD__) || defined(__DragonFly__)
576   return makedev(major, minor);
577 #else
578 #error
579 #endif
580 }
581 
fs_type_name(struct statfs * statfs)582 char *fs_type_name(struct statfs *statfs)
583 {
584 #if defined(__APPLE__) || defined(__OpenBSD__)
585   // macOS has an `f_type` field, but assigns values dynamically as filesystems
586   // are registered. They do give you the name directly though, so use that.
587   return statfs->f_fstypename;
588 #else
589   char *s = NULL;
590   struct {unsigned num; char *name;} nn[] = {
591     {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"},
592     {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"},
593     {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"},
594     {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"},
595     {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"},
596     {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"},
597     {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"},
598     {0x73717368, "squashfs"}
599   };
600   int i;
601 
602   for (i=0; i<ARRAY_LEN(nn); i++)
603     if (nn[i].num == statfs->f_type) s = nn[i].name;
604   if (!s) sprintf(s = libbuf, "0x%x", (unsigned)statfs->f_type);
605   return s;
606 #endif
607 }
608 
609 #if defined(__APPLE__)
610 #include <sys/disk.h>
get_block_device_size(int fd,unsigned long long * size)611 int get_block_device_size(int fd, unsigned long long* size)
612 {
613   unsigned long block_size, block_count;
614 
615   if (!ioctl(fd, DKIOCGETBLOCKSIZE, &block_size) &&
616       !ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count)) {
617     *size = block_count * block_size;
618     return 1;
619   }
620   return 0;
621 }
622 #elif defined(__linux__)
get_block_device_size(int fd,unsigned long long * size)623 int get_block_device_size(int fd, unsigned long long* size)
624 {
625   return (ioctl(fd, BLKGETSIZE64, size) >= 0);
626 }
627 #elif defined(__OpenBSD__)
628 #include <sys/dkio.h>
629 #include <sys/disklabel.h>
get_block_device_size(int fd,unsigned long long * size)630 int get_block_device_size(int fd, unsigned long long* size)
631 {
632   struct disklabel lab;
633   int status = (ioctl(fd, DIOCGDINFO, &lab) >= 0);
634   *size = lab.d_secsize * lab.d_nsectors;
635   return status;
636 }
637 #elif defined(__FreeBSD__)
638 #include <sys/disk.h>
get_block_device_size(int fd,unsigned long long * size)639 int get_block_device_size(int fd, unsigned long long* size)
640 {
641   off_t sz = 0;
642   if (ioctl(fd, DIOCGMEDIASIZE, &sz) >= 0) {
643     *size = sz;
644     return 1;
645   }
646   return 0;
647 }
648 #elif defined(__DragonFly__)
649 #include <sys/diskslice.h>
get_block_device_size(int fd,unsigned long long * size)650 int get_block_device_size(int fd, unsigned long long* size)
651 {
652   struct partinfo dp;
653 
654   if (ioctl(fd, DIOCGPART, &dp) >= 0) {
655     *size = (off_t)dp.media_size;
656     return 1;
657   }
658   return 0;
659 }
660 #endif
661 
662 // TODO copy_file_range
663 // Return bytes copied from in to out. If bytes <0 copy all of in to out.
664 // If consuemd isn't null, amount read saved there (return is written or error)
sendfile_len(int in,int out,long long bytes,long long * consumed)665 long long sendfile_len(int in, int out, long long bytes, long long *consumed)
666 {
667   long long total = 0, len, ww;
668 
669   if (consumed) *consumed = 0;
670   if (in<0) return 0;
671   while (bytes != total) {
672     ww = 0;
673     len = bytes-total;
674     if (bytes<0 || len>sizeof(libbuf)) len = sizeof(libbuf);
675 
676     errno = 0;
677 #if CFG_TOYBOX_COPYFILERANGE
678     len = copy_file_range(in, 0, out, 0, bytes, 0);
679 #else
680     ww = len = read(in, libbuf, len);
681 #endif
682     if (len<1 && errno==EAGAIN) continue;
683     if (len<1) break;
684     if (consumed) *consumed += len;
685     if (ww && writeall(out, libbuf, len) != len) return -1;
686     total += len;
687   }
688 
689   return total;
690 }
691 
692