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