X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnfsumount.c;h=4311473b5536f892ad65492b601479c77f394831;hp=28f42448105d20efcf18a00ab77e330ccc256851;hb=e90a21266b0bed721d6df4468cf85f06d578fc8e;hpb=940c7c304d4a43c00c27529cdddc7c87db6eef87 diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index 28f4244..4311473 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -17,21 +17,23 @@ * */ +#include #include #include #include #include #include #include +#include #include "xcommon.h" #include "fstab.h" #include "nls.h" -#include "conn.h" #include "mount_constants.h" #include "mount.h" -#include "nfsumount.h" +#include "error.h" +#include "network.h" #if !defined(MNT_FORCE) /* dare not try to include -- lots of errors */ @@ -43,228 +45,122 @@ #endif extern char *progname; -extern int nfs_mount_version; extern int nomtab; extern int verbose; int force; int lazy; int remount; -extern int find_kernel_nfs_mount_version(void); -extern int probe_mntport(clnt_addr_t *); -extern int nfs_gethostbyname(const char *, struct sockaddr_in *); -static inline enum clnt_stat -nfs3_umount(dirpath *argp, CLIENT *clnt) +static int try_remount(const char *spec, const char *node) { - static char clnt_res; - memset (&clnt_res, 0, sizeof(clnt_res)); - return clnt_call(clnt, MOUNTPROC_UMNT, - (xdrproc_t) xdr_dirpath, (caddr_t)argp, - (xdrproc_t) xdr_void, (caddr_t) &clnt_res, - TIMEOUT); -} - -static inline enum clnt_stat -nfs2_umount(dirpath *argp, CLIENT *clnt) -{ - static char clnt_res; - memset (&clnt_res, 0, sizeof(clnt_res)); - return clnt_call(clnt, MOUNTPROC_UMNT, - (xdrproc_t) xdr_dirpath, (caddr_t)argp, - (xdrproc_t) xdr_void, (caddr_t) &clnt_res, - TIMEOUT); -} - -int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp) -{ - CLIENT *clnt; - enum clnt_stat res = 0; - int msock; - - clnt = mnt_openclnt(mnt_server, &msock); - if (!clnt) - goto out_bad; - switch (mnt_server->pmap.pm_vers) { - case 3: - res = nfs3_umount(argp, clnt); - break; - case 2: - case 1: - res = nfs2_umount(argp, clnt); - break; - default: - break; + int res; + + res = mount(spec, node, NULL, + MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); + if (res == 0) { + struct mntent remnt; + nfs_error(_("%s: %s busy - remounted read-only"), + progname, spec); + remnt.mnt_type = remnt.mnt_fsname = NULL; + remnt.mnt_dir = xstrdup(node); + remnt.mnt_opts = xstrdup("ro"); + if (!nomtab) + update_mtab(node, &remnt); + } else if (errno != EBUSY) { /* hmm ... */ + perror(_("remount")); + nfs_error(_("%s: could not remount %s read-only"), + progname, spec); } - mnt_closeclnt(clnt, msock); - if (res == RPC_SUCCESS) - return 1; - out_bad: - return 0; + return res; } -u_int get_mntproto(const char *); -u_int -get_mntproto(const char *dirname) +static int del_mtab(const char *spec, const char *node) { - FILE *mtab; - struct mntent mntbuf; - char tmpbuf[BUFSIZ]; - u_int proto = IPPROTO_TCP; /* assume tcp */ - - mtab = setmntent ("/proc/mounts", "r"); - if (mtab == NULL) - mtab = setmntent (_PATH_MOUNTED, "r"); - if (mtab == NULL) - return proto; - - while(getmntent_r(mtab, &mntbuf, tmpbuf, sizeof (tmpbuf))) { - if (strcmp(mntbuf.mnt_type, "nfs")) - continue; - if (strcmp(dirname, mntbuf.mnt_fsname)) - continue; - if (hasmntopt(&mntbuf, "udp")) - proto = IPPROTO_UDP; - break; + int umnt_err, res; + + umnt_err = 0; + if (lazy) { + res = umount2 (node, MNT_DETACH); + if (res < 0) + umnt_err = errno; + goto writemtab; } - endmntent (mtab); - return proto; -} + if (force) { + res = umount2 (node, MNT_FORCE); + if (res == -1) { + int errsv = errno; + perror(_("umount2")); + errno = errsv; + if (errno == ENOSYS) { + if (verbose) + printf(_("no umount2, trying umount...\n")); + res = umount (node); + } + } + } else + res = umount (node); -/* complain about a failed umount */ -static void complain(int err, const char *dev) { - switch (err) { - case ENXIO: - nfs_error (_("umount: %s: invalid block device"), dev); break; - case EINVAL: - nfs_error (_("umount: %s: not mounted"), dev); break; - case EIO: - nfs_error (_("umount: %s: can't write superblock"), dev); break; - case EBUSY: - /* Let us hope fstab has a line "proc /proc ..." - and not "none /proc ..."*/ - nfs_error (_("umount: %s: device is busy"), dev); break; - case ENOENT: - nfs_error (_("umount: %s: not found"), dev); break; - case EPERM: - nfs_error (_("umount: %s: must be superuser to umount"), dev); break; - case EACCES: - nfs_error (_("umount: %s: block devices not permitted on fs"), dev); break; - default: - nfs_error (_("umount: %s: %s"), dev, strerror (err)); break; - } -} + if (res < 0 && remount && errno == EBUSY && spec) { + res = try_remount(spec, node); + if (!res) + return 0; + } -int add_mtab2(const char *spec, const char *node, const char *type, - const char *opts, struct mntentchn *mc) -{ - int umnt_err, umnt_err2, res; - - umnt_err = umnt_err2 = 0; - if (lazy) { - res = umount2 (node, MNT_DETACH); - if (res < 0) - umnt_err = errno; - goto writemtab; - } - - if (force) { - res = umount2 (node, MNT_FORCE); - if (res == -1) { - int errsv = errno; - perror("umount2"); - errno = errsv; - if (errno == ENOSYS) { - if (verbose) - printf(_("no umount2, trying umount...\n")); - res = umount (node); - } - } - } else - res = umount (node); - - if (res < 0) { - umnt_err = errno; - /* A device might have been mounted on a node that has since - been deleted or renamed, so if node fails, also try spec. */ - /* Note that this is incorrect in case spec was mounted - several times. */ - /* if (umnt_err == ENOENT || umnt_err == EINVAL) */ - if (umnt_err != EBUSY && strcmp(node, spec)) { - if (verbose) - printf (_("could not umount %s - trying %s instead\n"), - node, spec); - res = umount (spec); - if (res < 0) - umnt_err2 = errno; - /* Do not complain about remote NFS mount points */ - if (errno == ENOENT && index(spec, ':')) - umnt_err2 = 0; - } - } - - if (res < 0 && remount && (umnt_err == EBUSY || umnt_err2 == EBUSY)) { - /* Umount failed - let us try a remount */ - res = mount(spec, node, NULL, - MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); - if (res == 0) { - nfs_mntent_t remnt; - fprintf(stderr, - _("umount: %s busy - remounted read-only\n"), - spec); - remnt.mnt_type = remnt.mnt_fsname = NULL; - remnt.mnt_dir = xstrdup(node); - remnt.mnt_opts = xstrdup("ro"); - if (!nomtab) - update_mtab(node, &remnt); - return 0; - } else if (errno != EBUSY) { /* hmm ... */ - perror("remount"); - fprintf(stderr, - _("umount: could not remount %s read-only\n"), - spec); - } - } - - if (res >= 0) { - /* Umount succeeded */ - if (verbose) - printf (_("%s umounted\n"), spec); - } + if (res >= 0) { + /* Umount succeeded */ + if (verbose) + printf(_("%s umounted\n"), spec ? spec : node); + } writemtab: - if (!nomtab && - (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) { - update_mtab (node, NULL); - } - - if (res >= 0) - return 0; - - if (umnt_err2) - complain(umnt_err2, spec); - if (umnt_err && umnt_err != umnt_err2) - complain(umnt_err, node); - return 1; + if (!nomtab && + (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) { + update_mtab(node, NULL); + } + + if (res >= 0) + return 0; + + if (umnt_err) + umount_error(umnt_err, node); + return EX_FILEIO; } /* - * Returns 1 if everything went well, else 0. + * Pick up certain mount options used during the original mount + * from /etc/mtab. The basics include the server's IP address and + * the server pathname of the share to unregister. + * + * These options might also describe the mount port, mount protocol + * version, and transport protocol used to punch through a firewall. + * We will need this information to get through the firewall again + * to do the umount. */ -int _nfsumount(const char *spec, const char *opts) +static int do_nfs_umount(const char *spec, char *opts) { char *hostname; char *dirname; clnt_addr_t mnt_server = { &hostname, }; + struct mntent mnt = { .mnt_opts = opts }; struct pmap *pmap = &mnt_server.pmap; char *p; - nfs_mount_version = find_kernel_nfs_mount_version(); - if (spec == NULL || (p = strchr(spec,':')) == NULL) - goto out_bad; - hostname = xstrndup(spec, p-spec); - dirname = xstrdup(p+1); + if (spec == NULL) { + nfs_error(_("%s: No NFS export name was provided"), + progname); + return EX_USAGE; + } + + p = strchr(spec, ':'); + if (p == NULL) { + nfs_error(_("%s: '%s' is not a legal NFS export name"), + progname, spec); + return EX_USAGE; + } + hostname = xstrndup(spec, p - spec); + dirname = xstrdup(p + 1); #ifdef NFS_MOUNT_DEBUG printf(_("host: %s, directory: %s\n"), hostname, dirname); #endif @@ -290,25 +186,39 @@ int _nfsumount(const char *spec, const char *opts) } pmap->pm_prog = MOUNTPROG; - pmap->pm_vers = MOUNTVERS; - pmap->pm_prot = get_mntproto(spec); + pmap->pm_vers = MOUNTVERS_NFSV3; if (opts && (p = strstr(opts, "mountprog=")) && isdigit(*(p+10))) pmap->pm_prog = atoi(p+10); if (opts && (p = strstr(opts, "mountport=")) && isdigit(*(p+10))) pmap->pm_port = atoi(p+10); - if (opts && (p = strstr(opts, "nfsvers=")) && isdigit(*(p+8))) - pmap->pm_vers = nfsvers_to_mnt(atoi(p+8)); + if (opts && hasmntopt(&mnt, "v2")) + pmap->pm_vers = nfsvers_to_mnt(2); + if (opts && hasmntopt(&mnt, "v3")) + pmap->pm_vers = nfsvers_to_mnt(3); + if (opts && hasmntopt(&mnt, "v4")) + pmap->pm_vers = nfsvers_to_mnt(4); + if (opts && (p = strstr(opts, "vers=")) && isdigit(*(p+5))) + pmap->pm_vers = nfsvers_to_mnt(atoi(p+5)); if (opts && (p = strstr(opts, "mountvers=")) && isdigit(*(p+10))) pmap->pm_vers = atoi(p+10); + if (opts && (hasmntopt(&mnt, "udp") || hasmntopt(&mnt, "proto=udp"))) + pmap->pm_prot = IPPROTO_UDP; + if (opts && (hasmntopt(&mnt, "tcp") || hasmntopt(&mnt, "proto=tcp"))) + pmap->pm_prot = IPPROTO_TCP; + + if (!nfs_gethostbyname(hostname, &mnt_server.saddr)) { + nfs_error(_("%s: '%s' does not contain a recognized hostname"), + progname, spec); + return EX_USAGE; + } - if (!nfs_gethostbyname(hostname, &mnt_server.saddr)) - goto out_bad; - if (!probe_mntport(&mnt_server)) - goto out_bad; - return nfs_call_umount(&mnt_server, &dirname); - out_bad: - printf("%s: %s: not found or not mounted\n", progname, spec); - return 0; + if (!nfs_call_umount(&mnt_server, &dirname)) { + nfs_error(_("%s: Server failed to unmount '%s'"), + progname, spec); + return EX_USAGE; + } + + return EX_SUCCESS; } static struct option umount_longopts[] = @@ -321,15 +231,15 @@ static struct option umount_longopts[] = { NULL, 0, 0, 0 } }; -void umount_usage() +static void umount_usage(void) { - printf("usage: %s dir [-fvnrlh]\n", progname); - printf("options:\n\t-f\t\tforce unmount\n"); - printf("\t-v\t\tverbose\n"); - printf("\t-n\t\tDo not update /etc/mtab\n"); - printf("\t-r\t\tremount\n"); - printf("\t-l\t\tlazy unmount\n"); - printf("\t-h\t\tprint this help\n\n"); + printf(_("usage: %s dir [-fvnrlh]\n"), progname); + printf(_("options:\n\t-f\t\tforce unmount\n")); + printf(_("\t-v\tverbose\n")); + printf(_("\t-n\tDo not update /etc/mtab\n")); + printf(_("\t-r\tremount\n")); + printf(_("\t-l\tlazy unmount\n")); + printf(_("\t-h\tprint this help\n\n")); } int nfsumount(int argc, char *argv[]) @@ -338,11 +248,17 @@ int nfsumount(int argc, char *argv[]) char *spec; struct mntentchn *mc; + if (argc < 2) { + umount_usage(); + return EX_USAGE; + } + spec = argv[1]; argv += 1; argc -= 1; + argv[0] = argv[-1]; /* So that getopt error messages are correct */ while ((c = getopt_long (argc, argv, "fvnrlh", umount_longopts, NULL)) != -1) { @@ -365,28 +281,78 @@ int nfsumount(int argc, char *argv[]) case 'h': default: umount_usage(); - return 0; + return EX_USAGE; } } + if (optind != argc) { + umount_usage(); + return EX_USAGE; + } + + if (spec == NULL || (*spec != '/' && strchr(spec,':') == NULL)) { + nfs_error(_("%s: %s: not found\n"), progname, spec); + return EX_USAGE; + } - mc = getmntdirbackward(spec, NULL); - if (!mc) + if (*spec == '/') + mc = getmntdirbackward(spec, NULL); + else mc = getmntdevbackward(spec, NULL); if (!mc && verbose) printf(_("Could not find %s in mtab\n"), spec); - if(mc) { - ret = _nfsumount(mc->m.mnt_fsname, mc->m.mnt_opts); - if(ret) - ret = add_mtab2(mc->m.mnt_fsname, mc->m.mnt_dir, - mc->m.mnt_type, mc->m.mnt_opts, mc); + if (mc && strcmp(mc->m.mnt_type, "nfs") != 0 && + strcmp(mc->m.mnt_type, "nfs4") != 0) { + nfs_error(_("%s: %s on %s is not an NFS filesystem"), + progname, mc->m.mnt_fsname, mc->m.mnt_dir); + return EX_USAGE; } - else { - ret = _nfsumount(spec, NULL); - if(ret) - ret = add_mtab2(spec, spec, spec, spec, NULL); + + if (getuid() != 0) { + /* only permitted if "user=" or "users" is in mount options */ + if (!mc) { + /* umount might call us twice. The second time there will + * be no entry in mtab and we should just exit quietly + */ + return EX_SUCCESS; + + only_root: + nfs_error(_("%s: You are not permitted to unmount %s"), + progname, spec); + return EX_USAGE; + } + if (hasmntopt(&mc->m, "users") == NULL) { + char *opt = hasmntopt(&mc->m, "user"); + struct passwd *pw; + char *comma; + int len; + if (!opt) + goto only_root; + if (opt[4] != '=') + goto only_root; + comma = strchr(opt, ','); + if (comma) + len = comma - (opt + 5); + else + len = strlen(opt+5); + pw = getpwuid(getuid()); + if (pw == NULL || strlen(pw->pw_name) != len + || strncmp(pw->pw_name, opt+5, len) != 0) + goto only_root; + } } - return(ret); + ret = 0; + if (mc) { + if (!lazy) + ret = do_nfs_umount(mc->m.mnt_fsname, mc->m.mnt_opts); + if (!ret || force) + ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir); + } else if (*spec != '/') { + if (!lazy) + ret = do_nfs_umount(spec, "tcp,v3"); + } else + ret = del_mtab(NULL, spec); + + return ret; } -