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