amfs_auto.c revision 51292
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.4 1999/08/09 06:09:43 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 * 452 For 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 504 endfor 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 } 710 711 if (!this_error) { 712 if (p->fs_flags & FS_MBACKGROUND) { 713 mf->mf_flags |= MFF_MOUNTING; /* XXX */ 714#ifdef DEBUG 715 dlog("backgrounding mount of \"%s\"", mf->mf_mount); 716#endif /* DEBUG */ 717 if (cp->callout) { 718 untimeout(cp->callout); 719 cp->callout = 0; 720 } 721 run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp); 722 mf->mf_flags |= MFF_MKMNT; /* XXX */ 723 if (mf_retry) 724 free_mntfs(mf_retry); 725 return -1; 726 } else { 727#ifdef DEBUG 728 dlog("foreground mount of \"%s\" ...", mf->mf_info); 729#endif /* DEBUG */ 730 this_error = try_mount((voidp) mp); 731 if (this_error < 0) { 732 if (!mf_retry) 733 mf_retry = dup_mntfs(mf); 734 cp->retry = TRUE; 735 } 736 } 737 } 738 739 if (this_error >= 0) { 740 if (this_error > 0) { 741 amd_stats.d_merr++; 742 if (mf != mf_retry) { 743 mf->mf_error = this_error; 744 mf->mf_flags |= MFF_ERROR; 745 } 746 } 747 748 /* 749 * Wakeup anything waiting for this mount 750 */ 751 wakeup((voidp) mf); 752 } 753 } 754 755 if (this_error && cp->retry) { 756 free_mntfs(mf); 757 mf = cp->mp->am_mnt = mf_retry; 758 /* 759 * Not retrying again (so far) 760 */ 761 cp->retry = FALSE; 762 cp->tried = FALSE; 763 /* 764 * Start at the beginning. 765 * Rewind the location vector and 766 * reset the default options. 767 */ 768 cp->ivec = cp->xivec; 769 cp->def_opts = strealloc(cp->def_opts, cp->auto_opts); 770 /* 771 * Arrange that amfs_auto_bgmount is called 772 * after anything else happens. 773 */ 774#ifdef DEBUG 775 dlog("Arranging to retry mount of %s", cp->mp->am_path); 776#endif /* DEBUG */ 777 sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf); 778 if (cp->callout) 779 untimeout(cp->callout); 780 cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf); 781 782 cp->mp->am_ttl = clocktime() + RETRY_INTERVAL; 783 784 /* 785 * Not done yet - so don't return anything 786 */ 787 return -1; 788 } 789 790 if (hard_error < 0 || this_error == 0) 791 hard_error = this_error; 792 793 /* 794 * Discard handle on duff filesystem. 795 * This should never happen since it 796 * should be caught by the case above. 797 */ 798 if (mf_retry) { 799 if (hard_error) 800 plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount); 801 free_mntfs(mf_retry); 802 } 803 804 /* 805 * If we get here, then either the mount succeeded or 806 * there is no more mount information available. 807 */ 808 if (hard_error < 0 && mp_error) 809 hard_error = cp->mp->am_error = mp_error; 810 if (hard_error > 0) { 811 /* 812 * Set a small(ish) timeout on an error node if 813 * the error was not a time out. 814 */ 815 switch (hard_error) { 816 case ETIMEDOUT: 817 case EWOULDBLOCK: 818 cp->mp->am_timeo = 17; 819 break; 820 default: 821 cp->mp->am_timeo = 5; 822 break; 823 } 824 new_ttl(cp->mp); 825 } 826 827 /* 828 * Make sure that the error value in the mntfs has a 829 * reasonable value. 830 */ 831 if (mf->mf_error < 0) { 832 mf->mf_error = hard_error; 833 if (hard_error) 834 mf->mf_flags |= MFF_ERROR; 835 } 836 837 /* 838 * In any case we don't need the continuation any more 839 */ 840 free_continuation(cp); 841 842 return hard_error; 843} 844 845 846/* 847 * Automount interface to RPC lookup routine 848 * Find the corresponding entry and return 849 * the file handle for it. 850 */ 851am_node * 852amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op) 853{ 854 am_node *ap, *new_mp, *ap_hung; 855 char *info; /* Mount info - where to get the file system */ 856 char **ivec, **xivec; /* Split version of info */ 857 char *auto_opts; /* Automount options */ 858 int error = 0; /* Error so far */ 859 char path_name[MAXPATHLEN]; /* General path name buffer */ 860 char *pfname; /* Path for database lookup */ 861 struct continuation *cp; /* Continuation structure if need to mount */ 862 int in_progress = 0; /* # of (un)mount in progress */ 863 char *dflts; 864 mntfs *mf; 865 866#ifdef DEBUG 867 dlog("in amfs_auto_lookuppn"); 868#endif /* DEBUG */ 869 870 /* 871 * If the server is shutting down 872 * then don't return information 873 * about the mount point. 874 */ 875 if (amd_state == Finishing) { 876#ifdef DEBUG 877 if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) { 878 dlog("%s mount ignored - going down", fname); 879 } else { 880 dlog("%s/%s mount ignored - going down", mp->am_path, fname); 881 } 882#endif /* DEBUG */ 883 ereturn(ENOENT); 884 } 885 886 /* 887 * Handle special case of "." and ".." 888 */ 889 if (fname[0] == '.') { 890 if (fname[1] == '\0') 891 return mp; /* "." is the current node */ 892 if (fname[1] == '.' && fname[2] == '\0') { 893 if (mp->am_parent) { 894#ifdef DEBUG 895 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); 896#endif /* DEBUG */ 897 return mp->am_parent; /* ".." is the parent node */ 898 } 899 ereturn(ESTALE); 900 } 901 } 902 903 /* 904 * Check for valid key name. 905 * If it is invalid then pretend it doesn't exist. 906 */ 907 if (!valid_key(fname)) { 908 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); 909 ereturn(ENOENT); 910 } 911 912 /* 913 * Expand key name. 914 * fname is now a private copy. 915 */ 916 fname = expand_key(fname); 917 918 for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) { 919 /* 920 * Otherwise search children of this node 921 */ 922 if (FSTREQ(ap->am_name, fname)) { 923 mf = ap->am_mnt; 924 if (ap->am_error) { 925 error = ap->am_error; 926 continue; 927 } 928 /* 929 * If the error code is undefined then it must be 930 * in progress. 931 */ 932 if (mf->mf_error < 0) 933 goto in_progrss; 934 935 /* 936 * Check for a hung node 937 */ 938 if (FSRV_ISDOWN(mf->mf_server)) { 939#ifdef DEBUG 940 dlog("server hung"); 941#endif /* DEBUG */ 942 error = ap->am_error; 943 ap_hung = ap; 944 continue; 945 } 946 /* 947 * If there was a previous error with this node 948 * then return that error code. 949 */ 950 if (mf->mf_flags & MFF_ERROR) { 951 error = mf->mf_error; 952 continue; 953 } 954 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) { 955 in_progrss: 956 /* 957 * If the fs is not mounted or it is unmounting then there 958 * is a background (un)mount in progress. In this case 959 * we just drop the RPC request (return nil) and 960 * wait for a retry, by which time the (un)mount may 961 * have completed. 962 */ 963#ifdef DEBUG 964 dlog("ignoring mount of %s in %s -- flags (%x) in progress", 965 fname, mf->mf_mount, mf->mf_flags); 966#endif /* DEBUG */ 967 in_progress++; 968 continue; 969 } 970 971 /* 972 * Otherwise we have a hit: return the current mount point. 973 */ 974#ifdef DEBUG 975 dlog("matched %s in %s", fname, ap->am_path); 976#endif /* DEBUG */ 977 XFREE(fname); 978 return ap; 979 } 980 } 981 982 if (in_progress) { 983#ifdef DEBUG 984 dlog("Waiting while %d mount(s) in progress", in_progress); 985#endif /* DEBUG */ 986 XFREE(fname); 987 ereturn(-1); 988 } 989 990 /* 991 * If an error occurred then return it. 992 */ 993 if (error) { 994#ifdef DEBUG 995 errno = error; /* XXX */ 996 dlog("Returning error: %m"); 997#endif /* DEBUG */ 998 XFREE(fname); 999 ereturn(error); 1000 } 1001 1002 /* 1003 * If doing a delete then don't create again! 1004 */ 1005 switch (op) { 1006 case VLOOK_DELETE: 1007 ereturn(ENOENT); 1008 1009 case VLOOK_CREATE: 1010 break; 1011 1012 default: 1013 plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op); 1014 ereturn(EINVAL); 1015 } 1016 1017 /* 1018 * If the server is going down then just return, 1019 * don't try to mount any more file systems 1020 */ 1021 if ((int) amd_state >= (int) Finishing) { 1022#ifdef DEBUG 1023 dlog("not found - server going down anyway"); 1024#endif /* DEBUG */ 1025 XFREE(fname); 1026 ereturn(ENOENT); 1027 } 1028 1029 /* 1030 * If we get there then this is a reference to an, 1031 * as yet, unknown name so we need to search the mount 1032 * map for it. 1033 */ 1034 if (mp->am_pref) { 1035 sprintf(path_name, "%s%s", mp->am_pref, fname); 1036 pfname = path_name; 1037 } else { 1038 pfname = fname; 1039 } 1040 1041 mf = mp->am_mnt; 1042 1043#ifdef DEBUG 1044 dlog("will search map info in %s to find %s", mf->mf_info, pfname); 1045#endif /* DEBUG */ 1046 /* 1047 * Consult the oracle for some mount information. 1048 * info is malloc'ed and belongs to this routine. 1049 * It ends up being free'd in free_continuation(). 1050 * 1051 * Note that this may return -1 indicating that information 1052 * is not yet available. 1053 */ 1054 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); 1055 if (error) { 1056 if (error > 0) 1057 plog(XLOG_MAP, "No map entry for %s", pfname); 1058 else 1059 plog(XLOG_MAP, "Waiting on map entry for %s", pfname); 1060 XFREE(fname); 1061 ereturn(error); 1062 } 1063#ifdef DEBUG 1064 dlog("mount info is %s", info); 1065#endif /* DEBUG */ 1066 1067 /* 1068 * Split info into an argument vector. 1069 * The vector is malloc'ed and belongs to 1070 * this routine. It is free'd in free_continuation() 1071 */ 1072 xivec = ivec = strsplit(info, ' ', '\"'); 1073 1074 /* 1075 * Default error code... 1076 */ 1077 if (ap_hung) 1078 error = EWOULDBLOCK; 1079 else 1080 error = ENOENT; 1081 1082 /* 1083 * Allocate a new map 1084 */ 1085 new_mp = exported_ap_alloc(); 1086 if (new_mp == 0) { 1087 XFREE(xivec); 1088 XFREE(info); 1089 XFREE(fname); 1090 ereturn(ENOSPC); 1091 } 1092 if (mf->mf_auto) 1093 auto_opts = mf->mf_auto; 1094 else 1095 auto_opts = ""; 1096 1097 auto_opts = strdup(auto_opts); 1098 1099#ifdef DEBUG 1100 dlog("searching for /defaults entry"); 1101#endif /* DEBUG */ 1102 if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) { 1103 char *dfl; 1104 char **rvec; 1105#ifdef DEBUG 1106 dlog("/defaults gave %s", dflts); 1107#endif /* DEBUG */ 1108 if (*dflts == '-') 1109 dfl = dflts + 1; 1110 else 1111 dfl = dflts; 1112 1113 /* 1114 * Chop the defaults up 1115 */ 1116 rvec = strsplit(dfl, ' ', '\"'); 1117 1118 if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) { 1119 /* 1120 * Pick whichever first entry matched the list of selectors. 1121 * Strip the selectors from the string, and assign to dfl the 1122 * rest of the string. 1123 */ 1124 if (rvec) { 1125 am_opts ap; 1126 am_ops *pt; 1127 char **sp = rvec; 1128 while (*sp) { /* loop until you find something, if any */ 1129 memset((char *) &ap, 0, sizeof(am_opts)); 1130 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", 1131 mp->am_parent->am_mnt->mf_info); 1132 free_opts(&ap); /* don't leak */ 1133 if (pt == &amfs_error_ops) { 1134 plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp); 1135 } else { 1136 dfl = strip_selectors(*sp, "/defaults"); 1137 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); 1138 break; 1139 } 1140 ++sp; 1141 } 1142 } 1143 } else { /* not enable_default_selectors */ 1144 /* 1145 * Extract first value 1146 */ 1147 dfl = rvec[0]; 1148 } 1149 1150 /* 1151 * If there were any values at all... 1152 */ 1153 if (dfl) { 1154 /* 1155 * Log error if there were other values 1156 */ 1157 if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) { 1158# ifdef DEBUG 1159 dlog("/defaults chopped into %s", dfl); 1160# endif /* DEBUG */ 1161 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); 1162 } 1163 1164 /* 1165 * Prepend to existing defaults if they exist, 1166 * otherwise just use these defaults. 1167 */ 1168 if (*auto_opts && *dfl) { 1169 char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2); 1170 sprintf(nopts, "%s;%s", dfl, auto_opts); 1171 XFREE(auto_opts); 1172 auto_opts = nopts; 1173 } else if (*dfl) { 1174 auto_opts = strealloc(auto_opts, dfl); 1175 } 1176 } 1177 XFREE(dflts); 1178 /* 1179 * Don't need info vector any more 1180 */ 1181 XFREE(rvec); 1182 } 1183 1184 /* 1185 * Fill it in 1186 */ 1187 init_map(new_mp, fname); 1188 1189 /* 1190 * Put it in the table 1191 */ 1192 insert_am(new_mp, mp); 1193 1194 /* 1195 * Fill in some other fields, 1196 * path and mount point. 1197 * 1198 * bugfix: do not prepend old am_path if direct map 1199 * <wls@astro.umd.edu> William Sebok 1200 */ 1201 new_mp->am_path = str3cat(new_mp->am_path, 1202 mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path, 1203 *fname == '/' ? "" : "/", fname); 1204 1205#ifdef DEBUG 1206 dlog("setting path to %s", new_mp->am_path); 1207#endif /* DEBUG */ 1208 1209 /* 1210 * Take private copy of pfname 1211 */ 1212 pfname = strdup(pfname); 1213 1214 /* 1215 * Construct a continuation 1216 */ 1217 cp = ALLOC(struct continuation); 1218 cp->callout = 0; 1219 cp->mp = new_mp; 1220 cp->xivec = xivec; 1221 cp->ivec = ivec; 1222 cp->info = info; 1223 cp->key = pfname; 1224 cp->auto_opts = auto_opts; 1225 cp->retry = FALSE; 1226 cp->tried = FALSE; 1227 cp->start = clocktime(); 1228 cp->def_opts = strdup(auto_opts); 1229 memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts)); 1230 1231 /* 1232 * Try and mount the file system. If this succeeds immediately (possible 1233 * for a ufs file system) then return the attributes, otherwise just 1234 * return an error. 1235 */ 1236 error = amfs_auto_bgmount(cp, error); 1237 reschedule_timeout_mp(); 1238 if (!error) { 1239 XFREE(fname); 1240 return new_mp; 1241 } 1242 1243 /* 1244 * Code for quick reply. If nfs_program_2_transp is set, then 1245 * its the transp that's been passed down from nfs_program_2(). 1246 * If new_mp->am_transp is not already set, set it by copying in 1247 * nfs_program_2_transp. Once am_transp is set, quick_reply() can 1248 * use it to send a reply to the client that requested this mount. 1249 */ 1250 if (nfs_program_2_transp && !new_mp->am_transp) { 1251 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); 1252 *(new_mp->am_transp) = *nfs_program_2_transp; 1253 } 1254 if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) 1255 new_mp->am_error = error; 1256 1257 assign_error_mntfs(new_mp); 1258 1259 XFREE(fname); 1260 1261 ereturn(error); 1262} 1263 1264 1265/* 1266 * Locate next node in sibling list which is mounted 1267 * and is not an error node. 1268 */ 1269am_node * 1270next_nonerror_node(am_node *xp) 1271{ 1272 mntfs *mf; 1273 1274 /* 1275 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> 1276 * Fixes a race condition when mounting direct automounts. 1277 * Also fixes a problem when doing a readdir on a directory 1278 * containing hung automounts. 1279 */ 1280 while (xp && 1281 (!(mf = xp->am_mnt) || /* No mounted filesystem */ 1282 mf->mf_error != 0 || /* There was a mntfs error */ 1283 xp->am_error != 0 || /* There was a mount error */ 1284 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ 1285 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ 1286 ) 1287 xp = xp->am_osib; 1288 1289 return xp; 1290} 1291 1292 1293/* 1294 * This readdir function which call a special version of it that allows 1295 * browsing if browsable_dirs=yes was set on the map. 1296 */ 1297int 1298amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count) 1299{ 1300 u_int gen = *(u_int *) cookie; 1301 am_node *xp; 1302 mntent_t mnt; 1303 1304 dp->dl_eof = FALSE; /* assume readdir not done */ 1305 1306 /* check if map is browsable */ 1307 if (mp->am_mnt && mp->am_mnt->mf_mopts) { 1308 mnt.mnt_opts = mp->am_mnt->mf_mopts; 1309 if (hasmntopt(&mnt, "fullybrowsable")) 1310 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE); 1311 if (hasmntopt(&mnt, "browsable")) 1312 return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE); 1313 } 1314 1315 if (gen == 0) { 1316 /* 1317 * In the default instance (which is used to start a search) we return 1318 * "." and "..". 1319 * 1320 * This assumes that the count is big enough to allow both "." and ".." 1321 * to be returned in a single packet. If it isn't (which would be 1322 * fairly unbelievable) then tough. 1323 */ 1324#ifdef DEBUG 1325 dlog("amfs_auto_readdir: default search"); 1326#endif /* DEBUG */ 1327 /* 1328 * Check for enough room. This is extremely approximate but is more 1329 * than enough space. Really need 2 times: 1330 * 4byte fileid 1331 * 4byte cookie 1332 * 4byte name length 1333 * 4byte name 1334 * plus the dirlist structure */ 1335 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 1336 return EINVAL; 1337 1338 xp = next_nonerror_node(mp->am_child); 1339 dp->dl_entries = ep; 1340 1341 /* construct "." */ 1342 ep[0].ne_fileid = mp->am_gen; 1343 ep[0].ne_name = "."; 1344 ep[0].ne_nextentry = &ep[1]; 1345 *(u_int *) ep[0].ne_cookie = 0; 1346 1347 /* construct ".." */ 1348 if (mp->am_parent) 1349 ep[1].ne_fileid = mp->am_parent->am_gen; 1350 else 1351 ep[1].ne_fileid = mp->am_gen; 1352 ep[1].ne_name = ".."; 1353 ep[1].ne_nextentry = 0; 1354 *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE); 1355 1356 if (!xp) 1357 dp->dl_eof = TRUE; /* by default assume readdir done */ 1358 1359 return 0; 1360 } 1361#ifdef DEBUG 1362 dlog("amfs_auto_readdir: real child"); 1363#endif /* DEBUG */ 1364 1365 if (gen == DOT_DOT_COOKIE) { 1366#ifdef DEBUG 1367 dlog("amfs_auto_readdir: 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 1383 dp->dl_entries = ep; 1384 do { 1385 am_node *xp_next = next_nonerror_node(xp->am_osib); 1386 1387 if (xp_next) { 1388 *(u_int *) ep->ne_cookie = xp_next->am_gen; 1389 } else { 1390 *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE; 1391 dp->dl_eof = TRUE; 1392 } 1393 1394 ep->ne_fileid = xp->am_gen; 1395 ep->ne_name = xp->am_name; 1396 nbytes -= sizeof(*ep) + 1; 1397 if (xp->am_name) 1398 nbytes -= strlen(xp->am_name); 1399 1400 xp = xp_next; 1401 1402 if (nbytes > 0 && !dp->dl_eof && todo > 1) { 1403 ep->ne_nextentry = ep + 1; 1404 ep++; 1405 --todo; 1406 } else { 1407 todo = 0; 1408 } 1409 } while (todo > 0); 1410 1411 ep->ne_nextentry = 0; 1412 1413 return 0; 1414 } 1415 return ESTALE; 1416} 1417 1418 1419/* This one is called only if map is browsable */ 1420static int 1421amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable) 1422{ 1423 u_int gen = *(u_int *) cookie; 1424 int chain_length, i; 1425 static nfsentry *te, *te_next; 1426#ifdef DEBUG_READDIR 1427 nfsentry *ne; 1428 static int j; 1429#endif /* DEBUG_READDIR */ 1430 1431 dp->dl_eof = FALSE; /* assume readdir not done */ 1432 1433#ifdef DEBUG_READDIR 1434 plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d", 1435 gen, count); 1436#endif /* DEBUG_READDIR */ 1437 1438 if (gen == 0) { 1439 /* 1440 * In the default instance (which is used to start a search) we return 1441 * "." and "..". 1442 * 1443 * This assumes that the count is big enough to allow both "." and ".." 1444 * to be returned in a single packet. If it isn't (which would be 1445 * fairly unbelievable) then tough. 1446 */ 1447#ifdef DEBUG 1448 dlog("amfs_auto_readdir_browsable: default search"); 1449#endif /* DEBUG */ 1450 /* 1451 * Check for enough room. This is extremely approximate but is more 1452 * than enough space. Really need 2 times: 1453 * 4byte fileid 1454 * 4byte cookie 1455 * 4byte name length 1456 * 4byte name 1457 * plus the dirlist structure */ 1458 if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) 1459 return EINVAL; 1460 1461 /* 1462 * compute # of entries to send in this chain. 1463 * heuristics: 128 bytes per entry. 1464 * This is too much probably, but it seems to work better because 1465 * of the re-entrant nature of nfs_readdir, and esp. on systems 1466 * like OpenBSD 2.2. 1467 */ 1468 chain_length = count / 128; 1469 1470 /* reset static state counters */ 1471 te = te_next = NULL; 1472 1473 dp->dl_entries = ep; 1474 1475 /* construct "." */ 1476 ep[0].ne_fileid = mp->am_gen; 1477 ep[0].ne_name = "."; 1478 ep[0].ne_nextentry = &ep[1]; 1479 *(u_int *) ep[0].ne_cookie = 0; 1480 1481 /* construct ".." */ 1482 if (mp->am_parent) 1483 ep[1].ne_fileid = mp->am_parent->am_gen; 1484 else 1485 ep[1].ne_fileid = mp->am_gen; 1486 ep[1].ne_name = ".."; 1487 ep[1].ne_nextentry = 0; 1488 *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE; 1489 1490 /* 1491 * If map is browsable, call a function make_entry_chain() to construct 1492 * a linked list of unmounted keys, and return it. Then link the chain 1493 * to the regular list. Get the chain only once, but return 1494 * chunks of it each time. 1495 */ 1496 te = make_entry_chain(mp, dp->dl_entries, fully_browsable); 1497 if (!te) 1498 return 0; 1499#ifdef DEBUG_READDIR 1500 for (j=0,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 iteration */ 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 (j=0,ne=te; ne; ne=ne->ne_nextentry) 1522 plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name); 1523 for (j=0,ne=ep; ne; ne=ne->ne_nextentry) 1524 plog(XLOG_INFO, "gen2+ key %4d \"%s\" fi=%d ck=%d", 1525 j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); 1526 plog(XLOG_INFO, "EOF is %d", dp->dl_eof); 1527#endif /* DEBUG_READDIR */ 1528 return 0; 1529 } /* end of "if (gen == 0)" statement */ 1530 1531#ifdef DEBUG 1532 dlog("amfs_auto_readdir_browsable: real child"); 1533#endif /* DEBUG */ 1534 1535 if (gen == DOT_DOT_COOKIE) { 1536#ifdef DEBUG 1537 dlog("amfs_auto_readdir_browsable: End of readdir in %s", mp->am_path); 1538#endif /* DEBUG */ 1539 dp->dl_eof = TRUE; 1540 dp->dl_entries = 0; 1541 return 0; 1542 } 1543 1544 /* 1545 * If browsable directories, then continue serving readdir() with another 1546 * chunk of entries, starting from where we left off (when gen was equal 1547 * to 0). Once again, assume last chunk served to readdir. 1548 */ 1549 dp->dl_eof = TRUE; 1550 dp->dl_entries = ep; 1551 1552 te = te_next; /* reset 'te' from last saved te_next */ 1553 if (!te) { /* another indicator of end of readdir */ 1554 dp->dl_entries = 0; 1555 return 0; 1556 } 1557 /* 1558 * compute # of entries to send in this chain. 1559 * heuristics: 128 bytes per entry. 1560 */ 1561 chain_length = count / 128; 1562 1563 /* return only "chain_length" entries */ 1564 for (i=1; i<chain_length; ++i) { 1565 te_next = te_next->ne_nextentry; 1566 if (!te_next) 1567 break; 1568 } 1569 if (te_next) { 1570 nfsentry *te_saved = te_next->ne_nextentry; 1571 te_next->ne_nextentry = NULL; /* terminate "te" chain */ 1572 te_next = te_saved; /* save rest of "te" for next iteration */ 1573 dp->dl_eof = FALSE; /* tell readdir there's more */ 1574 } 1575 ep = te; /* send next chunk of "te" chain */ 1576 dp->dl_entries = ep; 1577#ifdef DEBUG_READDIR 1578 plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d", 1579 (int) dp->dl_entries, (int) te_next, dp->dl_eof); 1580 for (ne=te; ne; ne=ne->ne_nextentry) 1581 plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name); 1582#endif /* DEBUG_READDIR */ 1583 return 0; 1584} 1585 1586 1587int 1588amfs_auto_fmount(am_node *mp) 1589{ 1590 mntfs *mf = mp->am_mnt; 1591 return (*mf->mf_ops->fmount_fs) (mf); 1592} 1593 1594 1595int 1596amfs_auto_fumount(am_node *mp) 1597{ 1598 mntfs *mf = mp->am_mnt; 1599 return (*mf->mf_ops->fumount_fs) (mf); 1600} 1601