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