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