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