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 return -1;
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 return -1;
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 // POSIX
426 SIGNIFY(ABRT), SIGNIFY(ALRM), SIGNIFY(BUS),
427 SIGNIFY(FPE), SIGNIFY(HUP), SIGNIFY(ILL), SIGNIFY(INT), SIGNIFY(KILL),
428 SIGNIFY(PIPE), SIGNIFY(QUIT), SIGNIFY(SEGV), SIGNIFY(TERM),
429 SIGNIFY(USR1), SIGNIFY(USR2), SIGNIFY(SYS), SIGNIFY(TRAP),
430 SIGNIFY(VTALRM), SIGNIFY(XCPU), SIGNIFY(XFSZ),
431 // Non-POSIX signals that cause termination
432 SIGNIFY(PROF), SIGNIFY(IO),
433 // signals only present/absent on some targets (mips and macos)
434 #ifdef SIGEMT
435 SIGNIFY(EMT),
436 #endif
437 #ifdef SIGINFO
438 SIGNIFY(INFO),
439 #endif
440 #ifdef SIGPOLL
441 SIGNIFY(POLL),
442 #endif
443 #ifdef SIGPWR
444 SIGNIFY(PWR),
445 #endif
446 #ifdef SIGSTKFLT
447 SIGNIFY(STKFLT),
448 #endif
449
450 // Note: sigatexit relies on all the signals with a default disposition that
451 // terminates the process coming *before* SIGCHLD.
452
453 // POSIX signals that don't cause termination
454 SIGNIFY(CHLD), SIGNIFY(CONT), SIGNIFY(STOP), SIGNIFY(TSTP),
455 SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(URG),
456 // Non-POSIX signals that don't cause termination
457 SIGNIFY(WINCH),
458 };
459 int signames_len = ARRAY_LEN(signames);
460
461 #undef SIGNIFY
462
xsignal_all_killers(void * handler)463 void xsignal_all_killers(void *handler)
464 {
465 int i;
466
467 if (!handler) handler = SIG_DFL;
468 for (i = 0; signames[i].num != SIGCHLD; i++)
469 if (signames[i].num != SIGKILL) xsignal(signames[i].num, handler);
470 }
471
472 // Convert a string like "9", "KILL", "SIGHUP", or "SIGRTMIN+2" to a number.
sig_to_num(char * sigstr)473 int sig_to_num(char *sigstr)
474 {
475 int i, offset;
476 char *s;
477
478 // Numeric?
479 i = estrtol(sigstr, &s, 10);
480 if (!errno && !*s) return i;
481
482 // Skip leading "SIG".
483 strcasestart(&sigstr, "sig");
484
485 // Named signal?
486 for (i=0; i<ARRAY_LEN(signames); i++)
487 if (!strcasecmp(sigstr, signames[i].name)) return signames[i].num;
488
489 // Real-time signal?
490 #ifdef SIGRTMIN
491 if (strcasestart(&sigstr, "rtmin")) i = SIGRTMIN;
492 else if (strcasestart(&sigstr, "rtmax")) i = SIGRTMAX;
493 else return -1;
494
495 // No offset?
496 if (!*sigstr) return i;
497
498 // We allow any offset that's still a real-time signal: SIGRTMIN+20 is fine.
499 // Others are more restrictive, only accepting what they show with -l.
500 offset = estrtol(sigstr, &s, 10);
501 if (errno || *s) return -1;
502 i += offset;
503 if (i >= SIGRTMIN && i <= SIGRTMAX) return i;
504 #endif
505
506 return -1;
507 }
508
num_to_sig(int sig)509 char *num_to_sig(int sig)
510 {
511 int i;
512
513 // A named signal?
514 for (i=0; i<signames_len; i++)
515 if (signames[i].num == sig) return signames[i].name;
516
517 // A real-time signal?
518 #ifdef SIGRTMIN
519 if (sig == SIGRTMIN) return "RTMIN";
520 if (sig == SIGRTMAX) return "RTMAX";
521 if (sig > SIGRTMIN && sig < SIGRTMAX) {
522 if (sig-SIGRTMIN <= SIGRTMAX-sig) sprintf(libbuf, "RTMIN+%d", sig-SIGRTMIN);
523 else sprintf(libbuf, "RTMAX-%d", SIGRTMAX-sig);
524 return libbuf;
525 }
526 #endif
527
528 return NULL;
529 }
530
dev_minor(int dev)531 int dev_minor(int dev)
532 {
533 #if defined(__linux__)
534 return ((dev&0xfff00000)>>12)|(dev&0xff);
535 #elif defined(__APPLE__)
536 return dev&0xffffff;
537 #elif defined(__OpenBSD__)
538 return minor(dev);
539 #else
540 #error
541 #endif
542 }
543
dev_major(int dev)544 int dev_major(int dev)
545 {
546 #if defined(__linux__)
547 return (dev&0xfff00)>>8;
548 #elif defined(__APPLE__)
549 return (dev>>24)&0xff;
550 #elif defined(__OpenBSD__)
551 return major(dev);
552 #else
553 #error
554 #endif
555 }
556
dev_makedev(int major,int minor)557 int dev_makedev(int major, int minor)
558 {
559 #if defined(__linux__)
560 return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12);
561 #elif defined(__APPLE__)
562 return (minor&0xffffff)|((major&0xff)<<24);
563 #elif defined(__OpenBSD__)
564 return makedev(major, minor);
565 #else
566 #error
567 #endif
568 }
569
fs_type_name(struct statfs * statfs)570 char *fs_type_name(struct statfs *statfs)
571 {
572 #if defined(__APPLE__) || defined(__OpenBSD__)
573 // macOS has an `f_type` field, but assigns values dynamically as filesystems
574 // are registered. They do give you the name directly though, so use that.
575 return statfs->f_fstypename;
576 #else
577 char *s = NULL;
578 struct {unsigned num; char *name;} nn[] = {
579 {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"},
580 {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"},
581 {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"},
582 {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"},
583 {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"},
584 {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"},
585 {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"},
586 {0x73717368, "squashfs"}
587 };
588 int i;
589
590 for (i=0; i<ARRAY_LEN(nn); i++)
591 if (nn[i].num == statfs->f_type) s = nn[i].name;
592 if (!s) sprintf(s = libbuf, "0x%x", (unsigned)statfs->f_type);
593 return s;
594 #endif
595 }
596
597 #if defined(__APPLE__)
598 #include <sys/disk.h>
get_block_device_size(int fd,unsigned long long * size)599 int get_block_device_size(int fd, unsigned long long* size)
600 {
601 unsigned long block_size, block_count;
602
603 if (!ioctl(fd, DKIOCGETBLOCKSIZE, &block_size) &&
604 !ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count)) {
605 *size = block_count * block_size;
606 return 1;
607 }
608 return 0;
609 }
610 #elif defined(__linux__)
get_block_device_size(int fd,unsigned long long * size)611 int get_block_device_size(int fd, unsigned long long* size)
612 {
613 return (ioctl(fd, BLKGETSIZE64, size) >= 0);
614 }
615 #elif defined(__OpenBSD__)
616 #include <sys/dkio.h>
617 #include <sys/disklabel.h>
get_block_device_size(int fd,unsigned long long * size)618 int get_block_device_size(int fd, unsigned long long* size)
619 {
620 struct disklabel lab;
621 int status = (ioctl(fd, DIOCGDINFO, &lab) >= 0);
622 *size = lab.d_secsize * lab.d_nsectors;
623 return status;
624 }
625 #endif
626
627 // TODO copy_file_range
628 // Return bytes copied from in to out. If bytes <0 copy all of in to out.
629 // 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)630 long long sendfile_len(int in, int out, long long bytes, long long *consumed)
631 {
632 long long total = 0, len, ww;
633
634 if (consumed) *consumed = 0;
635 if (in<0) return 0;
636 while (bytes != total) {
637 ww = 0;
638 len = bytes-total;
639 if (bytes<0 || len>sizeof(libbuf)) len = sizeof(libbuf);
640
641 errno = 0;
642 #if CFG_TOYBOX_COPYFILERANGE
643 len = copy_file_range(in, 0, out, 0, bytes, 0);
644 #else
645 ww = len = read(in, libbuf, len);
646 #endif
647 if (len<1 && errno==EAGAIN) continue;
648 if (len<1) break;
649 if (consumed) *consumed += len;
650 if (ww && writeall(out, libbuf, len) != len) return -1;
651 total += len;
652 }
653
654 return total;
655 }
656
657