Merge branch 'vendor/GCC47'
[dragonfly.git] / contrib / amd / amd / ops_autofs.c
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.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
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.
26  *
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.
38  *
39  *      %W% (Berkeley) %G%
40  *
41  * $Id: ops_autofs.c,v 1.4 1999/01/13 23:31:00 ezk Exp $
42  *
43  */
44
45 /*
46  * Automounter filesystem
47  */
48
49 #ifdef HAVE_CONFIG_H
50 # include <config.h>
51 #endif /* HAVE_CONFIG_H */
52 #include <am_defs.h>
53 #include <amd.h>
54
55 /*
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...
59  */
60
61 #ifdef HAVE_FS_AUTOFS
62
63 /*
64  * MACROS:
65  */
66 #ifndef AUTOFS_NULL
67 # define AUTOFS_NULL    ((u_long)0)
68 #endif /* not AUTOFS_NULL */
69
70 /*
71  * VARIABLES:
72  */
73
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);
78
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 *);
84
85 /*
86  * STRUCTURES:
87  */
88
89 /* Sun's kernel-based automounter-supporting file system */
90 am_ops autofs_ops =
91 {
92   "autofs",
93   amfs_auto_match,
94   0,                            /* amfs_auto_init */
95   autofs_mount,
96   0,
97   autofs_umount,
98   0,
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
106 };
107
108
109 /****************************************************************************
110  *** FUNCTIONS                                                            ***
111  ****************************************************************************/
112
113 /*
114  * Mount the top-level using autofs
115  */
116 int
117 autofs_mount(am_node *mp)
118 {
119   mntfs *mf = mp->am_mnt;
120   struct stat stb;
121   char opts[256], preopts[256];
122   int error;
123   char *mnttype;
124
125   /*
126    * Mounting the automounter.
127    * Make sure the mount directory exists, construct
128    * the mount options and call the mount_autofs routine.
129    */
130
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;
136   }
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";
147
148   /*
149    * Construct some mount options:
150    *
151    * Tack on magic map=<mapname> option in mtab to emulate
152    * SunOS automounter behavior.
153    */
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);
170
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;
177   }
178   return 0;
179 }
180
181
182 void
183 autofs_mounted(mntfs *mf)
184 {
185   amfs_auto_mkcacheref(mf);
186 }
187
188
189 /*
190  * Unmount a top-level automount node
191  */
192 int
193 autofs_umount(am_node *mp)
194 {
195   int error;
196   struct stat stb;
197
198   /*
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.
206    */
207   if (lstat(mp->am_path, &stb) < 0) {
208 #ifdef DEBUG
209     dlog("lstat(%s): %m", mp->am_path);
210 #endif /* DEBUG */
211   }
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 */
216   }
217   return error;
218 }
219
220
221 /*
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.
227  */
228 static int
229 mount_autofs(char *dir, char *opts)
230 {
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;
238
239   memset((voidp) &autofs_args, 0, sizeof(autofs_args)); /* Paranoid */
240
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;
246
247   retry = hasmntval(&mnt, "retry");
248   if (retry <= 0)
249     retry = 2;                  /* XXX */
250
251   /*
252    * SET MOUNT ARGS
253    */
254   if (uname(&utsname) < 0) {
255     strcpy(buf, "localhost.autofs");
256   } else {
257     strcpy(buf, utsname.nodename);
258     strcat(buf, ".autofs");
259   }
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 */
265
266   autofs_args.path = dir;
267   autofs_args.opts = opts;
268
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";
275     }
276   }
277   autofs_args.map = map_opt;
278
279   /* XXX: these I set arbitrarily... */
280   autofs_args.mount_to = 300;
281   autofs_args.rpc_to = 60;
282   autofs_args.direct = 0;
283
284   /*
285    * Make a ``hostname'' string for the kernel
286    */
287   sprintf(fs_hostname, "pid%ld@%s:%s",
288           (long) (foreground ? am_mypid : getppid()),
289           am_get_hostname(), dir);
290
291   /*
292    * Most kernels have a name length restriction.
293    */
294   if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
295     strcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..");
296
297   /*
298    * Finally we can compute the mount flags set above.
299    */
300   flags = compute_mount_flags(&mnt);
301
302   /*
303    * This is it!  Here we try to mount amd on its mount points.
304    */
305   error = mount_fs(&mnt, flags, (caddr_t) &autofs_args, retry, type, 0, NULL, mnttab_file_name);
306   return error;
307 }
308
309
310 /****************************************************************************/
311 /* autofs program dispatcher */
312 void
313 autofs_program_1(struct svc_req *rqstp, SVCXPRT *transp)
314 {
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;
324
325   bool_t (*xdr_argument)(), (*xdr_result)();
326   int (*local)();
327
328   switch (rqstp->rq_proc) {
329
330   case AUTOFS_NULL:
331     svc_sendreply(transp,
332                   (XDRPROC_T_TYPE) xdr_void,
333                   (SVC_IN_ARG_TYPE) NULL);
334     return;
335
336   case AUTOFS_MOUNT:
337     xdr_argument = xdr_mntrequest;
338     xdr_result = xdr_mntres;
339     local = (int (*)()) autofs_mount_1_svc;
340     break;
341
342   case AUTOFS_UNMOUNT:
343     xdr_argument = xdr_umntrequest;
344     xdr_result = xdr_umntres;
345     local = (int (*)()) autofs_unmount_1_svc;
346     break;
347
348   default:
349     svcerr_noproc(transp);
350     return;
351   }
352
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;
362   }
363
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);
369   }
370
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);
376   }
377 }
378
379
380 static int
381 autofs_mount_1_svc(struct mntrequest *mr, struct mntres *result, struct authunix_parms *cred)
382 {
383   int err = 0;
384   am_node *anp, *anp2;
385
386   plog(XLOG_INFO, "XXX: autofs_mount_1_svc: %s:%s:%s:%s",
387        mr->map, mr->name, mr->opts, mr->path);
388
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;
395   }
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;
400   }
401
402   /*
403    * Look for (and create if needed) the new node.
404    *
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.
408    *
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;
420     }
421     goto out;
422   }
423
424 out:
425   result->status = err;
426   return err;
427 }
428
429
430 static int
431 autofs_unmount_1_svc(struct umntrequest *ur, struct umntres *result, struct authunix_parms *cred)
432 {
433   int err = 0;
434
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 */
444
445   err = EINVAL;                 /* XXX: not implemented yet */
446   goto out;
447
448 out:
449   result->status = err;
450   return err;
451 }
452
453
454 /*
455  * Pick a file system to try mounting and
456  * do that in the background if necessary
457  *
458  For each location:
459  if it is new -defaults then
460  extract and process
461  continue;
462  fi
463  if it is a cut then
464  if a location has been tried then
465  break;
466  fi
467  continue;
468  fi
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
475  fi
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
485  fi
486  if no error on this mount then
487  this_error = initialize mount point
488  fi
489  if no error on this mount and mount is delayed then
490  this_error = -1
491  fi
492  if this_error < 0 then
493  retry = true
494  fi
495  if no error on this mount then
496  make mount point if required
497  fi
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
504  fi
505  fi
506  if an error occurred on this mount then
507  update stats
508  save error in mount point
509  fi
510  endfor
511  */
512 static int
513 autofs_bgmount(struct continuation * cp, int mpe)
514 {
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;
520
521   /*
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
527    */
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;
533
534     if (hard_error < 0)
535       hard_error = this_error;
536
537     this_error = -1;
538
539     if (**cp->ivec == '-') {
540       /*
541        * Pick up new defaults
542        */
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;
551     }
552     /*
553      * If a mount has been attempted, and we find
554      * a cut then don't try any more locations.
555      */
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;
563       }
564       continue;
565     }
566
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);
569
570     /*
571      * Find a mounted filesystem for this node.
572      */
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);
579
580     p = mf->mf_ops;
581 #ifdef DEBUG
582     dlog("Got a hit with %s", p->fs_type);
583 #endif /* DEBUG */
584
585     /*
586      * Note whether this is a real mount attempt
587      */
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);
597       }
598       cp->tried = TRUE;
599     }
600
601     this_error = 0;
602     dont_retry = FALSE;
603
604     if (mp->am_link) {
605       XFREE(mp->am_link);
606       mp->am_link = 0;
607     }
608     link_dir = mf->mf_fo->opt_sublink;
609
610     if (link_dir && *link_dir) {
611       if (*link_dir == '/') {
612         mp->am_link = strdup(link_dir);
613       } else {
614         /*
615          * try getting fs option from continuation, not mountpoint!
616          * Don't try logging the string from mf, since it may be bad!
617          */
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);
621
622         mp->am_link = str3cat((char *) 0,
623                               cp->fs_opts.opt_fs, "/", link_dir);
624
625         normalize_slash(mp->am_link);
626       }
627     }
628
629     if (mf->mf_error > 0) {
630       this_error = mf->mf_error;
631     } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
632       /*
633        * Still mounting - retry later
634        */
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)) {
640       /*
641        * Would just mount from the same place
642        * as a hung mount - so give up
643        */
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 */
654
655       /*
656        * Just call mounted()
657        */
658       am_mounted(mp);
659
660       this_error = 0;
661       break;
662     }
663
664     /*
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.
670      */
671     if (!this_error) {
672       /*
673        * Fill in attribute fields.
674        */
675       if (mf->mf_ops->fs_flags & FS_DIRECTORY)
676         mk_fattr(mp, NFDIR);
677       else
678         mk_fattr(mp, NFLNK);
679
680       mp->am_fattr.na_fileid = mp->am_gen;
681
682       if (p->fs_init)
683         this_error = (*p->fs_init) (mf);
684     }
685
686     /*
687      * Make sure the fileserver is UP before doing any more work
688      */
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;
694     }
695
696     if (!this_error && mf->mf_fo->opt_delay) {
697       /*
698        * If there is a delay timer on the mount
699        * then don't try to mount if the timer
700        * has not expired.
701        */
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;
708       }
709     }
710
711     if (this_error < 0 && !dont_retry) {
712       if (!mf_retry)
713         mf_retry = dup_mntfs(mf);
714       cp->retry = TRUE;
715     }
716
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;
726         }
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;
741         }
742       }
743     }
744
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;
751         }
752       }
753
754       /*
755        * Wakeup anything waiting for this mount
756        */
757       wakeup((voidp) mf);
758     }
759   }
760
761   if (this_error && cp->retry) {
762     free_mntfs(mf);
763     mf = cp->mp->am_mnt = mf_retry;
764     /*
765      * Not retrying again (so far)
766      */
767     cp->retry = FALSE;
768     cp->tried = FALSE;
769     /*
770      * Start at the beginning.
771      * Rewind the location vector and
772      * reset the default options.
773      */
774     cp->ivec = cp->xivec;
775     cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
776     /*
777      * Arrange that autofs_bgmount is called
778      * after anything else happens.
779      */
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);
787
788     cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
789
790     /*
791      * Not done yet - so don't return anything
792      */
793     return -1;
794   }
795
796   if (hard_error < 0 || this_error == 0)
797     hard_error = this_error;
798
799   /*
800    * Discard handle on duff filesystem.
801    * This should never happen since it
802    * should be caught by the case above.
803    */
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);
808   }
809
810   /*
811    * If we get here, then either the mount succeeded or
812    * there is no more mount information available.
813    */
814   if (hard_error < 0 && mp_error)
815     hard_error = cp->mp->am_error = mp_error;
816   if (hard_error > 0) {
817     /*
818      * Set a small(ish) timeout on an error node if
819      * the error was not a time out.
820      */
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;
829     }
830     new_ttl(cp->mp);
831   }
832
833   /*
834    * Make sure that the error value in the mntfs has a
835    * reasonable value.
836    */
837   if (mf->mf_error < 0) {
838     mf->mf_error = hard_error;
839     if (hard_error)
840       mf->mf_flags |= MFF_ERROR;
841   }
842
843   /*
844    * In any case we don't need the continuation any more
845    */
846   free_continuation(cp);
847
848   return hard_error;
849 }
850
851
852 /*
853  * Automount interface to RPC lookup routine
854  * Find the corresponding entry and return
855  * the file handle for it.
856  */
857 am_node *
858 autofs_lookuppn(am_node *mp, char *fname, int *error_return, int op)
859 {
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;
872
873 #ifdef DEBUG
874   dlog("in autofs_lookuppn");
875 #endif /* DEBUG */
876
877   /*
878    * If the server is shutting down
879    * then don't return information
880    * about the mount point.
881    */
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);
888     }
889 #endif /* DEBUG */
890     ereturn(ENOENT);
891   }
892
893   /*
894    * Handle special case of "." and ".."
895    */
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 */
905       }
906       ereturn(ESTALE);
907     }
908   }
909
910   /*
911    * Check for valid key name.
912    * If it is invalid then pretend it doesn't exist.
913    */
914   if (!valid_key(fname)) {
915     plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
916     ereturn(ENOENT);
917   }
918
919   /*
920    * Expand key name.
921    * fname is now a private copy.
922    */
923   fname = expand_key(fname);
924
925   for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
926     /*
927      * Otherwise search children of this node
928      */
929     if (FSTREQ(ap->am_name, fname)) {
930       mf = ap->am_mnt;
931       if (ap->am_error) {
932         error = ap->am_error;
933         continue;
934       }
935       /*
936        * If the error code is undefined then it must be
937        * in progress.
938        */
939       if (mf->mf_error < 0)
940         goto in_progrss;
941
942       /*
943        * Check for a hung node
944        */
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;
952       }
953       /*
954        * If there was a previous error with this node
955        * then return that error code.
956        */
957       if (mf->mf_flags & MFF_ERROR) {
958         error = mf->mf_error;
959         continue;
960       }
961       if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
962       in_progrss:
963         /*
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.
969          */
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;
976       }
977
978       /*
979        * Otherwise we have a hit: return the current mount point.
980        */
981 #ifdef DEBUG
982       dlog("matched %s in %s", fname, ap->am_path);
983 #endif /* DEBUG */
984       XFREE(fname);
985       return ap;
986     }
987   }
988
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);
995   }
996
997   /*
998    * If an error occurred then return it.
999    */
1000   if (error) {
1001 #ifdef DEBUG
1002     errno = error;              /* XXX */
1003     dlog("Returning error: %m");
1004 #endif /* DEBUG */
1005     XFREE(fname);
1006     ereturn(error);
1007   }
1008
1009   /*
1010    * If doing a delete then don't create again!
1011    */
1012   switch (op) {
1013   case VLOOK_DELETE:
1014     ereturn(ENOENT);
1015
1016   case VLOOK_CREATE:
1017     break;
1018
1019   default:
1020     plog(XLOG_FATAL, "Unknown op to autofs_lookuppn: 0x%x", op);
1021     ereturn(EINVAL);
1022   }
1023
1024   /*
1025    * If the server is going down then just return,
1026    * don't try to mount any more file systems
1027    */
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);
1034   }
1035
1036   /*
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.
1040    */
1041   if (mp->am_pref) {
1042     sprintf(path_name, "%s%s", mp->am_pref, fname);
1043     pfname = path_name;
1044   } else {
1045     pfname = fname;
1046   }
1047
1048   mf = mp->am_mnt;
1049
1050 #ifdef DEBUG
1051   dlog("will search map info in %s to find %s", mf->mf_info, pfname);
1052 #endif /* DEBUG */
1053   /*
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().
1057    *
1058    * Note that this may return -1 indicating that information
1059    * is not yet available.
1060    */
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);
1069   }
1070 #ifdef DEBUG
1071   dlog("mount info is %s", info);
1072 #endif /* DEBUG */
1073
1074   /*
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()
1078    */
1079   xivec = ivec = strsplit(info, ' ', '\"');
1080
1081   /*
1082    * Default error code...
1083    */
1084   if (ap_hung)
1085     error = EWOULDBLOCK;
1086   else
1087     error = ENOENT;
1088
1089   /*
1090    * Allocate a new map
1091    */
1092   new_mp = exported_ap_alloc();
1093   if (new_mp == 0) {
1094     XFREE(xivec);
1095     XFREE(info);
1096     XFREE(fname);
1097     ereturn(ENOSPC);
1098   }
1099   if (mf->mf_auto)
1100     auto_opts = mf->mf_auto;
1101   else
1102     auto_opts = "";
1103
1104   auto_opts = strdup(auto_opts);
1105
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;
1119
1120     /*
1121      * Chop the defaults up
1122      */
1123     rvec = strsplit(dfl, ' ', '\"');
1124
1125     if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
1126       /*
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.
1130        */
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;
1146           }
1147           ++sp;
1148         }
1149       }
1150     } else {                    /* not enable_default_selectors */
1151       /*
1152        * Extract first value
1153        */
1154       dfl = rvec[0];
1155     }
1156
1157     /*
1158      * If there were any values at all...
1159      */
1160     if (dfl) {
1161       /*
1162        * Log error if there were other values
1163        */
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);
1169       }
1170
1171       /*
1172        * Prepend to existing defaults if they exist,
1173        * otherwise just use these defaults.
1174        */
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);
1182       }
1183     }
1184     XFREE(dflts);
1185     /*
1186      * Don't need info vector any more
1187      */
1188     XFREE(rvec);
1189   }
1190
1191   /*
1192    * Fill it in
1193    */
1194   init_map(new_mp, fname);
1195
1196   /*
1197    * Turn on autofs flag if needed.
1198    */
1199   if (mp->am_flags & AMF_AUTOFS) {
1200     new_mp->am_flags |= AMF_AUTOFS;
1201   }
1202
1203   /*
1204    * Put it in the table
1205    */
1206   insert_am(new_mp, mp);
1207
1208   /*
1209    * Fill in some other fields,
1210    * path and mount point.
1211    *
1212    * bugfix: do not prepend old am_path if direct map
1213    *         <wls@astro.umd.edu> William Sebok
1214    */
1215
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);
1222
1223 #ifdef DEBUG
1224   dlog("setting path to \"%s\"", new_mp->am_path);
1225 #endif /* DEBUG */
1226
1227   /*
1228    * Take private copy of pfname
1229    */
1230   pfname = strdup(pfname);
1231
1232   /*
1233    * Construct a continuation
1234    */
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));
1248
1249   /*
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.
1253    */
1254   error = autofs_bgmount(cp, error);
1255   reschedule_timeout_mp();
1256   if (!error) {
1257     XFREE(fname);
1258     return new_mp;
1259   }
1260
1261   /*
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.
1267    */
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;
1271   }
1272   if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
1273     new_mp->am_error = error;
1274
1275   assign_error_mntfs(new_mp);
1276
1277   XFREE(fname);
1278
1279   ereturn(error);
1280 }
1281 #endif /* HAVE_FS_AUTOFS */