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