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