mount: do not pass "comment=ANYTHING" option to kernel. Closes 5240
[oweals/busybox.git] / util-linux / mount.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini mount implementation for busybox
4  *
5  * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7  * Copyright (C) 2005-2006 by Rob Landley <rob@landley.net>
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10  */
11 // Design notes: There is no spec for mount.  Remind me to write one.
12 //
13 // mount_main() calls singlemount() which calls mount_it_now().
14 //
15 // mount_main() can loop through /etc/fstab for mount -a
16 // singlemount() can loop through /etc/filesystems for fstype detection.
17 // mount_it_now() does the actual mount.
18 //
19
20 //usage:#define mount_trivial_usage
21 //usage:       "[OPTIONS] [-o OPTS] DEVICE NODE"
22 //usage:#define mount_full_usage "\n\n"
23 //usage:       "Mount a filesystem. Filesystem autodetection requires /proc.\n"
24 //usage:     "\n        -a              Mount all filesystems in fstab"
25 //usage:        IF_FEATURE_MOUNT_FAKE(
26 //usage:        IF_FEATURE_MTAB_SUPPORT(
27 //usage:     "\n        -f              Update /etc/mtab, but don't mount"
28 //usage:        )
29 //usage:        IF_NOT_FEATURE_MTAB_SUPPORT(
30 //usage:     "\n        -f              Dry run"
31 //usage:        )
32 //usage:        )
33 //usage:        IF_FEATURE_MOUNT_HELPERS(
34 //usage:     "\n        -i              Don't run mount helper"
35 //usage:        )
36 //usage:        IF_FEATURE_MTAB_SUPPORT(
37 //usage:     "\n        -n              Don't update /etc/mtab"
38 //usage:        )
39 //usage:     "\n        -r              Read-only mount"
40 //usage:     "\n        -w              Read-write mount (default)"
41 //usage:     "\n        -t FSTYPE[,...] Filesystem type(s)"
42 //usage:     "\n        -O OPT          Mount only filesystems with option OPT (-a only)"
43 //usage:     "\n-o OPT:"
44 //usage:        IF_FEATURE_MOUNT_LOOP(
45 //usage:     "\n        loop            Ignored (loop devices are autodetected)"
46 //usage:        )
47 //usage:        IF_FEATURE_MOUNT_FLAGS(
48 //usage:     "\n        [a]sync         Writes are [a]synchronous"
49 //usage:     "\n        [no]atime       Disable/enable updates to inode access times"
50 //usage:     "\n        [no]diratime    Disable/enable atime updates to directories"
51 //usage:     "\n        [no]relatime    Disable/enable atime updates relative to modification time"
52 //usage:     "\n        [no]dev         (Dis)allow use of special device files"
53 //usage:     "\n        [no]exec        (Dis)allow use of executable files"
54 //usage:     "\n        [no]suid        (Dis)allow set-user-id-root programs"
55 //usage:     "\n        [r]shared       Convert [recursively] to a shared subtree"
56 //usage:     "\n        [r]slave        Convert [recursively] to a slave subtree"
57 //usage:     "\n        [r]private      Convert [recursively] to a private subtree"
58 //usage:     "\n        [un]bindable    Make mount point [un]able to be bind mounted"
59 //usage:     "\n        [r]bind         Bind a file or directory [recursively] to another location"
60 //usage:     "\n        move            Relocate an existing mount point"
61 //usage:        )
62 //usage:     "\n        remount         Remount a mounted filesystem, changing flags"
63 //usage:     "\n        ro/rw           Same as -r/-w"
64 //usage:     "\n"
65 //usage:     "\nThere are filesystem-specific -o flags."
66 //usage:
67 //usage:#define mount_example_usage
68 //usage:       "$ mount\n"
69 //usage:       "/dev/hda3 on / type minix (rw)\n"
70 //usage:       "proc on /proc type proc (rw)\n"
71 //usage:       "devpts on /dev/pts type devpts (rw)\n"
72 //usage:       "$ mount /dev/fd0 /mnt -t msdos -o ro\n"
73 //usage:       "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
74 //usage:       "$ mount cd_image.iso mydir\n"
75 //usage:#define mount_notes_usage
76 //usage:       "Returns 0 for success, number of failed mounts for -a, or errno for one mount."
77
78 #include <mntent.h>
79 #include <syslog.h>
80 #include <sys/mount.h>
81 // Grab more as needed from util-linux's mount/mount_constants.h
82 #ifndef MS_DIRSYNC
83 # define MS_DIRSYNC     (1 << 7) // Directory modifications are synchronous
84 #endif
85 #ifndef MS_UNION
86 # define MS_UNION       (1 << 8)
87 #endif
88 #ifndef MS_BIND
89 # define MS_BIND        (1 << 12)
90 #endif
91 #ifndef MS_MOVE
92 # define MS_MOVE        (1 << 13)
93 #endif
94 #ifndef MS_RECURSIVE
95 # define MS_RECURSIVE   (1 << 14)
96 #endif
97 #ifndef MS_SILENT
98 # define MS_SILENT      (1 << 15)
99 #endif
100 // The shared subtree stuff, which went in around 2.6.15
101 #ifndef MS_UNBINDABLE
102 # define MS_UNBINDABLE  (1 << 17)
103 #endif
104 #ifndef MS_PRIVATE
105 # define MS_PRIVATE     (1 << 18)
106 #endif
107 #ifndef MS_SLAVE
108 # define MS_SLAVE       (1 << 19)
109 #endif
110 #ifndef MS_SHARED
111 # define MS_SHARED      (1 << 20)
112 #endif
113 #ifndef MS_RELATIME
114 # define MS_RELATIME    (1 << 21)
115 #endif
116
117 #include "libbb.h"
118 #if ENABLE_FEATURE_MOUNT_LABEL
119 # include "volume_id.h"
120 #else
121 # define resolve_mount_spec(fsname) ((void)0)
122 #endif
123
124 // Needed for nfs support only
125 #include <sys/utsname.h>
126 #undef TRUE
127 #undef FALSE
128 #if ENABLE_FEATURE_MOUNT_NFS
129 /* This is just a warning of a common mistake.  Possibly this should be a
130  * uclibc faq entry rather than in busybox... */
131 # if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
132 #  error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
133 # endif
134 # include <rpc/rpc.h>
135 # include <rpc/pmap_prot.h>
136 # include <rpc/pmap_clnt.h>
137 #endif
138
139
140 #if defined(__dietlibc__)
141 // 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
142 // dietlibc-0.30 does not have implementation of getmntent_r()
143 static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
144                 char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
145 {
146         struct mntent* ment = getmntent(stream);
147         return memcpy(result, ment, sizeof(*ment));
148 }
149 #endif
150
151
152 // Not real flags, but we want to be able to check for this.
153 enum {
154         MOUNT_USERS  = (1 << 28) * ENABLE_DESKTOP,
155         MOUNT_NOAUTO = (1 << 29),
156         MOUNT_SWAP   = (1 << 30),
157 };
158
159
160 #define OPTION_STR "o:t:rwanfvsiO:"
161 enum {
162         OPT_o = (1 << 0),
163         OPT_t = (1 << 1),
164         OPT_r = (1 << 2),
165         OPT_w = (1 << 3),
166         OPT_a = (1 << 4),
167         OPT_n = (1 << 5),
168         OPT_f = (1 << 6),
169         OPT_v = (1 << 7),
170         OPT_s = (1 << 8),
171         OPT_i = (1 << 9),
172         OPT_O = (1 << 10),
173 };
174
175 #if ENABLE_FEATURE_MTAB_SUPPORT
176 #define USE_MTAB (!(option_mask32 & OPT_n))
177 #else
178 #define USE_MTAB 0
179 #endif
180
181 #if ENABLE_FEATURE_MOUNT_FAKE
182 #define FAKE_IT (option_mask32 & OPT_f)
183 #else
184 #define FAKE_IT 0
185 #endif
186
187 #if ENABLE_FEATURE_MOUNT_HELPERS
188 #define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
189 #else
190 #define HELPERS_ALLOWED 0
191 #endif
192
193
194 // TODO: more "user" flag compatibility.
195 // "user" option (from mount manpage):
196 // Only the user that mounted a filesystem can unmount it again.
197 // If any user should be able to unmount, then use users instead of user
198 // in the fstab line.  The owner option is similar to the user option,
199 // with the restriction that the user must be the owner of the special file.
200 // This may be useful e.g. for /dev/fd if a login script makes
201 // the console user owner of this device.
202
203 // Standard mount options (from -o options or --options),
204 // with corresponding flags
205 static const int32_t mount_options[] = {
206         // MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
207
208         IF_FEATURE_MOUNT_LOOP(
209                 /* "loop" */ 0,
210         )
211
212         IF_FEATURE_MOUNT_FSTAB(
213                 /* "defaults" */ 0,
214                 /* "quiet" 0 - do not filter out, vfat wants to see it */
215                 /* "noauto" */ MOUNT_NOAUTO,
216                 /* "sw"     */ MOUNT_SWAP,
217                 /* "swap"   */ MOUNT_SWAP,
218                 IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
219                 IF_DESKTOP(/* "users" */ MOUNT_USERS,)
220                 /* "_netdev" */ 0,
221                 IF_DESKTOP(/* "comment" */ 0,) /* systemd uses this in fstab */
222         )
223
224         IF_FEATURE_MOUNT_FLAGS(
225                 // vfs flags
226                 /* "nosuid"      */ MS_NOSUID,
227                 /* "suid"        */ ~MS_NOSUID,
228                 /* "dev"         */ ~MS_NODEV,
229                 /* "nodev"       */ MS_NODEV,
230                 /* "exec"        */ ~MS_NOEXEC,
231                 /* "noexec"      */ MS_NOEXEC,
232                 /* "sync"        */ MS_SYNCHRONOUS,
233                 /* "dirsync"     */ MS_DIRSYNC,
234                 /* "async"       */ ~MS_SYNCHRONOUS,
235                 /* "atime"       */ ~MS_NOATIME,
236                 /* "noatime"     */ MS_NOATIME,
237                 /* "diratime"    */ ~MS_NODIRATIME,
238                 /* "nodiratime"  */ MS_NODIRATIME,
239                 /* "mand"        */ MS_MANDLOCK,
240                 /* "nomand"      */ ~MS_MANDLOCK,
241                 /* "relatime"    */ MS_RELATIME,
242                 /* "norelatime"  */ ~MS_RELATIME,
243                 /* "loud"        */ ~MS_SILENT,
244                 /* "rbind"       */ MS_BIND|MS_RECURSIVE,
245
246                 // action flags
247                 /* "union"       */ MS_UNION,
248                 /* "bind"        */ MS_BIND,
249                 /* "move"        */ MS_MOVE,
250                 /* "shared"      */ MS_SHARED,
251                 /* "slave"       */ MS_SLAVE,
252                 /* "private"     */ MS_PRIVATE,
253                 /* "unbindable"  */ MS_UNBINDABLE,
254                 /* "rshared"     */ MS_SHARED|MS_RECURSIVE,
255                 /* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
256                 /* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE,
257                 /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
258         )
259
260         // Always understood.
261         /* "ro"      */ MS_RDONLY,  // vfs flag
262         /* "rw"      */ ~MS_RDONLY, // vfs flag
263         /* "remount" */ MS_REMOUNT  // action flag
264 };
265
266 static const char mount_option_str[] =
267         IF_FEATURE_MOUNT_LOOP(
268                 "loop\0"
269         )
270         IF_FEATURE_MOUNT_FSTAB(
271                 "defaults\0"
272                 // "quiet\0" - do not filter out, vfat wants to see it
273                 "noauto\0"
274                 "sw\0"
275                 "swap\0"
276                 IF_DESKTOP("user\0")
277                 IF_DESKTOP("users\0")
278                 "_netdev\0"
279                 IF_DESKTOP("comment\0") /* systemd uses this in fstab */
280         )
281         IF_FEATURE_MOUNT_FLAGS(
282                 // vfs flags
283                 "nosuid\0"
284                 "suid\0"
285                 "dev\0"
286                 "nodev\0"
287                 "exec\0"
288                 "noexec\0"
289                 "sync\0"
290                 "dirsync\0"
291                 "async\0"
292                 "atime\0"
293                 "noatime\0"
294                 "diratime\0"
295                 "nodiratime\0"
296                 "mand\0"
297                 "nomand\0"
298                 "relatime\0"
299                 "norelatime\0"
300                 "loud\0"
301                 "rbind\0"
302
303                 // action flags
304                 "union\0"
305                 "bind\0"
306                 "move\0"
307                 "make-shared\0"
308                 "make-slave\0"
309                 "make-private\0"
310                 "make-unbindable\0"
311                 "make-rshared\0"
312                 "make-rslave\0"
313                 "make-rprivate\0"
314                 "make-runbindable\0"
315         )
316
317         // Always understood.
318         "ro\0"        // vfs flag
319         "rw\0"        // vfs flag
320         "remount\0"   // action flag
321 ;
322
323
324 struct globals {
325 #if ENABLE_FEATURE_MOUNT_NFS
326         smalluint nfs_mount_version;
327 #endif
328 #if ENABLE_FEATURE_MOUNT_VERBOSE
329         unsigned verbose;
330 #endif
331         llist_t *fslist;
332         char getmntent_buf[1];
333 } FIX_ALIASING;
334 enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
335 #define G (*(struct globals*)&bb_common_bufsiz1)
336 #define nfs_mount_version (G.nfs_mount_version)
337 #if ENABLE_FEATURE_MOUNT_VERBOSE
338 #define verbose           (G.verbose          )
339 #else
340 #define verbose           0
341 #endif
342 #define fslist            (G.fslist           )
343 #define getmntent_buf     (G.getmntent_buf    )
344 #define INIT_G() do { } while (0)
345
346 #if ENABLE_FEATURE_MTAB_SUPPORT
347 /*
348  * update_mtab_entry_on_move() is used to update entry in case of mount --move.
349  * we are looking for existing entries mnt_dir which is equal to mnt_fsname of
350  * input mntent and replace it by new one.
351  */
352 static void FAST_FUNC update_mtab_entry_on_move(const struct mntent *mp)
353 {
354         struct mntent *entries, *m;
355         int i, count;
356         FILE *mountTable;
357
358         mountTable = setmntent(bb_path_mtab_file, "r");
359         if (!mountTable) {
360                 bb_perror_msg(bb_path_mtab_file);
361                 return;
362         }
363
364         entries = NULL;
365         count = 0;
366         while ((m = getmntent(mountTable)) != NULL) {
367                 entries = xrealloc_vector(entries, 3, count);
368                 entries[count].mnt_fsname = xstrdup(m->mnt_fsname);
369                 entries[count].mnt_dir = xstrdup(m->mnt_dir);
370                 entries[count].mnt_type = xstrdup(m->mnt_type);
371                 entries[count].mnt_opts = xstrdup(m->mnt_opts);
372                 entries[count].mnt_freq = m->mnt_freq;
373                 entries[count].mnt_passno = m->mnt_passno;
374                 count++;
375         }
376         endmntent(mountTable);
377
378         mountTable = setmntent(bb_path_mtab_file, "w");
379         if (mountTable) {
380                 for (i = 0; i < count; i++) {
381                         if (strcmp(entries[i].mnt_dir, mp->mnt_fsname) != 0)
382                                 addmntent(mountTable, &entries[i]);
383                         else
384                                 addmntent(mountTable, mp);
385                 }
386                 endmntent(mountTable);
387         } else if (errno != EROFS)
388                 bb_perror_msg(bb_path_mtab_file);
389
390         if (ENABLE_FEATURE_CLEAN_UP) {
391                 for (i = 0; i < count; i++) {
392                         free(entries[i].mnt_fsname);
393                         free(entries[i].mnt_dir);
394                         free(entries[i].mnt_type);
395                         free(entries[i].mnt_opts);
396                 }
397                 free(entries);
398         }
399 }
400 #endif
401
402 #if ENABLE_FEATURE_MOUNT_VERBOSE
403 static int verbose_mount(const char *source, const char *target,
404                 const char *filesystemtype,
405                 unsigned long mountflags, const void *data)
406 {
407         int rc;
408
409         errno = 0;
410         rc = mount(source, target, filesystemtype, mountflags, data);
411         if (verbose >= 2)
412                 bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
413                         source, target, filesystemtype,
414                         mountflags, (char*)data, rc);
415         return rc;
416 }
417 #else
418 #define verbose_mount(...) mount(__VA_ARGS__)
419 #endif
420
421 // Append mount options to string
422 static void append_mount_options(char **oldopts, const char *newopts)
423 {
424         if (*oldopts && **oldopts) {
425                 // Do not insert options which are already there
426                 while (newopts[0]) {
427                         char *p;
428                         int len = strlen(newopts);
429                         p = strchr(newopts, ',');
430                         if (p) len = p - newopts;
431                         p = *oldopts;
432                         while (1) {
433                                 if (!strncmp(p, newopts, len)
434                                  && (p[len] == ',' || p[len] == '\0'))
435                                         goto skip;
436                                 p = strchr(p,',');
437                                 if (!p) break;
438                                 p++;
439                         }
440                         p = xasprintf("%s,%.*s", *oldopts, len, newopts);
441                         free(*oldopts);
442                         *oldopts = p;
443  skip:
444                         newopts += len;
445                         while (newopts[0] == ',') newopts++;
446                 }
447         } else {
448                 if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
449                 *oldopts = xstrdup(newopts);
450         }
451 }
452
453 // Use the mount_options list to parse options into flags.
454 // Also update list of unrecognized options if unrecognized != NULL
455 static unsigned long parse_mount_options(char *options, char **unrecognized)
456 {
457         unsigned long flags = MS_SILENT;
458
459         // Loop through options
460         for (;;) {
461                 unsigned i;
462                 char *comma = strchr(options, ',');
463                 const char *option_str = mount_option_str;
464
465                 if (comma) *comma = '\0';
466
467 // FIXME: use hasmntopt()
468                 // Find this option in mount_options
469                 for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
470                         /* We support "option=" match for "comment=" thingy */
471                         unsigned opt_len = strlen(option_str);
472                         if (strncasecmp(option_str, options, opt_len) == 0
473                          && (options[opt_len] == '\0' || options[opt_len] == '=')
474                         ) {
475                                 unsigned long fl = mount_options[i];
476                                 if ((long)fl < 0)
477                                         flags &= fl;
478                                 else
479                                         flags |= fl;
480                                 goto found;
481                         }
482                         option_str += opt_len + 1;
483                 }
484                 // We did not recognize this option.
485                 // If "unrecognized" is not NULL, append option there.
486                 // Note that we should not append *empty* option -
487                 // in this case we want to pass NULL, not "", to "data"
488                 // parameter of mount(2) syscall.
489                 // This is crucial for filesystems that don't accept
490                 // any arbitrary mount options, like cgroup fs:
491                 // "mount -t cgroup none /mnt"
492                 if (options[0] && unrecognized) {
493                         // Add it to strflags, to pass on to kernel
494                         char *p = *unrecognized;
495                         unsigned len = p ? strlen(p) : 0;
496                         *unrecognized = p = xrealloc(p, len + strlen(options) + 2);
497
498                         // Comma separated if it's not the first one
499                         if (len) p[len++] = ',';
500                         strcpy(p + len, options);
501                 }
502  found:
503                 if (!comma)
504                         break;
505                 // Advance to next option
506                 *comma = ',';
507                 options = ++comma;
508         }
509
510         return flags;
511 }
512
513 // Return a list of all block device backed filesystems
514 static llist_t *get_block_backed_filesystems(void)
515 {
516         static const char filesystems[2][sizeof("/proc/filesystems")] = {
517                 "/etc/filesystems",
518                 "/proc/filesystems",
519         };
520         char *fs, *buf;
521         llist_t *list = NULL;
522         int i;
523         FILE *f;
524
525         for (i = 0; i < 2; i++) {
526                 f = fopen_for_read(filesystems[i]);
527                 if (!f) continue;
528
529                 while ((buf = xmalloc_fgetline(f)) != NULL) {
530                         if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
531                                 goto next;
532                         fs = skip_whitespace(buf);
533                         if (*fs == '#' || *fs == '*' || !*fs)
534                                 goto next;
535
536                         llist_add_to_end(&list, xstrdup(fs));
537  next:
538                         free(buf);
539                 }
540                 if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
541         }
542
543         return list;
544 }
545
546 #if ENABLE_FEATURE_CLEAN_UP
547 static void delete_block_backed_filesystems(void)
548 {
549         llist_free(fslist, free);
550 }
551 #else
552 void delete_block_backed_filesystems(void);
553 #endif
554
555 // Perform actual mount of specific filesystem at specific location.
556 // NB: mp->xxx fields may be trashed on exit
557 static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filteropts)
558 {
559         int rc = 0;
560
561         if (FAKE_IT) {
562                 if (verbose >= 2)
563                         bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
564                                 mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
565                                 vfsflags, filteropts);
566                 goto mtab;
567         }
568
569         // Mount, with fallback to read-only if necessary.
570         for (;;) {
571                 errno = 0;
572                 rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
573                                 vfsflags, filteropts);
574
575                 // If mount failed, try
576                 // helper program mount.<mnt_type>
577                 if (HELPERS_ALLOWED && rc && mp->mnt_type) {
578                         char *args[8];
579                         int errno_save = errno;
580                         args[0] = xasprintf("mount.%s", mp->mnt_type);
581                         rc = 1;
582                         if (FAKE_IT)
583                                 args[rc++] = (char *)"-f";
584                         if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
585                                 args[rc++] = (char *)"-n";
586                         args[rc++] = mp->mnt_fsname;
587                         args[rc++] = mp->mnt_dir;
588                         if (filteropts) {
589                                 args[rc++] = (char *)"-o";
590                                 args[rc++] = filteropts;
591                         }
592                         args[rc] = NULL;
593                         rc = spawn_and_wait(args);
594                         free(args[0]);
595                         if (!rc)
596                                 break;
597                         errno = errno_save;
598                 }
599
600                 if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
601                         break;
602                 if (!(vfsflags & MS_SILENT))
603                         bb_error_msg("%s is write-protected, mounting read-only",
604                                                 mp->mnt_fsname);
605                 vfsflags |= MS_RDONLY;
606         }
607
608         // Abort entirely if permission denied.
609
610         if (rc && errno == EPERM)
611                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
612
613         // If the mount was successful, and we're maintaining an old-style
614         // mtab file by hand, add the new entry to it now.
615  mtab:
616         if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
617                 char *fsname;
618                 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
619                 const char *option_str = mount_option_str;
620                 int i;
621
622                 if (!mountTable) {
623                         bb_perror_msg(bb_path_mtab_file);
624                         goto ret;
625                 }
626
627                 // Add vfs string flags
628                 for (i = 0; mount_options[i] != MS_REMOUNT; i++) {
629                         if (mount_options[i] > 0 && (mount_options[i] & vfsflags))
630                                 append_mount_options(&(mp->mnt_opts), option_str);
631                         option_str += strlen(option_str) + 1;
632                 }
633
634                 // Remove trailing / (if any) from directory we mounted on
635                 i = strlen(mp->mnt_dir) - 1;
636                 while (i > 0 && mp->mnt_dir[i] == '/')
637                         mp->mnt_dir[i--] = '\0';
638
639                 // Convert to canonical pathnames as needed
640                 mp->mnt_dir = bb_simplify_path(mp->mnt_dir);
641                 fsname = NULL;
642                 if (!mp->mnt_type || !*mp->mnt_type) { // bind mount
643                         mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname);
644                         mp->mnt_type = (char*)"bind";
645                 }
646                 mp->mnt_freq = mp->mnt_passno = 0;
647
648                 // Write and close
649 #if ENABLE_FEATURE_MTAB_SUPPORT
650                 if (vfsflags & MS_MOVE)
651                         update_mtab_entry_on_move(mp);
652                 else
653 #endif
654                         addmntent(mountTable, mp);
655                 endmntent(mountTable);
656
657                 if (ENABLE_FEATURE_CLEAN_UP) {
658                         free(mp->mnt_dir);
659                         free(fsname);
660                 }
661         }
662  ret:
663         return rc;
664 }
665
666 #if ENABLE_FEATURE_MOUNT_NFS
667
668 /*
669  * Linux NFS mount
670  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
671  *
672  * Licensed under GPLv2, see file LICENSE in this source tree.
673  *
674  * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
675  * numbers to be specified on the command line.
676  *
677  * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
678  * Omit the call to connect() for Linux version 1.3.11 or later.
679  *
680  * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
681  * Implemented the "bg", "fg" and "retry" mount options for NFS.
682  *
683  * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
684  * - added Native Language Support
685  *
686  * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
687  * plus NFSv3 stuff.
688  */
689
690 #define MOUNTPORT 635
691 #define MNTPATHLEN 1024
692 #define MNTNAMLEN 255
693 #define FHSIZE 32
694 #define FHSIZE3 64
695
696 typedef char fhandle[FHSIZE];
697
698 typedef struct {
699         unsigned int fhandle3_len;
700         char *fhandle3_val;
701 } fhandle3;
702
703 enum mountstat3 {
704         MNT_OK = 0,
705         MNT3ERR_PERM = 1,
706         MNT3ERR_NOENT = 2,
707         MNT3ERR_IO = 5,
708         MNT3ERR_ACCES = 13,
709         MNT3ERR_NOTDIR = 20,
710         MNT3ERR_INVAL = 22,
711         MNT3ERR_NAMETOOLONG = 63,
712         MNT3ERR_NOTSUPP = 10004,
713         MNT3ERR_SERVERFAULT = 10006,
714 };
715 typedef enum mountstat3 mountstat3;
716
717 struct fhstatus {
718         unsigned int fhs_status;
719         union {
720                 fhandle fhs_fhandle;
721         } fhstatus_u;
722 };
723 typedef struct fhstatus fhstatus;
724
725 struct mountres3_ok {
726         fhandle3 fhandle;
727         struct {
728                 unsigned int auth_flavours_len;
729                 char *auth_flavours_val;
730         } auth_flavours;
731 };
732 typedef struct mountres3_ok mountres3_ok;
733
734 struct mountres3 {
735         mountstat3 fhs_status;
736         union {
737                 mountres3_ok mountinfo;
738         } mountres3_u;
739 };
740 typedef struct mountres3 mountres3;
741
742 typedef char *dirpath;
743
744 typedef char *name;
745
746 typedef struct mountbody *mountlist;
747
748 struct mountbody {
749         name ml_hostname;
750         dirpath ml_directory;
751         mountlist ml_next;
752 };
753 typedef struct mountbody mountbody;
754
755 typedef struct groupnode *groups;
756
757 struct groupnode {
758         name gr_name;
759         groups gr_next;
760 };
761 typedef struct groupnode groupnode;
762
763 typedef struct exportnode *exports;
764
765 struct exportnode {
766         dirpath ex_dir;
767         groups ex_groups;
768         exports ex_next;
769 };
770 typedef struct exportnode exportnode;
771
772 struct ppathcnf {
773         int pc_link_max;
774         short pc_max_canon;
775         short pc_max_input;
776         short pc_name_max;
777         short pc_path_max;
778         short pc_pipe_buf;
779         uint8_t pc_vdisable;
780         char pc_xxx;
781         short pc_mask[2];
782 };
783 typedef struct ppathcnf ppathcnf;
784
785 #define MOUNTPROG 100005
786 #define MOUNTVERS 1
787
788 #define MOUNTPROC_NULL 0
789 #define MOUNTPROC_MNT 1
790 #define MOUNTPROC_DUMP 2
791 #define MOUNTPROC_UMNT 3
792 #define MOUNTPROC_UMNTALL 4
793 #define MOUNTPROC_EXPORT 5
794 #define MOUNTPROC_EXPORTALL 6
795
796 #define MOUNTVERS_POSIX 2
797
798 #define MOUNTPROC_PATHCONF 7
799
800 #define MOUNT_V3 3
801
802 #define MOUNTPROC3_NULL 0
803 #define MOUNTPROC3_MNT 1
804 #define MOUNTPROC3_DUMP 2
805 #define MOUNTPROC3_UMNT 3
806 #define MOUNTPROC3_UMNTALL 4
807 #define MOUNTPROC3_EXPORT 5
808
809 enum {
810 #ifndef NFS_FHSIZE
811         NFS_FHSIZE = 32,
812 #endif
813 #ifndef NFS_PORT
814         NFS_PORT = 2049
815 #endif
816 };
817
818 /*
819  * We want to be able to compile mount on old kernels in such a way
820  * that the binary will work well on more recent kernels.
821  * Thus, if necessary we teach nfsmount.c the structure of new fields
822  * that will come later.
823  *
824  * Moreover, the new kernel includes conflict with glibc includes
825  * so it is easiest to ignore the kernel altogether (at compile time).
826  */
827
828 struct nfs2_fh {
829         char            data[32];
830 };
831 struct nfs3_fh {
832         unsigned short  size;
833         unsigned char   data[64];
834 };
835
836 struct nfs_mount_data {
837         int             version;        /* 1 */
838         int             fd;             /* 1 */
839         struct nfs2_fh  old_root;       /* 1 */
840         int             flags;          /* 1 */
841         int             rsize;          /* 1 */
842         int             wsize;          /* 1 */
843         int             timeo;          /* 1 */
844         int             retrans;        /* 1 */
845         int             acregmin;       /* 1 */
846         int             acregmax;       /* 1 */
847         int             acdirmin;       /* 1 */
848         int             acdirmax;       /* 1 */
849         struct sockaddr_in addr;        /* 1 */
850         char            hostname[256];  /* 1 */
851         int             namlen;         /* 2 */
852         unsigned int    bsize;          /* 3 */
853         struct nfs3_fh  root;           /* 4 */
854 };
855
856 /* bits in the flags field */
857 enum {
858         NFS_MOUNT_SOFT = 0x0001,        /* 1 */
859         NFS_MOUNT_INTR = 0x0002,        /* 1 */
860         NFS_MOUNT_SECURE = 0x0004,      /* 1 */
861         NFS_MOUNT_POSIX = 0x0008,       /* 1 */
862         NFS_MOUNT_NOCTO = 0x0010,       /* 1 */
863         NFS_MOUNT_NOAC = 0x0020,        /* 1 */
864         NFS_MOUNT_TCP = 0x0040,         /* 2 */
865         NFS_MOUNT_VER3 = 0x0080,        /* 3 */
866         NFS_MOUNT_KERBEROS = 0x0100,    /* 3 */
867         NFS_MOUNT_NONLM = 0x0200,       /* 3 */
868         NFS_MOUNT_NOACL = 0x0800,       /* 4 */
869         NFS_MOUNT_NORDIRPLUS = 0x4000
870 };
871
872
873 /*
874  * We need to translate between nfs status return values and
875  * the local errno values which may not be the same.
876  *
877  * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
878  * "after #include <errno.h> the symbol errno is reserved for any use,
879  *  it cannot even be used as a struct tag or field name".
880  */
881 #ifndef EDQUOT
882 # define EDQUOT ENOSPC
883 #endif
884 /* Convert each NFSERR_BLAH into EBLAH */
885 static const uint8_t nfs_err_stat[] = {
886          1,  2,  5,  6, 13, 17,
887         19, 20, 21, 22, 27, 28,
888         30, 63, 66, 69, 70, 71
889 };
890 #if ( \
891         EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \
892         ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \
893         EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
894 typedef uint8_t nfs_err_type;
895 #else
896 typedef uint16_t nfs_err_type;
897 #endif
898 static const nfs_err_type nfs_err_errnum[] = {
899         EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
900         ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
901         EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
902 };
903 static char *nfs_strerror(int status)
904 {
905         int i;
906
907         for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
908                 if (nfs_err_stat[i] == status)
909                         return strerror(nfs_err_errnum[i]);
910         }
911         return xasprintf("unknown nfs status return value: %d", status);
912 }
913
914 static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
915 {
916         return xdr_opaque(xdrs, objp, FHSIZE);
917 }
918
919 static bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp)
920 {
921         if (!xdr_u_int(xdrs, &objp->fhs_status))
922                  return FALSE;
923         if (objp->fhs_status == 0)
924                 return xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle);
925         return TRUE;
926 }
927
928 static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp)
929 {
930         return xdr_string(xdrs, objp, MNTPATHLEN);
931 }
932
933 static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
934 {
935         return xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
936                            (unsigned int *) &objp->fhandle3_len,
937                            FHSIZE3);
938 }
939
940 static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
941 {
942         if (!xdr_fhandle3(xdrs, &objp->fhandle))
943                 return FALSE;
944         return xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val),
945                            &(objp->auth_flavours.auth_flavours_len),
946                            ~0,
947                            sizeof(int),
948                            (xdrproc_t) xdr_int);
949 }
950
951 static bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
952 {
953         return xdr_enum(xdrs, (enum_t *) objp);
954 }
955
956 static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp)
957 {
958         if (!xdr_mountstat3(xdrs, &objp->fhs_status))
959                 return FALSE;
960         if (objp->fhs_status == MNT_OK)
961                 return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
962         return TRUE;
963 }
964
965 #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
966
967 /*
968  * Unfortunately, the kernel prints annoying console messages
969  * in case of an unexpected nfs mount version (instead of
970  * just returning some error).  Therefore we'll have to try
971  * and figure out what version the kernel expects.
972  *
973  * Variables:
974  *      KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
975  *      NFS_MOUNT_VERSION: these nfsmount sources at compile time
976  *      nfs_mount_version: version this source and running kernel can handle
977  */
978 static void
979 find_kernel_nfs_mount_version(void)
980 {
981         int kernel_version;
982
983         if (nfs_mount_version)
984                 return;
985
986         nfs_mount_version = 4; /* default */
987
988         kernel_version = get_linux_version_code();
989         if (kernel_version) {
990                 if (kernel_version < KERNEL_VERSION(2,2,18))
991                         nfs_mount_version = 3;
992                 /* else v4 since 2.3.99pre4 */
993         }
994 }
995
996 static void
997 get_mountport(struct pmap *pm_mnt,
998         struct sockaddr_in *server_addr,
999         long unsigned prog,
1000         long unsigned version,
1001         long unsigned proto,
1002         long unsigned port)
1003 {
1004         struct pmaplist *pmap;
1005
1006         server_addr->sin_port = PMAPPORT;
1007 /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
1008  * I understand it like "IPv6 for this is not 100% ready" */
1009         pmap = pmap_getmaps(server_addr);
1010
1011         if (version > MAX_NFSPROT)
1012                 version = MAX_NFSPROT;
1013         if (!prog)
1014                 prog = MOUNTPROG;
1015         pm_mnt->pm_prog = prog;
1016         pm_mnt->pm_vers = version;
1017         pm_mnt->pm_prot = proto;
1018         pm_mnt->pm_port = port;
1019
1020         while (pmap) {
1021                 if (pmap->pml_map.pm_prog != prog)
1022                         goto next;
1023                 if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
1024                         goto next;
1025                 if (version > 2 && pmap->pml_map.pm_vers != version)
1026                         goto next;
1027                 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
1028                         goto next;
1029                 if (pmap->pml_map.pm_vers > MAX_NFSPROT
1030                  || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
1031                  || (port && pmap->pml_map.pm_port != port)
1032                 ) {
1033                         goto next;
1034                 }
1035                 memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
1036  next:
1037                 pmap = pmap->pml_next;
1038         }
1039         if (!pm_mnt->pm_vers)
1040                 pm_mnt->pm_vers = MOUNTVERS;
1041         if (!pm_mnt->pm_port)
1042                 pm_mnt->pm_port = MOUNTPORT;
1043         if (!pm_mnt->pm_prot)
1044                 pm_mnt->pm_prot = IPPROTO_TCP;
1045 }
1046
1047 #if BB_MMU
1048 static int daemonize(void)
1049 {
1050         int pid = fork();
1051         if (pid < 0) /* error */
1052                 return -errno;
1053         if (pid > 0) /* parent */
1054                 return 0;
1055         /* child */
1056         close(0);
1057         xopen(bb_dev_null, O_RDWR);
1058         xdup2(0, 1);
1059         xdup2(0, 2);
1060         setsid();
1061         openlog(applet_name, LOG_PID, LOG_DAEMON);
1062         logmode = LOGMODE_SYSLOG;
1063         return 1;
1064 }
1065 #else
1066 static inline int daemonize(void) { return -ENOSYS; }
1067 #endif
1068
1069 /* TODO */
1070 static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1071 {
1072         return 0;
1073 }
1074
1075 /* RPC strerror analogs are terminally idiotic:
1076  * *mandatory* prefix and \n at end.
1077  * This hopefully helps. Usage:
1078  * error_msg_rpc(clnt_*error*(" ")) */
1079 static void error_msg_rpc(const char *msg)
1080 {
1081         int len;
1082         while (msg[0] == ' ' || msg[0] == ':') msg++;
1083         len = strlen(msg);
1084         while (len && msg[len-1] == '\n') len--;
1085         bb_error_msg("%.*s", len, msg);
1086 }
1087
1088 /* NB: mp->xxx fields may be trashed on exit */
1089 static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1090 {
1091         CLIENT *mclient;
1092         char *hostname;
1093         char *pathname;
1094         char *mounthost;
1095         /* prior to 2.6.23, kernel took NFS options in a form of this struct
1096          * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1097          * then data pointer is interpreted as a string. */
1098         struct nfs_mount_data data;
1099         char *opt;
1100         struct hostent *hp;
1101         struct sockaddr_in server_addr;
1102         struct sockaddr_in mount_server_addr;
1103         int msock, fsock;
1104         union {
1105                 struct fhstatus nfsv2;
1106                 struct mountres3 nfsv3;
1107         } status;
1108         int daemonized;
1109         char *s;
1110         int port;
1111         int mountport;
1112         int proto;
1113 #if BB_MMU
1114         smallint bg = 0;
1115 #else
1116         enum { bg = 0 };
1117 #endif
1118         int retry;
1119         int mountprog;
1120         int mountvers;
1121         int nfsprog;
1122         int nfsvers;
1123         int retval;
1124         /* these all are one-bit really. gcc 4.3.1 likes this combination: */
1125         smallint tcp;
1126         smallint soft;
1127         int intr;
1128         int posix;
1129         int nocto;
1130         int noac;
1131         int nordirplus;
1132         int nolock;
1133         int noacl;
1134
1135         find_kernel_nfs_mount_version();
1136
1137         daemonized = 0;
1138         mounthost = NULL;
1139         retval = ETIMEDOUT;
1140         msock = fsock = -1;
1141         mclient = NULL;
1142
1143         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1144
1145         filteropts = xstrdup(filteropts); /* going to trash it later... */
1146
1147         hostname = xstrdup(mp->mnt_fsname);
1148         /* mount_main() guarantees that ':' is there */
1149         s = strchr(hostname, ':');
1150         pathname = s + 1;
1151         *s = '\0';
1152         /* Ignore all but first hostname in replicated mounts
1153          * until they can be fully supported. (mack@sgi.com) */
1154         s = strchr(hostname, ',');
1155         if (s) {
1156                 *s = '\0';
1157                 bb_error_msg("warning: multiple hostnames not supported");
1158         }
1159
1160         server_addr.sin_family = AF_INET;
1161         if (!inet_aton(hostname, &server_addr.sin_addr)) {
1162                 hp = gethostbyname(hostname);
1163                 if (hp == NULL) {
1164                         bb_herror_msg("%s", hostname);
1165                         goto fail;
1166                 }
1167                 if (hp->h_length != (int)sizeof(struct in_addr)) {
1168                         bb_error_msg_and_die("only IPv4 is supported");
1169                 }
1170                 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1171         }
1172
1173         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1174
1175         /* add IP address to mtab options for use when unmounting */
1176
1177         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1178                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1179         } else {
1180                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1181                                         mp->mnt_opts[0] ? "," : "",
1182                                         inet_ntoa(server_addr.sin_addr));
1183                 free(mp->mnt_opts);
1184                 mp->mnt_opts = tmp;
1185         }
1186
1187         /* Set default options.
1188          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1189          * let the kernel decide.
1190          * timeo is filled in after we know whether it'll be TCP or UDP. */
1191         memset(&data, 0, sizeof(data));
1192         data.retrans  = 3;
1193         data.acregmin = 3;
1194         data.acregmax = 60;
1195         data.acdirmin = 30;
1196         data.acdirmax = 60;
1197         data.namlen   = NAME_MAX;
1198
1199         soft = 0;
1200         intr = 0;
1201         posix = 0;
1202         nocto = 0;
1203         nolock = 0;
1204         noac = 0;
1205         nordirplus = 0;
1206         noacl = 0;
1207         retry = 10000;          /* 10000 minutes ~ 1 week */
1208         tcp = 1;                        /* nfs-utils uses tcp per default */
1209
1210         mountprog = MOUNTPROG;
1211         mountvers = 0;
1212         port = 0;
1213         mountport = 0;
1214         nfsprog = 100003;
1215         nfsvers = 0;
1216
1217         /* parse options */
1218         if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1219                 char *opteq = strchr(opt, '=');
1220                 if (opteq) {
1221                         int val, idx;
1222                         static const char options[] ALIGN1 =
1223                                 /* 0 */ "rsize\0"
1224                                 /* 1 */ "wsize\0"
1225                                 /* 2 */ "timeo\0"
1226                                 /* 3 */ "retrans\0"
1227                                 /* 4 */ "acregmin\0"
1228                                 /* 5 */ "acregmax\0"
1229                                 /* 6 */ "acdirmin\0"
1230                                 /* 7 */ "acdirmax\0"
1231                                 /* 8 */ "actimeo\0"
1232                                 /* 9 */ "retry\0"
1233                                 /* 10 */ "port\0"
1234                                 /* 11 */ "mountport\0"
1235                                 /* 12 */ "mounthost\0"
1236                                 /* 13 */ "mountprog\0"
1237                                 /* 14 */ "mountvers\0"
1238                                 /* 15 */ "nfsprog\0"
1239                                 /* 16 */ "nfsvers\0"
1240                                 /* 17 */ "vers\0"
1241                                 /* 18 */ "proto\0"
1242                                 /* 19 */ "namlen\0"
1243                                 /* 20 */ "addr\0";
1244
1245                         *opteq++ = '\0';
1246                         idx = index_in_strings(options, opt);
1247                         switch (idx) {
1248                         case 12: // "mounthost"
1249                                 mounthost = xstrndup(opteq,
1250                                                 strcspn(opteq, " \t\n\r,"));
1251                                 continue;
1252                         case 18: // "proto"
1253                                 if (!strncmp(opteq, "tcp", 3))
1254                                         tcp = 1;
1255                                 else if (!strncmp(opteq, "udp", 3))
1256                                         tcp = 0;
1257                                 else
1258                                         bb_error_msg("warning: unrecognized proto= option");
1259                                 continue;
1260                         case 20: // "addr" - ignore
1261                                 continue;
1262                         case -1: // unknown
1263                                 if (vfsflags & MS_REMOUNT)
1264                                         continue;
1265                         }
1266
1267                         val = xatoi_positive(opteq);
1268                         switch (idx) {
1269                         case 0: // "rsize"
1270                                 data.rsize = val;
1271                                 continue;
1272                         case 1: // "wsize"
1273                                 data.wsize = val;
1274                                 continue;
1275                         case 2: // "timeo"
1276                                 data.timeo = val;
1277                                 continue;
1278                         case 3: // "retrans"
1279                                 data.retrans = val;
1280                                 continue;
1281                         case 4: // "acregmin"
1282                                 data.acregmin = val;
1283                                 continue;
1284                         case 5: // "acregmax"
1285                                 data.acregmax = val;
1286                                 continue;
1287                         case 6: // "acdirmin"
1288                                 data.acdirmin = val;
1289                                 continue;
1290                         case 7: // "acdirmax"
1291                                 data.acdirmax = val;
1292                                 continue;
1293                         case 8: // "actimeo"
1294                                 data.acregmin = val;
1295                                 data.acregmax = val;
1296                                 data.acdirmin = val;
1297                                 data.acdirmax = val;
1298                                 continue;
1299                         case 9: // "retry"
1300                                 retry = val;
1301                                 continue;
1302                         case 10: // "port"
1303                                 port = val;
1304                                 continue;
1305                         case 11: // "mountport"
1306                                 mountport = val;
1307                                 continue;
1308                         case 13: // "mountprog"
1309                                 mountprog = val;
1310                                 continue;
1311                         case 14: // "mountvers"
1312                                 mountvers = val;
1313                                 continue;
1314                         case 15: // "nfsprog"
1315                                 nfsprog = val;
1316                                 continue;
1317                         case 16: // "nfsvers"
1318                         case 17: // "vers"
1319                                 nfsvers = val;
1320                                 continue;
1321                         case 19: // "namlen"
1322                                 //if (nfs_mount_version >= 2)
1323                                         data.namlen = val;
1324                                 //else
1325                                 //      bb_error_msg("warning: option namlen is not supported\n");
1326                                 continue;
1327                         default:
1328                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1329                                 goto fail;
1330                         }
1331                 }
1332                 else { /* not of the form opt=val */
1333                         static const char options[] ALIGN1 =
1334                                 "bg\0"
1335                                 "fg\0"
1336                                 "soft\0"
1337                                 "hard\0"
1338                                 "intr\0"
1339                                 "posix\0"
1340                                 "cto\0"
1341                                 "ac\0"
1342                                 "tcp\0"
1343                                 "udp\0"
1344                                 "lock\0"
1345                                 "rdirplus\0"
1346                                 "acl\0";
1347                         int val = 1;
1348                         if (!strncmp(opt, "no", 2)) {
1349                                 val = 0;
1350                                 opt += 2;
1351                         }
1352                         switch (index_in_strings(options, opt)) {
1353                         case 0: // "bg"
1354 #if BB_MMU
1355                                 bg = val;
1356 #endif
1357                                 break;
1358                         case 1: // "fg"
1359 #if BB_MMU
1360                                 bg = !val;
1361 #endif
1362                                 break;
1363                         case 2: // "soft"
1364                                 soft = val;
1365                                 break;
1366                         case 3: // "hard"
1367                                 soft = !val;
1368                                 break;
1369                         case 4: // "intr"
1370                                 intr = val;
1371                                 break;
1372                         case 5: // "posix"
1373                                 posix = val;
1374                                 break;
1375                         case 6: // "cto"
1376                                 nocto = !val;
1377                                 break;
1378                         case 7: // "ac"
1379                                 noac = !val;
1380                                 break;
1381                         case 8: // "tcp"
1382                                 tcp = val;
1383                                 break;
1384                         case 9: // "udp"
1385                                 tcp = !val;
1386                                 break;
1387                         case 10: // "lock"
1388                                 if (nfs_mount_version >= 3)
1389                                         nolock = !val;
1390                                 else
1391                                         bb_error_msg("warning: option nolock is not supported");
1392                                 break;
1393                         case 11: //rdirplus
1394                                 nordirplus = !val;
1395                                 break;
1396                         case 12: // acl
1397                                 noacl = !val;
1398                                 break;
1399                         default:
1400                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1401                                 goto fail;
1402                         }
1403                 }
1404         }
1405         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1406
1407         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1408                 | (intr ? NFS_MOUNT_INTR : 0)
1409                 | (posix ? NFS_MOUNT_POSIX : 0)
1410                 | (nocto ? NFS_MOUNT_NOCTO : 0)
1411                 | (noac ? NFS_MOUNT_NOAC : 0)
1412                 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1413                 | (noacl ? NFS_MOUNT_NOACL : 0);
1414         if (nfs_mount_version >= 2)
1415                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1416         if (nfs_mount_version >= 3)
1417                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1418         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1419                 bb_error_msg("NFSv%d not supported", nfsvers);
1420                 goto fail;
1421         }
1422         if (nfsvers && !mountvers)
1423                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1424         if (nfsvers && nfsvers < mountvers) {
1425                 mountvers = nfsvers;
1426         }
1427
1428         /* Adjust options if none specified */
1429         if (!data.timeo)
1430                 data.timeo = tcp ? 70 : 7;
1431
1432         data.version = nfs_mount_version;
1433
1434         if (vfsflags & MS_REMOUNT)
1435                 goto do_mount;
1436
1437         /*
1438          * If the previous mount operation on the same host was
1439          * backgrounded, and the "bg" for this mount is also set,
1440          * give up immediately, to avoid the initial timeout.
1441          */
1442         if (bg && we_saw_this_host_before(hostname)) {
1443                 daemonized = daemonize();
1444                 if (daemonized <= 0) { /* parent or error */
1445                         retval = -daemonized;
1446                         goto ret;
1447                 }
1448         }
1449
1450         /* Create mount daemon client */
1451         /* See if the nfs host = mount host. */
1452         if (mounthost) {
1453                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1454                         mount_server_addr.sin_family = AF_INET;
1455                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1456                 } else {
1457                         hp = gethostbyname(mounthost);
1458                         if (hp == NULL) {
1459                                 bb_herror_msg("%s", mounthost);
1460                                 goto fail;
1461                         }
1462                         if (hp->h_length != (int)sizeof(struct in_addr)) {
1463                                 bb_error_msg_and_die("only IPv4 is supported");
1464                         }
1465                         mount_server_addr.sin_family = AF_INET;
1466                         memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1467                 }
1468         }
1469
1470         /*
1471          * The following loop implements the mount retries. When the mount
1472          * times out, and the "bg" option is set, we background ourself
1473          * and continue trying.
1474          *
1475          * The case where the mount point is not present and the "bg"
1476          * option is set, is treated as a timeout. This is done to
1477          * support nested mounts.
1478          *
1479          * The "retry" count specified by the user is the number of
1480          * minutes to retry before giving up.
1481          */
1482         {
1483                 struct timeval total_timeout;
1484                 struct timeval retry_timeout;
1485                 struct pmap pm_mnt;
1486                 time_t t;
1487                 time_t prevt;
1488                 time_t timeout;
1489
1490                 retry_timeout.tv_sec = 3;
1491                 retry_timeout.tv_usec = 0;
1492                 total_timeout.tv_sec = 20;
1493                 total_timeout.tv_usec = 0;
1494 /* FIXME: use monotonic()? */
1495                 timeout = time(NULL) + 60 * retry;
1496                 prevt = 0;
1497                 t = 30;
1498  retry:
1499                 /* Be careful not to use too many CPU cycles */
1500                 if (t - prevt < 30)
1501                         sleep(30);
1502
1503                 get_mountport(&pm_mnt, &mount_server_addr,
1504                                 mountprog,
1505                                 mountvers,
1506                                 proto,
1507                                 mountport);
1508                 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1509
1510                 /* contact the mount daemon via TCP */
1511                 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1512                 msock = RPC_ANYSOCK;
1513
1514                 switch (pm_mnt.pm_prot) {
1515                 case IPPROTO_UDP:
1516                         mclient = clntudp_create(&mount_server_addr,
1517                                                  pm_mnt.pm_prog,
1518                                                  pm_mnt.pm_vers,
1519                                                  retry_timeout,
1520                                                  &msock);
1521                         if (mclient)
1522                                 break;
1523                         mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1524                         msock = RPC_ANYSOCK;
1525                 case IPPROTO_TCP:
1526                         mclient = clnttcp_create(&mount_server_addr,
1527                                                  pm_mnt.pm_prog,
1528                                                  pm_mnt.pm_vers,
1529                                                  &msock, 0, 0);
1530                         break;
1531                 default:
1532                         mclient = NULL;
1533                 }
1534                 if (!mclient) {
1535                         if (!daemonized && prevt == 0)
1536                                 error_msg_rpc(clnt_spcreateerror(" "));
1537                 } else {
1538                         enum clnt_stat clnt_stat;
1539
1540                         /* Try to mount hostname:pathname */
1541                         mclient->cl_auth = authunix_create_default();
1542
1543                         /* Make pointers in xdr_mountres3 NULL so
1544                          * that xdr_array allocates memory for us
1545                          */
1546                         memset(&status, 0, sizeof(status));
1547
1548                         if (pm_mnt.pm_vers == 3)
1549                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1550                                               (xdrproc_t) xdr_dirpath,
1551                                               (caddr_t) &pathname,
1552                                               (xdrproc_t) xdr_mountres3,
1553                                               (caddr_t) &status,
1554                                               total_timeout);
1555                         else
1556                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1557                                               (xdrproc_t) xdr_dirpath,
1558                                               (caddr_t) &pathname,
1559                                               (xdrproc_t) xdr_fhstatus,
1560                                               (caddr_t) &status,
1561                                               total_timeout);
1562
1563                         if (clnt_stat == RPC_SUCCESS)
1564                                 goto prepare_kernel_data; /* we're done */
1565                         if (errno != ECONNREFUSED) {
1566                                 error_msg_rpc(clnt_sperror(mclient, " "));
1567                                 goto fail;      /* don't retry */
1568                         }
1569                         /* Connection refused */
1570                         if (!daemonized && prevt == 0) /* print just once */
1571                                 error_msg_rpc(clnt_sperror(mclient, " "));
1572                         auth_destroy(mclient->cl_auth);
1573                         clnt_destroy(mclient);
1574                         mclient = NULL;
1575                         close(msock);
1576                         msock = -1;
1577                 }
1578
1579                 /* Timeout. We are going to retry... maybe */
1580                 if (!bg)
1581                         goto fail;
1582                 if (!daemonized) {
1583                         daemonized = daemonize();
1584                         if (daemonized <= 0) { /* parent or error */
1585                                 retval = -daemonized;
1586                                 goto ret;
1587                         }
1588                 }
1589                 prevt = t;
1590                 t = time(NULL);
1591                 if (t >= timeout)
1592                         /* TODO error message */
1593                         goto fail;
1594
1595                 goto retry;
1596         }
1597
1598  prepare_kernel_data:
1599
1600         if (nfsvers == 2) {
1601                 if (status.nfsv2.fhs_status != 0) {
1602                         bb_error_msg("%s:%s failed, reason given by server: %s",
1603                                 hostname, pathname,
1604                                 nfs_strerror(status.nfsv2.fhs_status));
1605                         goto fail;
1606                 }
1607                 memcpy(data.root.data,
1608                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1609                                 NFS_FHSIZE);
1610                 data.root.size = NFS_FHSIZE;
1611                 memcpy(data.old_root.data,
1612                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1613                                 NFS_FHSIZE);
1614         } else {
1615                 fhandle3 *my_fhandle;
1616                 if (status.nfsv3.fhs_status != 0) {
1617                         bb_error_msg("%s:%s failed, reason given by server: %s",
1618                                 hostname, pathname,
1619                                 nfs_strerror(status.nfsv3.fhs_status));
1620                         goto fail;
1621                 }
1622                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1623                 memset(data.old_root.data, 0, NFS_FHSIZE);
1624                 memset(&data.root, 0, sizeof(data.root));
1625                 data.root.size = my_fhandle->fhandle3_len;
1626                 memcpy(data.root.data,
1627                                 (char *) my_fhandle->fhandle3_val,
1628                                 my_fhandle->fhandle3_len);
1629
1630                 data.flags |= NFS_MOUNT_VER3;
1631         }
1632
1633         /* Create nfs socket for kernel */
1634         if (tcp) {
1635                 if (nfs_mount_version < 3) {
1636                         bb_error_msg("NFS over TCP is not supported");
1637                         goto fail;
1638                 }
1639                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1640         } else
1641                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1642         if (fsock < 0) {
1643                 bb_perror_msg("nfs socket");
1644                 goto fail;
1645         }
1646         if (bindresvport(fsock, 0) < 0) {
1647                 bb_perror_msg("nfs bindresvport");
1648                 goto fail;
1649         }
1650         if (port == 0) {
1651                 server_addr.sin_port = PMAPPORT;
1652                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1653                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
1654                 if (port == 0)
1655                         port = NFS_PORT;
1656         }
1657         server_addr.sin_port = htons(port);
1658
1659         /* Prepare data structure for kernel */
1660         data.fd = fsock;
1661         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1662         strncpy(data.hostname, hostname, sizeof(data.hostname));
1663
1664         /* Clean up */
1665         auth_destroy(mclient->cl_auth);
1666         clnt_destroy(mclient);
1667         close(msock);
1668         msock = -1;
1669
1670         if (bg) {
1671                 /* We must wait until mount directory is available */
1672                 struct stat statbuf;
1673                 int delay = 1;
1674                 while (stat(mp->mnt_dir, &statbuf) == -1) {
1675                         if (!daemonized) {
1676                                 daemonized = daemonize();
1677                                 if (daemonized <= 0) { /* parent or error */
1678 /* FIXME: parent doesn't close fsock - ??! */
1679                                         retval = -daemonized;
1680                                         goto ret;
1681                                 }
1682                         }
1683                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1684                         delay *= 2;
1685                         if (delay > 30)
1686                                 delay = 30;
1687                 }
1688         }
1689
1690         /* Perform actual mount */
1691  do_mount:
1692         retval = mount_it_now(mp, vfsflags, (char*)&data);
1693         goto ret;
1694
1695         /* Abort */
1696  fail:
1697         if (msock >= 0) {
1698                 if (mclient) {
1699                         auth_destroy(mclient->cl_auth);
1700                         clnt_destroy(mclient);
1701                 }
1702                 close(msock);
1703         }
1704         if (fsock >= 0)
1705                 close(fsock);
1706
1707  ret:
1708         free(hostname);
1709         free(mounthost);
1710         free(filteropts);
1711         return retval;
1712 }
1713
1714 #else // !ENABLE_FEATURE_MOUNT_NFS
1715
1716 /* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1717  * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1718  * (However, note that then you lose any chances that NFS over IPv6 would work).
1719  */
1720 static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1721 {
1722         len_and_sockaddr *lsa;
1723         char *opts;
1724         char *end;
1725         char *dotted;
1726         int ret;
1727
1728 # if ENABLE_FEATURE_IPV6
1729         end = strchr(mp->mnt_fsname, ']');
1730         if (end && end[1] == ':')
1731                 end++;
1732         else
1733 # endif
1734                 /* mount_main() guarantees that ':' is there */
1735                 end = strchr(mp->mnt_fsname, ':');
1736
1737         *end = '\0';
1738         lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1739         *end = ':';
1740         dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1741         if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1742         opts = xasprintf("%s%saddr=%s",
1743                 filteropts ? filteropts : "",
1744                 filteropts ? "," : "",
1745                 dotted
1746         );
1747         if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1748         ret = mount_it_now(mp, vfsflags, opts);
1749         if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1750
1751         return ret;
1752 }
1753
1754 #endif // !ENABLE_FEATURE_MOUNT_NFS
1755
1756 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1757 // type detection.  Returns 0 for success, nonzero for failure.
1758 // NB: mp->xxx fields may be trashed on exit
1759 static int singlemount(struct mntent *mp, int ignore_busy)
1760 {
1761         int rc = -1;
1762         unsigned long vfsflags;
1763         char *loopFile = NULL, *filteropts = NULL;
1764         llist_t *fl = NULL;
1765         struct stat st;
1766
1767         errno = 0;
1768
1769         vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1770
1771         // Treat fstype "auto" as unspecified
1772         if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1773                 mp->mnt_type = NULL;
1774
1775         // Might this be a virtual filesystem?
1776         if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1777                 char *args[35];
1778                 char *s;
1779                 int n;
1780                 // fsname: "cmd#arg1#arg2..."
1781                 // WARNING: allows execution of arbitrary commands!
1782                 // Try "mount 'sh#-c#sh' bogus_dir".
1783                 // It is safe ONLY because non-root
1784                 // cannot use two-argument mount command
1785                 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1786                 // "mount: can't find sh#-c#sh in /etc/fstab"
1787                 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1788
1789                 s = mp->mnt_fsname;
1790                 n = 0;
1791                 args[n++] = s;
1792                 while (*s && n < 35 - 2) {
1793                         if (*s++ == '#' && *s != '#') {
1794                                 s[-1] = '\0';
1795                                 args[n++] = s;
1796                         }
1797                 }
1798                 args[n++] = mp->mnt_dir;
1799                 args[n] = NULL;
1800                 rc = spawn_and_wait(args);
1801                 goto report_error;
1802         }
1803
1804         // Might this be an CIFS filesystem?
1805         if (ENABLE_FEATURE_MOUNT_CIFS
1806          && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1807          && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1808          && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1809         ) {
1810                 int len;
1811                 char c;
1812                 len_and_sockaddr *lsa;
1813                 char *hostname, *dotted, *ip;
1814
1815                 hostname = mp->mnt_fsname + 2;
1816                 len = strcspn(hostname, "/\\");
1817                 if (len == 0 || hostname[len] == '\0')
1818                         goto report_error;
1819                 c = hostname[len];
1820                 hostname[len] = '\0';
1821                 lsa = host2sockaddr(hostname, 0);
1822                 hostname[len] = c;
1823                 if (!lsa)
1824                         goto report_error;
1825
1826                 // Insert "ip=..." option into options
1827                 dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1828                 if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1829                 ip = xasprintf("ip=%s", dotted);
1830                 if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1831                 parse_mount_options(ip, &filteropts);
1832                 if (ENABLE_FEATURE_CLEAN_UP) free(ip);
1833
1834                 // "-o mand" is required [why?]
1835                 vfsflags |= MS_MANDLOCK;
1836                 mp->mnt_type = (char*)"cifs";
1837                 rc = mount_it_now(mp, vfsflags, filteropts);
1838
1839                 goto report_error;
1840         }
1841
1842         // Might this be an NFS filesystem?
1843         if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
1844          && strchr(mp->mnt_fsname, ':') != NULL
1845         ) {
1846                 if (!mp->mnt_type)
1847                         mp->mnt_type = (char*)"nfs";
1848                 rc = nfsmount(mp, vfsflags, filteropts);
1849                 goto report_error;
1850         }
1851
1852         // Look at the file.  (Not found isn't a failure for remount, or for
1853         // a synthetic filesystem like proc or sysfs.)
1854         // (We use stat, not lstat, in order to allow
1855         // mount symlink_to_file_or_blkdev dir)
1856         if (!stat(mp->mnt_fsname, &st)
1857          && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
1858         ) {
1859                 // Do we need to allocate a loopback device for it?
1860                 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
1861                         loopFile = bb_simplify_path(mp->mnt_fsname);
1862                         mp->mnt_fsname = NULL; // will receive malloced loop dev name
1863                         if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
1864                                 if (errno == EPERM || errno == EACCES)
1865                                         bb_error_msg(bb_msg_perm_denied_are_you_root);
1866                                 else
1867                                         bb_perror_msg("can't setup loop device");
1868                                 return errno;
1869                         }
1870
1871                 // Autodetect bind mounts
1872                 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
1873                         vfsflags |= MS_BIND;
1874         }
1875
1876         // If we know the fstype (or don't need to), jump straight
1877         // to the actual mount.
1878         if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
1879                 char *next;
1880                 for (;;) {
1881                         next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
1882                         if (next)
1883                                 *next = '\0';
1884                         rc = mount_it_now(mp, vfsflags, filteropts);
1885                         if (rc == 0 || !next)
1886                                 break;
1887                         mp->mnt_type = next + 1;
1888                 }
1889         } else {
1890                 // Loop through filesystem types until mount succeeds
1891                 // or we run out
1892
1893                 // Initialize list of block backed filesystems.
1894                 // This has to be done here so that during "mount -a",
1895                 // mounts after /proc shows up can autodetect.
1896                 if (!fslist) {
1897                         fslist = get_block_backed_filesystems();
1898                         if (ENABLE_FEATURE_CLEAN_UP && fslist)
1899                                 atexit(delete_block_backed_filesystems);
1900                 }
1901
1902                 for (fl = fslist; fl; fl = fl->link) {
1903                         mp->mnt_type = fl->data;
1904                         rc = mount_it_now(mp, vfsflags, filteropts);
1905                         if (rc == 0)
1906                                 break;
1907                 }
1908         }
1909
1910         // If mount failed, clean up loop file (if any).
1911         if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
1912                 del_loop(mp->mnt_fsname);
1913                 if (ENABLE_FEATURE_CLEAN_UP) {
1914                         free(loopFile);
1915                         free(mp->mnt_fsname);
1916                 }
1917         }
1918
1919  report_error:
1920         if (ENABLE_FEATURE_CLEAN_UP)
1921                 free(filteropts);
1922
1923         if (errno == EBUSY && ignore_busy)
1924                 return 0;
1925         if (rc != 0)
1926                 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
1927         return rc;
1928 }
1929
1930 // -O support
1931 //    -O interprets a list of filter options which select whether a mount
1932 // point will be mounted: only mounts with options matching *all* filtering
1933 // options will be selected.
1934 //    By default each -O filter option must be present in the list of mount
1935 // options, but if it is prefixed by "no" then it must be absent.
1936 // For example,
1937 //  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
1938 //              (and also fails to match  -o a  because  -o c  is absent).
1939 //
1940 // It is different from -t in that each option is matched exactly; a leading
1941 // "no" at the beginning of one option does not negate the rest.
1942 static int match_opt(const char *fs_opt_in, const char *O_opt)
1943 {
1944         if (!O_opt)
1945                 return 1;
1946
1947         while (*O_opt) {
1948                 const char *fs_opt = fs_opt_in;
1949                 int O_len;
1950                 int match;
1951
1952                 // If option begins with "no" then treat as an inverted match:
1953                 // matching is a failure
1954                 match = 0;
1955                 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
1956                         match = 1;
1957                         O_opt += 2;
1958                 }
1959                 // Isolate the current O option
1960                 O_len = strchrnul(O_opt, ',') - O_opt;
1961                 // Check for a match against existing options
1962                 while (1) {
1963                         if (strncmp(fs_opt, O_opt, O_len) == 0
1964                          && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
1965                         ) {
1966                                 if (match)
1967                                         return 0;  // "no" prefix, but option found
1968                                 match = 1;  // current O option found, go check next one
1969                                 break;
1970                         }
1971                         fs_opt = strchr(fs_opt, ',');
1972                         if (!fs_opt)
1973                                 break;
1974                         fs_opt++;
1975                 }
1976                 if (match == 0)
1977                         return 0;     // match wanted but not found
1978                 if (O_opt[O_len] == '\0') // end?
1979                         break;
1980                 // Step to the next O option
1981                 O_opt += O_len + 1;
1982         }
1983         // If we get here then everything matched
1984         return 1;
1985 }
1986
1987 // Parse options, if necessary parse fstab/mtab, and call singlemount for
1988 // each directory to be mounted.
1989 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1990 int mount_main(int argc UNUSED_PARAM, char **argv)
1991 {
1992         char *cmdopts = xzalloc(1);
1993         char *fstype = NULL;
1994         char *O_optmatch = NULL;
1995         char *storage_path;
1996         llist_t *lst_o = NULL;
1997         const char *fstabname;
1998         FILE *fstab;
1999         int i, j;
2000         int rc = EXIT_SUCCESS;
2001         unsigned long cmdopt_flags;
2002         unsigned opt;
2003         struct mntent mtpair[2], *mtcur = mtpair;
2004         IF_NOT_DESKTOP(const int nonroot = 0;)
2005
2006         IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2007
2008         INIT_G();
2009
2010         // Parse long options, like --bind and --move.  Note that -o option
2011         // and --option are synonymous.  Yes, this means --remount,rw works.
2012         for (i = j = 1; argv[i]; i++) {
2013                 if (argv[i][0] == '-' && argv[i][1] == '-')
2014                         append_mount_options(&cmdopts, argv[i] + 2);
2015                 else
2016                         argv[j++] = argv[i];
2017         }
2018         argv[j] = NULL;
2019
2020         // Parse remaining options
2021         // Max 2 params; -o is a list, -v is a counter
2022         opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
2023         opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
2024                         IF_FEATURE_MOUNT_VERBOSE(, &verbose));
2025         while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2026         if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2027         if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
2028         argv += optind;
2029
2030         // If we have no arguments, show currently mounted filesystems
2031         if (!argv[0]) {
2032                 if (!(opt & OPT_a)) {
2033                         FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2034
2035                         if (!mountTable)
2036                                 bb_error_msg_and_die("no %s", bb_path_mtab_file);
2037
2038                         while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2039                                                                 GETMNTENT_BUFSIZE))
2040                         {
2041                                 // Don't show rootfs. FIXME: why??
2042                                 // util-linux 2.12a happily shows rootfs...
2043                                 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
2044
2045                                 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2046                                         printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2047                                                         mtpair->mnt_dir, mtpair->mnt_type,
2048                                                         mtpair->mnt_opts);
2049                         }
2050                         if (ENABLE_FEATURE_CLEAN_UP)
2051                                 endmntent(mountTable);
2052                         return EXIT_SUCCESS;
2053                 }
2054                 storage_path = NULL;
2055         } else {
2056                 // When we have two arguments, the second is the directory and we can
2057                 // skip looking at fstab entirely.  We can always abspath() the directory
2058                 // argument when we get it.
2059                 if (argv[1]) {
2060                         if (nonroot)
2061                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2062                         mtpair->mnt_fsname = argv[0];
2063                         mtpair->mnt_dir = argv[1];
2064                         mtpair->mnt_type = fstype;
2065                         mtpair->mnt_opts = cmdopts;
2066                         resolve_mount_spec(&mtpair->mnt_fsname);
2067                         rc = singlemount(mtpair, /*ignore_busy:*/ 0);
2068                         return rc;
2069                 }
2070                 storage_path = bb_simplify_path(argv[0]); // malloced
2071         }
2072
2073         // Past this point, we are handling either "mount -a [opts]"
2074         // or "mount [opts] single_param"
2075
2076         cmdopt_flags = parse_mount_options(cmdopts, NULL);
2077         if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
2078                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2079
2080         // If we have a shared subtree flag, don't worry about fstab or mtab.
2081         if (ENABLE_FEATURE_MOUNT_FLAGS
2082          && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2083         ) {
2084                 // verbose_mount(source, target, type, flags, data)
2085                 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
2086                 if (rc)
2087                         bb_simple_perror_msg_and_die(argv[0]);
2088                 return rc;
2089         }
2090
2091         // Open either fstab or mtab
2092         fstabname = "/etc/fstab";
2093         if (cmdopt_flags & MS_REMOUNT) {
2094                 // WARNING. I am not sure this matches util-linux's
2095                 // behavior. It's possible util-linux does not
2096                 // take -o opts from mtab (takes only mount source).
2097                 fstabname = bb_path_mtab_file;
2098         }
2099         fstab = setmntent(fstabname, "r");
2100         if (!fstab)
2101                 bb_perror_msg_and_die("can't read '%s'", fstabname);
2102
2103         // Loop through entries until we find what we're looking for
2104         memset(mtpair, 0, sizeof(mtpair));
2105         for (;;) {
2106                 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2107
2108                 // Get next fstab entry
2109                 if (!getmntent_r(fstab, mtcur, getmntent_buf
2110                                         + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2111                                 GETMNTENT_BUFSIZE/2)
2112                 ) { // End of fstab/mtab is reached
2113                         mtcur = mtother; // the thing we found last time
2114                         break;
2115                 }
2116
2117                 // If we're trying to mount something specific and this isn't it,
2118                 // skip it.  Note we must match the exact text in fstab (ala
2119                 // "proc") or a full path from root
2120                 if (argv[0]) {
2121
2122                         // Is this what we're looking for?
2123                         if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2124                          && strcmp(storage_path, mtcur->mnt_fsname) != 0
2125                          && strcmp(argv[0], mtcur->mnt_dir) != 0
2126                          && strcmp(storage_path, mtcur->mnt_dir) != 0
2127                         ) {
2128                                 continue; // no
2129                         }
2130
2131                         // Remember this entry.  Something later may have
2132                         // overmounted it, and we want the _last_ match.
2133                         mtcur = mtother;
2134
2135                 // If we're mounting all
2136                 } else {
2137                         struct mntent *mp;
2138                         // No, mount -a won't mount anything,
2139                         // even user mounts, for mere humans
2140                         if (nonroot)
2141                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2142
2143                         // Does type match? (NULL matches always)
2144                         if (!match_fstype(mtcur, fstype))
2145                                 continue;
2146
2147                         // Skip noauto and swap anyway
2148                         if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2149                         // swap is bogus "fstype", parse_mount_options can't check fstypes
2150                          || strcasecmp(mtcur->mnt_type, "swap") == 0
2151                         ) {
2152                                 continue;
2153                         }
2154
2155                         // Does (at least one) option match?
2156                         // (NULL matches always)
2157                         if (!match_opt(mtcur->mnt_opts, O_optmatch))
2158                                 continue;
2159
2160                         resolve_mount_spec(&mtcur->mnt_fsname);
2161
2162                         // NFS mounts want this to be xrealloc-able
2163                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2164
2165                         // If nothing is mounted on this directory...
2166                         // (otherwise repeated "mount -a" mounts everything again)
2167                         mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2168                         // We do not check fsname match of found mount point -
2169                         // "/" may have fsname of "/dev/root" while fstab
2170                         // says "/dev/something_else".
2171                         if (mp) {
2172                                 if (verbose) {
2173                                         bb_error_msg("according to %s, "
2174                                                 "%s is already mounted on %s",
2175                                                 bb_path_mtab_file,
2176                                                 mp->mnt_fsname, mp->mnt_dir);
2177                                 }
2178                         } else {
2179                                 // ...mount this thing
2180                                 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2181                                         // Count number of failed mounts
2182                                         rc++;
2183                                 }
2184                         }
2185                         free(mtcur->mnt_opts);
2186                 }
2187         }
2188
2189         // End of fstab/mtab is reached.
2190         // Were we looking for something specific?
2191         if (argv[0]) { // yes
2192                 unsigned long l;
2193
2194                 // If we didn't find anything, complain
2195                 if (!mtcur->mnt_fsname)
2196                         bb_error_msg_and_die("can't find %s in %s",
2197                                 argv[0], fstabname);
2198
2199                 // What happens when we try to "mount swap_partition"?
2200                 // (fstab containts "swap_partition swap swap defaults 0 0")
2201                 // util-linux-ng 2.13.1 does this:
2202                 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2203                 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2204                 // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
2205                 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2206                 // exit_group(32)                          = ?
2207 #if 0
2208                 // In case we want to simply skip swap partitions:
2209                 l = parse_mount_options(mtcur->mnt_opts, NULL);
2210                 if ((l & MOUNT_SWAP)
2211                 // swap is bogus "fstype", parse_mount_options can't check fstypes
2212                  || strcasecmp(mtcur->mnt_type, "swap") == 0
2213                 ) {
2214                         goto ret;
2215                 }
2216 #endif
2217                 if (nonroot) {
2218                         // fstab must have "users" or "user"
2219                         l = parse_mount_options(mtcur->mnt_opts, NULL);
2220                         if (!(l & MOUNT_USERS))
2221                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2222                 }
2223
2224                 //util-linux-2.12 does not do this check.
2225                 //// If nothing is mounted on this directory...
2226                 //// (otherwise repeated "mount FOO" mounts FOO again)
2227                 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2228                 //if (mp) {
2229                 //      bb_error_msg("according to %s, "
2230                 //              "%s is already mounted on %s",
2231                 //              bb_path_mtab_file,
2232                 //              mp->mnt_fsname, mp->mnt_dir);
2233                 //} else {
2234                         // ...mount the last thing we found
2235                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2236                         append_mount_options(&(mtcur->mnt_opts), cmdopts);
2237                         resolve_mount_spec(&mtpair->mnt_fsname);
2238                         rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2239                         if (ENABLE_FEATURE_CLEAN_UP)
2240                                 free(mtcur->mnt_opts);
2241                 //}
2242         }
2243
2244  //ret:
2245         if (ENABLE_FEATURE_CLEAN_UP)
2246                 endmntent(fstab);
2247         if (ENABLE_FEATURE_CLEAN_UP) {
2248                 free(storage_path);
2249                 free(cmdopts);
2250         }
2251
2252 //TODO: exitcode should be ORed mask of (from "man mount"):
2253 // 0 success
2254 // 1 incorrect invocation or permissions
2255 // 2 system error (out of memory, cannot fork, no more loop devices)
2256 // 4 internal mount bug or missing nfs support in mount
2257 // 8 user interrupt
2258 //16 problems writing or locking /etc/mtab
2259 //32 mount failure
2260 //64 some mount succeeded
2261         return rc;
2262 }