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