X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=util-linux%2Fmount.c;h=5e85f998641c473ef5f4e66c42dc394b245adc80;hb=38292b68c962b9d470fa4e577020749c8c69226d;hp=694057bbef7f9fd0dd01c00736165cab2f27d2e5;hpb=5c3299300905698c45d1ace64a61a431312993a4;p=oweals%2Fbusybox.git diff --git a/util-linux/mount.c b/util-linux/mount.c index 694057bbe..5e85f9986 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -6,9 +6,8 @@ * Copyright (C) 1999-2004 by Erik Andersen * Copyright (C) 2005-2006 by Rob Landley * - * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ - // Design notes: There is no spec for mount. Remind me to write one. // // mount_main() calls singlemount() which calls mount_it_now(). @@ -17,29 +16,65 @@ // singlemount() can loop through /etc/filesystems for fstype detection. // mount_it_now() does the actual mount. // - #include #include -#include "libbb.h" +#include +// 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 #undef TRUE #undef FALSE -#include -#include -#include - -#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 +#if ENABLE_FEATURE_MOUNT_NFS +/* 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" +# endif +# include +# include +# include #endif @@ -79,15 +114,21 @@ enum { }; #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 @@ -105,22 +146,22 @@ enum { static const int32_t mount_options[] = { // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs. - USE_FEATURE_MOUNT_LOOP( + IF_FEATURE_MOUNT_LOOP( /* "loop" */ 0, ) - USE_FEATURE_MOUNT_FSTAB( + IF_FEATURE_MOUNT_FSTAB( /* "defaults" */ 0, /* "quiet" 0 - do not filter out, vfat wants to see it */ /* "noauto" */ MOUNT_NOAUTO, /* "sw" */ MOUNT_SWAP, /* "swap" */ MOUNT_SWAP, - USE_DESKTOP(/* "user" */ MOUNT_USERS,) - USE_DESKTOP(/* "users" */ MOUNT_USERS,) + IF_DESKTOP(/* "user" */ MOUNT_USERS,) + IF_DESKTOP(/* "users" */ MOUNT_USERS,) /* "_netdev" */ 0, ) - USE_FEATURE_MOUNT_FLAGS( + IF_FEATURE_MOUNT_FLAGS( // vfs flags /* "nosuid" */ MS_NOSUID, /* "suid" */ ~MS_NOSUID, @@ -142,6 +183,7 @@ static const int32_t mount_options[] = { /* "loud" */ ~MS_SILENT, // action flags + /* "union" */ MS_UNION, /* "bind" */ MS_BIND, /* "move" */ MS_MOVE, /* "shared" */ MS_SHARED, @@ -161,20 +203,20 @@ static const int32_t mount_options[] = { }; static const char mount_option_str[] = - USE_FEATURE_MOUNT_LOOP( + IF_FEATURE_MOUNT_LOOP( "loop\0" ) - USE_FEATURE_MOUNT_FSTAB( + IF_FEATURE_MOUNT_FSTAB( "defaults\0" // "quiet\0" - do not filter out, vfat wants to see it "noauto\0" "sw\0" "swap\0" - USE_DESKTOP("user\0") - USE_DESKTOP("users\0") + IF_DESKTOP("user\0") + IF_DESKTOP("users\0") "_netdev\0" ) - USE_FEATURE_MOUNT_FLAGS( + IF_FEATURE_MOUNT_FLAGS( // vfs flags "nosuid\0" "suid\0" @@ -196,6 +238,7 @@ static const char mount_option_str[] = "loud\0" // action flags + "union\0" "bind\0" "move\0" "shared\0" @@ -224,8 +267,7 @@ struct globals { #endif llist_t *fslist; char getmntent_buf[1]; - -}; +} FIX_ALIASING; enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) }; #define G (*(struct globals*)&bb_common_bufsiz1) #define nfs_mount_version (G.nfs_mount_version) @@ -257,23 +299,6 @@ static int verbose_mount(const char *source, const char *target, #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) { @@ -360,7 +385,7 @@ static llist_t *get_block_backed_filesystems(void) "/proc/filesystems", }; char *fs, *buf; - llist_t *list = 0; + llist_t *list = NULL; int i; FILE *f; @@ -369,10 +394,11 @@ static llist_t *get_block_backed_filesystems(void) 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); @@ -398,7 +424,7 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts) { 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, @@ -414,11 +440,15 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts) // If mount failed, try // helper program mount. - 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) { @@ -426,7 +456,7 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts) args[rc++] = filteropts; } args[rc] = NULL; - rc = wait4pid(spawn(args)); + rc = spawn_and_wait(args); free(args[0]); if (!rc) break; @@ -449,7 +479,7 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *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; @@ -502,7 +532,7 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts) * Linux NFS mount * Copyright (C) 1993 Rick Sladkey * - * Licensed under GPLv2, see file LICENSE in this tarball for details. + * Licensed under GPLv2, see file LICENSE in this source tree. * * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port * numbers to be specified on the command line. @@ -520,12 +550,6 @@ static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts) * plus NFSv3 stuff. */ -/* 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." -#endif - #define MOUNTPORT 635 #define MNTPATHLEN 1024 #define MNTNAMLEN 255 @@ -716,28 +740,35 @@ enum { * "after #include the symbol errno is reserved for any use, * it cannot even be used as a struct tag or field name". */ - #ifndef EDQUOT -#define EDQUOT ENOSPC +# define EDQUOT ENOSPC #endif - /* Convert each NFSERR_BLAH into EBLAH */ -static const struct { - short stat; - short errnum; -} nfs_errtbl[] = { - {0,0}, {1,EPERM}, {2,ENOENT}, {5,EIO}, {6,ENXIO}, {13,EACCES}, {17,EEXIST}, - {19,ENODEV}, {20,ENOTDIR}, {21,EISDIR}, {22,EINVAL}, {27,EFBIG}, - {28,ENOSPC}, {30,EROFS}, {63,ENAMETOOLONG}, {66,ENOTEMPTY}, {69,EDQUOT}, - {70,ESTALE}, {71,EREMOTE}, {-1,EIO} +static const uint8_t nfs_err_stat[] = { + 1, 2, 5, 6, 13, 17, + 19, 20, 21, 22, 27, 28, + 30, 63, 66, 69, 70, 71 +}; +#if ( \ + EPERM | ENOENT | EIO | ENXIO | EACCES| EEXIST | \ + ENODEV| ENOTDIR | EISDIR | EINVAL| EFBIG | ENOSPC | \ + EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256 +typedef uint8_t nfs_err_type; +#else +typedef uint16_t nfs_err_type; +#endif +static const nfs_err_type nfs_err_errnum[] = { + EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST, + ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC, + EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE }; static char *nfs_strerror(int status) { int i; - for (i = 0; nfs_errtbl[i].stat != -1; i++) { - if (nfs_errtbl[i].stat == status) - return strerror(nfs_errtbl[i].errnum); + for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) { + if (nfs_err_stat[i] == status) + return strerror(nfs_err_errnum[i]); } return xasprintf("unknown nfs status return value: %d", status); } @@ -773,8 +804,12 @@ static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp) static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp) { - if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3)) + if (!xdr_bytes(xdrs, (char **)&objp->fhandle3_val, + (unsigned int *) &objp->fhandle3_len, + FHSIZE3) + ) { return FALSE; + } return TRUE; } @@ -782,9 +817,14 @@ static bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp) { 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)) + if (!xdr_array(xdrs, &(objp->auth_flavours.auth_flavours_val), + &(objp->auth_flavours.auth_flavours_len), + ~0, + sizeof(int), + (xdrproc_t) xdr_int) + ) { return FALSE; + } return TRUE; } @@ -835,11 +875,7 @@ find_kernel_nfs_mount_version(void) kernel_version = get_linux_version_code(); if (kernel_version) { - if (kernel_version < KERNEL_VERSION(2,1,32)) - nfs_mount_version = 1; - else if (kernel_version < KERNEL_VERSION(2,2,18) || - (kernel_version >= KERNEL_VERSION(2,3,0) && - kernel_version < KERNEL_VERSION(2,3,99))) + if (kernel_version < KERNEL_VERSION(2,2,18)) nfs_mount_version = 3; /* else v4 since 2.3.99pre4 */ } @@ -878,10 +914,12 @@ get_mountport(struct pmap *pm_mnt, goto next; if (version && version <= 2 && pmap->pml_map.pm_vers > 2) goto next; - if (pmap->pml_map.pm_vers > MAX_NFSPROT || - (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) || - (port && pmap->pml_map.pm_port != port)) + if (pmap->pml_map.pm_vers > MAX_NFSPROT + || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) + || (port && pmap->pml_map.pm_port != port) + ) { goto next; + } memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt)); next: pmap = pmap->pml_next; @@ -936,12 +974,15 @@ static void error_msg_rpc(const char *msg) } /* 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; @@ -968,7 +1009,7 @@ static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts) 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; @@ -1010,12 +1051,10 @@ static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts) bb_herror_msg("%s", hostname); goto fail; } - if ((size_t)hp->h_length > sizeof(struct in_addr)) { - bb_error_msg("got bad hp->h_length"); - hp->h_length = sizeof(struct in_addr); + if (hp->h_length != (int)sizeof(struct in_addr)) { + bb_error_msg_and_die("only IPv4 is supported"); } - memcpy(&server_addr.sin_addr, - hp->h_addr, hp->h_length); + memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); } memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); @@ -1108,7 +1147,7 @@ static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts) continue; } - val = xatoi_u(opteq); + val = xatoi_positive(opteq); switch (idx) { case 0: // "rsize" data.rsize = val; @@ -1298,13 +1337,11 @@ static int nfsmount(struct mntent *mp, long vfsflags, char *filteropts) bb_herror_msg("%s", mounthost); goto fail; } - if ((size_t)hp->h_length > sizeof(struct in_addr)) { - bb_error_msg("got bad hp->h_length"); - hp->h_length = sizeof(struct in_addr); + if (hp->h_length != (int)sizeof(struct in_addr)) { + bb_error_msg_and_die("only IPv4 is supported"); } mount_server_addr.sin_family = AF_INET; - memcpy(&mount_server_addr.sin_addr, - hp->h_addr, hp->h_length); + memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); } } @@ -1567,10 +1604,12 @@ static int singlemount(struct mntent *mp, int ignore_busy) { 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 @@ -1578,24 +1617,31 @@ static int singlemount(struct mntent *mp, int ignore_busy) 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)); + rc = spawn_and_wait(args); goto report_error; } @@ -1605,52 +1651,41 @@ static int singlemount(struct mntent *mp, int ignore_busy) && (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); @@ -1668,11 +1703,11 @@ static int singlemount(struct mntent *mp, int ignore_busy) 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; } @@ -1683,15 +1718,15 @@ static int singlemount(struct mntent *mp, int ignore_busy) // If we know the fstype (or don't need to), jump straight // to the actual mount. - if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) + if (mp->mnt_type || (vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))) { rc = mount_it_now(mp, vfsflags, filteropts); - else { + } else { // 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) @@ -1701,7 +1736,8 @@ static int singlemount(struct mntent *mp, int ignore_busy) 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; } } @@ -1720,54 +1756,70 @@ static int singlemount(struct mntent *mp, int ignore_busy) if (errno == EBUSY && ignore_busy) return 0; - if (rc < 0) + if (rc != 0) bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir); 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) { @@ -1778,12 +1830,13 @@ 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; - SKIP_DESKTOP(const int nonroot = 0;) + IF_NOT_DESKTOP(const int nonroot = 0;) - USE_DESKTOP(int nonroot = ) sanitize_env_if_suid(); + IF_DESKTOP(int nonroot = ) sanitize_env_if_suid(); // Parse long options, like --bind and --move. Note that -o option // and --option are synonymous. Yes, this means --remount,rw works. @@ -1797,9 +1850,9 @@ int mount_main(int argc UNUSED_PARAM, char **argv) // Parse remaining options // Max 2 params; -o is a list, -v is a counter - opt_complementary = "?2o::" USE_FEATURE_MOUNT_VERBOSE("vv"); + opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv"); opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch - USE_FEATURE_MOUNT_VERBOSE(, &verbose)); + 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 @@ -1818,9 +1871,9 @@ int mount_main(int argc UNUSED_PARAM, char **argv) { // 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); @@ -1836,13 +1889,13 @@ int mount_main(int argc UNUSED_PARAM, char **argv) // 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 @@ -1853,7 +1906,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv) 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 @@ -1876,7 +1929,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv) } 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)); @@ -1898,10 +1951,13 @@ int mount_main(int argc UNUSED_PARAM, char **argv) 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. @@ -1909,16 +1965,17 @@ int mount_main(int argc UNUSED_PARAM, char **argv) // 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 @@ -1936,10 +1993,25 @@ int mount_main(int argc UNUSED_PARAM, char **argv) // 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); } @@ -1947,7 +2019,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv) // 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 @@ -1977,16 +2049,27 @@ int mount_main(int argc UNUSED_PARAM, char **argv) // 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: @@ -1996,5 +2079,15 @@ int mount_main(int argc UNUSED_PARAM, char **argv) 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; }