amfs_auto.c revision 82794
1/* 2 * Copyright (c) 1997-2001 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 * %W% (Berkeley) %G% 40 * 41 * $Id: amfs_auto.c,v 1.9.2.7 2001/04/14 21:08:19 ezk Exp $ 42 * 43 */ 44 45/* 46 * Automount file system 47 */ 48 49#ifdef HAVE_CONFIG_H 50# include <config.h> 51#endif /* HAVE_CONFIG_H */ 52#include <am_defs.h> 53#include <amd.h> 54 55/**************************************************************************** 56 *** MACROS *** 57 ****************************************************************************/ 58#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING) 59 60#define DOT_DOT_COOKIE (u_int) 1 61 62/**************************************************************************** 63 *** STRUCTURES *** 64 ****************************************************************************/ 65 66 67/**************************************************************************** 68 *** FORWARD DEFINITIONS *** 69 ****************************************************************************/ 70static int amfs_auto_bgmount(struct continuation *cp, int mpe); 71static int amfs_auto_mount(am_node *mp); 72static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable); 73static void amfs_auto_umounted(am_node *mp); 74 75 76/**************************************************************************** 77 *** OPS STRUCTURES *** 78 ****************************************************************************/ 79am_ops amfs_auto_ops = 80{ 81 "auto", 82 amfs_auto_match, 83 0, /* amfs_auto_init */ 84 amfs_auto_mount, 85 0, 86 amfs_auto_umount, 87 0, 88 amfs_auto_lookuppn, 89 amfs_auto_readdir, 90 0, /* amfs_auto_readlink */ 91 0, /* amfs_auto_mounted */ 92 amfs_auto_umounted, 93 find_amfs_auto_srvr, 94 FS_AMQINFO | FS_DIRECTORY 95}; 96 97 98/**************************************************************************** 99 *** FUNCTIONS *** 100 ****************************************************************************/ 101/* 102 * AMFS_AUTO needs nothing in particular. 103 */ 104char * 105amfs_auto_match(am_opts *fo) 106{ 107 char *p = fo->opt_rfs; 108 109 if (!fo->opt_rfs) { 110 plog(XLOG_USER, "auto: no mount point named (rfs:=)"); 111 return 0; 112 } 113 if (!fo->opt_fs) { 114 plog(XLOG_USER, "auto: no map named (fs:=)"); 115 return 0; 116 } 117 118 /* 119 * Swap round fs:= and rfs:= options 120 * ... historical (jsp) 121 */ 122 fo->opt_rfs = fo->opt_fs; 123 fo->opt_fs = p; 124 125 /* 126 * mtab entry turns out to be the name of the mount map 127 */ 128 return strdup(fo->opt_rfs ? fo->opt_rfs : "."); 129} 130 131 132 133 134/* 135 * Build a new map cache for this node, or re-use 136 * an existing cache for the same map. 137 */ 138void 139amfs_auto_mkcacheref(mntfs *mf) 140{ 141 char *cache; 142 143 if (mf->mf_fo && mf->mf_fo->opt_cache) 144 cache = mf->mf_fo->opt_cache; 145 else 146 cache = "none"; 147 mf->mf_private = (voidp) mapc_find(mf->mf_info, cache, 148 mf->mf_fo->opt_maptype); 149 mf->mf_prfree = mapc_free; 150} 151 152 153/* 154 * Mount a sub-mount 155 */ 156static int 157amfs_auto_mount(am_node *mp) 158{ 159 mntfs *mf = mp->am_mnt; 160 161 /* 162 * Pseudo-directories are used to provide some structure 163 * to the automounted directories instead 164 * of putting them all in the top-level automount directory. 165 * 166 * Here, just increment the parent's link count. 167 */ 168 mp->am_parent->am_fattr.na_nlink++; 169 170 /* 171 * Info field of . means use parent's info field. 172 * Historical - not documented. 173 */ 174 if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0') 175 mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info); 176 177 /* 178 * Compute prefix: 179 * 180 * If there is an option prefix then use that else 181 * If the parent had a prefix then use that with name 182 * of this node appended else 183 * Use the name of this node. 184 * 185 * That means if you want no prefix you must say so 186 * in the map. 187 */ 188 if (mf->mf_fo->opt_pref) { 189 /* allow pref:=null to set a real null prefix */ 190 if (STREQ(mf->mf_fo->opt_pref, "null")) { 191 mp->am_pref = ""; 192 } else { 193 /* 194 * the prefix specified as an option 195 */ 196 mp->am_pref = strdup(mf->mf_fo->opt_pref); 197 } 198 } else { 199 /* 200 * else the parent's prefix 201 * followed by the name 202 * followed by / 203 */ 204 char *ppref = mp->am_parent->am_pref; 205 if (ppref == 0) 206 ppref = ""; 207 mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/"); 208 } 209 210 /* 211 * Attach a map cache 212 */ 213 amfs_auto_mkcacheref(mf); 214 215 return 0; 216} 217 218 219 220 221/* 222 * Unmount an automount sub-node 223 */ 224int 225amfs_auto_umount(am_node *mp) 226{ 227 return 0; 228} 229 230 231/* 232 * Unmount an automount node 233 */ 234static void 235amfs_auto_umounted(am_node *mp) 236{ 237 /* 238 * If this is a pseudo-directory then just adjust the link count 239 * in the parent, otherwise call the generic unmount routine 240 */ 241 if (mp->am_parent && mp->am_parent->am_parent) 242 --mp->am_parent->am_fattr.na_nlink; 243} 244 245 246/* 247 * Discard an old continuation 248 */ 249void 250free_continuation(struct continuation *cp) 251{ 252 if (cp->callout) 253 untimeout(cp->callout); 254 XFREE(cp->key); 255 XFREE(cp->xivec); 256 XFREE(cp->info); 257 XFREE(cp->auto_opts); 258 XFREE(cp->def_opts); 259 free_opts(&cp->fs_opts); 260 XFREE(cp); 261} 262 263 264/* 265 * Discard the underlying mount point and replace 266 * with a reference to an error filesystem. 267 */ 268void 269assign_error_mntfs(am_node *mp) 270{ 271 if (mp->am_error > 0) { 272 /* 273 * Save the old error code 274 */ 275 int error = mp->am_error; 276 if (error <= 0) 277 error = mp->am_mnt->mf_error; 278 /* 279 * Discard the old filesystem 280 */ 281 free_mntfs(mp->am_mnt); 282 /* 283 * Allocate a new error reference 284 */ 285 mp->am_mnt = new_mntfs(); 286 /* 287 * Put back the error code 288 */ 289 mp->am_mnt->mf_error = error; 290 mp->am_mnt->mf_flags |= MFF_ERROR; 291 /* 292 * Zero the error in the mount point 293 */ 294 mp->am_error = 0; 295 } 296} 297 298 299/* 300 * The continuation function. This is called by 301 * the task notifier when a background mount attempt 302 * completes. 303 */ 304void 305amfs_auto_cont(int rc, int term, voidp closure) 306{ 307 struct continuation *cp = (struct continuation *) closure; 308 mntfs *mf = cp->mp->am_mnt; 309 310 /* 311 * Definitely not trying to mount at the moment 312 */ 313 mf->mf_flags &= ~MFF_MOUNTING; 314 315 /* 316 * While we are mounting - try to avoid race conditions 317 */ 318 new_ttl(cp->mp); 319 320 /* 321 * Wakeup anything waiting for this mount 322 */ 323 wakeup((voidp) mf); 324 325 /* 326 * Check for termination signal or exit status... 327 */ 328 if (rc || term) { 329 am_node *xmp; 330 331 if (term) { 332 /* 333 * Not sure what to do for an error code. 334 */ 335 mf->mf_error = EIO; /* XXX ? */ 336 mf->mf_flags |= MFF_ERROR; 337 plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term); 338 } else { 339 /* 340 * Check for exit status... 341 */ 342 mf->mf_error = rc; 343 mf->mf_flags |= MFF_ERROR; 344 errno = rc; /* XXX */ 345 if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx")) 346 plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path); 347 } 348 349 /* 350 * If we get here then that attempt didn't work, so 351 * move the info vector pointer along by one and 352 * call the background mount routine again 353 */ 354 amd_stats.d_merr++; 355 cp->ivec++; 356 xmp = cp->mp; 357 (void) amfs_auto_bgmount(cp, 0); 358 assign_error_mntfs(xmp); 359 } else { 360 /* 361 * The mount worked. 362 */ 363 am_mounted(cp->mp); 364 free_continuation(cp); 365 } 366 367 reschedule_timeout_mp(); 368} 369 370 371/* 372 * Retry a mount 373 */ 374void 375amfs_auto_retry(int rc, int term, voidp closure) 376{ 377 struct continuation *cp = (struct continuation *) closure; 378 int error = 0; 379 380#ifdef DEBUG 381 dlog("Commencing retry for mount of %s", cp->mp->am_path); 382#endif /* DEBUG */ 383 384 new_ttl(cp->mp); 385 386 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) { 387 /* 388 * The entire mount has timed out. Set the error code and skip past all 389 * the info vectors so that amfs_auto_bgmount will not have any more 390 * ways to try the mount, so causing an error. 391 */ 392 plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path); 393 error = ETIMEDOUT; 394 while (*cp->ivec) 395 cp->ivec++; 396 /* explicitly forbid further retries after timeout */ 397 cp->retry = FALSE; 398 } 399 if (error || !IN_PROGRESS(cp)) { 400 (void) amfs_auto_bgmount(cp, error); 401 } 402 reschedule_timeout_mp(); 403} 404 405 406/* 407 * Try to mount a file system. Can be called 408 * directly or in a sub-process by run_task. 409 */ 410int 411try_mount(voidp mvp) 412{ 413 int error = 0; 414 am_node *mp = (am_node *) mvp; 415 mntfs *mf = mp->am_mnt; 416 417 /* 418 * If the directory is not yet made and it needs to be made, then make it! 419 * This may be run in a background process in which case the flag setting 420 * won't be noticed later - but it is set anyway just after run_task is 421 * called. It should probably go away totally... 422 */ 423 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) { 424 error = mkdirs(mf->mf_mount, 0555); 425 if (!error) 426 mf->mf_flags |= MFF_MKMNT; 427 } 428 429 /* 430 * Mount it! 431 */ 432 error = mount_node(mp); 433 434#ifdef DEBUG 435 if (error > 0) { 436 errno = error; 437 dlog("amfs_auto call to mount_node failed: %m"); 438 } 439#endif /* DEBUG */ 440 441 return error; 442} 443 444 445/* 446 * Pick a file system to try mounting and 447 * do that in the background if necessary 448 * 449For each location: 450 if it is new -defaults then 451 extract and process 452 continue; 453 fi 454 if it is a cut then 455 if a location has been tried then 456 break; 457 fi 458 continue; 459 fi 460 parse mount location 461 discard previous mount location if required 462 find matching mounted filesystem 463 if not applicable then 464 this_error = No such file or directory 465 continue 466 fi 467 if the filesystem failed to be mounted then 468 this_error = error from filesystem 469 elif the filesystem is mounting or unmounting then 470 this_error = -1 471 elif the fileserver is down then 472 this_error = -1 473 elif the filesystem is already mounted 474 this_error = 0 475 break 476 fi 477 if no error on this mount then 478 this_error = initialize mount point 479 fi 480 if no error on this mount and mount is delayed then 481 this_error = -1 482 fi 483 if this_error < 0 then 484 retry = true 485 fi 486 if no error on this mount then 487 make mount point if required 488 fi 489 if no error on this mount then 490 if mount in background then 491 run mount in background 492 return -1 493 else 494 this_error = mount in foreground 495 fi 496 fi 497 if an error occurred on this mount then 498 update stats 499 save error in mount point 500 fi 501endfor 502 */ 503static int 504amfs_auto_bgmount(struct continuation *cp, int mpe) 505{ 506 mntfs *mf = cp->mp->am_mnt; /* Current mntfs */ 507 mntfs *mf_retry = 0; /* First mntfs which needed retrying */ 508 int this_error = -1; /* Per-mount error */ 509 int hard_error = -1; 510 int mp_error = mpe; 511 512 /* 513 * Try to mount each location. 514 * At the end: 515 * hard_error == 0 indicates something was mounted. 516 * hard_error > 0 indicates everything failed with a hard error 517 * hard_error < 0 indicates nothing could be mounted now 518 */ 519 for (; this_error && *cp->ivec; cp->ivec++) { 520 am_ops *p; 521 am_node *mp = cp->mp; 522 char *link_dir; 523 int dont_retry; 524 525 if (hard_error < 0) 526 hard_error = this_error; 527 528 this_error = -1; 529 530 if (**cp->ivec == '-') { 531 /* 532 * Pick up new defaults 533 */ 534 if (cp->auto_opts && *cp->auto_opts) 535 cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1); 536 else 537 cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1); 538#ifdef DEBUG 539 dlog("Setting def_opts to \"%s\"", cp->def_opts); 540#endif /* DEBUG */ 541 continue; 542 } 543 /* 544 * If a mount has been attempted, and we find 545 * a cut then don't try any more locations. 546 */ 547 if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) { 548 if (cp->tried) { 549#ifdef DEBUG 550 dlog("Cut: not trying any more locations for %s", 551 mp->am_path); 552#endif /* DEBUG */ 553 break; 554 } 555 continue; 556 } 557 558 /* match the operators */ 559 p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info); 560 561 /* 562 * Find a mounted filesystem for this node. 563 */ 564 mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, 565 cp->fs_opts.opt_fs, 566 cp->fs_opts.fs_mtab, 567 cp->auto_opts, 568 cp->fs_opts.opt_opts, 569 cp->fs_opts.opt_remopts); 570 571 p = mf->mf_ops; 572#ifdef DEBUG 573 dlog("Got a hit with %s", p->fs_type); 574#endif /* DEBUG */ 575 576 /* 577 * Note whether this is a real mount attempt 578 */ 579 if (p == &amfs_error_ops) { 580 plog(XLOG_MAP, "Map entry %s for %s did not match", *cp->ivec, mp->am_path); 581 if (this_error <= 0) 582 this_error = ENOENT; 583 continue; 584 } else { 585 if (cp->fs_opts.fs_mtab) { 586 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s", 587 cp->fs_opts.fs_mtab, mp->am_path, p->fs_type); 588 } 589 cp->tried = TRUE; 590 } 591 592 this_error = 0; 593 dont_retry = FALSE; 594 595 if (mp->am_link) { 596 XFREE(mp->am_link); 597 mp->am_link = 0; 598 } 599 link_dir = mf->mf_fo->opt_sublink; 600 601 if (link_dir && *link_dir) { 602 if (*link_dir == '/') { 603 mp->am_link = strdup(link_dir); 604 } else { 605 /* 606 * Try getting fs option from continuation, not mountpoint! 607 * Don't try logging the string from mf, since it may be bad! 608 */ 609 if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs) 610 plog(XLOG_ERROR, "use %s instead of 0x%lx", 611 cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs); 612 613 mp->am_link = str3cat((char *) 0, 614 cp->fs_opts.opt_fs, "/", link_dir); 615 616 normalize_slash(mp->am_link); 617 } 618 } 619 620 if (mf->mf_error > 0) { 621 this_error = mf->mf_error; 622 } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { 623 /* 624 * Still mounting - retry later 625 */ 626#ifdef DEBUG 627 dlog("Duplicate pending mount fstype %s", p->fs_type); 628#endif /* DEBUG */ 629 this_error = -1; 630 } else if (FSRV_ISDOWN(mf->mf_server)) { 631 /* 632 * Would just mount from the same place 633 * as a hung mount - so give up 634 */ 635#ifdef DEBUG 636 dlog("%s is already hung - giving up", mf->mf_mount); 637#endif /* DEBUG */ 638 mp_error = EWOULDBLOCK; 639 dont_retry = TRUE; 640 this_error = -1; 641 } else if (mf->mf_flags & MFF_MOUNTED) { 642#ifdef DEBUG 643 dlog("duplicate mount of \"%s\" ...", mf->mf_info); 644#endif /* DEBUG */ 645 646 /* 647 * Just call mounted() 648 */ 649 am_mounted(mp); 650 651 this_error = 0; 652 break; 653 } 654 655 /* 656 * Will usually need to play around with the mount nodes 657 * file attribute structure. This must be done here. 658 * Try and get things initialized, even if the fileserver 659 * is not known to be up. In the common case this will 660 * progress things faster. 661 */ 662 if (!this_error) { 663 /* 664 * Fill in attribute fields. 665 */ 666 if (mf->mf_ops->fs_flags & FS_DIRECTORY) 667 mk_fattr(mp, NFDIR); 668 else 669 mk_fattr(mp, NFLNK); 670 671 if (p->fs_init) 672 this_error = (*p->fs_init) (mf); 673 } 674 675 /* 676 * Make sure the fileserver is UP before doing any more work 677 */ 678 if (!FSRV_ISUP(mf->mf_server)) { 679#ifdef DEBUG 680 dlog("waiting for server %s to become available", mf->mf_server->fs_host); 681#endif /* DEBUG */ 682 this_error = -1; 683 } 684 685 if (!this_error && mf->mf_fo->opt_delay) { 686 /* 687 * If there is a delay timer on the mount 688 * then don't try to mount if the timer 689 * has not expired. 690 */ 691 int i = atoi(mf->mf_fo->opt_delay); 692 if (i > 0 && clocktime() < (cp->start + i)) { 693#ifdef DEBUG 694 dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - clocktime() + cp->start)); 695#endif /* DEBUG */ 696 this_error = -1; 697 } 698 } 699 700 if (this_error < 0 && !dont_retry) { 701 if (!mf_retry) 702 mf_retry = dup_mntfs(mf); 703 cp->retry = TRUE; 704#ifdef DEBUG 705 dlog("will retry ...\n"); 706#endif /* DEBUG */ 707 break; 708 } 709 710 if (!this_error) { 711 if (p->fs_flags & FS_MBACKGROUND) { 712 mf->mf_flags |= MFF_MOUNTING; /* XXX */ 713#ifdef DEBUG 714 dlog("backgrounding mount of \"%s\"", mf->mf_mount); 715#endif /* DEBUG */ 716 if (cp->callout) { 717 untimeout(cp->callout); 718 cp->callout = 0; 719 } 720 721 /* actually run the task, backgrounding as necessary */ 722 run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp); 723 724 mf->mf_flags |= MFF_MKMNT; /* XXX */ 725 if (mf_retry) 726 free_mntfs(mf_retry); 727 return -1; 728 } else { 729#ifdef DEBUG 730 dlog("foreground mount of \"%s\" ...", mf->mf_info); 731#endif /* DEBUG */ 732 this_error = try_mount((voidp) mp); 733 if (this_error < 0) { 734 if (!mf_retry) 735 mf_retry = dup_mntfs(mf); 736 cp->retry = TRUE; 737 } 738 } 739 } 740 741 if (this_error >= 0) { 742 if (this_error > 0) { 743 amd_stats.d_merr++; 744 if (mf != mf_retry) { 745 mf->mf_error = this_error; 746 mf->mf_flags |= MFF_ERROR; 747 } 748 } 749 750 /* 751 * Wakeup anything waiting for this mount 752 */ 753 wakeup((voidp) mf); 754 } 755 } 756 757 if (this_error && cp->retry) { 758 free_mntfs(mf); 759 mf = cp->mp->am_mnt = mf_retry; 760 /* 761 * Not retrying again (so far) 762 */ 763 cp->retry = FALSE; 764 cp->tried = FALSE; 765 /* 766 * Start at the beginning. 767 * Rewind the location vector and 768 * reset the default options. 769 */ 770#ifdef DEBUG 771 dlog("(skipping rewind)\n"); 772#endif /* DEBUG */ 773 /* 774 * Arrange that amfs_auto_bgmount is called 775 * after anything else happens. 776 */ 777#ifdef DEBUG 778 dlog("Arranging to retry mount of %s", cp->mp->am_path); 779#endif /* DEBUG */ 780 sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf); 781 if (cp->callout) 782 untimeout(cp->callout); 783 cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf); 784 785 cp->mp->am_ttl = clocktime() + RETRY_INTERVAL; 786 787 /* 788 * Not done yet - so don't return anything 789 */ 790 return -1; 791 } 792 793 if (hard_error < 0 || this_error == 0) 794 hard_error = this_error; 795 796 /* 797 * Discard handle on duff filesystem. 798 * This should never happen since it 799 * should be caught by the case above. 800 */ 801 if (mf_retry) { 802 if (hard_error) 803 plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount); 804 free_mntfs(mf_retry); 805 } 806 807 /* 808 * If we get here, then either the mount succeeded or 809 * there is no more mount information available. 810 */ 811 if (hard_error < 0 && mp_error) 812 hard_error = cp->mp->am_error = mp_error; 813 if (hard_error > 0) { 814 /* 815 * Set a small(ish) timeout on an error node if 816 * the error was not a time out. 817 */ 818 switch (hard_error) { 819 case ETIMEDOUT: 820 case EWOULDBLOCK: 821 cp->mp->am_timeo = 17; 822 break; 823 default: 824 cp->mp->am_timeo = 5; 825 break; 826 } 827 new_ttl(cp->mp); 828 } 829 830 /* 831 * Make sure that the error value in the mntfs has a 832 * reasonable value. 833 */ 834 if (mf->mf_error < 0) { 835 mf->mf_error = hard_error; 836 if (hard_error) 837 mf->mf_flags |= MFF_ERROR; 838 } 839 840 /* 841 * In any case we don't need the continuation any more 842 */ 843 free_continuation(cp); 844 845 return hard_error; 846} 847 848 849/* 850 * Automount interface to RPC lookup routine 851 * Find the corresponding entry and return 852 * the file handle for it. 853 */ 854am_node * 855amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op) 856{ 857 am_node *ap, *new_mp, *ap_hung; 858 char *info; /* Mount info - where to get the file system */ 859 char **ivec, **xivec; /* Split version of info */ 860 char *auto_opts; /* Automount options */ 861 int error = 0; /* Error so far */ 862 char path_name[MAXPATHLEN]; /* General path name buffer */ 863 char *pfname; /* Path for database lookup */ 864 struct continuation *cp; /* Continuation structure if need to mount */ 865 int in_progress = 0; /* # of (un)mount in progress */ 866 char *dflts; 867 mntfs *mf; 868 869#ifdef DEBUG 870 dlog("in amfs_auto_lookuppn"); 871#endif /* DEBUG */ 872 873 /* 874 * If the server is shutting down 875 * then don't return information 876 * about the mount point. 877 */ 878 if (amd_state == Finishing) { 879#ifdef DEBUG 880 if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) { 881 dlog("%s mount ignored - going down", fname); 882 } else { 883 dlog("%s/%s mount ignored - going down", mp->am_path, fname); 884 } 885#endif /* DEBUG */ 886 ereturn(ENOENT); 887 } 888 889 /* 890 * Handle special case of "." and ".." 891 */ 892 if (fname[0] == '.') { 893 if (fname[1] == '\0') 894 return mp; /* "." is the current node */ 895 if (fname[1] == '.' && fname[2] == '\0') { 896 if (mp->am_parent) { 897#ifdef DEBUG 898 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); 899#endif /* DEBUG */ 900 return mp->am_parent; /* ".." is the parent node */ 901 } 902 ereturn(ESTALE); 903 } 904 } 905 906 /* 907 * Check for valid key name. 908 * If it is invalid then pretend it doesn't exist. 909 */ 910 if (!valid_key(fname)) { 911 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); 912 ereturn(ENOENT); 913 } 914 915 /* 916 * Expand key name. 917 * fname is now a private copy. 918 */ 919 fname = expand_key(fname); 920 921 for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) { 922 /* 923 * Otherwise search children of this node 924 */ 925 if (FSTREQ(ap->am_name, fname)) { 926 mf = ap->am_mnt; 927 if (ap->am_error) { 928 error = ap->am_error; 929 continue; 930 } 931 /* 932 * If the error code is undefined then it must be 933 * in progress. 934 */ 935 if (mf->mf_error < 0) 936 goto in_progrss; 937 938 /* 939 * Check for a hung node 940 */ 941 if (FSRV_ISDOWN(mf->mf_server)) { 942#ifdef DEBUG 943 dlog("server hung"); 944#endif /* DEBUG */ 945 error = ap->am_error; 946 ap_hung = ap; 947 continue; 948 } 949 /* 950 * If there was a previous error with this node 951 * then return that error code. 952 */ 953 if (mf->mf_flags & MFF_ERROR) { 954 error = mf->mf_error; 955 continue; 956 } 957 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) { 958 in_progrss: 959 /* 960 * If the fs is not mounted or it is unmounting then there 961 * is a background (un)mount in progress. In this case 962 * we just drop the RPC request (return nil) and 963 * wait for a retry, by which time the (un)mount may 964 * have completed. 965 */ 966#ifdef DEBUG 967 dlog("ignoring mount of %s in %s -- flags (%x) in progress", 968 fname, mf->mf_mount, mf->mf_flags); 969#endif /* DEBUG */ 970 in_progress++; 971 continue; 972 } 973 974 /* 975 * Otherwise we have a hit: return the current mount point. 976 */ 977#ifdef DEBUG 978 dlog("matched %s in %s", fname, ap->am_path); 979#endif /* DEBUG */ 980 XFREE(fname); 981 return ap; 982 } 983 } 984 985 if (in_progress) { 986#ifdef DEBUG 987 dlog("Waiting while %d mount(s) in progress", in_progress); 988#endif /* DEBUG */ 989 XFREE(fname); 990 ereturn(-1); 991 } 992 993 /* 994 * If an error occurred then return it. 995 */ 996 if (error) { 997#ifdef DEBUG 998 errno = error; /* XXX */ 999 dlog("Returning error: %m"); 1000#endif /* DEBUG */ 1001 XFREE(fname); 1002 ereturn(error); 1003 } 1004 1005 /* 1006 * If doing a delete then don't create again! 1007 */ 1008 switch (op) { 1009 case VLOOK_DELETE: 1010 ereturn(ENOENT); 1011 1012 case VLOOK_CREATE: 1013 break; 1014 1015 default: 1016 plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op); 1017 ereturn(EINVAL); 1018 } 1019 1020 /* 1021 * If the server is going down then just return, 1022 * don't try to mount any more file systems 1023 */ 1024 if ((int) amd_state >= (int) Finishing) { 1025#ifdef DEBUG 1026 dlog("not found - server going down anyway"); 1027#endif /* DEBUG */ 1028 XFREE(fname); 1029 ereturn(ENOENT); 1030 } 1031 1032 /* 1033 * If we get there then this is a reference to an, 1034 * as yet, unknown name so we need to search the mount 1035 * map for it. 1036 */ 1037 if (mp->am_pref) { 1038 sprintf(path_name, "%s%s", mp->am_pref, fname); 1039 pfname = path_name; 1040 } else { 1041 pfname = fname; 1042 } 1043 1044 mf = mp->am_mnt; 1045 1046#ifdef DEBUG 1047 dlog("will search map info in %s to find %s", mf->mf_info, pfname); 1048#endif /* DEBUG */ 1049 /* 1050 * Consult the oracle for some mount information. 1051 * info is malloc'ed and belongs to this routine. 1052 * It ends up being free'd in free_continuation(). 1053 * 1054 * Note that this may return -1 indicating that information 1055 * is not yet available. 1056 */ 1057 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); 1058 if (error) { 1059 if (error > 0) 1060 plog(XLOG_MAP, "No map entry for %s", pfname); 1061 else 1062 plog(XLOG_MAP, "Waiting on map entry for %s", pfname); 1063 XFREE(fname); 1064 ereturn(error); 1065 } 1066#ifdef DEBUG 1067 dlog("mount info is %s", info); 1068#endif /* DEBUG */ 1069 1070 /* 1071 * Split info into an argument vector. 1072 * The vector is malloc'ed and belongs to 1073 * this routine. It is free'd in free_continuation() 1074 */ 1075 xivec = ivec = strsplit(info, ' ', '\"'); 1076 1077 /* 1078 * Default error code... 1079 */ 1080 if (ap_hung) 1081 error = EWOULDBLOCK; 1082 else 1083 error = ENOENT; 1084 1085 /* 1086 * Allocate a new map 1087 */ 1088 new_mp = exported_ap_alloc(); 1089 if (new_mp == 0) { 1090 XFREE(xivec); 1091 XFREE(info); 1092 XFREE(fname); 1093 ereturn(ENOSPC); 1094 } 1095 if (mf->mf_auto) 1096 auto_opts = mf->mf_auto; 1097 else 1098 auto_opts = ""; 1099 1100 auto_opts = strdup(auto_opts); 1101 1102#ifdef DEBUG 1103 dlog("searching for /defaults entry"); 1104#endif /* DEBUG */ 1105 if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) { 1106 char *dfl; 1107 char **rvec; 1108#ifdef DEBUG 1109 dlog("/defaults gave %s", dflts); 1110#endif /* DEBUG */ 1111 if (*dflts == '-') 1112 dfl = dflts + 1; 1113 else 1114 dfl = dflts; 1115 1116 /* 1117 * Chop the defaults up 1118 */ 1119 rvec = strsplit(dfl, ' ', '\"'); 1120 1121 if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) { 1122 /* 1123 * Pick whichever first entry matched the list of selectors. 1124 * Strip the selectors from the string, and assign to dfl the 1125 * rest of the string. 1126 */ 1127 if (rvec) { 1128 am_opts ap; 1129 am_ops *pt; 1130 char **sp = rvec; 1131 while (*sp) { /* loop until you find something, if any */ 1132 memset((char *) &ap, 0, sizeof(am_opts)); 1133 /* 1134 * This next routine cause many spurious "expansion of ... is" 1135 * messages, which are ignored, b/c all we need out of this 1136 * routine is to match selectors. These spurious messages may 1137 * be wrong, esp. if they try to expand ${key} b/c it will 1138 * get expanded to "/defaults" 1139 */ 1140 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", 1141 mp->am_parent->am_mnt->mf_info); 1142 free_opts(&ap); /* don't leak */ 1143 if (pt == &amfs_error_ops) { 1144 plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp); 1145 } else { 1146 dfl = strip_selectors(*sp, "/defaults"); 1147 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); 1148 break; 1149 } 1150 ++sp; 1151 } 1152 } 1153 } else { /* not enable_default_selectors */ 1154 /* 1155 * Extract first value 1156 */ 1157 dfl = rvec[0]; 1158 } 1159 1160 /* 1161 * If there were any values at all... 1162 */ 1163 if (dfl) { 1164 /* 1165 * Log error if there were other values 1166 */ 1167 if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) { 1168# ifdef DEBUG 1169 dlog("/defaults chopped into %s", dfl); 1170# endif /* DEBUG */ 1171 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); 1172 } 1173 1174 /* 1175 * Prepend to existing defaults if they exist, 1176 * otherwise just use these defaults. 1177 */ 1178 if (*auto_opts && *dfl) { 1179 char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2); 1180 sprintf(nopts, "%s;%s", dfl, auto_opts); 1181 XFREE(auto_opts); 1182 auto_opts = nopts; 1183 } else if (*dfl) { 1184 auto_opts = strealloc(auto_opts, dfl); 1185 } 1186 } 1187 XFREE(dflts); 1188 /* 1189 * Don't need info vector any more 1190 */ 1191 XFREE(rvec); 1192 } 1193 1194 /* 1195 * Fill it in 1196 */ 1197 init_map(new_mp, fname); 1198 1199 /* 1200 * Put it in the table 1201 */ 1202 insert_am(new_mp, mp); 1203 1204 /* 1205 * Fill in some other fields, 1206 * path and mount point. 1207 * 1208 * bugfix: do not prepend old am_path if direct map 1209 * <wls@astro.umd.edu> William Sebok 1210 */ 1211 new_mp->am_path = str3cat(new_mp->am_path, 1212 mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path, 1213 *fname == '/' ? "" : "/", fname); 1214 1215#ifdef DEBUG 1216 dlog("setting path to %s", new_mp->am_path); 1217#endif /* DEBUG */ 1218 1219 /* 1220 * Take private copy of pfname 1221 */ 1222 pfname = strdup(pfname); 1223 1224 /* 1225 * Construct a continuation 1226 */ 1227 cp = ALLOC(struct continuation); 1228 cp->callout = 0; 1229 cp->mp = new_mp; 1230 cp->xivec = xivec; 1231 cp->ivec = ivec; 1232 cp->info = info; 1233 cp->key = pfname; 1234 cp->auto_opts = auto_opts; 1235 cp->retry = FALSE; 1236 cp->tried = FALSE; 1237 cp->start = clocktime(); 1238 cp->def_opts = strdup(auto_opts); 1239 memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts)); 1240 1241 /* 1242 * Try and mount the file system. If this succeeds immediately (possible 1243 * for a ufs file system) then return the attributes, otherwise just 1244 * return an error. 1245 */ 1246 error = amfs_auto_bgmount(cp, error); 1247 reschedule_timeout_mp(); 1248 if (!error) { 1249 XFREE(fname); 1250 return new_mp; 1251 } 1252 1253 /* 1254 * Code for quick reply. If nfs_program_2_transp is set, then 1255 * its the transp that's been passed down from nfs_program_2(). 1256 * If new_mp->am_transp is not already set, set it by copying in 1257 * nfs_program_2_transp. Once am_transp is set, quick_reply() can 1258 * use it to send a reply to the client that requested this mount. 1259 */ 1260 if (nfs_program_2_transp && !new_mp->am_transp) { 1261 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); 1262 *(new_mp->am_transp) = *nfs_program_2_transp; 1263 } 1264 if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) 1265 new_mp->am_error = error; 1266 1267 assign_error_mntfs(new_mp); 1268 1269 XFREE(fname); 1270 1271 ereturn(error); 1272} 1273 1274 1275/* 1276 * Locate next node in sibling list which is mounted 1277 * and is not an error node. 1278 */ 1279am_node * 1280next_nonerror_node(am_node *xp) 1281{ 1282 mntfs *mf; 1283 1284 /* 1285 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> 1286 * Fixes a race condition when mounting direct automounts. 1287 * Also fixes a problem when doing a readdir on a directory 1288 * containing hung automounts. 1289 */ 1290 while (xp && 1291 (!(mf = xp->am_mnt) || /* No mounted filesystem */ 1292 mf->mf_error != 0 || /* There was a mntfs error */ 1293 xp->am_error != 0 || /* There was a mount error */ 1294 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ 1295 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ 1296 ) 1297 xp = xp->am_osib; 1298 1299 return xp; 1300} 1301 1302 1303/* 1304 * This readdir function which call a special version of it that allows 1305 * browsing if browsable_dirs=yes was set on the map. 1306 */ 1307int 1308amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count) 1309{ 1310 u_int gen = *(u_int *) cookie; 1311 am_node *xp; 1312 mntent_t mnt; 1313#ifdef DEBUG 1314 nfsentry *ne; 1315 static int j; 1316#endif /* DEBUG */ 1317 1318 dp->dl_eof = FALSE; /* assume readdir not done */ 1319 1320 /* check if map is browsable */ 1321 if (mp->am_mnt && mp->am_mnt->mf_mopts) { 1322 mnt.mnt_opts = mp->am_mnt->mf_mopts; 1323 if (hasmntopt(&mnt, "fullybrowsable")) 1324 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE); 1325 if (hasmntopt(&mnt, "browsable")) 1326 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE); 1327 } 1328 1329 /* when gen is 0, we start reading from the beginning of the directory */ 1330 if (gen == 0) { 1331 /* 1332 * In the default instance (which is used to start a search) we return 1333 * "." and "..". 1334 * 1335 * This assumes that the count is big enough to allow both "." and ".." 1336 * to be returned in a single packet. If it isn't (which would be 1337 * fairly unbelievable) then tough. 1338 */ 1339#ifdef DEBUG 1340 dlog("amfs_auto_readdir: default search"); 1341#endif /* DEBUG */ 1342 /* 1343 * Check for enough room. This is extremely approximate but is more 1344 * than enough space. Really need 2 times: 1345 * 4byte fileid 1346 * 4byte cookie 1347 * 4byte name length 1348 * 4byte name 1349 * plus the dirlist structure */ 1350 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 1351 return EINVAL; 1352 1353 xp = next_nonerror_node(mp->am_child); 1354 dp->dl_entries = ep; 1355 1356 /* construct "." */ 1357 ep[0].ne_fileid = mp->am_gen; 1358 ep[0].ne_name = "."; 1359 ep[0].ne_nextentry = &ep[1]; 1360 *(u_int *) ep[0].ne_cookie = 0; 1361 1362 /* construct ".." */ 1363 if (mp->am_parent) 1364 ep[1].ne_fileid = mp->am_parent->am_gen; 1365 else 1366 ep[1].ne_fileid = mp->am_gen; 1367 ep[1].ne_name = ".."; 1368 ep[1].ne_nextentry = 0; 1369 *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE); 1370 1371 if (!xp) 1372 dp->dl_eof = TRUE; /* by default assume readdir done */ 1373 1374#ifdef DEBUG 1375 amuDebug(D_READDIR) 1376 for (j=0,ne=ep; ne; ne=ne->ne_nextentry) 1377 plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d", 1378 j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); 1379#endif /* DEBUG */ 1380 return 0; 1381 } 1382#ifdef DEBUG 1383 dlog("amfs_auto_readdir: real child"); 1384#endif /* DEBUG */ 1385 1386 if (gen == DOT_DOT_COOKIE) { 1387#ifdef DEBUG 1388 dlog("amfs_auto_readdir: End of readdir in %s", mp->am_path); 1389#endif /* DEBUG */ 1390 dp->dl_eof = TRUE; 1391 dp->dl_entries = 0; 1392#ifdef DEBUG 1393 amuDebug(D_READDIR) 1394 plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n"); 1395#endif /* DEBUG */ 1396 return 0; 1397 } 1398 1399 /* non-browsable directories code */ 1400 xp = mp->am_child; 1401 while (xp && xp->am_gen != gen) 1402 xp = xp->am_osib; 1403 1404 if (xp) { 1405 int nbytes = count / 2; /* conservative */ 1406 int todo = MAX_READDIR_ENTRIES; 1407 1408 dp->dl_entries = ep; 1409 do { 1410 am_node *xp_next = next_nonerror_node(xp->am_osib); 1411 1412 if (xp_next) { 1413 *(u_int *) ep->ne_cookie = xp_next->am_gen; 1414 } else { 1415 *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE; 1416 dp->dl_eof = TRUE; 1417 } 1418 1419 ep->ne_fileid = xp->am_gen; 1420 ep->ne_name = xp->am_name; 1421 nbytes -= sizeof(*ep) + 1; 1422 if (xp->am_name) 1423 nbytes -= strlen(xp->am_name); 1424 1425 xp = xp_next; 1426 1427 if (nbytes > 0 && !dp->dl_eof && todo > 1) { 1428 ep->ne_nextentry = ep + 1; 1429 ep++; 1430 --todo; 1431 } else { 1432 todo = 0; 1433 } 1434 } while (todo > 0); 1435 1436 ep->ne_nextentry = 0; 1437 1438#ifdef DEBUG 1439 amuDebug(D_READDIR) 1440 for (j=0,ne=ep; ne; ne=ne->ne_nextentry) 1441 plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d", 1442 j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); 1443#endif /* DEBUG */ 1444 return 0; 1445 } 1446 return ESTALE; 1447} 1448 1449 1450/* This one is called only if map is browsable */ 1451static int 1452amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable) 1453{ 1454 u_int gen = *(u_int *) cookie; 1455 int chain_length, i; 1456 static nfsentry *te, *te_next; 1457#ifdef DEBUG 1458 nfsentry *ne; 1459 static int j; 1460#endif /* DEBUG */ 1461 1462 dp->dl_eof = FALSE; /* assume readdir not done */ 1463 1464#ifdef DEBUG 1465 amuDebug(D_READDIR) 1466 plog(XLOG_DEBUG, "amfs_auto_readdir_browsable gen=%u, count=%d", 1467 gen, count); 1468#endif /* DEBUG */ 1469 1470 if (gen == 0) { 1471 /* 1472 * In the default instance (which is used to start a search) we return 1473 * "." and "..". 1474 * 1475 * This assumes that the count is big enough to allow both "." and ".." 1476 * to be returned in a single packet. If it isn't (which would be 1477 * fairly unbelievable) then tough. 1478 */ 1479#ifdef DEBUG 1480 dlog("amfs_auto_readdir_browsable: default search"); 1481#endif /* DEBUG */ 1482 /* 1483 * Check for enough room. This is extremely approximate but is more 1484 * than enough space. Really need 2 times: 1485 * 4byte fileid 1486 * 4byte cookie 1487 * 4byte name length 1488 * 4byte name 1489 * plus the dirlist structure */ 1490 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 1491 return EINVAL; 1492 1493 /* 1494 * compute # of entries to send in this chain. 1495 * heuristics: 128 bytes per entry. 1496 * This is too much probably, but it seems to work better because 1497 * of the re-entrant nature of nfs_readdir, and esp. on systems 1498 * like OpenBSD 2.2. 1499 */ 1500 chain_length = count / 128; 1501 1502 /* reset static state counters */ 1503 te = te_next = NULL; 1504 1505 dp->dl_entries = ep; 1506 1507 /* construct "." */ 1508 ep[0].ne_fileid = mp->am_gen; 1509 ep[0].ne_name = "."; 1510 ep[0].ne_nextentry = &ep[1]; 1511 *(u_int *) ep[0].ne_cookie = 0; 1512 1513 /* construct ".." */ 1514 if (mp->am_parent) 1515 ep[1].ne_fileid = mp->am_parent->am_gen; 1516 else 1517 ep[1].ne_fileid = mp->am_gen; 1518 1519 ep[1].ne_name = ".."; 1520 ep[1].ne_nextentry = 0; 1521 *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE; 1522 1523 /* 1524 * If map is browsable, call a function make_entry_chain() to construct 1525 * a linked list of unmounted keys, and return it. Then link the chain 1526 * to the regular list. Get the chain only once, but return 1527 * chunks of it each time. 1528 */ 1529 te = make_entry_chain(mp, dp->dl_entries, fully_browsable); 1530 if (!te) 1531 return 0; 1532#ifdef DEBUG 1533 amuDebug(D_READDIR) 1534 for (j=0,ne=te; ne; ne=ne->ne_nextentry) 1535 plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name); 1536#endif /* DEBUG */ 1537 1538 /* return only "chain_length" entries */ 1539 te_next = te; 1540 for (i=1; i<chain_length; ++i) { 1541 te_next = te_next->ne_nextentry; 1542 if (!te_next) 1543 break; 1544 } 1545 if (te_next) { 1546 nfsentry *te_saved = te_next->ne_nextentry; 1547 te_next->ne_nextentry = NULL; /* terminate "te" chain */ 1548 te_next = te_saved; /* save rest of "te" for next iteration */ 1549 dp->dl_eof = FALSE; /* tell readdir there's more */ 1550 } else { 1551 dp->dl_eof = TRUE; /* tell readdir that's it */ 1552 } 1553 ep[1].ne_nextentry = te; /* append this chunk of "te" chain */ 1554#ifdef DEBUG 1555 amuDebug(D_READDIR) { 1556 for (j=0,ne=te; ne; ne=ne->ne_nextentry) 1557 plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name); 1558 for (j=0,ne=ep; ne; ne=ne->ne_nextentry) 1559 plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d", 1560 j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); 1561 plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof); 1562 } 1563#endif /* DEBUG_READDIR */ 1564 return 0; 1565 } /* end of "if (gen == 0)" statement */ 1566 1567#ifdef DEBUG 1568 dlog("amfs_auto_readdir_browsable: real child"); 1569#endif /* DEBUG */ 1570 1571 if (gen == DOT_DOT_COOKIE) { 1572#ifdef DEBUG 1573 dlog("amfs_auto_readdir_browsable: End of readdir in %s", mp->am_path); 1574#endif /* DEBUG */ 1575 dp->dl_eof = TRUE; 1576 dp->dl_entries = 0; 1577 return 0; 1578 } 1579 1580 /* 1581 * If browsable directories, then continue serving readdir() with another 1582 * chunk of entries, starting from where we left off (when gen was equal 1583 * to 0). Once again, assume last chunk served to readdir. 1584 */ 1585 dp->dl_eof = TRUE; 1586 dp->dl_entries = ep; 1587 1588 te = te_next; /* reset 'te' from last saved te_next */ 1589 if (!te) { /* another indicator of end of readdir */ 1590 dp->dl_entries = 0; 1591 return 0; 1592 } 1593 /* 1594 * compute # of entries to send in this chain. 1595 * heuristics: 128 bytes per entry. 1596 */ 1597 chain_length = count / 128; 1598 1599 /* return only "chain_length" entries */ 1600 for (i=1; i<chain_length; ++i) { 1601 te_next = te_next->ne_nextentry; 1602 if (!te_next) 1603 break; 1604 } 1605 if (te_next) { 1606 nfsentry *te_saved = te_next->ne_nextentry; 1607 te_next->ne_nextentry = NULL; /* terminate "te" chain */ 1608 te_next = te_saved; /* save rest of "te" for next iteration */ 1609 dp->dl_eof = FALSE; /* tell readdir there's more */ 1610 } 1611 ep = te; /* send next chunk of "te" chain */ 1612 dp->dl_entries = ep; 1613#ifdef DEBUG 1614 amuDebug(D_READDIR) { 1615 plog(XLOG_DEBUG, "dl_entries=0x%lx, te_next=0x%lx, dl_eof=%d", 1616 (long) dp->dl_entries, (long) te_next, dp->dl_eof); 1617 for (ne=te; ne; ne=ne->ne_nextentry) 1618 plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name); 1619 } 1620#endif /* DEBUG */ 1621 return 0; 1622} 1623 1624 1625int 1626amfs_auto_fmount(am_node *mp) 1627{ 1628 mntfs *mf = mp->am_mnt; 1629 return (*mf->mf_ops->fmount_fs) (mf); 1630} 1631 1632 1633int 1634amfs_auto_fumount(am_node *mp) 1635{ 1636 mntfs *mf = mp->am_mnt; 1637 return (*mf->mf_ops->fumount_fs) (mf); 1638} 1639