1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 1997-2009 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/map.c 43 * 44 */ 45 46#ifdef HAVE_CONFIG_H 47# include <config.h> 48#endif /* HAVE_CONFIG_H */ 49#include <am_defs.h> 50#include <amd.h> 51 52#define smallest_t(t1, t2) (t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2) 53#define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART) 54#define new_gen() (am_gen++) 55 56/* 57 * Generation Numbers. 58 * 59 * Generation numbers are allocated to every node created 60 * by amd. When a filehandle is computed and sent to the 61 * kernel, the generation number makes sure that it is safe 62 * to reallocate a node slot even when the kernel has a cached 63 * reference to its old incarnation. 64 * No garbage collection is done, since it is assumed that 65 * there is no way that 2^32 generation numbers could ever 66 * be allocated by a single run of amd - there is simply 67 * not enough cpu time available. 68 * Famous last words... -Ion 69 */ 70static u_int am_gen = 2; /* Initial generation number */ 71static int timeout_mp_id; /* Id from last call to timeout */ 72 73static am_node *root_node; /* The root of the mount tree */ 74static am_node **exported_ap = (am_node **) NULL; 75static int exported_ap_size = 0; 76static int first_free_map = 0; /* First available free slot */ 77static int last_used_map = -1; /* Last unavailable used slot */ 78 79 80/* 81 * This is the default attributes field which 82 * is copied into every new node to be created. 83 * The individual filesystem fs_init() routines 84 * patch the copy to represent the particular 85 * details for the relevant filesystem type 86 */ 87static nfsfattr gen_fattr = 88{ 89 NFLNK, /* type */ 90 NFSMODE_LNK | 0777, /* mode */ 91 1, /* nlink */ 92 0, /* uid */ 93 0, /* gid */ 94 0, /* size */ 95 4096, /* blocksize */ 96 0, /* rdev */ 97 1, /* blocks */ 98 0, /* fsid */ 99 0, /* fileid */ 100 {0, 0}, /* atime */ 101 {0, 0}, /* mtime */ 102 {0, 0}, /* ctime */ 103}; 104 105/* forward declarations */ 106static int unmount_node(opaque_t arg); 107static void exported_ap_free(am_node *mp); 108static void remove_am(am_node *mp); 109static am_node *get_root_ap(char *dir); 110 111 112/* 113 * Iterator functions for exported_ap[] 114 */ 115am_node * 116get_first_exported_ap(int *index) 117{ 118 *index = -1; 119 return get_next_exported_ap(index); 120} 121 122 123am_node * 124get_next_exported_ap(int *index) 125{ 126 (*index)++; 127 while (*index < exported_ap_size) { 128 if (exported_ap[*index] != NULL) 129 return exported_ap[*index]; 130 (*index)++; 131 } 132 return NULL; 133} 134 135 136/* 137 * Get exported_ap by index 138 */ 139am_node * 140get_exported_ap(int index) 141{ 142 if (index < 0 || index >= exported_ap_size) 143 return 0; 144 return exported_ap[index]; 145} 146 147 148/* 149 * Get exported_ap by path 150 */ 151am_node * 152path_to_exported_ap(char *path) 153{ 154 int index; 155 am_node *mp; 156 157 mp = get_first_exported_ap(&index); 158 while (mp != NULL) { 159 if (STREQ(mp->am_path, path)) 160 break; 161 mp = get_next_exported_ap(&index); 162 } 163 return mp; 164} 165 166 167/* 168 * Resize exported_ap map 169 */ 170static int 171exported_ap_realloc_map(int nsize) 172{ 173 /* 174 * this shouldn't happen, but... 175 */ 176 if (nsize < 0 || nsize == exported_ap_size) 177 return 0; 178 179 exported_ap = (am_node **) xrealloc((voidp) exported_ap, nsize * sizeof(am_node *)); 180 181 if (nsize > exported_ap_size) 182 memset((char *) (exported_ap + exported_ap_size), 0, 183 (nsize - exported_ap_size) * sizeof(am_node *)); 184 exported_ap_size = nsize; 185 186 return 1; 187} 188 189 190 191am_node * 192get_ap_child(am_node *mp, char *fname) 193{ 194 am_node *new_mp; 195 mntfs *mf = mp->am_mnt; 196 197 /* 198 * Allocate a new map 199 */ 200 new_mp = exported_ap_alloc(); 201 if (new_mp) { 202 /* 203 * Fill it in 204 */ 205 init_map(new_mp, fname); 206 207 /* 208 * Put it in the table 209 */ 210 insert_am(new_mp, mp); 211 212 /* 213 * Fill in some other fields, 214 * path and mount point. 215 * 216 * bugfix: do not prepend old am_path if direct map 217 * <wls@astro.umd.edu> William Sebok 218 */ 219 new_mp->am_path = str3cat(new_mp->am_path, 220 (mf->mf_fsflags & FS_DIRECT) 221 ? "" 222 : mp->am_path, 223 *fname == '/' ? "" : "/", fname); 224 dlog("setting path to %s", new_mp->am_path); 225 } 226 227 return new_mp; 228} 229 230/* 231 * Allocate a new mount slot and create 232 * a new node. 233 * Fills in the map number of the node, 234 * but leaves everything else uninitialized. 235 */ 236am_node * 237exported_ap_alloc(void) 238{ 239 am_node *mp, **mpp; 240 241 /* 242 * First check if there are any slots left, realloc if needed 243 */ 244 if (first_free_map >= exported_ap_size) 245 if (!exported_ap_realloc_map(exported_ap_size + NEXP_AP)) 246 return 0; 247 248 /* 249 * Grab the next free slot 250 */ 251 mpp = exported_ap + first_free_map; 252 mp = *mpp = ALLOC(struct am_node); 253 memset((char *) mp, 0, sizeof(struct am_node)); 254 255 mp->am_mapno = first_free_map++; 256 257 /* 258 * Update free pointer 259 */ 260 while (first_free_map < exported_ap_size && exported_ap[first_free_map]) 261 first_free_map++; 262 263 if (first_free_map > last_used_map) 264 last_used_map = first_free_map - 1; 265 266 return mp; 267} 268 269 270/* 271 * Free a mount slot 272 */ 273static void 274exported_ap_free(am_node *mp) 275{ 276 /* 277 * Sanity check 278 */ 279 if (!mp) 280 return; 281 282 /* 283 * Zero the slot pointer to avoid double free's 284 */ 285 exported_ap[mp->am_mapno] = NULL; 286 287 /* 288 * Update the free and last_used indices 289 */ 290 if (mp->am_mapno == last_used_map) 291 while (last_used_map >= 0 && exported_ap[last_used_map] == 0) 292 --last_used_map; 293 294 if (first_free_map > mp->am_mapno) 295 first_free_map = mp->am_mapno; 296 297 /* 298 * Free the mount node, and zero out it's internal struct data. 299 */ 300 memset((char *) mp, 0, sizeof(am_node)); 301 XFREE(mp); 302} 303 304 305/* 306 * Insert mp into the correct place, 307 * where p_mp is its parent node. 308 * A new node gets placed as the youngest sibling 309 * of any other children, and the parent's child 310 * pointer is adjusted to point to the new child node. 311 */ 312void 313insert_am(am_node *mp, am_node *p_mp) 314{ 315 /* 316 * If this is going in at the root then flag it 317 * so that it cannot be unmounted by amq. 318 */ 319 if (p_mp == root_node) 320 mp->am_flags |= AMF_ROOT; 321 /* 322 * Fill in n-way links 323 */ 324 mp->am_parent = p_mp; 325 mp->am_osib = p_mp->am_child; 326 if (mp->am_osib) 327 mp->am_osib->am_ysib = mp; 328 p_mp->am_child = mp; 329#ifdef HAVE_FS_AUTOFS 330 if (p_mp->am_mnt->mf_flags & MFF_IS_AUTOFS) 331 mp->am_flags |= AMF_AUTOFS; 332#endif /* HAVE_FS_AUTOFS */ 333} 334 335 336/* 337 * Remove am from its place in the mount tree 338 */ 339static void 340remove_am(am_node *mp) 341{ 342 /* 343 * 1. Consistency check 344 */ 345 if (mp->am_child && mp->am_parent) { 346 plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path); 347 } 348 349 /* 350 * 2. Update parent's child pointer 351 */ 352 if (mp->am_parent && mp->am_parent->am_child == mp) 353 mp->am_parent->am_child = mp->am_osib; 354 355 /* 356 * 3. Unlink from sibling chain 357 */ 358 if (mp->am_ysib) 359 mp->am_ysib->am_osib = mp->am_osib; 360 if (mp->am_osib) 361 mp->am_osib->am_ysib = mp->am_ysib; 362} 363 364 365/* 366 * Compute a new time to live value for a node. 367 */ 368void 369new_ttl(am_node *mp) 370{ 371 mp->am_timeo_w = 0; 372 mp->am_ttl = clocktime(&mp->am_fattr.na_atime); 373 mp->am_ttl += mp->am_timeo; /* sun's -tl option */ 374} 375 376 377void 378mk_fattr(nfsfattr *fattr, nfsftype vntype) 379{ 380 switch (vntype) { 381 case NFDIR: 382 fattr->na_type = NFDIR; 383 fattr->na_mode = NFSMODE_DIR | 0555; 384 fattr->na_nlink = 2; 385 fattr->na_size = 512; 386 break; 387 case NFLNK: 388 fattr->na_type = NFLNK; 389 fattr->na_mode = NFSMODE_LNK | 0777; 390 fattr->na_nlink = 1; 391 fattr->na_size = 0; 392 break; 393 default: 394 plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype); 395 break; 396 } 397} 398 399 400/* 401 * Initialize an allocated mount node. 402 * It is assumed that the mount node was b-zero'd 403 * before getting here so anything that would 404 * be set to zero isn't done here. 405 */ 406void 407init_map(am_node *mp, char *dir) 408{ 409 /* 410 * mp->am_mapno is initialized by exported_ap_alloc 411 * other fields don't need to be set to zero. 412 */ 413 mp->am_mnt = new_mntfs(); 414 mp->am_mfarray = NULL; 415 mp->am_name = strdup(dir); 416 mp->am_path = strdup(dir); 417 mp->am_gen = new_gen(); 418#ifdef HAVE_FS_AUTOFS 419 mp->am_autofs_fh = NULL; 420#endif /* HAVE_FS_AUTOFS */ 421 422 mp->am_timeo = gopt.am_timeo; 423 mp->am_attr.ns_status = NFS_OK; 424 mp->am_fattr = gen_fattr; 425 mp->am_fattr.na_fsid = 42; 426 mp->am_fattr.na_fileid = mp->am_gen; 427 clocktime(&mp->am_fattr.na_atime); 428 /* next line copies a "struct nfstime" among several fields */ 429 mp->am_fattr.na_mtime = mp->am_fattr.na_ctime = mp->am_fattr.na_atime; 430 431 new_ttl(mp); 432 mp->am_stats.s_mtime = mp->am_fattr.na_atime.nt_seconds; 433 mp->am_dev = -1; 434 mp->am_rdev = -1; 435 mp->am_fd[0] = -1; 436 mp->am_fd[1] = -1; 437} 438 439 440void 441notify_child(am_node *mp, au_etype au_etype, int au_errno, int au_signal) 442{ 443 amq_sync_umnt rv; 444 445 if (mp->am_fd[1] >= 0) { /* we have a child process */ 446 rv.au_etype = au_etype; 447 rv.au_signal = au_signal; 448 rv.au_errno = au_errno; 449 450 write(mp->am_fd[1], &rv, sizeof(rv)); 451 close(mp->am_fd[1]); 452 mp->am_fd[1] = -1; 453 } 454} 455 456 457/* 458 * Free a mount node. 459 * The node must be already unmounted. 460 */ 461void 462free_map(am_node *mp) 463{ 464 remove_am(mp); 465 466 if (mp->am_fd[1] != -1) 467 plog(XLOG_FATAL, "free_map: called prior to notifying the child for %s.", 468 mp->am_path); 469 470 if (mp->am_link) 471 XFREE(mp->am_link); 472 if (mp->am_name) 473 XFREE(mp->am_name); 474 if (mp->am_path) 475 XFREE(mp->am_path); 476 if (mp->am_pref) 477 XFREE(mp->am_pref); 478 if (mp->am_transp) 479 XFREE(mp->am_transp); 480 481 if (mp->am_mnt) 482 free_mntfs(mp->am_mnt); 483 484 if (mp->am_mfarray) { 485 mntfs **temp_mf; 486 for (temp_mf = mp->am_mfarray; *temp_mf; temp_mf++) 487 free_mntfs(*temp_mf); 488 XFREE(mp->am_mfarray); 489 } 490 491#ifdef HAVE_FS_AUTOFS 492 if (mp->am_autofs_fh) 493 autofs_release_fh(mp); 494#endif /* HAVE_FS_AUTOFS */ 495 496 exported_ap_free(mp); 497} 498 499 500static am_node * 501find_ap_recursive(char *dir, am_node *mp) 502{ 503 if (mp) { 504 am_node *mp2; 505 if (STREQ(mp->am_path, dir)) 506 return mp; 507 508 if ((mp->am_mnt->mf_flags & MFF_MOUNTED) && 509 STREQ(mp->am_mnt->mf_mount, dir)) 510 return mp; 511 512 mp2 = find_ap_recursive(dir, mp->am_osib); 513 if (mp2) 514 return mp2; 515 return find_ap_recursive(dir, mp->am_child); 516 } 517 518 return 0; 519} 520 521 522/* 523 * Find the mount node corresponding to dir. dir can match either the 524 * automount path or, if the node is mounted, the mount location. 525 */ 526am_node * 527find_ap(char *dir) 528{ 529 int i; 530 531 for (i = last_used_map; i >= 0; --i) { 532 am_node *mp = exported_ap[i]; 533 if (mp && (mp->am_flags & AMF_ROOT)) { 534 mp = find_ap_recursive(dir, exported_ap[i]); 535 if (mp) { 536 return mp; 537 } 538 } 539 } 540 541 return 0; 542} 543 544 545/* 546 * Find the mount node corresponding 547 * to the mntfs structure. 548 */ 549am_node * 550find_mf(mntfs *mf) 551{ 552 int i; 553 554 for (i = last_used_map; i >= 0; --i) { 555 am_node *mp = exported_ap[i]; 556 if (mp && mp->am_mnt == mf) 557 return mp; 558 } 559 560 return 0; 561} 562 563 564/* 565 * Get the filehandle for a particular named directory. 566 * This is used during the bootstrap to tell the kernel 567 * the filehandles of the initial automount points. 568 */ 569am_nfs_fh * 570get_root_nfs_fh(char *dir) 571{ 572 static am_nfs_fh nfh; 573 am_node *mp = get_root_ap(dir); 574 if (mp) { 575 mp_to_fh(mp, &nfh); 576 return &nfh; 577 } 578 579 /* 580 * Should never get here... 581 */ 582 plog(XLOG_ERROR, "Can't find root filehandle for %s", dir); 583 584 return 0; 585} 586 587 588static am_node * 589get_root_ap(char *dir) 590{ 591 am_node *mp = find_ap(dir); 592 593 if (mp && mp->am_parent == root_node) 594 return mp; 595 596 return 0; 597} 598 599 600/* 601 * Timeout all nodes waiting on 602 * a given Fserver. 603 */ 604void 605map_flush_srvr(fserver *fs) 606{ 607 int i; 608 int done = 0; 609 610 for (i = last_used_map; i >= 0; --i) { 611 am_node *mp = exported_ap[i]; 612 if (mp && mp->am_mnt && mp->am_mnt->mf_server == fs) { 613 plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host); 614 mp->am_ttl = clocktime(NULL); 615 done = 1; 616 } 617 } 618 if (done) 619 reschedule_timeout_mp(); 620} 621 622 623/* 624 * Mount a top level automount node 625 * by calling lookup in the parent 626 * (root) node which will cause the 627 * automount node to be automounted. 628 */ 629int 630mount_auto_node(char *dir, opaque_t arg) 631{ 632 int error = 0; 633 am_node *mp = (am_node *) arg; 634 am_node *new_mp; 635 636 new_mp = mp->am_mnt->mf_ops->lookup_child(mp, dir, &error, VLOOK_CREATE); 637 if (new_mp && error < 0) { 638 /* 639 * We can't allow the fileid of the root node to change. 640 * Should be ok to force it to 1, always. 641 */ 642 new_mp->am_gen = new_mp->am_fattr.na_fileid = 1; 643 644 new_mp = mp->am_mnt->mf_ops->mount_child(new_mp, &error); 645 } 646 647 if (error > 0) { 648 errno = error; /* XXX */ 649 plog(XLOG_ERROR, "Could not mount %s: %m", dir); 650 } 651 return error; 652} 653 654 655/* 656 * Cause all the top-level mount nodes 657 * to be automounted 658 */ 659int 660mount_exported(void) 661{ 662 /* 663 * Iterate over all the nodes to be started 664 */ 665 return root_keyiter(mount_auto_node, root_node); 666} 667 668 669/* 670 * Construct top-level node 671 */ 672void 673make_root_node(void) 674{ 675 mntfs *root_mnt; 676 char *rootmap = ROOT_MAP; 677 root_node = exported_ap_alloc(); 678 679 /* 680 * Allocate a new map 681 */ 682 init_map(root_node, ""); 683 684 /* 685 * Allocate a new mounted filesystem 686 */ 687 root_mnt = find_mntfs(&amfs_root_ops, (am_opts *) NULL, "", rootmap, "", "", ""); 688 689 /* 690 * Replace the initial null reference 691 */ 692 free_mntfs(root_node->am_mnt); 693 root_node->am_mnt = root_mnt; 694 695 /* 696 * Initialize the root 697 */ 698 if (root_mnt->mf_ops->fs_init) 699 (*root_mnt->mf_ops->fs_init) (root_mnt); 700 701 /* 702 * Mount the root 703 */ 704 root_mnt->mf_error = root_mnt->mf_ops->mount_fs(root_node, root_mnt); 705} 706 707 708/* 709 * Cause all the nodes to be unmounted by timing 710 * them out. 711 */ 712void 713umount_exported(void) 714{ 715 int i; 716 717 for (i = last_used_map; i >= 0; --i) { 718 am_node *mp = exported_ap[i]; 719 mntfs *mf; 720 721 if (!mp) 722 continue; 723 724 mf = mp->am_mnt; 725 if (mf->mf_flags & MFF_UNMOUNTING) { 726 /* 727 * If this node is being unmounted then just ignore it. However, 728 * this could prevent amd from finishing if the unmount gets blocked 729 * since the am_node will never be free'd. am_unmounted needs 730 * telling about this possibility. - XXX 731 */ 732 continue; 733 } 734 735 if (!(mf->mf_fsflags & FS_DIRECTORY)) 736 /* 737 * When shutting down this had better 738 * look like a directory, otherwise it 739 * can't be unmounted! 740 */ 741 mk_fattr(&mp->am_fattr, NFDIR); 742 743 if ((--immediate_abort < 0 && 744 !(mp->am_flags & AMF_ROOT) && mp->am_parent) || 745 (mf->mf_flags & MFF_RESTART)) { 746 747 /* 748 * Just throw this node away without bothering to unmount it. If 749 * the server is not known to be up then don't discard the mounted 750 * on directory or Amd might hang... 751 */ 752 if (mf->mf_server && 753 (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) != FSF_VALID) 754 mf->mf_flags &= ~MFF_MKMNT; 755 if (gopt.flags & CFM_UNMOUNT_ON_EXIT || mp->am_flags & AMF_AUTOFS) { 756 plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount); 757 /* 758 * use unmount_mp, not unmount_node, so that unmounts be 759 * backgrounded as needed. 760 */ 761 unmount_mp((opaque_t) mp); 762 } else { 763 am_unmounted(mp); 764 } 765 exported_ap[i] = NULL; 766 } else { 767 /* 768 * Any other node gets forcibly timed out. 769 */ 770 mp->am_flags &= ~AMF_NOTIMEOUT; 771 mp->am_mnt->mf_flags &= ~MFF_RSTKEEP; 772 mp->am_ttl = 0; 773 mp->am_timeo = 1; 774 mp->am_timeo_w = 0; 775 } 776 } 777} 778 779 780/* 781 * Try to mount a file system. Can be called directly or in a sub-process by run_task. 782 * 783 * Warning: this function might be running in a child process context. 784 * Don't expect any changes made here to survive in the parent amd process. 785 */ 786int 787mount_node(opaque_t arg) 788{ 789 am_node *mp = (am_node *) arg; 790 mntfs *mf = mp->am_mnt; 791 int error = 0; 792 793#ifdef HAVE_FS_AUTOFS 794 if (mp->am_flags & AMF_AUTOFS) 795 error = autofs_mount_fs(mp, mf); 796 else 797#endif /* HAVE_FS_AUTOFS */ 798 if (!(mf->mf_flags & MFF_MOUNTED)) 799 error = mf->mf_ops->mount_fs(mp, mf); 800 801 if (error > 0) 802 dlog("mount_node: call to mf_ops->mount_fs(%s) failed: %s", 803 mp->am_path, strerror(error)); 804 return error; 805} 806 807 808static int 809unmount_node(opaque_t arg) 810{ 811 am_node *mp = (am_node *) arg; 812 mntfs *mf = mp->am_mnt; 813 int error = 0; 814 815 if (mf->mf_flags & MFF_ERROR) { 816 /* 817 * Just unlink 818 */ 819 dlog("No-op unmount of error node %s", mf->mf_info); 820 } else { 821 dlog("Unmounting <%s> <%s> (%s) flags %x", 822 mp->am_path, mf->mf_mount, mf->mf_info, mf->mf_flags); 823#ifdef HAVE_FS_AUTOFS 824 if (mp->am_flags & AMF_AUTOFS) 825 error = autofs_umount_fs(mp, mf); 826 else 827#endif /* HAVE_FS_AUTOFS */ 828 if (mf->mf_refc == 1) 829 error = mf->mf_ops->umount_fs(mp, mf); 830 } 831 832 /* do this again, it might have changed */ 833 mf = mp->am_mnt; 834 if (error) { 835 errno = error; /* XXX */ 836 dlog("%s: unmount: %m", mf->mf_mount); 837 } 838 839 return error; 840} 841 842 843static void 844free_map_if_success(int rc, int term, opaque_t arg) 845{ 846 am_node *mp = (am_node *) arg; 847 mntfs *mf = mp->am_mnt; 848 wchan_t wchan = get_mntfs_wchan(mf); 849 850 /* 851 * Not unmounting any more 852 */ 853 mf->mf_flags &= ~MFF_UNMOUNTING; 854 855 /* 856 * If a timeout was deferred because the underlying filesystem 857 * was busy then arrange for a timeout as soon as possible. 858 */ 859 if (mf->mf_flags & MFF_WANTTIMO) { 860 mf->mf_flags &= ~MFF_WANTTIMO; 861 reschedule_timeout_mp(); 862 } 863 if (term) { 864 notify_child(mp, AMQ_UMNT_SIGNAL, 0, term); 865 plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term); 866#if defined(DEBUG) && defined(SIGTRAP) 867 /* 868 * dbx likes to put a trap on exit(). 869 * Pretend it succeeded for now... 870 */ 871 if (term == SIGTRAP) { 872 am_unmounted(mp); 873 } 874#endif /* DEBUG */ 875#ifdef HAVE_FS_AUTOFS 876 if (mp->am_flags & AMF_AUTOFS) 877 autofs_umount_failed(mp); 878#endif /* HAVE_FS_AUTOFS */ 879 amd_stats.d_uerr++; 880 } else if (rc) { 881 notify_child(mp, AMQ_UMNT_FAILED, rc, 0); 882 if (mf->mf_ops == &amfs_program_ops || rc == EBUSY) 883 plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount); 884 else 885 plog(XLOG_ERROR, "%s: unmount: %s", mp->am_path, strerror(rc)); 886#ifdef HAVE_FS_AUTOFS 887 if (mf->mf_flags & MFF_IS_AUTOFS) 888 autofs_get_mp(mp); 889 if (mp->am_flags & AMF_AUTOFS) 890 autofs_umount_failed(mp); 891#endif /* HAVE_FS_AUTOFS */ 892 amd_stats.d_uerr++; 893 } else { 894 /* 895 * am_unmounted() will call notify_child() appropriately. 896 */ 897 am_unmounted(mp); 898 } 899 900 /* 901 * Wakeup anything waiting for this unmount 902 */ 903 wakeup(wchan); 904} 905 906 907int 908unmount_mp(am_node *mp) 909{ 910 int was_backgrounded = 0; 911 mntfs *mf = mp->am_mnt; 912 913#ifdef notdef 914 plog(XLOG_INFO, "\"%s\" on %s timed out (flags 0x%x)", 915 mp->am_path, mp->am_mnt->mf_mount, (int) mf->mf_flags); 916#endif /* notdef */ 917 918#ifndef MNT2_NFS_OPT_SYMTTL 919 /* 920 * This code is needed to defeat Solaris 2.4's (and newer) symlink 921 * values cache. It forces the last-modified time of the symlink to be 922 * current. It is not needed if the O/S has an nfs flag to turn off the 923 * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez. 924 * 925 * Additionally, Linux currently ignores the nt_useconds field, 926 * so we must update the nt_seconds field every time if clocktime(NULL) 927 * didn't return a new number of seconds. 928 */ 929 if (mp->am_parent) { 930 time_t last = mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds; 931 clocktime(&mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime); 932 /* defensive programming... can't we assert the above condition? */ 933 if (last == (time_t) mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds) 934 mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds++; 935 } 936#endif /* not MNT2_NFS_OPT_SYMTTL */ 937 938 if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) { 939 /* 940 * Don't try to unmount from a server that is known to be down 941 */ 942 if (!(mf->mf_flags & MFF_LOGDOWN)) { 943 /* Only log this once, otherwise gets a bit boring */ 944 plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path); 945 mf->mf_flags |= MFF_LOGDOWN; 946 } 947 notify_child(mp, AMQ_UMNT_SERVER, 0, 0); 948 return 0; 949 } 950 951 dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount); 952 mf->mf_flags |= MFF_UNMOUNTING; 953 954#ifdef HAVE_FS_AUTOFS 955 if (mf->mf_flags & MFF_IS_AUTOFS) 956 autofs_release_mp(mp); 957#endif /* HAVE_FS_AUTOFS */ 958 959 if ((mf->mf_fsflags & FS_UBACKGROUND) && 960 (mf->mf_flags & MFF_MOUNTED)) { 961 dlog("Trying unmount in background"); 962 run_task(unmount_node, (opaque_t) mp, 963 free_map_if_success, (opaque_t) mp); 964 was_backgrounded = 1; 965 } else { 966 dlog("Trying unmount in foreground"); 967 free_map_if_success(unmount_node((opaque_t) mp), 0, (opaque_t) mp); 968 dlog("unmount attempt done"); 969 } 970 971 return was_backgrounded; 972} 973 974 975void 976timeout_mp(opaque_t v) /* argument not used?! */ 977{ 978 int i; 979 time_t t = NEVER; 980 time_t now = clocktime(NULL); 981 int backoff = NumChildren / 4; 982 983 dlog("Timing out automount points..."); 984 985 for (i = last_used_map; i >= 0; --i) { 986 am_node *mp = exported_ap[i]; 987 mntfs *mf; 988 989 /* 990 * Just continue if nothing mounted 991 */ 992 if (!mp) 993 continue; 994 995 /* 996 * Pick up mounted filesystem 997 */ 998 mf = mp->am_mnt; 999 if (!mf) 1000 continue; 1001 1002#ifdef HAVE_FS_AUTOFS 1003 if (mf->mf_flags & MFF_IS_AUTOFS && mp->am_autofs_ttl != NEVER) { 1004 if (now >= mp->am_autofs_ttl) 1005 autofs_timeout_mp(mp); 1006 t = smallest_t(t, mp->am_autofs_ttl); 1007 } 1008#endif /* HAVE_FS_AUTOFS */ 1009 1010 if (mp->am_flags & AMF_NOTIMEOUT) 1011 continue; 1012 1013 /* 1014 * Don't delete last reference to a restarted filesystem. 1015 */ 1016 if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1) 1017 continue; 1018 1019 /* 1020 * If there is action on this filesystem then ignore it 1021 */ 1022 if (!(mf->mf_flags & IGNORE_FLAGS)) { 1023 int expired = 0; 1024 mf->mf_flags &= ~MFF_WANTTIMO; 1025 if (now >= mp->am_ttl) { 1026 if (!backoff) { 1027 expired = 1; 1028 1029 /* 1030 * Move the ttl forward to avoid thrashing effects 1031 * on the next call to timeout! 1032 */ 1033 /* sun's -tw option */ 1034 if (mp->am_timeo_w < 4 * gopt.am_timeo_w) 1035 mp->am_timeo_w += gopt.am_timeo_w; 1036 mp->am_ttl = now + mp->am_timeo_w; 1037 1038 } else { 1039 /* 1040 * Just backoff this unmount for 1041 * a couple of seconds to avoid 1042 * many multiple unmounts being 1043 * started in parallel. 1044 */ 1045 mp->am_ttl = now + backoff + 1; 1046 } 1047 } 1048 1049 /* 1050 * If the next ttl is smallest, use that 1051 */ 1052 t = smallest_t(t, mp->am_ttl); 1053 1054 if (!mp->am_child && mf->mf_error >= 0 && expired) { 1055 /* 1056 * If the unmount was backgrounded then 1057 * bump the backoff counter. 1058 */ 1059 if (unmount_mp(mp)) { 1060 backoff = 2; 1061 } 1062 } 1063 } else if (mf->mf_flags & MFF_UNMOUNTING) { 1064 mf->mf_flags |= MFF_WANTTIMO; 1065 } 1066 } 1067 1068 if (t == NEVER) { 1069 dlog("No further timeouts"); 1070 t = now + ONE_HOUR; 1071 } 1072 1073 /* 1074 * Sanity check to avoid runaways. 1075 * Absolutely should never get this but 1076 * if you do without this trap amd will thrash. 1077 */ 1078 if (t <= now) { 1079 t = now + 6; /* XXX */ 1080 plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!"); 1081 } 1082 1083 /* 1084 * XXX - when shutting down, make things happen faster 1085 */ 1086 if ((int) amd_state >= (int) Finishing) 1087 t = now + 1; 1088 dlog("Next mount timeout in %lds", (long) (t - now)); 1089 1090 timeout_mp_id = timeout(t - now, timeout_mp, NULL); 1091} 1092 1093 1094/* 1095 * Cause timeout_mp to be called soonest 1096 */ 1097void 1098reschedule_timeout_mp(void) 1099{ 1100 if (timeout_mp_id) 1101 untimeout(timeout_mp_id); 1102 timeout_mp_id = timeout(0, timeout_mp, NULL); 1103} 1104