1#ifndef _DARWIN_USE_64_BIT_INODE 2# define _DARWIN_USE_64_BIT_INODE 1 3#endif 4/* 5 * Copyright (c) 1999-2010 Apple Inc. All rights reserved. 6 * 7 * @APPLE_LICENSE_HEADER_START@ 8 * 9 * This file contains Original Code and/or Modifications of Original Code 10 * as defined in and that are subject to the Apple Public Source License 11 * Version 2.0 (the 'License'). You may not use this file except in 12 * compliance with the License. Please obtain a copy of the License at 13 * http://www.opensource.apple.com/apsl/ and read it before using this 14 * file. 15 * 16 * The Original Code and all software distributed under the License are 17 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 18 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 19 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 21 * Please see the License for the specific language governing rights and 22 * limitations under the License. 23 * 24 * @APPLE_LICENSE_HEADER_END@ 25 */ 26/*- 27 * Copyright (c) 1980, 1989, 1993 28 * The Regents of the University of California. All rights reserved. 29 * 30 * Redistribution and use in source and binary forms, with or without 31 * modification, are permitted provided that the following conditions 32 * are met: 33 * 1. Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in the 37 * documentation and/or other materials provided with the distribution. 38 * 3. All advertising materials mentioning features or use of this software 39 * must display the following acknowledgement: 40 * This product includes software developed by the University of 41 * California, Berkeley and its contributors. 42 * 4. Neither the name of the University nor the names of its contributors 43 * may be used to endorse or promote products derived from this software 44 * without specific prior written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 49 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 56 * SUCH DAMAGE. 57 */ 58 59 60#include <sys/param.h> 61#include <sys/stat.h> 62#include <sys/mount.h> 63#include <sys/time.h> 64#include <sys/sysctl.h> 65#include <System/sys/fsctl.h> 66 67#include <netdb.h> 68#include <arpa/inet.h> 69 70#include <err.h> 71#include <fstab.h> 72#include <stdio.h> 73#include <stdlib.h> 74#include <string.h> 75#include <ctype.h> 76#include <unistd.h> 77#include <errno.h> 78 79#include <pthread.h> 80 81struct syncarg { 82 const char *mntname; 83 int wakeup_flag; 84 pthread_cond_t *wakeup_cond; 85 pthread_mutex_t *wakeup_lock; 86}; 87 88typedef enum { MNTON, MNTFROM } mntwhat; 89 90int fake, fflag, vflag; 91char *nfshost; 92 93int checkvfsname(const char *, char **); 94char *getmntname(const char *, mntwhat, char **); 95int getmntfsid(const char *, fsid_t *); 96int sysctl_fsid(int, fsid_t *, void *, size_t *, void *, size_t); 97int unmount_by_fsid(const char *mntpt, int flag); 98char **makevfslist(char *); 99int selected(int); 100int namematch(struct hostent *); 101int umountall(char **); 102int umountfs(char *, char **); 103void usage(void); 104 105static void* 106syncit(void *vap) { 107 int rv; 108 pthread_mutex_t *lock; 109 int full_sync = FSCTL_SYNC_WAIT; 110 struct syncarg *args = vap; 111 112 rv = fsctl(args->mntname, FSIOC_SYNC_VOLUME, &full_sync, 0); 113 if (rv == -1) { 114#ifdef DEBUG 115 warn("fsctl %s", args->mntname); 116#endif 117 } 118 119 lock = args->wakeup_lock; 120 (void)pthread_mutex_lock(lock); 121 args->wakeup_flag = 1; 122 pthread_cond_signal(args->wakeup_cond); 123 (void)pthread_mutex_unlock(lock); 124 125 return NULL; 126} 127 128int 129main(int argc, char *argv[]) 130{ 131 int all, ch, errs, mnts; 132 char **typelist = NULL; 133 struct statfs *mntbuf; 134 135 /* 136 * We used to call sync(2) here, but this should be unneccessary 137 * given that a sync occurs at a more proper level (VFS_SYNC() 138 * in dounmount() in the non-forced unmount case). 139 * 140 * We add the sync() back in for the -f case below to cover the 141 * situation where the filesystem was mounted RW and force 142 * unmounted when it really didn't have to be. 143 * 144 * See 5328558 for some context. 145 */ 146 147 all = 0; 148 while ((ch = getopt(argc, argv, "AaFfh:t:v")) != EOF) 149 switch (ch) { 150 case 'A': 151 all = 2; 152 break; 153 case 'a': 154 all = 1; 155 break; 156 case 'F': 157 fake = 1; 158 break; 159 case 'f': 160 fflag = MNT_FORCE; 161 break; 162 case 'h': /* -h implies -A. */ 163 all = 2; 164 nfshost = optarg; 165 break; 166 case 't': 167 if (typelist != NULL) 168 errx(1, "only one -t option may be specified."); 169 typelist = makevfslist(optarg); 170 break; 171 case 'v': 172 vflag = 1; 173 break; 174 default: 175 usage(); 176 /* NOTREACHED */ 177 } 178 argc -= optind; 179 argv += optind; 180 181 if ((argc == 0 && !all) || (argc != 0 && all)) 182 usage(); 183 184 /* -h implies "-t nfs" if no -t flag. */ 185 if ((nfshost != NULL) && (typelist == NULL)) 186 typelist = makevfslist("nfs"); 187 188 if (fflag & MNT_FORCE) { 189 /* 190 * If we really mean business, we don't want to get hung up on 191 * any remote file systems. So, we set the "noremotehang" flag. 192 */ 193 pid_t pid; 194 pid = getpid(); 195 errs = sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &pid, sizeof(pid)); 196 if ((errs != 0) && vflag) 197 warn("sysctl vfs.generic.noremotehang"); 198 } 199 200 errs = EXIT_SUCCESS; 201 switch (all) { 202 case 2: 203 if ((mnts = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { 204 warn("getmntinfo"); 205 errs = 1; 206 break; 207 } 208 for (errs = 0, mnts--; mnts > 0; mnts--) { 209 if (checkvfsname(mntbuf[mnts].f_fstypename, typelist)) 210 continue; 211 if (umountfs(mntbuf[mnts].f_mntonname, typelist) != 0) 212 errs = 1; 213 } 214 break; 215 case 1: 216 if (setfsent() == 0) 217 err(1, "%s", _PATH_FSTAB); 218 errs = umountall(typelist); 219 break; 220 case 0: 221 for (errs = 0; *argv != NULL; ++argv) 222 if (umountfs(*argv, typelist) != 0) 223 errs = 1; 224 break; 225 } 226 exit(errs); 227} 228 229int 230umountall(char **typelist) 231{ 232 struct fstab *fs; 233 int rval; 234 char *cp; 235 236 while ((fs = getfsent()) != NULL) { 237 /* Ignore the root. */ 238 if (strcmp(fs->fs_file, "/") == 0) 239 continue; 240 /* 241 * !!! 242 * Historic practice: ignore unknown FSTAB_* fields. 243 */ 244 if (strcmp(fs->fs_type, FSTAB_RW) && 245 strcmp(fs->fs_type, FSTAB_RO) && 246 strcmp(fs->fs_type, FSTAB_RQ)) 247 continue; 248#if 0 249 /* If an unknown file system type, complain. */ 250 if (getvfsbyname(fs->fs_vfstype, &vfc) < 0) { 251 warnx("%s: unknown mount type `%s'", fs->fs_spec, fs->fs_vfstype); 252 continue; 253 } 254 if (checkvfsname(fs->fs_vfstype, typelist)) 255 continue; 256#endif 257 258 /* 259 * We want to unmount the file systems in the reverse order 260 * that they were mounted. So, we save off the file name 261 * in some allocated memory, and then call recursively. 262 */ 263 if ((cp = malloc((size_t)strlen(fs->fs_file) + 1)) == NULL) 264 err(1, NULL); 265 (void)strcpy(cp, fs->fs_file); 266 rval = umountall(typelist); 267 rval = umountfs(cp, typelist) || rval; 268 free(cp); 269 return (rval); 270 } 271 return (0); 272} 273 274int 275umountfs(char *name, char **typelist) 276{ 277 struct hostent *hp, *hp6; 278 struct stat sb; 279 int isftpfs, errnum; 280 char *type, *delimp, *hostname, *mntpt, rname[MAXPATHLEN], *tname; 281 char *pname = name; /* save the name parameter */ 282 283 /* 284 * First directly check the 285 * current mount list for a match. If we find it, 286 * we skip the realpath()/stat() below. 287 */ 288 tname = name; 289 /* check if name is a non-device "mount from" name */ 290 if ((mntpt = getmntname(tname, MNTON, &type)) == NULL) { 291 /* or if name is a mounted-on directory */ 292 mntpt = tname; 293 tname = getmntname(mntpt, MNTFROM, &type); 294 } 295 if (mntpt && tname) { 296 if (fflag & MNT_FORCE) { 297 /* 298 * The bulk of this block is to try to do a sync on the filesystem 299 * being unmounted. We want to do this in another thread, so we 300 * can avoid blocking for a hardware or network reason. We will 301 * wait 10 seconds for the sync to finish; after that, we just 302 * ignore it and go ahead with the unmounting. 303 * 304 * We only want to do this in the event of a forced unmount. 305 */ 306 int rv; 307 pthread_t tid; 308 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 309 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 310 struct syncarg args; 311 struct timespec timeout; 312 313 /* we found a match */ 314 name = tname; 315 316 args.mntname = mntpt; 317 args.wakeup_flag = 0; 318 args.wakeup_cond = &cond; 319 args.wakeup_lock = &lock; 320 321 timeout.tv_sec = time(NULL) + 10; /* Wait 10 seconds */ 322 timeout.tv_nsec = 0; 323 324 rv = pthread_create(&tid, NULL, &syncit, &args); 325 if (rv == 0 && pthread_mutex_lock(&lock) == 0) { 326 while (args.wakeup_flag == 0 && rv == 0) 327 rv = pthread_cond_timedwait(&cond, &lock, &timeout); 328 329 /* If this fails, not much we can do at this point... */ 330 (void)pthread_mutex_unlock(&lock); 331 if (rv != 0) { 332 errno = rv; 333 warn("pthread_cond_timeout failed; continuing with unmount"); 334 } 335 } 336 } 337 goto got_mount_point; 338 } 339 340 /* 341 * Note: in the face of path resolution errors (realpath/stat), 342 * we just try using the name passed in as is. 343 */ 344 /* even if path resolution succeeds, but can't find mountpoint 345 * with the resolved path, we still want to try using the name 346 * as passed in. 347 */ 348 349 if (realpath(name, rname) == NULL) { 350 if (vflag) 351 warn("realpath(%s)", rname); 352 } else { 353 name = rname; 354 } 355 356 /* we could just try MNTON and MNTFROM on name and again (if 357 * name is not the passed in param) MNTON and MNTFROM on 358 * pname. 359 * 360 * but we stat(name) here to avoid umounting the wrong thing 361 * if the mount table has an entry with the MNTFROM that is 362 * the same as the MNTON in another entry. 363 */ 364 365 if (stat(name, &sb) < 0) { 366 if (vflag) 367 warn("stat(%s)", name); 368 /* maybe name is a non-device "mount from" name? */ 369 if ((mntpt = getmntname(name, MNTON, &type))) 370 goto got_mount_point; 371 mntpt = name; 372 /* or name is a directory we simply can't reach? */ 373 if ((name = getmntname(mntpt, MNTFROM, &type))) 374 goto got_mount_point; 375 } else if (S_ISBLK(sb.st_mode)) { 376 if ((mntpt = getmntname(name, MNTON, &type))) 377 goto got_mount_point; 378 } else if (S_ISDIR(sb.st_mode)) { 379 mntpt = name; 380 if ((name = getmntname(mntpt, MNTFROM, &type))) 381 goto got_mount_point; 382 } else { 383 warnx("%s: not a directory or special device", name); 384 } 385 386 /* haven't found mountpoint. 387 * 388 * if we were not using the name as passed in, then try using it. 389 */ 390 if ((NULL == name) || (strcmp(name, pname) != 0)) { 391 name = pname; 392 393 if ((mntpt = getmntname(name, MNTON, &type))) 394 goto got_mount_point; 395 mntpt = name; 396 if ((name = getmntname(mntpt, MNTFROM, &type))) 397 goto got_mount_point; 398 } 399 400 warnx("%s: not currently mounted", pname); 401 return (1); 402 403got_mount_point: 404 405 if (checkvfsname(type, typelist)) 406 return (1); 407 408 if (!strncmp("ftp://", name, 6)) 409 isftpfs = 1; 410 else 411 isftpfs = 0; 412 413 hp = hp6 = NULL; 414 delimp = NULL; 415 if (!strcmp(type, "nfs") && !isftpfs) { 416 /* 417 * Parse the NFS host out of the name. 418 * 419 * If it starts with '[' then skip IPv6 literal characters 420 * until we find ']'. If we find other characters (or the 421 * closing ']' isn't followed by a ':', then don't consider 422 * it to be an IPv6 literal address. 423 * 424 * Scan the name string to find ":/" (or just ":"). The name 425 * is the portion of the string preceding the first ":/" (or ":"). 426 */ 427 char *p, *colon, *colonslash, c; 428 hostname = colon = colonslash = NULL; 429 p = name; 430 if (*p == '[') { /* Looks like an IPv6 literal address */ 431 p++; 432 while (isxdigit(*p) || (*p == ':')) { 433 if (*p == ':') { 434 if (!colon) 435 colon = p; 436 if (!colonslash && (*(p+1) == '/')) 437 colonslash = p; 438 } 439 p++; 440 } 441 if ((*p == ']') && (*(p+1) == ':')) { 442 /* Found "[IPv6]:", double check that it's acceptable and use it. */ 443 struct sockaddr_in6 sin6; 444 c = *p; 445 *p = '\0'; 446 if (inet_pton(AF_INET6, name+1, &sin6)) 447 hostname = name + 1; 448 *p = c; 449 } 450 } 451 /* if hostname not found yet, search for ":/" and ":" */ 452 while (!hostname && *p && (!colon || !colonslash)) { 453 if (*p == ':') { 454 if (!colon) 455 colon = p; 456 if (!colonslash && (*(p+1) == '/')) 457 colonslash = p; 458 } 459 p++; 460 } 461 if (!hostname && (colonslash || colon)) { 462 /* host name is the string preceding the colon(slash) */ 463 hostname = name; 464 if (colonslash) 465 p = colonslash; 466 else if (colon) 467 p = colon; 468 } 469 if (hostname) { 470 c = *p; 471 *p = '\0'; 472 /* we just want all the names/aliases */ 473 hp = getipnodebyname(hostname, AF_INET, 0, &errnum); 474 hp6 = getipnodebyname(hostname, AF_INET6, 0, &errnum); 475 *p = c; 476 } 477 } 478 479 if (hp || hp6) { 480 int match = (namematch(hp) || namematch(hp6)); 481 if (hp) 482 freehostent(hp); 483 if (hp6) 484 freehostent(hp6); 485 if (!match) 486 return (1); 487 } 488 489 if (vflag) 490 (void)printf("%s unmount from %s\n", name, mntpt); 491 if (fake) 492 return (0); 493 494 if (unmount(mntpt, fflag) < 0) { 495 /* 496 * If we're root and it looks like the error is that the 497 * mounted on directory is just not reachable or if we really 498 * want this filesystem unmounted (MNT_FORCE), then try doing 499 * the unmount by fsid. (Note: the sysctl only works for root) 500 */ 501 if ((getuid() == 0) && 502 ((errno == ESTALE) || (errno == ENOENT) || (fflag & MNT_FORCE))) { 503 if (vflag) 504 warn("unmount(%s)", mntpt); 505 if (unmount_by_fsid(mntpt, fflag) < 0) { 506 warn("unmount(%s)", mntpt); 507 return (1); 508 } 509 } else if (errno == EBUSY) { 510 fprintf(stderr, "umount(%s): %s -- try 'diskutil unmount'\n", mntpt, strerror(errno)); 511 return (1); 512 } else { 513 warn("unmount(%s)", mntpt); 514 return (1); 515 } 516 } 517 518 return (0); 519} 520 521static struct statfs *mntbuf; 522static int mntsize; 523 524char * 525getmntname(const char *name, mntwhat what, char **type) 526{ 527 int i; 528 529 if (mntbuf == NULL && 530 (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { 531 warn("getmntinfo"); 532 return (NULL); 533 } 534 for (i = mntsize-1; i >= 0; i--) { 535 if ((what == MNTON) && !strcmp(mntbuf[i].f_mntfromname, name)) { 536 if (type) 537 *type = mntbuf[i].f_fstypename; 538 return (mntbuf[i].f_mntonname); 539 } 540 if ((what == MNTFROM) && !strcmp(mntbuf[i].f_mntonname, name)) { 541 if (type) 542 *type = mntbuf[i].f_fstypename; 543 return (mntbuf[i].f_mntfromname); 544 } 545 } 546 return (NULL); 547} 548 549int 550getmntfsid(const char *name, fsid_t *fsid) 551{ 552 int i; 553 554 if (mntbuf == NULL && 555 (mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) { 556 warn("getmntinfo"); 557 return (-1); 558 } 559 for (i = mntsize-1; i >= 0; i--) { 560 if (!strcmp(mntbuf[i].f_mntonname, name)) { 561 *fsid = mntbuf[i].f_fsid; 562 return (0); 563 } 564 } 565 return (-1); 566} 567 568int 569namematch(struct hostent *hp) 570{ 571 char *cp, **np; 572 573 if (nfshost == NULL) 574 return (1); 575 576 if (hp == NULL) 577 return (0); 578 579 if (strcasecmp(nfshost, hp->h_name) == 0) 580 return (1); 581 582 if ((cp = strchr(hp->h_name, '.')) != NULL) { 583 *cp = '\0'; 584 if (strcasecmp(nfshost, hp->h_name) == 0) 585 return (1); 586 } 587 for (np = hp->h_aliases; *np; np++) { 588 if (strcasecmp(nfshost, *np) == 0) 589 return (1); 590 if ((cp = strchr(*np, '.')) != NULL) { 591 *cp = '\0'; 592 if (strcasecmp(nfshost, *np) == 0) 593 return (1); 594 } 595 } 596 return (0); 597} 598 599 600int 601sysctl_fsid( 602 int op, 603 fsid_t *fsid, 604 void *oldp, 605 size_t *oldlenp, 606 void *newp, 607 size_t newlen) 608{ 609 int ctlname[CTL_MAXNAME+2]; 610 size_t ctllen; 611 const char *sysstr = "vfs.generic.ctlbyfsid"; 612 struct vfsidctl vc; 613 614 ctllen = CTL_MAXNAME+2; 615 if (sysctlnametomib(sysstr, ctlname, &ctllen) == -1) { 616 warn("sysctlnametomib(%s)", sysstr); 617 return (-1); 618 }; 619 ctlname[ctllen] = op; 620 621 bzero(&vc, sizeof(vc)); 622 vc.vc_vers = VFS_CTL_VERS1; 623 vc.vc_fsid = *fsid; 624 vc.vc_ptr = newp; 625 vc.vc_len = newlen; 626 return (sysctl(ctlname, ctllen + 1, oldp, oldlenp, &vc, sizeof(vc))); 627} 628 629 630int 631unmount_by_fsid(const char *mntpt, int flag) 632{ 633 fsid_t fsid; 634 if (getmntfsid(mntpt, &fsid) < 0) 635 return (-1); 636 if (vflag) 637 printf("attempting to unmount %s by fsid\n", mntpt); 638 return sysctl_fsid(VFS_CTL_UMOUNT, &fsid, NULL, 0, &flag, sizeof(flag)); 639} 640 641void 642usage() 643{ 644 (void)fprintf(stderr, 645 "usage: %s\n %s\n", 646 "umount [-fv] [-t fstypelist] special | node", 647 "umount -a[fv] [-h host] [-t fstypelist]"); 648 exit(1); 649} 650