// mount_it_now() does the actual mount.
//
+//config:config MOUNT
+//config: bool "mount (30 kb)"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: All files and filesystems in Unix are arranged into one big directory
+//config: tree. The 'mount' utility is used to graft a filesystem onto a
+//config: particular part of the tree. A filesystem can either live on a block
+//config: device, or it can be accessible over the network, as is the case with
+//config: NFS filesystems.
+//config:
+//config:config FEATURE_MOUNT_FAKE
+//config: bool "Support -f (fake mount)"
+//config: default y
+//config: depends on MOUNT
+//config: help
+//config: Enable support for faking a file system mount.
+//config:
+//config:config FEATURE_MOUNT_VERBOSE
+//config: bool "Support -v (verbose)"
+//config: default y
+//config: depends on MOUNT
+//config: help
+//config: Enable multi-level -v[vv...] verbose messages. Useful if you
+//config: debug mount problems and want to see what is exactly passed
+//config: to the kernel.
+//config:
+//config:config FEATURE_MOUNT_HELPERS
+//config: bool "Support mount helpers"
+//config: default n
+//config: depends on MOUNT
+//config: help
+//config: Enable mounting of virtual file systems via external helpers.
+//config: E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
+//config: "obexfs -b00.11.22.33.44.55 /mnt"
+//config: Also "mount -t sometype [-o opts] fs /mnt" will try
+//config: "sometype [-o opts] fs /mnt" if simple mount syscall fails.
+//config: The idea is to use such virtual filesystems in /etc/fstab.
+//config:
+//config:config FEATURE_MOUNT_LABEL
+//config: bool "Support specifying devices by label or UUID"
+//config: default y
+//config: depends on MOUNT
+//config: select VOLUMEID
+//config: help
+//config: This allows for specifying a device by label or uuid, rather than by
+//config: name. This feature utilizes the same functionality as blkid/findfs.
+//config:
+//config:config FEATURE_MOUNT_NFS
+//config: bool "Support mounting NFS file systems on Linux < 2.6.23"
+//config: default n
+//config: depends on MOUNT
+//config: select FEATURE_SYSLOG
+//config: help
+//config: Enable mounting of NFS file systems on Linux kernels prior
+//config: to version 2.6.23. Note that in this case mounting of NFS
+//config: over IPv6 will not be possible.
+//config:
+//config: Note that this option links in RPC support from libc,
+//config: which is rather large (~10 kbytes on uclibc).
+//config:
+//config:config FEATURE_MOUNT_CIFS
+//config: bool "Support mounting CIFS/SMB file systems"
+//config: default y
+//config: depends on MOUNT
+//config: help
+//config: Enable support for samba mounts.
+//config:
+//config:config FEATURE_MOUNT_FLAGS
+//config: depends on MOUNT
+//config: bool "Support lots of -o flags"
+//config: default y
+//config: help
+//config: Without this, mount only supports ro/rw/remount. With this, it
+//config: supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
+//config: noatime, diratime, nodiratime, loud, bind, move, shared, slave,
+//config: private, unbindable, rshared, rslave, rprivate, and runbindable.
+//config:
+//config:config FEATURE_MOUNT_FSTAB
+//config: depends on MOUNT
+//config: bool "Support /etc/fstab and -a (mount all)"
+//config: default y
+//config: help
+//config: Support mount all and looking for files in /etc/fstab.
+//config:
+//config:config FEATURE_MOUNT_OTHERTAB
+//config: depends on FEATURE_MOUNT_FSTAB
+//config: bool "Support -T <alt_fstab>"
+//config: default y
+//config: help
+//config: Support mount -T (specifying an alternate fstab)
+
+/* On full-blown systems, requires suid for user mounts.
+ * But it's not unthinkable to have it available in non-suid flavor on some systems,
+ * for viewing mount table.
+ * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
+//applet:IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
+
+//kbuild:lib-$(CONFIG_MOUNT) += mount.o
+
//usage:#define mount_trivial_usage
-//usage: "[OPTIONS] [-o OPTS] DEVICE NODE"
+//usage: "[OPTIONS] [-o OPT] DEVICE NODE"
//usage:#define mount_full_usage "\n\n"
//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
//usage: "\n -a Mount all filesystems in fstab"
//usage: )
////usage: "\n -s Sloppy (ignored)"
//usage: "\n -r Read-only mount"
-//usage: "\n -w Read-write mount (default)"
+////usage: "\n -w Read-write mount (default)"
//usage: "\n -t FSTYPE[,...] Filesystem type(s)"
+//usage: IF_FEATURE_MOUNT_OTHERTAB(
+//usage: "\n -T FILE Read FILE instead of /etc/fstab"
+//usage: )
//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
//usage: "\n-o OPT:"
//usage: IF_FEATURE_MOUNT_LOOP(
//usage: "\n move Relocate an existing mount point"
//usage: )
//usage: "\n remount Remount a mounted filesystem, changing flags"
-//usage: "\n ro/rw Same as -r/-w"
+//usage: "\n ro Same as -r"
//usage: "\n"
//usage: "\nThere are filesystem-specific -o flags."
//usage:
#define BB_MS_INVERTED_VALUE (1u << 31)
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_FEATURE_MOUNT_LABEL
# include "volume_id.h"
#else
/* This is just a warning of a common mistake. Possibly this should be a
* uclibc faq entry rather than in busybox... */
# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
-# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support"
+# warning "You probably need to build uClibc with UCLIBC_HAS_RPC for NFS support"
+ /* not #error, since user may be using e.g. libtirpc instead.
+ * This might work:
+ * CONFIG_EXTRA_CFLAGS="-I/usr/include/tirpc"
+ * CONFIG_EXTRA_LDLIBS="tirpc"
+ */
# endif
# include <rpc/rpc.h>
# include <rpc/pmap_prot.h>
// Not real flags, but we want to be able to check for this.
enum {
- MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
+ MOUNT_USERS = (1 << 27) * ENABLE_DESKTOP,
+ MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
MOUNT_NOAUTO = (1 << 29),
MOUNT_SWAP = (1 << 30),
+ MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
};
-#define OPTION_STR "o:t:rwanfvsiO:"
+#define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
enum {
OPT_o = (1 << 0),
OPT_t = (1 << 1),
OPT_s = (1 << 8),
OPT_i = (1 << 9),
OPT_O = (1 << 10),
+ OPT_T = (1 << 11),
};
#if ENABLE_FEATURE_MTAB_SUPPORT
/* "swap" */ MOUNT_SWAP,
IF_DESKTOP(/* "user" */ MOUNT_USERS,)
IF_DESKTOP(/* "users" */ MOUNT_USERS,)
+ IF_DESKTOP(/* "nofail" */ MOUNT_NOFAIL,)
/* "_netdev" */ 0,
IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
)
/* "remount" */ MS_REMOUNT // action flag
};
-static const char mount_option_str[] =
+static const char mount_option_str[] ALIGN1 =
IF_FEATURE_MOUNT_LOOP(
"loop\0"
)
"swap\0"
IF_DESKTOP("user\0")
IF_DESKTOP("users\0")
+ IF_DESKTOP("nofail\0")
"_netdev\0"
IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
)
char getmntent_buf[1];
} FIX_ALIASING;
enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define nfs_mount_version (G.nfs_mount_version)
#if ENABLE_FEATURE_MOUNT_VERBOSE
#define verbose (G.verbose )
#endif
#define fslist (G.fslist )
#define getmntent_buf (G.getmntent_buf )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
#if ENABLE_FEATURE_MTAB_SUPPORT
/*
if (!f) continue;
while ((buf = xmalloc_fgetline(f)) != NULL) {
- if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
+ if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
goto next;
fs = skip_whitespace(buf);
if (*fs == '#' || *fs == '*' || !*fs)
{
int rc = 0;
+ vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
+
if (FAKE_IT) {
if (verbose >= 2)
bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
# define EDQUOT ENOSPC
#endif
/* Convert each NFSERR_BLAH into EBLAH */
-static const uint8_t nfs_err_stat[] = {
+static const uint8_t nfs_err_stat[] ALIGN1 = {
1, 2, 5, 6, 13, 17,
19, 20, 21, 22, 27, 28,
30, 63, 66, 69, 70, 71
#else
typedef uint16_t nfs_err_type;
#endif
-static const nfs_err_type nfs_err_errnum[] = {
+static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
strcspn(opteq, " \t\n\r,"));
continue;
case 18: // "proto"
- if (!strncmp(opteq, "tcp", 3))
+ if (is_prefixed_with(opteq, "tcp"))
tcp = 1;
- else if (!strncmp(opteq, "udp", 3))
+ else if (is_prefixed_with(opteq, "udp"))
tcp = 0;
else
bb_error_msg("warning: unrecognized proto= option");
"rdirplus\0"
"acl\0";
int val = 1;
- if (!strncmp(opt, "no", 2)) {
+ if (is_prefixed_with(opt, "no")) {
val = 0;
opt += 2;
}
// NB: mp->xxx fields may be trashed on exit
static int singlemount(struct mntent *mp, int ignore_busy)
{
+ int loopfd = -1;
int rc = -1;
unsigned long vfsflags;
char *loopFile = NULL, *filteropts = NULL;
int len;
char c;
char *hostname, *share;
- char *dotted, *ip;
len_and_sockaddr *lsa;
// Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
if (!lsa)
goto report_error;
- // Insert "ip=..." option into options
- dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
- if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
- ip = xasprintf("ip=%s", dotted);
- if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
- parse_mount_options(ip, &filteropts);
- if (ENABLE_FEATURE_CLEAN_UP) free(ip);
+ // If there is no "ip=..." option yet
+ if (!is_prefixed_with(filteropts, ",ip="+1)
+ && !strstr(filteropts, ",ip=")
+ ) {
+ char *dotted, *ip;
+ // Insert "ip=..." option into options
+ dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+ if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
+ ip = xasprintf("ip=%s", dotted);
+ if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
+// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
+// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
+// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
+// musl apparently does. This results in "ip=numericIPv6%iface_name"
+// (instead of _numeric_ iface_id) with glibc.
+// This probably should be fixed in glibc, not here.
+// The workaround is to manually specify correct "ip=ADDR%n" option.
+ parse_mount_options(ip, &filteropts);
+ if (ENABLE_FEATURE_CLEAN_UP) free(ip);
+ }
mp->mnt_type = (char*)"cifs";
rc = mount_it_now(mp, vfsflags, filteropts);
}
// Might this be an NFS filesystem?
- if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
+ if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
&& strchr(mp->mnt_fsname, ':') != NULL
) {
if (!mp->mnt_type)
if (ENABLE_FEATURE_MOUNT_LOOP && S_ISREG(st.st_mode)) {
loopFile = bb_simplify_path(mp->mnt_fsname);
mp->mnt_fsname = NULL; // will receive malloced loop dev name
- if (set_loop(&mp->mnt_fsname, loopFile, 0, /*ro:*/ (vfsflags & MS_RDONLY)) < 0) {
+
+ // mount always creates AUTOCLEARed loopdevs, so that umounting
+ // drops them without any code in the userspace.
+ // This happens since circa linux-2.6.25:
+ // commit 96c5865559cee0f9cbc5173f3c949f6ce3525581
+ // Date: Wed Feb 6 01:36:27 2008 -0800
+ // Subject: Allow auto-destruction of loop devices
+ loopfd = set_loop(&mp->mnt_fsname,
+ loopFile,
+ 0,
+ ((vfsflags & MS_RDONLY) ? BB_LO_FLAGS_READ_ONLY : 0)
+ | BB_LO_FLAGS_AUTOCLEAR
+ );
+ if (loopfd < 0) {
if (errno == EPERM || errno == EACCES)
bb_error_msg(bb_msg_perm_denied_are_you_root);
else
}
// If mount failed, clean up loop file (if any).
+ // (Newer kernels which support LO_FLAGS_AUTOCLEAR should not need this,
+ // merely "close(loopfd)" should do it?)
if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
del_loop(mp->mnt_fsname);
if (ENABLE_FEATURE_CLEAN_UP) {
free(loopFile);
- free(mp->mnt_fsname);
+ /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
}
}
if (ENABLE_FEATURE_CLEAN_UP)
free(filteropts);
+ if (loopfd >= 0)
+ close(loopfd);
+
if (errno == EBUSY && ignore_busy)
return 0;
+ if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
+ return 0;
if (rc != 0)
bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
return rc;
char *O_optmatch = NULL;
char *storage_path;
llist_t *lst_o = NULL;
- const char *fstabname;
+ const char *fstabname = "/etc/fstab";
FILE *fstab;
int i, j;
int rc = EXIT_SUCCESS;
// Parse remaining options
// Max 2 params; -o is a list, -v is a counter
- opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
- opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
- IF_FEATURE_MOUNT_VERBOSE(, &verbose));
+ opt = getopt32(argv, "^"
+ OPTION_STR
+ "\0" "?2"IF_FEATURE_MOUNT_VERBOSE("vv"),
+ &lst_o, &fstype, &O_optmatch
+ IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
+ IF_FEATURE_MOUNT_VERBOSE(, &verbose)
+ );
+
while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
return rc;
}
+ // A malicious user could overmount /usr without this.
+ if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
+ fstabname = "/etc/fstab";
// Open either fstab or mtab
- fstabname = "/etc/fstab";
if (cmdopt_flags & MS_REMOUNT) {
// WARNING. I am not sure this matches util-linux's
// behavior. It's possible util-linux does not
bb_error_msg_and_die(bb_msg_you_must_be_root);
// Does type match? (NULL matches always)
- if (!match_fstype(mtcur, fstype))
+ if (!fstype_matches(mtcur->mnt_type, fstype))
continue;
// Skip noauto and swap anyway