xref: /freebsd/sbin/umount/umount.c (revision dce6e6518b85561495cff38a3074a69d29d58a55)
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   "$FreeBSD$";
46 #endif /* not lint */
47 
48 #include <sys/param.h>
49 #include <sys/mount.h>
50 #include <sys/socket.h>
51 
52 #include <netdb.h>
53 #include <rpc/rpc.h>
54 #include <nfs/rpcv2.h>
55 
56 #include <err.h>
57 #include <errno.h>
58 #include <fstab.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 
64 #include "mounttab.h"
65 
66 #define ISDOT(x)	((x)[0] == '.' && (x)[1] == '\0')
67 #define ISDOTDOT(x)	((x)[0] == '.' && (x)[1] == '.' && (x)[2] == '\0')
68 
69 typedef enum { MNTON, MNTFROM, NOTHING } mntwhat;
70 typedef enum { MARK, UNMARK, NAME, COUNT, FREE } dowhat;
71 
72 struct  addrinfo *nfshost_ai = NULL;
73 int	fflag, vflag;
74 char   *nfshost;
75 
76 struct statfs *checkmntlist (char *, char **);
77 int	 checkvfsname (const char *, char **);
78 struct statfs *getmntentry (const char *, const char *, mntwhat, char **,
79 	     dowhat);
80 char 	*getrealname(char *, char *resolved_path);
81 char   **makevfslist (const char *);
82 size_t	 mntinfo (struct statfs **);
83 int	 namematch (struct addrinfo *);
84 int	 sacmp (struct sockaddr *, struct sockaddr *);
85 int	 umountall (char **);
86 int	 checkname (char *, char **);
87 int	 umountfs (char *, char *, fsid_t *, char *);
88 void	 usage (void);
89 int	 xdr_dir (XDR *, char *);
90 
91 int
92 main(int argc, char *argv[])
93 {
94 	int all, errs, ch, mntsize, error;
95 	char **typelist = NULL, *mntonname, *mntfromname;
96 	char *type;
97 	struct statfs *mntbuf, *sfsrev;
98 	struct addrinfo hints;
99 
100 	/* Start disks transferring immediately. */
101 	sync();
102 
103 	all = errs = 0;
104 	while ((ch = getopt(argc, argv, "AaF:fh:t:v")) != -1)
105 		switch (ch) {
106 		case 'A':
107 			all = 2;
108 			break;
109 		case 'a':
110 			all = 1;
111 			break;
112 		case 'F':
113 			setfstab(optarg);
114 			break;
115 		case 'f':
116 			fflag = MNT_FORCE;
117 			break;
118 		case 'h':	/* -h implies -A. */
119 			all = 2;
120 			nfshost = optarg;
121 			break;
122 		case 't':
123 			if (typelist != NULL)
124 				err(1, "only one -t option may be specified");
125 			typelist = makevfslist(optarg);
126 			break;
127 		case 'v':
128 			vflag = 1;
129 			break;
130 		default:
131 			usage();
132 			/* NOTREACHED */
133 		}
134 	argc -= optind;
135 	argv += optind;
136 
137 	if ((argc == 0 && !all) || (argc != 0 && all))
138 		usage();
139 
140 	/* -h implies "-t nfs" if no -t flag. */
141 	if ((nfshost != NULL) && (typelist == NULL))
142 		typelist = makevfslist("nfs");
143 
144 	if (nfshost != NULL) {
145 		memset(&hints, 0, sizeof hints);
146 		error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai);
147 		if (error)
148 			errx(1, "%s: %s", nfshost, gai_strerror(error));
149 	}
150 
151 	switch (all) {
152 	case 2:
153 		if ((mntsize = mntinfo(&mntbuf)) <= 0)
154 			break;
155 		/*
156 		 * We unmount the nfs-mounts in the reverse order
157 		 * that they were mounted.
158 		 */
159 		for (errs = 0, mntsize--; mntsize > 0; mntsize--) {
160 			if (checkvfsname(mntbuf[mntsize].f_fstypename,
161 			    typelist))
162 				continue;
163 			/*
164 			 * Check if a mountpoint is laid over by another mount.
165 			 * A warning will be printed to stderr if this is
166 			 * the case. The laid over mount remains unmounted.
167 			 */
168 			mntonname = mntbuf[mntsize].f_mntonname;
169 			mntfromname = mntbuf[mntsize].f_mntfromname;
170 
171 			sfsrev = getmntentry(mntonname, NULL, MNTON, &type,
172 			    NAME);
173 
174 			if (!fflag && bcmp(&sfsrev->f_fsid,
175 			    &mntbuf[mntsize].f_fsid, sizeof(fsid_t)) != 0) {
176 				warnx("cannot umount %s, %s\n        "
177 				    "is mounted there, umount it first",
178 				    mntonname, sfsrev->f_mntfromname);
179 			}
180 
181 			if (checkname(mntbuf[mntsize].f_mntonname,
182 			    typelist) != 0)
183 				errs = 1;
184 		}
185 		free(mntbuf);
186 		break;
187 	case 1:
188 		if (setfsent() == 0)
189 			err(1, "%s", getfstab());
190 		errs = umountall(typelist);
191 		break;
192 	case 0:
193 		for (errs = 0; *argv != NULL; ++argv)
194 			if (checkname(*argv, typelist) != 0)
195 				errs = 1;
196 		break;
197 	}
198 	(void)getmntentry(NULL, NULL, NOTHING, NULL, FREE);
199 	exit(errs);
200 }
201 
202 int
203 umountall(char **typelist)
204 {
205 	struct xvfsconf vfc;
206 	struct fstab *fs;
207 	int rval;
208 	char *cp;
209 	static int firstcall = 1;
210 
211 	if ((fs = getfsent()) != NULL)
212 		firstcall = 0;
213 	else if (firstcall)
214 		errx(1, "fstab reading failure");
215 	else
216 		return (0);
217 	do {
218 		/* Ignore the root. */
219 		if (strcmp(fs->fs_file, "/") == 0)
220 			continue;
221 		/*
222 		 * !!!
223 		 * Historic practice: ignore unknown FSTAB_* fields.
224 		 */
225 		if (strcmp(fs->fs_type, FSTAB_RW) &&
226 		    strcmp(fs->fs_type, FSTAB_RO) &&
227 		    strcmp(fs->fs_type, FSTAB_RQ))
228 			continue;
229 		/* Ignore unknown file system types. */
230 		if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
231 			continue;
232 		if (checkvfsname(fs->fs_vfstype, typelist))
233 			continue;
234 
235 		/*
236 		 * We want to unmount the file systems in the reverse order
237 		 * that they were mounted.  So, we save off the file name
238 		 * in some allocated memory, and then call recursively.
239 		 */
240 		if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
241 			err(1, "malloc failed");
242 		(void)strcpy(cp, fs->fs_file);
243 		rval = umountall(typelist);
244 		rval = checkname(cp, typelist) || rval;
245 		free(cp);
246 		return (rval);
247 	} while ((fs = getfsent()) != NULL);
248 	return (0);
249 }
250 
251 /*
252  * Do magic checks on mountpoint and device or hand over
253  * it to unmount(2) if everything fails.
254  */
255 int
256 checkname(char *name, char **typelist)
257 {
258 	size_t len;
259 	int speclen;
260 	char *resolved, realname[MAXPATHLEN];
261 	char *type, *hostp, *delimp, *origname;
262 	struct statfs *sfs, *sfsrev;
263 
264 	len = 0;
265 	delimp = hostp = NULL;
266 	sfs = NULL;
267 
268 	/*
269 	 * 1. Check if the name exists in the mounttable.
270 	 */
271 	sfs = checkmntlist(name, &type);
272 	/*
273 	 * 2. Remove trailing slashes if there are any. After that
274 	 * we look up the name in the mounttable again.
275 	 */
276 	if (sfs == NULL) {
277 		speclen = strlen(name);
278 		for (speclen = strlen(name);
279 		    speclen > 1 && name[speclen - 1] == '/';
280 		    speclen--)
281 			name[speclen - 1] = '\0';
282 		sfs = checkmntlist(name, &type);
283 		resolved = name;
284 		/* Save off original name in origname */
285 		if ((origname = strdup(name)) == NULL)
286 			err(1, "strdup");
287 		/*
288 		 * 3. Check if the deprecated nfs-syntax with an '@'
289 		 * has been used and translate it to the ':' syntax.
290 		 * Look up the name in the mounttable again.
291 		 */
292 		if (sfs == NULL) {
293 			if ((delimp = strrchr(name, '@')) != NULL) {
294 				hostp = delimp + 1;
295 				if (*hostp != '\0') {
296 					/*
297 					 * Make both '@' and ':'
298 					 * notations equal
299 					 */
300 					char *host = strdup(hostp);
301 					len = strlen(hostp);
302 					if (host == NULL)
303 						err(1, "strdup");
304 					memmove(name + len + 1, name,
305 					    (size_t)(delimp - name));
306 					name[len] = ':';
307 					memmove(name, host, len);
308 					free(host);
309 				}
310 				for (speclen = strlen(name);
311 				    speclen > 1 && name[speclen - 1] == '/';
312 				    speclen--)
313 					name[speclen - 1] = '\0';
314 				name[len + speclen + 1] = '\0';
315 				sfs = checkmntlist(name, &type);
316 				resolved = name;
317 			}
318 			/*
319 			 * 4. Check if a relative mountpoint has been
320 			 * specified. This should happen as last check,
321 			 * the order is important. To prevent possible
322 			 * nfs-hangs, we just call realpath(3) on the
323 			 * basedir of mountpoint and add the dirname again.
324 			 * Check the name in mounttable one last time.
325 			 */
326 			if (sfs == NULL) {
327 				(void)strcpy(name, origname);
328 				if ((getrealname(name, realname)) != NULL) {
329 					sfs = checkmntlist(realname, &type);
330 					resolved = realname;
331 				}
332 				/*
333 				 * 5. All tests failed, just hand over the
334 				 * mountpoint to the kernel, maybe the statfs
335 				 * structure has been truncated or is not
336 				 * useful anymore because of a chroot(2).
337 				 * Please note that nfs will not be able to
338 				 * notify the nfs-server about unmounting.
339 				 * These things can change in future when the
340 				 * fstat structure get's more reliable,
341 				 * but at the moment we cannot thrust it.
342 				 */
343 				if (sfs == NULL) {
344 					(void)strcpy(name, origname);
345 					if (umountfs(NULL, origname, NULL,
346 					    "none") == 0) {;
347 						warnx("%s not found in "
348 						    "mount table, "
349 						    "unmounted it anyway",
350 						    origname);
351 						free(origname);
352 						return (0);
353 					} else
354 						free(origname);
355 						return (1);
356 				}
357 			}
358 		}
359 		free(origname);
360 	} else
361 		resolved = name;
362 
363 	if (checkvfsname(type, typelist))
364 		return (1);
365 
366 	/*
367 	 * Check if the reverse entrys of the mounttable are really the
368 	 * same as the normal ones.
369 	 */
370 	sfsrev = getmntentry(sfs->f_mntonname, NULL, MNTON, &type, NAME);
371 	/*
372 	 * Mark the uppermost mount as unmounted.
373 	 */
374 	(void)getmntentry(sfs->f_mntfromname, sfs->f_mntonname, NOTHING, &type,
375 	     MARK);
376 	/*
377 	 * If several equal mounts are in the mounttable, check the order
378 	 * and warn the user if necessary.
379 	 */
380 	if (fflag != MNT_FORCE && sfsrev != sfs) {
381 		warnx("cannot umount %s, %s\n        "
382 		    "is mounted there, umount it first",
383 		    sfs->f_mntonname, sfsrev->f_mntfromname);
384 
385 		/* call getmntentry again to set mntcheck[i] to 0 */
386 		(void)getmntentry(sfs->f_mntfromname, sfs->f_mntonname,
387 		    NOTHING, &type, UNMARK);
388 		return (1);
389 	}
390 	return (umountfs(sfs->f_mntfromname, sfs->f_mntonname, &sfs->f_fsid,
391 	    type));
392 }
393 
394 /*
395  * NFS stuff and unmount(2) call
396  */
397 int
398 umountfs(char *mntfromname, char *mntonname, fsid_t *fsid, char *type)
399 {
400 	char fsidbuf[64];
401 	enum clnt_stat clnt_stat;
402 	struct timeval try;
403 	struct addrinfo *ai, hints;
404 	int do_rpc;
405 	CLIENT *clp;
406 	char *nfsdirname, *orignfsdirname;
407 	char *hostp, *delimp;
408 
409 	ai = NULL;
410 	do_rpc = 0;
411 	hostp = NULL;
412 	nfsdirname = delimp = orignfsdirname = NULL;
413 	memset(&hints, 0, sizeof hints);
414 
415 	if (strcmp(type, "nfs") == 0) {
416 		if ((nfsdirname = strdup(mntfromname)) == NULL)
417 			err(1, "strdup");
418 		orignfsdirname = nfsdirname;
419 		if ((delimp = strrchr(nfsdirname, ':')) != NULL) {
420 			*delimp = '\0';
421 			hostp = nfsdirname;
422 			getaddrinfo(hostp, NULL, &hints, &ai);
423 			if (ai == NULL) {
424 				warnx("can't get net id for host");
425 			}
426 			nfsdirname = delimp + 1;
427 		}
428 
429 		/*
430 		 * Check if we have to start the rpc-call later.
431 		 * If there are still identical nfs-names mounted,
432 		 * we skip the rpc-call. Obviously this has to
433 		 * happen before unmount(2), but it should happen
434 		 * after the previous namecheck.
435 		 * A non-NULL return means that this is the last
436 		 * mount from mntfromname that is still mounted.
437 		 */
438 		if (getmntentry(mntfromname, NULL, NOTHING, &type, COUNT)
439 		    != NULL)
440 			do_rpc = 1;
441 	}
442 
443 	if (!namematch(ai))
444 		return (1);
445 	/* First try to unmount using the specified file system ID. */
446 	if (fsid != NULL) {
447 		snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", fsid->val[0],
448 		    fsid->val[1]);
449 		if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) {
450 			warn("unmount of %s failed", mntonname);
451 			if (errno != ENOENT)
452 				return (1);
453 			/* Compatability for old kernels. */
454 			warnx("retrying using path instead of file system ID");
455 			fsid = NULL;
456 		}
457 	}
458 	if (fsid == NULL && unmount(mntonname, fflag) != 0) {
459 		warn("unmount of %s failed", mntonname);
460 		return (1);
461 	}
462 	if (vflag)
463 		(void)printf("%s: unmount from %s\n", mntfromname, mntonname);
464 	/*
465 	 * Report to mountd-server which nfsname
466 	 * has been unmounted.
467 	 */
468 	if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) {
469 		clp = clnt_create(hostp, RPCPROG_MNT, RPCMNT_VER1, "udp");
470 		if (clp  == NULL) {
471 			warnx("%s: %s", hostp,
472 			    clnt_spcreateerror("RPCPROG_MNT"));
473 			return (1);
474 		}
475 		clp->cl_auth = authsys_create_default();
476 		try.tv_sec = 20;
477 		try.tv_usec = 0;
478 		clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, (xdrproc_t)xdr_dir,
479 		    nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try);
480 		if (clnt_stat != RPC_SUCCESS) {
481 			warnx("%s: %s", hostp,
482 			    clnt_sperror(clp, "RPCMNT_UMOUNT"));
483 			return (1);
484 		}
485 		/*
486 		 * Remove the unmounted entry from /var/db/mounttab.
487 		 */
488 		if (read_mtab()) {
489 			clean_mtab(hostp, nfsdirname, vflag);
490 			if(!write_mtab(vflag))
491 				warnx("cannot remove mounttab entry %s:%s",
492 				    hostp, nfsdirname);
493 			free_mtab();
494 		}
495 		free(orignfsdirname);
496 		auth_destroy(clp->cl_auth);
497 		clnt_destroy(clp);
498 	}
499 	return (0);
500 }
501 
502 struct statfs *
503 getmntentry(const char *fromname, const char *onname, mntwhat what,
504     char **type, dowhat mark)
505 {
506 	static struct statfs *mntbuf;
507 	static size_t mntsize = 0;
508 	static char *mntcheck = NULL;
509 	static char *mntcount = NULL;
510 	int i, count;
511 
512 	if (mntsize <= 0) {
513 		if ((mntsize = mntinfo(&mntbuf)) <= 0)
514 			return (NULL);
515 	}
516 	if (mntcheck == NULL) {
517 		if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL ||
518 		    (mntcount = calloc(mntsize + 1, sizeof(int))) == NULL)
519 			err(1, "calloc");
520 	}
521 	/*
522 	 * We want to get the file systems in the reverse order
523 	 * that they were mounted. Mounted and unmounted file systems
524 	 * are marked or unmarked in a table called 'mntcheck'.
525 	 * Unmount(const char *dir, int flags) does only take the
526 	 * mountpoint as argument, not the destination. If we don't pay
527 	 * attention to the order, it can happen that an overlaying
528 	 * file system gets unmounted instead of the one the user
529 	 * has choosen.
530 	 */
531 	switch (mark) {
532 	case NAME:
533 		/* Return only the specific name */
534 		for (i = mntsize - 1; i >= 0; i--) {
535 			if (fromname != NULL && !strcmp((what == MNTFROM) ?
536 			    mntbuf[i].f_mntfromname : mntbuf[i].f_mntonname,
537 			    fromname) && mntcheck[i] != 1) {
538 				if (type)
539 					*type = mntbuf[i].f_fstypename;
540 				return (&mntbuf[i]);
541 			}
542 		}
543 
544 		return (NULL);
545 	case MARK:
546 		/* Mark current mount with '1' and return name */
547 		for (i = mntsize - 1; i >= 0; i--) {
548 			if (mntcheck[i] == 0 &&
549 			    (strcmp(mntbuf[i].f_mntonname, onname) == 0) &&
550 			    (strcmp(mntbuf[i].f_mntfromname, fromname) == 0)) {
551 				mntcheck[i] = 1;
552 				return (&mntbuf[i]);
553 			}
554 		}
555 		return (NULL);
556 	case UNMARK:
557 		/* Unmark current mount with '0' and return name */
558 		for (i = 0; i < mntsize; i++) {
559 			if (mntcheck[i] == 1 &&
560 			    (strcmp(mntbuf[i].f_mntonname, onname) == 0) &&
561 			    (strcmp(mntbuf[i].f_mntfromname, fromname) == 0)) {
562 				mntcheck[i] = 0;
563 				return (&mntbuf[i]);
564 			}
565 		}
566 		return (NULL);
567 	case COUNT:
568 		/* Count the equal mntfromnames */
569 		count = 0;
570 		for (i = mntsize - 1; i >= 0; i--) {
571 			if (strcmp(mntbuf[i].f_mntfromname, fromname) == 0)
572 				count++;
573 		}
574 		/* Mark the already unmounted mounts and return
575 		 * mntfromname if count <= 1. Else return NULL.
576 		 */
577 		for (i = mntsize - 1; i >= 0; i--) {
578 			if (strcmp(mntbuf[i].f_mntfromname, fromname) == 0) {
579 				if (mntcount[i] == 1)
580 					count--;
581 				else {
582 					mntcount[i] = 1;
583 					break;
584 				}
585 			}
586 		}
587 		if (count <= 1)
588 			return (&mntbuf[i]);
589 		else
590 			return (NULL);
591 	case FREE:
592 		free(mntbuf);
593 		free(mntcheck);
594 		free(mntcount);
595 		return (NULL);
596 	default:
597 		return (NULL);
598 	}
599 }
600 
601 int
602 sacmp(struct sockaddr *sa1, struct sockaddr *sa2)
603 {
604 	void *p1, *p2;
605 	int len;
606 
607 	if (sa1->sa_family != sa2->sa_family)
608 		return (1);
609 
610 	switch (sa1->sa_family) {
611 	case AF_INET:
612 		p1 = &((struct sockaddr_in *)sa1)->sin_addr;
613 		p2 = &((struct sockaddr_in *)sa2)->sin_addr;
614 		len = 4;
615 		break;
616 	case AF_INET6:
617 		p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
618 		p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
619 		len = 16;
620 		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
621 		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
622 			return (1);
623 		break;
624 	default:
625 		return (1);
626 	}
627 
628 	return memcmp(p1, p2, len);
629 }
630 
631 int
632 namematch(struct addrinfo *ai)
633 {
634 	struct addrinfo *aip;
635 
636 	if (nfshost == NULL || nfshost_ai == NULL)
637 		return (1);
638 
639 	while (ai != NULL) {
640 		aip = nfshost_ai;
641 		while (aip != NULL) {
642 			if (sacmp(ai->ai_addr, aip->ai_addr) == 0)
643 				return (1);
644 			aip = aip->ai_next;
645 		}
646 		ai = ai->ai_next;
647 	}
648 
649 	return (0);
650 }
651 
652 struct statfs *
653 checkmntlist(char *name, char **type)
654 {
655 	struct statfs *sfs;
656 
657 	sfs = getmntentry(name, NULL, MNTON, type, NAME);
658 	if (sfs == NULL)
659 		sfs = getmntentry(name, NULL, MNTFROM, type, NAME);
660 	return (sfs);
661 }
662 
663 size_t
664 mntinfo(struct statfs **mntbuf)
665 {
666 	static struct statfs *origbuf;
667 	size_t bufsize;
668 	int mntsize;
669 
670 	mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
671 	if (mntsize <= 0)
672 		return (0);
673 	bufsize = (mntsize + 1) * sizeof(struct statfs);
674 	if ((origbuf = malloc(bufsize)) == NULL)
675 		err(1, "malloc");
676 	mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT);
677 	*mntbuf = origbuf;
678 	return (mntsize);
679 }
680 
681 char *
682 getrealname(char *name, char *realname)
683 {
684 	char *dirname;
685 	int havedir;
686 	size_t baselen;
687 	size_t dirlen;
688 
689 	dirname = '\0';
690 	havedir = 0;
691 	if (*name == '/') {
692 		if (ISDOT(name + 1) || ISDOTDOT(name + 1))
693 			strcpy(realname, "/");
694 		else {
695 			if ((dirname = strrchr(name + 1, '/')) == NULL)
696 				snprintf(realname, MAXPATHLEN, "%s", name);
697 			else
698 				havedir = 1;
699 		}
700 	} else {
701 		if (ISDOT(name) || ISDOTDOT(name))
702 			(void)realpath(name, realname);
703 		else {
704 			if ((dirname = strrchr(name, '/')) == NULL) {
705 				if ((realpath(name, realname)) == NULL)
706 					return (NULL);
707 			} else
708 				havedir = 1;
709 		}
710 	}
711 	if (havedir) {
712 		*dirname++ = '\0';
713 		if (ISDOT(dirname)) {
714 			*dirname = '\0';
715 			if ((realpath(name, realname)) == NULL)
716 				return (NULL);
717 		} else if (ISDOTDOT(dirname)) {
718 			*--dirname = '/';
719 			if ((realpath(name, realname)) == NULL)
720 				return (NULL);
721 		} else {
722 			if ((realpath(name, realname)) == NULL)
723 				return (NULL);
724 			baselen = strlen(realname);
725 			dirlen = strlen(dirname);
726 			if (baselen + dirlen + 1 > MAXPATHLEN)
727 				return (NULL);
728 			if (realname[1] == '\0') {
729 				memmove(realname + 1, dirname, dirlen);
730 				realname[dirlen + 1] = '\0';
731 			} else {
732 				realname[baselen] = '/';
733 				memmove(realname + baselen + 1,
734 				    dirname, dirlen);
735 				realname[baselen + dirlen + 1] = '\0';
736 			}
737 		}
738 	}
739 	return (realname);
740 }
741 
742 /*
743  * xdr routines for mount rpc's
744  */
745 int
746 xdr_dir(XDR *xdrsp, char *dirp)
747 {
748 
749 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
750 }
751 
752 void
753 usage()
754 {
755 
756 	(void)fprintf(stderr, "%s\n%s\n",
757 	    "usage: umount [-fv] special | node",
758 	    "       umount -a | -A [ -F fstab] [-fv] [-h host] [-t type]");
759 	exit(1);
760 }
761