1 /* 2 * Copyright (c) 1990 Jan-Simon Pendry 3 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 4 * Copyright (c) 1990 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry at Imperial College, London. 9 * 10 * %sccs.include.redist.c% 11 * 12 * @(#)afs_ops.c 5.5 (Berkeley) 05/31/92 13 * 14 * $Id: afs_ops.c,v 5.2.2.4 1992/05/31 16:36:36 jsp Exp $ 15 * 16 */ 17 18 #include "am.h" 19 20 #define NFS 21 #define NFSCLIENT 22 23 #include <sys/stat.h> 24 #ifdef NFS_3 25 typedef nfs_fh fhandle_t; 26 #endif /* NFS_3 */ 27 #ifdef NFS_HDR 28 #include NFS_HDR 29 #endif /* NFS_HDR */ 30 #include <sys/mount.h> 31 #include "mount.h" 32 33 /* 34 * Automount file system 35 * Direct file system 36 * Root file system 37 * Top-level file system 38 */ 39 40 /* 41 * Interval between forced retries of a mount. 42 */ 43 #define RETRY_INTERVAL 2 44 45 /* 46 * AFS needs nothing in particular. 47 */ 48 static char *afs_match P((am_opts *fo)); 49 static char *afs_match(fo) 50 am_opts *fo; 51 { 52 char *p = fo->opt_rfs; 53 if (!fo->opt_rfs) { 54 plog(XLOG_USER, "auto: no mount point named (rfs:=)"); 55 return 0; 56 } 57 if (!fo->opt_fs) { 58 plog(XLOG_USER, "auto: no map named (fs:=)"); 59 return 0; 60 } 61 /* 62 * Swap round fs:= and rfs:= options 63 * ... historical (jsp) 64 */ 65 fo->opt_rfs = fo->opt_fs; 66 fo->opt_fs = p; 67 /* 68 * mtab entry turns out to be the name of the mount map 69 */ 70 return strdup(fo->opt_rfs ? fo->opt_rfs : "."); 71 } 72 73 /* 74 * Mount an automounter directory. 75 * The automounter is connected into the system 76 * as a user-level NFS server. mount_toplvl constructs 77 * the necessary NFS parameters to be given to the 78 * kernel so that it will talk back to us. 79 */ 80 static int mount_toplvl P((char *dir, char *opts)); 81 static int mount_toplvl(dir, opts) 82 char *dir; 83 char *opts; 84 { 85 struct nfs_args nfs_args; 86 struct mntent mnt; 87 int retry; 88 struct sockaddr_in sin; 89 unsigned short port; 90 int flags; 91 extern nfs_fh *root_fh(); 92 nfs_fh *fhp; 93 char fs_hostname[MAXHOSTNAMELEN+MAXPATHLEN+1]; 94 95 MTYPE_TYPE type = MOUNT_TYPE_NFS; 96 97 bzero((voidp) &nfs_args, sizeof(nfs_args)); /* Paranoid */ 98 99 mnt.mnt_dir = dir; 100 mnt.mnt_fsname = pid_fsname; 101 mnt.mnt_type = MNTTYPE_AUTO; 102 mnt.mnt_opts = opts; 103 mnt.mnt_freq = 0; 104 mnt.mnt_passno = 0; 105 106 retry = hasmntval(&mnt, "retry"); 107 if (retry <= 0) 108 retry = 2; /* XXX */ 109 110 /* 111 * get fhandle of remote path for automount point 112 */ 113 114 fhp = root_fh(dir); 115 if (!fhp) { 116 plog(XLOG_FATAL, "Can't find root file handle for %s", dir); 117 return EINVAL; 118 } 119 120 NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) fhp); 121 122 /* 123 * Create sockaddr to point to the local machine. 127.0.0.1 124 * is not used since that will not work in HP-UX clusters and 125 * this is no more expensive. 126 */ 127 bzero((voidp) &sin, sizeof(sin)); 128 sin.sin_family = AF_INET; 129 sin.sin_addr = myipaddr; 130 if (port = hasmntval(&mnt, "port")) { 131 sin.sin_port = htons(port); 132 } else { 133 plog(XLOG_ERROR, "no port number specified for %s", dir); 134 return EINVAL; 135 } 136 137 /* 138 * set mount args 139 */ 140 NFS_SA_DREF(nfs_args, &sin); 141 142 /* 143 * Make a ``hostname'' string for the kernel 144 */ 145 #ifndef HOSTNAMESZ 146 #define SHORT_MOUNT_NAME 147 #endif /* HOSTNAMESZ */ 148 #ifdef SHORT_MOUNT_NAME 149 sprintf(fs_hostname, "amd:%d", foreground ? mypid : getppid()); 150 #else 151 sprintf(fs_hostname, "pid%d@%s:%s", foreground ? mypid : getppid(), hostname, dir); 152 #endif /* SHORT_MOUNT_NAME */ 153 nfs_args.hostname = fs_hostname; 154 nfs_args.flags |= NFSMNT_HOSTNAME; 155 #ifdef HOSTNAMESZ 156 /* 157 * Most kernels have a name length restriction. 158 */ 159 if (strlen(fs_hostname) >= HOSTNAMESZ) 160 strcpy(fs_hostname + HOSTNAMESZ - 3, ".."); 161 #endif /* HOSTNAMESZ */ 162 163 /* 164 * Parse a subset of the standard nfs options. The 165 * others are probably irrelevant for this application 166 */ 167 if (nfs_args.timeo = hasmntval(&mnt, "timeo")) 168 nfs_args.flags |= NFSMNT_TIMEO; 169 170 if (nfs_args.retrans = hasmntval(&mnt, "retrans")) 171 nfs_args.flags |= NFSMNT_RETRANS; 172 173 #ifdef NFSMNT_BIODS 174 if (nfs_args.biods = hasmntval(&mnt, "biods")) 175 nfs_args.flags |= NFSMNT_BIODS; 176 177 #endif /* NFSMNT_BIODS */ 178 179 #if defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX) 180 /* 181 * Don't cache attributes - they are changing under 182 * the kernel's feet... 183 */ 184 nfs_args.acregmin = nfs_args.acregmax = 1; 185 nfs_args.flags |= NFSMNT_ACREGMIN|NFSMNT_ACREGMAX; 186 #endif /* defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX) */ 187 /* 188 * These two are constructed internally by the calling routine 189 */ 190 if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL) 191 nfs_args.flags |= NFSMNT_SOFT; 192 193 #ifdef MNTOPT_INTR 194 if (hasmntopt(&mnt, MNTOPT_INTR) != NULL) 195 nfs_args.flags |= NFSMNT_INT; 196 #endif /* MNTOPT_INTR */ 197 198 flags = compute_mount_flags(&mnt); 199 #ifdef ULTRIX_HACK 200 nfs_args.gfs_flags = flags; 201 flags &= M_RDONLY; 202 if (flags & M_RDONLY) 203 nfs_args.flags |= NFSMNT_RONLY; 204 #endif /* ULTRIX_HACK */ 205 return mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type); 206 } 207 208 static void afs_mkcacheref P((mntfs *mf)); 209 static void afs_mkcacheref(mf) 210 mntfs *mf; 211 { 212 /* 213 * Build a new map cache for this node, or re-use 214 * an existing cache for the same map. 215 */ 216 char *cache; 217 if (mf->mf_fo && mf->mf_fo->opt_cache) 218 cache = mf->mf_fo->opt_cache; 219 else 220 cache = "none"; 221 mf->mf_private = (voidp) mapc_find(mf->mf_info, cache); 222 mf->mf_prfree = mapc_free; 223 } 224 225 /* 226 * Mount the root... 227 */ 228 static int root_mount P((am_node *mp)); 229 static int root_mount(mp) 230 am_node *mp; 231 { 232 mntfs *mf = mp->am_mnt; 233 234 mf->mf_mount = strealloc(mf->mf_mount, pid_fsname); 235 mf->mf_private = (voidp) mapc_find(mf->mf_info, ""); 236 mf->mf_prfree = mapc_free; 237 238 return 0; 239 } 240 241 /* 242 * Mount a sub-mount 243 */ 244 static int afs_mount P((am_node *mp)); 245 static int afs_mount(mp) 246 am_node *mp; 247 { 248 mntfs *mf = mp->am_mnt; 249 250 /* 251 * Pseudo-directories are used to provide some structure 252 * to the automounted directories instead 253 * of putting them all in the top-level automount directory. 254 * 255 * Here, just increment the parent's link count. 256 */ 257 mp->am_parent->am_fattr.nlink++; 258 /* 259 * Info field of . means use parent's info field. 260 * Historical - not documented. 261 */ 262 if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0') 263 mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info); 264 /* 265 * Compute prefix: 266 * 267 * If there is an option prefix then use that else 268 * If the parent had a prefix then use that with name 269 * of this node appended else 270 * Use the name of this node. 271 * 272 * That means if you want no prefix you must say so 273 * in the map. 274 */ 275 if (mf->mf_fo->opt_pref) { 276 /* 277 * the prefix specified as an option 278 */ 279 mp->am_pref = strdup(mf->mf_fo->opt_pref); 280 } else { 281 /* 282 * else the parent's prefix 283 * followed by the name 284 * followed by / 285 */ 286 char *ppref = mp->am_parent->am_pref; 287 if (ppref == 0) 288 ppref = ""; 289 mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/"); 290 } 291 292 /* 293 * Attach a map cache 294 */ 295 afs_mkcacheref(mf); 296 297 return 0; 298 } 299 300 /* 301 * Mount the top-level 302 */ 303 static int toplvl_mount P((am_node *mp)); 304 static int toplvl_mount(mp) 305 am_node *mp; 306 { 307 mntfs *mf = mp->am_mnt; 308 struct stat stb; 309 char opts[256]; 310 int error; 311 char *mnttype; 312 313 /* 314 * Mounting the automounter. 315 * Make sure the mount directory exists, construct 316 * the mount options and call the mount_toplvl routine. 317 */ 318 319 if (stat(mp->am_path, &stb) < 0) { 320 return errno; 321 } else if ((stb.st_mode & S_IFMT) != S_IFDIR) { 322 plog(XLOG_WARNING, "%s is not a directory", mp->am_path); 323 return ENOTDIR; 324 } 325 326 if (mf->mf_ops == &toplvl_ops) mnttype = "indirect"; 327 else if (mf->mf_ops == &dfs_ops) mnttype = "direct"; 328 #ifdef HAS_UNION_FS 329 else if (mf->mf_ops == &union_ops) mnttype = "union"; 330 #endif 331 else mnttype = "auto"; 332 333 /* 334 * Construct some mount options 335 */ 336 sprintf(opts, 337 #ifdef MNTOPT_INTR 338 "%s,%s,%s=%d,%s=%d,%s=%d,%s", 339 MNTOPT_INTR, 340 #else 341 "%s,%s=%d,%s=%d,%s=%d,%s", 342 #endif /* MNTOPT_INTR */ 343 "rw", 344 "port", nfs_port, 345 "timeo", afs_timeo, 346 "retrans", afs_retrans, 347 mnttype); 348 349 error = mount_toplvl(mf->mf_mount, opts); 350 if (error) { 351 errno = error; 352 plog(XLOG_FATAL, "mount_toplvl: %m"); 353 return error; 354 } 355 356 return 0; 357 } 358 359 static void toplvl_mounted P((mntfs *mf)); 360 static void toplvl_mounted(mf) 361 mntfs *mf; 362 { 363 afs_mkcacheref(mf); 364 } 365 366 #ifdef HAS_UNION_FS 367 /* 368 * Create a reference to a union'ed entry 369 */ 370 static int create_union_node P((char *dir, voidp arg)); 371 static int create_union_node(dir, arg) 372 char *dir; 373 voidp arg; 374 { 375 if (strcmp(dir, "/defaults") != 0) { 376 int error = 0; 377 (void) toplvl_ops.lookuppn(arg, dir, &error, VLOOK_CREATE); 378 if (error > 0) { 379 errno = error; /* XXX */ 380 plog(XLOG_ERROR, "Could not mount %s: %m", dir); 381 } 382 return error; 383 } 384 return 0; 385 } 386 387 static void union_mounted P((mntfs *mf)); 388 static void union_mounted(mf) 389 mntfs *mf; 390 { 391 int i; 392 393 afs_mkcacheref(mf); 394 395 /* 396 * Having made the union mount point, 397 * populate all the entries... 398 */ 399 for (i = 0; i <= last_used_map; i++) { 400 am_node *mp = exported_ap[i]; 401 if (mp && mp->am_mnt == mf) { 402 /* return value from create_union_node is ignored by mapc_keyiter */ 403 (void) mapc_keyiter((mnt_map *) mp->am_mnt->mf_private, 404 (void (*)P((char*,void*))) create_union_node, mp); 405 break; 406 } 407 } 408 409 #ifdef notdef 410 /* 411 * would be nice to flush most of the cache, but we need to 412 * keep the wildcard and /defaults entries... 413 */ 414 mapc_free(mf->mf_private); 415 mf->mf_private = (voidp) mapc_find(mf->mf_info, "inc"); 416 /* mapc_add_kv(mf->mf_private, strdup("/defaults"), 417 strdup("type:=link;opts:=nounmount;sublink:=${key}")); */ 418 #endif 419 } 420 #endif /* HAS_UNION_FS */ 421 422 /* 423 * Unmount an automount sub-node 424 */ 425 static int afs_umount P((am_node *mp)); 426 static int afs_umount(mp) 427 am_node *mp; 428 { 429 return 0; 430 } 431 432 /* 433 * Unmount a top-level automount node 434 */ 435 static int toplvl_umount P((am_node *mp)); 436 static int toplvl_umount(mp) 437 am_node *mp; 438 { 439 int error; 440 441 struct stat stb; 442 again: 443 /* 444 * The lstat is needed if this mount is type=direct. 445 * When that happens, the kernel cache gets confused 446 * between the underlying type (dir) and the mounted 447 * type (link) and so needs to be re-synced before 448 * the unmount. This is all because the unmount system 449 * call follows links and so can't actually unmount 450 * a link (stupid!). It was noted that doing an ls -ld 451 * of the mount point to see why things were not working 452 * actually fixed the problem - so simulate an ls -ld here. 453 */ 454 if (lstat(mp->am_path, &stb) < 0) { 455 #ifdef DEBUG 456 dlog("lstat(%s): %m", mp->am_path); 457 #endif /* DEBUG */ 458 } 459 error = UMOUNT_FS(mp->am_path); 460 if (error == EBUSY) { 461 plog(XLOG_WARNING, "afs_unmount retrying %s in 1s", mp->am_path); 462 sleep(1); /* XXX */ 463 goto again; 464 } 465 466 return error; 467 } 468 469 /* 470 * Unmount an automount node 471 */ 472 static void afs_umounted P((am_node *mp)); 473 static void afs_umounted(mp) 474 am_node *mp; 475 { 476 /* 477 * If this is a pseudo-directory then just adjust the link count 478 * in the parent, otherwise call the generic unmount routine 479 */ 480 if (mp->am_parent && mp->am_parent->am_parent) 481 --mp->am_parent->am_fattr.nlink; 482 } 483 484 /* 485 * Mounting a file system may take a significant period of time. The 486 * problem is that if this is done in the main process thread then 487 * the entire automounter could be blocked, possibly hanging lots of 488 * processes on the system. Instead we use a continuation scheme to 489 * allow mounts to be attempted in a sub-process. When the sub-process 490 * exits we pick up the exit status (by convention a UN*X error number) 491 * and continue in a notifier. The notifier gets handed a data structure 492 * and can then determine whether the mount was successful or not. If 493 * not, it updates the data structure and tries again until there are no 494 * more ways to try the mount, or some other permanent error occurs. 495 * In the mean time no RPC reply is sent, even after the mount is succesful. 496 * We rely on the RPC retry mechanism to resend the lookup request which 497 * can then be handled. 498 */ 499 500 501 struct continuation { 502 char **ivec; /* Current mount info */ 503 am_node *mp; /* Node we are trying to mount */ 504 char *key; /* Map key */ 505 char *info; /* Info string */ 506 char **xivec; /* Saved strsplit vector */ 507 char *auto_opts; /* Automount options */ 508 am_opts fs_opts; /* Filesystem options */ 509 char *def_opts; /* Default automount options */ 510 int retry; /* Try again? */ 511 int tried; /* Have we tried any yet? */ 512 time_t start; /* Time we started this mount */ 513 int callout; /* Callout identifier */ 514 }; 515 516 #define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING) 517 518 /* 519 * Discard an old continuation 520 */ 521 static void free_continuation P((struct continuation *cp)); 522 static void free_continuation(cp) 523 struct continuation *cp; 524 { 525 if (cp->callout) 526 untimeout(cp->callout); 527 free((voidp) cp->key); 528 free((voidp) cp->xivec); 529 free((voidp) cp->info); 530 free((voidp) cp->auto_opts); 531 free((voidp) cp->def_opts); 532 free_opts(&cp->fs_opts); 533 free((voidp) cp); 534 } 535 536 static int afs_bgmount P((struct continuation*, int)); 537 538 /* 539 * Discard the underlying mount point and replace 540 * with a reference to an error filesystem. 541 */ 542 static void assign_error_mntfs P((am_node *mp)); 543 static void assign_error_mntfs(mp) 544 am_node *mp; 545 { 546 if (mp->am_error > 0) { 547 /* 548 * Save the old error code 549 */ 550 int error = mp->am_error; 551 if (error <= 0) 552 error = mp->am_mnt->mf_error; 553 /* 554 * Discard the old filesystem 555 */ 556 free_mntfs(mp->am_mnt); 557 /* 558 * Allocate a new error reference 559 */ 560 mp->am_mnt = new_mntfs(); 561 /* 562 * Put back the error code 563 */ 564 mp->am_mnt->mf_error = error; 565 mp->am_mnt->mf_flags |= MFF_ERROR; 566 /* 567 * Zero the error in the mount point 568 */ 569 mp->am_error = 0; 570 } 571 } 572 573 /* 574 * The continuation function. This is called by 575 * the task notifier when a background mount attempt 576 * completes. 577 */ 578 static void afs_cont P((int rc, int term, voidp closure)); 579 static void afs_cont(rc, term, closure) 580 int rc; 581 int term; 582 voidp closure; 583 { 584 struct continuation *cp = (struct continuation *) closure; 585 mntfs *mf = cp->mp->am_mnt; 586 587 /* 588 * Definitely not trying to mount at the moment 589 */ 590 mf->mf_flags &= ~MFF_MOUNTING; 591 /* 592 * While we are mounting - try to avoid race conditions 593 */ 594 new_ttl(cp->mp); 595 596 /* 597 * Wakeup anything waiting for this mount 598 */ 599 wakeup((voidp) mf); 600 601 /* 602 * Check for termination signal or exit status... 603 */ 604 if (rc || term) { 605 am_node *xmp; 606 607 if (term) { 608 /* 609 * Not sure what to do for an error code. 610 */ 611 mf->mf_error = EIO; /* XXX ? */ 612 mf->mf_flags |= MFF_ERROR; 613 plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term); 614 } else { 615 /* 616 * Check for exit status... 617 */ 618 mf->mf_error = rc; 619 mf->mf_flags |= MFF_ERROR; 620 errno = rc; /* XXX */ 621 plog(XLOG_ERROR, "%s: mount (afs_cont): %m", cp->mp->am_path); 622 } 623 624 /* 625 * If we get here then that attempt didn't work, so 626 * move the info vector pointer along by one and 627 * call the background mount routine again 628 */ 629 amd_stats.d_merr++; 630 cp->ivec++; 631 xmp = cp->mp; 632 (void) afs_bgmount(cp, 0); 633 assign_error_mntfs(xmp); 634 } else { 635 /* 636 * The mount worked. 637 */ 638 am_mounted(cp->mp); 639 free_continuation(cp); 640 } 641 642 reschedule_timeout_mp(); 643 } 644 645 /* 646 * Retry a mount 647 */ 648 /*ARGSUSED*/ 649 static void afs_retry P((int rc, int term, voidp closure)); 650 static void afs_retry(rc, term, closure) 651 int rc; 652 int term; 653 voidp closure; 654 { 655 struct continuation *cp = (struct continuation *) closure; 656 int error = 0; 657 658 #ifdef DEBUG 659 dlog("Commencing retry for mount of %s", cp->mp->am_path); 660 #endif /* DEBUG */ 661 662 new_ttl(cp->mp); 663 664 if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) { 665 /* 666 * The entire mount has timed out. 667 * Set the error code and skip past 668 * all the info vectors so that 669 * afs_bgmount will not have any more 670 * ways to try the mount, so causing 671 * an error. 672 */ 673 plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path); 674 error = ETIMEDOUT; 675 while (*cp->ivec) 676 cp->ivec++; 677 } 678 679 if (error || !IN_PROGRESS(cp)) { 680 (void) afs_bgmount(cp, error); 681 } 682 reschedule_timeout_mp(); 683 } 684 685 /* 686 * Try to mount a file system. Can be called 687 * directly or in a sub-process by run_task 688 */ 689 static int try_mount P((voidp mvp)); 690 static int try_mount(mvp) 691 voidp mvp; 692 { 693 /* 694 * Mount it! 695 */ 696 int error; 697 am_node *mp = (am_node *) mvp; 698 mntfs *mf = mp->am_mnt; 699 700 /* 701 * If the directory is not yet made and 702 * it needs to be made, then make it! 703 * This may be run in a backgroun process 704 * in which case the flag setting won't be 705 * noticed later - but it is set anyway 706 * just after run_task is called. It 707 * should probably go away totally... 708 */ 709 if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) { 710 error = mkdirs(mf->mf_mount, 0555); 711 if (!error) 712 mf->mf_flags |= MFF_MKMNT; 713 } 714 715 error = mount_node(mp); 716 #ifdef DEBUG 717 if (error > 0) { 718 errno = error; 719 dlog("afs call to mount_node failed: %m"); 720 } 721 #endif /* DEBUG */ 722 return error; 723 } 724 725 /* 726 * Pick a file system to try mounting and 727 * do that in the background if necessary 728 * 729 For each location: 730 if it is new -defaults then 731 extract and process 732 continue; 733 fi 734 if it is a cut then 735 if a location has been tried then 736 break; 737 fi 738 continue; 739 fi 740 parse mount location 741 discard previous mount location if required 742 find matching mounted filesystem 743 if not applicable then 744 this_error = No such file or directory 745 continue 746 fi 747 if the filesystem failed to be mounted then 748 this_error = error from filesystem 749 elif the filesystem is mounting or unmounting then 750 this_error = -1 751 elif the fileserver is down then 752 this_error = -1 753 elif the filesystem is already mounted 754 this_error = 0 755 break 756 fi 757 if no error on this mount then 758 this_error = initialise mount point 759 fi 760 if no error on this mount and mount is delayed then 761 this_error = -1 762 fi 763 if this_error < 0 then 764 retry = true 765 fi 766 if no error on this mount then 767 make mount point if required 768 fi 769 if no error on this mount then 770 if mount in background then 771 run mount in background 772 return -1 773 else 774 this_error = mount in foreground 775 fi 776 fi 777 if an error occured on this mount then 778 update stats 779 save error in mount point 780 fi 781 endfor 782 */ 783 784 static int afs_bgmount P((struct continuation *cp, int mpe)); 785 static int afs_bgmount(cp, mpe) 786 struct continuation *cp; 787 int mpe; 788 { 789 mntfs *mf = cp->mp->am_mnt; /* Current mntfs */ 790 mntfs *mf_retry = 0; /* First mntfs which needed retrying */ 791 int this_error = -1; /* Per-mount error */ 792 int hard_error = -1; 793 int mp_error = mpe; 794 795 /* 796 * Try to mount each location. 797 * At the end: 798 * hard_error == 0 indicates something was mounted. 799 * hard_error > 0 indicates everything failed with a hard error 800 * hard_error < 0 indicates nothing could be mounted now 801 */ 802 for (; this_error && *cp->ivec; cp->ivec++) { 803 am_ops *p; 804 am_node *mp = cp->mp; 805 char *link_dir; 806 int dont_retry; 807 808 if (hard_error < 0) 809 hard_error = this_error; 810 811 this_error = -1; 812 813 if (**cp->ivec == '-') { 814 /* 815 * Pick up new defaults 816 */ 817 if (cp->auto_opts && *cp->auto_opts) 818 cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec+1); 819 else 820 cp->def_opts = strealloc(cp->def_opts, *cp->ivec+1); 821 #ifdef DEBUG 822 dlog("Setting def_opts to \"%s\"", cp->def_opts); 823 #endif /* DEBUG */ 824 continue; 825 } 826 827 /* 828 * If a mount has been attempted, and we find 829 * a cut then don't try any more locations. 830 */ 831 if (strcmp(*cp->ivec, "/") == 0 || strcmp(*cp->ivec, "||") == 0) { 832 if (cp->tried) { 833 #ifdef DEBUG 834 dlog("Cut: not trying any more locations for %s", 835 mp->am_path); 836 #endif /* DEBUG */ 837 break; 838 } 839 continue; 840 } 841 842 #ifdef SUNOS4_COMPAT 843 #ifdef nomore 844 /* 845 * By default, you only get this bit on SunOS4. 846 * If you want this anyway, then define SUNOS4_COMPAT 847 * in the relevant "os-blah.h" file. 848 * 849 * We make the observation that if the local key line contains 850 * no '=' signs then either it is sick, or it is a SunOS4-style 851 * "host:fs[:link]" line. In the latter case the am_opts field 852 * is also assumed to be in old-style, so you can't mix & match. 853 * You can use ${} expansions for the fs and link bits though... 854 * 855 * Actually, this doesn't really cover all the possibilities for 856 * the latest SunOS automounter and it is debatable whether there 857 * is any point bothering. 858 */ 859 if (strchr(*cp->ivec, '=') == 0) 860 p = sunos4_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info); 861 else 862 #endif 863 #endif /* SUNOS4_COMPAT */ 864 p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info); 865 866 /* 867 * Find a mounted filesystem for this node. 868 */ 869 mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, cp->fs_opts.opt_fs, 870 cp->fs_opts.fs_mtab, cp->auto_opts, cp->fs_opts.opt_opts, cp->fs_opts.opt_remopts); 871 872 p = mf->mf_ops; 873 #ifdef DEBUG 874 dlog("Got a hit with %s", p->fs_type); 875 #endif /* DEBUG */ 876 /* 877 * Note whether this is a real mount attempt 878 */ 879 if (p == &efs_ops) { 880 plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path); 881 if (this_error <= 0) 882 this_error = ENOENT; 883 continue; 884 } else { 885 if (cp->fs_opts.fs_mtab) { 886 plog(XLOG_MAP, "Trying mount of %s on %s fstype %s", 887 cp->fs_opts.fs_mtab, mp->am_path, p->fs_type); 888 } 889 cp->tried = TRUE; 890 } 891 892 this_error = 0; 893 dont_retry = FALSE; 894 895 if (mp->am_link) { 896 free(mp->am_link); 897 mp->am_link = 0; 898 } 899 900 link_dir = mf->mf_fo->opt_sublink; 901 902 if (link_dir && *link_dir) { 903 if (*link_dir == '/') { 904 mp->am_link = strdup(link_dir); 905 } else { 906 mp->am_link = str3cat((char *) 0, 907 mf->mf_fo->opt_fs, "/", link_dir); 908 normalize_slash(mp->am_link); 909 } 910 } 911 912 if (mf->mf_error > 0) { 913 this_error = mf->mf_error; 914 } else if (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)) { 915 /* 916 * Still mounting - retry later 917 */ 918 #ifdef DEBUG 919 dlog("Duplicate pending mount fstype %s", p->fs_type); 920 #endif /* DEBUG */ 921 this_error = -1; 922 } else if (FSRV_ISDOWN(mf->mf_server)) { 923 /* 924 * Would just mount from the same place 925 * as a hung mount - so give up 926 */ 927 #ifdef DEBUG 928 dlog("%s is already hung - giving up", mf->mf_mount); 929 #endif /* DEBUG */ 930 mp_error = EWOULDBLOCK; 931 dont_retry = TRUE; 932 this_error = -1; 933 } else if (mf->mf_flags & MFF_MOUNTED) { 934 #ifdef DEBUG 935 dlog("duplicate mount of \"%s\" ...", mf->mf_info); 936 #endif /* DEBUG */ 937 /* 938 * Just call mounted() 939 */ 940 am_mounted(mp); 941 942 this_error = 0; 943 break; 944 } 945 946 /* 947 * Will usually need to play around with the mount nodes 948 * file attribute structure. This must be done here. 949 * Try and get things initialised, even if the fileserver 950 * is not known to be up. In the common case this will 951 * progress things faster. 952 */ 953 if (!this_error) { 954 /* 955 * Fill in attribute fields. 956 */ 957 if (mf->mf_ops->fs_flags & FS_DIRECTORY) 958 mk_fattr(mp, NFDIR); 959 else 960 mk_fattr(mp, NFLNK); 961 962 mp->am_fattr.fileid = mp->am_gen; 963 964 if (p->fs_init) 965 this_error = (*p->fs_init)(mf); 966 } 967 968 /* 969 * Make sure the fileserver is UP before doing any more work 970 */ 971 if (!FSRV_ISUP(mf->mf_server)) { 972 #ifdef DEBUG 973 dlog("waiting for server %s to become available", mf->mf_server->fs_host); 974 #endif 975 this_error = -1; 976 } 977 978 if (!this_error && mf->mf_fo->opt_delay) { 979 /* 980 * If there is a delay timer on the mount 981 * then don't try to mount if the timer 982 * has not expired. 983 */ 984 int i = atoi(mf->mf_fo->opt_delay); 985 if (i > 0 && clocktime() < (cp->start + i)) { 986 #ifdef DEBUG 987 dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start); 988 #endif /* DEBUG */ 989 this_error = -1; 990 } 991 } 992 993 if (this_error < 0 && !dont_retry) { 994 if (!mf_retry) 995 mf_retry = dup_mntfs(mf); 996 cp->retry = TRUE; 997 } 998 999 if (!this_error) 1000 if (p->fs_flags & FS_MBACKGROUND) { 1001 mf->mf_flags |= MFF_MOUNTING; /*XXX*/ 1002 #ifdef DEBUG 1003 dlog("backgrounding mount of \"%s\"", mf->mf_mount); 1004 #endif /* DEBUG */ 1005 if (cp->callout) { 1006 untimeout(cp->callout); 1007 cp->callout = 0; 1008 } 1009 run_task(try_mount, (voidp) mp, afs_cont, (voidp) cp); 1010 mf->mf_flags |= MFF_MKMNT; /* XXX */ 1011 if (mf_retry) free_mntfs(mf_retry); 1012 return -1; 1013 } else { 1014 #ifdef DEBUG 1015 dlog("foreground mount of \"%s\" ...", mf->mf_info); 1016 #endif /* DEBUG */ 1017 this_error = try_mount((voidp) mp); 1018 if (this_error < 0) { 1019 if (!mf_retry) 1020 mf_retry = dup_mntfs(mf); 1021 cp->retry = TRUE; 1022 } 1023 } 1024 1025 if (this_error >= 0) { 1026 if (this_error > 0) { 1027 amd_stats.d_merr++; 1028 if (mf != mf_retry) { 1029 mf->mf_error = this_error; 1030 mf->mf_flags |= MFF_ERROR; 1031 } 1032 } 1033 /* 1034 * Wakeup anything waiting for this mount 1035 */ 1036 wakeup((voidp) mf); 1037 } 1038 } 1039 1040 if (this_error && cp->retry) { 1041 free_mntfs(mf); 1042 mf = cp->mp->am_mnt = mf_retry; 1043 /* 1044 * Not retrying again (so far) 1045 */ 1046 cp->retry = FALSE; 1047 cp->tried = FALSE; 1048 /* 1049 * Start at the beginning. 1050 * Rewind the location vector and 1051 * reset the default options. 1052 */ 1053 cp->ivec = cp->xivec; 1054 cp->def_opts = strealloc(cp->def_opts, cp->auto_opts); 1055 /* 1056 * Arrange that afs_bgmount is called 1057 * after anything else happens. 1058 */ 1059 #ifdef DEBUG 1060 dlog("Arranging to retry mount of %s", cp->mp->am_path); 1061 #endif /* DEBUG */ 1062 sched_task(afs_retry, (voidp) cp, (voidp) mf); 1063 if (cp->callout) 1064 untimeout(cp->callout); 1065 cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf); 1066 1067 cp->mp->am_ttl = clocktime() + RETRY_INTERVAL; 1068 1069 /* 1070 * Not done yet - so don't return anything 1071 */ 1072 return -1; 1073 } 1074 1075 if (hard_error < 0 || this_error == 0) 1076 hard_error = this_error; 1077 1078 /* 1079 * Discard handle on duff filesystem. 1080 * This should never happen since it 1081 * should be caught by the case above. 1082 */ 1083 if (mf_retry) { 1084 if (hard_error) 1085 plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount); 1086 free_mntfs(mf_retry); 1087 } 1088 1089 /* 1090 * If we get here, then either the mount succeeded or 1091 * there is no more mount information available. 1092 */ 1093 if (hard_error < 0 && mp_error) 1094 hard_error = cp->mp->am_error = mp_error; 1095 if (hard_error > 0) { 1096 /* 1097 * Set a small(ish) timeout on an error node if 1098 * the error was not a time out. 1099 */ 1100 switch (hard_error) { 1101 case ETIMEDOUT: 1102 case EWOULDBLOCK: 1103 cp->mp->am_timeo = 5; 1104 break; 1105 default: 1106 cp->mp->am_timeo = 17; 1107 break; 1108 } 1109 new_ttl(cp->mp); 1110 } 1111 1112 /* 1113 * Make sure that the error value in the mntfs has a 1114 * reasonable value. 1115 */ 1116 if (mf->mf_error < 0) { 1117 mf->mf_error = hard_error; 1118 if (hard_error) 1119 mf->mf_flags |= MFF_ERROR; 1120 } 1121 1122 /* 1123 * In any case we don't need the continuation any more 1124 */ 1125 free_continuation(cp); 1126 1127 return hard_error; 1128 } 1129 1130 /* 1131 * Automount interface to RPC lookup routine 1132 */ 1133 static am_node *afs_lookuppn P((am_node *mp, char *fname, int *error_return, int op)); 1134 static am_node *afs_lookuppn(mp, fname, error_return, op) 1135 am_node *mp; 1136 char *fname; 1137 int *error_return; 1138 int op; 1139 { 1140 #define ereturn(x) { *error_return = x; return 0; } 1141 1142 /* 1143 * Find the corresponding entry and return 1144 * the file handle for it. 1145 */ 1146 am_node *ap, *new_mp, *ap_hung; 1147 char *info; /* Mount info - where to get the file system */ 1148 char **ivec, **xivec; /* Split version of info */ 1149 char *auto_opts; /* Automount options */ 1150 int error = 0; /* Error so far */ 1151 char path_name[MAXPATHLEN]; /* General path name buffer */ 1152 char *pfname; /* Path for database lookup */ 1153 struct continuation *cp; /* Continuation structure if we need to mount */ 1154 int in_progress = 0; /* # of (un)mount in progress */ 1155 char *dflts; 1156 mntfs *mf; 1157 1158 #ifdef DEBUG 1159 dlog("in afs_lookuppn"); 1160 #endif /* DEBUG */ 1161 1162 /* 1163 * If the server is shutting down 1164 * then don't return information 1165 * about the mount point. 1166 */ 1167 if (amd_state == Finishing) { 1168 #ifdef DEBUG 1169 if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &dfs_ops) 1170 dlog("%s mount ignored - going down", fname); 1171 else 1172 dlog("%s/%s mount ignored - going down", mp->am_path, fname); 1173 #endif /* DEBUG */ 1174 ereturn(ENOENT); 1175 } 1176 1177 /* 1178 * Handle special case of "." and ".." 1179 */ 1180 if (fname[0] == '.') { 1181 if (fname[1] == '\0') 1182 return mp; /* "." is the current node */ 1183 if (fname[1] == '.' && fname[2] == '\0') { 1184 if (mp->am_parent) { 1185 #ifdef DEBUG 1186 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); 1187 #endif /* DEBUG */ 1188 return mp->am_parent; /* ".." is the parent node */ 1189 } 1190 ereturn(ESTALE); 1191 } 1192 } 1193 1194 /* 1195 * Check for valid key name. 1196 * If it is invalid then pretend it doesn't exist. 1197 */ 1198 if (!valid_key(fname)) { 1199 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); 1200 ereturn(ENOENT); 1201 } 1202 1203 /* 1204 * Expand key name. 1205 * fname is now a private copy. 1206 */ 1207 fname = expand_key(fname); 1208 1209 for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) { 1210 /* 1211 * Otherwise search children of this node 1212 */ 1213 if (FSTREQ(ap->am_name, fname)) { 1214 mf = ap->am_mnt; 1215 if (ap->am_error) { 1216 error = ap->am_error; 1217 continue; 1218 } 1219 1220 /* 1221 * If the error code is undefined then it must be 1222 * in progress. 1223 */ 1224 if (mf->mf_error < 0) 1225 goto in_progrss; 1226 1227 /* 1228 * Check for a hung node 1229 */ 1230 if (FSRV_ISDOWN(mf->mf_server)) { 1231 #ifdef DEBUG 1232 dlog("server hung"); 1233 #endif /* DEBUG */ 1234 error = ap->am_error; 1235 ap_hung = ap; 1236 continue; 1237 } 1238 1239 /* 1240 * If there was a previous error with this node 1241 * then return that error code. 1242 */ 1243 if (mf->mf_flags & MFF_ERROR) { 1244 error = mf->mf_error; 1245 continue; 1246 } 1247 1248 if (!(mf->mf_flags & MFF_MOUNTED) /*|| (mf->mf_flags & MFF_UNMOUNTING)*/) { 1249 in_progrss: 1250 /* 1251 * If the fs is not mounted or it is unmounting then there 1252 * is a background (un)mount in progress. In this case 1253 * we just drop the RPC request (return nil) and 1254 * wait for a retry, by which time the (un)mount may 1255 * have completed. 1256 */ 1257 #ifdef DEBUG 1258 dlog("ignoring mount of %s in %s -- in progress", 1259 fname, mf->mf_mount); 1260 #endif /* DEBUG */ 1261 in_progress++; 1262 continue; 1263 } 1264 1265 /* 1266 * Otherwise we have a hit: return the current mount point. 1267 */ 1268 #ifdef DEBUG 1269 dlog("matched %s in %s", fname, ap->am_path); 1270 #endif /* DEBUG */ 1271 free(fname); 1272 return ap; 1273 } 1274 } 1275 1276 if (in_progress) { 1277 #ifdef DEBUG 1278 dlog("Waiting while %d mount(s) in progress", in_progress); 1279 #endif /* DEBUG */ 1280 free(fname); 1281 ereturn(-1); 1282 } 1283 1284 /* 1285 * If an error occured then return it. 1286 */ 1287 if (error) { 1288 #ifdef DEBUG 1289 errno = error; /* XXX */ 1290 dlog("Returning error: %m", error); 1291 #endif /* DEBUG */ 1292 free(fname); 1293 ereturn(error); 1294 } 1295 1296 /* 1297 * If doing a delete then don't create again! 1298 */ 1299 switch (op) { 1300 case VLOOK_DELETE: 1301 ereturn(ENOENT); 1302 break; 1303 1304 case VLOOK_CREATE: 1305 break; 1306 1307 default: 1308 plog(XLOG_FATAL, "Unknown op to afs_lookuppn: 0x%x", op); 1309 ereturn(EINVAL); 1310 break; 1311 } 1312 1313 /* 1314 * If the server is going down then just return, 1315 * don't try to mount any more file systems 1316 */ 1317 if ((int)amd_state >= (int)Finishing) { 1318 #ifdef DEBUG 1319 dlog("not found - server going down anyway"); 1320 #endif /* DEBUG */ 1321 free(fname); 1322 ereturn(ENOENT); 1323 } 1324 1325 /* 1326 * If we get there then this is a reference to an, 1327 * as yet, unknown name so we need to search the mount 1328 * map for it. 1329 */ 1330 if (mp->am_pref) { 1331 sprintf(path_name, "%s%s", mp->am_pref, fname); 1332 pfname = path_name; 1333 } else { 1334 pfname = fname; 1335 } 1336 1337 mf = mp->am_mnt; 1338 1339 #ifdef DEBUG 1340 dlog("will search map info in %s to find %s", mf->mf_info, pfname); 1341 #endif /* DEBUG */ 1342 /* 1343 * Consult the oracle for some mount information. 1344 * info is malloc'ed and belongs to this routine. 1345 * It ends up being free'd in free_continuation(). 1346 * 1347 * Note that this may return -1 indicating that information 1348 * is not yet available. 1349 */ 1350 error = mapc_search((mnt_map*) mf->mf_private, pfname, &info); 1351 if (error) { 1352 if (error > 0) 1353 plog(XLOG_MAP, "No map entry for %s", pfname); 1354 else 1355 plog(XLOG_MAP, "Waiting on map entry for %s", pfname); 1356 free(fname); 1357 ereturn(error); 1358 } 1359 1360 #ifdef DEBUG 1361 dlog("mount info is %s", info); 1362 #endif /* DEBUG */ 1363 1364 /* 1365 * Split info into an argument vector. 1366 * The vector is malloc'ed and belongs to 1367 * this routine. It is free'd in free_continuation() 1368 */ 1369 xivec = ivec = strsplit(info, ' ', '\"'); 1370 1371 /* 1372 * Default error code... 1373 */ 1374 if (ap_hung) 1375 error = EWOULDBLOCK; 1376 else 1377 error = ENOENT; 1378 1379 /* 1380 * Allocate a new map 1381 */ 1382 new_mp = exported_ap_alloc(); 1383 if (new_mp == 0) { 1384 free((voidp) xivec); 1385 free((voidp) info); 1386 free((voidp) fname); 1387 ereturn(ENOSPC); 1388 } 1389 1390 if (mf->mf_auto) 1391 auto_opts = mf->mf_auto; 1392 else 1393 auto_opts = ""; 1394 1395 auto_opts = strdup(auto_opts); 1396 1397 #ifdef DEBUG 1398 dlog("searching for /defaults entry"); 1399 #endif /* DEBUG */ 1400 if (mapc_search((mnt_map*) mf->mf_private, "/defaults", &dflts) == 0) { 1401 char *dfl; 1402 char **rvec; 1403 #ifdef DEBUG 1404 dlog("/defaults gave %s", dflts); 1405 #endif /* DEBUG */ 1406 if (*dflts == '-') 1407 dfl = dflts+1; 1408 else 1409 dfl = dflts; 1410 1411 /* 1412 * Chop the defaults up 1413 */ 1414 rvec = strsplit(dfl, ' ', '\"'); 1415 /* 1416 * Extract first value 1417 */ 1418 dfl = rvec[0]; 1419 1420 /* 1421 * If there were any values at all... 1422 */ 1423 if (dfl) { 1424 /* 1425 * Log error if there were other values 1426 */ 1427 if (rvec[1]) { 1428 #ifdef DEBUG 1429 dlog("/defaults chopped into %s", dfl); 1430 #endif /* DEBUG */ 1431 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); 1432 } 1433 1434 /* 1435 * Prepend to existing defaults if they exist, 1436 * otherwise just use these defaults. 1437 */ 1438 if (*auto_opts && *dfl) { 1439 char *nopts = (char *) xmalloc(strlen(auto_opts)+strlen(dfl)+2); 1440 sprintf(nopts, "%s;%s", dfl, auto_opts); 1441 free(auto_opts); 1442 auto_opts = nopts; 1443 } else if (*dfl) { 1444 auto_opts = strealloc(auto_opts, dfl); 1445 } 1446 } 1447 free(dflts); 1448 /* 1449 * Don't need info vector any more 1450 */ 1451 free((voidp) rvec); 1452 } 1453 1454 /* 1455 * Fill it in 1456 */ 1457 init_map(new_mp, fname); 1458 1459 /* 1460 * Put it in the table 1461 */ 1462 insert_am(new_mp, mp); 1463 1464 /* 1465 * Fill in some other fields, 1466 * path and mount point. 1467 * 1468 * bugfix: do not prepend old am_path if direct map 1469 * <wls@astro.umd.edu> William Sebok 1470 */ 1471 new_mp->am_path = str3cat(new_mp->am_path, 1472 mf->mf_ops == &dfs_ops ? "" : mp->am_path, 1473 *fname == '/' ? "" : "/", fname); 1474 1475 #ifdef DEBUG 1476 dlog("setting path to %s", new_mp->am_path); 1477 #endif /* DEBUG */ 1478 1479 /* 1480 * Take private copy of pfname 1481 */ 1482 pfname = strdup(pfname); 1483 1484 /* 1485 * Construct a continuation 1486 */ 1487 cp = ALLOC(continuation); 1488 cp->mp = new_mp; 1489 cp->xivec = xivec; 1490 cp->ivec = ivec; 1491 cp->info = info; 1492 cp->key = pfname; 1493 cp->auto_opts = auto_opts; 1494 cp->retry = FALSE; 1495 cp->tried = FALSE; 1496 cp->start = clocktime(); 1497 cp->def_opts = strdup(auto_opts); 1498 bzero((voidp) &cp->fs_opts, sizeof(cp->fs_opts)); 1499 1500 /* 1501 * Try and mount the file system 1502 * If this succeeds immediately (possible 1503 * for a ufs file system) then return 1504 * the attributes, otherwise just 1505 * return an error. 1506 */ 1507 error = afs_bgmount(cp, error); 1508 reschedule_timeout_mp(); 1509 if (!error) { 1510 free(fname); 1511 return new_mp; 1512 } 1513 1514 if (error && (cp->mp->am_mnt->mf_ops == &efs_ops)) 1515 cp->mp->am_error = error; 1516 1517 assign_error_mntfs(new_mp); 1518 1519 free(fname); 1520 1521 ereturn(error); 1522 #undef ereturn 1523 } 1524 1525 /* 1526 * Locate next node in sibling list which is mounted 1527 * and is not an error node. 1528 */ 1529 static am_node *next_nonerror_node P((am_node *xp)); 1530 static am_node *next_nonerror_node(xp) 1531 am_node *xp; 1532 { 1533 mntfs *mf; 1534 1535 /* 1536 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> 1537 * Fixes a race condition when mounting direct automounts. 1538 * Also fixes a problem when doing a readdir on a directory 1539 * containing hung automounts. 1540 */ 1541 while (xp && 1542 (!(mf = xp->am_mnt) || /* No mounted filesystem */ 1543 mf->mf_error != 0 || /* There was a mntfs error */ 1544 xp->am_error != 0 || /* There was a mount error */ 1545 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ 1546 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ 1547 ) 1548 xp = xp->am_osib; 1549 1550 return xp; 1551 } 1552 1553 static int afs_readdir P((am_node *mp, nfscookie cookie, struct dirlist *dp, struct entry *ep, int count)); 1554 static int afs_readdir(mp, cookie, dp, ep, count) 1555 am_node *mp; 1556 nfscookie cookie; 1557 struct dirlist *dp; 1558 struct entry *ep; 1559 int count; 1560 { 1561 unsigned int gen = *(unsigned int*) cookie; 1562 am_node *xp; 1563 1564 dp->eof = FALSE; 1565 1566 if (gen == 0) { 1567 /* 1568 * In the default instance (which is used to 1569 * start a search) we return "." and "..". 1570 * 1571 * This assumes that the count is big enough 1572 * to allow both "." and ".." to be returned in 1573 * a single packet. If it isn't (which would 1574 * be fairly unbelievable) then tough. 1575 */ 1576 #ifdef DEBUG 1577 dlog("default search"); 1578 #endif /* DEBUG */ 1579 /* 1580 * Check for enough room. This is extremely 1581 * approximate but is more than enough space. 1582 * Really need 2 times: 1583 * 4byte fileid 1584 * 4byte cookie 1585 * 4byte name length 1586 * 4byte name 1587 * plus the dirlist structure 1588 */ 1589 if (count < 1590 (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) 1591 + sizeof(*dp)))) 1592 return EINVAL; 1593 1594 xp = next_nonerror_node(mp->am_child); 1595 dp->entries = ep; 1596 1597 /* construct "." */ 1598 ep[0].fileid = mp->am_gen; 1599 ep[0].name = "."; 1600 ep[0].nextentry = &ep[1]; 1601 *(unsigned int *) ep[0].cookie = 0; 1602 1603 /* construct ".." */ 1604 if (mp->am_parent) 1605 ep[1].fileid = mp->am_parent->am_gen; 1606 else 1607 ep[1].fileid = mp->am_gen; 1608 ep[1].name = ".."; 1609 ep[1].nextentry = 0; 1610 *(unsigned int *) ep[1].cookie = 1611 xp ? xp->am_gen : ~(unsigned int)0; 1612 1613 if (!xp) dp->eof = TRUE; 1614 return 0; 1615 } 1616 1617 #ifdef DEBUG 1618 dlog("real child"); 1619 #endif /* DEBUG */ 1620 1621 if (gen == ~(unsigned int)0) { 1622 #ifdef DEBUG 1623 dlog("End of readdir in %s", mp->am_path); 1624 #endif /* DEBUG */ 1625 dp->eof = TRUE; 1626 dp->entries = 0; 1627 return 0; 1628 } 1629 1630 xp = mp->am_child; 1631 while (xp && xp->am_gen != gen) 1632 xp = xp->am_osib; 1633 1634 if (xp) { 1635 int nbytes = count / 2; /* conservative */ 1636 int todo = MAX_READDIR_ENTRIES; 1637 dp->entries = ep; 1638 do { 1639 am_node *xp_next = next_nonerror_node(xp->am_osib); 1640 1641 if (xp_next) { 1642 *(unsigned int *) ep->cookie = xp_next->am_gen; 1643 } else { 1644 *(unsigned int *) ep->cookie = ~(unsigned int)0; 1645 dp->eof = TRUE; 1646 } 1647 1648 ep->fileid = xp->am_gen; 1649 ep->name = xp->am_name; 1650 nbytes -= sizeof(*ep) + strlen(xp->am_name) + 1; 1651 1652 xp = xp_next; 1653 1654 if (nbytes > 0 && !dp->eof && todo > 1) { 1655 ep->nextentry = ep + 1; 1656 ep++; 1657 --todo; 1658 } else { 1659 todo = 0; 1660 } 1661 } while (todo > 0); 1662 1663 ep->nextentry = 0; 1664 1665 return 0; 1666 } 1667 1668 return ESTALE; 1669 1670 } 1671 1672 static am_node *dfs_readlink P((am_node *mp, int *error_return)); 1673 static am_node *dfs_readlink(mp, error_return) 1674 am_node *mp; 1675 int *error_return; 1676 { 1677 am_node *xp; 1678 int rc = 0; 1679 1680 xp = next_nonerror_node(mp->am_child); 1681 if (!xp) { 1682 if (!mp->am_mnt->mf_private) 1683 afs_mkcacheref(mp->am_mnt); /* XXX */ 1684 xp = afs_lookuppn(mp, mp->am_path+1, &rc, VLOOK_CREATE); 1685 } 1686 1687 if (xp) { 1688 new_ttl(xp); /* (7/12/89) from Rein Tollevik */ 1689 return xp; 1690 } 1691 if (amd_state == Finishing) 1692 rc = ENOENT; 1693 *error_return = rc; 1694 return 0; 1695 } 1696 1697 /* 1698 * Ops structure 1699 */ 1700 am_ops root_ops = { 1701 "root", 1702 0, /* root_match */ 1703 0, /* root_init */ 1704 root_mount, 1705 0, 1706 afs_umount, 1707 0, 1708 afs_lookuppn, 1709 afs_readdir, 1710 0, /* root_readlink */ 1711 0, /* root_mounted */ 1712 0, /* root_umounted */ 1713 find_afs_srvr, 1714 FS_NOTIMEOUT|FS_AMQINFO|FS_DIRECTORY 1715 }; 1716 1717 am_ops afs_ops = { 1718 "auto", 1719 afs_match, 1720 0, /* afs_init */ 1721 afs_mount, 1722 0, 1723 afs_umount, 1724 0, 1725 afs_lookuppn, 1726 afs_readdir, 1727 0, /* afs_readlink */ 1728 0, /* afs_mounted */ 1729 afs_umounted, 1730 find_afs_srvr, 1731 FS_AMQINFO|FS_DIRECTORY 1732 }; 1733 1734 am_ops toplvl_ops = { 1735 "toplvl", 1736 afs_match, 1737 0, /* afs_init */ 1738 toplvl_mount, 1739 0, 1740 toplvl_umount, 1741 0, 1742 afs_lookuppn, 1743 afs_readdir, 1744 0, /* toplvl_readlink */ 1745 toplvl_mounted, 1746 0, /* toplvl_umounted */ 1747 find_afs_srvr, 1748 FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO|FS_DIRECTORY 1749 }; 1750 1751 am_ops dfs_ops = { 1752 "direct", 1753 afs_match, 1754 0, /* dfs_init */ 1755 toplvl_mount, 1756 0, 1757 toplvl_umount, 1758 0, 1759 efs_lookuppn, 1760 efs_readdir, 1761 dfs_readlink, 1762 toplvl_mounted, 1763 0, /* afs_umounted */ 1764 find_afs_srvr, 1765 FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO 1766 }; 1767 1768 #ifdef HAS_UNION_FS 1769 am_ops union_ops = { 1770 "union", 1771 afs_match, 1772 0, /* afs_init */ 1773 toplvl_mount, 1774 0, 1775 toplvl_umount, 1776 0, 1777 afs_lookuppn, 1778 afs_readdir, 1779 0, /* toplvl_readlink */ 1780 union_mounted, 1781 0, /* toplvl_umounted */ 1782 find_afs_srvr, 1783 FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO|FS_DIRECTORY 1784 }; 1785 #endif /* HAS_UNION_FS */ 1786