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