• 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__)
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   skip = strncmp(typelist, "no", 2);
130 
131   for (;;) {
132     if (!(t = comma_iterate(&typelist, &len))) break;
133     if (!skip) {
134       // If one -t starts with "no", the rest must too
135       if (strncmp(t, "no", 2)) error_exit("bad typelist");
136       if (!strncmp(t+2, ml->type, len-2)) {
137         skip = 1;
138         break;
139       }
140     } else if (!strncmp(t, ml->type, len) && !ml->type[len]) {
141       skip = 0;
142       break;
143     }
144   }
145 
146   return !skip;
147 }
148 
xgetmountlist(char * path)149 struct mtab_list *xgetmountlist(char *path)
150 {
151   struct mtab_list *mtlist = 0, *mt;
152   struct mntent *me;
153   FILE *fp;
154   char *p = path ? path : "/proc/mounts";
155 
156   if (!(fp = setmntent(p, "r"))) perror_exit("bad %s", p);
157 
158   // The "test" part of the loop is done before the first time through and
159   // again after each "increment", so putting the actual load there avoids
160   // duplicating it. If the load was NULL, the loop stops.
161 
162   while ((me = getmntent(fp))) {
163     mt = xzalloc(sizeof(struct mtab_list) + strlen(me->mnt_fsname) +
164       strlen(me->mnt_dir) + strlen(me->mnt_type) + strlen(me->mnt_opts) + 4);
165     dlist_add_nomalloc((void *)&mtlist, (void *)mt);
166 
167     // Collect details about mounted filesystem
168     // Don't report errors, just leave data zeroed
169     if (!path) {
170       stat(me->mnt_dir, &(mt->stat));
171       statvfs(me->mnt_dir, &(mt->statvfs));
172     }
173 
174     // Remember information from /proc/mounts
175     mt->dir = stpcpy(mt->type, me->mnt_type)+1;
176     mt->device = stpcpy(mt->dir, me->mnt_dir)+1;
177     mt->opts = stpcpy(mt->device, me->mnt_fsname)+1;
178     strcpy(mt->opts, me->mnt_opts);
179 
180     octal_deslash(mt->dir);
181     octal_deslash(mt->device);
182   }
183   endmntent(fp);
184 
185   return mtlist;
186 }
187 
188 #endif
189 
190 #ifdef __APPLE__
191 
192 #include <sys/event.h>
193 
xnotify_init(int max)194 struct xnotify *xnotify_init(int max)
195 {
196   struct xnotify *not = xzalloc(sizeof(struct xnotify));
197 
198   not->max = max;
199   if ((not->kq = kqueue()) == -1) perror_exit("kqueue");
200   not->paths = xmalloc(max * sizeof(char *));
201   not->fds = xmalloc(max * sizeof(int));
202 
203   return not;
204 }
205 
xnotify_add(struct xnotify * not,int fd,char * path)206 int xnotify_add(struct xnotify *not, int fd, char *path)
207 {
208   struct kevent event;
209 
210   if (not->count == not->max) error_exit("xnotify_add overflow");
211   EV_SET(&event, fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE, 0, NULL);
212   if (kevent(not->kq, &event, 1, NULL, 0, NULL) == -1 || event.flags & EV_ERROR)
213     return -1;
214   not->paths[not->count] = path;
215   not->fds[not->count++] = fd;
216 
217   return 0;
218 }
219 
xnotify_wait(struct xnotify * not,char ** path)220 int xnotify_wait(struct xnotify *not, char **path)
221 {
222   struct kevent event;
223   int i;
224 
225   for (;;) {
226     if (kevent(not->kq, NULL, 0, &event, 1, NULL) != -1) {
227       // We get the fd for free, but still have to search for the path.
228       for (i = 0; i<not->count; i++) if (not->fds[i]==event.ident) {
229         *path = not->paths[i];
230 
231         return event.ident;
232       }
233     }
234   }
235 }
236 
237 #else
238 
239 #include <sys/inotify.h>
240 
xnotify_init(int max)241 struct xnotify *xnotify_init(int max)
242 {
243   struct xnotify *not = xzalloc(sizeof(struct xnotify));
244 
245   not->max = max;
246   if ((not->kq = inotify_init()) < 0) perror_exit("inotify_init");
247   not->paths = xmalloc(max * sizeof(char *));
248   not->fds = xmalloc(max * 2 * sizeof(int));
249 
250   return not;
251 }
252 
xnotify_add(struct xnotify * not,int fd,char * path)253 int xnotify_add(struct xnotify *not, int fd, char *path)
254 {
255   int i = 2*not->count;
256 
257   if (not->max == not->count) error_exit("xnotify_add overflow");
258   if ((not->fds[i] = inotify_add_watch(not->kq, path, IN_MODIFY))==-1)
259     return -1;
260   not->fds[i+1] = fd;
261   not->paths[not->count++] = path;
262 
263   return 0;
264 }
265 
xnotify_wait(struct xnotify * not,char ** path)266 int xnotify_wait(struct xnotify *not, char **path)
267 {
268   struct inotify_event ev;
269   int i;
270 
271   for (;;) {
272     if (sizeof(ev)!=read(not->kq, &ev, sizeof(ev))) perror_exit("inotify");
273 
274     for (i = 0; i<not->count; i++) if (ev.wd==not->fds[2*i]) {
275       *path = not->paths[i];
276 
277       return not->fds[2*i+1];
278     }
279   }
280 }
281 
282 #endif
283 
284 #ifdef __APPLE__
285 
xattr_get(const char * path,const char * name,void * value,size_t size)286 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
287 {
288   return getxattr(path, name, value, size, 0, 0);
289 }
290 
xattr_lget(const char * path,const char * name,void * value,size_t size)291 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
292 {
293   return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
294 }
295 
xattr_fget(int fd,const char * name,void * value,size_t size)296 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
297 {
298   return fgetxattr(fd, name, value, size, 0, 0);
299 }
300 
xattr_list(const char * path,char * list,size_t size)301 ssize_t xattr_list(const char *path, char *list, size_t size)
302 {
303   return listxattr(path, list, size, 0);
304 }
305 
xattr_llist(const char * path,char * list,size_t size)306 ssize_t xattr_llist(const char *path, char *list, size_t size)
307 {
308   return listxattr(path, list, size, XATTR_NOFOLLOW);
309 }
310 
xattr_flist(int fd,char * list,size_t size)311 ssize_t xattr_flist(int fd, char *list, size_t size)
312 {
313   return flistxattr(fd, list, size, 0);
314 }
315 
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)316 ssize_t xattr_set(const char* path, const char* name,
317                   const void* value, size_t size, int flags)
318 {
319   return setxattr(path, name, value, size, 0, flags);
320 }
321 
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)322 ssize_t xattr_lset(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 | XATTR_NOFOLLOW);
326 }
327 
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)328 ssize_t xattr_fset(int fd, const char* name,
329                    const void* value, size_t size, int flags)
330 {
331   return fsetxattr(fd, name, value, size, 0, flags);
332 }
333 
334 #else
335 
xattr_get(const char * path,const char * name,void * value,size_t size)336 ssize_t xattr_get(const char *path, const char *name, void *value, size_t size)
337 {
338   return getxattr(path, name, value, size);
339 }
340 
xattr_lget(const char * path,const char * name,void * value,size_t size)341 ssize_t xattr_lget(const char *path, const char *name, void *value, size_t size)
342 {
343   return lgetxattr(path, name, value, size);
344 }
345 
xattr_fget(int fd,const char * name,void * value,size_t size)346 ssize_t xattr_fget(int fd, const char *name, void *value, size_t size)
347 {
348   return fgetxattr(fd, name, value, size);
349 }
350 
xattr_list(const char * path,char * list,size_t size)351 ssize_t xattr_list(const char *path, char *list, size_t size)
352 {
353   return listxattr(path, list, size);
354 }
355 
xattr_llist(const char * path,char * list,size_t size)356 ssize_t xattr_llist(const char *path, char *list, size_t size)
357 {
358   return llistxattr(path, list, size);
359 }
360 
xattr_flist(int fd,char * list,size_t size)361 ssize_t xattr_flist(int fd, char *list, size_t size)
362 {
363   return flistxattr(fd, list, size);
364 }
365 
xattr_set(const char * path,const char * name,const void * value,size_t size,int flags)366 ssize_t xattr_set(const char* path, const char* name,
367                   const void* value, size_t size, int flags)
368 {
369   return setxattr(path, name, value, size, flags);
370 }
371 
xattr_lset(const char * path,const char * name,const void * value,size_t size,int flags)372 ssize_t xattr_lset(const char* path, const char* name,
373                    const void* value, size_t size, int flags)
374 {
375   return lsetxattr(path, name, value, size, flags);
376 }
377 
xattr_fset(int fd,const char * name,const void * value,size_t size,int flags)378 ssize_t xattr_fset(int fd, const char* name,
379                    const void* value, size_t size, int flags)
380 {
381   return fsetxattr(fd, name, value, size, flags);
382 }
383 
384 
385 #endif
386 
387 #ifdef __APPLE__
388 // In the absence of a mknodat system call, fchdir to dirfd and back
389 // around a regular mknod call...
mknodat(int dirfd,const char * path,mode_t mode,dev_t dev)390 int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev)
391 {
392   int old_dirfd = open(".", O_RDONLY), result;
393 
394   if (old_dirfd == -1 || fchdir(dirfd) == -1) return -1;
395   result = mknod(path, mode, dev);
396   if (fchdir(old_dirfd) == -1) perror_exit("mknodat couldn't return");
397   return result;
398 }
399 
400 // As of 10.15, macOS offers an fcntl F_PREALLOCATE rather than fallocate()
401 // or posix_fallocate() calls.
posix_fallocate(int fd,off_t offset,off_t length)402 int posix_fallocate(int fd, off_t offset, off_t length)
403 {
404   int e = errno, result;
405   fstore_t f;
406 
407   f.fst_flags = F_ALLOCATEALL;
408   f.fst_posmode = F_PEOFPOSMODE;
409   f.fst_offset = offset;
410   f.fst_length = length;
411   if (fcntl(fd, F_PREALLOCATE, &f) == -1) result = errno;
412   else result = ftruncate(fd, length);
413   errno = e;
414   return result;
415 }
416 #endif
417 
418 // Signals required by POSIX 2008:
419 // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html
420 
421 #define SIGNIFY(x) {SIG##x, #x}
422 
423 static const struct signame signames[] = {
424   // POSIX
425   SIGNIFY(ABRT), SIGNIFY(ALRM), SIGNIFY(BUS),
426   SIGNIFY(FPE), SIGNIFY(HUP), SIGNIFY(ILL), SIGNIFY(INT), SIGNIFY(KILL),
427   SIGNIFY(PIPE), SIGNIFY(QUIT), SIGNIFY(SEGV), SIGNIFY(TERM),
428   SIGNIFY(USR1), SIGNIFY(USR2), SIGNIFY(SYS), SIGNIFY(TRAP),
429   SIGNIFY(VTALRM), SIGNIFY(XCPU), SIGNIFY(XFSZ),
430   // Non-POSIX signals that cause termination
431   SIGNIFY(PROF), SIGNIFY(IO),
432 #ifdef __linux__
433   SIGNIFY(STKFLT), SIGNIFY(POLL), SIGNIFY(PWR),
434 #elif defined(__APPLE__)
435   SIGNIFY(EMT), SIGNIFY(INFO),
436 #endif
437 
438   // Note: sigatexit relies on all the signals with a default disposition that
439   // terminates the process coming *before* SIGCHLD.
440 
441   // POSIX signals that don't cause termination
442   SIGNIFY(CHLD), SIGNIFY(CONT), SIGNIFY(STOP), SIGNIFY(TSTP),
443   SIGNIFY(TTIN), SIGNIFY(TTOU), SIGNIFY(URG),
444   // Non-POSIX signals that don't cause termination
445   SIGNIFY(WINCH),
446 };
447 int signames_len = ARRAY_LEN(signames);
448 
449 #undef SIGNIFY
450 
xsignal_all_killers(void * handler)451 void xsignal_all_killers(void *handler)
452 {
453   int i;
454 
455   for (i=0; signames[i].num != SIGCHLD; i++)
456     if (signames[i].num != SIGKILL)
457       xsignal(signames[i].num, handler ? exit_signal : SIG_DFL);
458 }
459 
460 // Convert a string like "9", "KILL", "SIGHUP", or "SIGRTMIN+2" to a number.
sig_to_num(char * sigstr)461 int sig_to_num(char *sigstr)
462 {
463   int i, offset;
464   char *s;
465 
466   // Numeric?
467   i = estrtol(sigstr, &s, 10);
468   if (!errno && !*s) return i;
469 
470   // Skip leading "SIG".
471   strcasestart(&sigstr, "sig");
472 
473   // Named signal?
474   for (i=0; i<ARRAY_LEN(signames); i++)
475     if (!strcasecmp(sigstr, signames[i].name)) return signames[i].num;
476 
477   // Real-time signal?
478 #ifdef SIGRTMIN
479   if (strcasestart(&sigstr, "rtmin")) i = SIGRTMIN;
480   else if (strcasestart(&sigstr, "rtmax")) i = SIGRTMAX;
481   else return -1;
482 
483   // No offset?
484   if (!*sigstr) return i;
485 
486   // We allow any offset that's still a real-time signal: SIGRTMIN+20 is fine.
487   // Others are more restrictive, only accepting what they show with -l.
488   offset = estrtol(sigstr, &s, 10);
489   if (errno || *s) return -1;
490   i += offset;
491   if (i >= SIGRTMIN && i <= SIGRTMAX) return i;
492 #endif
493 
494   return -1;
495 }
496 
num_to_sig(int sig)497 char *num_to_sig(int sig)
498 {
499   int i;
500 
501   // A named signal?
502   for (i=0; i<signames_len; i++)
503     if (signames[i].num == sig) return signames[i].name;
504 
505   // A real-time signal?
506 #ifdef SIGRTMIN
507   if (sig == SIGRTMIN) return "RTMIN";
508   if (sig == SIGRTMAX) return "RTMAX";
509   if (sig > SIGRTMIN && sig < SIGRTMAX) {
510     if (sig-SIGRTMIN <= SIGRTMAX-sig) sprintf(libbuf, "RTMIN+%d", sig-SIGRTMIN);
511     else sprintf(libbuf, "RTMAX-%d", SIGRTMAX-sig);
512     return libbuf;
513   }
514 #endif
515 
516   return NULL;
517 }
518 
dev_minor(int dev)519 int dev_minor(int dev)
520 {
521 #if defined(__linux__)
522   return ((dev&0xfff00000)>>12)|(dev&0xff);
523 #elif defined(__APPLE__)
524   return dev&0xffffff;
525 #else
526 #error
527 #endif
528 }
529 
dev_major(int dev)530 int dev_major(int dev)
531 {
532 #if defined(__linux__)
533   return (dev&0xfff00)>>8;
534 #elif defined(__APPLE__)
535   return (dev>>24)&0xff;
536 #else
537 #error
538 #endif
539 }
540 
dev_makedev(int major,int minor)541 int dev_makedev(int major, int minor)
542 {
543 #if defined(__linux__)
544   return (minor&0xff)|((major&0xfff)<<8)|((minor&0xfff00)<<12);
545 #elif defined(__APPLE__)
546   return (minor&0xffffff)|((major&0xff)<<24);
547 #else
548 #error
549 #endif
550 }
551 
fs_type_name(struct statfs * statfs)552 char *fs_type_name(struct statfs *statfs)
553 {
554 #if defined(__APPLE__)
555   // macOS has an `f_type` field, but assigns values dynamically as filesystems
556   // are registered. They do give you the name directly though, so use that.
557   return statfs->f_fstypename;
558 #else
559   char *s = NULL;
560   struct {unsigned num; char *name;} nn[] = {
561     {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"},
562     {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"},
563     {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"},
564     {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"},
565     {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"},
566     {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"},
567     {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"},
568     {0x73717368, "squashfs"}
569   };
570   int i;
571 
572   for (i=0; i<ARRAY_LEN(nn); i++)
573     if (nn[i].num == statfs->f_type) s = nn[i].name;
574   if (!s) sprintf(s = libbuf, "0x%x", (unsigned)statfs->f_type);
575   return s;
576 #endif
577 }
578