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