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 } 755