1 /* $NetBSD: map.c,v 1.1.1.1 2008/09/19 20:07:16 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/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 */ 70 static u_int am_gen = 2; /* Initial generation number */ 71 static int timeout_mp_id; /* Id from last call to timeout */ 72 73 static am_node *root_node; /* The root of the mount tree */ 74 static am_node **exported_ap = (am_node **) NULL; 75 static int exported_ap_size = 0; 76 static int first_free_map = 0; /* First available free slot */ 77 static 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 */ 87 static 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 */ 106 static int unmount_node(opaque_t arg); 107 static void exported_ap_free(am_node *mp); 108 static void remove_am(am_node *mp); 109 static am_node *get_root_ap(char *dir); 110 111 112 /* 113 * Iterator functions for exported_ap[] 114 */ 115 am_node * 116 get_first_exported_ap(int *index) 117 { 118 *index = -1; 119 return get_next_exported_ap(index); 120 } 121 122 123 am_node * 124 get_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 */ 139 am_node * 140 get_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 */ 151 am_node * 152 path_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 */ 170 static int 171 exported_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 191 am_node * 192 get_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 */ 236 am_node * 237 exported_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 */ 273 static void 274 exported_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 */ 312 void 313 insert_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 */ 339 static void 340 remove_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 */ 368 void 369 new_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 377 void 378 mk_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 */ 406 void 407 init_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 } 436 437 438 /* 439 * Free a mount node. 440 * The node must be already unmounted. 441 */ 442 void 443 free_map(am_node *mp) 444 { 445 remove_am(mp); 446 447 if (mp->am_link) 448 XFREE(mp->am_link); 449 if (mp->am_name) 450 XFREE(mp->am_name); 451 if (mp->am_path) 452 XFREE(mp->am_path); 453 if (mp->am_pref) 454 XFREE(mp->am_pref); 455 if (mp->am_transp) 456 XFREE(mp->am_transp); 457 458 if (mp->am_mnt) 459 free_mntfs(mp->am_mnt); 460 461 if (mp->am_mfarray) { 462 mntfs **temp_mf; 463 for (temp_mf = mp->am_mfarray; *temp_mf; temp_mf++) 464 free_mntfs(*temp_mf); 465 XFREE(mp->am_mfarray); 466 } 467 468 #ifdef HAVE_FS_AUTOFS 469 if (mp->am_autofs_fh) 470 autofs_release_fh(mp); 471 #endif /* HAVE_FS_AUTOFS */ 472 473 exported_ap_free(mp); 474 } 475 476 477 static am_node * 478 find_ap_recursive(char *dir, am_node *mp) 479 { 480 if (mp) { 481 am_node *mp2; 482 if (STREQ(mp->am_path, dir)) 483 return mp; 484 485 if ((mp->am_mnt->mf_flags & MFF_MOUNTED) && 486 STREQ(mp->am_mnt->mf_mount, dir)) 487 return mp; 488 489 mp2 = find_ap_recursive(dir, mp->am_osib); 490 if (mp2) 491 return mp2; 492 return find_ap_recursive(dir, mp->am_child); 493 } 494 495 return 0; 496 } 497 498 499 /* 500 * Find the mount node corresponding to dir. dir can match either the 501 * automount path or, if the node is mounted, the mount location. 502 */ 503 am_node * 504 find_ap(char *dir) 505 { 506 int i; 507 508 for (i = last_used_map; i >= 0; --i) { 509 am_node *mp = exported_ap[i]; 510 if (mp && (mp->am_flags & AMF_ROOT)) { 511 mp = find_ap_recursive(dir, exported_ap[i]); 512 if (mp) { 513 return mp; 514 } 515 } 516 } 517 518 return 0; 519 } 520 521 522 /* 523 * Find the mount node corresponding 524 * to the mntfs structure. 525 */ 526 am_node * 527 find_mf(mntfs *mf) 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_mnt == mf) 534 return mp; 535 } 536 537 return 0; 538 } 539 540 541 /* 542 * Get the filehandle for a particular named directory. 543 * This is used during the bootstrap to tell the kernel 544 * the filehandles of the initial automount points. 545 */ 546 am_nfs_fh * 547 get_root_nfs_fh(char *dir) 548 { 549 static am_nfs_fh nfh; 550 am_node *mp = get_root_ap(dir); 551 if (mp) { 552 mp_to_fh(mp, &nfh); 553 return &nfh; 554 } 555 556 /* 557 * Should never get here... 558 */ 559 plog(XLOG_ERROR, "Can't find root filehandle for %s", dir); 560 561 return 0; 562 } 563 564 565 static am_node * 566 get_root_ap(char *dir) 567 { 568 am_node *mp = find_ap(dir); 569 570 if (mp && mp->am_parent == root_node) 571 return mp; 572 573 return 0; 574 } 575 576 577 /* 578 * Timeout all nodes waiting on 579 * a given Fserver. 580 */ 581 void 582 map_flush_srvr(fserver *fs) 583 { 584 int i; 585 int done = 0; 586 587 for (i = last_used_map; i >= 0; --i) { 588 am_node *mp = exported_ap[i]; 589 if (mp && mp->am_mnt && mp->am_mnt->mf_server == fs) { 590 plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host); 591 mp->am_ttl = clocktime(NULL); 592 done = 1; 593 } 594 } 595 if (done) 596 reschedule_timeout_mp(); 597 } 598 599 600 /* 601 * Mount a top level automount node 602 * by calling lookup in the parent 603 * (root) node which will cause the 604 * automount node to be automounted. 605 */ 606 int 607 mount_auto_node(char *dir, opaque_t arg) 608 { 609 int error = 0; 610 am_node *mp = (am_node *) arg; 611 am_node *new_mp; 612 613 new_mp = mp->am_mnt->mf_ops->lookup_child(mp, dir, &error, VLOOK_CREATE); 614 if (new_mp && error < 0) { 615 /* 616 * We can't allow the fileid of the root node to change. 617 * Should be ok to force it to 1, always. 618 */ 619 new_mp->am_gen = new_mp->am_fattr.na_fileid = 1; 620 621 new_mp = mp->am_mnt->mf_ops->mount_child(new_mp, &error); 622 } 623 624 if (error > 0) { 625 errno = error; /* XXX */ 626 plog(XLOG_ERROR, "Could not mount %s: %m", dir); 627 } 628 return error; 629 } 630 631 632 /* 633 * Cause all the top-level mount nodes 634 * to be automounted 635 */ 636 int 637 mount_exported(void) 638 { 639 /* 640 * Iterate over all the nodes to be started 641 */ 642 return root_keyiter(mount_auto_node, root_node); 643 } 644 645 646 /* 647 * Construct top-level node 648 */ 649 void 650 make_root_node(void) 651 { 652 mntfs *root_mnt; 653 char *rootmap = ROOT_MAP; 654 root_node = exported_ap_alloc(); 655 656 /* 657 * Allocate a new map 658 */ 659 init_map(root_node, ""); 660 661 /* 662 * Allocate a new mounted filesystem 663 */ 664 root_mnt = find_mntfs(&amfs_root_ops, (am_opts *) NULL, "", rootmap, "", "", ""); 665 666 /* 667 * Replace the initial null reference 668 */ 669 free_mntfs(root_node->am_mnt); 670 root_node->am_mnt = root_mnt; 671 672 /* 673 * Initialize the root 674 */ 675 if (root_mnt->mf_ops->fs_init) 676 (*root_mnt->mf_ops->fs_init) (root_mnt); 677 678 /* 679 * Mount the root 680 */ 681 root_mnt->mf_error = root_mnt->mf_ops->mount_fs(root_node, root_mnt); 682 } 683 684 685 /* 686 * Cause all the nodes to be unmounted by timing 687 * them out. 688 */ 689 void 690 umount_exported(void) 691 { 692 int i; 693 694 for (i = last_used_map; i >= 0; --i) { 695 am_node *mp = exported_ap[i]; 696 mntfs *mf; 697 698 if (!mp) 699 continue; 700 701 mf = mp->am_mnt; 702 if (mf->mf_flags & MFF_UNMOUNTING) { 703 /* 704 * If this node is being unmounted then just ignore it. However, 705 * this could prevent amd from finishing if the unmount gets blocked 706 * since the am_node will never be free'd. am_unmounted needs 707 * telling about this possibility. - XXX 708 */ 709 continue; 710 } 711 712 if (!(mf->mf_fsflags & FS_DIRECTORY)) 713 /* 714 * When shutting down this had better 715 * look like a directory, otherwise it 716 * can't be unmounted! 717 */ 718 mk_fattr(&mp->am_fattr, NFDIR); 719 720 if ((--immediate_abort < 0 && 721 !(mp->am_flags & AMF_ROOT) && mp->am_parent) || 722 (mf->mf_flags & MFF_RESTART)) { 723 724 /* 725 * Just throw this node away without bothering to unmount it. If 726 * the server is not known to be up then don't discard the mounted 727 * on directory or Amd might hang... 728 */ 729 if (mf->mf_server && 730 (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) != FSF_VALID) 731 mf->mf_flags &= ~MFF_MKMNT; 732 if (gopt.flags & CFM_UNMOUNT_ON_EXIT || mp->am_flags & AMF_AUTOFS) { 733 plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount); 734 /* 735 * use unmount_mp, not unmount_node, so that unmounts be 736 * backgrounded as needed. 737 */ 738 unmount_mp((opaque_t) mp); 739 } else { 740 am_unmounted(mp); 741 } 742 exported_ap[i] = NULL; 743 } else { 744 /* 745 * Any other node gets forcibly timed out. 746 */ 747 mp->am_flags &= ~AMF_NOTIMEOUT; 748 mp->am_mnt->mf_flags &= ~MFF_RSTKEEP; 749 mp->am_ttl = 0; 750 mp->am_timeo = 1; 751 mp->am_timeo_w = 0; 752 } 753 } 754 } 755 756 757 /* 758 * Try to mount a file system. Can be called directly or in a sub-process by run_task. 759 * 760 * Warning: this function might be running in a child process context. 761 * Don't expect any changes made here to survive in the parent amd process. 762 */ 763 int 764 mount_node(opaque_t arg) 765 { 766 am_node *mp = (am_node *) arg; 767 mntfs *mf = mp->am_mnt; 768 int error = 0; 769 770 #ifdef HAVE_FS_AUTOFS 771 if (mp->am_flags & AMF_AUTOFS) 772 error = autofs_mount_fs(mp, mf); 773 else 774 #endif /* HAVE_FS_AUTOFS */ 775 if (!(mf->mf_flags & MFF_MOUNTED)) 776 error = mf->mf_ops->mount_fs(mp, mf); 777 778 if (error > 0) 779 dlog("mount_node: call to mf_ops->mount_fs(%s) failed: %s", 780 mp->am_path, strerror(error)); 781 return error; 782 } 783 784 785 static int 786 unmount_node(opaque_t arg) 787 { 788 am_node *mp = (am_node *) arg; 789 mntfs *mf = mp->am_mnt; 790 int error = 0; 791 792 if (mf->mf_flags & MFF_ERROR) { 793 /* 794 * Just unlink 795 */ 796 dlog("No-op unmount of error node %s", mf->mf_info); 797 } else { 798 dlog("Unmounting <%s> <%s> (%s) flags %x", 799 mp->am_path, mf->mf_mount, mf->mf_info, mf->mf_flags); 800 #ifdef HAVE_FS_AUTOFS 801 if (mp->am_flags & AMF_AUTOFS) 802 error = autofs_umount_fs(mp, mf); 803 else 804 #endif /* HAVE_FS_AUTOFS */ 805 if (mf->mf_refc == 1) 806 error = mf->mf_ops->umount_fs(mp, mf); 807 } 808 809 /* do this again, it might have changed */ 810 mf = mp->am_mnt; 811 if (error) { 812 errno = error; /* XXX */ 813 dlog("%s: unmount: %m", mf->mf_mount); 814 } 815 816 return error; 817 } 818 819 820 static void 821 free_map_if_success(int rc, int term, opaque_t arg) 822 { 823 am_node *mp = (am_node *) arg; 824 mntfs *mf = mp->am_mnt; 825 wchan_t wchan = get_mntfs_wchan(mf); 826 827 /* 828 * Not unmounting any more 829 */ 830 mf->mf_flags &= ~MFF_UNMOUNTING; 831 832 /* 833 * If a timeout was deferred because the underlying filesystem 834 * was busy then arrange for a timeout as soon as possible. 835 */ 836 if (mf->mf_flags & MFF_WANTTIMO) { 837 mf->mf_flags &= ~MFF_WANTTIMO; 838 reschedule_timeout_mp(); 839 } 840 if (term) { 841 plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term); 842 #if defined(DEBUG) && defined(SIGTRAP) 843 /* 844 * dbx likes to put a trap on exit(). 845 * Pretend it succeeded for now... 846 */ 847 if (term == SIGTRAP) { 848 am_unmounted(mp); 849 } 850 #endif /* DEBUG */ 851 #ifdef HAVE_FS_AUTOFS 852 if (mp->am_flags & AMF_AUTOFS) 853 autofs_umount_failed(mp); 854 #endif /* HAVE_FS_AUTOFS */ 855 amd_stats.d_uerr++; 856 } else if (rc) { 857 if (mf->mf_ops == &amfs_program_ops || rc == EBUSY) 858 plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount); 859 else 860 plog(XLOG_ERROR, "%s: unmount: %s", mp->am_path, strerror(rc)); 861 #ifdef HAVE_FS_AUTOFS 862 if (mf->mf_flags & MFF_IS_AUTOFS) 863 autofs_get_mp(mp); 864 if (mp->am_flags & AMF_AUTOFS) 865 autofs_umount_failed(mp); 866 #endif /* HAVE_FS_AUTOFS */ 867 amd_stats.d_uerr++; 868 } else { 869 am_unmounted(mp); 870 } 871 872 /* 873 * Wakeup anything waiting for this unmount 874 */ 875 wakeup(wchan); 876 } 877 878 879 int 880 unmount_mp(am_node *mp) 881 { 882 int was_backgrounded = 0; 883 mntfs *mf = mp->am_mnt; 884 885 #ifdef notdef 886 plog(XLOG_INFO, "\"%s\" on %s timed out (flags 0x%x)", 887 mp->am_path, mp->am_mnt->mf_mount, (int) mf->mf_flags); 888 #endif /* notdef */ 889 890 #ifndef MNT2_NFS_OPT_SYMTTL 891 /* 892 * This code is needed to defeat Solaris 2.4's (and newer) symlink 893 * values cache. It forces the last-modified time of the symlink to be 894 * current. It is not needed if the O/S has an nfs flag to turn off the 895 * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez. 896 * 897 * Additionally, Linux currently ignores the nt_useconds field, 898 * so we must update the nt_seconds field every time if clocktime(NULL) 899 * didn't return a new number of seconds. 900 */ 901 if (mp->am_parent) { 902 time_t last = mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds; 903 clocktime(&mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime); 904 /* defensive programming... can't we assert the above condition? */ 905 if (last == (time_t) mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds) 906 mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds++; 907 } 908 #endif /* not MNT2_NFS_OPT_SYMTTL */ 909 910 if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) { 911 /* 912 * Don't try to unmount from a server that is known to be down 913 */ 914 if (!(mf->mf_flags & MFF_LOGDOWN)) { 915 /* Only log this once, otherwise gets a bit boring */ 916 plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path); 917 mf->mf_flags |= MFF_LOGDOWN; 918 } 919 return 0; 920 } 921 922 dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount); 923 mf->mf_flags |= MFF_UNMOUNTING; 924 925 #ifdef HAVE_FS_AUTOFS 926 if (mf->mf_flags & MFF_IS_AUTOFS) 927 autofs_release_mp(mp); 928 #endif /* HAVE_FS_AUTOFS */ 929 930 if ((mf->mf_fsflags & FS_UBACKGROUND) && 931 (mf->mf_flags & MFF_MOUNTED)) { 932 dlog("Trying unmount in background"); 933 run_task(unmount_node, (opaque_t) mp, 934 free_map_if_success, (opaque_t) mp); 935 was_backgrounded = 1; 936 } else { 937 dlog("Trying unmount in foreground"); 938 free_map_if_success(unmount_node((opaque_t) mp), 0, (opaque_t) mp); 939 dlog("unmount attempt done"); 940 } 941 942 return was_backgrounded; 943 } 944 945 946 void 947 timeout_mp(opaque_t v) /* argument not used?! */ 948 { 949 int i; 950 time_t t = NEVER; 951 time_t now = clocktime(NULL); 952 int backoff = NumChildren / 4; 953 954 dlog("Timing out automount points..."); 955 956 for (i = last_used_map; i >= 0; --i) { 957 am_node *mp = exported_ap[i]; 958 mntfs *mf; 959 960 /* 961 * Just continue if nothing mounted 962 */ 963 if (!mp) 964 continue; 965 966 /* 967 * Pick up mounted filesystem 968 */ 969 mf = mp->am_mnt; 970 if (!mf) 971 continue; 972 973 #ifdef HAVE_FS_AUTOFS 974 if (mf->mf_flags & MFF_IS_AUTOFS && mp->am_autofs_ttl != NEVER) { 975 if (now >= mp->am_autofs_ttl) 976 autofs_timeout_mp(mp); 977 t = smallest_t(t, mp->am_autofs_ttl); 978 } 979 #endif /* HAVE_FS_AUTOFS */ 980 981 if (mp->am_flags & AMF_NOTIMEOUT) 982 continue; 983 984 /* 985 * Don't delete last reference to a restarted filesystem. 986 */ 987 if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1) 988 continue; 989 990 /* 991 * If there is action on this filesystem then ignore it 992 */ 993 if (!(mf->mf_flags & IGNORE_FLAGS)) { 994 int expired = 0; 995 mf->mf_flags &= ~MFF_WANTTIMO; 996 if (now >= mp->am_ttl) { 997 if (!backoff) { 998 expired = 1; 999 1000 /* 1001 * Move the ttl forward to avoid thrashing effects 1002 * on the next call to timeout! 1003 */ 1004 /* sun's -tw option */ 1005 if (mp->am_timeo_w < 4 * gopt.am_timeo_w) 1006 mp->am_timeo_w += gopt.am_timeo_w; 1007 mp->am_ttl = now + mp->am_timeo_w; 1008 1009 } else { 1010 /* 1011 * Just backoff this unmount for 1012 * a couple of seconds to avoid 1013 * many multiple unmounts being 1014 * started in parallel. 1015 */ 1016 mp->am_ttl = now + backoff + 1; 1017 } 1018 } 1019 1020 /* 1021 * If the next ttl is smallest, use that 1022 */ 1023 t = smallest_t(t, mp->am_ttl); 1024 1025 if (!mp->am_child && mf->mf_error >= 0 && expired) { 1026 /* 1027 * If the unmount was backgrounded then 1028 * bump the backoff counter. 1029 */ 1030 if (unmount_mp(mp)) { 1031 backoff = 2; 1032 } 1033 } 1034 } else if (mf->mf_flags & MFF_UNMOUNTING) { 1035 mf->mf_flags |= MFF_WANTTIMO; 1036 } 1037 } 1038 1039 if (t == NEVER) { 1040 dlog("No further timeouts"); 1041 t = now + ONE_HOUR; 1042 } 1043 1044 /* 1045 * Sanity check to avoid runaways. 1046 * Absolutely should never get this but 1047 * if you do without this trap amd will thrash. 1048 */ 1049 if (t <= now) { 1050 t = now + 6; /* XXX */ 1051 plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!"); 1052 } 1053 1054 /* 1055 * XXX - when shutting down, make things happen faster 1056 */ 1057 if ((int) amd_state >= (int) Finishing) 1058 t = now + 1; 1059 dlog("Next mount timeout in %lds", (long) (t - now)); 1060 1061 timeout_mp_id = timeout(t - now, timeout_mp, NULL); 1062 } 1063 1064 1065 /* 1066 * Cause timeout_mp to be called soonest 1067 */ 1068 void 1069 reschedule_timeout_mp(void) 1070 { 1071 if (timeout_mp_id) 1072 untimeout(timeout_mp_id); 1073 timeout_mp_id = timeout(0, timeout_mp, NULL); 1074 } 1075