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