xref: /freebsd/sbin/umount/umount.c (revision e96d0d74)
18fae3551SRodney W. Grimes /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
48fae3551SRodney W. Grimes  * Copyright (c) 1980, 1989, 1993
58fae3551SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
68fae3551SRodney W. Grimes  *
78fae3551SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
88fae3551SRodney W. Grimes  * modification, are permitted provided that the following conditions
98fae3551SRodney W. Grimes  * are met:
108fae3551SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
118fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
128fae3551SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
138fae3551SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
148fae3551SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
168fae3551SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
178fae3551SRodney W. Grimes  *    without specific prior written permission.
188fae3551SRodney W. Grimes  *
198fae3551SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
208fae3551SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
218fae3551SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
228fae3551SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
238fae3551SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
248fae3551SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
258fae3551SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
268fae3551SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
278fae3551SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
288fae3551SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
298fae3551SRodney W. Grimes  * SUCH DAMAGE.
308fae3551SRodney W. Grimes  */
318fae3551SRodney W. Grimes 
328fae3551SRodney W. Grimes #include <sys/param.h>
338fae3551SRodney W. Grimes #include <sys/mount.h>
348360efbdSAlfred Perlstein #include <sys/socket.h>
35eddb4805SIan Dowse #include <sys/stat.h>
368fae3551SRodney W. Grimes 
378fae3551SRodney W. Grimes #include <netdb.h>
388fae3551SRodney W. Grimes #include <rpc/rpc.h>
390775314bSDoug Rabson #include <rpcsvc/mount.h>
4049a8bad7SRick Macklem #include <nfs/nfssvc.h>
418fae3551SRodney W. Grimes 
4238f102c2SIan Dowse #include <ctype.h>
438fae3551SRodney W. Grimes #include <err.h>
44318f2fb4SIan Dowse #include <errno.h>
458fae3551SRodney W. Grimes #include <fstab.h>
468fae3551SRodney W. Grimes #include <stdio.h>
478fae3551SRodney W. Grimes #include <stdlib.h>
488fae3551SRodney W. Grimes #include <string.h>
498fae3551SRodney W. Grimes #include <unistd.h>
508fae3551SRodney W. Grimes 
51a69497d7SMatthew Dillon #include "mounttab.h"
52a69497d7SMatthew Dillon 
53840a8027SRicardo Branco /* used by md_detach() */
54840a8027SRicardo Branco #include <sys/ioctl.h>
55840a8027SRicardo Branco #include <sys/mdioctl.h>
56840a8027SRicardo Branco #include <fcntl.h>
57840a8027SRicardo Branco #include <paths.h>
58840a8027SRicardo Branco 
59840a8027SRicardo Branco #define DEV_MD     _PATH_DEV   MD_NAME
60840a8027SRicardo Branco #define DEV_MDCTL   _PATH_DEV  MDCTL_NAME
61840a8027SRicardo Branco 
62eddb4805SIan Dowse typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat;
63bc70c172SBrian Feldman 
641efe3c6bSEd Schouten static struct addrinfo *nfshost_ai = NULL;
65840a8027SRicardo Branco static int	dflag, fflag, vflag;
66840a8027SRicardo Branco static int	all;
671efe3c6bSEd Schouten static char	*nfshost;
688fae3551SRodney W. Grimes 
691add162cSIan Dowse struct statfs *checkmntlist(char *);
70bc70c172SBrian Feldman int	 checkvfsname (const char *, char **);
71eddb4805SIan Dowse struct statfs *getmntentry(const char *fromname, const char *onname,
72eddb4805SIan Dowse 	     fsid_t *fsid, dowhat what);
73bc70c172SBrian Feldman char   **makevfslist (const char *);
74bc70c172SBrian Feldman size_t	 mntinfo (struct statfs **);
758360efbdSAlfred Perlstein int	 namematch (struct addrinfo *);
76eddb4805SIan Dowse int	 parsehexfsid(const char *hex, fsid_t *fsid);
777f471a32SXin LI int	 sacmp (void *, void *);
78bc70c172SBrian Feldman int	 umountall (char **);
798360efbdSAlfred Perlstein int	 checkname (char *, char **);
80eddb4805SIan Dowse int	 umountfs(struct statfs *sfs);
81bc70c172SBrian Feldman void	 usage (void);
82bc70c172SBrian Feldman int	 xdr_dir (XDR *, char *);
83840a8027SRicardo Branco int	 md_detach(const char *);
848fae3551SRodney W. Grimes 
858fae3551SRodney W. Grimes int
main(int argc,char * argv[])86bc70c172SBrian Feldman main(int argc, char *argv[])
878fae3551SRodney W. Grimes {
88840a8027SRicardo Branco 	int errs, ch, mntsize, error, nfsforce, ret;
89f73d495fSIan Dowse 	char **typelist = NULL;
90f73d495fSIan Dowse 	struct statfs *mntbuf, *sfs;
918360efbdSAlfred Perlstein 	struct addrinfo hints;
928fae3551SRodney W. Grimes 
934261923eSRick Macklem 	nfsforce = all = errs = 0;
94840a8027SRicardo Branco 	while ((ch = getopt(argc, argv, "AadF:fh:Nnt: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;
102840a8027SRicardo Branco 		case 'd':
103840a8027SRicardo Branco 			dflag = 1;
104840a8027SRicardo Branco 			break;
105ef258dd9SMatthew N. Dodd 		case 'F':
106ef258dd9SMatthew N. Dodd 			setfstab(optarg);
107ef258dd9SMatthew N. Dodd 			break;
1088fae3551SRodney W. Grimes 		case 'f':
109debc480eSEdward Tomasz Napierala 			fflag |= MNT_FORCE;
1108fae3551SRodney W. Grimes 			break;
111d499a0efSBruce Evans 		case 'h':	/* -h implies -A. */
112d499a0efSBruce Evans 			all = 2;
1138fae3551SRodney W. Grimes 			nfshost = optarg;
1148fae3551SRodney W. Grimes 			break;
1154261923eSRick Macklem 		case 'N':
1164261923eSRick Macklem 			nfsforce = 1;
1174261923eSRick Macklem 			break;
118debc480eSEdward Tomasz Napierala 		case 'n':
119debc480eSEdward Tomasz Napierala 			fflag |= MNT_NONBUSY;
120debc480eSEdward Tomasz Napierala 			break;
1218fae3551SRodney W. Grimes 		case 't':
122d499a0efSBruce Evans 			if (typelist != NULL)
123bc70c172SBrian Feldman 				err(1, "only one -t option may be specified");
124d499a0efSBruce Evans 			typelist = makevfslist(optarg);
1258fae3551SRodney W. Grimes 			break;
1268fae3551SRodney W. Grimes 		case 'v':
1278fae3551SRodney W. Grimes 			vflag = 1;
1288fae3551SRodney W. Grimes 			break;
1298fae3551SRodney W. Grimes 		default:
1308fae3551SRodney W. Grimes 			usage();
1318fae3551SRodney W. Grimes 			/* NOTREACHED */
1328fae3551SRodney W. Grimes 		}
1338fae3551SRodney W. Grimes 	argc -= optind;
1348fae3551SRodney W. Grimes 	argv += optind;
1358fae3551SRodney W. Grimes 
136debc480eSEdward Tomasz Napierala 	if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0)
137debc480eSEdward Tomasz Napierala 		err(1, "-f and -n are mutually exclusive");
138debc480eSEdward Tomasz Napierala 
139adb378ceSPhilippe Charnier 	if ((argc == 0 && !all) || (argc != 0 && all))
1408fae3551SRodney W. Grimes 		usage();
1418fae3551SRodney W. Grimes 
1424261923eSRick Macklem 	if (nfsforce != 0 && (argc == 0 || nfshost != NULL || typelist != NULL))
1434261923eSRick Macklem 		usage();
1444261923eSRick Macklem 
1458fae3551SRodney W. Grimes 	/* -h implies "-t nfs" if no -t flag. */
1468fae3551SRodney W. Grimes 	if ((nfshost != NULL) && (typelist == NULL))
147d499a0efSBruce Evans 		typelist = makevfslist("nfs");
1488fae3551SRodney W. Grimes 
1498360efbdSAlfred Perlstein 	if (nfshost != NULL) {
1508360efbdSAlfred Perlstein 		memset(&hints, 0, sizeof hints);
1518360efbdSAlfred Perlstein 		error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai);
15221eff82fSIan Dowse 		if (error)
15313e2e1afSIan Dowse 			errx(1, "%s: %s", nfshost, gai_strerror(error));
1548360efbdSAlfred Perlstein 	}
1558360efbdSAlfred Perlstein 
156d499a0efSBruce Evans 	switch (all) {
157d499a0efSBruce Evans 	case 2:
158bc70c172SBrian Feldman 		if ((mntsize = mntinfo(&mntbuf)) <= 0)
159d499a0efSBruce Evans 			break;
160bc70c172SBrian Feldman 		/*
161bc70c172SBrian Feldman 		 * We unmount the nfs-mounts in the reverse order
162bc70c172SBrian Feldman 		 * that they were mounted.
163bc70c172SBrian Feldman 		 */
164bc70c172SBrian Feldman 		for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
165f73d495fSIan Dowse 			sfs = &mntbuf[mntsize];
166f73d495fSIan Dowse 			if (checkvfsname(sfs->f_fstypename, typelist))
167d499a0efSBruce Evans 				continue;
16880bbaeb5SKirk McKusick 			if (strcmp(sfs->f_mntonname, "/dev") == 0)
16980bbaeb5SKirk McKusick 				continue;
170eddb4805SIan Dowse 			if (umountfs(sfs) != 0)
171d499a0efSBruce Evans 				errs = 1;
172d499a0efSBruce Evans 		}
173bc70c172SBrian Feldman 		free(mntbuf);
174d499a0efSBruce Evans 		break;
175d499a0efSBruce Evans 	case 1:
1768fae3551SRodney W. Grimes 		if (setfsent() == 0)
177ef258dd9SMatthew N. Dodd 			err(1, "%s", getfstab());
178d499a0efSBruce Evans 		errs = umountall(typelist);
179d499a0efSBruce Evans 		break;
180d499a0efSBruce Evans 	case 0:
1818fae3551SRodney W. Grimes 		for (errs = 0; *argv != NULL; ++argv)
1824261923eSRick Macklem 			if (nfsforce != 0) {
1834261923eSRick Macklem 				/*
1844261923eSRick Macklem 				 * First do the nfssvc() syscall to shut down
1854261923eSRick Macklem 				 * the mount point and then do the forced
1864261923eSRick Macklem 				 * dismount.
1874261923eSRick Macklem 				 */
1884261923eSRick Macklem 				ret = nfssvc(NFSSVC_FORCEDISM, *argv);
1894261923eSRick Macklem 				if (ret >= 0)
1904261923eSRick Macklem 					ret = unmount(*argv, MNT_FORCE);
1914261923eSRick Macklem 				if (ret < 0) {
1924261923eSRick Macklem 					warn("%s", *argv);
1934261923eSRick Macklem 					errs = 1;
1944261923eSRick Macklem 				}
1954261923eSRick Macklem 			} else if (checkname(*argv, typelist) != 0)
196d499a0efSBruce Evans 				errs = 1;
197d499a0efSBruce Evans 		break;
198d499a0efSBruce Evans 	}
1998fae3551SRodney W. Grimes 	exit(errs);
2008fae3551SRodney W. Grimes }
2018fae3551SRodney W. Grimes 
2028fae3551SRodney W. Grimes int
umountall(char ** typelist)203bc70c172SBrian Feldman umountall(char **typelist)
2048fae3551SRodney W. Grimes {
2055965373eSMaxime Henrion 	struct xvfsconf vfc;
2068fae3551SRodney W. Grimes 	struct fstab *fs;
207adb378ceSPhilippe Charnier 	int rval;
2088fae3551SRodney W. Grimes 	char *cp;
2090602ee7cSBrian Feldman 	static int firstcall = 1;
2108fae3551SRodney W. Grimes 
21191a81678SBrian Feldman 	if ((fs = getfsent()) != NULL)
2120602ee7cSBrian Feldman 		firstcall = 0;
21391a81678SBrian Feldman 	else if (firstcall)
21491a81678SBrian Feldman 		errx(1, "fstab reading failure");
21591a81678SBrian Feldman 	else
21691a81678SBrian Feldman 		return (0);
217bc70c172SBrian Feldman 	do {
2188fae3551SRodney W. Grimes 		/* Ignore the root. */
2198fae3551SRodney W. Grimes 		if (strcmp(fs->fs_file, "/") == 0)
2208fae3551SRodney W. Grimes 			continue;
2218fae3551SRodney W. Grimes 		/*
2228fae3551SRodney W. Grimes 		 * !!!
2238fae3551SRodney W. Grimes 		 * Historic practice: ignore unknown FSTAB_* fields.
2248fae3551SRodney W. Grimes 		 */
2258fae3551SRodney W. Grimes 		if (strcmp(fs->fs_type, FSTAB_RW) &&
2268fae3551SRodney W. Grimes 		    strcmp(fs->fs_type, FSTAB_RO) &&
2278fae3551SRodney W. Grimes 		    strcmp(fs->fs_type, FSTAB_RQ))
2288fae3551SRodney W. Grimes 			continue;
229b6e55a05SDag-Erling Smørgrav 		/* Ignore unknown file system types. */
23081667275SDag-Erling Smørgrav 		if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
2318fae3551SRodney W. Grimes 			continue;
232d499a0efSBruce Evans 		if (checkvfsname(fs->fs_vfstype, typelist))
2338fae3551SRodney W. Grimes 			continue;
2348fae3551SRodney W. Grimes 
2358fae3551SRodney W. Grimes 		/*
2368fae3551SRodney W. Grimes 		 * We want to unmount the file systems in the reverse order
2378fae3551SRodney W. Grimes 		 * that they were mounted.  So, we save off the file name
2388fae3551SRodney W. Grimes 		 * in some allocated memory, and then call recursively.
2398fae3551SRodney W. Grimes 		 */
2408fae3551SRodney W. Grimes 		if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
241bc70c172SBrian Feldman 			err(1, "malloc failed");
2428fae3551SRodney W. Grimes 		(void)strcpy(cp, fs->fs_file);
243d499a0efSBruce Evans 		rval = umountall(typelist);
2448360efbdSAlfred Perlstein 		rval = checkname(cp, typelist) || rval;
245bc70c172SBrian Feldman 		free(cp);
2460602ee7cSBrian Feldman 		return (rval);
2470602ee7cSBrian Feldman 	} while ((fs = getfsent()) != NULL);
2488fae3551SRodney W. Grimes 	return (0);
2498fae3551SRodney W. Grimes }
2508fae3551SRodney W. Grimes 
2518360efbdSAlfred Perlstein /*
252eddb4805SIan Dowse  * Do magic checks on mountpoint/device/fsid, and then call unmount(2).
2538360efbdSAlfred Perlstein  */
2548fae3551SRodney W. Grimes int
checkname(char * mntname,char ** typelist)2557f471a32SXin LI checkname(char *mntname, char **typelist)
2568fae3551SRodney W. Grimes {
257eddb4805SIan Dowse 	char buf[MAXPATHLEN];
258eddb4805SIan Dowse 	struct statfs sfsbuf;
259eddb4805SIan Dowse 	struct stat sb;
260f73d495fSIan Dowse 	struct statfs *sfs;
261eddb4805SIan Dowse 	char *delimp;
262eddb4805SIan Dowse 	dev_t dev;
263eddb4805SIan Dowse 	int len;
2648fae3551SRodney W. Grimes 
265bc70c172SBrian Feldman 	/*
266bc70c172SBrian Feldman 	 * 1. Check if the name exists in the mounttable.
267bc70c172SBrian Feldman 	 */
2687f471a32SXin LI 	sfs = checkmntlist(mntname);
269bc70c172SBrian Feldman 	/*
270bc70c172SBrian Feldman 	 * 2. Remove trailing slashes if there are any. After that
271bc70c172SBrian Feldman 	 * we look up the name in the mounttable again.
272bc70c172SBrian Feldman 	 */
273318f2fb4SIan Dowse 	if (sfs == NULL) {
2747f471a32SXin LI 		len = strlen(mntname);
2757f471a32SXin LI 		while (len > 1 && mntname[len - 1] == '/')
2767f471a32SXin LI 			mntname[--len] = '\0';
2777f471a32SXin LI 		sfs = checkmntlist(mntname);
278bc70c172SBrian Feldman 	}
279bc70c172SBrian Feldman 	/*
280eddb4805SIan Dowse 	 * 3. Check if the deprecated NFS syntax with an '@' has been used
281eddb4805SIan Dowse 	 * and translate it to the ':' syntax. Look up the name in the
282eddb4805SIan Dowse 	 * mount table again.
283bc70c172SBrian Feldman 	 */
2847f471a32SXin LI 	if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) {
2857f471a32SXin LI 		snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1,
2867f471a32SXin LI 		    (int)(delimp - mntname), mntname);
287eddb4805SIan Dowse 		len = strlen(buf);
2885fff0914SIan Dowse 		while (len > 1 && buf[len - 1] == '/')
289eddb4805SIan Dowse 			buf[--len] = '\0';
290eddb4805SIan Dowse 		sfs = checkmntlist(buf);
291bc70c172SBrian Feldman 	}
292bc70c172SBrian Feldman 	/*
293eddb4805SIan Dowse 	 * 4. Resort to a statfs(2) call. This is the last check so that
294eddb4805SIan Dowse 	 * hung NFS filesystems for example can be unmounted without
295eddb4805SIan Dowse 	 * potentially blocking forever in statfs() as long as the
296eddb4805SIan Dowse 	 * filesystem is specified unambiguously. This covers all the
297eddb4805SIan Dowse 	 * hard cases such as symlinks and mismatches between the
298eddb4805SIan Dowse 	 * mount list and reality.
299eddb4805SIan Dowse 	 * We also do this if an ambiguous mount point was specified.
300bc70c172SBrian Feldman 	 */
3017f471a32SXin LI 	if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL &&
3027f471a32SXin LI 	    getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) {
3037f471a32SXin LI 		if (statfs(mntname, &sfsbuf) != 0) {
3047f471a32SXin LI 			warn("%s: statfs", mntname);
3057f471a32SXin LI 		} else if (stat(mntname, &sb) != 0) {
3067f471a32SXin LI 			warn("%s: stat", mntname);
307eddb4805SIan Dowse 		} else if (S_ISDIR(sb.st_mode)) {
3087f471a32SXin LI 			/* Check that `mntname' is the root directory. */
309eddb4805SIan Dowse 			dev = sb.st_dev;
3107f471a32SXin LI 			snprintf(buf, sizeof(buf), "%s/..", mntname);
311eddb4805SIan Dowse 			if (stat(buf, &sb) != 0) {
312eddb4805SIan Dowse 				warn("%s: stat", buf);
313eddb4805SIan Dowse 			} else if (sb.st_dev == dev) {
314eddb4805SIan Dowse 				warnx("%s: not a file system root directory",
3157f471a32SXin LI 				    mntname);
316eddb4805SIan Dowse 				return (1);
3178360efbdSAlfred Perlstein 			} else
318eddb4805SIan Dowse 				sfs = &sfsbuf;
319eddb4805SIan Dowse 		}
320eddb4805SIan Dowse 	}
321eddb4805SIan Dowse 	if (sfs == NULL) {
3227f471a32SXin LI 		warnx("%s: unknown file system", mntname);
3238fae3551SRodney W. Grimes 		return (1);
3248fae3551SRodney W. Grimes 	}
3251add162cSIan Dowse 	if (checkvfsname(sfs->f_fstypename, typelist))
326d499a0efSBruce Evans 		return (1);
327eddb4805SIan Dowse 	return (umountfs(sfs));
3288360efbdSAlfred Perlstein }
3298360efbdSAlfred Perlstein 
3308360efbdSAlfred Perlstein /*
3318360efbdSAlfred Perlstein  * NFS stuff and unmount(2) call
3328360efbdSAlfred Perlstein  */
3338360efbdSAlfred Perlstein int
umountfs(struct statfs * sfs)334eddb4805SIan Dowse umountfs(struct statfs *sfs)
3358360efbdSAlfred Perlstein {
336318f2fb4SIan Dowse 	char fsidbuf[64];
3378360efbdSAlfred Perlstein 	enum clnt_stat clnt_stat;
3388360efbdSAlfred Perlstein 	struct timeval try;
3398360efbdSAlfred Perlstein 	struct addrinfo *ai, hints;
3408360efbdSAlfred Perlstein 	int do_rpc;
3418360efbdSAlfred Perlstein 	CLIENT *clp;
3428360efbdSAlfred Perlstein 	char *nfsdirname, *orignfsdirname;
3438360efbdSAlfred Perlstein 	char *hostp, *delimp;
34449a8bad7SRick Macklem 	char buf[1024];
34549a8bad7SRick Macklem 	struct nfscl_dumpmntopts dumpmntopts;
34649a8bad7SRick Macklem 	const char *proto_ptr = NULL;
3478360efbdSAlfred Perlstein 
3488360efbdSAlfred Perlstein 	ai = NULL;
34913e2e1afSIan Dowse 	do_rpc = 0;
35013e2e1afSIan Dowse 	hostp = NULL;
3518360efbdSAlfred Perlstein 	nfsdirname = delimp = orignfsdirname = NULL;
3528360efbdSAlfred Perlstein 	memset(&hints, 0, sizeof hints);
3538360efbdSAlfred Perlstein 
354eddb4805SIan Dowse 	if (strcmp(sfs->f_fstypename, "nfs") == 0) {
355eddb4805SIan Dowse 		if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL)
3568360efbdSAlfred Perlstein 			err(1, "strdup");
3578360efbdSAlfred Perlstein 		orignfsdirname = nfsdirname;
358a505d435SHajimu UMEMOTO 		if (*nfsdirname == '[' &&
359a505d435SHajimu UMEMOTO 		    (delimp = strchr(nfsdirname + 1, ']')) != NULL &&
360a505d435SHajimu UMEMOTO 		    *(delimp + 1) == ':') {
361a505d435SHajimu UMEMOTO 			hostp = nfsdirname + 1;
362a505d435SHajimu UMEMOTO 			nfsdirname = delimp + 2;
363a505d435SHajimu UMEMOTO 		} else if ((delimp = strrchr(nfsdirname, ':')) != NULL) {
3648360efbdSAlfred Perlstein 			hostp = nfsdirname;
365a505d435SHajimu UMEMOTO 			nfsdirname = delimp + 1;
366a505d435SHajimu UMEMOTO 		}
367a505d435SHajimu UMEMOTO 		if (hostp != NULL) {
368a505d435SHajimu UMEMOTO 			*delimp = '\0';
3698360efbdSAlfred Perlstein 			getaddrinfo(hostp, NULL, &hints, &ai);
3708360efbdSAlfred Perlstein 			if (ai == NULL) {
3718360efbdSAlfred Perlstein 				warnx("can't get net id for host");
3728360efbdSAlfred Perlstein 			}
3738360efbdSAlfred Perlstein 		}
37413e2e1afSIan Dowse 
375bc70c172SBrian Feldman 		/*
376bc70c172SBrian Feldman 		 * Check if we have to start the rpc-call later.
377bc70c172SBrian Feldman 		 * If there are still identical nfs-names mounted,
378bc70c172SBrian Feldman 		 * we skip the rpc-call. Obviously this has to
379bc70c172SBrian Feldman 		 * happen before unmount(2), but it should happen
380bc70c172SBrian Feldman 		 * after the previous namecheck.
38113e2e1afSIan Dowse 		 * A non-NULL return means that this is the last
38213e2e1afSIan Dowse 		 * mount from mntfromname that is still mounted.
383bc70c172SBrian Feldman 		 */
384eddb4805SIan Dowse 		if (getmntentry(sfs->f_mntfromname, NULL, NULL,
38549a8bad7SRick Macklem 		    CHECKUNIQUE) != NULL) {
3860fe9a7daSBrian Feldman 			do_rpc = 1;
38749a8bad7SRick Macklem 			proto_ptr = "udp";
38849a8bad7SRick Macklem 			/*
38949a8bad7SRick Macklem 			 * Try and find out whether this NFS mount is NFSv4 and
39049a8bad7SRick Macklem 			 * what protocol is being used. If this fails, the
39149a8bad7SRick Macklem 			 * default is NFSv2,3 and use UDP for the Unmount RPC.
39249a8bad7SRick Macklem 			 */
39349a8bad7SRick Macklem 			dumpmntopts.ndmnt_fname = sfs->f_mntonname;
39449a8bad7SRick Macklem 			dumpmntopts.ndmnt_buf = buf;
39549a8bad7SRick Macklem 			dumpmntopts.ndmnt_blen = sizeof(buf);
39649a8bad7SRick Macklem 			if (nfssvc(NFSSVC_DUMPMNTOPTS, &dumpmntopts) >= 0) {
39749a8bad7SRick Macklem 				if (strstr(buf, "nfsv4,") != NULL)
39849a8bad7SRick Macklem 					do_rpc = 0;
39949a8bad7SRick Macklem 				else if (strstr(buf, ",tcp,") != NULL)
40049a8bad7SRick Macklem 					proto_ptr = "tcp";
40149a8bad7SRick Macklem 			}
40249a8bad7SRick Macklem 		}
40313e2e1afSIan Dowse 	}
4048360efbdSAlfred Perlstein 
405c27063b9SEitan Adler 	if (!namematch(ai)) {
406c27063b9SEitan Adler 		free(orignfsdirname);
407d499a0efSBruce Evans 		return (1);
408c27063b9SEitan Adler 	}
409eddb4805SIan Dowse 	/* First try to unmount using the file system ID. */
410eddb4805SIan Dowse 	snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0],
411eddb4805SIan Dowse 	    sfs->f_fsid.val[1]);
412318f2fb4SIan Dowse 	if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
4130ed25a9aSIan Dowse 		/* XXX, non-root users get a zero fsid, so don't warn. */
4140ed25a9aSIan Dowse 		if (errno != ENOENT || sfs->f_fsid.val[0] != 0 ||
4150ed25a9aSIan Dowse 		    sfs->f_fsid.val[1] != 0)
416eddb4805SIan Dowse 			warn("unmount of %s failed", sfs->f_mntonname);
417c27063b9SEitan Adler 		if (errno != ENOENT) {
418c27063b9SEitan Adler 			free(orignfsdirname);
419318f2fb4SIan Dowse 			return (1);
420c27063b9SEitan Adler 		}
4218b918187SIan Dowse 		/* Compatibility for old kernels. */
4228b918187SIan Dowse 		if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0)
423318f2fb4SIan Dowse 			warnx("retrying using path instead of file system ID");
424eddb4805SIan Dowse 		if (unmount(sfs->f_mntonname, fflag) != 0) {
425eddb4805SIan Dowse 			warn("unmount of %s failed", sfs->f_mntonname);
426c27063b9SEitan Adler 			free(orignfsdirname);
4278fae3551SRodney W. Grimes 			return (1);
4288fae3551SRodney W. Grimes 		}
429eddb4805SIan Dowse 	}
430efb45ad0SGordon Bergling 	/* Mark this file system as unmounted. */
431eddb4805SIan Dowse 	getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE);
432bc70c172SBrian Feldman 	if (vflag)
433eddb4805SIan Dowse 		(void)printf("%s: unmount from %s\n", sfs->f_mntfromname,
434eddb4805SIan Dowse 		    sfs->f_mntonname);
435bc70c172SBrian Feldman 	/*
436bc70c172SBrian Feldman 	 * Report to mountd-server which nfsname
437bc70c172SBrian Feldman 	 * has been unmounted.
438bc70c172SBrian Feldman 	 */
4398360efbdSAlfred Perlstein 	if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) {
44049a8bad7SRick Macklem 		clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS3, proto_ptr);
4418360efbdSAlfred Perlstein 		if (clp  == NULL) {
44213e2e1afSIan Dowse 			warnx("%s: %s", hostp,
4430775314bSDoug Rabson 			    clnt_spcreateerror("MOUNTPROG"));
444c27063b9SEitan Adler 			free(orignfsdirname);
4458fae3551SRodney W. Grimes 			return (1);
4468fae3551SRodney W. Grimes 		}
4478360efbdSAlfred Perlstein 		clp->cl_auth = authsys_create_default();
4488fae3551SRodney W. Grimes 		try.tv_sec = 20;
4498fae3551SRodney W. Grimes 		try.tv_usec = 0;
4500775314bSDoug Rabson 		clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir,
4515c514aeeSMatthew N. Dodd 		    nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try);
4528fae3551SRodney W. Grimes 		if (clnt_stat != RPC_SUCCESS) {
45313e2e1afSIan Dowse 			warnx("%s: %s", hostp,
45413e2e1afSIan Dowse 			    clnt_sperror(clp, "RPCMNT_UMOUNT"));
455c27063b9SEitan Adler 			free(orignfsdirname);
4568fae3551SRodney W. Grimes 			return (1);
4578fae3551SRodney W. Grimes 		}
458a69497d7SMatthew Dillon 		/*
459a69497d7SMatthew Dillon 		 * Remove the unmounted entry from /var/db/mounttab.
460a69497d7SMatthew Dillon 		 */
461afe1ef24SIan Dowse 		if (read_mtab()) {
462afe1ef24SIan Dowse 			clean_mtab(hostp, nfsdirname, vflag);
463afe1ef24SIan Dowse 			if(!write_mtab(vflag))
46413e2e1afSIan Dowse 				warnx("cannot remove mounttab entry %s:%s",
465a69497d7SMatthew Dillon 				    hostp, nfsdirname);
466a69497d7SMatthew Dillon 			free_mtab();
467a69497d7SMatthew Dillon 		}
4688fae3551SRodney W. Grimes 		auth_destroy(clp->cl_auth);
4698fae3551SRodney W. Grimes 		clnt_destroy(clp);
4708fae3551SRodney W. Grimes 	}
471c27063b9SEitan Adler 	free(orignfsdirname);
472840a8027SRicardo Branco 
473840a8027SRicardo Branco 	if (dflag) {
474840a8027SRicardo Branco 		if (md_detach(sfs->f_mntfromname) == 0) {
475840a8027SRicardo Branco 			if (vflag)
476840a8027SRicardo Branco 				(void)printf("%s: detached\n",
477840a8027SRicardo Branco 				    sfs->f_mntfromname);
478840a8027SRicardo Branco 		} else if (!all)
479840a8027SRicardo Branco 			return (-1);
480840a8027SRicardo Branco 	}
481840a8027SRicardo Branco 
4828fae3551SRodney W. Grimes 	return (0);
4838fae3551SRodney W. Grimes }
4848fae3551SRodney W. Grimes 
485318f2fb4SIan Dowse struct statfs *
getmntentry(const char * fromname,const char * onname,fsid_t * fsid,dowhat what)486eddb4805SIan Dowse getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what)
4878fae3551SRodney W. Grimes {
488d499a0efSBruce Evans 	static struct statfs *mntbuf;
489bc70c172SBrian Feldman 	static size_t mntsize = 0;
490e96092e8SUlrich Spörlein 	static int *mntcheck = NULL;
491eddb4805SIan Dowse 	struct statfs *sfs, *foundsfs;
492bc70c172SBrian Feldman 	int i, count;
4938fae3551SRodney W. Grimes 
494bc70c172SBrian Feldman 	if (mntsize <= 0) {
495bc70c172SBrian Feldman 		if ((mntsize = mntinfo(&mntbuf)) <= 0)
4968fae3551SRodney W. Grimes 			return (NULL);
4978fae3551SRodney W. Grimes 	}
498bc70c172SBrian Feldman 	if (mntcheck == NULL) {
499eddb4805SIan Dowse 		if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL)
500bc70c172SBrian Feldman 			err(1, "calloc");
501bc70c172SBrian Feldman 	}
502bc70c172SBrian Feldman 	/*
503bc70c172SBrian Feldman 	 * We want to get the file systems in the reverse order
504eddb4805SIan Dowse 	 * that they were mounted. Unmounted file systems are marked
505eddb4805SIan Dowse 	 * in a table called 'mntcheck'.
506bc70c172SBrian Feldman 	 */
507eddb4805SIan Dowse 	count = 0;
508eddb4805SIan Dowse 	foundsfs = NULL;
509bc70c172SBrian Feldman 	for (i = mntsize - 1; i >= 0; i--) {
510eddb4805SIan Dowse 		if (mntcheck[i])
511eddb4805SIan Dowse 			continue;
512eddb4805SIan Dowse 		sfs = &mntbuf[i];
513eddb4805SIan Dowse 		if (fromname != NULL && strcmp(sfs->f_mntfromname,
514eddb4805SIan Dowse 		    fromname) != 0)
515eddb4805SIan Dowse 			continue;
516eddb4805SIan Dowse 		if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0)
517eddb4805SIan Dowse 			continue;
518245bfd34SRyan Moeller 		if (fsid != NULL && fsidcmp(&sfs->f_fsid, fsid) != 0)
519eddb4805SIan Dowse 			continue;
520eddb4805SIan Dowse 
52138f102c2SIan Dowse 		switch (what) {
522eddb4805SIan Dowse 		case CHECKUNIQUE:
523eddb4805SIan Dowse 			foundsfs = sfs;
524eddb4805SIan Dowse 			count++;
52538f102c2SIan Dowse 			continue;
526eddb4805SIan Dowse 		case REMOVE:
527eddb4805SIan Dowse 			mntcheck[i] = 1;
52838f102c2SIan Dowse 			break;
529eddb4805SIan Dowse 		default:
53038f102c2SIan Dowse 			break;
53138f102c2SIan Dowse 		}
532eddb4805SIan Dowse 		return (sfs);
5338fae3551SRodney W. Grimes 	}
534318f2fb4SIan Dowse 
535eddb4805SIan Dowse 	if (what == CHECKUNIQUE && count == 1)
536eddb4805SIan Dowse 		return (foundsfs);
5378fae3551SRodney W. Grimes 	return (NULL);
5388fae3551SRodney W. Grimes }
5398fae3551SRodney W. Grimes 
5408fae3551SRodney W. Grimes int
sacmp(void * sa1,void * sa2)5417f471a32SXin LI sacmp(void *sa1, void *sa2)
5428fae3551SRodney W. Grimes {
5438360efbdSAlfred Perlstein 	void *p1, *p2;
5448360efbdSAlfred Perlstein 	int len;
5458fae3551SRodney W. Grimes 
5467f471a32SXin LI 	if (((struct sockaddr *)sa1)->sa_family !=
5477f471a32SXin LI 	    ((struct sockaddr *)sa2)->sa_family)
5488fae3551SRodney W. Grimes 		return (1);
5498fae3551SRodney W. Grimes 
5507f471a32SXin LI 	switch (((struct sockaddr *)sa1)->sa_family) {
5518360efbdSAlfred Perlstein 	case AF_INET:
5528360efbdSAlfred Perlstein 		p1 = &((struct sockaddr_in *)sa1)->sin_addr;
5538360efbdSAlfred Perlstein 		p2 = &((struct sockaddr_in *)sa2)->sin_addr;
5548360efbdSAlfred Perlstein 		len = 4;
5558360efbdSAlfred Perlstein 		break;
5568360efbdSAlfred Perlstein 	case AF_INET6:
5578360efbdSAlfred Perlstein 		p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
5588360efbdSAlfred Perlstein 		p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
5598360efbdSAlfred Perlstein 		len = 16;
5608360efbdSAlfred Perlstein 		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
5618360efbdSAlfred Perlstein 		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
5628360efbdSAlfred Perlstein 			return (1);
5638360efbdSAlfred Perlstein 		break;
5648360efbdSAlfred Perlstein 	default:
5658360efbdSAlfred Perlstein 		return (1);
5668360efbdSAlfred Perlstein 	}
5678360efbdSAlfred Perlstein 
5688360efbdSAlfred Perlstein 	return memcmp(p1, p2, len);
5698360efbdSAlfred Perlstein }
5708360efbdSAlfred Perlstein 
5718360efbdSAlfred Perlstein int
namematch(struct addrinfo * ai)5728360efbdSAlfred Perlstein namematch(struct addrinfo *ai)
5738360efbdSAlfred Perlstein {
5748360efbdSAlfred Perlstein 	struct addrinfo *aip;
5758360efbdSAlfred Perlstein 
5768360efbdSAlfred Perlstein 	if (nfshost == NULL || nfshost_ai == NULL)
5778fae3551SRodney W. Grimes 		return (1);
5788fae3551SRodney W. Grimes 
5798360efbdSAlfred Perlstein 	while (ai != NULL) {
5808360efbdSAlfred Perlstein 		aip = nfshost_ai;
5818360efbdSAlfred Perlstein 		while (aip != NULL) {
5828360efbdSAlfred Perlstein 			if (sacmp(ai->ai_addr, aip->ai_addr) == 0)
5838fae3551SRodney W. Grimes 				return (1);
5848360efbdSAlfred Perlstein 			aip = aip->ai_next;
5858fae3551SRodney W. Grimes 		}
5868360efbdSAlfred Perlstein 		ai = ai->ai_next;
5878fae3551SRodney W. Grimes 	}
5888360efbdSAlfred Perlstein 
5898fae3551SRodney W. Grimes 	return (0);
5908fae3551SRodney W. Grimes }
5918fae3551SRodney W. Grimes 
592318f2fb4SIan Dowse struct statfs *
checkmntlist(char * mntname)5937f471a32SXin LI checkmntlist(char *mntname)
594bc70c172SBrian Feldman {
595318f2fb4SIan Dowse 	struct statfs *sfs;
596eddb4805SIan Dowse 	fsid_t fsid;
597bc70c172SBrian Feldman 
598eddb4805SIan Dowse 	sfs = NULL;
5997f471a32SXin LI 	if (parsehexfsid(mntname, &fsid) == 0)
600eddb4805SIan Dowse 		sfs = getmntentry(NULL, NULL, &fsid, FIND);
60138f102c2SIan Dowse 	if (sfs == NULL)
6027f471a32SXin LI 		sfs = getmntentry(NULL, mntname, NULL, FIND);
603318f2fb4SIan Dowse 	if (sfs == NULL)
6047f471a32SXin LI 		sfs = getmntentry(mntname, NULL, NULL, FIND);
605318f2fb4SIan Dowse 	return (sfs);
606bc70c172SBrian Feldman }
607bc70c172SBrian Feldman 
608bc70c172SBrian Feldman size_t
mntinfo(struct statfs ** mntbuf)609bc70c172SBrian Feldman mntinfo(struct statfs **mntbuf)
610bc70c172SBrian Feldman {
611bc70c172SBrian Feldman 	static struct statfs *origbuf;
612bc70c172SBrian Feldman 	size_t bufsize;
613bc70c172SBrian Feldman 	int mntsize;
614bc70c172SBrian Feldman 
615a69497d7SMatthew Dillon 	mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
616bc70c172SBrian Feldman 	if (mntsize <= 0)
617bc70c172SBrian Feldman 		return (0);
618bc70c172SBrian Feldman 	bufsize = (mntsize + 1) * sizeof(struct statfs);
619bc70c172SBrian Feldman 	if ((origbuf = malloc(bufsize)) == NULL)
620bc70c172SBrian Feldman 		err(1, "malloc");
621bc70c172SBrian Feldman 	mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
622bc70c172SBrian Feldman 	*mntbuf = origbuf;
623bc70c172SBrian Feldman 	return (mntsize);
624bc70c172SBrian Feldman }
625bc70c172SBrian Feldman 
626eddb4805SIan Dowse /*
6278b918187SIan Dowse  * Convert a hexadecimal filesystem ID to an fsid_t.
628eddb4805SIan Dowse  * Returns 0 on success.
629eddb4805SIan Dowse  */
630eddb4805SIan Dowse int
parsehexfsid(const char * hex,fsid_t * fsid)631eddb4805SIan Dowse parsehexfsid(const char *hex, fsid_t *fsid)
632bc70c172SBrian Feldman {
633eddb4805SIan Dowse 	char hexbuf[3];
634eddb4805SIan Dowse 	int i;
635bc70c172SBrian Feldman 
636eddb4805SIan Dowse 	if (strlen(hex) != sizeof(*fsid) * 2)
637eddb4805SIan Dowse 		return (-1);
638eddb4805SIan Dowse 	hexbuf[2] = '\0';
6395fff0914SIan Dowse 	for (i = 0; i < (int)sizeof(*fsid); i++) {
640eddb4805SIan Dowse 		hexbuf[0] = hex[i * 2];
641eddb4805SIan Dowse 		hexbuf[1] = hex[i * 2 + 1];
642eddb4805SIan Dowse 		if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1]))
643eddb4805SIan Dowse 			return (-1);
644eddb4805SIan Dowse 		((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16);
645bc70c172SBrian Feldman 	}
646eddb4805SIan Dowse 	return (0);
647bc70c172SBrian Feldman }
648bc70c172SBrian Feldman 
6498fae3551SRodney W. Grimes /*
6508fae3551SRodney W. Grimes  * xdr routines for mount rpc's
6518fae3551SRodney W. Grimes  */
6528fae3551SRodney W. Grimes int
xdr_dir(XDR * xdrsp,char * dirp)653bc70c172SBrian Feldman xdr_dir(XDR *xdrsp, char *dirp)
6548fae3551SRodney W. Grimes {
655bc70c172SBrian Feldman 
6560775314bSDoug Rabson 	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
6578fae3551SRodney W. Grimes }
6588fae3551SRodney W. Grimes 
6598fae3551SRodney W. Grimes void
usage(void)660ca25fa25SEd Schouten usage(void)
6618fae3551SRodney W. Grimes {
662bc70c172SBrian Feldman 
663210a5dc8SPhilippe Charnier 	(void)fprintf(stderr, "%s\n%s\n",
664840a8027SRicardo Branco 	    "usage: umount [-dfNnv] special ... | node ... | fsid ...",
665840a8027SRicardo Branco 	    "       umount -a | -A [-F fstab] [-dfnv] [-h host] [-t type]");
6668fae3551SRodney W. Grimes 	exit(1);
6678fae3551SRodney W. Grimes }
668840a8027SRicardo Branco 
669840a8027SRicardo Branco int
md_detach(const char * device)670840a8027SRicardo Branco md_detach(const char *device)
671840a8027SRicardo Branco {
672840a8027SRicardo Branco 	struct md_ioctl mdio;
673840a8027SRicardo Branco 	char *eptr;
674840a8027SRicardo Branco 	int fd;
675840a8027SRicardo Branco 
676840a8027SRicardo Branco 	if (strncmp(device, DEV_MD, sizeof(DEV_MD) - 1)) {
677840a8027SRicardo Branco 		if (!all)
678840a8027SRicardo Branco 			warnx("invalid md device: %s", device);
679840a8027SRicardo Branco 		return (-1);
680840a8027SRicardo Branco 	}
681840a8027SRicardo Branco 
682e96d0d74SRicardo Branco 	memset(&mdio, 0, sizeof(mdio));
683e96d0d74SRicardo Branco 	mdio.md_version = MDIOVERSION;
684e96d0d74SRicardo Branco 	mdio.md_options = fflag ? MD_FORCE : 0;
685840a8027SRicardo Branco 	mdio.md_unit = strtoul(device + sizeof(DEV_MD) - 1, &eptr, 0);
686e96d0d74SRicardo Branco 	if (mdio.md_unit == (unsigned)ULONG_MAX || eptr - device == sizeof(DEV_MD) - 1) {
687840a8027SRicardo Branco 		warnx("invalid md device: %s", device);
688840a8027SRicardo Branco 		return (-1);
689840a8027SRicardo Branco 	}
690840a8027SRicardo Branco 
691840a8027SRicardo Branco 	fd = open(DEV_MDCTL, O_RDWR, 0);
692840a8027SRicardo Branco 	if (fd < 0) {
693840a8027SRicardo Branco 		warn("%s", DEV_MDCTL);
694840a8027SRicardo Branco 		return (-1);
695840a8027SRicardo Branco 	}
696840a8027SRicardo Branco 
697840a8027SRicardo Branco 	if (ioctl(fd, MDIOCDETACH, &mdio) < 0) {
698840a8027SRicardo Branco 		warn("%s", DEV_MD);
699840a8027SRicardo Branco 		close(fd);
700840a8027SRicardo Branco 		return (-1);
701840a8027SRicardo Branco 	}
702840a8027SRicardo Branco 
703840a8027SRicardo Branco 	close(fd);
704840a8027SRicardo Branco 	return (0);
705840a8027SRicardo Branco }
706