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>