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