1 /* $NetBSD: autil.c,v 1.1.1.1 2008/09/19 20:07:15 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2007 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgment: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * 42 * File: am-utils/amd/autil.c 43 * 44 */ 45 46 /* 47 * utilities specified to amd, taken out of the older amd/util.c. 48 */ 49 50 #ifdef HAVE_CONFIG_H 51 # include <config.h> 52 #endif /* HAVE_CONFIG_H */ 53 #include <am_defs.h> 54 #include <amd.h> 55 56 int NumChildren = 0; /* number of children of primary amd */ 57 static char invalid_keys[] = "\"'!;@ \t\n"; 58 59 /**************************************************************************** 60 *** MACROS *** 61 ****************************************************************************/ 62 63 #ifdef HAVE_TRANSPORT_TYPE_TLI 64 # define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */ 65 #endif /* HAVE_TRANSPORT_TYPE_TLI */ 66 67 68 /**************************************************************************** 69 *** FORWARD DEFINITIONS *** 70 ****************************************************************************/ 71 static void domain_strip(char *otherdom, char *localdom); 72 static int dofork(void); 73 74 75 /**************************************************************************** 76 *** FUNCTIONS *** 77 ****************************************************************************/ 78 79 /* 80 * Copy s into p, reallocating p if necessary 81 */ 82 char * 83 strealloc(char *p, char *s) 84 { 85 size_t len = strlen(s) + 1; 86 87 p = (char *) xrealloc((voidp) p, len); 88 89 xstrlcpy(p, s, len); 90 #ifdef DEBUG_MEM 91 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) 92 malloc_verify(); 93 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ 94 #endif /* DEBUG_MEM */ 95 return p; 96 } 97 98 99 /* 100 * Strip off the trailing part of a domain 101 * to produce a short-form domain relative 102 * to the local host domain. 103 * Note that this has no effect if the domain 104 * names do not have the same number of 105 * components. If that restriction proves 106 * to be a problem then the loop needs recoding 107 * to skip from right to left and do partial 108 * matches along the way -- ie more expensive. 109 */ 110 static void 111 domain_strip(char *otherdom, char *localdom) 112 { 113 char *p1, *p2; 114 115 if ((p1 = strchr(otherdom, '.')) && 116 (p2 = strchr(localdom, '.')) && 117 STREQ(p1 + 1, p2 + 1)) 118 *p1 = '\0'; 119 } 120 121 122 /* 123 * Normalize a host name: replace cnames with real names, and decide if to 124 * strip domain name or not. 125 */ 126 void 127 host_normalize(char **chp) 128 { 129 /* 130 * Normalize hosts is used to resolve host name aliases 131 * and replace them with the standard-form name. 132 * Invoked with "-n" command line option. 133 */ 134 if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) { 135 struct hostent *hp; 136 hp = gethostbyname(*chp); 137 if (hp && hp->h_addrtype == AF_INET) { 138 dlog("Hostname %s normalized to %s", *chp, hp->h_name); 139 *chp = strealloc(*chp, (char *) hp->h_name); 140 } 141 } 142 if (gopt.flags & CFM_DOMAIN_STRIP) { 143 domain_strip(*chp, hostd); 144 } 145 } 146 147 148 /* 149 * Keys are not allowed to contain " ' ! or ; to avoid 150 * problems with macro expansions. 151 */ 152 int 153 valid_key(char *key) 154 { 155 while (*key) 156 if (strchr(invalid_keys, *key++)) 157 return FALSE; 158 return TRUE; 159 } 160 161 162 void 163 forcibly_timeout_mp(am_node *mp) 164 { 165 mntfs *mf = mp->am_mnt; 166 /* 167 * Arrange to timeout this node 168 */ 169 if (mf && ((mp->am_flags & AMF_ROOT) || 170 (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) { 171 if (mf->mf_flags & MFF_UNMOUNTING) 172 plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path); 173 else 174 plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path); 175 } else { 176 plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path); 177 mp->am_flags &= ~AMF_NOTIMEOUT; 178 mp->am_ttl = clocktime(NULL); 179 /* 180 * Force mtime update of parent dir, to prevent DNLC/dcache from caching 181 * the old entry, which could result in ESTALE errors, bad symlinks, and 182 * more. 183 */ 184 clocktime(&mp->am_parent->am_fattr.na_mtime); 185 reschedule_timeout_mp(); 186 } 187 } 188 189 190 void 191 mf_mounted(mntfs *mf, bool_t call_free_opts) 192 { 193 int quoted; 194 int wasmounted = mf->mf_flags & MFF_MOUNTED; 195 196 if (!wasmounted) { 197 /* 198 * If this is a freshly mounted 199 * filesystem then update the 200 * mntfs structure... 201 */ 202 mf->mf_flags |= MFF_MOUNTED; 203 mf->mf_error = 0; 204 205 /* 206 * Do mounted callback 207 */ 208 if (mf->mf_ops->mounted) 209 mf->mf_ops->mounted(mf); 210 211 /* 212 * Be careful when calling free_ops and XFREE here. Some pseudo file 213 * systems like nfsx call this function (mf_mounted), even though it 214 * would be called by the lower-level amd file system functions. nfsx 215 * needs to call this function because of the other actions it takes. 216 * So we pass a boolean from the caller (yes, not so clean workaround) 217 * to determine if we should free or not. If we're not freeing (often 218 * because we're called from a callback function), then just to be sure, 219 * we'll zero out the am_opts structure and set the pointer to NULL. 220 * The parent mntfs node owns this memory and is going to free it with a 221 * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code). 222 */ 223 if (call_free_opts) { 224 free_opts(mf->mf_fo); /* this free is needed to prevent leaks */ 225 XFREE(mf->mf_fo); /* (also this one) */ 226 } else { 227 memset(mf->mf_fo, 0, sizeof(am_opts)); 228 mf->mf_fo = NULL; 229 } 230 } 231 232 if (mf->mf_flags & MFF_RESTART) { 233 mf->mf_flags &= ~MFF_RESTART; 234 dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags); 235 } 236 237 /* 238 * Log message 239 */ 240 quoted = strchr(mf->mf_info, ' ') != 0; 241 plog(XLOG_INFO, "%s%s%s %s fstype %s on %s", 242 quoted ? "\"" : "", 243 mf->mf_info, 244 quoted ? "\"" : "", 245 wasmounted ? "referenced" : "mounted", 246 mf->mf_ops->fs_type, mf->mf_mount); 247 } 248 249 250 void 251 am_mounted(am_node *mp) 252 { 253 int notimeout = 0; /* assume normal timeouts initially */ 254 mntfs *mf = mp->am_mnt; 255 256 /* 257 * This is the parent mntfs which does the mf->mf_fo (am_opts type), and 258 * we're passing TRUE here to tell mf_mounted to actually free the 259 * am_opts. See a related comment in mf_mounted(). 260 */ 261 mf_mounted(mf, TRUE); 262 263 #ifdef HAVE_FS_AUTOFS 264 if (mf->mf_flags & MFF_IS_AUTOFS) 265 autofs_mounted(mp); 266 #endif /* HAVE_FS_AUTOFS */ 267 268 /* 269 * Patch up path for direct mounts 270 */ 271 if (mp->am_parent && mp->am_parent->am_mnt->mf_fsflags & FS_DIRECT) 272 mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); 273 274 /* 275 * Check whether this mount should be cached permanently or not, 276 * and handle user-requested timeouts. 277 */ 278 /* first check if file system was set to never timeout */ 279 if (mf->mf_fsflags & FS_NOTIMEOUT) 280 notimeout = 1; 281 /* next, alter that decision by map flags */ 282 if (mf->mf_mopts) { 283 mntent_t mnt; 284 mnt.mnt_opts = mf->mf_mopts; 285 286 /* umount option: user wants to unmount this entry */ 287 if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount")) 288 notimeout = 0; 289 /* noumount option: user does NOT want to unmount this entry */ 290 if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount")) 291 notimeout = 1; 292 /* utimeout=N option: user wants to unmount this option AND set timeout */ 293 if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) 294 mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */ 295 else 296 notimeout = 0; 297 /* special case: don't try to unmount "/" (it can never succeed) */ 298 if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0') 299 notimeout = 1; 300 } 301 /* finally set actual flags */ 302 if (notimeout) { 303 mp->am_flags |= AMF_NOTIMEOUT; 304 plog(XLOG_INFO, "%s set to never timeout", mp->am_path); 305 } else { 306 mp->am_flags &= ~AMF_NOTIMEOUT; 307 plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo); 308 } 309 310 /* 311 * If this node is a symlink then 312 * compute the length of the returned string. 313 */ 314 if (mp->am_fattr.na_type == NFLNK) 315 mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount); 316 317 /* 318 * Record mount time, and update am_stats at the same time. 319 */ 320 mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime); 321 new_ttl(mp); 322 323 /* 324 * Update mtime of parent node (copying "struct nfstime" in '=' below) 325 */ 326 if (mp->am_parent && mp->am_parent->am_mnt) 327 mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime; 328 329 /* 330 * This is ugly, but essentially unavoidable 331 * Sublinks must be treated separately as type==link 332 * when the base type is different. 333 */ 334 if (mp->am_link && mf->mf_ops != &amfs_link_ops) 335 amfs_link_ops.mount_fs(mp, mf); 336 337 /* 338 * Now, if we can, do a reply to our client here 339 * to speed things up. 340 */ 341 #ifdef HAVE_FS_AUTOFS 342 if (mp->am_flags & AMF_AUTOFS) 343 autofs_mount_succeeded(mp); 344 else 345 #endif /* HAVE_FS_AUTOFS */ 346 nfs_quick_reply(mp, 0); 347 348 /* 349 * Update stats 350 */ 351 amd_stats.d_mok++; 352 } 353 354 355 /* 356 * Replace mount point with a reference to an error filesystem. 357 * The mount point (struct mntfs) is NOT discarded, 358 * the caller must do it if it wants to _before_ calling this function. 359 */ 360 void 361 assign_error_mntfs(am_node *mp) 362 { 363 int error; 364 dlog("assign_error_mntfs"); 365 /* 366 * Save the old error code 367 */ 368 error = mp->am_error; 369 if (error <= 0) 370 error = mp->am_mnt->mf_error; 371 /* 372 * Allocate a new error reference 373 */ 374 mp->am_mnt = new_mntfs(); 375 /* 376 * Put back the error code 377 */ 378 mp->am_mnt->mf_error = error; 379 mp->am_mnt->mf_flags |= MFF_ERROR; 380 /* 381 * Zero the error in the mount point 382 */ 383 mp->am_error = 0; 384 } 385 386 387 /* 388 * Build a new map cache for this node, or re-use 389 * an existing cache for the same map. 390 */ 391 void 392 amfs_mkcacheref(mntfs *mf) 393 { 394 char *cache; 395 396 if (mf->mf_fo && mf->mf_fo->opt_cache) 397 cache = mf->mf_fo->opt_cache; 398 else 399 cache = "none"; 400 mf->mf_private = (opaque_t) mapc_find(mf->mf_info, 401 cache, 402 (mf->mf_fo ? mf->mf_fo->opt_maptype : NULL), 403 mf->mf_mount); 404 mf->mf_prfree = mapc_free; 405 } 406 407 408 /* 409 * Locate next node in sibling list which is mounted 410 * and is not an error node. 411 */ 412 am_node * 413 next_nonerror_node(am_node *xp) 414 { 415 mntfs *mf; 416 417 /* 418 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> 419 * Fixes a race condition when mounting direct automounts. 420 * Also fixes a problem when doing a readdir on a directory 421 * containing hung automounts. 422 */ 423 while (xp && 424 (!(mf = xp->am_mnt) || /* No mounted filesystem */ 425 mf->mf_error != 0 || /* There was a mntfs error */ 426 xp->am_error != 0 || /* There was a mount error */ 427 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ 428 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ 429 ) 430 xp = xp->am_osib; 431 432 return xp; 433 } 434 435 436 /* 437 * Mount an automounter directory. 438 * The automounter is connected into the system 439 * as a user-level NFS server. amfs_mount constructs 440 * the necessary NFS parameters to be given to the 441 * kernel so that it will talk back to us. 442 * 443 * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP). 444 * 445 * NEW: on certain systems, mounting can be done using the 446 * kernel-level automount (autofs) support. In that case, 447 * we don't need NFS at all here. 448 */ 449 int 450 amfs_mount(am_node *mp, mntfs *mf, char *opts) 451 { 452 char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1]; 453 int retry, error = 0, genflags; 454 int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; 455 char *dir = mf->mf_mount; 456 mntent_t mnt; 457 MTYPE_TYPE type; 458 int forced_unmount = 0; /* are we using forced unmounts? */ 459 460 memset((voidp) &mnt, 0, sizeof(mnt)); 461 mnt.mnt_dir = dir; 462 mnt.mnt_fsname = pid_fsname; 463 mnt.mnt_opts = opts; 464 465 #ifdef HAVE_FS_AUTOFS 466 if (mf->mf_flags & MFF_IS_AUTOFS) { 467 type = MOUNT_TYPE_AUTOFS; 468 /* 469 * Make sure that amd's top-level autofs mounts are hidden by default 470 * from df. 471 * XXX: It works ok on Linux, might not work on other systems. 472 */ 473 mnt.mnt_type = "autofs"; 474 } else 475 #endif /* HAVE_FS_AUTOFS */ 476 { 477 type = MOUNT_TYPE_NFS; 478 /* 479 * Make sure that amd's top-level NFS mounts are hidden by default 480 * from df. 481 * If they don't appear to support the either the "ignore" mnttab 482 * option entry, or the "auto" one, set the mount type to "nfs". 483 */ 484 mnt.mnt_type = HIDE_MOUNT_TYPE; 485 } 486 487 retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); 488 if (retry <= 0) 489 retry = 2; /* XXX: default to 2 retries */ 490 491 /* 492 * SET MOUNT ARGS 493 */ 494 495 /* 496 * Make a ``hostname'' string for the kernel 497 */ 498 xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s", 499 get_server_pid(), am_get_hostname(), dir); 500 /* 501 * Most kernels have a name length restriction (64 bytes)... 502 */ 503 if (strlen(fs_hostname) >= MAXHOSTNAMELEN) 504 xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..", 505 sizeof(fs_hostname) - MAXHOSTNAMELEN + 3); 506 #ifdef HOSTNAMESZ 507 /* 508 * ... and some of these restrictions are 32 bytes (HOSTNAMESZ) 509 * If you need to get the definition for HOSTNAMESZ found, you may 510 * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file. 511 */ 512 if (strlen(fs_hostname) >= HOSTNAMESZ) 513 xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..", 514 sizeof(fs_hostname) - HOSTNAMESZ + 3); 515 #endif /* HOSTNAMESZ */ 516 517 /* 518 * Finally we can compute the mount genflags set above, 519 * and add any automounter specific flags. 520 */ 521 genflags = compute_mount_flags(&mnt); 522 #ifdef HAVE_FS_AUTOFS 523 if (on_autofs) 524 genflags |= autofs_compute_mount_flags(&mnt); 525 #endif /* HAVE_FS_AUTOFS */ 526 genflags |= compute_automounter_mount_flags(&mnt); 527 528 again: 529 if (!(mf->mf_flags & MFF_IS_AUTOFS)) { 530 nfs_args_t nfs_args; 531 am_nfs_fh *fhp; 532 am_nfs_handle_t anh; 533 #ifndef HAVE_TRANSPORT_TYPE_TLI 534 u_short port; 535 struct sockaddr_in sin; 536 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 537 538 /* 539 * get fhandle of remote path for automount point 540 */ 541 fhp = get_root_nfs_fh(dir); 542 if (!fhp) { 543 plog(XLOG_FATAL, "Can't find root file handle for %s", dir); 544 return EINVAL; 545 } 546 547 #ifndef HAVE_TRANSPORT_TYPE_TLI 548 /* 549 * Create sockaddr to point to the local machine. 550 */ 551 memset((voidp) &sin, 0, sizeof(sin)); 552 /* as per POSIX, sin_len need not be set (used internally by kernel) */ 553 sin.sin_family = AF_INET; 554 sin.sin_addr = myipaddr; 555 port = hasmntval(&mnt, MNTTAB_OPT_PORT); 556 if (port) { 557 sin.sin_port = htons(port); 558 } else { 559 plog(XLOG_ERROR, "no port number specified for %s", dir); 560 return EINVAL; 561 } 562 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 563 564 /* setup the many fields and flags within nfs_args */ 565 memmove(&anh.v2, fhp, sizeof(*fhp)); 566 #ifdef HAVE_TRANSPORT_TYPE_TLI 567 compute_nfs_args(&nfs_args, 568 &mnt, 569 genflags, 570 nfsncp, 571 NULL, /* remote host IP addr is set below */ 572 NFS_VERSION, /* version 2 */ 573 "udp", 574 &anh, 575 fs_hostname, 576 pid_fsname); 577 /* 578 * IMPORTANT: set the correct IP address AFTERWARDS. It cannot 579 * be done using the normal mechanism of compute_nfs_args(), because 580 * that one will allocate a new address and use NFS_SA_DREF() to copy 581 * parts to it, while assuming that the ip_addr passed is always 582 * a "struct sockaddr_in". That assumption is incorrect on TLI systems, 583 * because they define a special macro HOST_SELF which is DIFFERENT 584 * than localhost (127.0.0.1)! 585 */ 586 nfs_args.addr = &nfsxprt->xp_ltaddr; 587 #else /* not HAVE_TRANSPORT_TYPE_TLI */ 588 compute_nfs_args(&nfs_args, 589 &mnt, 590 genflags, 591 NULL, 592 &sin, 593 NFS_VERSION, /* version 2 */ 594 "udp", 595 &anh, 596 fs_hostname, 597 pid_fsname); 598 #endif /* not HAVE_TRANSPORT_TYPE_TLI */ 599 600 /************************************************************************* 601 * NOTE: while compute_nfs_args() works ok for regular NFS mounts * 602 * the toplvl one is not quite regular, and so some options must be * 603 * corrected by hand more carefully, *after* compute_nfs_args() runs. * 604 *************************************************************************/ 605 compute_automounter_nfs_args(&nfs_args, &mnt); 606 607 if (amuDebug(D_TRACE)) { 608 print_nfs_args(&nfs_args, 0); 609 plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags); 610 } 611 612 /* This is it! Here we try to mount amd on its mount points */ 613 error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, 614 retry, type, 0, NULL, mnttab_file_name, on_autofs); 615 616 #ifdef HAVE_TRANSPORT_TYPE_TLI 617 free_knetconfig(nfs_args.knconf); 618 /* 619 * local automounter mounts do not allocate a special address, so 620 * no need to XFREE(nfs_args.addr) under TLI. 621 */ 622 #endif /* HAVE_TRANSPORT_TYPE_TLI */ 623 624 #ifdef HAVE_FS_AUTOFS 625 } else { 626 /* This is it! Here we try to mount amd on its mount points */ 627 error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh, 628 retry, type, 0, NULL, mnttab_file_name, on_autofs); 629 #endif /* HAVE_FS_AUTOFS */ 630 } 631 if (error == 0 || forced_unmount) 632 return error; 633 634 /* 635 * If user wants forced/lazy unmount semantics, then try it iff the 636 * current mount failed with EIO or ESTALE. 637 */ 638 if (gopt.flags & CFM_FORCED_UNMOUNTS) { 639 switch (errno) { 640 case ESTALE: 641 case EIO: 642 forced_unmount = errno; 643 plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path); 644 if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name, 645 AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) { 646 plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path); 647 errno = forced_unmount; 648 } else 649 goto again; 650 default: 651 break; 652 } 653 } 654 655 return error; 656 } 657 658 659 void 660 am_unmounted(am_node *mp) 661 { 662 mntfs *mf = mp->am_mnt; 663 664 if (!foreground) /* firewall - should never happen */ 665 return; 666 667 /* 668 * Do unmounted callback 669 */ 670 if (mf->mf_ops->umounted) 671 mf->mf_ops->umounted(mf); 672 673 /* 674 * This is ugly, but essentially unavoidable. 675 * Sublinks must be treated separately as type==link 676 * when the base type is different. 677 */ 678 if (mp->am_link && mf->mf_ops != &amfs_link_ops) 679 amfs_link_ops.umount_fs(mp, mf); 680 681 #ifdef HAVE_FS_AUTOFS 682 if (mf->mf_flags & MFF_IS_AUTOFS) 683 autofs_release_fh(mp); 684 if (mp->am_flags & AMF_AUTOFS) 685 autofs_umount_succeeded(mp); 686 #endif /* HAVE_FS_AUTOFS */ 687 688 /* 689 * Clean up any directories that were made 690 * 691 * If we remove the mount point of a pending mount, any queued access 692 * to it will fail. So don't do it in that case. 693 * Also don't do it if the refcount is > 1. 694 */ 695 if (mf->mf_flags & MFF_MKMNT && 696 mf->mf_refc == 1 && 697 !(mp->am_flags & AMF_REMOUNT)) { 698 plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount); 699 rmdirs(mf->mf_mount); 700 mf->mf_flags &= ~MFF_MKMNT; 701 } 702 703 /* 704 * If this is a pseudo-directory then adjust the link count 705 * in the parent 706 */ 707 if (mp->am_parent && mp->am_fattr.na_type == NFDIR) 708 --mp->am_parent->am_fattr.na_nlink; 709 710 /* 711 * Update mtime of parent node 712 */ 713 if (mp->am_parent && mp->am_parent->am_mnt) 714 clocktime(&mp->am_parent->am_fattr.na_mtime); 715 716 if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) { 717 char *fname = strdup(mp->am_name); 718 am_node *mp_parent = mp->am_parent; 719 mntfs *mf_parent = mp_parent->am_mnt; 720 int error = 0; 721 722 free_map(mp); 723 plog(XLOG_INFO, "am_unmounted: remounting %s", fname); 724 mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE); 725 if (mp && error < 0) 726 mp = mf_parent->mf_ops->mount_child(mp, &error); 727 if (error > 0) { 728 errno = error; 729 plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname); 730 } 731 XFREE(fname); 732 } else 733 /* 734 * We have a race here. 735 * If this node has a pending mount and amd is going down (unmounting 736 * everything in the process), then we could potentially free it here 737 * while a struct continuation still has a reference to it. So when 738 * amfs_cont is called, it blows up. 739 * We avoid the race by refusing to free any nodes that have 740 * pending mounts (defined as having a non-NULL am_mfarray). 741 */ 742 if (!mp->am_mfarray) 743 free_map(mp); 744 } 745 746 747 /* 748 * Fork the automounter 749 * 750 * TODO: Need a better strategy for handling errors 751 */ 752 static int 753 dofork(void) 754 { 755 int pid; 756 757 top: 758 pid = fork(); 759 760 if (pid < 0) { /* fork error, retry in 1 second */ 761 sleep(1); 762 goto top; 763 } 764 if (pid == 0) { /* child process (foreground==false) */ 765 am_set_mypid(); 766 foreground = 0; 767 } else { /* parent process, has one more child */ 768 NumChildren++; 769 } 770 771 return pid; 772 } 773 774 775 int 776 background(void) 777 { 778 int pid = dofork(); 779 780 if (pid == 0) { 781 dlog("backgrounded"); 782 foreground = 0; 783 } else 784 dlog("forked process %d", pid); 785 return pid; 786 } 787