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