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