*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-
// Design notes: There is no spec for mount. Remind me to write one.
//
// mount_main() calls singlemount() which calls mount_it_now().
// singlemount() can loop through /etc/filesystems for fstype detection.
// mount_it_now() does the actual mount.
//
-
#include <mntent.h>
#include <syslog.h>
-#include "libbb.h"
+#include <sys/mount.h>
+// Grab more as needed from util-linux's mount/mount_constants.h
+#ifndef MS_DIRSYNC
+# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
+#endif
+#ifndef MS_UNION
+# define MS_UNION (1 << 8)
+#endif
+#ifndef MS_BIND
+# define MS_BIND (1 << 12)
+#endif
+#ifndef MS_MOVE
+# define MS_MOVE (1 << 13)
+#endif
+#ifndef MS_RECURSIVE
+# define MS_RECURSIVE (1 << 14)
+#endif
+#ifndef MS_SILENT
+# define MS_SILENT (1 << 15)
+#endif
+// The shared subtree stuff, which went in around 2.6.15
+#ifndef MS_UNBINDABLE
+# define MS_UNBINDABLE (1 << 17)
+#endif
+#ifndef MS_PRIVATE
+# define MS_PRIVATE (1 << 18)
+#endif
+#ifndef MS_SLAVE
+# define MS_SLAVE (1 << 19)
+#endif
+#ifndef MS_SHARED
+# define MS_SHARED (1 << 20)
+#endif
+#ifndef MS_RELATIME
+# define MS_RELATIME (1 << 21)
+#endif
+#include "libbb.h"
#if ENABLE_FEATURE_MOUNT_LABEL
-#include "volume_id.h"
+# include "volume_id.h"
+#else
+# define resolve_mount_spec(fsname) ((void)0)
#endif
// Needed for nfs support only
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
-#ifndef MS_SILENT
-#define MS_SILENT (1 << 15)
-#endif
-// Grab more as needed from util-linux's mount/mount_constants.h
-#ifndef MS_DIRSYNC
-#define MS_DIRSYNC 128 // Directory modifications are synchronous
-#endif
-
#if defined(__dietlibc__)
// 16.12.2006, Sampo Kellomaki (sampo@iki.fi)
};
#if ENABLE_FEATURE_MTAB_SUPPORT
-#define useMtab (!(option_mask32 & OPT_n))
+#define USE_MTAB (!(option_mask32 & OPT_n))
#else
-#define useMtab 0
+#define USE_MTAB 0
#endif
#if ENABLE_FEATURE_MOUNT_FAKE
-#define fakeIt (option_mask32 & OPT_f)
+#define FAKE_IT (option_mask32 & OPT_f)
#else
-#define fakeIt 0
+#define FAKE_IT 0
+#endif
+
+#if ENABLE_FEATURE_MOUNT_HELPERS
+#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
+#else
+#define HELPERS_ALLOWED 0
#endif
/* "loud" */ ~MS_SILENT,
// action flags
+ /* "union" */ MS_UNION,
/* "bind" */ MS_BIND,
/* "move" */ MS_MOVE,
/* "shared" */ MS_SHARED,
"loud\0"
// action flags
+ "union\0"
"bind\0"
"move\0"
"shared\0"
#define verbose_mount(...) mount(__VA_ARGS__)
#endif
-#if ENABLE_FEATURE_MOUNT_LABEL
-static void resolve_mount_spec(char **fsname)
-{
- char *tmp = NULL;
-
- if (!strncmp(*fsname, "UUID=", 5))
- tmp = get_devname_from_uuid(*fsname + 5);
- else if (!strncmp(*fsname, "LABEL=", 6))
- tmp = get_devname_from_label(*fsname + 6);
-
- if (tmp)
- *fsname = tmp;
-}
-#else
-#define resolve_mount_spec(fsname) ((void)0)
-#endif
-
// Append mount options to string
static void append_mount_options(char **oldopts, const char *newopts)
{
"/proc/filesystems",
};
char *fs, *buf;
- llist_t *list = 0;
+ llist_t *list = NULL;
int i;
FILE *f;
if (!f) continue;
while ((buf = xmalloc_fgetline(f)) != NULL) {
- if (!strncmp(buf, "nodev", 5) && isspace(buf[5]))
+ if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
continue;
fs = skip_whitespace(buf);
- if (*fs=='#' || *fs=='*' || !*fs) continue;
+ if (*fs == '#' || *fs == '*' || !*fs)
+ continue;
llist_add_to_end(&list, xstrdup(fs));
free(buf);
{
int rc = 0;
- if (fakeIt) {
+ if (FAKE_IT) {
if (verbose >= 2)
bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
// If mount failed, try
// helper program mount.<mnt_type>
- if (ENABLE_FEATURE_MOUNT_HELPERS && rc) {
- char *args[6];
+ if (HELPERS_ALLOWED && rc && mp->mnt_type) {
+ char *args[8];
int errno_save = errno;
args[0] = xasprintf("mount.%s", mp->mnt_type);
rc = 1;
+ if (FAKE_IT)
+ args[rc++] = (char *)"-f";
+ if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
+ args[rc++] = (char *)"-n";
args[rc++] = mp->mnt_fsname;
args[rc++] = mp->mnt_dir;
if (filteropts) {
// If the mount was successful, and we're maintaining an old-style
// mtab file by hand, add the new entry to it now.
mtab:
- if (useMtab && !rc && !(vfsflags & MS_REMOUNT)) {
+ if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
char *fsname;
FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
const char *option_str = mount_option_str;
if (!xdr_fhandle3(xdrs, &objp->fhandle))
return FALSE;
if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), &(objp->auth_flavours.auth_flavours_len), ~0,
- sizeof (int), (xdrproc_t) xdr_int))
+ sizeof(int), (xdrproc_t) xdr_int))
return FALSE;
return TRUE;
}
}
/* NB: mp->xxx fields may be trashed on exit */
-static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
+static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
{
CLIENT *mclient;
char *hostname;
char *pathname;
char *mounthost;
+ /* prior to 2.6.23, kernel took NFS options in a form of this struct
+ * only. 2.6.23+ looks at data->version, and if it's not 1..6,
+ * then data pointer is interpreted as a string. */
struct nfs_mount_data data;
char *opt;
struct hostent *hp;
int nfsprog;
int nfsvers;
int retval;
- /* these all are one-bit really. 4.3.1 likes this combination: */
+ /* these all are one-bit really. gcc 4.3.1 likes this combination: */
smallint tcp;
smallint soft;
int intr;
{
int rc = -1;
long vfsflags;
- char *loopFile = 0, *filteropts = 0;
- llist_t *fl = 0;
+ char *loopFile = NULL, *filteropts = NULL;
+ llist_t *fl = NULL;
struct stat st;
+ errno = 0;
+
vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
// Treat fstype "auto" as unspecified
mp->mnt_type = NULL;
// Might this be a virtual filesystem?
- if (ENABLE_FEATURE_MOUNT_HELPERS
- && (strchr(mp->mnt_fsname, '#'))
- ) {
- char *s, *p, *args[35];
- int n = 0;
-// FIXME: does it allow execution of arbitrary commands?!
-// What args[0] can end up with?
- for (s = p = mp->mnt_fsname; *s && n < 35-3; ++s) {
- if (s[0] == '#' && s[1] != '#') {
- *s = '\0';
- args[n++] = p;
- p = s + 1;
+ if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
+ char *args[35];
+ char *s;
+ int n;
+ // fsname: "cmd#arg1#arg2..."
+ // WARNING: allows execution of arbitrary commands!
+ // Try "mount 'sh#-c#sh' bogus_dir".
+ // It is safe ONLY because non-root
+ // cannot use two-argument mount command
+ // and using one-argument "mount 'sh#-c#sh'" doesn't work:
+ // "mount: can't find sh#-c#sh in /etc/fstab"
+ // (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
+
+ s = mp->mnt_fsname;
+ n = 0;
+ args[n++] = s;
+ while (*s && n < 35 - 2) {
+ if (*s++ == '#' && *s != '#') {
+ s[-1] = '\0';
+ args[n++] = s;
}
}
- args[n++] = p;
args[n++] = mp->mnt_dir;
args[n] = NULL;
rc = wait4pid(xspawn(args));
&& (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
&& mp->mnt_fsname[0] == mp->mnt_fsname[1]
) {
-#if 0 /* reported to break things */
+ int len;
+ char c;
len_and_sockaddr *lsa;
- char *ip, *dotted;
- char *s;
-
- // Replace '/' with '\' and verify that unc points to "//server/share".
- for (s = mp->mnt_fsname; *s; ++s)
- if (*s == '/') *s = '\\';
+ char *hostname, *dotted, *ip;
- // Get server IP
- s = strrchr(mp->mnt_fsname, '\\');
- if (s <= mp->mnt_fsname+1)
+ hostname = mp->mnt_fsname + 2;
+ len = strcspn(hostname, "/\\");
+ if (len == 0 || hostname[len] == '\0')
goto report_error;
- *s = '\0';
- lsa = host2sockaddr(mp->mnt_fsname+2, 0);
- *s = '\\';
+ c = hostname[len];
+ hostname[len] = '\0';
+ lsa = host2sockaddr(hostname, 0);
+ hostname[len] = c;
if (!lsa)
goto report_error;
- // Insert ip=... option into string flags.
+ // 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);
- // Compose new unc '\\server-ip\share'
- // (s => slash after hostname)
- mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s);
-#endif
- // Lock is required [why?]
+ // "-o mand" is required [why?]
vfsflags |= MS_MANDLOCK;
mp->mnt_type = (char*)"cifs";
rc = mount_it_now(mp, vfsflags, filteropts);
-#if 0
- if (ENABLE_FEATURE_CLEAN_UP) {
- free(mp->mnt_fsname);
- free(ip);
- free(dotted);
- free(lsa);
- }
-#endif
+
goto report_error;
}
// Might this be an NFS filesystem?
if (ENABLE_FEATURE_MOUNT_NFS
- && (!mp->mnt_type || !strcmp(mp->mnt_type, "nfs"))
+ && (!mp->mnt_type || strcmp(mp->mnt_type, "nfs") == 0)
&& strchr(mp->mnt_fsname, ':') != NULL
) {
rc = nfsmount(mp, vfsflags, filteropts);
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) < 0) {
+ if (set_loop(&mp->mnt_fsname, loopFile, 0) < 0) {
if (errno == EPERM || errno == EACCES)
bb_error_msg(bb_msg_perm_denied_are_you_root);
else
- bb_perror_msg("cannot setup loop device");
+ bb_perror_msg("can't setup loop device");
return errno;
}
// Loop through filesystem types until mount succeeds
// or we run out
- // Initialize list of block backed filesystems. This has to be
- // done here so that during "mount -a", mounts after /proc shows up
- // can autodetect.
+ // Initialize list of block backed filesystems.
+ // This has to be done here so that during "mount -a",
+ // mounts after /proc shows up can autodetect.
if (!fslist) {
fslist = get_block_backed_filesystems();
if (ENABLE_FEATURE_CLEAN_UP && fslist)
for (fl = fslist; fl; fl = fl->link) {
mp->mnt_type = fl->data;
rc = mount_it_now(mp, vfsflags, filteropts);
- if (!rc) break;
+ if (!rc)
+ break;
}
}
return rc;
}
-/* -O support
- * Unlike -t, -O should interpret "no" prefix differently:
- * -t noa,b,c = -t no(a,b,c) = mount all except fs'es with types a,b, and c
- * -O noa,b,c = -O noa,b,c = mount all with without option a,
- * or with option b or c.
- * But for now we do not support -O a,b,c at all (only -O a).
- *
- * Another difference from -t support (match_fstype) is that
- * we need to examine the _list_ of options in fsopt, not just a string.
- */
-static int match_opt(const char *fs_opt, const char *O_opt)
+// -O support
+// -O interprets a list of filter options which select whether a mount
+// point will be mounted: only mounts with options matching *all* filtering
+// options will be selected.
+// By default each -O filter option must be present in the list of mount
+// options, but if it is prefixed by "no" then it must be absent.
+// For example,
+// -O a,nob,c matches -o a,c but fails to match -o a,b,c
+// (and also fails to match -o a because -o c is absent).
+//
+// It is different from -t in that each option is matched exactly; a leading
+// "no" at the beginning of one option does not negate the rest.
+static int match_opt(const char *fs_opt_in, const char *O_opt)
{
- int match = 1;
- int len;
-
if (!O_opt)
- return match;
-
- if (O_opt[0] == 'n' && O_opt[1] == 'o') {
- match--;
- O_opt += 2;
- }
-
- len = strlen(O_opt);
- while (1) {
- if (strncmp(fs_opt, O_opt, len) == 0
- && (fs_opt[len] == '\0' || fs_opt[len] == ',')
- ) {
- return match;
+ return 1;
+
+ while (*O_opt) {
+ const char *fs_opt = fs_opt_in;
+ int O_len;
+ int match;
+
+ // If option begins with "no" then treat as an inverted match:
+ // matching is a failure
+ match = 0;
+ if (O_opt[0] == 'n' && O_opt[1] == 'o') {
+ match = 1;
+ O_opt += 2;
+ }
+ // Isolate the current O option
+ O_len = strchrnul(O_opt, ',') - O_opt;
+ // Check for a match against existing options
+ while (1) {
+ if (strncmp(fs_opt, O_opt, O_len) == 0
+ && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
+ ) {
+ if (match)
+ return 0; // "no" prefix, but option found
+ match = 1; // current O option found, go check next one
+ break;
+ }
+ fs_opt = strchr(fs_opt, ',');
+ if (!fs_opt)
+ break;
+ fs_opt++;
}
- fs_opt = strchr(fs_opt, ',');
- if (!fs_opt)
+ if (match == 0)
+ return 0; // match wanted but not found
+ if (O_opt[O_len] == '\0') // end?
break;
- fs_opt++;
+ // Step to the next O option
+ O_opt += O_len + 1;
}
-
- return !match;
+ // If we get here then everything matched
+ return 1;
}
// Parse options, if necessary parse fstab/mtab, and call singlemount for
// each directory to be mounted.
-static const char must_be_root[] ALIGN1 = "you must be root";
-
int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int mount_main(int argc UNUSED_PARAM, char **argv)
{
llist_t *lst_o = NULL;
const char *fstabname;
FILE *fstab;
- int i, j, rc = 0;
+ int i, j;
+ int rc = EXIT_SUCCESS;
unsigned opt;
struct mntent mtpair[2], *mtcur = mtpair;
IF_NOT_DESKTOP(const int nonroot = 0;)
{
// Don't show rootfs. FIXME: why??
// util-linux 2.12a happily shows rootfs...
- //if (!strcmp(mtpair->mnt_fsname, "rootfs")) continue;
+ //if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
- if (!fstype || !strcmp(mtpair->mnt_type, fstype))
+ if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
mtpair->mnt_dir, mtpair->mnt_type,
mtpair->mnt_opts);
// argument when we get it.
if (argv[1]) {
if (nonroot)
- bb_error_msg_and_die(must_be_root);
+ bb_error_msg_and_die(bb_msg_you_must_be_root);
mtpair->mnt_fsname = argv[0];
mtpair->mnt_dir = argv[1];
mtpair->mnt_type = fstype;
mtpair->mnt_opts = cmdopts;
resolve_mount_spec(&mtpair->mnt_fsname);
- rc = singlemount(mtpair, 0);
+ rc = singlemount(mtpair, /*ignore_busy:*/ 0);
return rc;
}
storage_path = bb_simplify_path(argv[0]); // malloced
i = parse_mount_options(cmdopts, NULL); // FIXME: should be "long", not "int"
if (nonroot && (i & ~MS_SILENT)) // Non-root users cannot specify flags
- bb_error_msg_and_die(must_be_root);
+ bb_error_msg_and_die(bb_msg_you_must_be_root);
// If we have a shared subtree flag, don't worry about fstab or mtab.
if (ENABLE_FEATURE_MOUNT_FLAGS
}
fstab = setmntent(fstabname, "r");
if (!fstab)
- bb_perror_msg_and_die("cannot read %s", fstabname);
+ bb_perror_msg_and_die("can't read %s", fstabname);
// Loop through entries until we find what we're looking for
memset(mtpair, 0, sizeof(mtpair));
if (argv[0]) {
// Is this what we're looking for?
- if (strcmp(argv[0], mtcur->mnt_fsname) &&
- strcmp(storage_path, mtcur->mnt_fsname) &&
- strcmp(argv[0], mtcur->mnt_dir) &&
- strcmp(storage_path, mtcur->mnt_dir)) continue;
+ if (strcmp(argv[0], mtcur->mnt_fsname) != 0
+ && strcmp(storage_path, mtcur->mnt_fsname) != 0
+ && strcmp(argv[0], mtcur->mnt_dir) != 0
+ && strcmp(storage_path, mtcur->mnt_dir) != 0
+ ) {
+ continue; // no
+ }
// Remember this entry. Something later may have
// overmounted it, and we want the _last_ match.
// If we're mounting all
} else {
+ struct mntent *mp;
// No, mount -a won't mount anything,
// even user mounts, for mere humans
if (nonroot)
- bb_error_msg_and_die(must_be_root);
+ bb_error_msg_and_die(bb_msg_you_must_be_root);
// Does type match? (NULL matches always)
if (!match_fstype(mtcur, fstype))
continue;
- // Skip noauto and swap anyway.
+ // Skip noauto and swap anyway
if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
// swap is bogus "fstype", parse_mount_options can't check fstypes
|| strcasecmp(mtcur->mnt_type, "swap") == 0
// NFS mounts want this to be xrealloc-able
mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
- // Mount this thing
- if (singlemount(mtcur, 1)) {
- // Count number of failed mounts
- rc++;
+ // If nothing is mounted on this directory...
+ // (otherwise repeated "mount -a" mounts everything again)
+ mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
+ // We do not check fsname match of found mount point -
+ // "/" may have fsname of "/dev/root" while fstab
+ // says "/dev/something_else".
+ if (mp) {
+ if (verbose) {
+ bb_error_msg("according to %s, "
+ "%s is already mounted on %s",
+ bb_path_mtab_file,
+ mp->mnt_fsname, mp->mnt_dir);
+ }
+ } else {
+ // ...mount this thing
+ if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
+ // Count number of failed mounts
+ rc++;
+ }
}
free(mtcur->mnt_opts);
}
// End of fstab/mtab is reached.
// Were we looking for something specific?
- if (argv[0]) {
+ if (argv[0]) { // yes
long l;
// If we didn't find anything, complain
// fstab must have "users" or "user"
l = parse_mount_options(mtcur->mnt_opts, NULL);
if (!(l & MOUNT_USERS))
- bb_error_msg_and_die(must_be_root);
+ bb_error_msg_and_die(bb_msg_you_must_be_root);
}
- // Mount the last thing we found
- mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
- append_mount_options(&(mtcur->mnt_opts), cmdopts);
- resolve_mount_spec(&mtpair->mnt_fsname);
- rc = singlemount(mtcur, 0);
- if (ENABLE_FEATURE_CLEAN_UP)
- free(mtcur->mnt_opts);
+ //util-linux-2.12 does not do this check.
+ //// If nothing is mounted on this directory...
+ //// (otherwise repeated "mount FOO" mounts FOO again)
+ //mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
+ //if (mp) {
+ // bb_error_msg("according to %s, "
+ // "%s is already mounted on %s",
+ // bb_path_mtab_file,
+ // mp->mnt_fsname, mp->mnt_dir);
+ //} else {
+ // ...mount the last thing we found
+ mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+ append_mount_options(&(mtcur->mnt_opts), cmdopts);
+ resolve_mount_spec(&mtpair->mnt_fsname);
+ rc = singlemount(mtcur, /*ignore_busy:*/ 0);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(mtcur->mnt_opts);
+ //}
}
//ret:
free(storage_path);
free(cmdopts);
}
+
+//TODO: exitcode should be ORed mask of (from "man mount"):
+// 0 success
+// 1 incorrect invocation or permissions
+// 2 system error (out of memory, cannot fork, no more loop devices)
+// 4 internal mount bug or missing nfs support in mount
+// 8 user interrupt
+//16 problems writing or locking /etc/mtab
+//32 mount failure
+//64 some mount succeeded
return rc;
}