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