X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnfsumount.c;h=04990f076a006893a5cdc61c8440697a3fa9be29;hp=e7f37efe90639a231cde438e3ff304c9bf9990ae;hb=c735a8331b082038a0e83ec4187c2656b0804eea;hpb=e227ac0348431eecc1ddadf5d1907c8348741519 diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index e7f37ef..04990f0 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -34,6 +34,8 @@ #include "mount.h" #include "error.h" #include "network.h" +#include "parse_opt.h" +#include "parse_dev.h" #if !defined(MNT_FORCE) /* dare not try to include -- lots of errors */ @@ -132,6 +134,64 @@ static int del_mtab(const char *spec, const char *node) return EX_FILEIO; } +/* + * Discover mount server's hostname/address by examining mount options + * + * Returns a pointer to a string that the caller must free, on + * success; otherwise NULL is returned. + */ +static char *nfs_umount_hostname(struct mount_options *options, + char *hostname) +{ + char *option; + + option = po_get(options, "mountaddr"); + if (option) + goto out; + option = po_get(options, "mounthost"); + if (option) + goto out; + option = po_get(options, "addr"); + if (option) + goto out; + + return hostname; + +out: + free(hostname); + return strdup(option); +} + +/* + * Returns EX_SUCCESS if mount options and device name have been + * parsed successfully; otherwise EX_FAIL. + */ +static int nfs_umount_do_umnt(struct mount_options *options, + char **hostname, char **dirname) +{ + struct sockaddr_storage address; + struct sockaddr *sap = (struct sockaddr *)&address; + socklen_t salen = sizeof(address); + struct pmap nfs_pmap, mnt_pmap; + + nfs_options2pmap(options, &nfs_pmap, &mnt_pmap); + + *hostname = nfs_umount_hostname(options, *hostname); + if (!*hostname) { + nfs_error(_("%s: out of memory"), progname); + return EX_FAIL; + } + + if (nfs_name_to_address(*hostname, AF_UNSPEC, sap, &salen)) { + if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) != 0) + return EX_SUCCESS; + else + nfs_error(_("%s: Server failed to unmount '%s:%s'"), + progname, *hostname, *dirname); + } + return EX_FAIL; +} + /* * Pick up certain mount options used during the original mount * from /etc/mtab. The basics include the server's IP address and @@ -141,8 +201,44 @@ static int del_mtab(const char *spec, const char *node) * 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. + * + * Note that option parsing failures won't necessarily cause the + * umount request to fail. Those values will be left zero in the + * pmap tuple. If the GETPORT call later fails to disambiguate them, + * then we fail. */ -static int do_nfs_umount(const char *spec, char *opts) +static int nfs_umount23(const char *devname, char *string) +{ + char *hostname, *dirname; + struct mount_options *options; + int result = EX_FAIL; + + if (!nfs_parse_devname(devname, &hostname, &dirname)) + return EX_USAGE; + + options = po_split(string); + if (options) { + result = nfs_umount_do_umnt(options, &hostname, &dirname); + po_destroy(options); + } else + nfs_error(_("%s: option parsing error"), progname); + + free(hostname); + free(dirname); + return result; +} + +/* + * 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. + */ +static int do_nfs_umount23(const char *spec, char *opts) { char *hostname; char *dirname; @@ -150,21 +246,11 @@ static int do_nfs_umount(const char *spec, char *opts) struct mntent mnt = { .mnt_opts = opts }; struct pmap *pmap = &mnt_server.pmap; char *p; + int result = EX_USAGE; + + if (!nfs_parse_devname(spec, &hostname, &dirname)) + return result; - 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 @@ -190,7 +276,7 @@ static int do_nfs_umount(const char *spec, char *opts) } pmap->pm_prog = MOUNTPROG; - pmap->pm_vers = MOUNTVERS_NFSV3; + pmap->pm_vers = 0; /* unknown */ if (opts && (p = strstr(opts, "mountprog=")) && isdigit(*(p+10))) pmap->pm_prog = atoi(p+10); if (opts && (p = strstr(opts, "mountport=")) && isdigit(*(p+10))) @@ -199,30 +285,40 @@ static int do_nfs_umount(const char *spec, char *opts) 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"))) + if (opts && (hasmntopt(&mnt, "udp") + || hasmntopt(&mnt, "proto=udp") + || hasmntopt(&mnt, "mountproto=udp") + )) pmap->pm_prot = IPPROTO_UDP; - if (opts && (hasmntopt(&mnt, "tcp") || hasmntopt(&mnt, "proto=tcp"))) + if (opts && (hasmntopt(&mnt, "tcp") + || hasmntopt(&mnt, "proto=tcp") + || hasmntopt(&mnt, "mountproto=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; + nfs_error(_("%s: DNS resolution of '%s' failed"), + progname, hostname); + goto out; } if (!nfs_call_umount(&mnt_server, &dirname)) { nfs_error(_("%s: Server failed to unmount '%s'"), progname, spec); - return EX_USAGE; + result = EX_FAIL; + goto out; } - return EX_SUCCESS; + result = EX_SUCCESS; + +out: + free(hostname); + free(dirname); + return result; } static struct option umount_longopts[] = @@ -348,13 +444,17 @@ int nfsumount(int argc, char *argv[]) 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); + if (!lazy && strcmp(mc->m.mnt_type, "nfs4") != 0) + /* We ignore the error from nfs_umount23. + * If the actual umount succeeds (in del_mtab), + * we don't want to signal an error, as that + * could cause /sbin/mount to retry! + */ + nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts); + ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir) ?: ret; } else if (*spec != '/') { if (!lazy) - ret = do_nfs_umount(spec, "tcp,v3"); + ret = nfs_umount23(spec, "tcp,v3"); } else ret = del_mtab(NULL, spec);