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