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