--- busybox/util-linux/umount.c 2015-03-23 12:07:31.276632213 +0530 +++ ws/Build/busybox/busybox/util-linux/umount.c 2015-03-23 14:35:13.688447431 +0530 @@ -32,6 +32,29 @@ #include #include "libbb.h" +/* Needed for nfs support only */ +#include +#undef TRUE +#undef FALSE +#include +#include +#include +#include + +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]; + +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define nfs_mount_version (G.nfs_mount_version) + #if defined(__dietlibc__) // TODO: This does not belong here. /* 16.12.2006, Sampo Kellomaki (sampo@iki.fi) @@ -44,27 +67,458 @@ } #endif -/* Ignored: -v -t -i - * bbox always acts as if -d is present. - * -D can be used to suppress it (bbox extension). - * Rationale: - * (1) util-linux's umount does it if "loop=..." is seen in /etc/mtab: - * thus, on many systems, bare umount _does_ drop loop devices. - * (2) many users request this feature. +/* ignored: -v -d -t -i */ +#define OPTION_STRING "fldnra" "vdt:i" +#define OPT_FORCE (1 << 0) +#define OPT_LAZY (1 << 1) +#define OPT_FREELOOP (1 << 2) +#define OPT_NO_MTAB (1 << 3) +#define OPT_REMOUNT (1 << 4) +#define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 5) : 0) + + +#if ENABLE_FEATURE_MOUNT_NFS + +/* + * Linux NFS umount + */ + +/* 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 + +typedef char *dirpath; + +#define MOUNTPROG 100005 +#define MOUNTVERS 1 + + +#define MOUNTPROC_UMNT 3 +#define MOUNTPROC3_UMNT 3 + +static bool_t xdr_dirpath(XDR *xdrs, dirpath *objp) +{ + if (!xdr_string(xdrs, objp, MNTPATHLEN)) + return FALSE; + return TRUE; +} + +#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2) + +/* + * Unfortunately, the kernel prints annoying console messages + * in case of an unexpected nfs mount version (instead of + * just returning some error). Therefore we'll have to try + * and figure out what version the kernel expects. + * + * Variables: + * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time + * NFS_MOUNT_VERSION: these nfsmount sources at compile time + * nfs_mount_version: version this source and running kernel can handle */ -#define OPTION_STRING "fldDnra" "vt:i" -#define OPT_FORCE (1 << 0) // Same as MNT_FORCE -#define OPT_LAZY (1 << 1) // Same as MNT_DETACH -//#define OPT_FREE_LOOP (1 << 2) // -d is assumed always present -#define OPT_DONT_FREE_LOOP (1 << 3) -#define OPT_NO_MTAB (1 << 4) -#define OPT_REMOUNT (1 << 5) -#define OPT_ALL (ENABLE_FEATURE_UMOUNT_ALL ? (1 << 6) : 0) +static void +find_kernel_nfs_mount_version(void) +{ + 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))) + nfs_mount_version = 3; + /* else v4 since 2.3.99pre4 */ + } +} + +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; + + server_addr->sin_port = PMAPPORT; +/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *). + * I understand it like "IPv6 for this is not 100% ready" */ + pmap = pmap_getmaps(server_addr); + + if (version > MAX_NFSPROT) + version = MAX_NFSPROT; + if (!prog) + prog = MOUNTPROG; + 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 && 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 && 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; + } + 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; +} + +/* RPC strerror analogs are terminally idiotic: + * *mandatory* prefix and \n at end. + * This hopefully helps. Usage: + * error_msg_rpc(clnt_*error*(" ")) */ +static void error_msg_rpc(const char *msg) +{ + int len; + while (msg[0] == ' ' || msg[0] == ':') msg++; + len = strlen(msg); + while (len && msg[len-1] == '\n') len--; + bb_error_msg("%.*s", len, msg); +} + +// NB: mp->xxx fields may be trashed on exit +static int nfsumount(struct mntent *mp) +{ + CLIENT *mclient; + char *hostname, *pathname, *mounthost, *opt; + struct hostent *hp; + struct sockaddr_in server_addr; + struct sockaddr_in mount_server_addr; + int msock, fsock; + static char clnt_res; + char *s; + int mountport; + int proto; + int mountprog; + int mountvers; + int nfsvers; + int retval; + /* these all are one-bit really. 4.3.1 likes this combination: */ + smallint tcp; + struct pmap pm_mnt; + char *filteropts = xstrdup(mp->mnt_opts); + struct timeval total_timeout; + struct timeval retry_timeout; + + find_kernel_nfs_mount_version(); + + mounthost = NULL; + retval = ETIMEDOUT; + msock = fsock = -1; + mclient = NULL; + + hostname = xstrdup(mp->mnt_fsname); + /* mount_main() guarantees that ':' is there */ + s = strchr(hostname, ':'); + pathname = s + 1; + *s = '\0'; + /* Ignore all but first hostname in replicated mounts + until they can be fully supported. (mack@sgi.com) */ + s = strchr(hostname, ','); + if (s) { + *s = '\0'; + bb_error_msg("warning: multiple hostnames not supported"); + } + + server_addr.sin_family = AF_INET; + if (!inet_aton(hostname, &server_addr.sin_addr)) { + hp = gethostbyname(hostname); + if (hp == NULL) { + 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); + } + memcpy(&server_addr.sin_addr, + hp->h_addr, hp->h_length); + } + + memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr)); + + tcp = 0; + mountprog = MOUNTPROG; + mountvers = 0; + mountport = 0; + nfsvers = 0; + + /* parse options */ + if (filteropts) for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) { + char *opteq = strchr(opt, '='); + if (opteq) { + int val = 0, idx = 0; + static const char options[] ALIGN1 = + /* 0 */ "rsize\0" + /* 1 */ "wsize\0" + /* 2 */ "timeo\0" + /* 3 */ "retrans\0" + /* 4 */ "acregmin\0" + /* 5 */ "acregmax\0" + /* 6 */ "acdirmin\0" + /* 7 */ "acdirmax\0" + /* 8 */ "actimeo\0" + /* 9 */ "retry\0" + /* 10 */ "port\0" + /* 11 */ "mountport\0" + /* 12 */ "mounthost\0" + /* 13 */ "mountprog\0" + /* 14 */ "mountvers\0" + /* 15 */ "nfsprog\0" + /* 16 */ "nfsvers\0" + /* 17 */ "vers\0" + /* 18 */ "proto\0" + /* 19 */ "namlen\0" + /* 20 */ "addr\0"; + + *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; + } + + if ((opteq[0] >= '0') && (opteq[0] <= '9')) + val = xatoi_positive(opteq); + switch (idx) { + case 0: // "rsize" + case 1: // "wsize" + case 2: // "timeo" + case 3: // "retrans" + case 4: // "acregmin" + case 5: // "acregmax" + case 6: // "acdirmin" + case 7: // "acdirmax" + case 8: // "actimeo" + case 9: // "retry" + case 10: // "port" + continue; + case 11: // "mountport" + mountport = val; + continue; + case 13: // "mountprog" + mountprog = val; + continue; + case 14: // "mountvers" + mountvers = val; + continue; + case 15: // "nfsprog" + continue; + case 16: // "nfsvers" + case 17: // "vers" + nfsvers = val; + continue; + case 19: // "namlen" + default: + continue;; + } + } + else { /* not of the form opt=val */ + static const char options[] ALIGN1 = + "bg\0" + "fg\0" + "soft\0" + "hard\0" + "intr\0" + "posix\0" + "cto\0" + "ac\0" + "tcp\0" + "udp\0" + "lock\0" + "rdirplus\0"; + int val = 1; + if (!strncmp(opt, "no", 2)) { + val = 0; + opt += 2; + } + switch (index_in_strings(options, opt)) { + case 0: // "bg" + case 2: // "soft" + case 3: // "hard" + case 4: // "intr" + case 5: // "posix" + case 6: // "cto" + case 7: // "ac" + break; + case 8: // "tcp" + tcp = val; + break; + case 9: // "udp" + tcp = !val; + break; + case 10: // "lock" + case 11: //rdirplus + break; + default: + break; + } + } + } + proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP; + + if (nfsvers && !mountvers) + mountvers = (nfsvers < 3) ? 1 : nfsvers; + if (nfsvers && nfsvers < mountvers) { + mountvers = nfsvers; + } + + if (mounthost) { + if (mounthost[0] >= '0' && mounthost[0] <= '9') { + mount_server_addr.sin_family = AF_INET; + mount_server_addr.sin_addr.s_addr = inet_addr(hostname); + } else { + hp = gethostbyname(mounthost); + if (hp == NULL) { + 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); + } + mount_server_addr.sin_family = AF_INET; + memcpy(&mount_server_addr.sin_addr, + hp->h_addr, hp->h_length); + } + } + + get_mountport(&pm_mnt, &mount_server_addr, + mountprog, + mountvers, + proto, + mountport); + 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); + msock = RPC_ANYSOCK; + + switch (pm_mnt.pm_prot) { + case IPPROTO_UDP: + mclient = clntudp_create(&mount_server_addr, + pm_mnt.pm_prog, + pm_mnt.pm_vers, + retry_timeout, + &msock); + if (mclient) + break; + 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, + &msock, 0, 0); + break; + default: + mclient = NULL; + } + if (!mclient) { + error_msg_rpc(clnt_spcreateerror(" ")); + } else { + enum clnt_stat clnt_stat; + /* try to mount hostname:pathname */ + mclient->cl_auth = authunix_create_default(); + + if (pm_mnt.pm_vers == 3) + clnt_stat = clnt_call(mclient, MOUNTPROC3_UMNT, + (xdrproc_t) xdr_dirpath, + (caddr_t) &pathname, + (xdrproc_t) xdr_void, + (caddr_t) &clnt_res, + total_timeout); + else + clnt_stat = clnt_call(mclient, MOUNTPROC_UMNT, + (xdrproc_t) xdr_dirpath, + (caddr_t) &pathname, + (xdrproc_t) xdr_void, + (caddr_t) &clnt_res, + total_timeout); + + if (clnt_stat != RPC_SUCCESS) + { + if (errno != ECONNREFUSED) { + error_msg_rpc(clnt_sperror(mclient, " ")); + } + else + { + /* Connection refused */ + error_msg_rpc(clnt_sperror(mclient, " ")); + } + } + } + + auth_destroy(mclient->cl_auth); + clnt_destroy(mclient); + mclient = NULL; + close(msock); + msock = -1; +fail: + free(hostname); + free(mounthost); + free(filteropts); + return retval; +} + +#else /* !ENABLE_FEATURE_MOUNT_NFS */ + +/* Never called. Call should be optimized out. */ +int nfsumount(struct mntent *mp); + +#endif /* !ENABLE_FEATURE_MOUNT_NFS */ + +// These constants from linux/fs.h must match OPT_FORCE and OPT_LAZY, +// otherwise "doForce" trick below won't work! +//#define MNT_FORCE 0x00000001 /* Attempt to forcibly umount */ +//#define MNT_DETACH 0x00000002 /* Just detach from the tree */ int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int umount_main(int argc UNUSED_PARAM, char **argv) { int doForce; + char *const path = xmalloc(PATH_MAX + 2); /* to save stack */ struct mntent me; FILE *fp; char *fstype = NULL; @@ -96,13 +550,13 @@ fp = setmntent(bb_path_mtab_file, "r"); if (!fp) { if (opt & OPT_ALL) - bb_error_msg_and_die("can't open '%s'", bb_path_mtab_file); + bb_error_msg_and_die("can't open %s", bb_path_mtab_file); } else { - while (getmntent_r(fp, &me, bb_common_bufsiz1, sizeof(bb_common_bufsiz1))) { + while (getmntent_r(fp, &me, path, PATH_MAX)) { /* Match fstype if passed */ - if (!match_fstype(&me, fstype)) + if (fstype && match_fstype(&me, fstype)) continue; - m = xzalloc(sizeof(*m)); + m = xmalloc(sizeof(struct mtab_list)); m->next = mtl; m->device = xstrdup(me.mnt_fsname); m->dir = xstrdup(me.mnt_dir); @@ -122,11 +576,10 @@ for (;;) { int curstat; char *zapit = *argv; - char *path; // Do we already know what to umount this time through the loop? if (m) - path = xstrdup(m->dir); + safe_strncpy(path, m->dir, PATH_MAX); // For umount -a, end of mtab means time to exit. else if (opt & OPT_ALL) break; @@ -135,12 +588,10 @@ if (!zapit) break; argv++; - path = xmalloc_realpath(zapit); - if (path) { - for (m = mtl; m; m = m->next) - if (strcmp(path, m->dir) == 0 || strcmp(path, m->device) == 0) - break; - } + realpath(zapit, path); + for (m = mtl; m; m = m->next) + if (!strcmp(path, m->dir) || !strcmp(path, m->device)) + break; } // If we couldn't find this sucker in /etc/mtab, punt by passing our // command line argument straight to the umount syscall. Otherwise, @@ -148,6 +599,28 @@ if (m) zapit = m->dir; // Let's ask the thing nicely to unmount. + +#if ENABLE_FEATURE_MOUNT_NFS + memset (path, 0, PATH_MAX); + fp = setmntent(bb_path_mtab_file, "r"); + if (!fp) { + bb_error_msg_and_die("can't open %s", bb_path_mtab_file); + } else { + while (getmntent_r(fp, &me, path, PATH_MAX)) { + /* Match fstype if passed */ + if ((strcmp(zapit, me.mnt_dir) == 0)) + { + if (match_fstype(&me, "nfs") == 1) + { + nfsumount(&me); + } + break; + } + } + endmntent(fp); + } +#endif + curstat = umount(zapit); // Force the unmount, if necessary. @@ -173,7 +646,7 @@ } else { // De-allocate the loop device. This ioctl should be ignored on // any non-loop block devices. - if (ENABLE_FEATURE_MOUNT_LOOP && !(opt & OPT_DONT_FREE_LOOP) && m) + if (ENABLE_FEATURE_MOUNT_LOOP && (opt & OPT_FREELOOP) && m) del_loop(m->device); if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m) erase_mtab(m->dir); @@ -182,13 +655,9 @@ // Find next matching mtab entry for -a or umount /dev // Note this means that "umount /dev/blah" will unmount all instances // of /dev/blah, not just the most recent. - if (m) { - while ((m = m->next) != NULL) - // NB: if m is non-NULL, path is non-NULL as well - if ((opt & OPT_ALL) || strcmp(path, m->device) == 0) - break; - } - free(path); + if (m) while ((m = m->next) != NULL) + if ((opt & OPT_ALL) || !strcmp(path, m->device)) + break; } // Free mtab list if necessary @@ -200,6 +669,7 @@ free(mtl); mtl = m; } + free(path); } return status;