xref: /freebsd/sbin/umount/umount.c (revision 1efe3c6b58ea5d0e5a0d49039d84d00f2370c7de)
18fae3551SRodney W. Grimes /*-
28fae3551SRodney W. Grimes  * Copyright (c) 1980, 1989, 1993
38fae3551SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
48fae3551SRodney W. Grimes  *
58fae3551SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
68fae3551SRodney W. Grimes  * modification, are permitted provided that the following conditions
78fae3551SRodney W. Grimes  * are met:
88fae3551SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
98fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
108fae3551SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
118fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
128fae3551SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
138fae3551SRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
148fae3551SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
158fae3551SRodney W. Grimes  *    without specific prior written permission.
168fae3551SRodney W. Grimes  *
178fae3551SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
188fae3551SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
198fae3551SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
208fae3551SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
218fae3551SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
228fae3551SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
238fae3551SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
248fae3551SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
258fae3551SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
268fae3551SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
278fae3551SRodney W. Grimes  * SUCH DAMAGE.
288fae3551SRodney W. Grimes  */
298fae3551SRodney W. Grimes 
308fae3551SRodney W. Grimes #ifndef lint
31adb378ceSPhilippe Charnier static const char copyright[] =
328fae3551SRodney W. Grimes "@(#) Copyright (c) 1980, 1989, 1993\n\
338fae3551SRodney W. Grimes 	The Regents of the University of California.  All rights reserved.\n";
348fae3551SRodney W. Grimes #endif /* not lint */
358fae3551SRodney W. Grimes 
368fae3551SRodney W. Grimes #ifndef lint
37adb378ceSPhilippe Charnier #if 0
38d499a0efSBruce Evans static char sccsid[] = "@(#)umount.c	8.8 (Berkeley) 5/8/95";
39adb378ceSPhilippe Charnier #endif
40adb378ceSPhilippe Charnier static const char rcsid[] =
417f3dea24SPeter Wemm   "$FreeBSD$";
428fae3551SRodney W. Grimes #endif /* not lint */
438fae3551SRodney W. Grimes 
448fae3551SRodney W. Grimes #include <sys/param.h>
458fae3551SRodney W. Grimes #include <sys/mount.h>
468360efbdSAlfred Perlstein #include <sys/socket.h>
47eddb4805SIan Dowse #include <sys/stat.h>
488fae3551SRodney W. Grimes 
498fae3551SRodney W. Grimes #include <netdb.h>
508fae3551SRodney W. Grimes #include <rpc/rpc.h>
510775314bSDoug Rabson #include <rpcsvc/mount.h>
528fae3551SRodney W. Grimes 
5338f102c2SIan Dowse #include <ctype.h>
548fae3551SRodney W. Grimes #include <err.h>
55318f2fb4SIan Dowse #include <errno.h>
568fae3551SRodney W. Grimes #include <fstab.h>
578fae3551SRodney W. Grimes #include <stdio.h>
588fae3551SRodney W. Grimes #include <stdlib.h>
598fae3551SRodney W. Grimes #include <string.h>
608fae3551SRodney W. Grimes #include <unistd.h>
618fae3551SRodney W. Grimes 
62a69497d7SMatthew Dillon #include "mounttab.h"
63a69497d7SMatthew Dillon 
64eddb4805SIan Dowse typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat;
65bc70c172SBrian Feldman 
66*1efe3c6bSEd Schouten static struct addrinfo *nfshost_ai = NULL;
67*1efe3c6bSEd Schouten static int	fflag, vflag;
68*1efe3c6bSEd Schouten static char	*nfshost;
698fae3551SRodney W. Grimes 
701add162cSIan Dowse struct statfs *checkmntlist(char *);
71bc70c172SBrian Feldman int	 checkvfsname (const char *, char **);
72eddb4805SIan Dowse struct statfs *getmntentry(const char *fromname, const char *onname,
73eddb4805SIan Dowse 	     fsid_t *fsid, dowhat what);
74bc70c172SBrian Feldman char   **makevfslist (const char *);
75bc70c172SBrian Feldman size_t	 mntinfo (struct statfs **);
768360efbdSAlfred Perlstein int	 namematch (struct addrinfo *);
77eddb4805SIan Dowse int	 parsehexfsid(const char *hex, fsid_t *fsid);
787f471a32SXin LI int	 sacmp (void *, void *);
79bc70c172SBrian Feldman int	 umountall (char **);
808360efbdSAlfred Perlstein int	 checkname (char *, char **);
81eddb4805SIan Dowse int	 umountfs(struct statfs *sfs);
82bc70c172SBrian Feldman void	 usage (void);
83bc70c172SBrian Feldman int	 xdr_dir (XDR *, char *);
848fae3551SRodney W. Grimes 
858fae3551SRodney W. Grimes int
86bc70c172SBrian Feldman main(int argc, char *argv[])
878fae3551SRodney W. Grimes {
888360efbdSAlfred Perlstein 	int all, errs, ch, mntsize, error;
89f73d495fSIan Dowse 	char **typelist = NULL;
90f73d495fSIan Dowse 	struct statfs *mntbuf, *sfs;
918360efbdSAlfred Perlstein 	struct addrinfo hints;
928fae3551SRodney W. Grimes 
930fe9a7daSBrian Feldman 	all = errs = 0;
94ef258dd9SMatthew N. Dodd 	while ((ch = getopt(argc, argv, "AaF:fh:t:v")) != -1)
958fae3551SRodney W. Grimes 		switch (ch) {
96d499a0efSBruce Evans 		case 'A':
97d499a0efSBruce Evans 			all = 2;
98d499a0efSBruce Evans 			break;
998fae3551SRodney W. Grimes 		case 'a':
1008fae3551SRodney W. Grimes 			all = 1;
1018fae3551SRodney W. Grimes 			break;
102ef258dd9SMatthew N. Dodd 		case 'F':
103ef258dd9SMatthew N. Dodd 			setfstab(optarg);
104ef258dd9SMatthew N. Dodd 			break;
1058fae3551SRodney W. Grimes 		case 'f':
1068fae3551SRodney W. Grimes 			fflag = MNT_FORCE;
1078fae3551SRodney W. Grimes 			break;
108d499a0efSBruce Evans 		case 'h':	/* -h implies -A. */
109d499a0efSBruce Evans 			all = 2;
1108fae3551SRodney W. Grimes 			nfshost = optarg;
1118fae3551SRodney W. Grimes 			break;
1128fae3551SRodney W. Grimes 		case 't':
113d499a0efSBruce Evans 			if (typelist != NULL)
114bc70c172SBrian Feldman 				err(1, "only one -t option may be specified");
115d499a0efSBruce Evans 			typelist = makevfslist(optarg);
1168fae3551SRodney W. Grimes 			break;
1178fae3551SRodney W. Grimes 		case 'v':
1188fae3551SRodney W. Grimes 			vflag = 1;
1198fae3551SRodney W. Grimes 			break;
1208fae3551SRodney W. Grimes 		default:
1218fae3551SRodney W. Grimes 			usage();
1228fae3551SRodney W. Grimes 			/* NOTREACHED */
1238fae3551SRodney W. Grimes 		}
1248fae3551SRodney W. Grimes 	argc -= optind;
1258fae3551SRodney W. Grimes 	argv += optind;
1268fae3551SRodney W. Grimes 
127b37ce154SRick Macklem 	/* Start disks transferring immediately. */
128b37ce154SRick Macklem 	if ((fflag & MNT_FORCE) == 0)
129b37ce154SRick Macklem 		sync();
130b37ce154SRick Macklem 
131adb378ceSPhilippe Charnier 	if ((argc == 0 && !all) || (argc != 0 && all))
1328fae3551SRodney W. Grimes 		usage();
1338fae3551SRodney W. Grimes 
1348fae3551SRodney W. Grimes 	/* -h implies "-t nfs" if no -t flag. */
1358fae3551SRodney W. Grimes 	if ((nfshost != NULL) && (typelist == NULL))
136d499a0efSBruce Evans 		typelist = makevfslist("nfs");
1378fae3551SRodney W. Grimes 
1388360efbdSAlfred Perlstein 	if (nfshost != NULL) {
1398360efbdSAlfred Perlstein 		memset(&hints, 0, sizeof hints);
1408360efbdSAlfred Perlstein 		error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai);
14121eff82fSIan Dowse 		if (error)
14213e2e1afSIan Dowse 			errx(1, "%s: %s", nfshost, gai_strerror(error));
1438360efbdSAlfred Perlstein 	}
1448360efbdSAlfred Perlstein 
145d499a0efSBruce Evans 	switch (all) {
146d499a0efSBruce Evans 	case 2:
147bc70c172SBrian Feldman 		if ((mntsize = mntinfo(&mntbuf)) <= 0)
148d499a0efSBruce Evans 			break;
149bc70c172SBrian Feldman 		/*
150bc70c172SBrian Feldman 		 * We unmount the nfs-mounts in the reverse order
151bc70c172SBrian Feldman 		 * that they were mounted.
152bc70c172SBrian Feldman 		 */
153bc70c172SBrian Feldman 		for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
154f73d495fSIan Dowse 			sfs = &mntbuf[mntsize];
155f73d495fSIan Dowse 			if (checkvfsname(sfs->f_fstypename, typelist))
156d499a0efSBruce Evans 				continue;
15780bbaeb5SKirk McKusick 			if (strcmp(sfs->f_mntonname, "/dev") == 0)
15880bbaeb5SKirk McKusick 				continue;
159eddb4805SIan Dowse 			if (umountfs(sfs) != 0)
160d499a0efSBruce Evans 				errs = 1;
161d499a0efSBruce Evans 		}
162bc70c172SBrian Feldman 		free(mntbuf);
163d499a0efSBruce Evans 		break;
164d499a0efSBruce Evans 	case 1:
1658fae3551SRodney W. Grimes 		if (setfsent() == 0)
166ef258dd9SMatthew N. Dodd 			err(1, "%s", getfstab());
167d499a0efSBruce Evans 		errs = umountall(typelist);
168d499a0efSBruce Evans 		break;
169d499a0efSBruce Evans 	case 0:
1708fae3551SRodney W. Grimes 		for (errs = 0; *argv != NULL; ++argv)
1718360efbdSAlfred Perlstein 			if (checkname(*argv, typelist) != 0)
172d499a0efSBruce Evans 				errs = 1;
173d499a0efSBruce Evans 		break;
174d499a0efSBruce Evans 	}
1758fae3551SRodney W. Grimes 	exit(errs);
1768fae3551SRodney W. Grimes }
1778fae3551SRodney W. Grimes 
1788fae3551SRodney W. Grimes int
179bc70c172SBrian Feldman umountall(char **typelist)
1808fae3551SRodney W. Grimes {
1815965373eSMaxime Henrion 	struct xvfsconf vfc;
1828fae3551SRodney W. Grimes 	struct fstab *fs;
183adb378ceSPhilippe Charnier 	int rval;
1848fae3551SRodney W. Grimes 	char *cp;
1850602ee7cSBrian Feldman 	static int firstcall = 1;
1868fae3551SRodney W. Grimes 
18791a81678SBrian Feldman 	if ((fs = getfsent()) != NULL)
1880602ee7cSBrian Feldman 		firstcall = 0;
18991a81678SBrian Feldman 	else if (firstcall)
19091a81678SBrian Feldman 		errx(1, "fstab reading failure");
19191a81678SBrian Feldman 	else
19291a81678SBrian Feldman 		return (0);
193bc70c172SBrian Feldman 	do {
1948fae3551SRodney W. Grimes 		/* Ignore the root. */
1958fae3551SRodney W. Grimes 		if (strcmp(fs->fs_file, "/") == 0)
1968fae3551SRodney W. Grimes 			continue;
1978fae3551SRodney W. Grimes 		/*
1988fae3551SRodney W. Grimes 		 * !!!
1998fae3551SRodney W. Grimes 		 * Historic practice: ignore unknown FSTAB_* fields.
2008fae3551SRodney W. Grimes 		 */
2018fae3551SRodney W. Grimes 		if (strcmp(fs->fs_type, FSTAB_RW) &&
2028fae3551SRodney W. Grimes 		    strcmp(fs->fs_type, FSTAB_RO) &&
2038fae3551SRodney W. Grimes 		    strcmp(fs->fs_type, FSTAB_RQ))
2048fae3551SRodney W. Grimes 			continue;
205b6e55a05SDag-Erling Smørgrav 		/* Ignore unknown file system types. */
20681667275SDag-Erling Smørgrav 		if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
2078fae3551SRodney W. Grimes 			continue;
208d499a0efSBruce Evans 		if (checkvfsname(fs->fs_vfstype, typelist))
2098fae3551SRodney W. Grimes 			continue;
2108fae3551SRodney W. Grimes 
2118fae3551SRodney W. Grimes 		/*
2128fae3551SRodney W. Grimes 		 * We want to unmount the file systems in the reverse order
2138fae3551SRodney W. Grimes 		 * that they were mounted.  So, we save off the file name
2148fae3551SRodney W. Grimes 		 * in some allocated memory, and then call recursively.
2158fae3551SRodney W. Grimes 		 */
2168fae3551SRodney W. Grimes 		if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
217bc70c172SBrian Feldman 			err(1, "malloc failed");
2188fae3551SRodney W. Grimes 		(void)strcpy(cp, fs->fs_file);
219d499a0efSBruce Evans 		rval = umountall(typelist);
2208360efbdSAlfred Perlstein 		rval = checkname(cp, typelist) || rval;
221bc70c172SBrian Feldman 		free(cp);
2220602ee7cSBrian Feldman 		return (rval);
2230602ee7cSBrian Feldman 	} while ((fs = getfsent()) != NULL);
2248fae3551SRodney W. Grimes 	return (0);
2258fae3551SRodney W. Grimes }
2268fae3551SRodney W. Grimes 
2278360efbdSAlfred Perlstein /*
228eddb4805SIan Dowse  * Do magic checks on mountpoint/device/fsid, and then call unmount(2).
2298360efbdSAlfred Perlstein  */
2308fae3551SRodney W. Grimes int
2317f471a32SXin LI checkname(char *mntname, char **typelist)
2328fae3551SRodney W. Grimes {
233eddb4805SIan Dowse 	char buf[MAXPATHLEN];
234eddb4805SIan Dowse 	struct statfs sfsbuf;
235eddb4805SIan Dowse 	struct stat sb;
236f73d495fSIan Dowse 	struct statfs *sfs;
237eddb4805SIan Dowse 	char *delimp;
238eddb4805SIan Dowse 	dev_t dev;
239eddb4805SIan Dowse 	int len;
2408fae3551SRodney W. Grimes 
241bc70c172SBrian Feldman 	/*
242bc70c172SBrian Feldman 	 * 1. Check if the name exists in the mounttable.
243bc70c172SBrian Feldman 	 */
2447f471a32SXin LI 	sfs = checkmntlist(mntname);
245bc70c172SBrian Feldman 	/*
246bc70c172SBrian Feldman 	 * 2. Remove trailing slashes if there are any. After that
247bc70c172SBrian Feldman 	 * we look up the name in the mounttable again.
248bc70c172SBrian Feldman 	 */
249318f2fb4SIan Dowse 	if (sfs == NULL) {
2507f471a32SXin LI 		len = strlen(mntname);
2517f471a32SXin LI 		while (len > 1 && mntname[len - 1] == '/')
2527f471a32SXin LI 			mntname[--len] = '\0';
2537f471a32SXin LI 		sfs = checkmntlist(mntname);
254bc70c172SBrian Feldman 	}
255bc70c172SBrian Feldman 	/*
256eddb4805SIan Dowse 	 * 3. Check if the deprecated NFS syntax with an '@' has been used
257eddb4805SIan Dowse 	 * and translate it to the ':' syntax. Look up the name in the
258eddb4805SIan Dowse 	 * mount table again.
259bc70c172SBrian Feldman 	 */
2607f471a32SXin LI 	if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) {
2617f471a32SXin LI 		snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1,
2627f471a32SXin LI 		    (int)(delimp - mntname), mntname);
263eddb4805SIan Dowse 		len = strlen(buf);
2645fff0914SIan Dowse 		while (len > 1 && buf[len - 1] == '/')
265eddb4805SIan Dowse 			buf[--len] = '\0';
266eddb4805SIan Dowse 		sfs = checkmntlist(buf);
267bc70c172SBrian Feldman 	}
268bc70c172SBrian Feldman 	/*
269eddb4805SIan Dowse 	 * 4. Resort to a statfs(2) call. This is the last check so that
270eddb4805SIan Dowse 	 * hung NFS filesystems for example can be unmounted without
271eddb4805SIan Dowse 	 * potentially blocking forever in statfs() as long as the
272eddb4805SIan Dowse 	 * filesystem is specified unambiguously. This covers all the
273eddb4805SIan Dowse 	 * hard cases such as symlinks and mismatches between the
274eddb4805SIan Dowse 	 * mount list and reality.
275eddb4805SIan Dowse 	 * We also do this if an ambiguous mount point was specified.
276bc70c172SBrian Feldman 	 */
2777f471a32SXin LI 	if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL &&
2787f471a32SXin LI 	    getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) {
2797f471a32SXin LI 		if (statfs(mntname, &sfsbuf) != 0) {
2807f471a32SXin LI 			warn("%s: statfs", mntname);
2817f471a32SXin LI 		} else if (stat(mntname, &sb) != 0) {
2827f471a32SXin LI 			warn("%s: stat", mntname);
283eddb4805SIan Dowse 		} else if (S_ISDIR(sb.st_mode)) {
2847f471a32SXin LI 			/* Check that `mntname' is the root directory. */
285eddb4805SIan Dowse 			dev = sb.st_dev;
2867f471a32SXin LI 			snprintf(buf, sizeof(buf), "%s/..", mntname);
287eddb4805SIan Dowse 			if (stat(buf, &sb) != 0) {
288eddb4805SIan Dowse 				warn("%s: stat", buf);
289eddb4805SIan Dowse 			} else if (sb.st_dev == dev) {
290eddb4805SIan Dowse 				warnx("%s: not a file system root directory",
2917f471a32SXin LI 				    mntname);
292eddb4805SIan Dowse 				return (1);
2938360efbdSAlfred Perlstein 			} else
294eddb4805SIan Dowse 				sfs = &sfsbuf;
295eddb4805SIan Dowse 		}
296eddb4805SIan Dowse 	}
297eddb4805SIan Dowse 	if (sfs == NULL) {
2987f471a32SXin LI 		warnx("%s: unknown file system", mntname);
2998fae3551SRodney W. Grimes 		return (1);
3008fae3551SRodney W. Grimes 	}
3011add162cSIan Dowse 	if (checkvfsname(sfs->f_fstypename, typelist))
302d499a0efSBruce Evans 		return (1);
303eddb4805SIan Dowse 	return (umountfs(sfs));
3048360efbdSAlfred Perlstein }
3058360efbdSAlfred Perlstein 
3068360efbdSAlfred Perlstein /*
3078360efbdSAlfred Perlstein  * NFS stuff and unmount(2) call
3088360efbdSAlfred Perlstein  */
3098360efbdSAlfred Perlstein int
310eddb4805SIan Dowse umountfs(struct statfs *sfs)
3118360efbdSAlfred Perlstein {
312318f2fb4SIan Dowse 	char fsidbuf[64];
3138360efbdSAlfred Perlstein 	enum clnt_stat clnt_stat;
3148360efbdSAlfred Perlstein 	struct timeval try;
3158360efbdSAlfred Perlstein 	struct addrinfo *ai, hints;
3168360efbdSAlfred Perlstein 	int do_rpc;
3178360efbdSAlfred Perlstein 	CLIENT *clp;
3188360efbdSAlfred Perlstein 	char *nfsdirname, *orignfsdirname;
3198360efbdSAlfred Perlstein 	char *hostp, *delimp;
3208360efbdSAlfred Perlstein 
3218360efbdSAlfred Perlstein 	ai = NULL;
32213e2e1afSIan Dowse 	do_rpc = 0;
32313e2e1afSIan Dowse 	hostp = NULL;
3248360efbdSAlfred Perlstein 	nfsdirname = delimp = orignfsdirname = NULL;
3258360efbdSAlfred Perlstein 	memset(&hints, 0, sizeof hints);
3268360efbdSAlfred Perlstein 
327eddb4805SIan Dowse 	if (strcmp(sfs->f_fstypename, "nfs") == 0) {
328eddb4805SIan Dowse 		if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL)
3298360efbdSAlfred Perlstein 			err(1, "strdup");
3308360efbdSAlfred Perlstein 		orignfsdirname = nfsdirname;
331a505d435SHajimu UMEMOTO 		if (*nfsdirname == '[' &&
332a505d435SHajimu UMEMOTO 		    (delimp = strchr(nfsdirname + 1, ']')) != NULL &&
333a505d435SHajimu UMEMOTO 		    *(delimp + 1) == ':') {
334a505d435SHajimu UMEMOTO 			hostp = nfsdirname + 1;
335a505d435SHajimu UMEMOTO 			nfsdirname = delimp + 2;
336a505d435SHajimu UMEMOTO 		} else if ((delimp = strrchr(nfsdirname, ':')) != NULL) {
3378360efbdSAlfred Perlstein 			hostp = nfsdirname;
338a505d435SHajimu UMEMOTO 			nfsdirname = delimp + 1;
339a505d435SHajimu UMEMOTO 		}
340a505d435SHajimu UMEMOTO 		if (hostp != NULL) {
341a505d435SHajimu UMEMOTO 			*delimp = '\0';
3428360efbdSAlfred Perlstein 			getaddrinfo(hostp, NULL, &hints, &ai);
3438360efbdSAlfred Perlstein 			if (ai == NULL) {
3448360efbdSAlfred Perlstein 				warnx("can't get net id for host");
3458360efbdSAlfred Perlstein 			}
3468360efbdSAlfred Perlstein 		}
34713e2e1afSIan Dowse 
348bc70c172SBrian Feldman 		/*
349bc70c172SBrian Feldman 		 * Check if we have to start the rpc-call later.
350bc70c172SBrian Feldman 		 * If there are still identical nfs-names mounted,
351bc70c172SBrian Feldman 		 * we skip the rpc-call. Obviously this has to
352bc70c172SBrian Feldman 		 * happen before unmount(2), but it should happen
353bc70c172SBrian Feldman 		 * after the previous namecheck.
35413e2e1afSIan Dowse 		 * A non-NULL return means that this is the last
35513e2e1afSIan Dowse 		 * mount from mntfromname that is still mounted.
356bc70c172SBrian Feldman 		 */
357eddb4805SIan Dowse 		if (getmntentry(sfs->f_mntfromname, NULL, NULL,
358eddb4805SIan Dowse 		    CHECKUNIQUE) != NULL)
3590fe9a7daSBrian Feldman 			do_rpc = 1;
36013e2e1afSIan Dowse 	}
3618360efbdSAlfred Perlstein 
3628360efbdSAlfred Perlstein 	if (!namematch(ai))
363d499a0efSBruce Evans 		return (1);
364eddb4805SIan Dowse 	/* First try to unmount using the file system ID. */
365eddb4805SIan Dowse 	snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0],
366eddb4805SIan Dowse 	    sfs->f_fsid.val[1]);
367318f2fb4SIan Dowse 	if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
3680ed25a9aSIan Dowse 		/* XXX, non-root users get a zero fsid, so don't warn. */
3690ed25a9aSIan Dowse 		if (errno != ENOENT || sfs->f_fsid.val[0] != 0 ||
3700ed25a9aSIan Dowse 		    sfs->f_fsid.val[1] != 0)
371eddb4805SIan Dowse 			warn("unmount of %s failed", sfs->f_mntonname);
372318f2fb4SIan Dowse 		if (errno != ENOENT)
373318f2fb4SIan Dowse 			return (1);
3748b918187SIan Dowse 		/* Compatibility for old kernels. */
3758b918187SIan Dowse 		if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0)
376318f2fb4SIan Dowse 			warnx("retrying using path instead of file system ID");
377eddb4805SIan Dowse 		if (unmount(sfs->f_mntonname, fflag) != 0) {
378eddb4805SIan Dowse 			warn("unmount of %s failed", sfs->f_mntonname);
3798fae3551SRodney W. Grimes 			return (1);
3808fae3551SRodney W. Grimes 		}
381eddb4805SIan Dowse 	}
382eddb4805SIan Dowse 	/* Mark this this file system as unmounted. */
383eddb4805SIan Dowse 	getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE);
384bc70c172SBrian Feldman 	if (vflag)
385eddb4805SIan Dowse 		(void)printf("%s: unmount from %s\n", sfs->f_mntfromname,
386eddb4805SIan Dowse 		    sfs->f_mntonname);
387bc70c172SBrian Feldman 	/*
388bc70c172SBrian Feldman 	 * Report to mountd-server which nfsname
389bc70c172SBrian Feldman 	 * has been unmounted.
390bc70c172SBrian Feldman 	 */
3918360efbdSAlfred Perlstein 	if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) {
3920775314bSDoug Rabson 		clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS, "udp");
3938360efbdSAlfred Perlstein 		if (clp  == NULL) {
39413e2e1afSIan Dowse 			warnx("%s: %s", hostp,
3950775314bSDoug Rabson 			    clnt_spcreateerror("MOUNTPROG"));
3968fae3551SRodney W. Grimes 			return (1);
3978fae3551SRodney W. Grimes 		}
3988360efbdSAlfred Perlstein 		clp->cl_auth = authsys_create_default();
3998fae3551SRodney W. Grimes 		try.tv_sec = 20;
4008fae3551SRodney W. Grimes 		try.tv_usec = 0;
4010775314bSDoug Rabson 		clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir,
4025c514aeeSMatthew N. Dodd 		    nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try);
4038fae3551SRodney W. Grimes 		if (clnt_stat != RPC_SUCCESS) {
40413e2e1afSIan Dowse 			warnx("%s: %s", hostp,
40513e2e1afSIan Dowse 			    clnt_sperror(clp, "RPCMNT_UMOUNT"));
4068fae3551SRodney W. Grimes 			return (1);
4078fae3551SRodney W. Grimes 		}
408a69497d7SMatthew Dillon 		/*
409a69497d7SMatthew Dillon 		 * Remove the unmounted entry from /var/db/mounttab.
410a69497d7SMatthew Dillon 		 */
411afe1ef24SIan Dowse 		if (read_mtab()) {
412afe1ef24SIan Dowse 			clean_mtab(hostp, nfsdirname, vflag);
413afe1ef24SIan Dowse 			if(!write_mtab(vflag))
41413e2e1afSIan Dowse 				warnx("cannot remove mounttab entry %s:%s",
415a69497d7SMatthew Dillon 				    hostp, nfsdirname);
416a69497d7SMatthew Dillon 			free_mtab();
417a69497d7SMatthew Dillon 		}
418bc70c172SBrian Feldman 		free(orignfsdirname);
4198fae3551SRodney W. Grimes 		auth_destroy(clp->cl_auth);
4208fae3551SRodney W. Grimes 		clnt_destroy(clp);
4218fae3551SRodney W. Grimes 	}
4228fae3551SRodney W. Grimes 	return (0);
4238fae3551SRodney W. Grimes }
4248fae3551SRodney W. Grimes 
425318f2fb4SIan Dowse struct statfs *
426eddb4805SIan Dowse getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what)
4278fae3551SRodney W. Grimes {
428d499a0efSBruce Evans 	static struct statfs *mntbuf;
429bc70c172SBrian Feldman 	static size_t mntsize = 0;
430bc70c172SBrian Feldman 	static char *mntcheck = NULL;
431eddb4805SIan Dowse 	struct statfs *sfs, *foundsfs;
432bc70c172SBrian Feldman 	int i, count;
4338fae3551SRodney W. Grimes 
434bc70c172SBrian Feldman 	if (mntsize <= 0) {
435bc70c172SBrian Feldman 		if ((mntsize = mntinfo(&mntbuf)) <= 0)
4368fae3551SRodney W. Grimes 			return (NULL);
4378fae3551SRodney W. Grimes 	}
438bc70c172SBrian Feldman 	if (mntcheck == NULL) {
439eddb4805SIan Dowse 		if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL)
440bc70c172SBrian Feldman 			err(1, "calloc");
441bc70c172SBrian Feldman 	}
442bc70c172SBrian Feldman 	/*
443bc70c172SBrian Feldman 	 * We want to get the file systems in the reverse order
444eddb4805SIan Dowse 	 * that they were mounted. Unmounted file systems are marked
445eddb4805SIan Dowse 	 * in a table called 'mntcheck'.
446bc70c172SBrian Feldman 	 */
447eddb4805SIan Dowse 	count = 0;
448eddb4805SIan Dowse 	foundsfs = NULL;
449bc70c172SBrian Feldman 	for (i = mntsize - 1; i >= 0; i--) {
450eddb4805SIan Dowse 		if (mntcheck[i])
451eddb4805SIan Dowse 			continue;
452eddb4805SIan Dowse 		sfs = &mntbuf[i];
453eddb4805SIan Dowse 		if (fromname != NULL && strcmp(sfs->f_mntfromname,
454eddb4805SIan Dowse 		    fromname) != 0)
455eddb4805SIan Dowse 			continue;
456eddb4805SIan Dowse 		if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0)
457eddb4805SIan Dowse 			continue;
458eddb4805SIan Dowse 		if (fsid != NULL && bcmp(&sfs->f_fsid, fsid,
459eddb4805SIan Dowse 		    sizeof(*fsid)) != 0)
460eddb4805SIan Dowse 			continue;
461eddb4805SIan Dowse 
46238f102c2SIan Dowse 		switch (what) {
463eddb4805SIan Dowse 		case CHECKUNIQUE:
464eddb4805SIan Dowse 			foundsfs = sfs;
465eddb4805SIan Dowse 			count++;
46638f102c2SIan Dowse 			continue;
467eddb4805SIan Dowse 		case REMOVE:
468eddb4805SIan Dowse 			mntcheck[i] = 1;
46938f102c2SIan Dowse 			break;
470eddb4805SIan Dowse 		default:
47138f102c2SIan Dowse 			break;
47238f102c2SIan Dowse 		}
473eddb4805SIan Dowse 		return (sfs);
4748fae3551SRodney W. Grimes 	}
475318f2fb4SIan Dowse 
476eddb4805SIan Dowse 	if (what == CHECKUNIQUE && count == 1)
477eddb4805SIan Dowse 		return (foundsfs);
4788fae3551SRodney W. Grimes 	return (NULL);
4798fae3551SRodney W. Grimes }
4808fae3551SRodney W. Grimes 
4818fae3551SRodney W. Grimes int
4827f471a32SXin LI sacmp(void *sa1, void *sa2)
4838fae3551SRodney W. Grimes {
4848360efbdSAlfred Perlstein 	void *p1, *p2;
4858360efbdSAlfred Perlstein 	int len;
4868fae3551SRodney W. Grimes 
4877f471a32SXin LI 	if (((struct sockaddr *)sa1)->sa_family !=
4887f471a32SXin LI 	    ((struct sockaddr *)sa2)->sa_family)
4898fae3551SRodney W. Grimes 		return (1);
4908fae3551SRodney W. Grimes 
4917f471a32SXin LI 	switch (((struct sockaddr *)sa1)->sa_family) {
4928360efbdSAlfred Perlstein 	case AF_INET:
4938360efbdSAlfred Perlstein 		p1 = &((struct sockaddr_in *)sa1)->sin_addr;
4948360efbdSAlfred Perlstein 		p2 = &((struct sockaddr_in *)sa2)->sin_addr;
4958360efbdSAlfred Perlstein 		len = 4;
4968360efbdSAlfred Perlstein 		break;
4978360efbdSAlfred Perlstein 	case AF_INET6:
4988360efbdSAlfred Perlstein 		p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
4998360efbdSAlfred Perlstein 		p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
5008360efbdSAlfred Perlstein 		len = 16;
5018360efbdSAlfred Perlstein 		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
5028360efbdSAlfred Perlstein 		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
5038360efbdSAlfred Perlstein 			return (1);
5048360efbdSAlfred Perlstein 		break;
5058360efbdSAlfred Perlstein 	default:
5068360efbdSAlfred Perlstein 		return (1);
5078360efbdSAlfred Perlstein 	}
5088360efbdSAlfred Perlstein 
5098360efbdSAlfred Perlstein 	return memcmp(p1, p2, len);
5108360efbdSAlfred Perlstein }
5118360efbdSAlfred Perlstein 
5128360efbdSAlfred Perlstein int
5138360efbdSAlfred Perlstein namematch(struct addrinfo *ai)
5148360efbdSAlfred Perlstein {
5158360efbdSAlfred Perlstein 	struct addrinfo *aip;
5168360efbdSAlfred Perlstein 
5178360efbdSAlfred Perlstein 	if (nfshost == NULL || nfshost_ai == NULL)
5188fae3551SRodney W. Grimes 		return (1);
5198fae3551SRodney W. Grimes 
5208360efbdSAlfred Perlstein 	while (ai != NULL) {
5218360efbdSAlfred Perlstein 		aip = nfshost_ai;
5228360efbdSAlfred Perlstein 		while (aip != NULL) {
5238360efbdSAlfred Perlstein 			if (sacmp(ai->ai_addr, aip->ai_addr) == 0)
5248fae3551SRodney W. Grimes 				return (1);
5258360efbdSAlfred Perlstein 			aip = aip->ai_next;
5268fae3551SRodney W. Grimes 		}
5278360efbdSAlfred Perlstein 		ai = ai->ai_next;
5288fae3551SRodney W. Grimes 	}
5298360efbdSAlfred Perlstein 
5308fae3551SRodney W. Grimes 	return (0);
5318fae3551SRodney W. Grimes }
5328fae3551SRodney W. Grimes 
533318f2fb4SIan Dowse struct statfs *
5347f471a32SXin LI checkmntlist(char *mntname)
535bc70c172SBrian Feldman {
536318f2fb4SIan Dowse 	struct statfs *sfs;
537eddb4805SIan Dowse 	fsid_t fsid;
538bc70c172SBrian Feldman 
539eddb4805SIan Dowse 	sfs = NULL;
5407f471a32SXin LI 	if (parsehexfsid(mntname, &fsid) == 0)
541eddb4805SIan Dowse 		sfs = getmntentry(NULL, NULL, &fsid, FIND);
54238f102c2SIan Dowse 	if (sfs == NULL)
5437f471a32SXin LI 		sfs = getmntentry(NULL, mntname, NULL, FIND);
544318f2fb4SIan Dowse 	if (sfs == NULL)
5457f471a32SXin LI 		sfs = getmntentry(mntname, NULL, NULL, FIND);
546318f2fb4SIan Dowse 	return (sfs);
547bc70c172SBrian Feldman }
548bc70c172SBrian Feldman 
549bc70c172SBrian Feldman size_t
550bc70c172SBrian Feldman mntinfo(struct statfs **mntbuf)
551bc70c172SBrian Feldman {
552bc70c172SBrian Feldman 	static struct statfs *origbuf;
553bc70c172SBrian Feldman 	size_t bufsize;
554bc70c172SBrian Feldman 	int mntsize;
555bc70c172SBrian Feldman 
556a69497d7SMatthew Dillon 	mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
557bc70c172SBrian Feldman 	if (mntsize <= 0)
558bc70c172SBrian Feldman 		return (0);
559bc70c172SBrian Feldman 	bufsize = (mntsize + 1) * sizeof(struct statfs);
560bc70c172SBrian Feldman 	if ((origbuf = malloc(bufsize)) == NULL)
561bc70c172SBrian Feldman 		err(1, "malloc");
562bc70c172SBrian Feldman 	mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
563bc70c172SBrian Feldman 	*mntbuf = origbuf;
564bc70c172SBrian Feldman 	return (mntsize);
565bc70c172SBrian Feldman }
566bc70c172SBrian Feldman 
567eddb4805SIan Dowse /*
5688b918187SIan Dowse  * Convert a hexadecimal filesystem ID to an fsid_t.
569eddb4805SIan Dowse  * Returns 0 on success.
570eddb4805SIan Dowse  */
571eddb4805SIan Dowse int
572eddb4805SIan Dowse parsehexfsid(const char *hex, fsid_t *fsid)
573bc70c172SBrian Feldman {
574eddb4805SIan Dowse 	char hexbuf[3];
575eddb4805SIan Dowse 	int i;
576bc70c172SBrian Feldman 
577eddb4805SIan Dowse 	if (strlen(hex) != sizeof(*fsid) * 2)
578eddb4805SIan Dowse 		return (-1);
579eddb4805SIan Dowse 	hexbuf[2] = '\0';
5805fff0914SIan Dowse 	for (i = 0; i < (int)sizeof(*fsid); i++) {
581eddb4805SIan Dowse 		hexbuf[0] = hex[i * 2];
582eddb4805SIan Dowse 		hexbuf[1] = hex[i * 2 + 1];
583eddb4805SIan Dowse 		if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1]))
584eddb4805SIan Dowse 			return (-1);
585eddb4805SIan Dowse 		((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16);
586bc70c172SBrian Feldman 	}
587eddb4805SIan Dowse 	return (0);
588bc70c172SBrian Feldman }
589bc70c172SBrian Feldman 
5908fae3551SRodney W. Grimes /*
5918fae3551SRodney W. Grimes  * xdr routines for mount rpc's
5928fae3551SRodney W. Grimes  */
5938fae3551SRodney W. Grimes int
594bc70c172SBrian Feldman xdr_dir(XDR *xdrsp, char *dirp)
5958fae3551SRodney W. Grimes {
596bc70c172SBrian Feldman 
5970775314bSDoug Rabson 	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
5988fae3551SRodney W. Grimes }
5998fae3551SRodney W. Grimes 
6008fae3551SRodney W. Grimes void
601ca25fa25SEd Schouten usage(void)
6028fae3551SRodney W. Grimes {
603bc70c172SBrian Feldman 
604210a5dc8SPhilippe Charnier 	(void)fprintf(stderr, "%s\n%s\n",
60534ae1bb6SRuslan Ermilov 	    "usage: umount [-fv] special ... | node ... | fsid ...",
606ef258dd9SMatthew N. Dodd 	    "       umount -a | -A [-F fstab] [-fv] [-h host] [-t type]");
6078fae3551SRodney W. Grimes 	exit(1);
6088fae3551SRodney W. Grimes }
609