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 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #ifndef lint 31 static const char copyright[] = 32 "@(#) Copyright (c) 1980, 1989, 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34 #endif /* not lint */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)umount.c 8.8 (Berkeley) 5/8/95"; 39 #endif 40 static const char rcsid[] = 41 "$FreeBSD$"; 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/mount.h> 46 #include <sys/socket.h> 47 #include <sys/stat.h> 48 49 #include <netdb.h> 50 #include <rpc/rpc.h> 51 #include <rpcsvc/mount.h> 52 #include <nfs/nfssvc.h> 53 54 #include <ctype.h> 55 #include <err.h> 56 #include <errno.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 typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat; 66 67 static struct addrinfo *nfshost_ai = NULL; 68 static int fflag, vflag; 69 static char *nfshost; 70 71 struct statfs *checkmntlist(char *); 72 int checkvfsname (const char *, char **); 73 struct statfs *getmntentry(const char *fromname, const char *onname, 74 fsid_t *fsid, dowhat what); 75 char **makevfslist (const char *); 76 size_t mntinfo (struct statfs **); 77 int namematch (struct addrinfo *); 78 int parsehexfsid(const char *hex, fsid_t *fsid); 79 int sacmp (void *, void *); 80 int umountall (char **); 81 int checkname (char *, char **); 82 int umountfs(struct statfs *sfs); 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, error; 90 char **typelist = NULL; 91 struct statfs *mntbuf, *sfs; 92 struct addrinfo hints; 93 94 all = errs = 0; 95 while ((ch = getopt(argc, argv, "AaF:fh:nt:v")) != -1) 96 switch (ch) { 97 case 'A': 98 all = 2; 99 break; 100 case 'a': 101 all = 1; 102 break; 103 case 'F': 104 setfstab(optarg); 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 'n': 114 fflag |= MNT_NONBUSY; 115 break; 116 case 't': 117 if (typelist != NULL) 118 err(1, "only one -t option may be specified"); 119 typelist = makevfslist(optarg); 120 break; 121 case 'v': 122 vflag = 1; 123 break; 124 default: 125 usage(); 126 /* NOTREACHED */ 127 } 128 argc -= optind; 129 argv += optind; 130 131 if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0) 132 err(1, "-f and -n are mutually exclusive"); 133 134 /* Start disks transferring immediately. */ 135 if ((fflag & (MNT_FORCE | MNT_NONBUSY)) == 0) 136 sync(); 137 138 if ((argc == 0 && !all) || (argc != 0 && all)) 139 usage(); 140 141 /* -h implies "-t nfs" if no -t flag. */ 142 if ((nfshost != NULL) && (typelist == NULL)) 143 typelist = makevfslist("nfs"); 144 145 if (nfshost != NULL) { 146 memset(&hints, 0, sizeof hints); 147 error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai); 148 if (error) 149 errx(1, "%s: %s", nfshost, gai_strerror(error)); 150 } 151 152 switch (all) { 153 case 2: 154 if ((mntsize = mntinfo(&mntbuf)) <= 0) 155 break; 156 /* 157 * We unmount the nfs-mounts in the reverse order 158 * that they were mounted. 159 */ 160 for (errs = 0, mntsize--; mntsize > 0; mntsize--) { 161 sfs = &mntbuf[mntsize]; 162 if (checkvfsname(sfs->f_fstypename, typelist)) 163 continue; 164 if (strcmp(sfs->f_mntonname, "/dev") == 0) 165 continue; 166 if (umountfs(sfs) != 0) 167 errs = 1; 168 } 169 free(mntbuf); 170 break; 171 case 1: 172 if (setfsent() == 0) 173 err(1, "%s", getfstab()); 174 errs = umountall(typelist); 175 break; 176 case 0: 177 for (errs = 0; *argv != NULL; ++argv) 178 if (checkname(*argv, typelist) != 0) 179 errs = 1; 180 break; 181 } 182 exit(errs); 183 } 184 185 int 186 umountall(char **typelist) 187 { 188 struct xvfsconf vfc; 189 struct fstab *fs; 190 int rval; 191 char *cp; 192 static int firstcall = 1; 193 194 if ((fs = getfsent()) != NULL) 195 firstcall = 0; 196 else if (firstcall) 197 errx(1, "fstab reading failure"); 198 else 199 return (0); 200 do { 201 /* Ignore the root. */ 202 if (strcmp(fs->fs_file, "/") == 0) 203 continue; 204 /* 205 * !!! 206 * Historic practice: ignore unknown FSTAB_* fields. 207 */ 208 if (strcmp(fs->fs_type, FSTAB_RW) && 209 strcmp(fs->fs_type, FSTAB_RO) && 210 strcmp(fs->fs_type, FSTAB_RQ)) 211 continue; 212 /* Ignore unknown file system types. */ 213 if (getvfsbyname(fs->fs_vfstype, &vfc) == -1) 214 continue; 215 if (checkvfsname(fs->fs_vfstype, typelist)) 216 continue; 217 218 /* 219 * We want to unmount the file systems in the reverse order 220 * that they were mounted. So, we save off the file name 221 * in some allocated memory, and then call recursively. 222 */ 223 if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL) 224 err(1, "malloc failed"); 225 (void)strcpy(cp, fs->fs_file); 226 rval = umountall(typelist); 227 rval = checkname(cp, typelist) || rval; 228 free(cp); 229 return (rval); 230 } while ((fs = getfsent()) != NULL); 231 return (0); 232 } 233 234 /* 235 * Do magic checks on mountpoint/device/fsid, and then call unmount(2). 236 */ 237 int 238 checkname(char *mntname, char **typelist) 239 { 240 char buf[MAXPATHLEN]; 241 struct statfs sfsbuf; 242 struct stat sb; 243 struct statfs *sfs; 244 char *delimp; 245 dev_t dev; 246 int len; 247 248 /* 249 * 1. Check if the name exists in the mounttable. 250 */ 251 sfs = checkmntlist(mntname); 252 /* 253 * 2. Remove trailing slashes if there are any. After that 254 * we look up the name in the mounttable again. 255 */ 256 if (sfs == NULL) { 257 len = strlen(mntname); 258 while (len > 1 && mntname[len - 1] == '/') 259 mntname[--len] = '\0'; 260 sfs = checkmntlist(mntname); 261 } 262 /* 263 * 3. Check if the deprecated NFS syntax with an '@' has been used 264 * and translate it to the ':' syntax. Look up the name in the 265 * mount table again. 266 */ 267 if (sfs == NULL && (delimp = strrchr(mntname, '@')) != NULL) { 268 snprintf(buf, sizeof(buf), "%s:%.*s", delimp + 1, 269 (int)(delimp - mntname), mntname); 270 len = strlen(buf); 271 while (len > 1 && buf[len - 1] == '/') 272 buf[--len] = '\0'; 273 sfs = checkmntlist(buf); 274 } 275 /* 276 * 4. Resort to a statfs(2) call. This is the last check so that 277 * hung NFS filesystems for example can be unmounted without 278 * potentially blocking forever in statfs() as long as the 279 * filesystem is specified unambiguously. This covers all the 280 * hard cases such as symlinks and mismatches between the 281 * mount list and reality. 282 * We also do this if an ambiguous mount point was specified. 283 */ 284 if (sfs == NULL || (getmntentry(NULL, mntname, NULL, FIND) != NULL && 285 getmntentry(NULL, mntname, NULL, CHECKUNIQUE) == NULL)) { 286 if (statfs(mntname, &sfsbuf) != 0) { 287 warn("%s: statfs", mntname); 288 } else if (stat(mntname, &sb) != 0) { 289 warn("%s: stat", mntname); 290 } else if (S_ISDIR(sb.st_mode)) { 291 /* Check that `mntname' is the root directory. */ 292 dev = sb.st_dev; 293 snprintf(buf, sizeof(buf), "%s/..", mntname); 294 if (stat(buf, &sb) != 0) { 295 warn("%s: stat", buf); 296 } else if (sb.st_dev == dev) { 297 warnx("%s: not a file system root directory", 298 mntname); 299 return (1); 300 } else 301 sfs = &sfsbuf; 302 } 303 } 304 if (sfs == NULL) { 305 warnx("%s: unknown file system", mntname); 306 return (1); 307 } 308 if (checkvfsname(sfs->f_fstypename, typelist)) 309 return (1); 310 return (umountfs(sfs)); 311 } 312 313 /* 314 * NFS stuff and unmount(2) call 315 */ 316 int 317 umountfs(struct statfs *sfs) 318 { 319 char fsidbuf[64]; 320 enum clnt_stat clnt_stat; 321 struct timeval try; 322 struct addrinfo *ai, hints; 323 int do_rpc; 324 CLIENT *clp; 325 char *nfsdirname, *orignfsdirname; 326 char *hostp, *delimp; 327 char buf[1024]; 328 struct nfscl_dumpmntopts dumpmntopts; 329 const char *proto_ptr = NULL; 330 331 ai = NULL; 332 do_rpc = 0; 333 hostp = NULL; 334 nfsdirname = delimp = orignfsdirname = NULL; 335 memset(&hints, 0, sizeof hints); 336 337 if (strcmp(sfs->f_fstypename, "nfs") == 0) { 338 if ((nfsdirname = strdup(sfs->f_mntfromname)) == NULL) 339 err(1, "strdup"); 340 orignfsdirname = nfsdirname; 341 if (*nfsdirname == '[' && 342 (delimp = strchr(nfsdirname + 1, ']')) != NULL && 343 *(delimp + 1) == ':') { 344 hostp = nfsdirname + 1; 345 nfsdirname = delimp + 2; 346 } else if ((delimp = strrchr(nfsdirname, ':')) != NULL) { 347 hostp = nfsdirname; 348 nfsdirname = delimp + 1; 349 } 350 if (hostp != NULL) { 351 *delimp = '\0'; 352 getaddrinfo(hostp, NULL, &hints, &ai); 353 if (ai == NULL) { 354 warnx("can't get net id for host"); 355 } 356 } 357 358 /* 359 * Check if we have to start the rpc-call later. 360 * If there are still identical nfs-names mounted, 361 * we skip the rpc-call. Obviously this has to 362 * happen before unmount(2), but it should happen 363 * after the previous namecheck. 364 * A non-NULL return means that this is the last 365 * mount from mntfromname that is still mounted. 366 */ 367 if (getmntentry(sfs->f_mntfromname, NULL, NULL, 368 CHECKUNIQUE) != NULL) { 369 do_rpc = 1; 370 proto_ptr = "udp"; 371 /* 372 * Try and find out whether this NFS mount is NFSv4 and 373 * what protocol is being used. If this fails, the 374 * default is NFSv2,3 and use UDP for the Unmount RPC. 375 */ 376 dumpmntopts.ndmnt_fname = sfs->f_mntonname; 377 dumpmntopts.ndmnt_buf = buf; 378 dumpmntopts.ndmnt_blen = sizeof(buf); 379 if (nfssvc(NFSSVC_DUMPMNTOPTS, &dumpmntopts) >= 0) { 380 if (strstr(buf, "nfsv4,") != NULL) 381 do_rpc = 0; 382 else if (strstr(buf, ",tcp,") != NULL) 383 proto_ptr = "tcp"; 384 } 385 } 386 } 387 388 if (!namematch(ai)) { 389 free(orignfsdirname); 390 return (1); 391 } 392 /* First try to unmount using the file system ID. */ 393 snprintf(fsidbuf, sizeof(fsidbuf), "FSID:%d:%d", sfs->f_fsid.val[0], 394 sfs->f_fsid.val[1]); 395 if (unmount(fsidbuf, fflag | MNT_BYFSID) != 0) { 396 /* XXX, non-root users get a zero fsid, so don't warn. */ 397 if (errno != ENOENT || sfs->f_fsid.val[0] != 0 || 398 sfs->f_fsid.val[1] != 0) 399 warn("unmount of %s failed", sfs->f_mntonname); 400 if (errno != ENOENT) { 401 free(orignfsdirname); 402 return (1); 403 } 404 /* Compatibility for old kernels. */ 405 if (sfs->f_fsid.val[0] != 0 || sfs->f_fsid.val[1] != 0) 406 warnx("retrying using path instead of file system ID"); 407 if (unmount(sfs->f_mntonname, fflag) != 0) { 408 warn("unmount of %s failed", sfs->f_mntonname); 409 free(orignfsdirname); 410 return (1); 411 } 412 } 413 /* Mark this this file system as unmounted. */ 414 getmntentry(NULL, NULL, &sfs->f_fsid, REMOVE); 415 if (vflag) 416 (void)printf("%s: unmount from %s\n", sfs->f_mntfromname, 417 sfs->f_mntonname); 418 /* 419 * Report to mountd-server which nfsname 420 * has been unmounted. 421 */ 422 if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) { 423 clp = clnt_create(hostp, MOUNTPROG, MOUNTVERS3, proto_ptr); 424 if (clp == NULL) { 425 warnx("%s: %s", hostp, 426 clnt_spcreateerror("MOUNTPROG")); 427 free(orignfsdirname); 428 return (1); 429 } 430 clp->cl_auth = authsys_create_default(); 431 try.tv_sec = 20; 432 try.tv_usec = 0; 433 clnt_stat = clnt_call(clp, MOUNTPROC_UMNT, (xdrproc_t)xdr_dir, 434 nfsdirname, (xdrproc_t)xdr_void, (caddr_t)0, try); 435 if (clnt_stat != RPC_SUCCESS) { 436 warnx("%s: %s", hostp, 437 clnt_sperror(clp, "RPCMNT_UMOUNT")); 438 free(orignfsdirname); 439 return (1); 440 } 441 /* 442 * Remove the unmounted entry from /var/db/mounttab. 443 */ 444 if (read_mtab()) { 445 clean_mtab(hostp, nfsdirname, vflag); 446 if(!write_mtab(vflag)) 447 warnx("cannot remove mounttab entry %s:%s", 448 hostp, nfsdirname); 449 free_mtab(); 450 } 451 auth_destroy(clp->cl_auth); 452 clnt_destroy(clp); 453 } 454 free(orignfsdirname); 455 return (0); 456 } 457 458 struct statfs * 459 getmntentry(const char *fromname, const char *onname, fsid_t *fsid, dowhat what) 460 { 461 static struct statfs *mntbuf; 462 static size_t mntsize = 0; 463 static int *mntcheck = NULL; 464 struct statfs *sfs, *foundsfs; 465 int i, count; 466 467 if (mntsize <= 0) { 468 if ((mntsize = mntinfo(&mntbuf)) <= 0) 469 return (NULL); 470 } 471 if (mntcheck == NULL) { 472 if ((mntcheck = calloc(mntsize + 1, sizeof(int))) == NULL) 473 err(1, "calloc"); 474 } 475 /* 476 * We want to get the file systems in the reverse order 477 * that they were mounted. Unmounted file systems are marked 478 * in a table called 'mntcheck'. 479 */ 480 count = 0; 481 foundsfs = NULL; 482 for (i = mntsize - 1; i >= 0; i--) { 483 if (mntcheck[i]) 484 continue; 485 sfs = &mntbuf[i]; 486 if (fromname != NULL && strcmp(sfs->f_mntfromname, 487 fromname) != 0) 488 continue; 489 if (onname != NULL && strcmp(sfs->f_mntonname, onname) != 0) 490 continue; 491 if (fsid != NULL && bcmp(&sfs->f_fsid, fsid, 492 sizeof(*fsid)) != 0) 493 continue; 494 495 switch (what) { 496 case CHECKUNIQUE: 497 foundsfs = sfs; 498 count++; 499 continue; 500 case REMOVE: 501 mntcheck[i] = 1; 502 break; 503 default: 504 break; 505 } 506 return (sfs); 507 } 508 509 if (what == CHECKUNIQUE && count == 1) 510 return (foundsfs); 511 return (NULL); 512 } 513 514 int 515 sacmp(void *sa1, void *sa2) 516 { 517 void *p1, *p2; 518 int len; 519 520 if (((struct sockaddr *)sa1)->sa_family != 521 ((struct sockaddr *)sa2)->sa_family) 522 return (1); 523 524 switch (((struct sockaddr *)sa1)->sa_family) { 525 case AF_INET: 526 p1 = &((struct sockaddr_in *)sa1)->sin_addr; 527 p2 = &((struct sockaddr_in *)sa2)->sin_addr; 528 len = 4; 529 break; 530 case AF_INET6: 531 p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; 532 p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; 533 len = 16; 534 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != 535 ((struct sockaddr_in6 *)sa2)->sin6_scope_id) 536 return (1); 537 break; 538 default: 539 return (1); 540 } 541 542 return memcmp(p1, p2, len); 543 } 544 545 int 546 namematch(struct addrinfo *ai) 547 { 548 struct addrinfo *aip; 549 550 if (nfshost == NULL || nfshost_ai == NULL) 551 return (1); 552 553 while (ai != NULL) { 554 aip = nfshost_ai; 555 while (aip != NULL) { 556 if (sacmp(ai->ai_addr, aip->ai_addr) == 0) 557 return (1); 558 aip = aip->ai_next; 559 } 560 ai = ai->ai_next; 561 } 562 563 return (0); 564 } 565 566 struct statfs * 567 checkmntlist(char *mntname) 568 { 569 struct statfs *sfs; 570 fsid_t fsid; 571 572 sfs = NULL; 573 if (parsehexfsid(mntname, &fsid) == 0) 574 sfs = getmntentry(NULL, NULL, &fsid, FIND); 575 if (sfs == NULL) 576 sfs = getmntentry(NULL, mntname, NULL, FIND); 577 if (sfs == NULL) 578 sfs = getmntentry(mntname, NULL, NULL, FIND); 579 return (sfs); 580 } 581 582 size_t 583 mntinfo(struct statfs **mntbuf) 584 { 585 static struct statfs *origbuf; 586 size_t bufsize; 587 int mntsize; 588 589 mntsize = getfsstat(NULL, 0, MNT_NOWAIT); 590 if (mntsize <= 0) 591 return (0); 592 bufsize = (mntsize + 1) * sizeof(struct statfs); 593 if ((origbuf = malloc(bufsize)) == NULL) 594 err(1, "malloc"); 595 mntsize = getfsstat(origbuf, (long)bufsize, MNT_NOWAIT); 596 *mntbuf = origbuf; 597 return (mntsize); 598 } 599 600 /* 601 * Convert a hexadecimal filesystem ID to an fsid_t. 602 * Returns 0 on success. 603 */ 604 int 605 parsehexfsid(const char *hex, fsid_t *fsid) 606 { 607 char hexbuf[3]; 608 int i; 609 610 if (strlen(hex) != sizeof(*fsid) * 2) 611 return (-1); 612 hexbuf[2] = '\0'; 613 for (i = 0; i < (int)sizeof(*fsid); i++) { 614 hexbuf[0] = hex[i * 2]; 615 hexbuf[1] = hex[i * 2 + 1]; 616 if (!isxdigit(hexbuf[0]) || !isxdigit(hexbuf[1])) 617 return (-1); 618 ((u_char *)fsid)[i] = strtol(hexbuf, NULL, 16); 619 } 620 return (0); 621 } 622 623 /* 624 * xdr routines for mount rpc's 625 */ 626 int 627 xdr_dir(XDR *xdrsp, char *dirp) 628 { 629 630 return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); 631 } 632 633 void 634 usage(void) 635 { 636 637 (void)fprintf(stderr, "%s\n%s\n", 638 "usage: umount [-fnv] special ... | node ... | fsid ...", 639 " umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]"); 640 exit(1); 641 } 642