umount.c revision 270258
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 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1980, 1989, 1993\n\
33	The Regents of the University of California.  All rights reserved.\n";
34#endif /* not lint */
35
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)umount.c	8.8 (Berkeley) 5/8/95";
39#endif
40static const char rcsid[] =
41  "$FreeBSD: stable/10/sbin/umount/umount.c 270258 2014-08-21 04:31:48Z peter $";
42#endif /* not lint */
43
44#include <sys/param.h>
45#include <sys/mount.h>
46#include <sys/socket.h>
47#include <sys/stat.h>
48
49#include <netdb.h>
50#include <rpc/rpc.h>
51#include <rpcsvc/mount.h>
52
53#include <ctype.h>
54#include <err.h>
55#include <errno.h>
56#include <fstab.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61
62#include "mounttab.h"
63
64typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat;
65
66static struct addrinfo *nfshost_ai = NULL;
67static int	fflag, vflag;
68static char	*nfshost;
69
70struct statfs *checkmntlist(char *);
71int	 checkvfsname (const char *, char **);
72struct statfs *getmntentry(const char *fromname, const char *onname,
73	     fsid_t *fsid, dowhat what);
74char   **makevfslist (const char *);
75size_t	 mntinfo (struct statfs **);
76int	 namematch (struct addrinfo *);
77int	 parsehexfsid(const char *hex, fsid_t *fsid);
78int	 sacmp (void *, void *);
79int	 umountall (char **);
80int	 checkname (char *, char **);
81int	 umountfs(struct statfs *sfs);
82void	 usage (void);
83int	 xdr_dir (XDR *, char *);
84
85int
86main(int argc, char *argv[])
87{
88	int all, errs, ch, mntsize, error;
89	char **typelist = NULL;
90	struct statfs *mntbuf, *sfs;
91	struct addrinfo hints;
92
93	all = errs = 0;
94	while ((ch = getopt(argc, argv, "AaF:fh:t:v")) != -1)
95		switch (ch) {
96		case 'A':
97			all = 2;
98			break;
99		case 'a':
100			all = 1;
101			break;
102		case 'F':
103			setfstab(optarg);
104			break;
105		case 'f':
106			fflag = MNT_FORCE;
107			break;
108		case 'h':	/* -h implies -A. */
109			all = 2;
110			nfshost = optarg;
111			break;
112		case 't':
113			if (typelist != NULL)
114				err(1, "only one -t option may be specified");
115			typelist = makevfslist(optarg);
116			break;
117		case 'v':
118			vflag = 1;
119			break;
120		default:
121			usage();
122			/* NOTREACHED */
123		}
124	argc -= optind;
125	argv += optind;
126
127	/* Start disks transferring immediately. */
128	if ((fflag & MNT_FORCE) == 0)
129		sync();
130
131	if ((argc == 0 && !all) || (argc != 0 && all))
132		usage();
133
134	/* -h implies "-t nfs" if no -t flag. */
135	if ((nfshost != NULL) && (typelist == NULL))
136		typelist = makevfslist("nfs");
137
138	if (nfshost != NULL) {
139		memset(&hints, 0, sizeof hints);
140		error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai);
141		if (error)
142			errx(1, "%s: %s", nfshost, gai_strerror(error));
143	}
144
145	switch (all) {
146	case 2:
147		if ((mntsize = mntinfo(&mntbuf)) <= 0)
148			break;
149		/*
150		 * We unmount the nfs-mounts in the reverse order
151		 * that they were mounted.
152		 */
153		for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
154			sfs = &mntbuf[mntsize];
155			if (checkvfsname(sfs->f_fstypename, typelist))
156				continue;
157			if (strcmp(sfs->f_mntonname, "/dev") == 0)
158				continue;
159			if (umountfs(sfs) != 0)
160				errs = 1;
161		}
162		free(mntbuf);
163		break;
164	case 1:
165		if (setfsent() == 0)
166			err(1, "%s", getfstab());
167		errs = umountall(typelist);
168		break;
169	case 0:
170		for (errs = 0; *argv != NULL; ++argv)
171			if (checkname(*argv, typelist) != 0)
172				errs = 1;
173		break;
174	}
175	exit(errs);
176}
177
178int
179umountall(char **typelist)
180{
181	struct xvfsconf vfc;
182	struct fstab *fs;
183	int rval;
184	char *cp;
185	static int firstcall = 1;
186
187	if ((fs = getfsent()) != NULL)
188		firstcall = 0;
189	else if (firstcall)
190		errx(1, "fstab reading failure");
191	else
192		return (0);
193	do {
194		/* Ignore the root. */
195		if (strcmp(fs->fs_file, "/") == 0)
196			continue;
197		/*
198		 * !!!
199		 * Historic practice: ignore unknown FSTAB_* fields.
200		 */
201		if (strcmp(fs->fs_type, FSTAB_RW) &&
202		    strcmp(fs->fs_type, FSTAB_RO) &&
203		    strcmp(fs->fs_type, FSTAB_RQ))
204			continue;
205		/* Ignore unknown file system types. */
206		if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
207			continue;
208		if (checkvfsname(fs->fs_vfstype, typelist))
209			continue;
210
211		/*
212		 * We want to unmount the file systems in the reverse order
213		 * that they were mounted.  So, we save off the file name
214		 * in some allocated memory, and then call recursively.
215		 */
216		if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
217			err(1, "malloc failed");
218		(void)strcpy(cp, fs->fs_file);
219		rval = umountall(typelist);
220		rval = checkname(cp, typelist) || rval;
221		free(cp);
222		return (rval);
223	} while ((fs = getfsent()) != NULL);
224	return (0);
225}
226
227/*
228 * Do magic checks on mountpoint/device/fsid, and then call unmount(2).
229 */
230int
231checkname(char *mntname, char **typelist)
232{
233	char buf[MAXPATHLEN];
234	struct statfs sfsbuf;
235	struct stat sb;
236	struct statfs *sfs;
237	char *delimp;
238	dev_t dev;
239	int len;
240
241	/*
242	 * 1. Check if the name exists in the mounttable.
243	 */
244	sfs = checkmntlist(mntname);
245	/*
246	 * 2. Remove trailing slashes if there are any. After that
247	 * we look up the name in the mounttable again.
248	 */
249	if (sfs == NULL) {
250		len = strlen(mntname);
251		while (len > 1 && mntname[len - 1] == '/')
252			mntname[--len] = '\0';
253		sfs = checkmntlist(mntname);
254	}
255	/*
256	 * 3. Check if the deprecated NFS syntax with an '@' has been used
257	 * and translate it to the ':' syntax. Look up the name in the
258	 * mount table again.
259	 */
260	if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) {
261		snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1,
262		    (int)(delimp - mntname), mntname);
263		len = strlen(buf);
264		while (len > 1 && buf[len - 1] == '/')
265			buf[--len] = '\0';
266		sfs = checkmntlist(buf);
267	}
268	/*
269	 * 4. Resort to a statfs(2) call. This is the last check so that
270	 * hung NFS filesystems for example can be unmounted without
271	 * potentially blocking forever in statfs() as long as the
272	 * filesystem is specified unambiguously. This covers all the
273	 * hard cases such as symlinks and mismatches between the
274	 * mount list and reality.
275	 * We also do this if an ambiguous mount point was specified.
276	 */
277	if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL &&
278	    getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) {
279		if (statfs(mntname, &sfsbuf) != 0) {
280			warn("%s: statfs", mntname);
281		} else if (stat(mntname, &sb) != 0) {
282			warn("%s: stat", mntname);
283		} else if (S_ISDIR(sb.st_mode)) {
284			/* Check that `mntname' is the root directory. */
285			dev = sb.st_dev;
286			snprintf(buf, sizeof(buf), "%s/..", mntname);
287			if (stat(buf, &sb) != 0) {
288				warn("%s: stat", buf);
289			} else if (sb.st_dev == dev) {
290				warnx("%s: not a file system root directory",
291				    mntname);
292				return (1);
293			} else
294				sfs = &sfsbuf;
295		}
296	}
297	if (sfs == NULL) {
298		warnx("%s: unknown file system", mntname);
299		return (1);
300	}
301	if (checkvfsname(sfs->f_fstypename, typelist))
302		return (1);
303	return (umountfs(sfs));
304}
305
306/*
307 * NFS stuff and unmount(2) call
308 */
309int
310umountfs(struct statfs *sfs)
311{
312	char fsidbuf[64];
313	enum clnt_stat clnt_stat;
314	struct timeval try;
315	struct addrinfo *ai, hints;
316	int do_rpc;
317	CLIENT *clp;
318	char *nfsdirname, *orignfsdirname;
319	char *hostp, *delimp;
320
321	ai = NULL;
322	do_rpc = 0;
323	hostp = NULL;
324	nfsdirname = delimp = orignfsdirname = NULL;
325	memset(&hints, 0, sizeof hints);
326
327	if (strcmp(sfs->f_fstypename, "nfs") == 0) {
328		if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL)
329			err(1, "strdup");
330		orignfsdirname = nfsdirname;
331		if (*nfsdirname == '[' &&
332		    (delimp = strchr(nfsdirname + 1, ']')) != NULL &&
333		    *(delimp + 1) == ':') {
334			hostp = nfsdirname + 1;
335			nfsdirname = delimp + 2;
336		} else if ((delimp = strrchr(nfsdirname, ':')) != NULL) {
337			hostp = nfsdirname;
338			nfsdirname = delimp + 1;
339		}
340		if (hostp != NULL) {
341			*delimp = '\0';
342			getaddrinfo(hostp, NULL, &hints, &ai);
343			if (ai == NULL) {
344				warnx("can't get net id for host");
345			}
346		}
347
348		/*
349		 * Check if we have to start the rpc-call later.
350		 * If there are still identical nfs-names mounted,
351		 * we skip the rpc-call. Obviously this has to
352		 * happen before unmount(2), but it should happen
353		 * after the previous namecheck.
354		 * A non-NULL return means that this is the last
355		 * mount from mntfromname that is still mounted.
356		 */
357		if (getmntentry(sfs->f_mntfromname, NULL, NULL,
358		    CHECKUNIQUE) != NULL)
359			do_rpc = 1;
360	}
361
362	if (!namematch(ai)) {
363		free(orignfsdirname);
364		return (1);
365	}
366	/* First try to unmount using the file system ID. */
367	snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0],
368	    sfs->f_fsid.val[1]);
369	if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
370		/* XXX, non-root users get a zero fsid, so don't warn. */
371		if (errno != ENOENT || sfs->f_fsid.val[0] != 0 ||
372		    sfs->f_fsid.val[1] != 0)
373			warn("unmount of %s failed", sfs->f_mntonname);
374		if (errno != ENOENT) {
375			free(orignfsdirname);
376			return (1);
377		}
378		/* Compatibility for old kernels. */
379		if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0)
380			warnx("retrying using path instead of file system ID");
381		if (unmount(sfs->f_mntonname, fflag) != 0) {
382			warn("unmount of %s failed", sfs->f_mntonname);
383			free(orignfsdirname);
384			return (1);
385		}
386	}
387	/* Mark this this file system as unmounted. */
388	getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE);
389	if (vflag)
390		(void)printf("%s: unmount from %s\n", sfs->f_mntfromname,
391		    sfs->f_mntonname);
392	/*
393	 * Report to mountd-server which nfsname
394	 * has been unmounted.
395	 */
396	if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) {
397		clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS3, "udp");
398		if (clp  == NULL) {
399			warnx("%s: %s", hostp,
400			    clnt_spcreateerror("MOUNTPROG"));
401			free(orignfsdirname);
402			return (1);
403		}
404		clp->cl_auth = authsys_create_default();
405		try.tv_sec = 20;
406		try.tv_usec = 0;
407		clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir,
408		    nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try);
409		if (clnt_stat != RPC_SUCCESS) {
410			warnx("%s: %s", hostp,
411			    clnt_sperror(clp, "RPCMNT_UMOUNT"));
412			free(orignfsdirname);
413			return (1);
414		}
415		/*
416		 * Remove the unmounted entry from /var/db/mounttab.
417		 */
418		if (read_mtab()) {
419			clean_mtab(hostp, nfsdirname, vflag);
420			if(!write_mtab(vflag))
421				warnx("cannot remove mounttab entry %s:%s",
422				    hostp, nfsdirname);
423			free_mtab();
424		}
425		auth_destroy(clp->cl_auth);
426		clnt_destroy(clp);
427	}
428	free(orignfsdirname);
429	return (0);
430}
431
432struct statfs *
433getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what)
434{
435	static struct statfs *mntbuf;
436	static size_t mntsize = 0;
437	static char *mntcheck = NULL;
438	struct statfs *sfs, *foundsfs;
439	int i, count;
440
441	if (mntsize <= 0) {
442		if ((mntsize = mntinfo(&mntbuf)) <= 0)
443			return (NULL);
444	}
445	if (mntcheck == NULL) {
446		if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL)
447			err(1, "calloc");
448	}
449	/*
450	 * We want to get the file systems in the reverse order
451	 * that they were mounted. Unmounted file systems are marked
452	 * in a table called 'mntcheck'.
453	 */
454	count = 0;
455	foundsfs = NULL;
456	for (i = mntsize - 1; i >= 0; i--) {
457		if (mntcheck[i])
458			continue;
459		sfs = &mntbuf[i];
460		if (fromname != NULL && strcmp(sfs->f_mntfromname,
461		    fromname) != 0)
462			continue;
463		if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0)
464			continue;
465		if (fsid != NULL && bcmp(&sfs->f_fsid, fsid,
466		    sizeof(*fsid)) != 0)
467			continue;
468
469		switch (what) {
470		case CHECKUNIQUE:
471			foundsfs = sfs;
472			count++;
473			continue;
474		case REMOVE:
475			mntcheck[i] = 1;
476			break;
477		default:
478			break;
479		}
480		return (sfs);
481	}
482
483	if (what == CHECKUNIQUE && count == 1)
484		return (foundsfs);
485	return (NULL);
486}
487
488int
489sacmp(void *sa1, void *sa2)
490{
491	void *p1, *p2;
492	int len;
493
494	if (((struct sockaddr *)sa1)->sa_family !=
495	    ((struct sockaddr *)sa2)->sa_family)
496		return (1);
497
498	switch (((struct sockaddr *)sa1)->sa_family) {
499	case AF_INET:
500		p1 = &((struct sockaddr_in *)sa1)->sin_addr;
501		p2 = &((struct sockaddr_in *)sa2)->sin_addr;
502		len = 4;
503		break;
504	case AF_INET6:
505		p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
506		p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
507		len = 16;
508		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
509		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
510			return (1);
511		break;
512	default:
513		return (1);
514	}
515
516	return memcmp(p1, p2, len);
517}
518
519int
520namematch(struct addrinfo *ai)
521{
522	struct addrinfo *aip;
523
524	if (nfshost == NULL || nfshost_ai == NULL)
525		return (1);
526
527	while (ai != NULL) {
528		aip = nfshost_ai;
529		while (aip != NULL) {
530			if (sacmp(ai->ai_addr, aip->ai_addr) == 0)
531				return (1);
532			aip = aip->ai_next;
533		}
534		ai = ai->ai_next;
535	}
536
537	return (0);
538}
539
540struct statfs *
541checkmntlist(char *mntname)
542{
543	struct statfs *sfs;
544	fsid_t fsid;
545
546	sfs = NULL;
547	if (parsehexfsid(mntname, &fsid) == 0)
548		sfs = getmntentry(NULL, NULL, &fsid, FIND);
549	if (sfs == NULL)
550		sfs = getmntentry(NULL, mntname, NULL, FIND);
551	if (sfs == NULL)
552		sfs = getmntentry(mntname, NULL, NULL, FIND);
553	return (sfs);
554}
555
556size_t
557mntinfo(struct statfs **mntbuf)
558{
559	static struct statfs *origbuf;
560	size_t bufsize;
561	int mntsize;
562
563	mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
564	if (mntsize <= 0)
565		return (0);
566	bufsize = (mntsize + 1) * sizeof(struct statfs);
567	if ((origbuf = malloc(bufsize)) == NULL)
568		err(1, "malloc");
569	mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
570	*mntbuf = origbuf;
571	return (mntsize);
572}
573
574/*
575 * Convert a hexadecimal filesystem ID to an fsid_t.
576 * Returns 0 on success.
577 */
578int
579parsehexfsid(const char *hex, fsid_t *fsid)
580{
581	char hexbuf[3];
582	int i;
583
584	if (strlen(hex) != sizeof(*fsid) * 2)
585		return (-1);
586	hexbuf[2] = '\0';
587	for (i = 0; i < (int)sizeof(*fsid); i++) {
588		hexbuf[0] = hex[i * 2];
589		hexbuf[1] = hex[i * 2 + 1];
590		if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1]))
591			return (-1);
592		((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16);
593	}
594	return (0);
595}
596
597/*
598 * xdr routines for mount rpc's
599 */
600int
601xdr_dir(XDR *xdrsp, char *dirp)
602{
603
604	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
605}
606
607void
608usage(void)
609{
610
611	(void)fprintf(stderr, "%s\n%s\n",
612	    "usage: umount [-fv] special ... | node ... | fsid ...",
613	    "       umount -a | -A [-F fstab] [-fv] [-h host] [-t type]");
614	exit(1);
615}
616