X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fmount%2Fnfsumount.c;h=04990f076a006893a5cdc61c8440697a3fa9be29;hp=97b96e855327537af30876a2182370f7fc8dbbbc;hb=c735a8331b082038a0e83ec4187c2656b0804eea;hpb=0dfc8a5426381c6d65aed4d9d0e50bae3238cc8f diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index 97b96e8..04990f0 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -29,13 +29,13 @@ #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" +#include "parse_opt.h" +#include "parse_dev.h" #if !defined(MNT_FORCE) /* dare not try to include -- lots of errors */ @@ -53,117 +53,192 @@ int force; int lazy; int remount; -static inline enum clnt_stat -nfs_umount(dirpath *argp, CLIENT *clnt) -{ - return clnt_call(clnt, MOUNTPROC_UMNT, - (xdrproc_t) xdr_dirpath, (caddr_t)argp, - (xdrproc_t) xdr_void, NULL, - TIMEOUT); -} -int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp) +static int try_remount(const char *spec, const char *node) { - CLIENT *clnt; - enum clnt_stat res = 0; - int msock; - - switch (mnt_server->pmap.pm_vers) { - case 3: - case 2: - case 1: - if (!probe_mntport(mnt_server)) - goto out_bad; - clnt = mnt_openclnt(mnt_server, &msock); - if (!clnt) - goto out_bad; - res = nfs_umount(argp, clnt); - mnt_closeclnt(clnt, msock); - if (res == RPC_SUCCESS) - return 1; - break; - default: - res = 1; - 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); } - out_bad: return res; } -int del_mtab(const char *spec, const char *node) +static int del_mtab(const char *spec, const char *node) { int umnt_err, res; - umnt_err = 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 && remount && errno == EBUSY && spec) { - /* Umount failed - let us try a remount */ - res = mount(spec, node, NULL, - MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL); - if (res == 0) { - struct mntent 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 ? spec : node); - } + umnt_err = 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) { + if (remount && errno == EBUSY && spec) { + res = try_remount(spec, node); + if (res) + goto writemtab; + return 0; + } else + umnt_err = errno; + } + + 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 (!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; +} + +/* + * 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 + * 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. + * + * 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 nfs_umount23(const char *devname, char *string) +{ + char *hostname, *dirname; + struct mount_options *options; + int result = EX_FAIL; - if (res >= 0) - return 0; + 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); - if (umnt_err) - umount_error(umnt_err, node); - return 1; + free(hostname); + free(dirname); + return result; } /* - * 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, char *opts) +static int do_nfs_umount23(const char *spec, char *opts) { char *hostname; char *dirname; @@ -171,11 +246,11 @@ int _nfsumount(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 || (p = strchr(spec,':')) == NULL) - goto out_bad; - hostname = xstrndup(spec, p-spec); - dirname = xstrdup(p+1); #ifdef NFS_MOUNT_DEBUG printf(_("host: %s, directory: %s\n"), hostname, dirname); #endif @@ -201,8 +276,7 @@ int _nfsumount(const char *spec, char *opts) } pmap->pm_prog = MOUNTPROG; - pmap->pm_vers = MOUNTVERS_NFSV3; - pmap->pm_prot = IPPROTO_TCP; + 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))) @@ -211,21 +285,40 @@ int _nfsumount(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") + || hasmntopt(&mnt, "mountproto=tcp") + )) + pmap->pm_prot = IPPROTO_TCP; + + if (!nfs_gethostbyname(hostname, &mnt_server.saddr)) { + nfs_error(_("%s: DNS resolution of '%s' failed"), + progname, hostname); + goto out; + } - if (!nfs_gethostbyname(hostname, &mnt_server.saddr)) - goto out_bad; - return nfs_call_umount(&mnt_server, &dirname); - out_bad: - fprintf(stderr, "%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); + result = EX_FAIL; + goto out; + } + + result = EX_SUCCESS; + +out: + free(hostname); + free(dirname); + return result; } static struct option umount_longopts[] = @@ -238,15 +331,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[]) @@ -255,6 +348,11 @@ int nfsumount(int argc, char *argv[]) char *spec; struct mntentchn *mc; + if (argc < 2) { + umount_usage(); + return EX_USAGE; + } + spec = argv[1]; argv += 1; @@ -283,17 +381,17 @@ int nfsumount(int argc, char *argv[]) case 'h': default: umount_usage(); - return 0; + return EX_USAGE; } } if (optind != argc) { umount_usage(); - return 0; + return EX_USAGE; } if (spec == NULL || (*spec != '/' && strchr(spec,':') == NULL)) { - printf(_("umount: %s: not found\n"), spec); - return 0; + nfs_error(_("%s: %s: not found\n"), progname, spec); + return EX_USAGE; } if (*spec == '/') @@ -305,9 +403,9 @@ int nfsumount(int argc, char *argv[]) if (mc && strcmp(mc->m.mnt_type, "nfs") != 0 && strcmp(mc->m.mnt_type, "nfs4") != 0) { - fprintf(stderr, "umount.nfs: %s on %s it not an nfs filesystem\n", - mc->m.mnt_fsname, mc->m.mnt_dir); - exit(1); + nfs_error(_("%s: %s on %s is not an NFS filesystem"), + progname, mc->m.mnt_fsname, mc->m.mnt_dir); + return EX_USAGE; } if (getuid() != 0) { @@ -316,12 +414,12 @@ int nfsumount(int argc, char *argv[]) /* umount might call us twice. The second time there will * be no entry in mtab and we should just exit quietly */ - return 0; + return EX_SUCCESS; only_root: - fprintf(stderr,"%s: You are not permitted to unmount %s\n", - progname, spec); - return 0; + 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"); @@ -346,15 +444,19 @@ int nfsumount(int argc, char *argv[]) ret = 0; if (mc) { - if (!lazy) - _nfsumount(mc->m.mnt_fsname, mc->m.mnt_opts); - 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) - _nfsumount(spec, "tcp,v3"); + ret = nfs_umount23(spec, "tcp,v3"); } else ret = del_mtab(NULL, spec); - return(ret); + return ret; } -