Parallelize in_ifaddrhead operation
[dragonfly.git] / contrib / amd / amd / ops_autofs.c
blobda77af1f6086c2b2222dd89367980d45a07cb753
1 /*
2 * Copyright (c) 1997-1999 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
39 * %W% (Berkeley) %G%
41 * $Id: ops_autofs.c,v 1.4 1999/01/13 23:31:00 ezk Exp $
46 * Automounter filesystem
49 #ifdef HAVE_CONFIG_H
50 # include <config.h>
51 #endif /* HAVE_CONFIG_H */
52 #include <am_defs.h>
53 #include <amd.h>
56 * KLUDGE: wrap whole file in HAVE_FS_AUTOFS, because
57 * not all systems with an automounter file system are supported
58 * by am-utils yet...
61 #ifdef HAVE_FS_AUTOFS
64 * MACROS:
66 #ifndef AUTOFS_NULL
67 # define AUTOFS_NULL ((u_long)0)
68 #endif /* not AUTOFS_NULL */
71 * VARIABLES:
74 /* forward declarations */
75 static int mount_autofs(char *dir, char *opts);
76 static int autofs_mount_1_svc(struct mntrequest *mr, struct mntres *result, struct authunix_parms *cred);
77 static int autofs_unmount_1_svc(struct umntrequest *ur, struct umntres *result, struct authunix_parms *cred);
79 /* external declarations */
80 extern bool_t xdr_mntrequest(XDR *, mntrequest *);
81 extern bool_t xdr_mntres(XDR *, mntres *);
82 extern bool_t xdr_umntrequest(XDR *, umntrequest *);
83 extern bool_t xdr_umntres(XDR *, umntres *);
86 * STRUCTURES:
89 /* Sun's kernel-based automounter-supporting file system */
90 am_ops autofs_ops =
92 "autofs",
93 amfs_auto_match,
94 0, /* amfs_auto_init */
95 autofs_mount,
97 autofs_umount,
99 amfs_auto_lookuppn,
100 amfs_auto_readdir, /* browsable version of readdir() */
101 0, /* autofs_readlink */
102 autofs_mounted,
103 0, /* autofs_umounted */
104 find_amfs_auto_srvr,
105 FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY
109 /****************************************************************************
110 *** FUNCTIONS ***
111 ****************************************************************************/
114 * Mount the top-level using autofs
117 autofs_mount(am_node *mp)
119 mntfs *mf = mp->am_mnt;
120 struct stat stb;
121 char opts[256], preopts[256];
122 int error;
123 char *mnttype;
126 * Mounting the automounter.
127 * Make sure the mount directory exists, construct
128 * the mount options and call the mount_autofs routine.
131 if (stat(mp->am_path, &stb) < 0) {
132 return errno;
133 } else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
134 plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
135 return ENOTDIR;
137 if (mf->mf_ops == &autofs_ops)
138 mnttype = "indirect";
139 else if (mf->mf_ops == &amfs_direct_ops)
140 mnttype = "direct";
141 #ifdef HAVE_AM_FS_UNION
142 else if (mf->mf_ops == &amfs_union_ops)
143 mnttype = "union";
144 #endif /* HAVE_AM_FS_UNION */
145 else
146 mnttype = "auto";
149 * Construct some mount options:
151 * Tack on magic map=<mapname> option in mtab to emulate
152 * SunOS automounter behavior.
154 preopts[0] = '\0';
155 #ifdef MNTTAB_OPT_INTR
156 strcat(preopts, MNTTAB_OPT_INTR);
157 strcat(preopts, ",");
158 #endif /* MNTTAB_OPT_INTR */
159 #ifdef MNTTAB_OPT_IGNORE
160 strcat(preopts, MNTTAB_OPT_IGNORE);
161 strcat(preopts, ",");
162 #endif /* MNTTAB_OPT_IGNORE */
163 sprintf(opts, "%s%s,%s=%d,%s=%d,%s=%d,%s,map=%s",
164 preopts,
165 MNTTAB_OPT_RW,
166 MNTTAB_OPT_PORT, nfs_port,
167 MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo,
168 MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans,
169 mnttype, mf->mf_info);
171 /* now do the mount */
172 error = mount_autofs(mf->mf_mount, opts);
173 if (error) {
174 errno = error;
175 plog(XLOG_FATAL, "mount_autofs: %m");
176 return error;
178 return 0;
182 void
183 autofs_mounted(mntfs *mf)
185 amfs_auto_mkcacheref(mf);
190 * Unmount a top-level automount node
193 autofs_umount(am_node *mp)
195 int error;
196 struct stat stb;
199 * The lstat is needed if this mount is type=direct. When that happens,
200 * the kernel cache gets confused between the underlying type (dir) and
201 * the mounted type (link) and so needs to be re-synced before the
202 * unmount. This is all because the unmount system call follows links and
203 * so can't actually unmount a link (stupid!). It was noted that doing an
204 * ls -ld of the mount point to see why things were not working actually
205 * fixed the problem - so simulate an ls -ld here.
207 if (lstat(mp->am_path, &stb) < 0) {
208 #ifdef DEBUG
209 dlog("lstat(%s): %m", mp->am_path);
210 #endif /* DEBUG */
212 error = UMOUNT_FS(mp->am_path, mnttab_file_name);
213 if (error == EBUSY && mp->am_flags & AMF_AUTOFS) {
214 plog(XLOG_WARNING, "autofs_unmount of %s busy (autofs). exit", mp->am_path);
215 error = 0; /* fake unmount was ok */
217 return error;
222 * Mount an automounter directory.
223 * The automounter is connected into the system
224 * as a user-level NFS server. mount_autofs constructs
225 * the necessary NFS parameters to be given to the
226 * kernel so that it will talk back to us.
228 static int
229 mount_autofs(char *dir, char *opts)
231 char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
232 char *map_opt, buf[MAXHOSTNAMELEN];
233 int retry, error, flags;
234 struct utsname utsname;
235 mntent_t mnt;
236 autofs_args_t autofs_args;
237 MTYPE_TYPE type = MOUNT_TYPE_AUTOFS;
239 memset((voidp) &autofs_args, 0, sizeof(autofs_args)); /* Paranoid */
241 memset((voidp) &mnt, 0, sizeof(mnt));
242 mnt.mnt_dir = dir;
243 mnt.mnt_fsname = pid_fsname;
244 mnt.mnt_opts = opts;
245 mnt.mnt_type = type;
247 retry = hasmntval(&mnt, "retry");
248 if (retry <= 0)
249 retry = 2; /* XXX */
252 * SET MOUNT ARGS
254 if (uname(&utsname) < 0) {
255 strcpy(buf, "localhost.autofs");
256 } else {
257 strcpy(buf, utsname.nodename);
258 strcat(buf, ".autofs");
260 #ifdef HAVE_FIELD_AUTOFS_ARGS_T_ADDR
261 autofs_args.addr.buf = buf;
262 autofs_args.addr.len = strlen(autofs_args.addr.buf);
263 autofs_args.addr.maxlen = autofs_args.addr.len;
264 #endif /* HAVE_FIELD_AUTOFS_ARGS_T_ADDR */
266 autofs_args.path = dir;
267 autofs_args.opts = opts;
269 map_opt = hasmntopt(&mnt, "map");
270 if (map_opt) {
271 map_opt += sizeof("map="); /* skip the "map=" */
272 if (map_opt == NULL) {
273 plog(XLOG_WARNING, "map= has a null map name. reset to amd.unknown");
274 map_opt = "amd.unknown";
277 autofs_args.map = map_opt;
279 /* XXX: these I set arbitrarily... */
280 autofs_args.mount_to = 300;
281 autofs_args.rpc_to = 60;
282 autofs_args.direct = 0;
285 * Make a ``hostname'' string for the kernel
287 sprintf(fs_hostname, "pid%ld@%s:%s",
288 (long) (foreground ? am_mypid : getppid()),
289 am_get_hostname(), dir);
292 * Most kernels have a name length restriction.
294 if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
295 strcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..");
298 * Finally we can compute the mount flags set above.
300 flags = compute_mount_flags(&mnt);
303 * This is it! Here we try to mount amd on its mount points.
305 error = mount_fs(&mnt, flags, (caddr_t) &autofs_args, retry, type, 0, NULL, mnttab_file_name);
306 return error;
310 /****************************************************************************/
311 /* autofs program dispatcher */
312 void
313 autofs_program_1(struct svc_req *rqstp, SVCXPRT *transp)
315 int ret;
316 union {
317 mntrequest autofs_mount_1_arg;
318 umntrequest autofs_umount_1_arg;
319 } argument;
320 union {
321 mntres mount_res;
322 umntres umount_res;
323 } result;
325 bool_t (*xdr_argument)(), (*xdr_result)();
326 int (*local)();
328 switch (rqstp->rq_proc) {
330 case AUTOFS_NULL:
331 svc_sendreply(transp,
332 (XDRPROC_T_TYPE) xdr_void,
333 (SVC_IN_ARG_TYPE) NULL);
334 return;
336 case AUTOFS_MOUNT:
337 xdr_argument = xdr_mntrequest;
338 xdr_result = xdr_mntres;
339 local = (int (*)()) autofs_mount_1_svc;
340 break;
342 case AUTOFS_UNMOUNT:
343 xdr_argument = xdr_umntrequest;
344 xdr_result = xdr_umntres;
345 local = (int (*)()) autofs_unmount_1_svc;
346 break;
348 default:
349 svcerr_noproc(transp);
350 return;
353 memset((char *) &argument, 0, sizeof(argument));
354 if (!svc_getargs(transp,
355 (XDRPROC_T_TYPE) xdr_argument,
356 (SVC_IN_ARG_TYPE) &argument)) {
357 plog(XLOG_ERROR,
358 "AUTOFS xdr decode failed for %d %d %d",
359 (int) rqstp->rq_prog, (int) rqstp->rq_vers, (int) rqstp->rq_proc);
360 svcerr_decode(transp);
361 return;
364 ret = (*local) (&argument, &result, rqstp);
365 if (!svc_sendreply(transp,
366 (XDRPROC_T_TYPE) xdr_result,
367 (SVC_IN_ARG_TYPE) &result)) {
368 svcerr_systemerr(transp);
371 if (!svc_freeargs(transp,
372 (XDRPROC_T_TYPE) xdr_argument,
373 (SVC_IN_ARG_TYPE) &argument)) {
374 plog(XLOG_FATAL, "unable to free rpc arguments in autofs_program_1");
375 going_down(1);
380 static int
381 autofs_mount_1_svc(struct mntrequest *mr, struct mntres *result, struct authunix_parms *cred)
383 int err = 0;
384 am_node *anp, *anp2;
386 plog(XLOG_INFO, "XXX: autofs_mount_1_svc: %s:%s:%s:%s",
387 mr->map, mr->name, mr->opts, mr->path);
389 /* look for map (eg. "/home") */
390 anp = find_ap(mr->path);
391 if (!anp) {
392 plog(XLOG_ERROR, "map %s not found", mr->path);
393 err = ENOENT;
394 goto out;
396 /* turn on autofs in map flags */
397 if (!(anp->am_flags & AMF_AUTOFS)) {
398 plog(XLOG_INFO, "turning on AMF_AUTOFS for node %s", mr->path);
399 anp->am_flags |= AMF_AUTOFS;
403 * Look for (and create if needed) the new node.
405 * If an error occurred, return it. If a -1 was returned, that indicates
406 * that a mount is in progress, so sleep a while (while the backgrounded
407 * mount is happening), and then signal the autofs to retry the mount.
409 * There's something I don't understand. I was thinking that this code
410 * here is the one which will succeed eventually and will send an RPC
411 * reply to the kernel, but apparently that happens somewhere else, not
412 * here. It works though, just that I don't know how. Arg. -Erez.
413 * */
414 err = 0;
415 anp2 = autofs_lookuppn(anp, mr->name, &err, VLOOK_CREATE);
416 if (!anp2) {
417 if (err == -1) { /* then tell autofs to retry */
418 sleep(1);
419 err = EAGAIN;
421 goto out;
424 out:
425 result->status = err;
426 return err;
430 static int
431 autofs_unmount_1_svc(struct umntrequest *ur, struct umntres *result, struct authunix_parms *cred)
433 int err = 0;
435 #ifdef HAVE_FIELD_UMNTREQUEST_RDEVID
436 plog(XLOG_INFO, "XXX: autofs_unmount_1_svc: %d:%lu:%lu:0x%lx",
437 ur->isdirect, (unsigned long) ur->devid, (unsigned long) ur->rdevid,
438 (unsigned long) ur->next);
439 #else /* HAVE_FIELD_UMNTREQUEST_RDEVID */
440 plog(XLOG_INFO, "XXX: autofs_unmount_1_svc: %d:%lu:0x%lx",
441 ur->isdirect, (unsigned long) ur->devid,
442 (unsigned long) ur->next);
443 #endif /* HAVE_FIELD_UMNTREQUEST_RDEVID */
445 err = EINVAL; /* XXX: not implemented yet */
446 goto out;
448 out:
449 result->status = err;
450 return err;
455 * Pick a file system to try mounting and
456 * do that in the background if necessary
458 For each location:
459 if it is new -defaults then
460 extract and process
461 continue;
463 if it is a cut then
464 if a location has been tried then
465 break;
467 continue;
469 parse mount location
470 discard previous mount location if required
471 find matching mounted filesystem
472 if not applicable then
473 this_error = No such file or directory
474 continue
476 if the filesystem failed to be mounted then
477 this_error = error from filesystem
478 elif the filesystem is mounting or unmounting then
479 this_error = -1
480 elif the fileserver is down then
481 this_error = -1
482 elif the filesystem is already mounted
483 this_error = 0
484 break
486 if no error on this mount then
487 this_error = initialize mount point
489 if no error on this mount and mount is delayed then
490 this_error = -1
492 if this_error < 0 then
493 retry = true
495 if no error on this mount then
496 make mount point if required
498 if no error on this mount then
499 if mount in background then
500 run mount in background
501 return -1
502 else
503 this_error = mount in foreground
506 if an error occurred on this mount then
507 update stats
508 save error in mount point
510 endfor
512 static int
513 autofs_bgmount(struct continuation * cp, int mpe)
515 mntfs *mf = cp->mp->am_mnt; /* Current mntfs */
516 mntfs *mf_retry = 0; /* First mntfs which needed retrying */
517 int this_error = -1; /* Per-mount error */
518 int hard_error = -1;
519 int mp_error = mpe;
522 * Try to mount each location.
523 * At the end:
524 * hard_error == 0 indicates something was mounted.
525 * hard_error > 0 indicates everything failed with a hard error
526 * hard_error < 0 indicates nothing could be mounted now
528 for (; this_error && *cp->ivec; cp->ivec++) {
529 am_ops *p;
530 am_node *mp = cp->mp;
531 char *link_dir;
532 int dont_retry;
534 if (hard_error < 0)
535 hard_error = this_error;
537 this_error = -1;
539 if (**cp->ivec == '-') {
541 * Pick up new defaults
543 if (cp->auto_opts && *cp->auto_opts)
544 cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
545 else
546 cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
547 #ifdef DEBUG
548 dlog("Setting def_opts to \"%s\"", cp->def_opts);
549 #endif /* DEBUG */
550 continue;
553 * If a mount has been attempted, and we find
554 * a cut then don't try any more locations.
556 if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
557 if (cp->tried) {
558 #ifdef DEBUG
559 dlog("Cut: not trying any more locations for %s",
560 mp->am_path);
561 #endif /* DEBUG */
562 break;
564 continue;
567 /* match the operators */
568 p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
571 * Find a mounted filesystem for this node.
573 mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
574 cp->fs_opts.opt_fs,
575 cp->fs_opts.fs_mtab,
576 cp->auto_opts,
577 cp->fs_opts.opt_opts,
578 cp->fs_opts.opt_remopts);
580 p = mf->mf_ops;
581 #ifdef DEBUG
582 dlog("Got a hit with %s", p->fs_type);
583 #endif /* DEBUG */
586 * Note whether this is a real mount attempt
588 if (p == &amfs_error_ops) {
589 plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
590 if (this_error <= 0)
591 this_error = ENOENT;
592 continue;
593 } else {
594 if (cp->fs_opts.fs_mtab) {
595 plog(XLOG_MAP, "Trying mount of %s on \"%s\" fstype %s",
596 cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
598 cp->tried = TRUE;
601 this_error = 0;
602 dont_retry = FALSE;
604 if (mp->am_link) {
605 XFREE(mp->am_link);
606 mp->am_link = 0;
608 link_dir = mf->mf_fo->opt_sublink;
610 if (link_dir && *link_dir) {
611 if (*link_dir == '/') {
612 mp->am_link = strdup(link_dir);
613 } else {
615 * try getting fs option from continuation, not mountpoint!
616 * Don't try logging the string from mf, since it may be bad!
618 if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
619 plog(XLOG_ERROR, "use %s instead of 0x%lx",
620 cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs);
622 mp->am_link = str3cat((char *) 0,
623 cp->fs_opts.opt_fs, "/", link_dir);
625 normalize_slash(mp->am_link);
629 if (mf->mf_error > 0) {
630 this_error = mf->mf_error;
631 } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
633 * Still mounting - retry later
635 #ifdef DEBUG
636 dlog("Duplicate pending mount fstype %s", p->fs_type);
637 #endif /* DEBUG */
638 this_error = -1;
639 } else if (FSRV_ISDOWN(mf->mf_server)) {
641 * Would just mount from the same place
642 * as a hung mount - so give up
644 #ifdef DEBUG
645 dlog("%s is already hung - giving up", mf->mf_mount);
646 #endif /* DEBUG */
647 mp_error = EWOULDBLOCK;
648 dont_retry = TRUE;
649 this_error = -1;
650 } else if (mf->mf_flags & MFF_MOUNTED) {
651 #ifdef DEBUG
652 dlog("duplicate mount of \"%s\" ...", mf->mf_info);
653 #endif /* DEBUG */
656 * Just call mounted()
658 am_mounted(mp);
660 this_error = 0;
661 break;
665 * Will usually need to play around with the mount nodes
666 * file attribute structure. This must be done here.
667 * Try and get things initialized, even if the fileserver
668 * is not known to be up. In the common case this will
669 * progress things faster.
671 if (!this_error) {
673 * Fill in attribute fields.
675 if (mf->mf_ops->fs_flags & FS_DIRECTORY)
676 mk_fattr(mp, NFDIR);
677 else
678 mk_fattr(mp, NFLNK);
680 mp->am_fattr.na_fileid = mp->am_gen;
682 if (p->fs_init)
683 this_error = (*p->fs_init) (mf);
687 * Make sure the fileserver is UP before doing any more work
689 if (!FSRV_ISUP(mf->mf_server)) {
690 #ifdef DEBUG
691 dlog("waiting for server %s to become available", mf->mf_server->fs_host);
692 #endif /* DEBUG */
693 this_error = -1;
696 if (!this_error && mf->mf_fo->opt_delay) {
698 * If there is a delay timer on the mount
699 * then don't try to mount if the timer
700 * has not expired.
702 int i = atoi(mf->mf_fo->opt_delay);
703 if (i > 0 && clocktime() < (cp->start + i)) {
704 #ifdef DEBUG
705 dlog("Mount of %s delayed by %lds", mf->mf_mount, i - clocktime() + cp->start);
706 #endif /* DEBUG */
707 this_error = -1;
711 if (this_error < 0 && !dont_retry) {
712 if (!mf_retry)
713 mf_retry = dup_mntfs(mf);
714 cp->retry = TRUE;
717 if (!this_error) {
718 if (p->fs_flags & FS_MBACKGROUND) {
719 mf->mf_flags |= MFF_MOUNTING; /* XXX */
720 #ifdef DEBUG
721 dlog("backgrounding mount of \"%s\"", mf->mf_mount);
722 #endif /* DEBUG */
723 if (cp->callout) {
724 untimeout(cp->callout);
725 cp->callout = 0;
727 run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
728 mf->mf_flags |= MFF_MKMNT; /* XXX */
729 if (mf_retry)
730 free_mntfs(mf_retry);
731 return -1;
732 } else {
733 #ifdef DEBUG
734 dlog("foreground mount of \"%s\" ...", mf->mf_info);
735 #endif /* DEBUG */
736 this_error = try_mount((voidp) mp);
737 if (this_error < 0) {
738 if (!mf_retry)
739 mf_retry = dup_mntfs(mf);
740 cp->retry = TRUE;
745 if (this_error >= 0) {
746 if (this_error > 0) {
747 amd_stats.d_merr++;
748 if (mf != mf_retry) {
749 mf->mf_error = this_error;
750 mf->mf_flags |= MFF_ERROR;
755 * Wakeup anything waiting for this mount
757 wakeup((voidp) mf);
761 if (this_error && cp->retry) {
762 free_mntfs(mf);
763 mf = cp->mp->am_mnt = mf_retry;
765 * Not retrying again (so far)
767 cp->retry = FALSE;
768 cp->tried = FALSE;
770 * Start at the beginning.
771 * Rewind the location vector and
772 * reset the default options.
774 cp->ivec = cp->xivec;
775 cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
777 * Arrange that autofs_bgmount is called
778 * after anything else happens.
780 #ifdef DEBUG
781 dlog("Arranging to retry mount of %s", cp->mp->am_path);
782 #endif /* DEBUG */
783 sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
784 if (cp->callout)
785 untimeout(cp->callout);
786 cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
788 cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
791 * Not done yet - so don't return anything
793 return -1;
796 if (hard_error < 0 || this_error == 0)
797 hard_error = this_error;
800 * Discard handle on duff filesystem.
801 * This should never happen since it
802 * should be caught by the case above.
804 if (mf_retry) {
805 if (hard_error)
806 plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
807 free_mntfs(mf_retry);
811 * If we get here, then either the mount succeeded or
812 * there is no more mount information available.
814 if (hard_error < 0 && mp_error)
815 hard_error = cp->mp->am_error = mp_error;
816 if (hard_error > 0) {
818 * Set a small(ish) timeout on an error node if
819 * the error was not a time out.
821 switch (hard_error) {
822 case ETIMEDOUT:
823 case EWOULDBLOCK:
824 cp->mp->am_timeo = 17;
825 break;
826 default:
827 cp->mp->am_timeo = 5;
828 break;
830 new_ttl(cp->mp);
834 * Make sure that the error value in the mntfs has a
835 * reasonable value.
837 if (mf->mf_error < 0) {
838 mf->mf_error = hard_error;
839 if (hard_error)
840 mf->mf_flags |= MFF_ERROR;
844 * In any case we don't need the continuation any more
846 free_continuation(cp);
848 return hard_error;
853 * Automount interface to RPC lookup routine
854 * Find the corresponding entry and return
855 * the file handle for it.
857 am_node *
858 autofs_lookuppn(am_node *mp, char *fname, int *error_return, int op)
860 am_node *ap, *new_mp, *ap_hung;
861 char *info; /* Mount info - where to get the file system */
862 char **ivec, **xivec; /* Split version of info */
863 char *auto_opts; /* Automount options */
864 int error = 0; /* Error so far */
865 char path_name[MAXPATHLEN]; /* General path name buffer */
866 char apath[MAXPATHLEN]; /* autofs path (added space) */
867 char *pfname; /* Path for database lookup */
868 struct continuation *cp; /* Continuation structure if need to mount */
869 int in_progress = 0; /* # of (un)mount in progress */
870 char *dflts;
871 mntfs *mf;
873 #ifdef DEBUG
874 dlog("in autofs_lookuppn");
875 #endif /* DEBUG */
878 * If the server is shutting down
879 * then don't return information
880 * about the mount point.
882 if (amd_state == Finishing) {
883 #ifdef DEBUG
884 if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
885 dlog("%s mount ignored - going down", fname);
886 } else {
887 dlog("%s/%s mount ignored - going down", mp->am_path, fname);
889 #endif /* DEBUG */
890 ereturn(ENOENT);
894 * Handle special case of "." and ".."
896 if (fname[0] == '.') {
897 if (fname[1] == '\0')
898 return mp; /* "." is the current node */
899 if (fname[1] == '.' && fname[2] == '\0') {
900 if (mp->am_parent) {
901 #ifdef DEBUG
902 dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
903 #endif /* DEBUG */
904 return mp->am_parent; /* ".." is the parent node */
906 ereturn(ESTALE);
911 * Check for valid key name.
912 * If it is invalid then pretend it doesn't exist.
914 if (!valid_key(fname)) {
915 plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
916 ereturn(ENOENT);
920 * Expand key name.
921 * fname is now a private copy.
923 fname = expand_key(fname);
925 for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
927 * Otherwise search children of this node
929 if (FSTREQ(ap->am_name, fname)) {
930 mf = ap->am_mnt;
931 if (ap->am_error) {
932 error = ap->am_error;
933 continue;
936 * If the error code is undefined then it must be
937 * in progress.
939 if (mf->mf_error < 0)
940 goto in_progrss;
943 * Check for a hung node
945 if (FSRV_ISDOWN(mf->mf_server)) {
946 #ifdef DEBUG
947 dlog("server hung");
948 #endif /* DEBUG */
949 error = ap->am_error;
950 ap_hung = ap;
951 continue;
954 * If there was a previous error with this node
955 * then return that error code.
957 if (mf->mf_flags & MFF_ERROR) {
958 error = mf->mf_error;
959 continue;
961 if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
962 in_progrss:
964 * If the fs is not mounted or it is unmounting then there
965 * is a background (un)mount in progress. In this case
966 * we just drop the RPC request (return nil) and
967 * wait for a retry, by which time the (un)mount may
968 * have completed.
970 #ifdef DEBUG
971 dlog("ignoring mount of %s in %s -- flags (%x) in progress",
972 fname, mf->mf_mount, mf->mf_flags);
973 #endif /* DEBUG */
974 in_progress++;
975 continue;
979 * Otherwise we have a hit: return the current mount point.
981 #ifdef DEBUG
982 dlog("matched %s in %s", fname, ap->am_path);
983 #endif /* DEBUG */
984 XFREE(fname);
985 return ap;
989 if (in_progress) {
990 #ifdef DEBUG
991 dlog("Waiting while %d mount(s) in progress", in_progress);
992 #endif /* DEBUG */
993 XFREE(fname);
994 ereturn(-1);
998 * If an error occurred then return it.
1000 if (error) {
1001 #ifdef DEBUG
1002 errno = error; /* XXX */
1003 dlog("Returning error: %m");
1004 #endif /* DEBUG */
1005 XFREE(fname);
1006 ereturn(error);
1010 * If doing a delete then don't create again!
1012 switch (op) {
1013 case VLOOK_DELETE:
1014 ereturn(ENOENT);
1016 case VLOOK_CREATE:
1017 break;
1019 default:
1020 plog(XLOG_FATAL, "Unknown op to autofs_lookuppn: 0x%x", op);
1021 ereturn(EINVAL);
1025 * If the server is going down then just return,
1026 * don't try to mount any more file systems
1028 if ((int) amd_state >= (int) Finishing) {
1029 #ifdef DEBUG
1030 dlog("not found - server going down anyway");
1031 #endif /* DEBUG */
1032 XFREE(fname);
1033 ereturn(ENOENT);
1037 * If we get there then this is a reference to an,
1038 * as yet, unknown name so we need to search the mount
1039 * map for it.
1041 if (mp->am_pref) {
1042 sprintf(path_name, "%s%s", mp->am_pref, fname);
1043 pfname = path_name;
1044 } else {
1045 pfname = fname;
1048 mf = mp->am_mnt;
1050 #ifdef DEBUG
1051 dlog("will search map info in %s to find %s", mf->mf_info, pfname);
1052 #endif /* DEBUG */
1054 * Consult the oracle for some mount information.
1055 * info is malloc'ed and belongs to this routine.
1056 * It ends up being free'd in free_continuation().
1058 * Note that this may return -1 indicating that information
1059 * is not yet available.
1061 error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
1062 if (error) {
1063 if (error > 0)
1064 plog(XLOG_MAP, "No map entry for %s", pfname);
1065 else
1066 plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
1067 XFREE(fname);
1068 ereturn(error);
1070 #ifdef DEBUG
1071 dlog("mount info is %s", info);
1072 #endif /* DEBUG */
1075 * Split info into an argument vector.
1076 * The vector is malloc'ed and belongs to
1077 * this routine. It is free'd in free_continuation()
1079 xivec = ivec = strsplit(info, ' ', '\"');
1082 * Default error code...
1084 if (ap_hung)
1085 error = EWOULDBLOCK;
1086 else
1087 error = ENOENT;
1090 * Allocate a new map
1092 new_mp = exported_ap_alloc();
1093 if (new_mp == 0) {
1094 XFREE(xivec);
1095 XFREE(info);
1096 XFREE(fname);
1097 ereturn(ENOSPC);
1099 if (mf->mf_auto)
1100 auto_opts = mf->mf_auto;
1101 else
1102 auto_opts = "";
1104 auto_opts = strdup(auto_opts);
1106 #ifdef DEBUG
1107 dlog("searching for /defaults entry");
1108 #endif /* DEBUG */
1109 if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
1110 char *dfl;
1111 char **rvec;
1112 #ifdef DEBUG
1113 dlog("/defaults gave %s", dflts);
1114 #endif /* DEBUG */
1115 if (*dflts == '-')
1116 dfl = dflts + 1;
1117 else
1118 dfl = dflts;
1121 * Chop the defaults up
1123 rvec = strsplit(dfl, ' ', '\"');
1125 if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
1127 * Pick whichever first entry matched the list of selectors.
1128 * Strip the selectors from the string, and assign to dfl the
1129 * rest of the string.
1131 if (rvec) {
1132 am_opts ap;
1133 am_ops *pt;
1134 char **sp = rvec;
1135 while (*sp) { /* loop until you find something, if any */
1136 memset((char *) &ap, 0, sizeof(am_opts));
1137 pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
1138 mp->am_parent->am_mnt->mf_info);
1139 free_opts(&ap); /* don't leak */
1140 if (pt == &amfs_error_ops) {
1141 plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
1142 } else {
1143 dfl = strip_selectors(*sp, "/defaults");
1144 plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
1145 break;
1147 ++sp;
1150 } else { /* not enable_default_selectors */
1152 * Extract first value
1154 dfl = rvec[0];
1158 * If there were any values at all...
1160 if (dfl) {
1162 * Log error if there were other values
1164 if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
1165 # ifdef DEBUG
1166 dlog("/defaults chopped into %s", dfl);
1167 # endif /* DEBUG */
1168 plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
1172 * Prepend to existing defaults if they exist,
1173 * otherwise just use these defaults.
1175 if (*auto_opts && *dfl) {
1176 char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
1177 sprintf(nopts, "%s;%s", dfl, auto_opts);
1178 XFREE(auto_opts);
1179 auto_opts = nopts;
1180 } else if (*dfl) {
1181 auto_opts = strealloc(auto_opts, dfl);
1184 XFREE(dflts);
1186 * Don't need info vector any more
1188 XFREE(rvec);
1192 * Fill it in
1194 init_map(new_mp, fname);
1197 * Turn on autofs flag if needed.
1199 if (mp->am_flags & AMF_AUTOFS) {
1200 new_mp->am_flags |= AMF_AUTOFS;
1204 * Put it in the table
1206 insert_am(new_mp, mp);
1209 * Fill in some other fields,
1210 * path and mount point.
1212 * bugfix: do not prepend old am_path if direct map
1213 * <wls@astro.umd.edu> William Sebok
1216 strcpy(apath, fname);
1217 strcat(apath, " ");
1218 new_mp->am_path = str3cat(new_mp->am_path,
1219 mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
1220 *fname == '/' ? "" : "/",
1221 apath);
1223 #ifdef DEBUG
1224 dlog("setting path to \"%s\"", new_mp->am_path);
1225 #endif /* DEBUG */
1228 * Take private copy of pfname
1230 pfname = strdup(pfname);
1233 * Construct a continuation
1235 cp = ALLOC(struct continuation);
1236 cp->callout = 0;
1237 cp->mp = new_mp;
1238 cp->xivec = xivec;
1239 cp->ivec = ivec;
1240 cp->info = info;
1241 cp->key = pfname;
1242 cp->auto_opts = auto_opts;
1243 cp->retry = FALSE;
1244 cp->tried = FALSE;
1245 cp->start = clocktime();
1246 cp->def_opts = strdup(auto_opts);
1247 memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
1250 * Try and mount the file system. If this succeeds immediately (possible
1251 * for a ufs file system) then return the attributes, otherwise just
1252 * return an error.
1254 error = autofs_bgmount(cp, error);
1255 reschedule_timeout_mp();
1256 if (!error) {
1257 XFREE(fname);
1258 return new_mp;
1262 * Code for quick reply. If nfs_program_2_transp is set, then
1263 * its the transp that's been passed down from nfs_program_2().
1264 * If new_mp->am_transp is not already set, set it by copying in
1265 * nfs_program_2_transp. Once am_transp is set, quick_reply() can
1266 * use it to send a reply to the client that requested this mount.
1268 if (nfs_program_2_transp && !new_mp->am_transp) {
1269 new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
1270 *(new_mp->am_transp) = *nfs_program_2_transp;
1272 if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1273 new_mp->am_error = error;
1275 assign_error_mntfs(new_mp);
1277 XFREE(fname);
1279 ereturn(error);
1281 #endif /* HAVE_FS_AUTOFS */