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