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