amfs_auto.c revision 41142
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 free_opts(&ap); /* don't leak */ 1132 if (pt == &amfs_error_ops) { 1133 plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp); 1134 } else { 1135 dfl = strip_selectors(*sp, "/defaults"); 1136 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); 1137 break; 1138 } 1139 ++sp; 1140 } 1141 } 1142 } else { /* not enable_default_selectors */ 1143 /* 1144 * Extract first value 1145 */ 1146 dfl = rvec[0]; 1147 } 1148 1149 /* 1150 * If there were any values at all... 1151 */ 1152 if (dfl) { 1153 /* 1154 * Log error if there were other values 1155 */ 1156 if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) { 1157# ifdef DEBUG 1158 dlog("/defaults chopped into %s", dfl); 1159# endif /* DEBUG */ 1160 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); 1161 } 1162 1163 /* 1164 * Prepend to existing defaults if they exist, 1165 * otherwise just use these defaults. 1166 */ 1167 if (*auto_opts && *dfl) { 1168 char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2); 1169 sprintf(nopts, "%s;%s", dfl, auto_opts); 1170 XFREE(auto_opts); 1171 auto_opts = nopts; 1172 } else if (*dfl) { 1173 auto_opts = strealloc(auto_opts, dfl); 1174 } 1175 } 1176 XFREE(dflts); 1177 /* 1178 * Don't need info vector any more 1179 */ 1180 XFREE(rvec); 1181 } 1182 1183 /* 1184 * Fill it in 1185 */ 1186 init_map(new_mp, fname); 1187 1188 /* 1189 * Put it in the table 1190 */ 1191 insert_am(new_mp, mp); 1192 1193 /* 1194 * Fill in some other fields, 1195 * path and mount point. 1196 * 1197 * bugfix: do not prepend old am_path if direct map 1198 * <wls@astro.umd.edu> William Sebok 1199 */ 1200 new_mp->am_path = str3cat(new_mp->am_path, 1201 mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path, 1202 *fname == '/' ? "" : "/", fname); 1203 1204#ifdef DEBUG 1205 dlog("setting path to %s", new_mp->am_path); 1206#endif /* DEBUG */ 1207 1208 /* 1209 * Take private copy of pfname 1210 */ 1211 pfname = strdup(pfname); 1212 1213 /* 1214 * Construct a continuation 1215 */ 1216 cp = ALLOC(struct continuation); 1217 cp->callout = 0; 1218 cp->mp = new_mp; 1219 cp->xivec = xivec; 1220 cp->ivec = ivec; 1221 cp->info = info; 1222 cp->key = pfname; 1223 cp->auto_opts = auto_opts; 1224 cp->retry = FALSE; 1225 cp->tried = FALSE; 1226 cp->start = clocktime(); 1227 cp->def_opts = strdup(auto_opts); 1228 memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts)); 1229 1230 /* 1231 * Try and mount the file system. If this succeeds immediately (possible 1232 * for a ufs file system) then return the attributes, otherwise just 1233 * return an error. 1234 */ 1235 error = amfs_auto_bgmount(cp, error); 1236 reschedule_timeout_mp(); 1237 if (!error) { 1238 XFREE(fname); 1239 return new_mp; 1240 } 1241 1242 /* 1243 * Code for quick reply. If nfs_program_2_transp is set, then 1244 * its the transp that's been passed down from nfs_program_2(). 1245 * If new_mp->am_transp is not already set, set it by copying in 1246 * nfs_program_2_transp. Once am_transp is set, quick_reply() can 1247 * use it to send a reply to the client that requested this mount. 1248 */ 1249 if (nfs_program_2_transp && !new_mp->am_transp) { 1250 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); 1251 *(new_mp->am_transp) = *nfs_program_2_transp; 1252 } 1253 if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) 1254 new_mp->am_error = error; 1255 1256 assign_error_mntfs(new_mp); 1257 1258 XFREE(fname); 1259 1260 ereturn(error); 1261} 1262 1263 1264/* 1265 * Locate next node in sibling list which is mounted 1266 * and is not an error node. 1267 */ 1268am_node * 1269next_nonerror_node(am_node *xp) 1270{ 1271 mntfs *mf; 1272 1273 /* 1274 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> 1275 * Fixes a race condition when mounting direct automounts. 1276 * Also fixes a problem when doing a readdir on a directory 1277 * containing hung automounts. 1278 */ 1279 while (xp && 1280 (!(mf = xp->am_mnt) || /* No mounted filesystem */ 1281 mf->mf_error != 0 || /* There was a mntfs error */ 1282 xp->am_error != 0 || /* There was a mount error */ 1283 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ 1284 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ 1285 ) 1286 xp = xp->am_osib; 1287 1288 return xp; 1289} 1290 1291 1292/* 1293 * This readdir function which call a special version of it that allows 1294 * browsing if browsable_dirs=yes was set on the map. 1295 */ 1296int 1297amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count) 1298{ 1299 u_int gen = *(u_int *) cookie; 1300 am_node *xp; 1301 mntent_t mnt; 1302 1303 dp->dl_eof = FALSE; /* assume readdir not done */ 1304 1305 /* check if map is browsable */ 1306 if (mp->am_mnt && mp->am_mnt->mf_mopts) { 1307 mnt.mnt_opts = mp->am_mnt->mf_mopts; 1308 if (hasmntopt(&mnt, "fullybrowsable")) 1309 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE); 1310 if (hasmntopt(&mnt, "browsable")) 1311 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE); 1312 } 1313 1314 if (gen == 0) { 1315 /* 1316 * In the default instance (which is used to start a search) we return 1317 * "." and "..". 1318 * 1319 * This assumes that the count is big enough to allow both "." and ".." 1320 * to be returned in a single packet. If it isn't (which would be 1321 * fairly unbelievable) then tough. 1322 */ 1323#ifdef DEBUG 1324 dlog("default search"); 1325#endif /* DEBUG */ 1326 /* 1327 * Check for enough room. This is extremely approximate but is more 1328 * than enough space. Really need 2 times: 1329 * 4byte fileid 1330 * 4byte cookie 1331 * 4byte name length 1332 * 4byte name 1333 * plus the dirlist structure */ 1334 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 1335 return EINVAL; 1336 1337 xp = next_nonerror_node(mp->am_child); 1338 dp->dl_entries = ep; 1339 1340 /* construct "." */ 1341 ep[0].ne_fileid = mp->am_gen; 1342 ep[0].ne_name = "."; 1343 ep[0].ne_nextentry = &ep[1]; 1344 *(u_int *) ep[0].ne_cookie = 0; 1345 1346 /* construct ".." */ 1347 if (mp->am_parent) 1348 ep[1].ne_fileid = mp->am_parent->am_gen; 1349 else 1350 ep[1].ne_fileid = mp->am_gen; 1351 ep[1].ne_name = ".."; 1352 ep[1].ne_nextentry = 0; 1353 *(u_int *) ep[1].ne_cookie = 1354 xp ? xp->am_gen : ~(u_int) 0; 1355 1356 if (!xp) 1357 dp->dl_eof = TRUE; /* by default assume readdir done */ 1358 1359 return 0; 1360 } 1361#ifdef DEBUG 1362 dlog("real child"); 1363#endif /* DEBUG */ 1364 1365 if (gen == ~(u_int) 0) { 1366#ifdef DEBUG 1367 dlog("End of readdir in %s", mp->am_path); 1368#endif /* DEBUG */ 1369 dp->dl_eof = TRUE; 1370 dp->dl_entries = 0; 1371 return 0; 1372 } 1373 1374 /* non-browsable directories code */ 1375 xp = mp->am_child; 1376 while (xp && xp->am_gen != gen) 1377 xp = xp->am_osib; 1378 1379 if (xp) { 1380 int nbytes = count / 2; /* conservative */ 1381 int todo = MAX_READDIR_ENTRIES; 1382 dp->dl_entries = ep; 1383 do { 1384 am_node *xp_next = next_nonerror_node(xp->am_osib); 1385 1386 if (xp_next) { 1387 *(u_int *) ep->ne_cookie = xp_next->am_gen; 1388 } else { 1389 *(u_int *) ep->ne_cookie = ~(u_int) 0; 1390 dp->dl_eof = TRUE; 1391 } 1392 1393 ep->ne_fileid = xp->am_gen; 1394 ep->ne_name = xp->am_name; 1395 nbytes -= sizeof(*ep) + 1; 1396 if (xp->am_name) 1397 nbytes -= strlen(xp->am_name); 1398 1399 xp = xp_next; 1400 1401 if (nbytes > 0 && !dp->dl_eof && todo > 1) { 1402 ep->ne_nextentry = ep + 1; 1403 ep++; 1404 --todo; 1405 } else { 1406 todo = 0; 1407 } 1408 } while (todo > 0); 1409 1410 ep->ne_nextentry = 0; 1411 1412 return 0; 1413 } 1414 return ESTALE; 1415} 1416 1417 1418/* This one is called only if map is browsable */ 1419static int 1420amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable) 1421{ 1422 u_int gen = *(u_int *) cookie; 1423 int chain_length, i; 1424 static nfsentry *te, *te_next; 1425#ifdef DEBUG_READDIR 1426 nfsentry *ne; 1427 static int j; 1428#endif /* DEBUG_READDIR */ 1429 1430 dp->dl_eof = FALSE; /* assume readdir not done */ 1431 1432#ifdef DEBUG_READDIR 1433 plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d", 1434 gen, count); 1435#endif /* DEBUG_READDIR */ 1436 1437 if (gen == 0) { 1438 /* 1439 * In the default instance (which is used to start a search) we return 1440 * "." and "..". 1441 * 1442 * This assumes that the count is big enough to allow both "." and ".." 1443 * to be returned in a single packet. If it isn't (which would be 1444 * fairly unbelievable) then tough. 1445 */ 1446#ifdef DEBUG 1447 dlog("default search"); 1448#endif /* DEBUG */ 1449 /* 1450 * Check for enough room. This is extremely approximate but is more 1451 * than enough space. Really need 2 times: 1452 * 4byte fileid 1453 * 4byte cookie 1454 * 4byte name length 1455 * 4byte name 1456 * plus the dirlist structure */ 1457 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 1458 return EINVAL; 1459 1460 /* 1461 * compute # of entries to send in this chain. 1462 * heuristics: 128 bytes per entry. 1463 * This is too much probably, but it seems to work better because 1464 * of the re-entrant nature of nfs_readdir, and esp. on systems 1465 * like OpenBSD 2.2. 1466 */ 1467 chain_length = count / 128; 1468 1469 /* reset static state counters */ 1470 te = te_next = NULL; 1471 1472 dp->dl_entries = ep; 1473 1474 /* construct "." */ 1475 ep[0].ne_fileid = mp->am_gen; 1476 ep[0].ne_name = "."; 1477 ep[0].ne_nextentry = &ep[1]; 1478 *(u_int *) ep[0].ne_cookie = 0; 1479 1480 /* construct ".." */ 1481 if (mp->am_parent) 1482 ep[1].ne_fileid = mp->am_parent->am_gen; 1483 else 1484 ep[1].ne_fileid = mp->am_gen; 1485 ep[1].ne_name = ".."; 1486 ep[1].ne_nextentry = 0; 1487 *(u_int *) ep[1].ne_cookie = ~(u_int) 0; 1488 1489 /* 1490 * If map is browsable, call a function make_entry_chain() to construct 1491 * a linked list of unmounted keys, and return it. Then link the chain 1492 * to the regular list. Get the chain only once, but return 1493 * chunks of it each time. 1494 */ 1495 te = make_entry_chain(mp, dp->dl_entries, fully_browsable); 1496 if (!te) 1497 return 0; 1498#ifdef DEBUG_READDIR 1499 j = 0; 1500 for (ne=te; ne; ne=ne->ne_nextentry) 1501 plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name); 1502#endif /* DEBUG_READDIR */ 1503 1504 /* return only "chain_length" entries */ 1505 te_next = te; 1506 for (i=1; i<chain_length; ++i) { 1507 te_next = te_next->ne_nextentry; 1508 if (!te_next) 1509 break; 1510 } 1511 if (te_next) { 1512 nfsentry *te_saved = te_next->ne_nextentry; 1513 te_next->ne_nextentry = NULL; /* terminate "te" chain */ 1514 te_next = te_saved; /* save rest of "te" for next interation */ 1515 dp->dl_eof = FALSE; /* tell readdir there's more */ 1516 } else { 1517 dp->dl_eof = TRUE; /* tell readdir that's it */ 1518 } 1519 ep[1].ne_nextentry = te; /* append this chunk of "te" chain */ 1520#ifdef DEBUG_READDIR 1521 for (ne=te; ne; ne=ne->ne_nextentry) 1522 plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name); 1523#endif /* DEBUG_READDIR */ 1524 return 0; 1525 } /* end of "if (gen == 0)" statement */ 1526 1527#ifdef DEBUG 1528 dlog("real child"); 1529#endif /* DEBUG */ 1530 1531 if (gen == ~(u_int) 0) { 1532#ifdef DEBUG 1533 dlog("End of readdir in %s", mp->am_path); 1534#endif /* DEBUG */ 1535 dp->dl_eof = TRUE; 1536 dp->dl_entries = 0; 1537 return 0; 1538 } 1539 1540 /* 1541 * If browsable directories, then continue serving readdir() with another 1542 * chunk of entries, starting from where we left off (when gen was equal 1543 * to 0). Once again, assume last chunk served to readdir. 1544 */ 1545 dp->dl_eof = TRUE; 1546 dp->dl_entries = ep; 1547 1548 te = te_next; /* reset 'te' from last saved te_next */ 1549 if (!te) { /* another indicator of end of readdir */ 1550 dp->dl_entries = 0; 1551 return 0; 1552 } 1553 /* 1554 * compute # of entries to send in this chain. 1555 * heuristics: 128 bytes per entry. 1556 */ 1557 chain_length = count / 128; 1558 1559 /* return only "chain_length" entries */ 1560 for (i=1; i<chain_length; ++i) { 1561 te_next = te_next->ne_nextentry; 1562 if (!te_next) 1563 break; 1564 } 1565 if (te_next) { 1566 nfsentry *te_saved = te_next->ne_nextentry; 1567 te_next->ne_nextentry = NULL; /* terminate "te" chain */ 1568 te_next = te_saved; /* save rest of "te" for next interation */ 1569 dp->dl_eof = FALSE; /* tell readdir there's more */ 1570 } 1571 ep = te; /* send next chunk of "te" chain */ 1572 dp->dl_entries = ep; 1573#ifdef DEBUG_READDIR 1574 plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d", 1575 dp->dl_entries, te_next, dp->dl_eof); 1576 for (ne=te; ne; ne=ne->ne_nextentry) 1577 plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name); 1578#endif /* DEBUG_READDIR */ 1579 return 0; 1580} 1581 1582 1583int 1584amfs_auto_fmount(am_node *mp) 1585{ 1586 mntfs *mf = mp->am_mnt; 1587 return (*mf->mf_ops->fmount_fs) (mf); 1588} 1589 1590 1591int 1592amfs_auto_fumount(am_node *mp) 1593{ 1594 mntfs *mf = mp->am_mnt; 1595 return (*mf->mf_ops->fumount_fs) (mf); 1596} 1597