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