1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22/* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27/* 28 * Portions Copyright 2007-2012 Apple Inc. 29 */ 30 31#pragma ident "@(#)automount.c 1.50 05/06/08 SMI" 32 33#include <ctype.h> 34#include <stdio.h> 35#include <unistd.h> 36#include <fcntl.h> 37#include <stdlib.h> 38#include <locale.h> 39#include <stdarg.h> 40#include <errno.h> 41#include <string.h> 42#include <dirent.h> 43#include <signal.h> 44#include <fstab.h> 45#include <mntopts.h> 46#include <syslog.h> 47#include <sys/param.h> 48#include <sys/time.h> 49#include <sys/types.h> 50#include <sys/stat.h> 51#include <sys/ioctl.h> 52#include <sys/mount.h> 53#include <fts.h> 54 55#include <OpenDirectory/OpenDirectory.h> 56 57#include "deflt.h" 58#include "autofs.h" 59#include "automount.h" 60#include "automount_od.h" 61#include "umount_by_fsid.h" 62 63static int parse_mntopts(const char *, int *, int *); 64static int paths_match(struct autodir *, struct autodir *); 65static int mkdir_r(char *); 66static void rmdir_r(char *); 67static int have_ad(void); 68struct autodir *dir_head; 69struct autodir *dir_tail; 70static int num_current_mounts; 71static struct statfs *current_mounts; 72static void make_symlink(const char *, const char *); 73static struct statfs *find_mount(const char *); 74int verbose = 0; 75int trace = 0; 76 77static int autofs_control_fd; 78 79static void usage(void); 80static void do_unmounts(void); 81static int load_autofs(void); 82 83static int mount_timeout = AUTOFS_MOUNT_TIMEOUT; 84 85static char gKextLoadCommand[] = "/sbin/kextload"; 86static char gKextLoadPath[] = "/System/Library/Extensions/autofs.kext"; 87 88/* 89 * XXX 90 * The following are needed because they're used in auto_subr.c and 91 * we link with it. Should avoid this. 92 */ 93pthread_mutex_t cleanup_lock; 94pthread_cond_t cleanup_start_cv; 95pthread_cond_t cleanup_done_cv; 96 97int 98main(int argc, char *argv[]) 99{ 100 long timeout_val; 101 int c; 102 int flushcache = 0; 103 int unmount_automounted = 0; // Unmount automounted mounts 104 struct autodir *dir, *d; 105 char real_mntpnt[PATH_MAX]; 106 struct stat stbuf; 107 char *master_map = "auto_master"; 108 int null; 109 struct statfs *mntp; 110 int count = 0; 111 char *stack[STACKSIZ]; 112 char **stkptr; 113 char *defval; 114 int fd; 115 int flags, altflags; 116 struct staticmap *static_ent; 117 118 /* 119 * Read in the values from config file first before we check 120 * commandline options so the options override the file. 121 */ 122 if ((defopen(AUTOFSADMIN)) == 0) { 123 if ((defval = defread("AUTOMOUNT_TIMEOUT=")) != NULL) { 124 errno = 0; 125 timeout_val = strtol(defval, (char **)NULL, 10); 126 if (errno == 0 && timeout_val > 0 && 127 timeout_val <= INT_MAX) 128 mount_timeout = (int)timeout_val; 129 } 130 if ((defval = defread("AUTOMOUNT_VERBOSE=")) != NULL) { 131 if (strncasecmp("true", defval, 4) == 0) 132 verbose = TRUE; 133 else 134 verbose = FALSE; 135 } 136 if ((defval = defread("AUTOMOUNTD_TRACE=")) != NULL) { 137 /* 138 * Turn on tracing here too if the automountd 139 * is set up to do it - since automount calls 140 * many of the common library functions. 141 */ 142 errno = 0; 143 trace = (int)strtol(defval, (char **)NULL, 10); 144 if (errno != 0) 145 trace = 0; 146 } 147 148 /* close defaults file */ 149 defopen(NULL); 150 } 151 152 while ((c = getopt(argc, argv, "mM:D:f:t:vcu?")) != EOF) { 153 switch (c) { 154 case 'm': 155 pr_msg("Warning: -m option not supported"); 156 break; 157 case 'M': 158 pr_msg("Warning: -M option not supported"); 159 break; 160 case 'D': 161 pr_msg("Warning: -D option not supported"); 162 break; 163 case 'f': 164 pr_msg("Error: -f option no longer supported"); 165 usage(); 166 break; 167 case 't': 168 if (strchr(optarg, '=')) { 169 pr_msg("Error: invalid value for -t"); 170 usage(); 171 } 172 mount_timeout = atoi(optarg); 173 break; 174 case 'v': 175 verbose++; 176 break; 177 case 'c': 178 flushcache++; 179 break; 180 case 'u': 181 unmount_automounted++; 182 break; 183 default: 184 usage(); 185 break; 186 } 187 } 188 189 if (optind < argc) { 190 pr_msg("%s: command line mountpoints/maps " 191 "no longer supported", 192 argv[optind]); 193 usage(); 194 } 195 196 /* 197 * Get an array of current system mounts 198 */ 199 num_current_mounts = getmntinfo(¤t_mounts, MNT_NOWAIT); 200 if (num_current_mounts == 0) { 201 pr_msg("Couldn't get current mounts: %m"); 202 exit(1); 203 } 204 205 autofs_control_fd = open("/dev/" AUTOFS_CONTROL_DEVICE, O_RDONLY); 206 if (autofs_control_fd == -1 && errno == ENOENT) { 207 /* 208 * Oops, we probably don't have the autofs kext 209 * loaded. 210 */ 211 FTS *fts; 212 static char *const paths[] = { "/Network", NULL }; 213 FTSENT *ftsent; 214 int error; 215 216 /* 217 * This means there can't be any autofs mounts yet, so 218 * this is the first time we're being run since a reboot. 219 * Clean out any stuff left in /Network from the reboot. 220 */ 221 fts = fts_open(paths, FTS_NOCHDIR|FTS_PHYSICAL|FTS_XDEV, 222 NULL); 223 if (fts != NULL) { 224 while ((ftsent = fts_read(fts)) != NULL) { 225 /* 226 * We only remove directories - if 227 * there are files, we assume they're 228 * there for a purpose. 229 * 230 * We remove directories after we've 231 * removed their children, so we want 232 * to process directories visited in 233 * post-order. 234 * 235 * We don't remove /Network itself. 236 */ 237 if (ftsent->fts_info == FTS_DP && 238 ftsent->fts_level > FTS_ROOTLEVEL) 239 rmdir(ftsent->fts_accpath); 240 } 241 fts_close(fts); 242 } 243 244 /* 245 * Now load it. 246 */ 247 error = load_autofs(); 248 if (error != 0) { 249 pr_msg("can't load autofs kext"); 250 exit(1); 251 } 252 253 /* 254 * Try the open again. 255 */ 256 autofs_control_fd = open("/dev/" AUTOFS_CONTROL_DEVICE, 257 O_RDONLY); 258 } 259 if (autofs_control_fd == -1) { 260 if (errno == EBUSY) 261 pr_msg("Another automount is running"); 262 else 263 pr_msg("Couldn't open %s: %m", "/dev/" AUTOFS_CONTROL_DEVICE); 264 exit(1); 265 } 266 267 /* 268 * Update the mount timeout. 269 */ 270 if (ioctl(autofs_control_fd, AUTOFS_SET_MOUNT_TO, &mount_timeout) == -1) 271 pr_msg("AUTOFS_SET_MOUNT_TO failed: %m"); 272 273 /* 274 * Attempt to unmount any non-busy triggered mounts; this includes 275 * not only autofs mounts, but, for example SMB Dfs mounts. 276 * 277 * This is done before sleep, and after a network change, to 278 * try to get rid of as many network mounts as we can; each 279 * unmounted network mount is a network mount on which we 280 * can't hang. 281 */ 282 if (unmount_automounted) { 283 if (verbose) 284 pr_msg("Unmounting triggered mounts"); 285 if (ioctl(autofs_control_fd, AUTOFS_UNMOUNT_TRIGGERED, 0) == -1) 286 pr_msg("AUTOFS_UNMOUNT_TRIGGERED failed: %m"); 287 exit(0); 288 } 289 290 if (flushcache) { 291 /* 292 * Notify the automounter that it should flush its caches, 293 * as we might be on a different network with different maps. 294 */ 295 if (ioctl(autofs_control_fd, AUTOFS_NOTIFYCHANGE, 0) == -1) 296 pr_msg("AUTOFS_NOTIFYCHANGE failed: %m"); 297 } 298 299 (void) umask(0); 300 ns_setup(stack, &stkptr); 301 302 (void) loadmaster_map(master_map, "", stack, &stkptr); 303 304 /* 305 * Mount the daemon at its mount points. 306 */ 307 for (dir = dir_head; dir; dir = dir->dir_next) { 308 309 if (realpath(dir->dir_name, real_mntpnt) == NULL) { 310 /* 311 * We couldn't get the real path for this, 312 * perhaps because it doesn't exist. 313 * If it's not because it doesn't exist, just 314 * give up on this entry. Otherwise, just null 315 * out the real path - we'll try creating the 316 * directory later, and will set dir_realpath 317 * then, if that succeeds. 318 */ 319 if (errno != ENOENT) { 320 pr_msg("%s: Can't convert to real path: %m", 321 dir->dir_name); 322 continue; 323 } 324 dir->dir_realpath = NULL; 325 } else { 326 dir->dir_realpath = strdup(real_mntpnt); 327 if (dir->dir_realpath == NULL) { 328 pr_msg("Couldn't allocate real path: %m"); 329 exit(1); 330 } 331 } 332 333 /* 334 * Skip null entries 335 */ 336 if (strcmp(dir->dir_map, "-null") == 0) 337 continue; 338 339 /* 340 * Skip null'ed entries 341 */ 342 null = 0; 343 for (d = dir->dir_prev; d; d = d->dir_prev) { 344 if (paths_match(dir, d)) 345 null = 1; 346 } 347 if (null) 348 continue; 349 350 /* 351 * If this is -fstab, and there are no fstab "net" entries, 352 * skip this map if our directory search path doesn't 353 * include Active Directory. We don't want /Network/Servers 354 * (or wherever it shows up) to exist if this system isn't 355 * using AD (AD supplies fstab entries on the fly, so they 356 * might not exist right now) and we don't have any fstab 357 * entries. 358 */ 359 if (strcmp(dir->dir_map, "-fstab") == 0) { 360 if (!have_ad() && !havefstabkeys()) { 361 /* 362 * We're not using AD, and fstab is 363 * inaccessible or devoid of "net" entries. 364 */ 365 free(dir->dir_map); 366 dir->dir_map = strdup("-null"); 367 continue; 368 } 369 endfsent(); 370 } 371 372 /* 373 * If this is -fstab or -static, and there's another entry 374 * that's supposed to mount something on the same directory 375 * and isn't "-fstab" or "-static", ignore this; we might 376 * have a server that's supplying real automounter maps for 377 * the benefit of OS X systems with autofs and also supplying 378 * fstab entries for the benefit of older OS X systems, and 379 * we want to mount the real automounter map, not the -fstab 380 * or -static map, in that case. 381 */ 382 if (strcmp(dir->dir_map, "-fstab") == 0 || 383 strcmp(dir->dir_map, "-static") == 0) { 384 for (d = dir_head; d; d = d->dir_next) { 385 if (paths_match(dir, d) && 386 strcmp(d->dir_map, "-fstab") != 0 && 387 strcmp(d->dir_map, "-static") != 0) { 388 pr_msg("%s: ignoring redundant %s map", 389 dir->dir_name, dir->dir_map); 390 continue; 391 } 392 } 393 } 394 395 /* 396 * Parse the mount options and get additional flags to pass 397 * to mount() (standard mount options) and autofs mount 398 * options. 399 * 400 * XXX - we ignore flags on an update; if they're different 401 * from the current flags for that mount, we'd need to do a 402 * remount. 403 */ 404 if (!parse_mntopts(dir->dir_opts, &flags, &altflags)) { 405 /* 406 * Failed. 407 */ 408 continue; 409 } 410 411 /* 412 * If this is -static, check whether the entry refers 413 * to this host; if so, make the appropriate symlink 414 * exist at the "mount point" path. 415 */ 416 if (strcmp(dir->dir_map, "-static") == 0) { 417 static_ent = get_staticmap_entry(dir->dir_name); 418 if (static_ent == NULL) { 419 /* 420 * Whiskey tango foxtrot? There should 421 * be an entry here. Log an error and 422 * ignore this mount. 423 */ 424 pr_msg("can't find fstab entry for %s", 425 dir->dir_name); 426 continue; 427 } 428 if (host_is_us(static_ent->host, strlen(static_ent->host)) || 429 self_check(static_ent->host)) { 430 /* 431 * Yup, this is us. 432 * Try to make the appropriate symlink. 433 */ 434 make_symlink(static_ent->localpath, 435 dir->dir_name); 436 release_staticmap_entry(static_ent); 437 continue; 438 } 439 release_staticmap_entry(static_ent); 440 } 441 442 /* 443 * Check whether there's already an entry 444 * in the mnttab for this mountpoint. 445 */ 446 if (dir->dir_realpath != NULL && 447 (mntp = find_mount(dir->dir_realpath)) != NULL) { 448 struct autofs_update_args au; 449 450 /* 451 * If it's not an autofs mount - don't 452 * mount over it. 453 */ 454 if (strcmp(mntp->f_fstypename, MNTTYPE_AUTOFS) != 0) { 455 pr_msg("%s: already mounted on %s", 456 mntp->f_mntfromname, dir->dir_realpath); 457 continue; 458 } 459 460 /* 461 * This is already mounted, so just update it. 462 * We don't bother to check whether any options are 463 * changing, as we'd have to make a trip into the 464 * kernel to get the current options to check them, 465 * so we might as well just make a trip to do the 466 * update. 467 */ 468 au.fsid = mntp->f_fsid; 469 au.opts = dir->dir_opts; 470 au.map = dir->dir_map; 471 au.mntflags = altflags; 472 au.direct = dir->dir_direct; 473 au.node_type = dir->dir_direct ? NT_TRIGGER : 0; 474 475 if (ioctl(autofs_control_fd, AUTOFS_UPDATE_OPTIONS, 476 &au) < 0) { 477 pr_msg("update %s: %m", dir->dir_realpath); 478 continue; 479 } 480 if (verbose) 481 pr_msg("%s updated", dir->dir_realpath); 482 } else { 483 struct autofs_args ai; 484 int st_flags = 0; 485 486 /* 487 * This trigger isn't already mounted; either 488 * the path doesn't exist at all, or it 489 * exists but nothing is mounted on it. 490 * 491 * Create a mount point if necessary 492 * If the path refers to an existing symbolic 493 * link, refuse to mount on it. This avoids 494 * future problems. (We don't use dir->dir_realpath 495 * because that's never a symbolic link.) 496 */ 497 if (lstat(dir->dir_name, &stbuf) == 0) { 498 if ((stbuf.st_mode & S_IFMT) != S_IFDIR) { 499 pr_msg("%s: Not a directory", dir->dir_name); 500 continue; 501 } 502 st_flags = stbuf.st_flags; 503 504 /* 505 * Either realpath() succeeded or it 506 * failed with ENOENT; otherwise, we 507 * would have quit before getting here. 508 * 509 * If it failed, report an error, as 510 * the problem isn't that "dir->dir_name" 511 * doesn't exist, the problem is that, 512 * somehow, we got ENOENT even though 513 * it exists. 514 */ 515 if (dir->dir_realpath == NULL) { 516 errno = ENOENT; 517 pr_msg("%s: Can't convert to real path: %m", 518 dir->dir_name); 519 continue; 520 } 521 } else { 522 /* 523 * Mountpoint doesn't exist. 524 * 525 * Create it unless it's under /Volumes. 526 * At boot time it's possible the volume 527 * containing the mountpoint hasn't mounted yet. 528 */ 529 if (strncmp(dir->dir_name, "/Volumes/", 9) == 0) { 530 pr_msg("%s: mountpoint unavailable", dir->dir_name); 531 continue; 532 } 533 534 if (mkdir_r(dir->dir_name)) { 535 pr_msg("%s: %m", dir->dir_name); 536 continue; 537 } 538 539 /* 540 * realpath() presumably didn't succeed, 541 * as dir->dir_name couldn't be statted. 542 * Call it again, to get the real path 543 * corresponding to the newly-created 544 * mount point. 545 */ 546 if (realpath(dir->dir_name, real_mntpnt) == NULL) { 547 /* 548 * Failed. 549 */ 550 pr_msg("%s: Can't convert to real path: %m", 551 dir->dir_name); 552 continue; 553 } 554 dir->dir_realpath = strdup(real_mntpnt); 555 if (dir->dir_realpath == NULL) { 556 pr_msg("Couldn't allocate real path for %s: %m", 557 dir->dir_name); 558 continue; 559 } 560 } 561 562 /* 563 * If the "hidefromfinder" option is set for 564 * this autofs mountpoint then also set the 565 * UF_HIDDEN bit on the directory so it'll still 566 * be invisible to the Finder even if not mounted on. 567 */ 568 if (altflags & AUTOFS_MNT_HIDEFROMFINDER) 569 st_flags |= UF_HIDDEN; 570 else 571 st_flags &= ~UF_HIDDEN; 572 if (chflags(dir->dir_name, st_flags) < 0) 573 pr_msg("%s: can't set hidden", dir->dir_name); 574 575 /* 576 * Mount it. Use the real path (symlink-free), 577 * for reasons mentioned above. 578 */ 579 ai.version = AUTOFS_ARGSVERSION; 580 ai.path = dir->dir_realpath; 581 ai.opts = dir->dir_opts; 582 ai.map = dir->dir_map; 583 ai.subdir = ""; 584 ai.direct = dir->dir_direct; 585 if (dir->dir_direct) 586 ai.key = dir->dir_name; 587 else 588 ai.key = ""; 589 ai.mntflags = altflags; 590 ai.mount_type = MOUNT_TYPE_MAP; /* top-level autofs mount */ 591 ai.node_type = dir->dir_direct ? NT_TRIGGER : 0; 592 593 if (mount(MNTTYPE_AUTOFS, dir->dir_realpath, 594 MNT_DONTBROWSE | MNT_AUTOMOUNTED | flags, 595 &ai) < 0) { 596 pr_msg("mount %s: %m", dir->dir_realpath); 597 continue; 598 } 599 if (verbose) 600 pr_msg("%s mounted", dir->dir_realpath); 601 } 602 603 count++; 604 } 605 606 if (verbose && count == 0) 607 pr_msg("no mounts"); 608 609 /* 610 * Now compare the /etc/mnttab with the master 611 * map. Any autofs mounts in the /etc/mnttab 612 * that are not in the master map must be 613 * unmounted 614 * 615 * XXX - if there are no autofs mounts left, should we 616 * unload autofs, or arrange that it be unloaded? 617 */ 618 do_unmounts(); 619 620 /* 621 * Let PremountHomeDirectoryWithAuthentication() know that we're 622 * done. 623 */ 624 fd = open("/var/run/automount.initialized", O_CREAT|O_WRONLY, 0600); 625 close(fd); 626 627 return (0); 628} 629 630static void 631make_symlink(const char *target, const char *path) 632{ 633 struct stat stbuf; 634 char linktarget[PATH_MAX + 1]; 635 ssize_t pathlength; 636 struct statfs *mnt; 637 struct stat st; 638 639 /* 640 * Does the target exist? 641 */ 642 if (lstat(path, &stbuf) == 0) { 643 /* 644 * Yes. What is it? 645 */ 646 if ((stbuf.st_mode & S_IFMT) == S_IFLNK) { 647 /* 648 * It's a symlink. 649 * What does it point to? 650 */ 651 pathlength = readlink(path, linktarget, PATH_MAX); 652 if (pathlength == -1) { 653 /* 654 * FAIL. 655 */ 656 pr_msg("can't read target of %s: %m", path); 657 return; 658 } 659 linktarget[pathlength] = '\0'; 660 661 /* 662 * Does it point to the same place that we 663 * want it to point to? 664 * 665 * XXX - case-sensitivity? That's hard to 666 * handle, given that the path might cross 667 * multiple file systems with different case- 668 * sensitivities. 669 */ 670 if (strcmp(linktarget, target) == 0) { 671 /* 672 * Yes, it does. 673 * We don't need to do anything. 674 */ 675 if (verbose) 676 pr_msg("link %s unchanged", path); 677 return; 678 } 679 680 /* 681 * Get rid of the existing symlink. 682 */ 683 if (unlink(path) == -1) { 684 pr_msg("can't unlink %s: %m", path); 685 return; 686 } 687 } else if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { 688 /* 689 * It's a directory. 690 * Is there an autofs mount atop it? 691 */ 692 mnt = find_mount(path); 693 if (mnt != NULL) { 694 /* 695 * Something's mounted atop it; is it 696 * autofs? 697 */ 698 if (strcmp(mnt->f_fstypename, MNTTYPE_AUTOFS) == 0) { 699 /* 700 * Yes. Try to unmount it (and 701 * everything under it). 702 */ 703 if (ioctl(autofs_control_fd, 704 AUTOFS_UNMOUNT, &mnt->f_fsid) != 0) { 705 /* 706 * Failed. 707 * Leave it alone for now. 708 */ 709 return; 710 } 711 } 712 } 713 714 /* 715 * Now try to remove the directory. 716 */ 717 if (rmdir(path) != 0) { 718 /* 719 * Failed. Leave it alone. 720 */ 721 return; 722 } 723 } else { 724 /* 725 * Neither a symlink nor a directory. 726 * Leave it alone. 727 */ 728 return; 729 } 730 } else { 731 /* 732 * lstat() failed; is it because the target doesn't 733 * exist, or because we couldn't get its 734 * information? 735 */ 736 if (errno != ENOENT) { 737 /* 738 * We couldn't get its information. 739 * Leave it alone. 740 */ 741 return; 742 } 743 } 744 745 /* 746 * OK, the target should not exist. 747 * Make the symlink. 748 */ 749 if (symlink(target, path) == -1) { 750 pr_msg("can't create symlink from %s to %s: %m", 751 path, target); 752 } 753 754 /* 755 * Validate the symlink in case the path and target 756 * don't match but still yield an ELOOP. 757 */ 758 if (stat(path, &st) < 0) { 759 pr_msg("Invalid symbolic link: %s to %s: %m", path, target); 760 (void) unlink(path); 761 } 762} 763 764/* 765 * Find the first mount entry given the mountpoint path. 766 */ 767static struct statfs * 768find_mount(mntpnt) 769 const char *mntpnt; 770{ 771 int i; 772 struct statfs *mnt; 773 774 for (i = 0; i < num_current_mounts; i++) { 775 mnt = ¤t_mounts[i]; 776 if (strcmp(mntpnt, mnt->f_mntonname) == 0) 777 return (mnt); 778 } 779 780 return (NULL); 781} 782 783static void 784usage() 785{ 786 pr_msg("Usage: automount [ -vcu ] [ -t duration ]"); 787 exit(1); 788 /* NOTREACHED */ 789} 790 791/* 792 * Given a mount options string, get the flags argument to pass to mount() 793 * and the autofs mount options. 794 * 795 * We put "nobrowse" in front of MOPT_STDOPTS so that the mount 796 * options "browse" and "nobrowse" are interpreted as autofs 797 * mount options controlling whether you can get a directory listing, 798 * not OS X mount options controlling whether the mounts are treated as 799 * "real" volumes or not (we force MNT_NOBROWSE on for any mounts we do). 800 */ 801static const struct mntopt mopts_autofs[] = { 802 { "browse", 1, AUTOFS_MNT_NOBROWSE, 1 }, 803 MOPT_STDOPTS, 804 { MNTOPT_RESTRICT, 0, AUTOFS_MNT_RESTRICT, 1 }, 805 { MNTOPT_HIDEFROMFINDER, 0, AUTOFS_MNT_HIDEFROMFINDER, 1 }, 806 { NULL, 0, 0, 0 } 807}; 808 809static int 810parse_mntopts(const char *opts, int *flags, int *altflags) 811{ 812 mntoptparse_t mp; 813 814 /* 815 * Parse the mount options and fill in "flags" and "altflags". 816 */ 817 *flags = *altflags = 0; 818 getmnt_silent = 1; 819 mp = getmntopts(opts, mopts_autofs, flags, altflags); 820 if (mp == NULL) { 821 pr_msg("memory allocation failure"); 822 return (0); 823 } 824 freemntopts(mp); 825 826 return (1); 827} 828 829/* 830 * Unmount any autofs mounts that 831 * aren't in the master map 832 */ 833static void 834do_unmounts(void) 835{ 836 int i; 837 struct statfs *mnt; 838 struct autodir *dir; 839 int current; 840 int count = 0; 841 static const char triggered[] = "triggered"; 842 843 for (i = 0; i < num_current_mounts; i++) { 844 mnt = ¤t_mounts[i]; 845 if (strcmp(mnt->f_fstypename, MNTTYPE_AUTOFS) != 0) 846 continue; 847 /* 848 * Don't unmount autofs mounts done 849 * from the autofs mount command. 850 * How do we tell them apart ? 851 * Autofs mounts not eligible for auto-unmount 852 * have an f_mntfromname of "subtrigger" (those 853 * are subtriggers on top of a non-autofs mount) 854 * or an f_mntfromname beginning with "triggered" 855 * (those are autofs maps specified by map entries 856 * to be automounted). 857 * They will be unmounted, if possible, when the 858 * top-level autofs mount they're under is 859 * unmounted - as will any other mounts under 860 * that top-level autofs mount. 861 */ 862 if (strcmp(mnt->f_mntfromname, "subtrigger") == 0 || 863 strncmp(mnt->f_mntfromname, triggered, sizeof (triggered) - 1) == 0) 864 continue; 865 866 current = 0; 867 for (dir = dir_head; dir; dir = dir->dir_next) { 868 if (dir->dir_realpath != NULL && 869 strcmp(dir->dir_realpath, mnt->f_mntonname) == 0) { 870 current = strcmp(dir->dir_map, "-null"); 871 break; 872 } 873 } 874 if (current) 875 continue; 876 877 /* 878 * Mark this as being unmounted, and try to unmount it. 879 */ 880 if (ioctl(autofs_control_fd, AUTOFS_UNMOUNT, 881 &mnt->f_fsid) == 0) { 882 static const char slashnetwork[] = "/Network/"; 883 884 if (verbose) { 885 pr_msg("%s unmounted", 886 mnt->f_mntonname); 887 } 888 count++; 889 890 /* 891 * If the path to this was under /Network, 892 * try to remove the directory it was 893 * mounted on, to keep /Network clean. 894 * 895 * (The system "owns" /Network, so we can get rid 896 * of stuff as we choose. For other trigger mount 897 * points, we don't know whether we created the 898 * mount point, so we don't know whether we should 899 * remove it.) 900 */ 901 if (strncmp(mnt->f_mntonname, slashnetwork, 902 sizeof slashnetwork - 1) == 0) 903 rmdir_r(mnt->f_mntonname); 904 } 905 } 906 if (verbose && count == 0) 907 pr_msg("no unmounts"); 908} 909 910/* 911 * Check whether two entries refer to the same directory; we compare 912 * both the path name and the realpath()ed path name. 913 */ 914static int 915paths_match(struct autodir *d1, struct autodir *d2) 916{ 917 if (strcmp(d1->dir_name, d2->dir_name) == 0) 918 return (1); 919 if (d1->dir_realpath != NULL) { 920 if (strcmp(d1->dir_realpath, d2->dir_name) == 0) 921 return (1); 922 if (d2->dir_realpath != NULL) { 923 if (strcmp(d1->dir_realpath, d2->dir_realpath) == 0) 924 return (1); 925 } 926 } 927 if (d2->dir_realpath != NULL) { 928 if (strcmp(d1->dir_name, d2->dir_realpath) == 0) 929 return (1); 930 } 931 return (0); 932} 933 934static int 935mkdir_r(dir) 936 char *dir; 937{ 938 int err; 939 char *slash; 940 941 if (mkdir(dir, 0555) == 0) { 942 /* 943 * We created the directory. 944 */ 945 return (0); 946 } 947 if (errno == EEXIST) { 948 /* 949 * Something already existed there; we'll assume it's 950 * a directory. (If it's not, something will fail later.) 951 */ 952 return (0); 953 } 954 if (errno != ENOENT) { 955 /* 956 * We failed to create it for some reason other than 957 * the absence of a directory in the path leading up to 958 * it. Give up. 959 */ 960 return (-1); 961 } 962 963 /* 964 * Create the parent directory (creating any directories leading 965 * up to it). 966 */ 967 slash = strrchr(dir, '/'); 968 if (slash == NULL) 969 return (-1); 970 *slash = '\0'; 971 err = mkdir_r(dir); 972 *slash++ = '/'; 973 if (err || !*slash) 974 return (err); 975 return (mkdir(dir, 0555)); 976} 977 978/* 979 * Inverse of mkdir_r() - removes a directory, and then removes all 980 * parent directories that it can, except for the one right under the 981 * root directory, stopping when it can't remove one. 982 * Modifies the path argument as it goes. 983 */ 984void 985rmdir_r(char *path) 986{ 987 char *p; 988 989 for (;;) { 990 /* 991 * Look for the separator between us and the parent 992 * directory. 993 */ 994 p = strrchr(path, '/'); 995 if (p == NULL) { 996 /* 997 * Not found; this is not an absolute path, so 998 * just give up. 999 */ 1000 break; 1001 } 1002 if (p == path) { 1003 /* 1004 * Our parent is the root directory, so we 1005 * shouldn't be removed (we want /Network to 1006 * stick around). 1007 */ 1008 break; 1009 } 1010 if (rmdir(path) == -1) 1011 break; /* failed */ 1012 1013 /* 1014 * Cut off our name, leaving the name of the parent 1015 * directory. 1016 */ 1017 *p = '\0'; 1018 } 1019} 1020 1021/* 1022 * Check whether there are any Active Directory entries in the 1023 * Directory Services search path. 1024 */ 1025static int 1026have_ad(void) 1027{ 1028 int have_it = 0; 1029 CFErrorRef error; 1030 char *errstring; 1031 ODNodeRef node_ref; 1032 CFArrayRef paths; 1033 CFIndex num_paths; 1034 CFIndex i; 1035 CFStringRef path; 1036 1037 /* 1038 * Create the search node. 1039 */ 1040 error = NULL; 1041 node_ref = ODNodeCreateWithNodeType(kCFAllocatorDefault, kODSessionDefault, 1042 kODNodeTypeAuthentication, &error); 1043 if (node_ref == NULL) { 1044 errstring = od_get_error_string(error); 1045 pr_msg("have_ad: can't create search node for /Search: %s", 1046 errstring); 1047 free(errstring); 1048 return (0); 1049 } 1050 1051 /* 1052 * Get the search paths from the node. 1053 */ 1054 paths = ODNodeCopySubnodeNames(node_ref, &error); 1055 if (paths == NULL) { 1056 errstring = od_get_error_string(error); 1057 pr_msg("have_ad: can't get subnode names for /Search: %s", 1058 errstring); 1059 free(errstring); 1060 return (0); 1061 } 1062 1063 /* 1064 * Scan the paths in that array looking for an Active 1065 * Directory search path entry, i.e. one beginning with 1066 * "/Active Directory". 1067 */ 1068 num_paths = CFArrayGetCount(paths); 1069 for (i = 0; i < num_paths && !have_it; i++) { 1070 path = CFArrayGetValueAtIndex(paths, i); 1071 1072 /* 1073 * Check whether this entry begins with "/Active Directory". 1074 */ 1075 have_it = CFStringHasPrefix(path, CFSTR("/Active Directory")); 1076 } 1077 CFRelease(paths); 1078 CFRelease(node_ref); 1079 return (have_it); 1080} 1081 1082/* 1083 * Print an error. 1084 * It works like printf 1085 * (fmt string and variable args) except that it will prepend "automount:" 1086 * and substitute an error message for a "%m" string (like syslog). 1087 */ 1088/* VARARGS1 */ 1089void 1090pr_msg(const char *fmt, ...) 1091{ 1092 va_list ap; 1093 char buf[BUFSIZ], *p2; 1094 const char *p1; 1095 1096 if (!isatty(fileno(stderr))) { 1097 va_start(ap, fmt); 1098 (void) vsyslog(LOG_ERR, fmt, ap); 1099 va_end(ap); 1100 return; 1101 } 1102 1103 (void) strlcpy(buf, "automount: ", sizeof buf); 1104 p2 = buf + strlen(buf); 1105 1106 for (p1 = fmt; *p1; p1++) { 1107 if (*p1 == '%' && *(p1+1) == 'm') { 1108 (void) strlcpy(p2, strerror(errno), 1109 sizeof buf - (p2 - buf)); 1110 p2 += strlen(p2); 1111 p1++; 1112 } else { 1113 *p2++ = *p1; 1114 } 1115 } 1116 if (p2 > buf && *(p2-1) != '\n') 1117 *p2++ = '\n'; 1118 *p2 = '\0'; 1119 1120 1121 va_start(ap, fmt); 1122 (void) vfprintf(stderr, buf, ap); 1123 va_end(ap); 1124} 1125 1126static int 1127load_autofs(void) 1128{ 1129 pid_t pid, terminated_pid; 1130 int result; 1131 union wait status; 1132 1133 pid = fork(); 1134 if (pid == 0) { 1135 result = execl(gKextLoadCommand, gKextLoadCommand, "-q", 1136 gKextLoadPath, NULL); 1137 /* IF WE ARE HERE, WE WERE UNSUCCESSFUL */ 1138 return (result ? result : ECHILD); 1139 } 1140 1141 if (pid == -1) 1142 return (-1); 1143 1144 /* Success! Wait for completion in-line here */ 1145 while ((terminated_pid = wait4(pid, (int *)&status, 0, NULL)) < 0) { 1146 /* retry if EINTR, else break out with error */ 1147 if (errno != EINTR) 1148 break; 1149 } 1150 1151 if (terminated_pid == pid && WIFEXITED(status)) 1152 result = WEXITSTATUS(status); 1153 else 1154 result = -1; 1155 1156 return (result); 1157} 1158