]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/umount/umount.c
This commit was generated by cvs2svn to compensate for changes in r69746,
[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                 /* Ignore unknown file system types. */
217                 if (getvfsbyname(fs->fs_vfstype, &vfc) == -1)
218                         continue;
219                 if (checkvfsname(fs->fs_vfstype, typelist))
220                         continue;
221
222                 /*
223                  * We want to unmount the file systems in the reverse order
224                  * that they were mounted.  So, we save off the file name
225                  * in some allocated memory, and then call recursively.
226                  */
227                 if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL)
228                         err(1, "malloc failed");
229                 (void)strcpy(cp, fs->fs_file);
230                 rval = umountall(typelist);
231                 rval = umountfs(cp, typelist) || rval;
232                 free(cp);
233                 return (rval);
234         } while ((fs = getfsent()) != NULL);
235         return (0);
236 }
237
238 int
239 umountfs(char *name, char **typelist)
240 {
241         enum clnt_stat clnt_stat;
242         struct hostent *hp;
243         struct mtablist *mtab;
244         struct sockaddr_in saddr;
245         struct timeval pertry, try;
246         CLIENT *clp;
247         size_t len;
248         int so, speclen, do_rpc;
249         char *mntonname, *mntfromname;
250         char *mntfromnamerev;
251         char *nfsdirname, *orignfsdirname;
252         char *resolved, realname[MAXPATHLEN];
253         char *type, *delimp, *hostp, *origname;
254
255         len = 0;
256         mtab = NULL;
257         mntfromname = mntonname = delimp = hostp = orignfsdirname = NULL;
258
259         /*
260          * 1. Check if the name exists in the mounttable.
261          */
262         (void)checkmntlist(name, &mntfromname, &mntonname, &type);
263         /*
264          * 2. Remove trailing slashes if there are any. After that
265          * we look up the name in the mounttable again.
266          */
267         if (mntfromname == NULL && mntonname == NULL) {
268                 speclen = strlen(name);
269                 for (speclen = strlen(name); 
270                     speclen > 1 && name[speclen - 1] == '/';
271                     speclen--)
272                         name[speclen - 1] = '\0';
273                 (void)checkmntlist(name, &mntfromname, &mntonname, &type);
274                 resolved = name;
275                 /* Save off original name in origname */
276                 if ((origname = strdup(name)) == NULL)
277                         err(1, "strdup");
278                 /*
279                  * 3. Check if the deprecated nfs-syntax with an '@'
280                  * has been used and translate it to the ':' syntax.
281                  * Look up the name in the mounttable again.
282                  */
283                 if (mntfromname == NULL && mntonname == NULL) {
284                         if ((delimp = strrchr(name, '@')) != NULL) {
285                                 hostp = delimp + 1;
286                                 if (*hostp != '\0') {
287                                         /*
288                                          * Make both '@' and ':'
289                                          * notations equal 
290                                          */
291                                         char *host = strdup(hostp);
292                                         len = strlen(hostp);
293                                         if (host == NULL)
294                                                 err(1, "strdup");
295                                         memmove(name + len + 1, name,
296                                             (size_t)(delimp - name));
297                                         name[len] = ':';
298                                         memmove(name, host, len);
299                                         free(host);
300                                 }
301                                 for (speclen = strlen(name); 
302                                     speclen > 1 && name[speclen - 1] == '/';
303                                     speclen--)
304                                         name[speclen - 1] = '\0';
305                                 name[len + speclen + 1] = '\0';
306                                 (void)checkmntlist(name, &mntfromname,
307                                     &mntonname, &type);
308                                 resolved = name;
309                         }
310                         /*
311                          * 4. Check if a relative mountpoint has been
312                          * specified. This should happen as last check,
313                          * the order is important. To prevent possible
314                          * nfs-hangs, we just call realpath(3) on the
315                          * basedir of mountpoint and add the dirname again.
316                          * Check the name in mounttable one last time.
317                          */
318                         if (mntfromname == NULL && mntonname == NULL) {
319                                 (void)strcpy(name, origname);
320                                 if ((getrealname(name, realname)) != NULL) {
321                                         (void)checkmntlist(realname,
322                                             &mntfromname, &mntonname, &type);
323                                         resolved = realname;
324                                 }
325                                 /*
326                                  * All tests failed, return to main()
327                                  */
328                                 if (mntfromname == NULL && mntonname == NULL) {
329                                         (void)strcpy(name, origname);
330                                         warnx("%s: not currently mounted",
331                                             origname);
332                                         free(origname);
333                                         return (1);
334                                 }
335                         }
336                 }
337                 free(origname);
338         } else
339                 resolved = name;
340
341         if (checkvfsname(type, typelist))
342                 return (1);
343
344         hp = NULL;
345         nfsdirname = NULL;
346         if (!strcmp(type, "nfs")) {
347                 if ((nfsdirname = strdup(mntfromname)) == NULL)
348                         err(1, "strdup");
349                 orignfsdirname = nfsdirname;
350                 if ((delimp = strchr(nfsdirname, ':')) != NULL) {
351                         *delimp = '\0';
352                         hostp = nfsdirname;
353                         if ((hp = gethostbyname(hostp)) == NULL) {
354                                 warnx("can't get net id for host");
355                         }
356                         nfsdirname = delimp + 1;
357                 }
358         }
359         /*
360          * Check if the reverse entrys of the mounttable are really the
361          * same as the normal ones.
362          */
363         if ((mntfromnamerev = strdup(getmntname(getmntname(mntfromname,
364             NULL, MNTON, &type, NAME), NULL, MNTFROM, &type, NAME))) == NULL)
365                 err(1, "strdup");
366         /*
367          * Mark the uppermost mount as unmounted.
368          */
369         (void)getmntname(mntfromname, mntonname, NOTHING, &type, MARK);
370         /*
371          * If several equal mounts are in the mounttable, check the order
372          * and warn the user if necessary.
373          */
374         if (strcmp(mntfromnamerev, mntfromname ) != 0 &&
375             strcmp(resolved, mntonname) != 0) {
376                 warnx("cannot umount %s, %s\n        "
377                     "is mounted there, umount it first",
378                     mntonname, mntfromnamerev);
379
380                 /* call getmntname again to set mntcheck[i] to 0 */
381                 (void)getmntname(mntfromname, mntonname,
382                     NOTHING, &type, UNMARK);
383                 return (1);
384         }
385         free(mntfromnamerev);
386         /*
387          * Check if we have to start the rpc-call later.
388          * If there are still identical nfs-names mounted,
389          * we skip the rpc-call. Obviously this has to
390          * happen before unmount(2), but it should happen
391          * after the previous namecheck.
392          */
393         if (strcmp(type, "nfs") == 0 && getmntname(mntfromname, NULL, NOTHING,
394             &type, COUNT) != NULL)
395                 do_rpc = 1;
396         else
397                 do_rpc = 0;
398         if (!namematch(hp))
399                 return (1);
400         if (unmount(mntonname, fflag) != 0 ) {
401                 warn("unmount of %s failed", mntonname);
402                 return (1);
403         }
404         if (vflag)
405                 (void)printf("%s: unmount from %s\n", mntfromname, mntonname);
406         /*
407          * Report to mountd-server which nfsname
408          * has been unmounted.
409          */
410         if (hp != NULL && !(fflag & MNT_FORCE) && do_rpc) {
411                 memset(&saddr, 0, sizeof(saddr));
412                 saddr.sin_family = AF_INET;
413                 saddr.sin_port = 0;
414                 memmove(&saddr.sin_addr, hp->h_addr, 
415                     MIN(hp->h_length, sizeof(saddr.sin_addr)));
416                 pertry.tv_sec = 3;
417                 pertry.tv_usec = 0;
418                 so = RPC_ANYSOCK;
419                 if ((clp = clntudp_create(&saddr,
420                     RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) {
421                         clnt_pcreateerror("Cannot MNT PRC");
422                         return (1);
423                 }
424                 clp->cl_auth = authunix_create_default();
425                 try.tv_sec = 20;
426                 try.tv_usec = 0;
427                 clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir,
428                     nfsdirname, xdr_void, (caddr_t)0, try);
429                 if (clnt_stat != RPC_SUCCESS) {
430                         clnt_perror(clp, "Bad MNT RPC");
431                         return (1);
432                 }
433                 /*
434                  * Remove the unmounted entry from /var/db/mounttab.
435                  */
436                 if (read_mtab(mtab)) {
437                         mtab = mtabhead;
438                         clean_mtab(hostp, nfsdirname);
439                         if(!write_mtab())
440                                 warnx("cannot remove 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 }