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