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