From: Chuck Lever Date: Wed, 13 Oct 2010 17:55:10 +0000 (-0400) Subject: umount.nfs: Distinguish between nfs4 and nfs mounts X-Git-Tag: nfs-utils-1-2-4-rc1~2 X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=commitdiff_plain;h=dc08c702a6c7f824f317af561f491635ee898a71 umount.nfs: Distinguish between nfs4 and nfs mounts Neil Brown reports that umount.nfs is still confused by "-t nfs -o vers=4" mounts. /etc/mtab can be confused. /proc/mounts is authoritative on the fstype of a mount. Have umount.nfs consult it to determine which mechanism to use for unmounting. The code to read /proc/mounts was lifted from the nfsstat command. The code introduced by this patch may look like belt-n-suspenders, but we have two use cases to consider: 1. Old kernels don't support the "vers=4" mount option, so umount.nfs must look for the "nfs4" fstype 2. Upcoming kernels may eliminate support the "nfs4" fstype, so umount.nfs must look for the "vers=4" mount option Thus this logic checks for "nfs4" first then looks for the NFS version setting. Note that we could handle unmounting entirely in the kernel, but that won't help older kernels that have this issue. See: https://bugzilla.linux-nfs.org/show_bug.cgi?id=189 Signed-off-by: Chuck Lever Signed-off-by: Steve Dickson --- diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index b1927de..02d40ff 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -38,6 +38,9 @@ #include "parse_opt.h" #include "parse_dev.h" +#define MOUNTSFILE "/proc/mounts" +#define LINELEN (4096) + #if !defined(MNT_FORCE) /* dare not try to include -- lots of errors */ #define MNT_FORCE 1 @@ -242,6 +245,91 @@ static int nfs_umount23(const char *devname, char *string) return result; } +/* + * Detect NFSv4 mounts. + * + * Consult /proc/mounts to determine if the mount point + * is an NFSv4 mount. The kernel is authoritative about + * what type of mount this is. + * + * Returns 1 if "mc" is an NFSv4 mount, zero if not, and + * -1 if some error occurred. + */ +static int nfs_umount_is_vers4(const struct mntentchn *mc) +{ + char buffer[LINELEN], *next; + int retval; + FILE *f; + + if ((f = fopen(MOUNTSFILE, "r")) == NULL) { + fprintf(stderr, "%s: %s\n", + MOUNTSFILE, strerror(errno)); + return -1; + } + + retval = -1; + while (fgets(buffer, sizeof(buffer), f) != NULL) { + char *device, *mntdir, *type, *flags; + struct mount_options *options; + char *line = buffer; + + next = strchr(line, '\n'); + if (next != NULL) + *next = '\0'; + + device = strtok(line, " \t"); + if (device == NULL) + continue; + mntdir = strtok(NULL, " \t"); + if (mntdir == NULL) + continue; + if (strcmp(device, mc->m.mnt_fsname) != 0 && + strcmp(mntdir, mc->m.mnt_dir) != 0) + continue; + + type = strtok(NULL, " \t"); + if (type == NULL) + continue; + if (strcmp(type, "nfs4") == 0) + goto out_nfs4; + + flags = strtok(NULL, " \t"); + if (flags == NULL) + continue; + options = po_split(flags); + if (options != NULL) { + unsigned long version; + int rc; + + rc = nfs_nfs_version(options, &version); + po_destroy(options); + if (rc && version == 4) + goto out_nfs4; + } + + goto out_nfs; + } + if (retval == -1) + fprintf(stderr, "%s was not found in %s\n", + mc->m.mnt_dir, MOUNTSFILE); + +out: + fclose(f); + return retval; + +out_nfs4: + if (verbose) + fprintf(stderr, "NFSv4 mount point detected\n"); + retval = 1; + goto out; + +out_nfs: + if (verbose) + fprintf(stderr, "Legacy NFS mount point detected\n"); + retval = 0; + goto out; +} + static struct option umount_longopts[] = { { "force", 0, 0, 'f' }, @@ -365,13 +453,22 @@ int nfsumount(int argc, char *argv[]) ret = EX_SUCCESS; if (mc) { - 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); + if (!lazy) { + switch (nfs_umount_is_vers4(mc)) { + case 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); + break; + case 1: + break; + default: + return EX_FAIL; + } + } ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir); } else if (*spec != '/') { if (!lazy)