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 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 struct mtablist *mtabhead; 71 int fflag, vflag; 72 char *nfshost; 73 74 void checkmntlist (char *, char **, char **, char **); 75 int checkvfsname (const char *, char **); 76 char *getmntname (const char *, const char *, 77 mntwhat, char **, dowhat); 78 char *getrealname(char *, char *resolved_path); 79 char **makevfslist (const char *); 80 size_t mntinfo (struct statfs **); 81 int namematch (struct hostent *); 82 int umountall (char **); 83 int umountfs (char *, char **); 84 void usage (void); 85 int xdr_dir (XDR *, char *); 86 87 int 88 main(int argc, char *argv[]) 89 { 90 int all, errs, ch, mntsize; 91 char **typelist = NULL, *mntonname, *mntfromname; 92 char *type, *mntfromnamerev, *mntonnamerev; 93 struct statfs *mntbuf; 94 95 /* Start disks transferring immediately. */ 96 sync(); 97 98 all = errs = 0; 99 while ((ch = getopt(argc, argv, "Aafh:t:v")) != -1) 100 switch (ch) { 101 case 'A': 102 all = 2; 103 break; 104 case 'a': 105 all = 1; 106 break; 107 case 'f': 108 fflag = MNT_FORCE; 109 break; 110 case 'h': /* -h implies -A. */ 111 all = 2; 112 nfshost = optarg; 113 break; 114 case 't': 115 if (typelist != NULL) 116 err(1, "only one -t option may be specified"); 117 typelist = makevfslist(optarg); 118 break; 119 case 'v': 120 vflag = 1; 121 break; 122 default: 123 usage(); 124 /* NOTREACHED */ 125 } 126 argc -= optind; 127 argv += optind; 128 129 if ((argc == 0 && !all) || (argc != 0 && all)) 130 usage(); 131 132 /* -h implies "-t nfs" if no -t flag. */ 133 if ((nfshost != NULL) && (typelist == NULL)) 134 typelist = makevfslist("nfs"); 135 136 switch (all) { 137 case 2: 138 if ((mntsize = mntinfo(&mntbuf)) <= 0) 139 break; 140 /* 141 * We unmount the nfs-mounts in the reverse order 142 * that they were mounted. 143 */ 144 for (errs = 0, mntsize--; mntsize > 0; mntsize--) { 145 if (checkvfsname(mntbuf[mntsize].f_fstypename, 146 typelist)) 147 continue; 148 /* 149 * Check if a mountpoint is laid over by another mount. 150 * A warning will be printed to stderr if this is 151 * the case. The laid over mount remains unmounted. 152 */ 153 mntonname = mntbuf[mntsize].f_mntonname; 154 mntfromname = mntbuf[mntsize].f_mntfromname; 155 mntonnamerev = getmntname(getmntname(mntonname, 156 NULL, MNTFROM, &type, NAME), NULL, 157 MNTON, &type, NAME); 158 159 mntfromnamerev = getmntname(mntonnamerev, 160 NULL, MNTFROM, &type, NAME); 161 162 if (strcmp(mntonnamerev, mntonname) == 0 && 163 strcmp(mntfromnamerev, mntfromname ) != 0) 164 warnx("cannot umount %s, %s\n " 165 "is mounted there, umount it first", 166 mntonname, mntfromnamerev); 167 168 if (umountfs(mntbuf[mntsize].f_mntonname, 169 typelist) != 0) 170 errs = 1; 171 } 172 free(mntbuf); 173 break; 174 case 1: 175 if (setfsent() == 0) 176 err(1, "%s", _PATH_FSTAB); 177 errs = umountall(typelist); 178 break; 179 case 0: 180 for (errs = 0; *argv != NULL; ++argv) 181 if (umountfs(*argv, typelist) != 0) 182 errs = 1; 183 break; 184 } 185 (void)getmntname(NULL, NULL, NOTHING, NULL, FREE); 186 exit(errs); 187 } 188 189 int 190 umountall(char **typelist) 191 { 192 struct vfsconf vfc; 193 struct fstab *fs; 194 int rval; 195 char *cp; 196 static int firstcall = 1; 197 198 if ((fs = getfsent()) != NULL) 199 firstcall = 0; 200 else if (firstcall) 201 errx(1, "fstab reading failure"); 202 else 203 return (0); 204 do { 205 /* Ignore the root. */ 206 if (strcmp(fs->fs_file, "/") == 0) 207 continue; 208 /* 209 * !!! 210 * Historic practice: ignore unknown FSTAB_* fields. 211 */ 212 if (strcmp(fs->fs_type, FSTAB_RW) && 213 strcmp(fs->fs_type, FSTAB_RO) && 214 strcmp(fs->fs_type, FSTAB_RQ)) 215 continue; 216 /* Ignore unknown file system types. */ 217 if (getvfsbyname(fs->fs_vfstype, &vfc) == -1) 218 continue; 219 if (checkvfsname(fs->fs_vfstype, typelist)) 220 continue; 221 222 /* 223 * We want to unmount the file systems in the reverse order 224 * that they were mounted. So, we save off the file name 225 * in some allocated memory, and then call recursively. 226 */ 227 if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL) 228 err(1, "malloc failed"); 229 (void)strcpy(cp, fs->fs_file); 230 rval = umountall(typelist); 231 rval = umountfs(cp, typelist) || rval; 232 free(cp); 233 return (rval); 234 } while ((fs = getfsent()) != NULL); 235 return (0); 236 } 237 238 int 239 umountfs(char *name, char **typelist) 240 { 241 enum clnt_stat clnt_stat; 242 struct hostent *hp; 243 struct mtablist *mtab; 244 struct sockaddr_in saddr; 245 struct timeval pertry, try; 246 CLIENT *clp; 247 size_t len; 248 int so, speclen, do_rpc; 249 char *mntonname, *mntfromname; 250 char *mntfromnamerev; 251 char *nfsdirname, *orignfsdirname; 252 char *resolved, realname[MAXPATHLEN]; 253 char *type, *delimp, *hostp, *origname; 254 255 len = 0; 256 mtab = NULL; 257 mntfromname = mntonname = delimp = hostp = orignfsdirname = NULL; 258 259 /* 260 * 1. Check if the name exists in the mounttable. 261 */ 262 (void)checkmntlist(name, &mntfromname, &mntonname, &type); 263 /* 264 * 2. Remove trailing slashes if there are any. After that 265 * we look up the name in the mounttable again. 266 */ 267 if (mntfromname == NULL && mntonname == NULL) { 268 speclen = strlen(name); 269 for (speclen = strlen(name); 270 speclen > 1 && name[speclen - 1] == '/'; 271 speclen--) 272 name[speclen - 1] = '\0'; 273 (void)checkmntlist(name, &mntfromname, &mntonname, &type); 274 resolved = name; 275 /* Save off original name in origname */ 276 if ((origname = strdup(name)) == NULL) 277 err(1, "strdup"); 278 /* 279 * 3. Check if the deprecated nfs-syntax with an '@' 280 * has been used and translate it to the ':' syntax. 281 * Look up the name in the mounttable again. 282 */ 283 if (mntfromname == NULL && mntonname == NULL) { 284 if ((delimp = strrchr(name, '@')) != NULL) { 285 hostp = delimp + 1; 286 if (*hostp != '\0') { 287 /* 288 * Make both '@' and ':' 289 * notations equal 290 */ 291 char *host = strdup(hostp); 292 len = strlen(hostp); 293 if (host == NULL) 294 err(1, "strdup"); 295 memmove(name + len + 1, name, 296 (size_t)(delimp - name)); 297 name[len] = ':'; 298 memmove(name, host, len); 299 free(host); 300 } 301 for (speclen = strlen(name); 302 speclen > 1 && name[speclen - 1] == '/'; 303 speclen--) 304 name[speclen - 1] = '\0'; 305 name[len + speclen + 1] = '\0'; 306 (void)checkmntlist(name, &mntfromname, 307 &mntonname, &type); 308 resolved = name; 309 } 310 /* 311 * 4. Check if a relative mountpoint has been 312 * specified. This should happen as last check, 313 * the order is important. To prevent possible 314 * nfs-hangs, we just call realpath(3) on the 315 * basedir of mountpoint and add the dirname again. 316 * Check the name in mounttable one last time. 317 */ 318 if (mntfromname == NULL && mntonname == NULL) { 319 (void)strcpy(name, origname); 320 if ((getrealname(name, realname)) != NULL) { 321 (void)checkmntlist(realname, 322 &mntfromname, &mntonname, &type); 323 resolved = realname; 324 } 325 /* 326 * All tests failed, return to main() 327 */ 328 if (mntfromname == NULL && mntonname == NULL) { 329 (void)strcpy(name, origname); 330 warnx("%s: not currently mounted", 331 origname); 332 free(origname); 333 return (1); 334 } 335 } 336 } 337 free(origname); 338 } else 339 resolved = name; 340 341 if (checkvfsname(type, typelist)) 342 return (1); 343 344 hp = NULL; 345 nfsdirname = NULL; 346 if (!strcmp(type, "nfs")) { 347 if ((nfsdirname = strdup(mntfromname)) == NULL) 348 err(1, "strdup"); 349 orignfsdirname = nfsdirname; 350 if ((delimp = strchr(nfsdirname, ':')) != NULL) { 351 *delimp = '\0'; 352 hostp = nfsdirname; 353 if ((hp = gethostbyname(hostp)) == NULL) { 354 warnx("can't get net id for host"); 355 } 356 nfsdirname = delimp + 1; 357 } 358 } 359 /* 360 * Check if the reverse entrys of the mounttable are really the 361 * same as the normal ones. 362 */ 363 if ((mntfromnamerev = strdup(getmntname(getmntname(mntfromname, 364 NULL, MNTON, &type, NAME), NULL, MNTFROM, &type, NAME))) == NULL) 365 err(1, "strdup"); 366 /* 367 * Mark the uppermost mount as unmounted. 368 */ 369 (void)getmntname(mntfromname, mntonname, NOTHING, &type, MARK); 370 /* 371 * If several equal mounts are in the mounttable, check the order 372 * and warn the user if necessary. 373 */ 374 if (strcmp(mntfromnamerev, mntfromname ) != 0 && 375 strcmp(resolved, mntonname) != 0) { 376 warnx("cannot umount %s, %s\n " 377 "is mounted there, umount it first", 378 mntonname, mntfromnamerev); 379 380 /* call getmntname again to set mntcheck[i] to 0 */ 381 (void)getmntname(mntfromname, mntonname, 382 NOTHING, &type, UNMARK); 383 return (1); 384 } 385 free(mntfromnamerev); 386 /* 387 * Check if we have to start the rpc-call later. 388 * If there are still identical nfs-names mounted, 389 * we skip the rpc-call. Obviously this has to 390 * happen before unmount(2), but it should happen 391 * after the previous namecheck. 392 */ 393 if (strcmp(type, "nfs") == 0 && getmntname(mntfromname, NULL, NOTHING, 394 &type, COUNT) != NULL) 395 do_rpc = 1; 396 else 397 do_rpc = 0; 398 if (!namematch(hp)) 399 return (1); 400 if (unmount(mntonname, fflag) != 0 ) { 401 warn("unmount of %s failed", mntonname); 402 return (1); 403 } 404 if (vflag) 405 (void)printf("%s: unmount from %s\n", mntfromname, mntonname); 406 /* 407 * Report to mountd-server which nfsname 408 * has been unmounted. 409 */ 410 if (hp != NULL && !(fflag & MNT_FORCE) && do_rpc) { 411 memset(&saddr, 0, sizeof(saddr)); 412 saddr.sin_family = AF_INET; 413 saddr.sin_port = 0; 414 memmove(&saddr.sin_addr, hp->h_addr, 415 MIN(hp->h_length, sizeof(saddr.sin_addr))); 416 pertry.tv_sec = 3; 417 pertry.tv_usec = 0; 418 so = RPC_ANYSOCK; 419 if ((clp = clntudp_create(&saddr, 420 RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { 421 clnt_pcreateerror("Cannot MNT PRC"); 422 return (1); 423 } 424 clp->cl_auth = authunix_create_default(); 425 try.tv_sec = 20; 426 try.tv_usec = 0; 427 clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, 428 nfsdirname, xdr_void, (caddr_t)0, try); 429 if (clnt_stat != RPC_SUCCESS) { 430 clnt_perror(clp, "Bad MNT RPC"); 431 return (1); 432 } 433 /* 434 * Remove the unmounted entry from /var/db/mounttab. 435 */ 436 if (read_mtab(mtab)) { 437 mtab = mtabhead; 438 clean_mtab(hostp, nfsdirname); 439 if(!write_mtab()) 440 warnx("cannot remove 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