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