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