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