X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=util-linux%2Fmount.c;h=52ced7acdba7142b76acced818f251c9dc2b8a4d;hb=cc428147c17a57d62d89b998debc1f4bd094ad56;hp=d391a26e19991619ea7942d661b8b3ba8acf628c;hpb=2535f12cf21b3dd481d59ea1678f9dc05c404efe;p=oweals%2Fbusybox.git diff --git a/util-linux/mount.c b/util-linux/mount.c index d391a26e1..52ced7acd 100644 --- a/util-linux/mount.c +++ b/util-linux/mount.c @@ -8,52 +8,130 @@ * * 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(). - - mount_main() can loop through /etc/fstab for mount -a - singlemount() can loop through /etc/filesystems for fstype detection. - mount_it_now() does the actual mount. -*/ - +// Design notes: There is no spec for mount. Remind me to write one. +// +// mount_main() calls singlemount() which calls mount_it_now(). +// +// mount_main() can loop through /etc/fstab for mount -a +// singlemount() can loop through /etc/filesystems for fstype detection. +// mount_it_now() does the actual mount. +// #include +#include +#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" +#else +# define resolve_mount_spec(fsname) ((void)0) +#endif -/* Needed for nfs support only... */ -#include +// Needed for nfs support only #include #undef TRUE #undef FALSE -#include -#include -#include - -#ifndef MS_SILENT -#define MS_SILENT (1 << 15) +#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 + #if defined(__dietlibc__) -/* 16.12.2006, Sampo Kellomaki (sampo@iki.fi) - * dietlibc-0.30 does not have implementation of getmntent_r() */ -struct mntent *getmntent_r(FILE* stream, struct mntent* result, char* buffer, int bufsize) +// 16.12.2006, Sampo Kellomaki (sampo@iki.fi) +// dietlibc-0.30 does not have implementation of getmntent_r() +static struct mntent *getmntent_r(FILE* stream, struct mntent* result, + char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM) { struct mntent* ment = getmntent(stream); - memcpy(result, ment, sizeof(struct mntent)); - return result; + return memcpy(result, ment, sizeof(*ment)); } #endif -#define getmntent_buf bb_common_bufsiz1 - // Not real flags, but we want to be able to check for this. enum { - MOUNT_USERS = (1<<28)*ENABLE_DESKTOP, - MOUNT_NOAUTO = (1<<29), - MOUNT_SWAP = (1<<30), + MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP, + MOUNT_NOAUTO = (1 << 29), + MOUNT_SWAP = (1 << 30), +}; + + +#define OPTION_STR "o:t:rwanfvsiO:" +enum { + OPT_o = (1 << 0), + OPT_t = (1 << 1), + OPT_r = (1 << 2), + OPT_w = (1 << 3), + OPT_a = (1 << 4), + OPT_n = (1 << 5), + OPT_f = (1 << 6), + OPT_v = (1 << 7), + OPT_s = (1 << 8), + OPT_i = (1 << 9), + OPT_O = (1 << 10), }; + +#if ENABLE_FEATURE_MTAB_SUPPORT +#define USE_MTAB (!(option_mask32 & OPT_n)) +#else +#define USE_MTAB 0 +#endif + +#if ENABLE_FEATURE_MOUNT_FAKE +#define FAKE_IT (option_mask32 & OPT_f) +#else +#define FAKE_IT 0 +#endif + +#if ENABLE_FEATURE_MOUNT_HELPERS +#define HELPERS_ALLOWED (!(option_mask32 & OPT_i)) +#else +#define HELPERS_ALLOWED 0 +#endif + + // TODO: more "user" flag compatibility. // "user" option (from mount manpage): // Only the user that mounted a filesystem can unmount it again. @@ -63,72 +141,170 @@ enum { // This may be useful e.g. for /dev/fd if a login script makes // the console user owner of this device. -/* Standard mount options (from -o options or --options), with corresponding - * flags */ - -struct { - const char *name; - long flags; -} static mount_options[] = { +// Standard mount options (from -o options or --options), +// with corresponding flags +static const int32_t mount_options[] = { // MS_FLAGS set a bit. ~MS_FLAGS disable that bit. 0 flags are NOPs. - USE_FEATURE_MOUNT_LOOP( - {"loop", 0}, + IF_FEATURE_MOUNT_LOOP( + /* "loop" */ 0, ) - USE_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_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, + 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}, - {"dev", ~MS_NODEV}, - {"nodev", MS_NODEV}, - {"exec", ~MS_NOEXEC}, - {"noexec", MS_NOEXEC}, - {"sync", MS_SYNCHRONOUS}, - {"async", ~MS_SYNCHRONOUS}, - {"atime", ~MS_NOATIME}, - {"noatime", MS_NOATIME}, - {"diratime", ~MS_NODIRATIME}, - {"nodiratime", MS_NODIRATIME}, - {"loud", ~MS_SILENT}, + /* "nosuid" */ MS_NOSUID, + /* "suid" */ ~MS_NOSUID, + /* "dev" */ ~MS_NODEV, + /* "nodev" */ MS_NODEV, + /* "exec" */ ~MS_NOEXEC, + /* "noexec" */ MS_NOEXEC, + /* "sync" */ MS_SYNCHRONOUS, + /* "dirsync" */ MS_DIRSYNC, + /* "async" */ ~MS_SYNCHRONOUS, + /* "atime" */ ~MS_NOATIME, + /* "noatime" */ MS_NOATIME, + /* "diratime" */ ~MS_NODIRATIME, + /* "nodiratime" */ MS_NODIRATIME, + /* "mand" */ MS_MANDLOCK, + /* "nomand" */ ~MS_MANDLOCK, + /* "relatime" */ MS_RELATIME, + /* "norelatime" */ ~MS_RELATIME, + /* "loud" */ ~MS_SILENT, // action flags + /* "union" */ MS_UNION, + /* "bind" */ MS_BIND, + /* "move" */ MS_MOVE, + /* "shared" */ MS_SHARED, + /* "slave" */ MS_SLAVE, + /* "private" */ MS_PRIVATE, + /* "unbindable" */ MS_UNBINDABLE, + /* "rshared" */ MS_SHARED|MS_RECURSIVE, + /* "rslave" */ MS_SLAVE|MS_RECURSIVE, + /* "rprivate" */ MS_SLAVE|MS_RECURSIVE, + /* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE, + ) + + // Always understood. + /* "ro" */ MS_RDONLY, // vfs flag + /* "rw" */ ~MS_RDONLY, // vfs flag + /* "remount" */ MS_REMOUNT // action flag +}; - {"bind", MS_BIND}, - {"move", MS_MOVE}, - {"shared", MS_SHARED}, - {"slave", MS_SLAVE}, - {"private", MS_PRIVATE}, - {"unbindable", MS_UNBINDABLE}, - {"rshared", MS_SHARED|MS_RECURSIVE}, - {"rslave", MS_SLAVE|MS_RECURSIVE}, - {"rprivate", MS_SLAVE|MS_RECURSIVE}, - {"runbindable", MS_UNBINDABLE|MS_RECURSIVE}, +static const char mount_option_str[] = + IF_FEATURE_MOUNT_LOOP( + "loop\0" + ) + IF_FEATURE_MOUNT_FSTAB( + "defaults\0" + // "quiet\0" - do not filter out, vfat wants to see it + "noauto\0" + "sw\0" + "swap\0" + IF_DESKTOP("user\0") + IF_DESKTOP("users\0") + "_netdev\0" + ) + IF_FEATURE_MOUNT_FLAGS( + // vfs flags + "nosuid\0" + "suid\0" + "dev\0" + "nodev\0" + "exec\0" + "noexec\0" + "sync\0" + "dirsync\0" + "async\0" + "atime\0" + "noatime\0" + "diratime\0" + "nodiratime\0" + "mand\0" + "nomand\0" + "relatime\0" + "norelatime\0" + "loud\0" + + // action flags + "union\0" + "bind\0" + "move\0" + "shared\0" + "slave\0" + "private\0" + "unbindable\0" + "rshared\0" + "rslave\0" + "rprivate\0" + "runbindable\0" ) // Always understood. + "ro\0" // vfs flag + "rw\0" // vfs flag + "remount\0" // action flag +; + + +struct globals { +#if ENABLE_FEATURE_MOUNT_NFS + smalluint nfs_mount_version; +#endif +#if ENABLE_FEATURE_MOUNT_VERBOSE + unsigned verbose; +#endif + llist_t *fslist; + char getmntent_buf[1]; - {"ro", MS_RDONLY}, // vfs flag - {"rw", ~MS_RDONLY}, // vfs flag - {"remount", MS_REMOUNT}, // action flag }; +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) +#if ENABLE_FEATURE_MOUNT_VERBOSE +#define verbose (G.verbose ) +#else +#define verbose 0 +#endif +#define fslist (G.fslist ) +#define getmntent_buf (G.getmntent_buf ) -/* Append mount options to string */ +#if ENABLE_FEATURE_MOUNT_VERBOSE +static int verbose_mount(const char *source, const char *target, + const char *filesystemtype, + unsigned long mountflags, const void *data) +{ + int rc; + + errno = 0; + rc = mount(source, target, filesystemtype, mountflags, data); + if (verbose >= 2) + bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d", + source, target, filesystemtype, + mountflags, (char*)data, rc); + return rc; +} +#else +#define verbose_mount(...) mount(__VA_ARGS__) +#endif + +// Append mount options to string static void append_mount_options(char **oldopts, const char *newopts) { if (*oldopts && **oldopts) { - /* do not insert options which are already there */ + // Do not insert options which are already there while (newopts[0]) { char *p; int len = strlen(newopts); @@ -137,7 +313,7 @@ static void append_mount_options(char **oldopts, const char *newopts) p = *oldopts; while (1) { if (!strncmp(p, newopts, len) - && (p[len]==',' || p[len]==0)) + && (p[len] == ',' || p[len] == '\0')) goto skip; p = strchr(p,','); if (!p) break; @@ -146,7 +322,7 @@ static void append_mount_options(char **oldopts, const char *newopts) p = xasprintf("%s,%.*s", *oldopts, len, newopts); free(*oldopts); *oldopts = p; -skip: + skip: newopts += len; while (newopts[0] == ',') newopts++; } @@ -156,33 +332,36 @@ skip: } } -/* Use the mount_options list to parse options into flags. - * Also return list of unrecognized options if unrecognized!=NULL */ -static int parse_mount_options(char *options, char **unrecognized) +// Use the mount_options list to parse options into flags. +// Also return list of unrecognized options if unrecognized != NULL +static long parse_mount_options(char *options, char **unrecognized) { - int flags = MS_SILENT; + long flags = MS_SILENT; // Loop through options for (;;) { - int i; + unsigned i; char *comma = strchr(options, ','); + const char *option_str = mount_option_str; - if (comma) *comma = 0; + if (comma) *comma = '\0'; +// FIXME: use hasmntopt() // Find this option in mount_options for (i = 0; i < ARRAY_SIZE(mount_options); i++) { - if (!strcasecmp(mount_options[i].name, options)) { - long fl = mount_options[i].flags; + if (!strcasecmp(option_str, options)) { + long fl = mount_options[i]; if (fl < 0) flags &= fl; else flags |= fl; break; } + option_str += strlen(option_str) + 1; } - // If unrecognized not NULL, append unrecognized mount options */ + // If unrecognized not NULL, append unrecognized mount options if (unrecognized && i == ARRAY_SIZE(mount_options)) { // Add it to strflags, to pass on to kernel i = *unrecognized ? strlen(*unrecognized) : 0; - *unrecognized = xrealloc(*unrecognized, i+strlen(options)+2); + *unrecognized = xrealloc(*unrecognized, i + strlen(options) + 2); // Comma separated if it's not the first one if (i) (*unrecognized)[i++] = ','; @@ -200,7 +379,6 @@ static int parse_mount_options(char *options, char **unrecognized) } // Return a list of all block device backed filesystems - static llist_t *get_block_backed_filesystems(void) { static const char filesystems[2][sizeof("/proc/filesystems")] = { @@ -208,19 +386,20 @@ 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; for (i = 0; i < 2; i++) { - f = fopen(filesystems[i], "r"); + f = fopen_for_read(filesystems[i]); if (!f) continue; - while ((buf = xmalloc_getline(f)) != 0) { - if (!strncmp(buf, "nodev", 5) && isspace(buf[5])) + while ((buf = xmalloc_fgetline(f)) != NULL) { + 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); @@ -231,8 +410,6 @@ static llist_t *get_block_backed_filesystems(void) return list; } -llist_t *fslist = 0; - #if ENABLE_FEATURE_CLEAN_UP static void delete_block_backed_filesystems(void) { @@ -242,28 +419,52 @@ static void delete_block_backed_filesystems(void) void delete_block_backed_filesystems(void); #endif -#if ENABLE_FEATURE_MTAB_SUPPORT -static int useMtab = 1; -static int fakeIt; -#else -#define useMtab 0 -#define fakeIt 0 -#endif - // Perform actual mount of specific filesystem at specific location. // NB: mp->xxx fields may be trashed on exit -static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts) +static int mount_it_now(struct mntent *mp, long vfsflags, char *filteropts) { int rc = 0; - if (fakeIt) goto mtab; + 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, + vfsflags, filteropts); + goto mtab; + } // Mount, with fallback to read-only if necessary. - for (;;) { - rc = mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, + errno = 0; + rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type, vfsflags, filteropts); - if (!rc || (vfsflags&MS_RDONLY) || (errno!=EACCES && errno!=EROFS)) + + // If mount failed, try + // helper program mount. + 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) { + args[rc++] = (char *)"-o"; + args[rc++] = filteropts; + } + args[rc] = NULL; + rc = wait4pid(spawn(args)); + free(args[0]); + if (!rc) + break; + errno = errno_save; + } + + if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS)) break; if (!(vfsflags & MS_SILENT)) bb_error_msg("%s is write-protected, mounting read-only", @@ -276,35 +477,38 @@ static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts) if (rc && errno == EPERM) bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); - /* If the mount was successful, and we're maintaining an old-style - * mtab file by hand, add the new entry to it now. */ + // 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 (ENABLE_FEATURE_MTAB_SUPPORT && 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; int i; if (!mountTable) { - bb_error_msg("no %s",bb_path_mtab_file); + bb_error_msg("no %s", bb_path_mtab_file); goto ret; } // Add vfs string flags - for (i=0; mount_options[i].flags != MS_REMOUNT; i++) - if (mount_options[i].flags > 0 && (mount_options[i].flags & vfsflags)) - append_mount_options(&(mp->mnt_opts), mount_options[i].name); + for (i = 0; mount_options[i] != MS_REMOUNT; i++) { + if (mount_options[i] > 0 && (mount_options[i] & vfsflags)) + append_mount_options(&(mp->mnt_opts), option_str); + option_str += strlen(option_str) + 1; + } // Remove trailing / (if any) from directory we mounted on i = strlen(mp->mnt_dir) - 1; - if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = 0; + if (i > 0 && mp->mnt_dir[i] == '/') mp->mnt_dir[i] = '\0'; // Convert to canonical pathnames as needed mp->mnt_dir = bb_simplify_path(mp->mnt_dir); fsname = 0; - if (!mp->mnt_type || !*mp->mnt_type) { /* bind mount */ + if (!mp->mnt_type || !*mp->mnt_type) { // bind mount mp->mnt_fsname = fsname = bb_simplify_path(mp->mnt_fsname); mp->mnt_type = (char*)"bind"; } @@ -340,19 +544,13 @@ static int mount_it_now(struct mntent *mp, int vfsflags, char *filteropts) * Wed Oct 1 23:55:28 1997: Dick Streefland * Implemented the "bg", "fg" and "retry" mount options for NFS. * - * 1999-02-22 Arkadiusz Mi¶kiewicz + * 1999-02-22 Arkadiusz Mickiewicz * - added Native Language Support * * Modified by Olaf Kirch and Trond Myklebust for new NFS code, * 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 @@ -530,7 +728,8 @@ enum { NFS_MOUNT_TCP = 0x0040, /* 2 */ NFS_MOUNT_VER3 = 0x0080, /* 3 */ NFS_MOUNT_KERBEROS = 0x0100, /* 3 */ - NFS_MOUNT_NONLM = 0x0200 /* 3 */ + NFS_MOUNT_NONLM = 0x0200, /* 3 */ + NFS_MOUNT_NORDIRPLUS = 0x4000 }; @@ -542,34 +741,29 @@ 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 { - int stat; - int 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} +/* Convert each NFSERR_BLAH into EBLAH */ +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 +}; +static const uint8_t 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; - static char buf[sizeof("unknown nfs status return value: ") + sizeof(int)*3]; - 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]); } - sprintf(buf, "unknown nfs status return value: %d", status); - return buf; + return xasprintf("unknown nfs status return value: %d", status); } static bool_t xdr_fhandle(XDR *xdrs, fhandle objp) @@ -603,8 +797,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; } @@ -612,9 +810,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; } @@ -642,12 +845,6 @@ static bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp) #define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) -/* - * nfs_mount_version according to the sources seen at compile time. - */ -static int nfs_mount_version; -static int kernel_version; - /* * Unfortunately, the kernel prints annoying console messages * in case of an unexpected nfs mount version (instead of @@ -662,32 +859,30 @@ static int kernel_version; static void find_kernel_nfs_mount_version(void) { - if (kernel_version) + int kernel_version; + + if (nfs_mount_version) return; nfs_mount_version = 4; /* default */ 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 */ } } -static struct pmap * -get_mountport(struct sockaddr_in *server_addr, +static void +get_mountport(struct pmap *pm_mnt, + struct sockaddr_in *server_addr, long unsigned prog, long unsigned version, long unsigned proto, long unsigned port) { struct pmaplist *pmap; - static struct pmap p = {0, 0, 0, 0}; server_addr->sin_port = PMAPPORT; /* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *). @@ -698,52 +893,49 @@ get_mountport(struct sockaddr_in *server_addr, version = MAX_NFSPROT; if (!prog) prog = MOUNTPROG; - p.pm_prog = prog; - p.pm_vers = version; - p.pm_prot = proto; - p.pm_port = port; + pm_mnt->pm_prog = prog; + pm_mnt->pm_vers = version; + pm_mnt->pm_prot = proto; + pm_mnt->pm_port = port; while (pmap) { if (pmap->pml_map.pm_prog != prog) goto next; - if (!version && p.pm_vers > pmap->pml_map.pm_vers) + if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers) goto next; if (version > 2 && pmap->pml_map.pm_vers != version) goto next; if (version && version <= 2 && pmap->pml_map.pm_vers > 2) goto next; if (pmap->pml_map.pm_vers > MAX_NFSPROT || - (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) || + (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto) || (port && pmap->pml_map.pm_port != port)) goto next; - memcpy(&p, &pmap->pml_map, sizeof(p)); -next: + memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt)); + next: pmap = pmap->pml_next; } - if (!p.pm_vers) - p.pm_vers = MOUNTVERS; - if (!p.pm_port) - p.pm_port = MOUNTPORT; - if (!p.pm_prot) - p.pm_prot = IPPROTO_TCP; - return &p; + if (!pm_mnt->pm_vers) + pm_mnt->pm_vers = MOUNTVERS; + if (!pm_mnt->pm_port) + pm_mnt->pm_port = MOUNTPORT; + if (!pm_mnt->pm_prot) + pm_mnt->pm_prot = IPPROTO_TCP; } #if BB_MMU static int daemonize(void) { - int fd; int pid = fork(); if (pid < 0) /* error */ return -errno; if (pid > 0) /* parent */ return 0; /* child */ - fd = xopen(bb_dev_null, O_RDWR); - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - while (fd > 2) close(fd--); + close(0); + xopen(bb_dev_null, O_RDWR); + xdup2(0, 1); + xdup2(0, 2); setsid(); openlog(applet_name, LOG_PID, LOG_DAEMON); logmode = LOGMODE_SYSLOG; @@ -753,8 +945,8 @@ static int daemonize(void) static inline int daemonize(void) { return -ENOSYS; } #endif -// TODO -static inline int we_saw_this_host_before(const char *hostname) +/* TODO */ +static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM) { return 0; } @@ -772,13 +964,16 @@ static void error_msg_rpc(const char *msg) bb_error_msg("%.*s", len, msg); } -// NB: mp->xxx fields may be trashed on exit -static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) +/* NB: mp->xxx fields may be trashed on exit */ +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; @@ -795,23 +990,25 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) int mountport; int proto; #if BB_MMU - int bg = 0; + smallint bg = 0; #else enum { bg = 0 }; #endif - int soft; - int intr; - int posix; - int nocto; - int noac; - int nolock; int retry; - int tcp; int mountprog; int mountvers; int nfsprog; int nfsvers; int retval; + /* these all are one-bit really. gcc 4.3.1 likes this combination: */ + smallint tcp; + smallint soft; + int intr; + int posix; + int nocto; + int noac; + int nordirplus; + int nolock; find_kernel_nfs_mount_version(); @@ -845,7 +1042,7 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) bb_herror_msg("%s", hostname); goto fail; } - if (hp->h_length > sizeof(struct in_addr)) { + 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); } @@ -872,12 +1069,12 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) * let the kernel decide. * timeo is filled in after we know whether it'll be TCP or UDP. */ memset(&data, 0, sizeof(data)); - data.retrans = 3; - data.acregmin = 3; - data.acregmax = 60; - data.acdirmin = 30; - data.acdirmax = 60; - data.namlen = NAME_MAX; + data.retrans = 3; + data.acregmin = 3; + data.acregmax = 60; + data.acdirmin = 30; + data.acdirmax = 60; + data.namlen = NAME_MAX; soft = 0; intr = 0; @@ -885,6 +1082,7 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) nocto = 0; nolock = 0; noac = 0; + nordirplus = 0; retry = 10000; /* 10000 minutes ~ 1 week */ tcp = 0; @@ -899,6 +1097,7 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { char *opteq = strchr(opt, '='); if (opteq) { + int val, idx; static const char options[] ALIGN1 = /* 0 */ "rsize\0" /* 1 */ "wsize\0" @@ -921,87 +1120,92 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) /* 18 */ "proto\0" /* 19 */ "namlen\0" /* 20 */ "addr\0"; - int val = xatoi_u(opteq + 1); - *opteq = '\0'; - switch (index_in_strings(options, opt)) { + + *opteq++ = '\0'; + idx = index_in_strings(options, opt); + switch (idx) { + case 12: // "mounthost" + mounthost = xstrndup(opteq, + strcspn(opteq, " \t\n\r,")); + continue; + case 18: // "proto" + if (!strncmp(opteq, "tcp", 3)) + tcp = 1; + else if (!strncmp(opteq, "udp", 3)) + tcp = 0; + else + bb_error_msg("warning: unrecognized proto= option"); + continue; + case 20: // "addr" - ignore + continue; + } + + val = xatoi_u(opteq); + switch (idx) { case 0: // "rsize" data.rsize = val; - break; + continue; case 1: // "wsize" data.wsize = val; - break; + continue; case 2: // "timeo" data.timeo = val; - break; + continue; case 3: // "retrans" data.retrans = val; - break; + continue; case 4: // "acregmin" data.acregmin = val; - break; + continue; case 5: // "acregmax" data.acregmax = val; - break; + continue; case 6: // "acdirmin" data.acdirmin = val; - break; + continue; case 7: // "acdirmax" data.acdirmax = val; - break; + continue; case 8: // "actimeo" data.acregmin = val; data.acregmax = val; data.acdirmin = val; data.acdirmax = val; - break; + continue; case 9: // "retry" retry = val; - break; + continue; case 10: // "port" port = val; - break; + continue; case 11: // "mountport" mountport = val; - break; - case 12: // "mounthost" - mounthost = xstrndup(opteq+1, - strcspn(opteq+1," \t\n\r,")); - break; + continue; case 13: // "mountprog" mountprog = val; - break; + continue; case 14: // "mountvers" mountvers = val; - break; + continue; case 15: // "nfsprog" nfsprog = val; - break; + continue; case 16: // "nfsvers" case 17: // "vers" nfsvers = val; - break; - case 18: // "proto" - if (!strncmp(opteq+1, "tcp", 3)) - tcp = 1; - else if (!strncmp(opteq+1, "udp", 3)) - tcp = 0; - else - bb_error_msg("warning: unrecognized proto= option"); - break; + continue; case 19: // "namlen" - if (nfs_mount_version >= 2) + //if (nfs_mount_version >= 2) data.namlen = val; - else - bb_error_msg("warning: option namlen is not supported\n"); - break; - case 20: // "addr" - ignore - break; + //else + // bb_error_msg("warning: option namlen is not supported\n"); + continue; default: bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val); goto fail; } } - else { + else { /* not of the form opt=val */ static const char options[] ALIGN1 = "bg\0" "fg\0" @@ -1013,7 +1217,8 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) "ac\0" "tcp\0" "udp\0" - "lock\0"; + "lock\0" + "rdirplus\0"; int val = 1; if (!strncmp(opt, "no", 2)) { val = 0; @@ -1060,6 +1265,9 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) else bb_error_msg("warning: option nolock is not supported"); break; + case 11: //rdirplus + nordirplus = !val; + break; default: bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt); goto fail; @@ -1072,7 +1280,8 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) | (intr ? NFS_MOUNT_INTR : 0) | (posix ? NFS_MOUNT_POSIX : 0) | (nocto ? NFS_MOUNT_NOCTO : 0) - | (noac ? NFS_MOUNT_NOAC : 0); + | (noac ? NFS_MOUNT_NOAC : 0) + | (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0); if (nfs_mount_version >= 2) data.flags |= (tcp ? NFS_MOUNT_TCP : 0); if (nfs_mount_version >= 3) @@ -1109,7 +1318,7 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) } } - /* create mount daemon client */ + /* Create mount daemon client */ /* See if the nfs host = mount host. */ if (mounthost) { if (mounthost[0] >= '0' && mounthost[0] <= '9') { @@ -1120,15 +1329,14 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) if (hp == NULL) { bb_herror_msg("%s", mounthost); goto fail; - } else { - if (hp->h_length > sizeof(struct in_addr)) { - bb_error_msg("got bad hp->h_length?"); - hp->h_length = sizeof(struct in_addr); - } - mount_server_addr.sin_family = AF_INET; - memcpy(&mount_server_addr.sin_addr, - hp->h_addr, hp->h_length); } + 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); + } + mount_server_addr.sin_family = AF_INET; + memcpy(&mount_server_addr.sin_addr, + hp->h_addr, hp->h_length); } } @@ -1147,7 +1355,7 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) { struct timeval total_timeout; struct timeval retry_timeout; - struct pmap* pm_mnt; + struct pmap pm_mnt; time_t t; time_t prevt; time_t timeout; @@ -1156,40 +1364,41 @@ static int nfsmount(struct mntent *mp, int vfsflags, char *filteropts) retry_timeout.tv_usec = 0; total_timeout.tv_sec = 20; total_timeout.tv_usec = 0; +/* FIXME: use monotonic()? */ timeout = time(NULL) + 60 * retry; prevt = 0; t = 30; -retry: - /* be careful not to use too many CPU cycles */ + retry: + /* Be careful not to use too many CPU cycles */ if (t - prevt < 30) sleep(30); - pm_mnt = get_mountport(&mount_server_addr, + get_mountport(&pm_mnt, &mount_server_addr, mountprog, mountvers, proto, mountport); - nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers; + nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers; /* contact the mount daemon via TCP */ - mount_server_addr.sin_port = htons(pm_mnt->pm_port); + mount_server_addr.sin_port = htons(pm_mnt.pm_port); msock = RPC_ANYSOCK; - switch (pm_mnt->pm_prot) { + switch (pm_mnt.pm_prot) { case IPPROTO_UDP: mclient = clntudp_create(&mount_server_addr, - pm_mnt->pm_prog, - pm_mnt->pm_vers, + pm_mnt.pm_prog, + pm_mnt.pm_vers, retry_timeout, &msock); if (mclient) break; - mount_server_addr.sin_port = htons(pm_mnt->pm_port); + mount_server_addr.sin_port = htons(pm_mnt.pm_port); msock = RPC_ANYSOCK; case IPPROTO_TCP: mclient = clnttcp_create(&mount_server_addr, - pm_mnt->pm_prog, - pm_mnt->pm_vers, + pm_mnt.pm_prog, + pm_mnt.pm_vers, &msock, 0, 0); break; default: @@ -1200,15 +1409,16 @@ retry: error_msg_rpc(clnt_spcreateerror(" ")); } else { enum clnt_stat clnt_stat; - /* try to mount hostname:pathname */ + + /* Try to mount hostname:pathname */ mclient->cl_auth = authunix_create_default(); - /* make pointers in xdr_mountres3 NULL so + /* Make pointers in xdr_mountres3 NULL so * that xdr_array allocates memory for us */ memset(&status, 0, sizeof(status)); - if (pm_mnt->pm_vers == 3) + if (pm_mnt.pm_vers == 3) clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT, (xdrproc_t) xdr_dirpath, (caddr_t) &pathname, @@ -1240,7 +1450,6 @@ retry: } /* Timeout. We are going to retry... maybe */ - if (!bg) goto fail; if (!daemonized) { @@ -1259,7 +1468,7 @@ retry: goto retry; } -prepare_kernel_data: + prepare_kernel_data: if (nfsvers == 2) { if (status.nfsv2.fhs_status != 0) { @@ -1294,8 +1503,7 @@ prepare_kernel_data: data.flags |= NFS_MOUNT_VER3; } - /* create nfs socket for kernel */ - + /* Create nfs socket for kernel */ if (tcp) { if (nfs_mount_version < 3) { bb_error_msg("NFS over TCP is not supported"); @@ -1321,14 +1529,12 @@ prepare_kernel_data: } server_addr.sin_port = htons(port); - /* prepare data structure for kernel */ - + /* Prepare data structure for kernel */ data.fd = fsock; memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr)); strncpy(data.hostname, hostname, sizeof(data.hostname)); - /* clean up */ - + /* Clean up */ auth_destroy(mclient->cl_auth); clnt_destroy(mclient); close(msock); @@ -1342,7 +1548,7 @@ prepare_kernel_data: if (!daemonized) { daemonized = daemonize(); if (daemonized <= 0) { /* parent or error */ - // FIXME: parent doesn't close fsock - ??! +/* FIXME: parent doesn't close fsock - ??! */ retval = -daemonized; goto ret; } @@ -1354,14 +1560,14 @@ prepare_kernel_data: } } -do_mount: /* perform actual mount */ - + /* Perform actual mount */ + do_mount: mp->mnt_type = (char*)"nfs"; retval = mount_it_now(mp, vfsflags, (char*)&data); goto ret; -fail: /* abort */ - + /* Abort */ + fail: if (msock >= 0) { if (mclient) { auth_destroy(mclient->cl_auth); @@ -1372,52 +1578,62 @@ fail: /* abort */ if (fsock >= 0) close(fsock); -ret: + ret: free(hostname); free(mounthost); free(filteropts); return retval; } -#else /* !ENABLE_FEATURE_MOUNT_NFS */ +#else // !ENABLE_FEATURE_MOUNT_NFS -/* Never called. Call should be optimized out. */ -int nfsmount(struct mntent *mp, int vfsflags, char *filteropts); +// Never called. Call should be optimized out. +int nfsmount(struct mntent *mp, long vfsflags, char *filteropts); -#endif /* !ENABLE_FEATURE_MOUNT_NFS */ +#endif // !ENABLE_FEATURE_MOUNT_NFS // Mount one directory. Handles CIFS, NFS, loopback, autobind, and filesystem // type detection. Returns 0 for success, nonzero for failure. // NB: mp->xxx fields may be trashed on exit static int singlemount(struct mntent *mp, int ignore_busy) { - int rc = -1, vfsflags; - char *loopFile = 0, *filteropts = 0; - llist_t *fl = 0; + int rc = -1; + long vfsflags; + char *loopFile = NULL, *filteropts = NULL; + llist_t *fl = NULL; struct stat st; - vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); + errno = 0; - // Treat fstype "auto" as unspecified. + vfsflags = parse_mount_options(mp->mnt_opts, &filteropts); - if (mp->mnt_type && strcmp(mp->mnt_type,"auto") == 0) - mp->mnt_type = 0; + // Treat fstype "auto" as unspecified + if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0) + 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; - 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)); @@ -1425,60 +1641,46 @@ static int singlemount(struct mntent *mp, int ignore_busy) } // Might this be an CIFS filesystem? - if (ENABLE_FEATURE_MOUNT_CIFS - && (!mp->mnt_type || strcmp(mp->mnt_type,"cifs") == 0) - && (mp->mnt_fsname[0]=='/' || mp->mnt_fsname[0]=='\\') - && mp->mnt_fsname[0]==mp->mnt_fsname[1] + && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0) + && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\') + && mp->mnt_fsname[0] == mp->mnt_fsname[1] ) { + int len; + char c; len_and_sockaddr *lsa; - char *ip, *dotted; - char *s; - - rc = 1; - // Replace '/' with '\' and verify that unc points to "//server/share". - - for (s = mp->mnt_fsname; *s; ++s) - if (*s == '/') *s = '\\'; - - // get server IP - - s = strrchr(mp->mnt_fsname, '\\'); - if (s <= mp->mnt_fsname+1) goto report_error; - *s = '\0'; - lsa = host2sockaddr(mp->mnt_fsname+2, 0); - *s = '\\'; - if (!lsa) goto report_error; - - // insert ip=... option into string flags. - - dotted = xmalloc_sockaddr2dotted_noport(&lsa->sa); + char *hostname, *dotted, *ip; + + hostname = mp->mnt_fsname + 2; + len = strcspn(hostname, "/\\"); + if (len == 0 || hostname[len] == '\0') + goto report_error; + c = hostname[len]; + hostname[len] = '\0'; + lsa = host2sockaddr(hostname, 0); + hostname[len] = c; + 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); - // compose new unc '\\server-ip\share' - // (s => slash after hostname) - - mp->mnt_fsname = xasprintf("\\\\%s%s", dotted, s); - - // lock is required + // "-o mand" is required [why?] vfsflags |= MS_MANDLOCK; - mp->mnt_type = (char*)"cifs"; rc = mount_it_now(mp, vfsflags, filteropts); - if (ENABLE_FEATURE_CLEAN_UP) { - free(mp->mnt_fsname); - free(ip); - free(dotted); - free(lsa); - } + 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); @@ -1489,42 +1691,37 @@ static int singlemount(struct mntent *mp, int ignore_busy) // a synthetic filesystem like proc or sysfs.) // (We use stat, not lstat, in order to allow // mount symlink_to_file_or_blkdev dir) - if (!stat(mp->mnt_fsname, &st) && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE)) ) { // Do we need to allocate a loopback device for it? - 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) { + mp->mnt_fsname = NULL; // will receive malloced loop dev name + 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; } // Autodetect bind mounts - } else if (S_ISDIR(st.st_mode) && !mp->mnt_type) vfsflags |= MS_BIND; } - /* If we know the fstype (or don't need to), jump straight - * to the actual mount. */ - + // 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))) rc = mount_it_now(mp, vfsflags, filteropts); 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) @@ -1534,12 +1731,12 @@ 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; } } // If mount failed, clean up loop file (if any). - if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) { del_loop(mp->mnt_fsname); if (ENABLE_FEATURE_CLEAN_UP) { @@ -1559,214 +1756,333 @@ static int singlemount(struct mntent *mp, int ignore_busy) return rc; } +// -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) +{ + if (!O_opt) + 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++; + } + if (match == 0) + return 0; // match wanted but not found + if (O_opt[O_len] == '\0') // end? + break; + // Step to the next O option + O_opt += O_len + 1; + } + // 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); -int mount_main(int argc, char **argv) +int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mount_main(int argc UNUSED_PARAM, char **argv) { - enum { OPT_ALL = 0x10 }; - - char *cmdopts = xstrdup(""), *fstype=0, *storage_path=0; - char *opt_o; + char *cmdopts = xzalloc(1); + char *fstype = NULL; + char *O_optmatch = NULL; + char *storage_path; + 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;) - USE_DESKTOP( int nonroot = (getuid() != 0);) + IF_NOT_DESKTOP(const int nonroot = 0;) - /* parse long options, like --bind and --move. Note that -o option - * and --option are synonymous. Yes, this means --remount,rw works. */ + IF_DESKTOP(int nonroot = ) sanitize_env_if_suid(); - for (i = j = 0; i < argc; i++) { - if (argv[i][0] == '-' && argv[i][1] == '-') { - append_mount_options(&cmdopts, argv[i]+2); - } else argv[j++] = argv[i]; + // Parse long options, like --bind and --move. Note that -o option + // and --option are synonymous. Yes, this means --remount,rw works. + for (i = j = 1; argv[i]; i++) { + if (argv[i][0] == '-' && argv[i][1] == '-') + append_mount_options(&cmdopts, argv[i] + 2); + else + argv[j++] = argv[i]; } - argv[j] = 0; - argc = j; + argv[j] = NULL; // Parse remaining options - - opt = getopt32(argv, "o:t:rwanfvsi", &opt_o, &fstype); - if (opt & 0x1) append_mount_options(&cmdopts, opt_o); // -o - //if (opt & 0x2) // -t - if (opt & 0x4) append_mount_options(&cmdopts, "ro"); // -r - if (opt & 0x8) append_mount_options(&cmdopts, "rw"); // -w - //if (opt & 0x10) // -a - if (opt & 0x20) USE_FEATURE_MTAB_SUPPORT(useMtab = 0); // -n - if (opt & 0x40) USE_FEATURE_MTAB_SUPPORT(fakeIt = 1); // -f - //if (opt & 0x80) // -v: verbose (ignore) - //if (opt & 0x100) // -s: sloppy (ignore) - //if (opt & 0x200) // -i: don't call mount. (ignore) + // 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)); + 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 argv += optind; - argc -= optind; - - // Three or more non-option arguments? Die with a usage message. - - if (argc > 2) bb_show_usage(); // If we have no arguments, show currently mounted filesystems - - if (!argc) { - if (!(opt & OPT_ALL)) { + if (!argv[0]) { + if (!(opt & OPT_a)) { FILE *mountTable = setmntent(bb_path_mtab_file, "r"); - if (!mountTable) bb_error_msg_and_die("no %s", bb_path_mtab_file); + if (!mountTable) + bb_error_msg_and_die("no %s", bb_path_mtab_file); while (getmntent_r(mountTable, &mtpair[0], getmntent_buf, - sizeof(getmntent_buf))) + GETMNTENT_BUFSIZE)) { // 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); } - if (ENABLE_FEATURE_CLEAN_UP) endmntent(mountTable); + if (ENABLE_FEATURE_CLEAN_UP) + endmntent(mountTable); return EXIT_SUCCESS; } - } else storage_path = bb_simplify_path(argv[0]); - - // When we have two arguments, the second is the directory and we can - // skip looking at fstab entirely. We can always abspath() the directory - // argument when we get it. - - if (argc == 2) { - if (nonroot) - bb_error_msg_and_die(must_be_root); - mtpair->mnt_fsname = argv[0]; - mtpair->mnt_dir = argv[1]; - mtpair->mnt_type = fstype; - mtpair->mnt_opts = cmdopts; - rc = singlemount(mtpair, 0); - goto clean_up; + storage_path = NULL; + } else { + // When we have two arguments, the second is the directory and we can + // skip looking at fstab entirely. We can always abspath() the directory + // argument when we get it. + if (argv[1]) { + if (nonroot) + 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, /*ignore_busy:*/ 0); + return rc; + } + storage_path = bb_simplify_path(argv[0]); // malloced } - i = parse_mount_options(cmdopts, 0); + // Past this point, we are handling either "mount -a [opts]" + // or "mount [opts] single_param" + + 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 && (i & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) ) { - rc = mount("", argv[0], "", i, ""); - if (rc) bb_perror_msg_and_die("%s", argv[0]); - goto clean_up; + // verbose_mount(source, target, type, flags, data) + rc = verbose_mount("", argv[0], "", i, ""); + if (rc) + bb_simple_perror_msg_and_die(argv[0]); + return rc; } // Open either fstab or mtab - fstabname = "/etc/fstab"; if (i & MS_REMOUNT) { + // WARNING. I am not sure this matches util-linux's + // behavior. It's possible util-linux does not + // take -o opts from mtab (takes only mount source). fstabname = bb_path_mtab_file; } fstab = setmntent(fstabname, "r"); if (!fstab) - bb_perror_msg_and_die("cannot read %s", fstabname); - - // Loop through entries until we find what we're looking for. + 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)); for (;;) { - struct mntent *mtnext = (mtcur==mtpair ? mtpair+1 : mtpair); + struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair); // Get next fstab entry - if (!getmntent_r(fstab, mtcur, getmntent_buf - + (mtcur==mtpair ? sizeof(getmntent_buf)/2 : 0), - sizeof(getmntent_buf)/2)) - { - // Were we looking for something specific? - - if (argc) { - - // If we didn't find anything, complain. - - if (!mtnext->mnt_fsname) - bb_error_msg_and_die("can't find %s in %s", - argv[0], fstabname); - - mtcur = mtnext; - if (nonroot) { - // fstab must have "users" or "user" - if (!(parse_mount_options(mtcur->mnt_opts, 0) & MOUNT_USERS)) - bb_error_msg_and_die(must_be_root); - } - - // Mount the last thing we found. - - mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); - append_mount_options(&(mtcur->mnt_opts), cmdopts); - rc = singlemount(mtcur, 0); - free(mtcur->mnt_opts); - } - goto clean_up; + + (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0), + GETMNTENT_BUFSIZE/2) + ) { // End of fstab/mtab is reached + mtcur = mtother; // the thing we found last time + break; } - /* If we're trying to mount something specific and this isn't it, - * skip it. Note we must match both the exact text in fstab (ala - * "proc") or a full path from root */ - - if (argc) { + // If we're trying to mount something specific and this isn't it, + // skip it. Note we must match the exact text in fstab (ala + // "proc") or a full path from root + if (argv[0]) { // Is this what we're looking for? + 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 + } - 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; - - // Remember this entry. Something later may have overmounted - // it, and we want the _last_ match. - - mtcur = mtnext; - - // If we're mounting all. + // Remember this entry. Something later may have + // overmounted it, and we want the _last_ match. + mtcur = mtother; + // If we're mounting all } else { - // Do we need to match a filesystem type? - if (fstype && match_fstype(mtcur, fstype)) continue; - - // Skip noauto and swap anyway. + struct mntent *mp; + // No, mount -a won't mount anything, + // even user mounts, for mere humans + if (nonroot) + bb_error_msg_and_die(bb_msg_you_must_be_root); - if (parse_mount_options(mtcur->mnt_opts, 0) - & (MOUNT_NOAUTO | MOUNT_SWAP)) continue; + // Does type match? (NULL matches always) + if (!match_fstype(mtcur, fstype)) + continue; - // No, mount -a won't mount anything, - // even user mounts, for mere humans. + // 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 + ) { + continue; + } - if (nonroot) - bb_error_msg_and_die(must_be_root); + // Does (at least one) option match? + // (NULL matches always) + if (!match_opt(mtcur->mnt_opts, O_optmatch)) + continue; - // Mount this thing. + resolve_mount_spec(&mtcur->mnt_fsname); // NFS mounts want this to be xrealloc-able mtcur->mnt_opts = xstrdup(mtcur->mnt_opts); - 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); } } - if (ENABLE_FEATURE_CLEAN_UP) endmntent(fstab); -clean_up: + // End of fstab/mtab is reached. + // Were we looking for something specific? + if (argv[0]) { // yes + long l; + + // If we didn't find anything, complain + if (!mtcur->mnt_fsname) + bb_error_msg_and_die("can't find %s in %s", + argv[0], fstabname); + + // What happens when we try to "mount swap_partition"? + // (fstab containts "swap_partition swap swap defaults 0 0") + // util-linux-ng 2.13.1 does this: + // stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory) + // mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory) + // lstat("swap", 0x7fff62a3a640) = -1 ENOENT (No such file or directory) + // write(2, "mount: mount point swap does not exist\n", 39) = 39 + // exit_group(32) = ? +#if 0 + // In case we want to simply skip swap partitions: + l = parse_mount_options(mtcur->mnt_opts, NULL); + if ((l & MOUNT_SWAP) + // swap is bogus "fstype", parse_mount_options can't check fstypes + || strcasecmp(mtcur->mnt_type, "swap") == 0 + ) { + goto ret; + } +#endif + if (nonroot) { + // fstab must have "users" or "user" + l = parse_mount_options(mtcur->mnt_opts, NULL); + if (!(l & MOUNT_USERS)) + bb_error_msg_and_die(bb_msg_you_must_be_root); + } + + //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: + if (ENABLE_FEATURE_CLEAN_UP) + endmntent(fstab); if (ENABLE_FEATURE_CLEAN_UP) { 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; }