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