quoting << Martin_Blapp

-	Completly changed the internals of umount(8). We do three
	checks now to see if 'argv' is in the mounttable. It they
	all fail, we return to main and print a warning.

-	fixed the umount mount-order. The checks are rather complex
	to do this. Cause umount(8) should also be able to unmount
	several devices at once ('umount -a', 'umount -A',
	'umount /mnt /mnt2'), the mount-order get's important.
	I added checks to mark and unmark already unmounted devices.

- 	Various fixes with nfs-unmounts (no rpc-calls were done,
	or they were done although there was an existing mount).
	Since we allow overlay-mounts, we should also handle them
	properly.

-	Translate the deprecated nfs-syntax with '@' to ':' like
	mount_nfs does. The ':' syntax has now precedence, but '@'
	still works.

-	'umount -v' is now fixed for all cases and doesn't print
	garbage like two times the mountpoint etc.

-	removed non documented and useless umount '-F'.

-	hanged nfsmounts can now unmounted 'without' any problems.
	I've removed stat() and realpath() checks on the mountpoint.
	Instead we just do a realpath() on the basedir of the
	mountpath and add the dirname again.
	Implemented this as an idea from phk. But there are still
	vfs-restrictions if the nfs_mount is busy. If there are
	unwritten metadata on a hanged nfs-mount, and we modify
	nfs_vfsops.c to not return EBUSY, we get a deadlock :(
	The problem has now moved from userland to kernel.

-	removed the BUGS part from the umount(8) manpage.

-	Converted it to ANSI C (more than 60% of the code have
	changed).

Martin_Blapp

Fixed PR's
----------

o [1999/02/03] bin/9893                 NFS umount of regular file impossible

s [1995/11/27] bin/841			stale nfs mounts cannot be umounted

o [1999/08/01] bin/12911	alfred  NFS umounts are not properly done
					if just the mountpoint gets umounted

Only partially solved:
----------------------

The problem is now in kernel:

o [1999/04/07] bin/11005		`umount -f' does not work if the
					NFS-server is down.

PR:		bin/9893 bin/841 bin/12911 bin/11005
Submitted by:	Martin Blapp <mb@imp.ch>
This commit is contained in:
Brian Feldman 1999-10-17 16:26:58 +00:00
parent d8b6eb316f
commit bc70c1725f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=52340
2 changed files with 416 additions and 145 deletions

View File

@ -122,28 +122,6 @@ filesystem table
.Xr unmount 2 ,
.Xr fstab 5 ,
.Xr mount 8
.Sh BUGS
When using union filesystems,
.Xr umount 8
cannot always determine the node which is the mountpoint.
In this case,
it is necessary to specify the relevant directory to be unmounted
in the same form as that displayed by
.Xr mount 8 .
For example, given a mount entry like this:
.Bd -literal -offset indent
<above>/tmpdir on /cdrom (local, user mount)
.Ed
.Pp
then the command:
.Bd -literal -offset indent
umount '<above>/tmpdir'
.Ed
.Pp
would unmount
.Ar /tmpdir
from the mountpoint
.Ar /cdrom .
.Sh HISTORY
A
.Nm

View File

@ -46,7 +46,6 @@ static const char rcsid[] =
#endif /* not lint */
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <netdb.h>
@ -60,35 +59,42 @@ static const char rcsid[] =
#include <string.h>
#include <unistd.h>
typedef enum { MNTON, MNTFROM } mntwhat;
#define ISDOT(x) ((x)[0] == '.' && (x)[1] == '\0')
#define ISDOTDOT(x) ((x)[0] == '.' && (x)[1] == '.' && (x)[2] == '\0')
int fake, fflag, vflag;
char *nfshost;
typedef enum { MNTON, MNTFROM, NOTHING } mntwhat;
typedef enum { MARK, UNMARK, NAME, COUNT, FREE } dowhat;
int checkvfsname __P((const char *, char **));
char *getmntname __P((char *, mntwhat, char **));
char **makevfslist __P((char *));
int selected __P((int));
int namematch __P((struct hostent *));
int umountall __P((char **));
int umountfs __P((char *, char **));
void usage __P((void));
int xdr_dir __P((XDR *, char *));
int fflag, vflag, count;
char *nfshost;
void checkmntlist (char *, char **, char **, char **);
int checkvfsname (const char *, char **);
char *getmntname (const char *, const char *,
mntwhat, char **, dowhat);
char *getrealname(char *, char *resolved_path);
char **makevfslist (const char *);
size_t mntinfo (struct statfs **);
int namematch (struct hostent *);
int selected (int);
int umountall (char **);
int umountfs (char *, char **);
void usage (void);
int xdr_dir (XDR *, char *);
int
main(argc, argv)
int argc;
char *argv[];
main(int argc, char *argv[])
{
int all, ch, errs = 0, mnts;
char **typelist = NULL;
int all, errs, ch, mntsize;
char **typelist = NULL, *mntonname, *mntfromname;
char *type, *mntfromnamerev, *mntonnamerev;
struct statfs *mntbuf;
/* Start disks transferring immediately. */
sync();
all = 0;
while ((ch = getopt(argc, argv, "AaFfh:t:v")) != -1)
all = count = errs = 0;
while ((ch = getopt(argc, argv, "Aafh:t:v")) != -1)
switch (ch) {
case 'A':
all = 2;
@ -96,9 +102,6 @@ main(argc, argv)
case 'a':
all = 1;
break;
case 'F':
fake = 1;
break;
case 'f':
fflag = MNT_FORCE;
break;
@ -108,7 +111,7 @@ main(argc, argv)
break;
case 't':
if (typelist != NULL)
errx(1, "only one -t option may be specified");
err(1, "only one -t option may be specified");
typelist = makevfslist(optarg);
break;
case 'v':
@ -130,17 +133,41 @@ main(argc, argv)
switch (all) {
case 2:
if ((mnts = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
warn("getmntinfo");
errs = 1;
if ((mntsize = mntinfo(&mntbuf)) <= 0)
break;
}
for (errs = 0, mnts--; mnts > 0; mnts--) {
if (checkvfsname(mntbuf[mnts].f_fstypename, typelist))
/*
* We unmount the nfs-mounts in the reverse order
* that they were mounted.
*/
for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
if (checkvfsname(mntbuf[mntsize].f_fstypename,
typelist))
continue;
if (umountfs(mntbuf[mnts].f_mntonname, typelist) != 0)
/*
* Check if a mountpoint is laid over by another mount.
* A warning will be printed to stderr if this is
* the case. The laid over mount remains unmounted.
*/
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);
if (strcmp(mntonnamerev, mntonname) == 0 &&
strcmp(mntfromnamerev, mntfromname ) != 0)
warnx("cannot umount %s, %s\n "
"is mounted there, umount it first",
mntonname, mntfromnamerev);
if (umountfs(mntbuf[mntsize].f_mntonname,
typelist) != 0)
errs = 1;
}
free(mntbuf);
break;
case 1:
if (setfsent() == 0)
@ -153,19 +180,21 @@ main(argc, argv)
errs = 1;
break;
}
(void)getmntname(NULL, NULL, NOTHING, NULL, FREE);
exit(errs);
}
int
umountall(typelist)
char **typelist;
umountall(char **typelist)
{
struct fstab *fs;
int rval;
char *cp;
struct vfsconf vfc;
while ((fs = getfsent()) != NULL) {
if ((fs = getfsent()) != NULL)
errx(1, "fstab reading failure");
do {
/* Ignore the root. */
if (strcmp(fs->fs_file, "/") == 0)
continue;
@ -178,7 +207,7 @@ umountall(typelist)
strcmp(fs->fs_type, FSTAB_RQ))
continue;
/* If an unknown file system type, complain. */
if (getvfsbyname(fs->fs_vfstype, &vfc) < 0) {
if (getvfsbyname(fs->fs_vfstype, &vfc) == -1) {
warnx("%s: unknown mount type", fs->fs_vfstype);
continue;
}
@ -191,93 +220,192 @@ umountall(typelist)
* in some allocated memory, and then call recursively.
*/
if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
errx(1, "malloc failed");
err(1, "malloc failed");
(void)strcpy(cp, fs->fs_file);
rval = umountall(typelist);
return (umountfs(cp, typelist) || rval);
}
} while ((fs = getfsent()) != NULL);
free(cp);
return (0);
}
int
umountfs(name, typelist)
char *name;
char **typelist;
umountfs(char *name, char **typelist)
{
enum clnt_stat clnt_stat;
struct hostent *hp;
struct sockaddr_in saddr;
struct stat sb;
struct timeval pertry, try;
CLIENT *clp;
int so;
char *type, *delimp = NULL, *hostp, *mntpt, *origname;
char rname[MAXPATHLEN];
size_t len;
int so, speclen;
char *mntonname, *mntfromname;
char *mntfromnamerev, *mntonnamerev;
char *nfsdirname, *orignfsdirname;
char *resolved, realname[MAXPATHLEN + 1];
char *type, *delimp, *hostp, *origname;
if (realpath(name, rname) == NULL) {
/* Continue and let the system call check it... */
strcpy(rname, name);
}
mntfromname = mntonname = delimp = hostp = orignfsdirname = NULL;
origname = name;
if (stat(name, &sb) < 0) {
mntpt = rname;
if ((getmntname(rname, MNTFROM, &type) == NULL) &&
((mntpt = getmntname(name, MNTON, &type)) == NULL)) {
warnx("%s: not currently mounted", name);
return (1);
/*
* 1. Check if the name exists in the mounttable.
*/
(void)checkmntlist(name, &mntfromname, &mntonname, &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) {
speclen = strlen(name);
for (speclen = strlen(name);
speclen > 1 && name[speclen - 1] == '/';
speclen--)
name[speclen - 1] = '\0';
(void)checkmntlist(name, &mntfromname, &mntonname, &type);
resolved = name;
/* Save off original name in origname */
if ((origname = strdup(name)) == NULL)
err(1, "strdup");
/*
* 3. Check if the deprecated nfs-syntax with an '@'
* has been used and translate it to the ':' syntax.
* Look up the name in the mounttable again.
*/
if (mntfromname == NULL && mntonname == NULL) {
if ((delimp = strrchr(name, '@')) != NULL) {
hostp = delimp + 1;
if (*hostp != '\0') {
/*
* Make both '@' and ':'
* notations equal
*/
char *host = strdup(hostp);
len = strlen(hostp);
if (host == NULL)
err(1, "strdup");
memmove(name + len + 1, name,
(size_t)(delimp - name));
name[len] = ':';
memmove(name, host, len);
free(host);
}
for (speclen = strlen(name);
speclen > 1 && name[speclen - 1] == '/';
speclen--)
name[speclen - 1] = '\0';
name[len + speclen + 1] = '\0';
(void)checkmntlist(name, &mntfromname,
&mntonname, &type);
resolved = name;
}
/*
* 4. Check if a relative mountpoint has been
* specified. This should happen as last check,
* the order is important. To prevent possible
* nfs-hangs, we just call realpath(3) on the
* basedir of mountpoint and add the dirname again.
* Check the name in mounttable one last time.
*/
if (mntfromname == NULL && mntonname == NULL) {
(void)strcpy(name, origname);
if ((getrealname(name, realname)) != NULL) {
(void)checkmntlist(realname,
&mntfromname, &mntonname, &type);
resolved = realname;
}
/*
* All tests failed, return to main()
*/
if (mntfromname == NULL && mntonname == NULL) {
(void)strcpy(name, origname);
warnx("%s: not currently mounted",
origname);
free(origname);
return (1);
}
}
}
} else if (S_ISBLK(sb.st_mode)) {
if ((mntpt = getmntname(name, MNTON, &type)) == NULL) {
warnx("%s: not currently mounted", name);
return (1);
}
} else if (S_ISDIR(sb.st_mode)) {
mntpt = rname;
if (getmntname(mntpt, MNTFROM, &type) == NULL) {
warnx("%s: not currently mounted", name);
return (1);
}
} else {
warnx("%s: not a directory or special device", name);
return (1);
}
name = rname;
free(origname);
} else
resolved = name;
if (checkvfsname(type, typelist))
return (1);
hp = NULL;
nfsdirname = NULL;
if (!strcmp(type, "nfs")) {
if ((delimp = strchr(name, '@')) != NULL) {
hostp = delimp + 1;
if ((nfsdirname = strdup(mntfromname)) == NULL)
err(1, "strdup");
orignfsdirname = nfsdirname;
if ((delimp = strchr(nfsdirname, ':')) != NULL) {
*delimp = '\0';
hp = gethostbyname(hostp);
*delimp = '@';
} else if ((delimp = strchr(name, ':')) != NULL) {
*delimp = '\0';
hostp = name;
hp = gethostbyname(hostp);
name = delimp + 1;
*delimp = ':';
hostp = nfsdirname;
if ((hp = gethostbyname(hostp)) == NULL) {
warnx("can't get net id for host");
}
nfsdirname = delimp + 1;
}
}
/*
* 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");
if ((mntonnamerev = strdup(getmntname(mntfromnamerev, NULL,
MNTON, &type, NAME))) == NULL)
err(1, "strdup");
/*
* Mark the uppermost mount as unmounted.
*/
(void)getmntname(mntfromnamerev, mntonnamerev, 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) {
warnx("cannot umount %s, %s\n "
"is mounted there, umount it first",
mntonname, mntfromnamerev);
if (!namematch(hp))
return (1);
if (vflag)
(void)printf("%s: unmount from %s\n", origname, mntpt);
if (fake)
return (0);
if (unmount(mntpt, fflag) < 0) {
warn("%s", mntpt);
/* call getmntname again to set mntcheck[i] to 0 */
(void)getmntname(mntfromnamerev, mntonnamerev,
NOTHING, &type, UNMARK);
return (1);
}
if ((hp != NULL) && !(fflag & MNT_FORCE)) {
*delimp = '\0';
free(mntonnamerev);
free(mntfromnamerev);
/*
* Check if we have to start the rpc-call later.
* If there are still identical nfs-names mounted,
* we skip the rpc-call. Obviously this has to
* happen before unmount(2), but it should happen
* after the previous namecheck.
*/
if (!strcmp(type, "nfs")) {
if (getmntname(mntfromname, NULL, NOTHING,
&type, COUNT) == NULL)
count = 1;
}
else
count = 0;
if (!namematch(hp))
return (1);
if (unmount(mntonname, fflag) != 0 ) {
warn("unmount of %s failed", mntonname);
return (1);
}
if (vflag)
(void)printf("%s: unmount from %s\n", mntfromname, mntonname);
/*
* Report to mountd-server which nfsname
* has been unmounted.
*/
if (hp != NULL && !(fflag & MNT_FORCE) && count == 0) {
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = 0;
@ -294,12 +422,13 @@ umountfs(name, typelist)
clp->cl_auth = authunix_create_default();
try.tv_sec = 20;
try.tv_usec = 0;
clnt_stat = clnt_call(clp,
RPCMNT_UMOUNT, xdr_dir, name, xdr_void, (caddr_t)0, try);
clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir,
nfsdirname, xdr_void, (caddr_t)0, try);
if (clnt_stat != RPC_SUCCESS) {
clnt_perror(clp, "Bad MNT RPC");
return (1);
}
free(orignfsdirname);
auth_destroy(clp->cl_auth);
clnt_destroy(clp);
}
@ -307,38 +436,110 @@ umountfs(name, typelist)
}
char *
getmntname(name, what, type)
char *name;
mntwhat what;
char **type;
getmntname(const char *fromname, const char *onname,
mntwhat what, char **type, dowhat mark)
{
static struct statfs *mntbuf;
static int mntsize;
int i;
static size_t mntsize = 0;
static char *mntcheck = NULL;
static char *mntcount = NULL;
int i, count;
if (mntbuf == NULL &&
(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
warn("getmntinfo");
if (mntsize <= 0) {
if ((mntsize = mntinfo(&mntbuf)) <= 0)
return (NULL);
}
if (mntcheck == NULL) {
if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL ||
(mntcount = calloc(mntsize + 1, sizeof(int))) == NULL)
err(1, "calloc");
}
/*
* We want to get the file systems in the reverse order
* that they were mounted. Mounted and unmounted filesystems
* are marked or unmarked in a table called 'mntcheck'.
* Unmount(const char *dir, int flags) does only take the
* mountpoint as argument, not the destination. If we don't pay
* attention to the order, it can happen that a overlaying
* filesystem get's unmounted instead of the one the user
* has choosen.
*/
switch (mark) {
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 (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 (NULL);
case MARK:
/* Mark current mount with '1' and return name */
for (i = mntsize - 1; i >= 0; i--) {
if (mntcheck[i] == 0 &&
(strcmp(mntbuf[i].f_mntonname, onname) == 0) &&
(strcmp(mntbuf[i].f_mntfromname, fromname) == 0)) {
mntcheck[i] = 1;
return (mntbuf[i].f_mntonname);
}
}
return (NULL);
case UNMARK:
/* Unmark current mount with '0' and return name */
for (i = 0; i < mntsize; i++) {
if (mntcheck[i] == 1 &&
(strcmp(mntbuf[i].f_mntonname, onname) == 0) &&
(strcmp(mntbuf[i].f_mntfromname, fromname) == 0)) {
mntcheck[i] = 0;
return (mntbuf[i].f_mntonname);
}
}
return (NULL);
case COUNT:
/* Count the equal mntfromnames */
count = 0;
for (i = mntsize - 1; i >= 0; i--) {
if (strcmp(mntbuf[i].f_mntfromname, fromname) == 0)
count++;
}
/* Mark the already unmounted mounts and return
* mntfromname if count == 1. Else return NULL.
*/
for (i = mntsize - 1; i >= 0; i--) {
if (strcmp(mntbuf[i].f_mntfromname, fromname) == 0) {
if (mntcount[i] == 1)
count--;
else
mntcount[i] = 1;
if (count == 1)
return (mntbuf[i].f_mntonname);
else
return (NULL);
}
}
return (NULL);
case FREE:
free(mntbuf);
free(mntcheck);
return (NULL);
default:
return (NULL);
}
for (i = 0; i < mntsize; i++) {
if ((what == MNTON) && !strcmp(mntbuf[i].f_mntfromname, name)) {
if (type)
*type = mntbuf[i].f_fstypename;
return (mntbuf[i].f_mntonname);
}
if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) {
if (type)
*type = mntbuf[i].f_fstypename;
return (mntbuf[i].f_mntfromname);
}
}
return (NULL);
}
int
namematch(hp)
struct hostent *hp;
namematch(struct hostent *hp)
{
char *cp, **np;
@ -365,20 +566,112 @@ namematch(hp)
return (0);
}
void
checkmntlist(char *name, char **fromname, char **onname, char **type)
{
*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;
}
size_t
mntinfo(struct statfs **mntbuf)
{
static struct statfs *origbuf;
size_t bufsize;
int mntsize;
mntsize = getfsstat(NULL, 0l, MNT_NOWAIT);
if (mntsize <= 0)
return (0);
bufsize = (mntsize + 1) * sizeof(struct statfs);
if ((origbuf = malloc(bufsize)) == NULL)
err(1, "malloc");
mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
*mntbuf = origbuf;
return (mntsize);
}
char *
getrealname(char *name, char *realname)
{
char *dirname;
int havedir;
size_t baselen;
size_t dirlen;
dirname = '\0';
havedir = 0;
if (*name == '/') {
if (ISDOT(name + 1) || ISDOTDOT(name + 1))
strcpy(realname, "/");
else {
if ((dirname = strrchr(name + 1, '/')) == NULL)
strncpy(realname, name, MAXPATHLEN);
else
havedir = 1;
}
} else {
if (ISDOT(name) || ISDOTDOT(name))
(void)realpath(name, realname);
else {
if ((dirname = strrchr(name, '/')) == NULL) {
if ((realpath(name, realname)) == NULL)
return (NULL);
} else
havedir = 1;
}
}
if (havedir) {
*dirname++ = '\0';
if (ISDOT(dirname)) {
*dirname = '\0';
if ((realpath(name, realname)) == NULL)
return (NULL);
} else if (ISDOTDOT(dirname)) {
*--dirname = '/';
if ((realpath(name, realname)) == NULL)
return (NULL);
} else {
if ((realpath(name, realname)) == NULL)
return (NULL);
baselen = strlen(realname);
dirlen = strlen(dirname);
if (baselen + dirlen + 1 > MAXPATHLEN)
return (NULL);
if (realname[1] == '\0') {
memmove(realname + 1, dirname, dirlen);
realname[dirlen + 1] = '\0';
} else {
realname[baselen] = '/';
memmove(realname + baselen + 1,
dirname, dirlen);
realname[baselen + dirlen + 1] = '\0';
}
}
}
return (realname);
}
/*
* xdr routines for mount rpc's
*/
int
xdr_dir(xdrsp, dirp)
XDR *xdrsp;
char *dirp;
xdr_dir(XDR *xdrsp, char *dirp)
{
return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
}
void
usage()
{
(void)fprintf(stderr, "%s\n%s\n",
"usage: umount [-fv] special | node",
" umount -a | -A [-fv] [-h host] [-t type]");