e6bad7c2c847983f086d129658274b18c88931e2
[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 (23 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)
1198 {
1199         return -ENOSYS;
1200 }
1201 #endif
1202
1203 /* TODO */
1204 static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
1205 {
1206         return 0;
1207 }
1208
1209 /* RPC strerror analogs are terminally idiotic:
1210  * *mandatory* prefix and \n at end.
1211  * This hopefully helps. Usage:
1212  * error_msg_rpc(clnt_*error*(" ")) */
1213 static void error_msg_rpc(const char *msg)
1214 {
1215         int len;
1216         while (msg[0] == ' ' || msg[0] == ':') msg++;
1217         len = strlen(msg);
1218         while (len && msg[len-1] == '\n') len--;
1219         bb_error_msg("%.*s", len, msg);
1220 }
1221
1222 /* NB: mp->xxx fields may be trashed on exit */
1223 static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1224 {
1225         CLIENT *mclient;
1226         char *hostname;
1227         char *pathname;
1228         char *mounthost;
1229         /* prior to 2.6.23, kernel took NFS options in a form of this struct
1230          * only. 2.6.23+ looks at data->version, and if it's not 1..6,
1231          * then data pointer is interpreted as a string. */
1232         struct nfs_mount_data data;
1233         char *opt;
1234         struct hostent *hp;
1235         struct sockaddr_in server_addr;
1236         struct sockaddr_in mount_server_addr;
1237         int msock, fsock;
1238         union {
1239                 struct fhstatus nfsv2;
1240                 struct mountres3 nfsv3;
1241         } status;
1242         int daemonized;
1243         char *s;
1244         int port;
1245         int mountport;
1246         int proto;
1247 #if BB_MMU
1248         smallint bg = 0;
1249 #else
1250         enum { bg = 0 };
1251 #endif
1252         int retry;
1253         int mountprog;
1254         int mountvers;
1255         int nfsprog;
1256         int nfsvers;
1257         int retval;
1258         /* these all are one-bit really. gcc 4.3.1 likes this combination: */
1259         smallint tcp;
1260         smallint soft;
1261         int intr;
1262         int posix;
1263         int nocto;
1264         int noac;
1265         int nordirplus;
1266         int nolock;
1267         int noacl;
1268
1269         find_kernel_nfs_mount_version();
1270
1271         daemonized = 0;
1272         mounthost = NULL;
1273         retval = ETIMEDOUT;
1274         msock = fsock = -1;
1275         mclient = NULL;
1276
1277         /* NB: hostname, mounthost, filteropts must be free()d prior to return */
1278
1279         filteropts = xstrdup(filteropts); /* going to trash it later... */
1280
1281         hostname = xstrdup(mp->mnt_fsname);
1282         /* mount_main() guarantees that ':' is there */
1283         s = strchr(hostname, ':');
1284         pathname = s + 1;
1285         *s = '\0';
1286         /* Ignore all but first hostname in replicated mounts
1287          * until they can be fully supported. (mack@sgi.com) */
1288         s = strchr(hostname, ',');
1289         if (s) {
1290                 *s = '\0';
1291                 bb_error_msg("warning: multiple hostnames not supported");
1292         }
1293
1294         server_addr.sin_family = AF_INET;
1295         if (!inet_aton(hostname, &server_addr.sin_addr)) {
1296                 hp = gethostbyname(hostname);
1297                 if (hp == NULL) {
1298                         bb_herror_msg("%s", hostname);
1299                         goto fail;
1300                 }
1301                 if (hp->h_length != (int)sizeof(struct in_addr)) {
1302                         bb_error_msg_and_die("only IPv4 is supported");
1303                 }
1304                 memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1305         }
1306
1307         memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
1308
1309         /* add IP address to mtab options for use when unmounting */
1310
1311         if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
1312                 mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
1313         } else {
1314                 char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
1315                                         mp->mnt_opts[0] ? "," : "",
1316                                         inet_ntoa(server_addr.sin_addr));
1317                 free(mp->mnt_opts);
1318                 mp->mnt_opts = tmp;
1319         }
1320
1321         /* Set default options.
1322          * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
1323          * let the kernel decide.
1324          * timeo is filled in after we know whether it'll be TCP or UDP. */
1325         memset(&data, 0, sizeof(data));
1326         data.retrans  = 3;
1327         data.acregmin = 3;
1328         data.acregmax = 60;
1329         data.acdirmin = 30;
1330         data.acdirmax = 60;
1331         data.namlen   = NAME_MAX;
1332
1333         soft = 0;
1334         intr = 0;
1335         posix = 0;
1336         nocto = 0;
1337         nolock = 0;
1338         noac = 0;
1339         nordirplus = 0;
1340         noacl = 0;
1341         retry = 10000;          /* 10000 minutes ~ 1 week */
1342         tcp = 1;                        /* nfs-utils uses tcp per default */
1343
1344         mountprog = MOUNTPROG;
1345         mountvers = 0;
1346         port = 0;
1347         mountport = 0;
1348         nfsprog = 100003;
1349         nfsvers = 0;
1350
1351         /* parse options */
1352         if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
1353                 char *opteq = strchr(opt, '=');
1354                 if (opteq) {
1355                         int val, idx;
1356                         static const char options[] ALIGN1 =
1357                                 /* 0 */ "rsize\0"
1358                                 /* 1 */ "wsize\0"
1359                                 /* 2 */ "timeo\0"
1360                                 /* 3 */ "retrans\0"
1361                                 /* 4 */ "acregmin\0"
1362                                 /* 5 */ "acregmax\0"
1363                                 /* 6 */ "acdirmin\0"
1364                                 /* 7 */ "acdirmax\0"
1365                                 /* 8 */ "actimeo\0"
1366                                 /* 9 */ "retry\0"
1367                                 /* 10 */ "port\0"
1368                                 /* 11 */ "mountport\0"
1369                                 /* 12 */ "mounthost\0"
1370                                 /* 13 */ "mountprog\0"
1371                                 /* 14 */ "mountvers\0"
1372                                 /* 15 */ "nfsprog\0"
1373                                 /* 16 */ "nfsvers\0"
1374                                 /* 17 */ "vers\0"
1375                                 /* 18 */ "proto\0"
1376                                 /* 19 */ "namlen\0"
1377                                 /* 20 */ "addr\0";
1378
1379                         *opteq++ = '\0';
1380                         idx = index_in_strings(options, opt);
1381                         switch (idx) {
1382                         case 12: // "mounthost"
1383                                 mounthost = xstrndup(opteq,
1384                                                 strcspn(opteq, " \t\n\r,"));
1385                                 continue;
1386                         case 18: // "proto"
1387                                 if (is_prefixed_with(opteq, "tcp"))
1388                                         tcp = 1;
1389                                 else if (is_prefixed_with(opteq, "udp"))
1390                                         tcp = 0;
1391                                 else
1392                                         bb_error_msg("warning: unrecognized proto= option");
1393                                 continue;
1394                         case 20: // "addr" - ignore
1395                                 continue;
1396                         case -1: // unknown
1397                                 if (vfsflags & MS_REMOUNT)
1398                                         continue;
1399                         }
1400
1401                         val = xatoi_positive(opteq);
1402                         switch (idx) {
1403                         case 0: // "rsize"
1404                                 data.rsize = val;
1405                                 continue;
1406                         case 1: // "wsize"
1407                                 data.wsize = val;
1408                                 continue;
1409                         case 2: // "timeo"
1410                                 data.timeo = val;
1411                                 continue;
1412                         case 3: // "retrans"
1413                                 data.retrans = val;
1414                                 continue;
1415                         case 4: // "acregmin"
1416                                 data.acregmin = val;
1417                                 continue;
1418                         case 5: // "acregmax"
1419                                 data.acregmax = val;
1420                                 continue;
1421                         case 6: // "acdirmin"
1422                                 data.acdirmin = val;
1423                                 continue;
1424                         case 7: // "acdirmax"
1425                                 data.acdirmax = val;
1426                                 continue;
1427                         case 8: // "actimeo"
1428                                 data.acregmin = val;
1429                                 data.acregmax = val;
1430                                 data.acdirmin = val;
1431                                 data.acdirmax = val;
1432                                 continue;
1433                         case 9: // "retry"
1434                                 retry = val;
1435                                 continue;
1436                         case 10: // "port"
1437                                 port = val;
1438                                 continue;
1439                         case 11: // "mountport"
1440                                 mountport = val;
1441                                 continue;
1442                         case 13: // "mountprog"
1443                                 mountprog = val;
1444                                 continue;
1445                         case 14: // "mountvers"
1446                                 mountvers = val;
1447                                 continue;
1448                         case 15: // "nfsprog"
1449                                 nfsprog = val;
1450                                 continue;
1451                         case 16: // "nfsvers"
1452                         case 17: // "vers"
1453                                 nfsvers = val;
1454                                 continue;
1455                         case 19: // "namlen"
1456                                 //if (nfs_mount_version >= 2)
1457                                         data.namlen = val;
1458                                 //else
1459                                 //      bb_error_msg("warning: option namlen is not supported\n");
1460                                 continue;
1461                         default:
1462                                 bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
1463                                 goto fail;
1464                         }
1465                 }
1466                 else { /* not of the form opt=val */
1467                         static const char options[] ALIGN1 =
1468                                 "bg\0"
1469                                 "fg\0"
1470                                 "soft\0"
1471                                 "hard\0"
1472                                 "intr\0"
1473                                 "posix\0"
1474                                 "cto\0"
1475                                 "ac\0"
1476                                 "tcp\0"
1477                                 "udp\0"
1478                                 "lock\0"
1479                                 "rdirplus\0"
1480                                 "acl\0";
1481                         int val = 1;
1482                         if (is_prefixed_with(opt, "no")) {
1483                                 val = 0;
1484                                 opt += 2;
1485                         }
1486                         switch (index_in_strings(options, opt)) {
1487                         case 0: // "bg"
1488 #if BB_MMU
1489                                 bg = val;
1490 #endif
1491                                 break;
1492                         case 1: // "fg"
1493 #if BB_MMU
1494                                 bg = !val;
1495 #endif
1496                                 break;
1497                         case 2: // "soft"
1498                                 soft = val;
1499                                 break;
1500                         case 3: // "hard"
1501                                 soft = !val;
1502                                 break;
1503                         case 4: // "intr"
1504                                 intr = val;
1505                                 break;
1506                         case 5: // "posix"
1507                                 posix = val;
1508                                 break;
1509                         case 6: // "cto"
1510                                 nocto = !val;
1511                                 break;
1512                         case 7: // "ac"
1513                                 noac = !val;
1514                                 break;
1515                         case 8: // "tcp"
1516                                 tcp = val;
1517                                 break;
1518                         case 9: // "udp"
1519                                 tcp = !val;
1520                                 break;
1521                         case 10: // "lock"
1522                                 if (nfs_mount_version >= 3)
1523                                         nolock = !val;
1524                                 else
1525                                         bb_error_msg("warning: option nolock is not supported");
1526                                 break;
1527                         case 11: //rdirplus
1528                                 nordirplus = !val;
1529                                 break;
1530                         case 12: // acl
1531                                 noacl = !val;
1532                                 break;
1533                         default:
1534                                 bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
1535                                 goto fail;
1536                         }
1537                 }
1538         }
1539         proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
1540
1541         data.flags = (soft ? NFS_MOUNT_SOFT : 0)
1542                 | (intr ? NFS_MOUNT_INTR : 0)
1543                 | (posix ? NFS_MOUNT_POSIX : 0)
1544                 | (nocto ? NFS_MOUNT_NOCTO : 0)
1545                 | (noac ? NFS_MOUNT_NOAC : 0)
1546                 | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0)
1547                 | (noacl ? NFS_MOUNT_NOACL : 0);
1548         if (nfs_mount_version >= 2)
1549                 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
1550         if (nfs_mount_version >= 3)
1551                 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
1552         if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
1553                 bb_error_msg("NFSv%d not supported", nfsvers);
1554                 goto fail;
1555         }
1556         if (nfsvers && !mountvers)
1557                 mountvers = (nfsvers < 3) ? 1 : nfsvers;
1558         if (nfsvers && nfsvers < mountvers) {
1559                 mountvers = nfsvers;
1560         }
1561
1562         /* Adjust options if none specified */
1563         if (!data.timeo)
1564                 data.timeo = tcp ? 70 : 7;
1565
1566         data.version = nfs_mount_version;
1567
1568         if (vfsflags & MS_REMOUNT)
1569                 goto do_mount;
1570
1571         /*
1572          * If the previous mount operation on the same host was
1573          * backgrounded, and the "bg" for this mount is also set,
1574          * give up immediately, to avoid the initial timeout.
1575          */
1576         if (bg && we_saw_this_host_before(hostname)) {
1577                 daemonized = daemonize();
1578                 if (daemonized <= 0) { /* parent or error */
1579                         retval = -daemonized;
1580                         goto ret;
1581                 }
1582         }
1583
1584         /* Create mount daemon client */
1585         /* See if the nfs host = mount host. */
1586         if (mounthost) {
1587                 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
1588                         mount_server_addr.sin_family = AF_INET;
1589                         mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
1590                 } else {
1591                         hp = gethostbyname(mounthost);
1592                         if (hp == NULL) {
1593                                 bb_herror_msg("%s", mounthost);
1594                                 goto fail;
1595                         }
1596                         if (hp->h_length != (int)sizeof(struct in_addr)) {
1597                                 bb_error_msg_and_die("only IPv4 is supported");
1598                         }
1599                         mount_server_addr.sin_family = AF_INET;
1600                         memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
1601                 }
1602         }
1603
1604         /*
1605          * The following loop implements the mount retries. When the mount
1606          * times out, and the "bg" option is set, we background ourself
1607          * and continue trying.
1608          *
1609          * The case where the mount point is not present and the "bg"
1610          * option is set, is treated as a timeout. This is done to
1611          * support nested mounts.
1612          *
1613          * The "retry" count specified by the user is the number of
1614          * minutes to retry before giving up.
1615          */
1616         {
1617                 struct timeval total_timeout;
1618                 struct timeval retry_timeout;
1619                 struct pmap pm_mnt;
1620                 time_t t;
1621                 time_t prevt;
1622                 time_t timeout;
1623
1624                 retry_timeout.tv_sec = 3;
1625                 retry_timeout.tv_usec = 0;
1626                 total_timeout.tv_sec = 20;
1627                 total_timeout.tv_usec = 0;
1628 /* FIXME: use monotonic()? */
1629                 timeout = time(NULL) + 60 * retry;
1630                 prevt = 0;
1631                 t = 30;
1632  retry:
1633                 /* Be careful not to use too many CPU cycles */
1634                 if (t - prevt < 30)
1635                         sleep(30);
1636
1637                 get_mountport(&pm_mnt, &mount_server_addr,
1638                                 mountprog,
1639                                 mountvers,
1640                                 proto,
1641                                 mountport);
1642                 nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
1643
1644                 /* contact the mount daemon via TCP */
1645                 mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1646                 msock = RPC_ANYSOCK;
1647
1648                 switch (pm_mnt.pm_prot) {
1649                 case IPPROTO_UDP:
1650                         mclient = clntudp_create(&mount_server_addr,
1651                                                 pm_mnt.pm_prog,
1652                                                 pm_mnt.pm_vers,
1653                                                 retry_timeout,
1654                                                 &msock);
1655                         if (mclient)
1656                                 break;
1657                         mount_server_addr.sin_port = htons(pm_mnt.pm_port);
1658                         msock = RPC_ANYSOCK;
1659                 case IPPROTO_TCP:
1660                         mclient = clnttcp_create(&mount_server_addr,
1661                                                 pm_mnt.pm_prog,
1662                                                 pm_mnt.pm_vers,
1663                                                 &msock, 0, 0);
1664                         break;
1665                 default:
1666                         mclient = NULL;
1667                 }
1668                 if (!mclient) {
1669                         if (!daemonized && prevt == 0)
1670                                 error_msg_rpc(clnt_spcreateerror(" "));
1671                 } else {
1672                         enum clnt_stat clnt_stat;
1673
1674                         /* Try to mount hostname:pathname */
1675                         mclient->cl_auth = authunix_create_default();
1676
1677                         /* Make pointers in xdr_mountres3 NULL so
1678                          * that xdr_array allocates memory for us
1679                          */
1680                         memset(&status, 0, sizeof(status));
1681
1682                         if (pm_mnt.pm_vers == 3)
1683                                 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
1684                                                 (xdrproc_t) xdr_dirpath,
1685                                                 (caddr_t) &pathname,
1686                                                 (xdrproc_t) xdr_mountres3,
1687                                                 (caddr_t) &status,
1688                                                 total_timeout);
1689                         else
1690                                 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
1691                                                 (xdrproc_t) xdr_dirpath,
1692                                                 (caddr_t) &pathname,
1693                                                 (xdrproc_t) xdr_fhstatus,
1694                                                 (caddr_t) &status,
1695                                                 total_timeout);
1696
1697                         if (clnt_stat == RPC_SUCCESS)
1698                                 goto prepare_kernel_data; /* we're done */
1699                         if (errno != ECONNREFUSED) {
1700                                 error_msg_rpc(clnt_sperror(mclient, " "));
1701                                 goto fail;      /* don't retry */
1702                         }
1703                         /* Connection refused */
1704                         if (!daemonized && prevt == 0) /* print just once */
1705                                 error_msg_rpc(clnt_sperror(mclient, " "));
1706                         auth_destroy(mclient->cl_auth);
1707                         clnt_destroy(mclient);
1708                         mclient = NULL;
1709                         close(msock);
1710                         msock = -1;
1711                 }
1712
1713                 /* Timeout. We are going to retry... maybe */
1714                 if (!bg)
1715                         goto fail;
1716                 if (!daemonized) {
1717                         daemonized = daemonize();
1718                         if (daemonized <= 0) { /* parent or error */
1719                                 retval = -daemonized;
1720                                 goto ret;
1721                         }
1722                 }
1723                 prevt = t;
1724                 t = time(NULL);
1725                 if (t >= timeout)
1726                         /* TODO error message */
1727                         goto fail;
1728
1729                 goto retry;
1730         }
1731
1732  prepare_kernel_data:
1733
1734         if (nfsvers == 2) {
1735                 if (status.nfsv2.fhs_status != 0) {
1736                         bb_error_msg("%s:%s failed, reason given by server: %s",
1737                                 hostname, pathname,
1738                                 nfs_strerror(status.nfsv2.fhs_status));
1739                         goto fail;
1740                 }
1741                 memcpy(data.root.data,
1742                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1743                                 NFS_FHSIZE);
1744                 data.root.size = NFS_FHSIZE;
1745                 memcpy(data.old_root.data,
1746                                 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
1747                                 NFS_FHSIZE);
1748         } else {
1749                 fhandle3 *my_fhandle;
1750                 if (status.nfsv3.fhs_status != 0) {
1751                         bb_error_msg("%s:%s failed, reason given by server: %s",
1752                                 hostname, pathname,
1753                                 nfs_strerror(status.nfsv3.fhs_status));
1754                         goto fail;
1755                 }
1756                 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
1757                 memset(data.old_root.data, 0, NFS_FHSIZE);
1758                 memset(&data.root, 0, sizeof(data.root));
1759                 data.root.size = my_fhandle->fhandle3_len;
1760                 memcpy(data.root.data,
1761                                 (char *) my_fhandle->fhandle3_val,
1762                                 my_fhandle->fhandle3_len);
1763
1764                 data.flags |= NFS_MOUNT_VER3;
1765         }
1766
1767         /* Create nfs socket for kernel */
1768         if (tcp) {
1769                 if (nfs_mount_version < 3) {
1770                         bb_error_msg("NFS over TCP is not supported");
1771                         goto fail;
1772                 }
1773                 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1774         } else
1775                 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1776         if (fsock < 0) {
1777                 bb_perror_msg("nfs socket");
1778                 goto fail;
1779         }
1780         if (bindresvport(fsock, 0) < 0) {
1781                 bb_perror_msg("nfs bindresvport");
1782                 goto fail;
1783         }
1784         if (port == 0) {
1785                 server_addr.sin_port = PMAPPORT;
1786                 port = pmap_getport(&server_addr, nfsprog, nfsvers,
1787                                         tcp ? IPPROTO_TCP : IPPROTO_UDP);
1788                 if (port == 0)
1789                         port = NFS_PORT;
1790         }
1791         server_addr.sin_port = htons(port);
1792
1793         /* Prepare data structure for kernel */
1794         data.fd = fsock;
1795         memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
1796         strncpy(data.hostname, hostname, sizeof(data.hostname));
1797
1798         /* Clean up */
1799         auth_destroy(mclient->cl_auth);
1800         clnt_destroy(mclient);
1801         close(msock);
1802         msock = -1;
1803
1804         if (bg) {
1805                 /* We must wait until mount directory is available */
1806                 struct stat statbuf;
1807                 int delay = 1;
1808                 while (stat(mp->mnt_dir, &statbuf) == -1) {
1809                         if (!daemonized) {
1810                                 daemonized = daemonize();
1811                                 if (daemonized <= 0) { /* parent or error */
1812 /* FIXME: parent doesn't close fsock - ??! */
1813                                         retval = -daemonized;
1814                                         goto ret;
1815                                 }
1816                         }
1817                         sleep(delay);   /* 1, 2, 4, 8, 16, 30, ... */
1818                         delay *= 2;
1819                         if (delay > 30)
1820                                 delay = 30;
1821                 }
1822         }
1823
1824         /* Perform actual mount */
1825  do_mount:
1826         retval = mount_it_now(mp, vfsflags, (char*)&data);
1827         goto ret;
1828
1829         /* Abort */
1830  fail:
1831         if (msock >= 0) {
1832                 if (mclient) {
1833                         auth_destroy(mclient->cl_auth);
1834                         clnt_destroy(mclient);
1835                 }
1836                 close(msock);
1837         }
1838         if (fsock >= 0)
1839                 close(fsock);
1840
1841  ret:
1842         free(hostname);
1843         free(mounthost);
1844         free(filteropts);
1845         return retval;
1846 }
1847
1848 #else // !ENABLE_FEATURE_MOUNT_NFS
1849
1850 /* Linux 2.6.23+ supports nfs mounts with options passed as a string.
1851  * For older kernels, you must build busybox with ENABLE_FEATURE_MOUNT_NFS.
1852  * (However, note that then you lose any chances that NFS over IPv6 would work).
1853  */
1854 static int nfsmount(struct mntent *mp, unsigned long vfsflags, char *filteropts)
1855 {
1856         len_and_sockaddr *lsa;
1857         char *opts;
1858         char *end;
1859         char *dotted;
1860         int ret;
1861
1862 # if ENABLE_FEATURE_IPV6
1863         end = strchr(mp->mnt_fsname, ']');
1864         if (end && end[1] == ':')
1865                 end++;
1866         else
1867 # endif
1868                 /* mount_main() guarantees that ':' is there */
1869                 end = strchr(mp->mnt_fsname, ':');
1870
1871         *end = '\0';
1872         lsa = xhost2sockaddr(mp->mnt_fsname, /*port:*/ 0);
1873         *end = ':';
1874         dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1875         if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1876         opts = xasprintf("%s%saddr=%s",
1877                 filteropts ? filteropts : "",
1878                 filteropts ? "," : "",
1879                 dotted
1880         );
1881         if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1882         ret = mount_it_now(mp, vfsflags, opts);
1883         if (ENABLE_FEATURE_CLEAN_UP) free(opts);
1884
1885         return ret;
1886 }
1887
1888 #endif // !ENABLE_FEATURE_MOUNT_NFS
1889
1890 // Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
1891 // type detection.  Returns 0 for success, nonzero for failure.
1892 // NB: mp->xxx fields may be trashed on exit
1893 static int singlemount(struct mntent *mp, int ignore_busy)
1894 {
1895         int loopfd = -1;
1896         int rc = -1;
1897         unsigned long vfsflags;
1898         char *loopFile = NULL, *filteropts = NULL;
1899         llist_t *fl = NULL;
1900         struct stat st;
1901
1902         errno = 0;
1903
1904         vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
1905
1906         // Treat fstype "auto" as unspecified
1907         if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
1908                 mp->mnt_type = NULL;
1909
1910         // Might this be a virtual filesystem?
1911         if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
1912                 char *args[35];
1913                 char *s;
1914                 int n;
1915                 // fsname: "cmd#arg1#arg2..."
1916                 // WARNING: allows execution of arbitrary commands!
1917                 // Try "mount 'sh#-c#sh' bogus_dir".
1918                 // It is safe ONLY because non-root
1919                 // cannot use two-argument mount command
1920                 // and using one-argument "mount 'sh#-c#sh'" doesn't work:
1921                 // "mount: can't find sh#-c#sh in /etc/fstab"
1922                 // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
1923
1924                 s = mp->mnt_fsname;
1925                 n = 0;
1926                 args[n++] = s;
1927                 while (*s && n < 35 - 2) {
1928                         if (*s++ == '#' && *s != '#') {
1929                                 s[-1] = '\0';
1930                                 args[n++] = s;
1931                         }
1932                 }
1933                 args[n++] = mp->mnt_dir;
1934                 args[n] = NULL;
1935                 rc = spawn_and_wait(args);
1936                 goto report_error;
1937         }
1938
1939         // Might this be an CIFS filesystem?
1940         if (ENABLE_FEATURE_MOUNT_CIFS
1941          && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
1942          && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
1943          && mp->mnt_fsname[0] == mp->mnt_fsname[1]
1944         ) {
1945                 int len;
1946                 char c;
1947                 char *hostname, *share;
1948                 len_and_sockaddr *lsa;
1949
1950                 // Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
1951
1952                 hostname = mp->mnt_fsname + 2;
1953                 len = strcspn(hostname, "/\\");
1954                 share = hostname + len + 1;
1955                 if (len == 0          // 3rd char is a [back]slash (IOW: empty hostname)
1956                  || share[-1] == '\0' // no [back]slash after hostname
1957                  || share[0] == '\0'  // empty share name
1958                 ) {
1959                         goto report_error;
1960                 }
1961                 c = share[-1];
1962                 share[-1] = '\0';
1963                 len = strcspn(share, "/\\");
1964
1965                 // "unc=\\hostname\share" option is mandatory
1966                 // after CIFS option parsing was rewritten in Linux 3.4.
1967                 // Must use backslashes.
1968                 // If /dir1/dir2 is present, also add "prefixpath=dir1/dir2"
1969                 {
1970                         char *unc = xasprintf(
1971                                 share[len] != '\0'  /* "/dir1/dir2" exists? */
1972                                         ? "unc=\\\\%s\\%.*s,prefixpath=%s"
1973                                         : "unc=\\\\%s\\%.*s",
1974                                 hostname,
1975                                 len, share,
1976                                 share + len + 1  /* "dir1/dir2" */
1977                         );
1978                         parse_mount_options(unc, &filteropts);
1979                         if (ENABLE_FEATURE_CLEAN_UP) free(unc);
1980                 }
1981
1982                 lsa = host2sockaddr(hostname, 0);
1983                 share[-1] = c;
1984                 if (!lsa)
1985                         goto report_error;
1986
1987                 // If there is no "ip=..." option yet
1988                 if (!is_prefixed_with(filteropts, ",ip="+1)
1989                  && !strstr(filteropts, ",ip=")
1990                 ) {
1991                         char *dotted, *ip;
1992                         // Insert "ip=..." option into options
1993                         dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
1994                         if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
1995                         ip = xasprintf("ip=%s", dotted);
1996                         if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
1997 // Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
1998 // handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
1999 // Currently, glibc does not support that (has no NI_NUMERICSCOPE),
2000 // musl apparently does. This results in "ip=numericIPv6%iface_name"
2001 // (instead of _numeric_ iface_id) with glibc.
2002 // This probably should be fixed in glibc, not here.
2003 // The workaround is to manually specify correct "ip=ADDR%n" option.
2004                         parse_mount_options(ip, &filteropts);
2005                         if (ENABLE_FEATURE_CLEAN_UP) free(ip);
2006                 }
2007
2008                 mp->mnt_type = (char*)"cifs";
2009                 rc = mount_it_now(mp, vfsflags, filteropts);
2010
2011                 goto report_error;
2012         }
2013
2014         // Might this be an NFS filesystem?
2015         if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
2016          && strchr(mp->mnt_fsname, ':') != NULL
2017         ) {
2018                 if (!mp->mnt_type)
2019                         mp->mnt_type = (char*)"nfs";
2020                 rc = nfsmount(mp, vfsflags, filteropts);
2021                 goto report_error;
2022         }
2023
2024         // Look at the file.  (Not found isn't a failure for remount, or for
2025         // a synthetic filesystem like proc or sysfs.)
2026         // (We use stat, not lstat, in order to allow
2027         // mount symlink_to_file_or_blkdev dir)
2028         if (!stat(mp->mnt_fsname, &st)
2029          && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
2030         ) {
2031                 // Do we need to allocate a loopback device for it?
2032                 if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
2033                         loopFile = bb_simplify_path(mp->mnt_fsname);
2034                         mp->mnt_fsname = NULL; // will receive malloced loop dev name
2035
2036                         // mount always creates AUTOCLEARed loopdevs, so that umounting
2037                         // drops them without any code in the userspace.
2038                         // This happens since circa linux-2.6.25:
2039                         // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
2040                         // Date:    Wed Feb 6 01:36:27 2008 -0800
2041                         // Subject: Allow auto-destruction of loop devices
2042                         loopfd = set_loop(&mp->mnt_fsname,
2043                                         loopFile,
2044                                         0,
2045                                         ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
2046                                                 | BB_LO_FLAGS_AUTOCLEAR
2047                         );
2048                         if (loopfd < 0) {
2049                                 if (errno == EPERM || errno == EACCES)
2050                                         bb_error_msg(bb_msg_perm_denied_are_you_root);
2051                                 else
2052                                         bb_perror_msg("can't setup loop device");
2053                                 return errno;
2054                         }
2055
2056                 // Autodetect bind mounts
2057                 } else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
2058                         vfsflags |= MS_BIND;
2059         }
2060
2061         // If we know the fstype (or don't need to), jump straight
2062         // to the actual mount.
2063         if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) {
2064                 char *next;
2065                 for (;;) {
2066                         next = mp->mnt_type ? strchr(mp->mnt_type, ',') : NULL;
2067                         if (next)
2068                                 *next = '\0';
2069                         rc = mount_it_now(mp, vfsflags, filteropts);
2070                         if (rc == 0 || !next)
2071                                 break;
2072                         mp->mnt_type = next + 1;
2073                 }
2074         } else {
2075                 // Loop through filesystem types until mount succeeds
2076                 // or we run out
2077
2078                 // Initialize list of block backed filesystems.
2079                 // This has to be done here so that during "mount -a",
2080                 // mounts after /proc shows up can autodetect.
2081                 if (!fslist) {
2082                         fslist = get_block_backed_filesystems();
2083                         if (ENABLE_FEATURE_CLEAN_UP && fslist)
2084                                 atexit(delete_block_backed_filesystems);
2085                 }
2086
2087                 for (fl = fslist; fl; fl = fl->link) {
2088                         mp->mnt_type = fl->data;
2089                         rc = mount_it_now(mp, vfsflags, filteropts);
2090                         if (rc == 0)
2091                                 break;
2092                 }
2093         }
2094
2095         // If mount failed, clean up loop file (if any).
2096         // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
2097         // merely "close(loopfd)" should do it?)
2098         if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
2099                 del_loop(mp->mnt_fsname);
2100                 if (ENABLE_FEATURE_CLEAN_UP) {
2101                         free(loopFile);
2102                         /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
2103                 }
2104         }
2105
2106  report_error:
2107         if (ENABLE_FEATURE_CLEAN_UP)
2108                 free(filteropts);
2109
2110         if (loopfd >= 0)
2111                 close(loopfd);
2112
2113         if (errno == EBUSY && ignore_busy)
2114                 return 0;
2115         if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
2116                 return 0;
2117         if (rc != 0)
2118                 bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
2119         return rc;
2120 }
2121
2122 // -O support
2123 //    -O interprets a list of filter options which select whether a mount
2124 // point will be mounted: only mounts with options matching *all* filtering
2125 // options will be selected.
2126 //    By default each -O filter option must be present in the list of mount
2127 // options, but if it is prefixed by "no" then it must be absent.
2128 // For example,
2129 //  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
2130 //              (and also fails to match  -o a  because  -o c  is absent).
2131 //
2132 // It is different from -t in that each option is matched exactly; a leading
2133 // "no" at the beginning of one option does not negate the rest.
2134 static int match_opt(const char *fs_opt_in, const char *O_opt)
2135 {
2136         if (!O_opt)
2137                 return 1;
2138
2139         while (*O_opt) {
2140                 const char *fs_opt = fs_opt_in;
2141                 int O_len;
2142                 int match;
2143
2144                 // If option begins with "no" then treat as an inverted match:
2145                 // matching is a failure
2146                 match = 0;
2147                 if (O_opt[0] == 'n' && O_opt[1] == 'o') {
2148                         match = 1;
2149                         O_opt += 2;
2150                 }
2151                 // Isolate the current O option
2152                 O_len = strchrnul(O_opt, ',') - O_opt;
2153                 // Check for a match against existing options
2154                 while (1) {
2155                         if (strncmp(fs_opt, O_opt, O_len) == 0
2156                          && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
2157                         ) {
2158                                 if (match)
2159                                         return 0;  // "no" prefix, but option found
2160                                 match = 1;  // current O option found, go check next one
2161                                 break;
2162                         }
2163                         fs_opt = strchr(fs_opt, ',');
2164                         if (!fs_opt)
2165                                 break;
2166                         fs_opt++;
2167                 }
2168                 if (match == 0)
2169                         return 0;     // match wanted but not found
2170                 if (O_opt[O_len] == '\0') // end?
2171                         break;
2172                 // Step to the next O option
2173                 O_opt += O_len + 1;
2174         }
2175         // If we get here then everything matched
2176         return 1;
2177 }
2178
2179 // Parse options, if necessary parse fstab/mtab, and call singlemount for
2180 // each directory to be mounted.
2181 int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
2182 int mount_main(int argc UNUSED_PARAM, char **argv)
2183 {
2184         char *cmdopts = xzalloc(1);
2185         char *fstype = NULL;
2186         char *O_optmatch = NULL;
2187         char *storage_path;
2188         llist_t *lst_o = NULL;
2189         const char *fstabname = "/etc/fstab";
2190         FILE *fstab;
2191         int i, j;
2192         int rc = EXIT_SUCCESS;
2193         unsigned long cmdopt_flags;
2194         unsigned opt;
2195         struct mntent mtpair[2], *mtcur = mtpair;
2196         IF_NOT_DESKTOP(const int nonroot = 0;)
2197
2198         IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
2199
2200         INIT_G();
2201
2202         // Parse long options, like --bind and --move.  Note that -o option
2203         // and --option are synonymous.  Yes, this means --remount,rw works.
2204         for (i = j = 1; argv[i]; i++) {
2205                 if (argv[i][0] == '-' && argv[i][1] == '-')
2206                         append_mount_options(&cmdopts, argv[i] + 2);
2207                 else
2208                         argv[j++] = argv[i];
2209         }
2210         argv[j] = NULL;
2211
2212         // Parse remaining options
2213         // Max 2 params; -o is a list, -v is a counter
2214         opt = getopt32(argv, "^"
2215                 OPTION_STR
2216                 "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
2217                 &lst_o, &fstype, &O_optmatch
2218                 IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
2219                 IF_FEATURE_MOUNT_VERBOSE(, &verbose)
2220         );
2221
2222         while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
2223         if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
2224         if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
2225         argv += optind;
2226
2227         // If we have no arguments, show currently mounted filesystems
2228         if (!argv[0]) {
2229                 if (!(opt & OPT_a)) {
2230                         FILE *mountTable = setmntent(bb_path_mtab_file, "r");
2231
2232                         if (!mountTable)
2233                                 bb_error_msg_and_die("no %s", bb_path_mtab_file);
2234
2235                         while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
2236                                                                 GETMNTENT_BUFSIZE))
2237                         {
2238                                 // Don't show rootfs. FIXME: why??
2239                                 // util-linux 2.12a happily shows rootfs...
2240                                 //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
2241
2242                                 if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
2243                                         printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
2244                                                         mtpair->mnt_dir, mtpair->mnt_type,
2245                                                         mtpair->mnt_opts);
2246                         }
2247                         if (ENABLE_FEATURE_CLEAN_UP)
2248                                 endmntent(mountTable);
2249                         return EXIT_SUCCESS;
2250                 }
2251                 storage_path = NULL;
2252         } else {
2253                 // When we have two arguments, the second is the directory and we can
2254                 // skip looking at fstab entirely.  We can always abspath() the directory
2255                 // argument when we get it.
2256                 if (argv[1]) {
2257                         if (nonroot)
2258                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2259                         mtpair->mnt_fsname = argv[0];
2260                         mtpair->mnt_dir = argv[1];
2261                         mtpair->mnt_type = fstype;
2262                         mtpair->mnt_opts = cmdopts;
2263                         resolve_mount_spec(&mtpair->mnt_fsname);
2264                         rc = singlemount(mtpair, /*ignore_busy:*/ 0);
2265                         return rc;
2266                 }
2267                 storage_path = bb_simplify_path(argv[0]); // malloced
2268         }
2269
2270         // Past this point, we are handling either "mount -a [opts]"
2271         // or "mount [opts] single_param"
2272
2273         cmdopt_flags = parse_mount_options(cmdopts, NULL);
2274         if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
2275                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2276
2277         // If we have a shared subtree flag, don't worry about fstab or mtab.
2278         if (ENABLE_FEATURE_MOUNT_FLAGS
2279          && (cmdopt_flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
2280         ) {
2281                 // verbose_mount(source, target, type, flags, data)
2282                 rc = verbose_mount("", argv[0], "", cmdopt_flags, "");
2283                 if (rc)
2284                         bb_simple_perror_msg_and_die(argv[0]);
2285                 return rc;
2286         }
2287
2288         // A malicious user could overmount /usr without this.
2289         if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
2290                 fstabname = "/etc/fstab";
2291         // Open either fstab or mtab
2292         if (cmdopt_flags & MS_REMOUNT) {
2293                 // WARNING. I am not sure this matches util-linux's
2294                 // behavior. It's possible util-linux does not
2295                 // take -o opts from mtab (takes only mount source).
2296                 fstabname = bb_path_mtab_file;
2297         }
2298         fstab = setmntent(fstabname, "r");
2299         if (!fstab)
2300                 bb_perror_msg_and_die("can't read '%s'", fstabname);
2301
2302         // Loop through entries until we find what we're looking for
2303         memset(mtpair, 0, sizeof(mtpair));
2304         for (;;) {
2305                 struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
2306
2307                 // Get next fstab entry
2308                 if (!getmntent_r(fstab, mtcur, getmntent_buf
2309                                         + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
2310                                 GETMNTENT_BUFSIZE/2)
2311                 ) { // End of fstab/mtab is reached
2312                         mtcur = mtother; // the thing we found last time
2313                         break;
2314                 }
2315
2316                 // If we're trying to mount something specific and this isn't it,
2317                 // skip it.  Note we must match the exact text in fstab (ala
2318                 // "proc") or a full path from root
2319                 if (argv[0]) {
2320
2321                         // Is this what we're looking for?
2322                         if (strcmp(argv[0], mtcur->mnt_fsname) != 0
2323                          && strcmp(storage_path, mtcur->mnt_fsname) != 0
2324                          && strcmp(argv[0], mtcur->mnt_dir) != 0
2325                          && strcmp(storage_path, mtcur->mnt_dir) != 0
2326                         ) {
2327                                 continue; // no
2328                         }
2329
2330                         // Remember this entry.  Something later may have
2331                         // overmounted it, and we want the _last_ match.
2332                         mtcur = mtother;
2333
2334                 // If we're mounting all
2335                 } else {
2336                         struct mntent *mp;
2337                         // No, mount -a won't mount anything,
2338                         // even user mounts, for mere humans
2339                         if (nonroot)
2340                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2341
2342                         // Does type match? (NULL matches always)
2343                         if (!fstype_matches(mtcur->mnt_type, fstype))
2344                                 continue;
2345
2346                         // Skip noauto and swap anyway
2347                         if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
2348                         // swap is bogus "fstype", parse_mount_options can't check fstypes
2349                          || strcasecmp(mtcur->mnt_type, "swap") == 0
2350                         ) {
2351                                 continue;
2352                         }
2353
2354                         // Does (at least one) option match?
2355                         // (NULL matches always)
2356                         if (!match_opt(mtcur->mnt_opts, O_optmatch))
2357                                 continue;
2358
2359                         resolve_mount_spec(&mtcur->mnt_fsname);
2360
2361                         // NFS mounts want this to be xrealloc-able
2362                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2363
2364                         // If nothing is mounted on this directory...
2365                         // (otherwise repeated "mount -a" mounts everything again)
2366                         mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2367                         // We do not check fsname match of found mount point -
2368                         // "/" may have fsname of "/dev/root" while fstab
2369                         // says "/dev/something_else".
2370                         if (mp) {
2371                                 if (verbose) {
2372                                         bb_error_msg("according to %s, "
2373                                                 "%s is already mounted on %s",
2374                                                 bb_path_mtab_file,
2375                                                 mp->mnt_fsname, mp->mnt_dir);
2376                                 }
2377                         } else {
2378                                 // ...mount this thing
2379                                 if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
2380                                         // Count number of failed mounts
2381                                         rc++;
2382                                 }
2383                         }
2384                         free(mtcur->mnt_opts);
2385                 }
2386         }
2387
2388         // End of fstab/mtab is reached.
2389         // Were we looking for something specific?
2390         if (argv[0]) { // yes
2391                 unsigned long l;
2392
2393                 // If we didn't find anything, complain
2394                 if (!mtcur->mnt_fsname)
2395                         bb_error_msg_and_die("can't find %s in %s",
2396                                 argv[0], fstabname);
2397
2398                 // What happens when we try to "mount swap_partition"?
2399                 // (fstab containts "swap_partition swap swap defaults 0 0")
2400                 // util-linux-ng 2.13.1 does this:
2401                 // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
2402                 // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
2403                 // lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
2404                 // write(2, "mount: mount point swap does not exist\n", 39) = 39
2405                 // exit_group(32)                          = ?
2406 #if 0
2407                 // In case we want to simply skip swap partitions:
2408                 l = parse_mount_options(mtcur->mnt_opts, NULL);
2409                 if ((l & MOUNT_SWAP)
2410                 // swap is bogus "fstype", parse_mount_options can't check fstypes
2411                  || strcasecmp(mtcur->mnt_type, "swap") == 0
2412                 ) {
2413                         goto ret;
2414                 }
2415 #endif
2416                 if (nonroot) {
2417                         // fstab must have "users" or "user"
2418                         l = parse_mount_options(mtcur->mnt_opts, NULL);
2419                         if (!(l & MOUNT_USERS))
2420                                 bb_error_msg_and_die(bb_msg_you_must_be_root);
2421                 }
2422
2423                 //util-linux-2.12 does not do this check.
2424                 //// If nothing is mounted on this directory...
2425                 //// (otherwise repeated "mount FOO" mounts FOO again)
2426                 //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
2427                 //if (mp) {
2428                 //      bb_error_msg("according to %s, "
2429                 //              "%s is already mounted on %s",
2430                 //              bb_path_mtab_file,
2431                 //              mp->mnt_fsname, mp->mnt_dir);
2432                 //} else {
2433                         // ...mount the last thing we found
2434                         mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
2435                         append_mount_options(&(mtcur->mnt_opts), cmdopts);
2436                         resolve_mount_spec(&mtpair->mnt_fsname);
2437                         rc = singlemount(mtcur, /*ignore_busy:*/ 0);
2438                         if (ENABLE_FEATURE_CLEAN_UP)
2439                                 free(mtcur->mnt_opts);
2440                 //}
2441         }
2442
2443  //ret:
2444         if (ENABLE_FEATURE_CLEAN_UP)
2445                 endmntent(fstab);
2446         if (ENABLE_FEATURE_CLEAN_UP) {
2447                 free(storage_path);
2448                 free(cmdopts);
2449         }
2450
2451 //TODO: exitcode should be ORed mask of (from "man mount"):
2452 // 0 success
2453 // 1 incorrect invocation or permissions
2454 // 2 system error (out of memory, cannot fork, no more loop devices)
2455 // 4 internal mount bug or missing nfs support in mount
2456 // 8 user interrupt
2457 //16 problems writing or locking /etc/mtab
2458 //32 mount failure
2459 //64 some mount succeeded
2460         return rc;
2461 }