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