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