Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 28 Jun 2003 22:54:04 +0100
From:      Ian Dowse <iedowse@maths.tcd.ie>
To:        freebsd-arch@freebsd.org
Subject:   Unmounting by filesystem ID
Message-ID:  <200306282254.aa83607@salmon.maths.tcd.ie>

next in thread | raw e-mail | index | archive | help

The patch below adds a new mount flag MNT_BYFSID that can be used
to unmount a filesystem by specifying its filesystem ID instead of
a path. The umount utility is changed to use this mechanism by
default. This approach has a number of advantages:

- It avoids any lookup operations that could potentially block
  forever, so filesystems such as NFS can be reliably unmounted
  even if the server is not responding but looking up the root node
  would require an RPC (maybe to an underlying filesystem).
- The filesystem specification is unambiguous, so umount(8) can
  be sure that it is unmounting the correct filesystem (more
  work in umount(8) may be required here).
- Detached filesystems can be unmounted. If a filesystem becomes
  detached from the filesystem hierarchy because the underlying
  filesystem got unmounted, it does not require a reboot to unmount
  it.

Since unmounting by a path name is now only required for compatibility,
in that case unmount() now just does a string comparison to find
the correct filesystem. Also, this patch only affects unmounting;
a similar approach could be applied to MNT_UPDATE mount operations.

I would like to commit this during the next few days. Any comments
or suggestions?

Ian

Index: sys/kern/vfs_mount.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/kern/vfs_mount.c,v
retrieving revision 1.108
diff -u -r1.108 vfs_mount.c
--- sys/kern/vfs_mount.c	11 Jun 2003 00:56:58 -0000	1.108
+++ sys/kern/vfs_mount.c	28 Jun 2003 21:12:18 -0000
@@ -1224,17 +1224,42 @@
 		int flags;
 	} */ *uap;
 {
-	register struct vnode *vp;
+	fsid_t fsid;
 	struct mount *mp;
-	int error;
-	struct nameidata nd;
+	char *pathbuf;
+	int error, id0, id1;
 
-	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->path, td);
-	if ((error = namei(&nd)) != 0)
+	pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
+	error = copyinstr(uap->path, pathbuf, MNAMELEN, NULL);
+	if (error) {
+		free(pathbuf, M_TEMP);
 		return (error);
-	vp = nd.ni_vp;
-	NDFREE(&nd, NDF_ONLY_PNBUF);
-	mp = vp->v_mount;
+	}
+	if (uap->flags & MNT_BYFSID) {
+		/* Decode the filesystem ID. */
+		if (sscanf(pathbuf, "FSID:%d:%d", &id0, &id1) != 2) {
+			free(pathbuf, M_TEMP);
+			return (EINVAL);
+		}
+		fsid.val[0] = id0;
+		fsid.val[1] = id1;
+		
+		mtx_lock(&mountlist_mtx);
+		TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list)
+			if (bcmp(&mp->mnt_stat.f_fsid, &fsid,
+			    sizeof(fsid)) == 0)
+				break;
+		mtx_unlock(&mountlist_mtx);
+	} else {
+		mtx_lock(&mountlist_mtx);
+		TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list)
+			if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0)
+				break;
+		mtx_unlock(&mountlist_mtx);
+	}
+	free(pathbuf, M_TEMP);
+	if (mp == NULL)
+		return (ENOENT);
 
 	/*
 	 * Only root, or the user that did the original mount is
@@ -1242,28 +1267,15 @@
 	 */
 	if (mp->mnt_cred->cr_uid != td->td_ucred->cr_uid) {
 		error = suser(td);
-		if (error) {
-			vput(vp);
+		if (error)
 			return (error);
-		}
 	}
 
 	/*
 	 * Don't allow unmounting the root filesystem.
 	 */
-	if (mp->mnt_flag & MNT_ROOTFS) {
-		vput(vp);
-		return (EINVAL);
-	}
-
-	/*
-	 * Must be the root of the filesystem
-	 */
-	if ((vp->v_vflag & VV_ROOT) == 0) {
-		vput(vp);
+	if (mp->mnt_flag & MNT_ROOTFS)
 		return (EINVAL);
-	}
-	vput(vp);
 	return (dounmount(mp, uap->flags, td));
 }
 
Index: sys/sys/mount.h
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/sys/mount.h,v
retrieving revision 1.147
diff -u -r1.147 mount.h
--- sys/sys/mount.h	26 Mar 2003 22:15:58 -0000	1.147
+++ sys/sys/mount.h	1 Apr 2003 12:32:39 -0000
@@ -224,12 +224,9 @@
 #define	MNT_RELOAD	0x00040000	/* reload filesystem data */
 #define	MNT_FORCE	0x00080000	/* force unmount or readonly change */
 #define	MNT_SNAPSHOT	0x01000000	/* snapshot the filesystem */
+#define	MNT_BYFSID	0x08000000	/* specify filesystem by ID. */
 #define MNT_CMDFLAGS   (MNT_UPDATE	| MNT_DELEXPORT	| MNT_RELOAD	| \
-			MNT_FORCE	| MNT_SNAPSHOT)
-/*
- * Still available
- */
-#define	MNT_SPARE3	0x08000000
+			MNT_FORCE	| MNT_SNAPSHOT	| MNT_BYFSID)
 /*
  * Internal filesystem control flags stored in mnt_kern_flag.
  *
Index: sbin/umount/umount.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sbin/umount/umount.c,v
retrieving revision 1.34
diff -u -r1.34 umount.c
--- sbin/umount/umount.c	7 Apr 2003 12:56:01 -0000	1.34
+++ sbin/umount/umount.c	28 Jun 2003 21:05:18 -0000
@@ -54,6 +54,7 @@
 #include <nfs/rpcv2.h>
 
 #include <err.h>
+#include <errno.h>
 #include <fstab.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -72,10 +73,10 @@
 int	fflag, vflag;
 char   *nfshost;
 
-void	 checkmntlist (char *, char **, char **, char **);
+struct statfs *checkmntlist (char *, char **);
 int	 checkvfsname (const char *, char **);
-char	*getmntname (const char *, const char *,
-	 mntwhat, char **, dowhat);
+struct statfs *getmntentry (const char *, const char *, mntwhat, char **,
+	     dowhat);
 char 	*getrealname(char *, char *resolved_path);
 char   **makevfslist (const char *);
 size_t	 mntinfo (struct statfs **);
@@ -83,7 +84,7 @@
 int	 sacmp (struct sockaddr *, struct sockaddr *);
 int	 umountall (char **);
 int	 checkname (char *, char **);
-int	 umountfs (char *, char *, char *);
+int	 umountfs (char *, char *, fsid_t *, char *);
 void	 usage (void);
 int	 xdr_dir (XDR *, char *);
 
@@ -92,8 +93,8 @@
 {
 	int all, errs, ch, mntsize, error;
 	char **typelist = NULL, *mntonname, *mntfromname;
-	char *type, *mntfromnamerev, *mntonnamerev;
-	struct statfs *mntbuf;
+	char *type;
+	struct statfs *mntbuf, *sfsrev;
 	struct addrinfo hints;
 
 	/* Start disks transferring immediately. */
@@ -166,18 +167,16 @@
 			 */
 			mntonname = mntbuf[mntsize].f_mntonname;
 			mntfromname = mntbuf[mntsize].f_mntfromname;
-			mntonnamerev = getmntname(getmntname(mntonname,
-			    NULL, MNTFROM, &type, NAME), NULL,
-			    MNTON, &type, NAME);
 
-			mntfromnamerev = getmntname(mntonnamerev,
-			    NULL, MNTFROM, &type, NAME);
+			sfsrev = getmntentry(mntonname, NULL, MNTON, &type,
+			    NAME);
 
-			if (strcmp(mntonnamerev, mntonname) == 0 &&
-			    strcmp(mntfromnamerev, mntfromname ) != 0)
+			if (!fflag && bcmp(&sfsrev->f_fsid,
+			    &mntbuf[mntsize].f_fsid, sizeof(fsid_t)) != 0) {
 				warnx("cannot umount %s, %s\n        "
 				    "is mounted there, umount it first",
-				    mntonname, mntfromnamerev);
+				    mntonname, sfsrev->f_mntfromname);
+			}
 
 			if (checkname(mntbuf[mntsize].f_mntonname,
 			    typelist) != 0)
@@ -196,7 +195,7 @@
 				errs = 1;
 		break;
 	}
-	(void)getmntname(NULL, NULL, NOTHING, NULL, FREE);
+	(void)getmntentry(NULL, NULL, NOTHING, NULL, FREE);
 	exit(errs);
 }
 
@@ -258,29 +257,29 @@
 {
 	size_t len;
 	int speclen;
-	char *mntonname, *mntfromname;
-	char *mntfromnamerev;
 	char *resolved, realname[MAXPATHLEN];
 	char *type, *hostp, *delimp, *origname;
+	struct statfs *sfs, *sfsrev;
 
 	len = 0;
-	mntfromname = mntonname = delimp = hostp = NULL;
+	delimp = hostp = NULL;
+	sfs = NULL;
 
 	/*
 	 * 1. Check if the name exists in the mounttable.
 	 */
-	(void)checkmntlist(name, &mntfromname, &mntonname, &type);
+	sfs = checkmntlist(name, &type);
 	/*
 	 * 2. Remove trailing slashes if there are any. After that
 	 * we look up the name in the mounttable again.
 	 */
-	if (mntfromname == NULL && mntonname == NULL) {
+	if (sfs == NULL) {
 		speclen = strlen(name);
 		for (speclen = strlen(name); 
 		    speclen > 1 && name[speclen - 1] == '/';
 		    speclen--)
 			name[speclen - 1] = '\0';
-		(void)checkmntlist(name, &mntfromname, &mntonname, &type);
+		sfs = checkmntlist(name, &type);
 		resolved = name;
 		/* Save off original name in origname */
 		if ((origname = strdup(name)) == NULL)
@@ -290,7 +289,7 @@
 		 * has been used and translate it to the ':' syntax.
 		 * Look up the name in the mounttable again.
 		 */
-		if (mntfromname == NULL && mntonname == NULL) {
+		if (sfs == NULL) {
 			if ((delimp = strrchr(name, '@')) != NULL) {
 				hostp = delimp + 1;
 				if (*hostp != '\0') {
@@ -313,8 +312,7 @@
 				    speclen--)
 					name[speclen - 1] = '\0';
 				name[len + speclen + 1] = '\0';
-				(void)checkmntlist(name, &mntfromname,
-				    &mntonname, &type);
+				sfs = checkmntlist(name, &type);
 				resolved = name;
 			}
 			/*
@@ -325,11 +323,10 @@
 			 * basedir of mountpoint and add the dirname again.
 			 * Check the name in mounttable one last time.
 			 */
-			if (mntfromname == NULL && mntonname == NULL) {
+			if (sfs == NULL) {
 				(void)strcpy(name, origname);
 				if ((getrealname(name, realname)) != NULL) {
-					(void)checkmntlist(realname,
-					    &mntfromname, &mntonname, &type);
+					sfs = checkmntlist(realname, &type);
 					resolved = realname;
 				}
 				/*
@@ -343,9 +340,9 @@
 				 * fstat structure get's more reliable,
 				 * but at the moment we cannot thrust it.
 				 */
-				if (mntfromname == NULL && mntonname == NULL) {
+				if (sfs == NULL) {
 					(void)strcpy(name, origname);
-					if (umountfs(NULL, origname,
+					if (umountfs(NULL, origname, NULL,
 					    "none") == 0) {;
 						warnx("%s not found in "
 						    "mount table, "
@@ -370,38 +367,37 @@
 	 * Check if the reverse entrys of the mounttable are really the
 	 * same as the normal ones.
 	 */
-	if ((mntfromnamerev = strdup(getmntname(getmntname(mntfromname,
-	    NULL, MNTON, &type, NAME), NULL, MNTFROM, &type, NAME))) == NULL)
-		err(1, "strdup");
+	sfsrev = getmntentry(sfs->f_mntonname, NULL, MNTON, &type, NAME);
 	/*
 	 * Mark the uppermost mount as unmounted.
 	 */
-	(void)getmntname(mntfromname, mntonname, NOTHING, &type, MARK);
+	(void)getmntentry(sfs->f_mntfromname, sfs->f_mntonname, NOTHING, &type,
+	     MARK);
 	/*
 	 * If several equal mounts are in the mounttable, check the order
 	 * and warn the user if necessary.
 	 */
-	if (strcmp(mntfromnamerev, mntfromname ) != 0 &&
-	    strcmp(resolved, mntonname) != 0) {
+	if (fflag != MNT_FORCE && sfsrev != sfs) {
 		warnx("cannot umount %s, %s\n        "
 		    "is mounted there, umount it first",
-		    mntonname, mntfromnamerev);
+		    sfs->f_mntonname, sfsrev->f_mntfromname);
 
-		/* call getmntname again to set mntcheck[i] to 0 */
-		(void)getmntname(mntfromname, mntonname,
+		/* call getmntentry again to set mntcheck[i] to 0 */
+		(void)getmntentry(sfs->f_mntfromname, sfs->f_mntonname,
 		    NOTHING, &type, UNMARK);
 		return (1);
 	}
-	free(mntfromnamerev);
-	return (umountfs(mntfromname, mntonname, type));
+	return (umountfs(sfs->f_mntfromname, sfs->f_mntonname, &sfs->f_fsid,
+	    type));
 }
 
 /*
  * NFS stuff and unmount(2) call
  */
 int
-umountfs(char *mntfromname, char *mntonname, char *type)
+umountfs(char *mntfromname, char *mntonname, fsid_t *fsid, char *type)
 {
+	char fsidbuf[64];
 	enum clnt_stat clnt_stat;
 	struct timeval try;
 	struct addrinfo *ai, hints;
@@ -439,14 +435,27 @@
 		 * A non-NULL return means that this is the last
 		 * mount from mntfromname that is still mounted.
 		 */
-		if (getmntname(mntfromname, NULL, NOTHING, &type, COUNT)
-		     != NULL)
+		if (getmntentry(mntfromname, NULL, NOTHING, &type, COUNT)
+		    != NULL)
 			do_rpc = 1;
 	}
 
 	if (!namematch(ai))
 		return (1);
-	if (unmount(mntonname, fflag) != 0 ) {
+	/* First try to unmount using the specified filesystem ID. */
+	if (fsid != NULL) {
+		snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", fsid->val[0],
+		    fsid->val[1]);
+		if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
+			warn("unmount of %s failed", mntonname);
+			if (errno != ENOENT)
+				return (1);
+			/* Compatability for old kernels. */
+			warnx("retrying using path instead of filesystem ID");
+			fsid = NULL;
+		}
+	}
+	if (fsid == NULL && unmount(mntonname, fflag) != 0) {
 		warn("unmount of %s failed", mntonname);
 		return (1);
 	}
@@ -490,9 +499,9 @@
 	return (0);
 }
 
-char *
-getmntname(const char *fromname, const char *onname,
-    mntwhat what, char **type, dowhat mark)
+struct statfs *
+getmntentry(const char *fromname, const char *onname, mntwhat what,
+    char **type, dowhat mark)
 {
 	static struct statfs *mntbuf;
 	static size_t mntsize = 0;
@@ -523,21 +532,15 @@
 	case NAME:
 		/* Return only the specific name */
 		for (i = mntsize - 1; i >= 0; i--) {
-			if (fromname != NULL && what == MNTON &&
-			    !strcmp(mntbuf[i].f_mntfromname, fromname) &&
-			    mntcheck[i] != 1) {
+			if (fromname != NULL && !strcmp((what == MNTFROM) ?
+			    mntbuf[i].f_mntfromname : mntbuf[i].f_mntonname,
+			    fromname) && mntcheck[i] != 1) {
 				if (type)
 					*type = mntbuf[i].f_fstypename;
-				return (mntbuf[i].f_mntonname);
-			}
-			if (fromname != NULL && what == MNTFROM &&
-			    !strcmp(mntbuf[i].f_mntonname, fromname) &&
-			    mntcheck[i] != 1) {
-				if (type)
-					*type = mntbuf[i].f_fstypename;
-				return (mntbuf[i].f_mntfromname);
+				return (&mntbuf[i]);
 			}
 		}
+
 		return (NULL);
 	case MARK:
 		/* Mark current mount with '1' and return name */
@@ -546,7 +549,7 @@
 			    (strcmp(mntbuf[i].f_mntonname, onname) == 0) &&
 			    (strcmp(mntbuf[i].f_mntfromname, fromname) == 0)) {
 				mntcheck[i] = 1;
-				return (mntbuf[i].f_mntonname);
+				return (&mntbuf[i]);
 			}
 		}
 		return (NULL);
@@ -557,7 +560,7 @@
 			    (strcmp(mntbuf[i].f_mntonname, onname) == 0) &&
 			    (strcmp(mntbuf[i].f_mntfromname, fromname) == 0)) {
 				mntcheck[i] = 0;
-				return (mntbuf[i].f_mntonname);
+				return (&mntbuf[i]);
 			}
 		}
 		return (NULL);
@@ -582,7 +585,7 @@
 			}
 		}
 		if (count <= 1)
-			return (mntbuf[i].f_mntonname);
+			return (&mntbuf[i]);
 		else
 			return (NULL);
 	case FREE:
@@ -646,17 +649,15 @@
 	return (0);
 }
 
-void
-checkmntlist(char *name, char **fromname, char **onname, char **type)
+struct statfs *
+checkmntlist(char *name, char **type)
 {
+	struct statfs *sfs;
 
-	*fromname = getmntname(name, NULL, MNTFROM, type, NAME);
-	if (*fromname == NULL) {
-		*onname = getmntname(name, NULL, MNTON, type, NAME);
-		if (*onname != NULL)
-			*fromname = name;
-	} else
-		*onname = name;
+	sfs = getmntentry(name, NULL, MNTON, type, NAME);
+	if (sfs == NULL)
+		sfs = getmntentry(name, NULL, MNTFROM, type, NAME);
+	return (sfs);
 }
 
 size_t



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200306282254.aa83607>