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