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