xref: /freebsd/sbin/umount/umount.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
1 /*-
2  * Copyright (c) 1980, 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1980, 1989, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)umount.c	8.8 (Berkeley) 5/8/95";
43 #endif
44 static const char rcsid[] =
45 	"$Id$";
46 #endif /* not lint */
47 
48 #include <sys/param.h>
49 #include <sys/stat.h>
50 #include <sys/mount.h>
51 
52 #include <netdb.h>
53 #include <rpc/rpc.h>
54 #include <nfs/rpcv2.h>
55 
56 #include <err.h>
57 #include <fstab.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 typedef enum { MNTON, MNTFROM } mntwhat;
64 
65 int	fake, fflag, vflag;
66 char	*nfshost;
67 
68 int	 checkvfsname __P((const char *, char **));
69 char	*getmntname __P((char *, mntwhat, char **));
70 char	**makevfslist __P((char *));
71 int	 selected __P((int));
72 int	 namematch __P((struct hostent *));
73 int	 umountall __P((char **));
74 int	 umountfs __P((char *, char **));
75 void	 usage __P((void));
76 int	 xdr_dir __P((XDR *, char *));
77 
78 int
79 main(argc, argv)
80 	int argc;
81 	char *argv[];
82 {
83 	int all, ch, errs, mnts;
84 	char **typelist = NULL;
85 	struct statfs *mntbuf;
86 
87 	/* Start disks transferring immediately. */
88 	sync();
89 
90 	all = 0;
91 	while ((ch = getopt(argc, argv, "AaFfh:t:v")) != -1)
92 		switch (ch) {
93 		case 'A':
94 			all = 2;
95 			break;
96 		case 'a':
97 			all = 1;
98 			break;
99 		case 'F':
100 			fake = 1;
101 			break;
102 		case 'f':
103 			fflag = MNT_FORCE;
104 			break;
105 		case 'h':	/* -h implies -A. */
106 			all = 2;
107 			nfshost = optarg;
108 			break;
109 		case 't':
110 			if (typelist != NULL)
111 				errx(1, "only one -t option may be specified");
112 			typelist = makevfslist(optarg);
113 			break;
114 		case 'v':
115 			vflag = 1;
116 			break;
117 		default:
118 			usage();
119 			/* NOTREACHED */
120 		}
121 	argc -= optind;
122 	argv += optind;
123 
124 	if ((argc == 0 && !all) || (argc != 0 && all))
125 		usage();
126 
127 	/* -h implies "-t nfs" if no -t flag. */
128 	if ((nfshost != NULL) && (typelist == NULL))
129 		typelist = makevfslist("nfs");
130 
131 	switch (all) {
132 	case 2:
133 		if ((mnts = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
134 			warn("getmntinfo");
135 			errs = 1;
136 			break;
137 		}
138 		for (errs = 0, mnts--; mnts > 0; mnts--) {
139 			if (checkvfsname(mntbuf[mnts].f_fstypename, typelist))
140 				continue;
141 			if (umountfs(mntbuf[mnts].f_mntonname, typelist) != 0)
142 				errs = 1;
143 		}
144 		break;
145 	case 1:
146 		if (setfsent() == 0)
147 			err(1, "%s", _PATH_FSTAB);
148 		errs = umountall(typelist);
149 		break;
150 	case 0:
151 		for (errs = 0; *argv != NULL; ++argv)
152 			if (umountfs(*argv, typelist) != 0)
153 				errs = 1;
154 		break;
155 	}
156 	exit(errs);
157 }
158 
159 int
160 umountall(typelist)
161 	char **typelist;
162 {
163 	struct fstab *fs;
164 	int rval;
165 	char *cp;
166 	struct vfsconf vfc;
167 
168 	while ((fs = getfsent()) != NULL) {
169 		/* Ignore the root. */
170 		if (strcmp(fs->fs_file, "/") == 0)
171 			continue;
172 		/*
173 		 * !!!
174 		 * Historic practice: ignore unknown FSTAB_* fields.
175 		 */
176 		if (strcmp(fs->fs_type, FSTAB_RW) &&
177 		    strcmp(fs->fs_type, FSTAB_RO) &&
178 		    strcmp(fs->fs_type, FSTAB_RQ))
179 			continue;
180 		/* If an unknown file system type, complain. */
181 		if (getvfsbyname(fs->fs_vfstype, &vfc) < 0) {
182 			warnx("%s: unknown mount type", fs->fs_vfstype);
183 			continue;
184 		}
185 		if (checkvfsname(fs->fs_vfstype, typelist))
186 			continue;
187 
188 		/*
189 		 * We want to unmount the file systems in the reverse order
190 		 * that they were mounted.  So, we save off the file name
191 		 * in some allocated memory, and then call recursively.
192 		 */
193 		if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
194 			errx(1, "malloc failed");
195 		(void)strcpy(cp, fs->fs_file);
196 		rval = umountall(typelist);
197 		return (umountfs(cp, typelist) || rval);
198 	}
199 	return (0);
200 }
201 
202 int
203 umountfs(name, typelist)
204 	char *name;
205 	char **typelist;
206 {
207 	enum clnt_stat clnt_stat;
208 	struct hostent *hp;
209 	struct sockaddr_in saddr;
210 	struct stat sb;
211 	struct timeval pertry, try;
212 	CLIENT *clp;
213 	int so;
214 	char *type, *delimp, *hostp, *mntpt, *origname, rname[MAXPATHLEN];
215 
216 	if (realpath(name, rname) == NULL) {
217 		/* Continue and let the system call check it... */
218 		strcpy(rname, name);
219 	}
220 
221 	origname = name;
222 	if (stat(name, &sb) < 0) {
223 		mntpt = rname;
224 		if ((getmntname(rname, MNTFROM, &type) == NULL) &&
225 		    ((mntpt = getmntname(name, MNTON, &type)) == NULL)) {
226 			warnx("%s: not currently mounted", name);
227 			return (1);
228 		}
229 	} else if (S_ISBLK(sb.st_mode)) {
230 		if ((mntpt = getmntname(name, MNTON, &type)) == NULL) {
231 			warnx("%s: not currently mounted", name);
232 			return (1);
233 		}
234 	} else if (S_ISDIR(sb.st_mode)) {
235 		mntpt = rname;
236 		if (getmntname(mntpt, MNTFROM, &type) == NULL) {
237 			warnx("%s: not currently mounted", name);
238 			return (1);
239 		}
240 	} else {
241 		warnx("%s: not a directory or special device", name);
242 		return (1);
243 	}
244 	name = rname;
245 
246 	if (checkvfsname(type, typelist))
247 		return (1);
248 
249 	hp = NULL;
250 	if (!strcmp(type, "nfs")) {
251 		if ((delimp = strchr(name, '@')) != NULL) {
252 			hostp = delimp + 1;
253 			*delimp = '\0';
254 			hp = gethostbyname(hostp);
255 			*delimp = '@';
256 		} else if ((delimp = strchr(name, ':')) != NULL) {
257 			*delimp = '\0';
258 			hostp = name;
259 			hp = gethostbyname(hostp);
260 			name = delimp + 1;
261 			*delimp = ':';
262 		}
263 	}
264 
265 	if (!namematch(hp))
266 		return (1);
267 
268 	if (vflag)
269 		(void)printf("%s: unmount from %s\n", origname, mntpt);
270 	if (fake)
271 		return (0);
272 
273 	if (unmount(mntpt, fflag) < 0) {
274 		warn("%s", mntpt);
275 		return (1);
276 	}
277 
278 	if ((hp != NULL) && !(fflag & MNT_FORCE)) {
279 		*delimp = '\0';
280 		memset(&saddr, 0, sizeof(saddr));
281 		saddr.sin_family = AF_INET;
282 		saddr.sin_port = 0;
283 		memmove(&saddr.sin_addr, hp->h_addr,
284 		    MIN(hp->h_length, sizeof(saddr.sin_addr)));
285 		pertry.tv_sec = 3;
286 		pertry.tv_usec = 0;
287 		so = RPC_ANYSOCK;
288 		if ((clp = clntudp_create(&saddr,
289 		    RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) {
290 			clnt_pcreateerror("Cannot MNT PRC");
291 			return (1);
292 		}
293 		clp->cl_auth = authunix_create_default();
294 		try.tv_sec = 20;
295 		try.tv_usec = 0;
296 		clnt_stat = clnt_call(clp,
297 		    RPCMNT_UMOUNT, xdr_dir, name, xdr_void, (caddr_t)0, try);
298 		if (clnt_stat != RPC_SUCCESS) {
299 			clnt_perror(clp, "Bad MNT RPC");
300 			return (1);
301 		}
302 		auth_destroy(clp->cl_auth);
303 		clnt_destroy(clp);
304 	}
305 	return (0);
306 }
307 
308 char *
309 getmntname(name, what, type)
310 	char *name;
311 	mntwhat what;
312 	char **type;
313 {
314 	static struct statfs *mntbuf;
315 	static int mntsize;
316 	int i;
317 
318 	if (mntbuf == NULL &&
319 	    (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
320 		warn("getmntinfo");
321 		return (NULL);
322 	}
323 	for (i = 0; i < mntsize; i++) {
324 		if ((what == MNTON) && !strcmp(mntbuf[i].f_mntfromname, name)) {
325 			if (type)
326 				*type = mntbuf[i].f_fstypename;
327 			return (mntbuf[i].f_mntonname);
328 		}
329 		if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) {
330 			if (type)
331 				*type = mntbuf[i].f_fstypename;
332 			return (mntbuf[i].f_mntfromname);
333 		}
334 	}
335 	return (NULL);
336 }
337 
338 int
339 namematch(hp)
340 	struct hostent *hp;
341 {
342 	char *cp, **np;
343 
344 	if ((hp == NULL) || (nfshost == NULL))
345 		return (1);
346 
347 	if (strcasecmp(nfshost, hp->h_name) == 0)
348 		return (1);
349 
350 	if ((cp = strchr(hp->h_name, '.')) != NULL) {
351 		*cp = '\0';
352 		if (strcasecmp(nfshost, hp->h_name) == 0)
353 			return (1);
354 	}
355 	for (np = hp->h_aliases; *np; np++) {
356 		if (strcasecmp(nfshost, *np) == 0)
357 			return (1);
358 		if ((cp = strchr(*np, '.')) != NULL) {
359 			*cp = '\0';
360 			if (strcasecmp(nfshost, *np) == 0)
361 				return (1);
362 		}
363 	}
364 	return (0);
365 }
366 
367 /*
368  * xdr routines for mount rpc's
369  */
370 int
371 xdr_dir(xdrsp, dirp)
372 	XDR *xdrsp;
373 	char *dirp;
374 {
375 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
376 }
377 
378 void
379 usage()
380 {
381 	(void)fprintf(stderr, "%s\n%s\n",
382 	    "usage: umount [-fv] special | node",
383 	    "       umount -a | -A [-fv] [-h host] [-t type]");
384 	exit(1);
385 }
386