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