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: src/sbin/umount/umount.c,v 1.22.2.1 2001/12/13 01:27:15 iedowse Exp $"; 46 #endif /* not lint */ 47 48 #include <sys/param.h> 49 #include <sys/mount.h> 50 51 #include <netdb.h> 52 #include <rpc/rpc.h> 53 #include <nfs/rpcv2.h> 54 55 #include <err.h> 56 #include <fstab.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 62 #include "mounttab.h" 63 64 #define ISDOT(x) ((x)[0] == '.' && (x)[1] == '\0') 65 #define ISDOTDOT(x) ((x)[0] == '.' && (x)[1] == '.' && (x)[2] == '\0') 66 67 typedef enum { MNTON, MNTFROM, NOTHING } mntwhat; 68 typedef enum { MARK, UNMARK, NAME, COUNT, FREE } dowhat; 69 70 int fflag, vflag; 71 char *nfshost; 72 73 void checkmntlist (char *, char **, char **, char **); 74 int checkvfsname (const char *, char **); 75 char *getmntname (const char *, const char *, 76 mntwhat, char **, dowhat); 77 char *getrealname(char *, char *resolved_path); 78 char **makevfslist (const char *); 79 size_t mntinfo (struct statfs **); 80 int namematch (struct hostent *); 81 int umountall (char **); 82 int umountfs (char *, char **); 83 void usage (void); 84 int xdr_dir (XDR *, char *); 85 86 int 87 main(int argc, char *argv[]) 88 { 89 int all, errs, ch, mntsize; 90 char **typelist = NULL, *mntonname, *mntfromname; 91 char *type, *mntfromnamerev, *mntonnamerev; 92 struct statfs *mntbuf; 93 94 /* Start disks transferring immediately. */ 95 sync(); 96 97 all = errs = 0; 98 while ((ch = getopt(argc, argv, "Aafh:t:v")) != -1) 99 switch (ch) { 100 case 'A': 101 all = 2; 102 break; 103 case 'a': 104 all = 1; 105 break; 106 case 'f': 107 fflag = MNT_FORCE; 108 break; 109 case 'h': /* -h implies -A. */ 110 all = 2; 111 nfshost = optarg; 112 break; 113 case 't': 114 if (typelist != NULL) 115 err(1, "only one -t option may be specified"); 116 typelist = makevfslist(optarg); 117 break; 118 case 'v': 119 vflag = 1; 120 break; 121 default: 122 usage(); 123 /* NOTREACHED */ 124 } 125 argc -= optind; 126 argv += optind; 127 128 if ((argc == 0 && !all) || (argc != 0 && all)) 129 usage(); 130 131 /* -h implies "-t nfs" if no -t flag. */ 132 if ((nfshost != NULL) && (typelist == NULL)) 133 typelist = makevfslist("nfs"); 134 135 switch (all) { 136 case 2: 137 if ((mntsize = mntinfo(&mntbuf)) <= 0) 138 break; 139 /* 140 * We unmount the nfs-mounts in the reverse order 141 * that they were mounted. 142 */ 143 for (errs = 0, mntsize--; mntsize > 0; mntsize--) { 144 if (checkvfsname(mntbuf[mntsize].f_fstypename, 145 typelist)) 146 continue; 147 /* 148 * Check if a mountpoint is laid over by another mount. 149 * A warning will be printed to stderr if this is 150 * the case. The laid over mount remains unmounted. 151 */ 152 mntonname = mntbuf[mntsize].f_mntonname; 153 mntfromname = mntbuf[mntsize].f_mntfromname; 154 mntonnamerev = getmntname(getmntname(mntonname, 155 NULL, MNTFROM, &type, NAME), NULL, 156 MNTON, &type, NAME); 157 158 mntfromnamerev = getmntname(mntonnamerev, 159 NULL, MNTFROM, &type, NAME); 160 161 if (strcmp(mntonnamerev, mntonname) == 0 && 162 strcmp(mntfromnamerev, mntfromname ) != 0) 163 warnx("cannot umount %s, %s\n " 164 "is mounted there, umount it first", 165 mntonname, mntfromnamerev); 166 167 if (umountfs(mntbuf[mntsize].f_mntonname, 168 typelist) != 0) 169 errs = 1; 170 } 171 free(mntbuf); 172 break; 173 case 1: 174 if (setfsent() == 0) 175 err(1, "%s", _PATH_FSTAB); 176 errs = umountall(typelist); 177 break; 178 case 0: 179 for (errs = 0; *argv != NULL; ++argv) 180 if (umountfs(*argv, typelist) != 0) 181 errs = 1; 182 break; 183 } 184 (void)getmntname(NULL, NULL, NOTHING, NULL, FREE); 185 exit(errs); 186 } 187 188 int 189 umountall(char **typelist) 190 { 191 struct vfsconf vfc; 192 struct fstab *fs; 193 int rval; 194 char *cp; 195 static int firstcall = 1; 196 197 if ((fs = getfsent()) != NULL) 198 firstcall = 0; 199 else if (firstcall) 200 errx(1, "fstab reading failure"); 201 else 202 return (0); 203 do { 204 /* Ignore the root. */ 205 if (strcmp(fs->fs_file, "/") == 0) 206 continue; 207 /* 208 * !!! 209 * Historic practice: ignore unknown FSTAB_* fields. 210 */ 211 if (strcmp(fs->fs_type, FSTAB_RW) && 212 strcmp(fs->fs_type, FSTAB_RO) && 213 strcmp(fs->fs_type, FSTAB_RQ)) 214 continue; 215 /* If an unknown file system type, complain. */ 216 if (getvfsbyname(fs->fs_vfstype, &vfc) == -1) { 217 warnx("%s: unknown mount type", fs->fs_vfstype); 218 continue; 219 } 220 if (checkvfsname(fs->fs_vfstype, typelist)) 221 continue; 222 223 /* 224 * We want to unmount the file systems in the reverse order 225 * that they were mounted. So, we save off the file name 226 * in some allocated memory, and then call recursively. 227 */ 228 if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL) 229 err(1, "malloc failed"); 230 (void)strcpy(cp, fs->fs_file); 231 rval = umountall(typelist); 232 rval = umountfs(cp, typelist) || rval; 233 free(cp); 234 return (rval); 235 } while ((fs = getfsent()) != NULL); 236 return (0); 237 } 238 239 int 240 umountfs(char *name, char **typelist) 241 { 242 enum clnt_stat clnt_stat; 243 struct hostent *hp; 244 struct mtablist *mtab; 245 struct sockaddr_in saddr; 246 struct timeval pertry, try; 247 CLIENT *clp; 248 size_t len; 249 int so, speclen, do_rpc; 250 char *mntonname, *mntfromname; 251 char *mntfromnamerev; 252 char *nfsdirname, *orignfsdirname; 253 char *resolved, realname[MAXPATHLEN]; 254 char *type, *delimp, *hostp, *origname; 255 256 len = 0; 257 mtab = NULL; 258 mntfromname = mntonname = delimp = hostp = orignfsdirname = NULL; 259 260 /* 261 * 1. Check if the name exists in the mounttable. 262 */ 263 (void)checkmntlist(name, &mntfromname, &mntonname, &type); 264 /* 265 * 2. Remove trailing slashes if there are any. After that 266 * we look up the name in the mounttable again. 267 */ 268 if (mntfromname == NULL && mntonname == NULL) { 269 speclen = strlen(name); 270 for (speclen = strlen(name); 271 speclen > 1 && name[speclen - 1] == '/'; 272 speclen--) 273 name[speclen - 1] = '\0'; 274 (void)checkmntlist(name, &mntfromname, &mntonname, &type); 275 resolved = name; 276 /* Save off original name in origname */ 277 if ((origname = strdup(name)) == NULL) 278 err(1, "strdup"); 279 /* 280 * 3. Check if the deprecated nfs-syntax with an '@' 281 * has been used and translate it to the ':' syntax. 282 * Look up the name in the mounttable again. 283 */ 284 if (mntfromname == NULL && mntonname == NULL) { 285 if ((delimp = strrchr(name, '@')) != NULL) { 286 hostp = delimp + 1; 287 if (*hostp != '\0') { 288 /* 289 * Make both '@' and ':' 290 * notations equal 291 */ 292 char *host = strdup(hostp); 293 len = strlen(hostp); 294 if (host == NULL) 295 err(1, "strdup"); 296 memmove(name + len + 1, name, 297 (size_t)(delimp - name)); 298 name[len] = ':'; 299 memmove(name, host, len); 300 free(host); 301 } 302 for (speclen = strlen(name); 303 speclen > 1 && name[speclen - 1] == '/'; 304 speclen--) 305 name[speclen - 1] = '\0'; 306 name[len + speclen + 1] = '\0'; 307 (void)checkmntlist(name, &mntfromname, 308 &mntonname, &type); 309 resolved = name; 310 } 311 /* 312 * 4. Check if a relative mountpoint has been 313 * specified. This should happen as last check, 314 * the order is important. To prevent possible 315 * nfs-hangs, we just call realpath(3) on the 316 * basedir of mountpoint and add the dirname again. 317 * Check the name in mounttable one last time. 318 */ 319 if (mntfromname == NULL && mntonname == NULL) { 320 (void)strcpy(name, origname); 321 if ((getrealname(name, realname)) != NULL) { 322 (void)checkmntlist(realname, 323 &mntfromname, &mntonname, &type); 324 resolved = realname; 325 } 326 /* 327 * All tests failed, return to main() 328 */ 329 if (mntfromname == NULL && mntonname == NULL) { 330 (void)strcpy(name, origname); 331 warnx("%s: not currently mounted", 332 origname); 333 free(origname); 334 return (1); 335 } 336 } 337 } 338 free(origname); 339 } else 340 resolved = name; 341 342 if (checkvfsname(type, typelist)) 343 return (1); 344 345 hp = NULL; 346 nfsdirname = NULL; 347 if (!strcmp(type, "nfs")) { 348 if ((nfsdirname = strdup(mntfromname)) == NULL) 349 err(1, "strdup"); 350 orignfsdirname = nfsdirname; 351 if ((delimp = strchr(nfsdirname, ':')) != NULL) { 352 *delimp = '\0'; 353 hostp = nfsdirname; 354 if ((hp = gethostbyname(hostp)) == NULL) { 355 warnx("can't get net id for host"); 356 } 357 nfsdirname = delimp + 1; 358 } 359 } 360 /* 361 * Check if the reverse entrys of the mounttable are really the 362 * same as the normal ones. 363 */ 364 if ((mntfromnamerev = strdup(getmntname(getmntname(mntfromname, 365 NULL, MNTON, &type, NAME), NULL, MNTFROM, &type, NAME))) == NULL) 366 err(1, "strdup"); 367 /* 368 * Mark the uppermost mount as unmounted. 369 */ 370 (void)getmntname(mntfromname, mntonname, NOTHING, &type, MARK); 371 /* 372 * If several equal mounts are in the mounttable, check the order 373 * and warn the user if necessary. 374 */ 375 if (strcmp(mntfromnamerev, mntfromname ) != 0 && 376 strcmp(resolved, mntonname) != 0) { 377 warnx("cannot umount %s, %s\n " 378 "is mounted there, umount it first", 379 mntonname, mntfromnamerev); 380 381 /* call getmntname again to set mntcheck[i] to 0 */ 382 (void)getmntname(mntfromname, mntonname, 383 NOTHING, &type, UNMARK); 384 return (1); 385 } 386 free(mntfromnamerev); 387 /* 388 * Check if we have to start the rpc-call later. 389 * If there are still identical nfs-names mounted, 390 * we skip the rpc-call. Obviously this has to 391 * happen before unmount(2), but it should happen 392 * after the previous namecheck. 393 */ 394 if (strcmp(type, "nfs") == 0 && getmntname(mntfromname, NULL, NOTHING, 395 &type, COUNT) != NULL) 396 do_rpc = 1; 397 else 398 do_rpc = 0; 399 if (!namematch(hp)) 400 return (1); 401 if (unmount(mntonname, fflag) != 0 ) { 402 warn("unmount of %s failed", mntonname); 403 return (1); 404 } 405 if (vflag) 406 (void)printf("%s: unmount from %s\n", mntfromname, mntonname); 407 /* 408 * Report to mountd-server which nfsname 409 * has been unmounted. 410 */ 411 if (hp != NULL && !(fflag & MNT_FORCE) && do_rpc) { 412 memset(&saddr, 0, sizeof(saddr)); 413 saddr.sin_family = AF_INET; 414 saddr.sin_port = 0; 415 memmove(&saddr.sin_addr, hp->h_addr, 416 MIN(hp->h_length, sizeof(saddr.sin_addr))); 417 pertry.tv_sec = 3; 418 pertry.tv_usec = 0; 419 so = RPC_ANYSOCK; 420 if ((clp = clntudp_create(&saddr, 421 RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { 422 clnt_pcreateerror("Cannot MNT PRC"); 423 return (1); 424 } 425 clp->cl_auth = authunix_create_default(); 426 try.tv_sec = 20; 427 try.tv_usec = 0; 428 clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, 429 nfsdirname, xdr_void, (caddr_t)0, try); 430 if (clnt_stat != RPC_SUCCESS) { 431 clnt_perror(clp, "Bad MNT RPC"); 432 return (1); 433 } 434 /* 435 * Remove the unmounted entry from /var/db/mounttab. 436 */ 437 if (read_mtab()) { 438 clean_mtab(hostp, nfsdirname, vflag); 439 if(!write_mtab(vflag)) 440 warnx("cannot remove mounttab entry %s:%s", 441 hostp, nfsdirname); 442 free_mtab(); 443 } 444 free(orignfsdirname); 445 auth_destroy(clp->cl_auth); 446 clnt_destroy(clp); 447 } 448 return (0); 449 } 450 451 char * 452 getmntname(const char *fromname, const char *onname, 453 mntwhat what, char **type, dowhat mark) 454 { 455 static struct statfs *mntbuf; 456 static size_t mntsize = 0; 457 static char *mntcheck = NULL; 458 static char *mntcount = NULL; 459 int i, count; 460 461 if (mntsize <= 0) { 462 if ((mntsize = mntinfo(&mntbuf)) <= 0) 463 return (NULL); 464 } 465 if (mntcheck == NULL) { 466 if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL || 467 (mntcount = calloc(mntsize + 1, sizeof(int))) == NULL) 468 err(1, "calloc"); 469 } 470 /* 471 * We want to get the file systems in the reverse order 472 * that they were mounted. Mounted and unmounted filesystems 473 * are marked or unmarked in a table called 'mntcheck'. 474 * Unmount(const char *dir, int flags) does only take the 475 * mountpoint as argument, not the destination. If we don't pay 476 * attention to the order, it can happen that a overlaying 477 * filesystem get's unmounted instead of the one the user 478 * has choosen. 479 */ 480 switch (mark) { 481 case NAME: 482 /* Return only the specific name */ 483 for (i = mntsize - 1; i >= 0; i--) { 484 if (fromname != NULL && what == MNTON && 485 !strcmp(mntbuf[i].f_mntfromname, fromname) && 486 mntcheck[i] != 1) { 487 if (type) 488 *type = mntbuf[i].f_fstypename; 489 return (mntbuf[i].f_mntonname); 490 } 491 if (fromname != NULL && what == MNTFROM && 492 !strcmp(mntbuf[i].f_mntonname, fromname) && 493 mntcheck[i] != 1) { 494 if (type) 495 *type = mntbuf[i].f_fstypename; 496 return (mntbuf[i].f_mntfromname); 497 } 498 } 499 return (NULL); 500 case MARK: 501 /* Mark current mount with '1' and return name */ 502 for (i = mntsize - 1; i >= 0; i--) { 503 if (mntcheck[i] == 0 && 504 (strcmp(mntbuf[i].f_mntonname, onname) == 0) && 505 (strcmp(mntbuf[i].f_mntfromname, fromname) == 0)) { 506 mntcheck[i] = 1; 507 return (mntbuf[i].f_mntonname); 508 } 509 } 510 return (NULL); 511 case UNMARK: 512 /* Unmark current mount with '0' and return name */ 513 for (i = 0; i < mntsize; i++) { 514 if (mntcheck[i] == 1 && 515 (strcmp(mntbuf[i].f_mntonname, onname) == 0) && 516 (strcmp(mntbuf[i].f_mntfromname, fromname) == 0)) { 517 mntcheck[i] = 0; 518 return (mntbuf[i].f_mntonname); 519 } 520 } 521 return (NULL); 522 case COUNT: 523 /* Count the equal mntfromnames */ 524 count = 0; 525 for (i = mntsize - 1; i >= 0; i--) { 526 if (strcmp(mntbuf[i].f_mntfromname, fromname) == 0) 527 count++; 528 } 529 /* Mark the already unmounted mounts and return 530 * mntfromname if count <= 1. Else return NULL. 531 */ 532 for (i = mntsize - 1; i >= 0; i--) { 533 if (strcmp(mntbuf[i].f_mntfromname, fromname) == 0) { 534 if (mntcount[i] == 1) 535 count--; 536 else { 537 mntcount[i] = 1; 538 break; 539 } 540 } 541 } 542 if (count <= 1) 543 return (mntbuf[i].f_mntonname); 544 else 545 return (NULL); 546 case FREE: 547 free(mntbuf); 548 free(mntcheck); 549 free(mntcount); 550 return (NULL); 551 default: 552 return (NULL); 553 } 554 } 555 556 int 557 namematch(struct hostent *hp) 558 { 559 char *cp, **np; 560 561 if ((hp == NULL) || (nfshost == NULL)) 562 return (1); 563 564 if (strcasecmp(nfshost, hp->h_name) == 0) 565 return (1); 566 567 if ((cp = strchr(hp->h_name, '.')) != NULL) { 568 *cp = '\0'; 569 if (strcasecmp(nfshost, hp->h_name) == 0) 570 return (1); 571 } 572 for (np = hp->h_aliases; *np; np++) { 573 if (strcasecmp(nfshost, *np) == 0) 574 return (1); 575 if ((cp = strchr(*np, '.')) != NULL) { 576 *cp = '\0'; 577 if (strcasecmp(nfshost, *np) == 0) 578 return (1); 579 } 580 } 581 return (0); 582 } 583 584 void 585 checkmntlist(char *name, char **fromname, char **onname, char **type) 586 { 587 588 *fromname = getmntname(name, NULL, MNTFROM, type, NAME); 589 if (*fromname == NULL) { 590 *onname = getmntname(name, NULL, MNTON, type, NAME); 591 if (*onname != NULL) 592 *fromname = name; 593 } else 594 *onname = name; 595 } 596 597 size_t 598 mntinfo(struct statfs **mntbuf) 599 { 600 static struct statfs *origbuf; 601 size_t bufsize; 602 int mntsize; 603 604 mntsize = getfsstat(NULL, 0, MNT_NOWAIT); 605 if (mntsize <= 0) 606 return (0); 607 bufsize = (mntsize + 1) * sizeof(struct statfs); 608 if ((origbuf = malloc(bufsize)) == NULL) 609 err(1, "malloc"); 610 mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT); 611 *mntbuf = origbuf; 612 return (mntsize); 613 } 614 615 char * 616 getrealname(char *name, char *realname) 617 { 618 char *dirname; 619 int havedir; 620 size_t baselen; 621 size_t dirlen; 622 623 dirname = '\0'; 624 havedir = 0; 625 if (*name == '/') { 626 if (ISDOT(name + 1) || ISDOTDOT(name + 1)) 627 strcpy(realname, "/"); 628 else { 629 if ((dirname = strrchr(name + 1, '/')) == NULL) 630 snprintf(realname, MAXPATHLEN, "%s", name); 631 else 632 havedir = 1; 633 } 634 } else { 635 if (ISDOT(name) || ISDOTDOT(name)) 636 (void)realpath(name, realname); 637 else { 638 if ((dirname = strrchr(name, '/')) == NULL) { 639 if ((realpath(name, realname)) == NULL) 640 return (NULL); 641 } else 642 havedir = 1; 643 } 644 } 645 if (havedir) { 646 *dirname++ = '\0'; 647 if (ISDOT(dirname)) { 648 *dirname = '\0'; 649 if ((realpath(name, realname)) == NULL) 650 return (NULL); 651 } else if (ISDOTDOT(dirname)) { 652 *--dirname = '/'; 653 if ((realpath(name, realname)) == NULL) 654 return (NULL); 655 } else { 656 if ((realpath(name, realname)) == NULL) 657 return (NULL); 658 baselen = strlen(realname); 659 dirlen = strlen(dirname); 660 if (baselen + dirlen + 1 > MAXPATHLEN) 661 return (NULL); 662 if (realname[1] == '\0') { 663 memmove(realname + 1, dirname, dirlen); 664 realname[dirlen + 1] = '\0'; 665 } else { 666 realname[baselen] = '/'; 667 memmove(realname + baselen + 1, 668 dirname, dirlen); 669 realname[baselen + dirlen + 1] = '\0'; 670 } 671 } 672 } 673 return (realname); 674 } 675 676 /* 677 * xdr routines for mount rpc's 678 */ 679 int 680 xdr_dir(XDR *xdrsp, char *dirp) 681 { 682 683 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN)); 684 } 685 686 void 687 usage() 688 { 689 690 (void)fprintf(stderr, "%s\n%s\n", 691 "usage: umount [-fv] special | node", 692 " umount -a | -A [-fv] [-h host] [-t type]"); 693 exit(1); 694 } 695