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