xref: /netbsd-src/external/bsd/am-utils/dist/amd/autil.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: autil.c,v 1.1.1.1 2008/09/19 20:07:15 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2007 Erez Zadok
5  * Copyright (c) 1990 Jan-Simon Pendry
6  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7  * Copyright (c) 1990 The Regents of the University of California.
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * Jan-Simon Pendry at Imperial College, London.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgment:
23  *      This product includes software developed by the University of
24  *      California, Berkeley and its contributors.
25  * 4. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *
42  * File: am-utils/amd/autil.c
43  *
44  */
45 
46 /*
47  * utilities specified to amd, taken out of the older amd/util.c.
48  */
49 
50 #ifdef HAVE_CONFIG_H
51 # include <config.h>
52 #endif /* HAVE_CONFIG_H */
53 #include <am_defs.h>
54 #include <amd.h>
55 
56 int NumChildren = 0;		/* number of children of primary amd */
57 static char invalid_keys[] = "\"'!;@ \t\n";
58 
59 /****************************************************************************
60  *** MACROS                                                               ***
61  ****************************************************************************/
62 
63 #ifdef HAVE_TRANSPORT_TYPE_TLI
64 # define PARENT_USLEEP_TIME	100000 /* 0.1 seconds */
65 #endif /* HAVE_TRANSPORT_TYPE_TLI */
66 
67 
68 /****************************************************************************
69  *** FORWARD DEFINITIONS                                                  ***
70  ****************************************************************************/
71 static void domain_strip(char *otherdom, char *localdom);
72 static int dofork(void);
73 
74 
75 /****************************************************************************
76  *** FUNCTIONS                                                             ***
77  ****************************************************************************/
78 
79 /*
80  * Copy s into p, reallocating p if necessary
81  */
82 char *
83 strealloc(char *p, char *s)
84 {
85   size_t len = strlen(s) + 1;
86 
87   p = (char *) xrealloc((voidp) p, len);
88 
89   xstrlcpy(p, s, len);
90 #ifdef DEBUG_MEM
91 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
92   malloc_verify();
93 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
94 #endif /* DEBUG_MEM */
95   return p;
96 }
97 
98 
99 /*
100  * Strip off the trailing part of a domain
101  * to produce a short-form domain relative
102  * to the local host domain.
103  * Note that this has no effect if the domain
104  * names do not have the same number of
105  * components.  If that restriction proves
106  * to be a problem then the loop needs recoding
107  * to skip from right to left and do partial
108  * matches along the way -- ie more expensive.
109  */
110 static void
111 domain_strip(char *otherdom, char *localdom)
112 {
113   char *p1, *p2;
114 
115   if ((p1 = strchr(otherdom, '.')) &&
116       (p2 = strchr(localdom, '.')) &&
117       STREQ(p1 + 1, p2 + 1))
118     *p1 = '\0';
119 }
120 
121 
122 /*
123  * Normalize a host name: replace cnames with real names, and decide if to
124  * strip domain name or not.
125  */
126 void
127 host_normalize(char **chp)
128 {
129   /*
130    * Normalize hosts is used to resolve host name aliases
131    * and replace them with the standard-form name.
132    * Invoked with "-n" command line option.
133    */
134   if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) {
135     struct hostent *hp;
136     hp = gethostbyname(*chp);
137     if (hp && hp->h_addrtype == AF_INET) {
138       dlog("Hostname %s normalized to %s", *chp, hp->h_name);
139       *chp = strealloc(*chp, (char *) hp->h_name);
140     }
141   }
142   if (gopt.flags & CFM_DOMAIN_STRIP) {
143     domain_strip(*chp, hostd);
144   }
145 }
146 
147 
148 /*
149  * Keys are not allowed to contain " ' ! or ; to avoid
150  * problems with macro expansions.
151  */
152 int
153 valid_key(char *key)
154 {
155   while (*key)
156     if (strchr(invalid_keys, *key++))
157       return FALSE;
158   return TRUE;
159 }
160 
161 
162 void
163 forcibly_timeout_mp(am_node *mp)
164 {
165   mntfs *mf = mp->am_mnt;
166   /*
167    * Arrange to timeout this node
168    */
169   if (mf && ((mp->am_flags & AMF_ROOT) ||
170 	     (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) {
171     if (mf->mf_flags & MFF_UNMOUNTING)
172       plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path);
173     else
174       plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
175   } else {
176     plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
177     mp->am_flags &= ~AMF_NOTIMEOUT;
178     mp->am_ttl = clocktime(NULL);
179     /*
180      * Force mtime update of parent dir, to prevent DNLC/dcache from caching
181      * the old entry, which could result in ESTALE errors, bad symlinks, and
182      * more.
183      */
184     clocktime(&mp->am_parent->am_fattr.na_mtime);
185     reschedule_timeout_mp();
186   }
187 }
188 
189 
190 void
191 mf_mounted(mntfs *mf, bool_t call_free_opts)
192 {
193   int quoted;
194   int wasmounted = mf->mf_flags & MFF_MOUNTED;
195 
196   if (!wasmounted) {
197     /*
198      * If this is a freshly mounted
199      * filesystem then update the
200      * mntfs structure...
201      */
202     mf->mf_flags |= MFF_MOUNTED;
203     mf->mf_error = 0;
204 
205     /*
206      * Do mounted callback
207      */
208     if (mf->mf_ops->mounted)
209       mf->mf_ops->mounted(mf);
210 
211     /*
212      * Be careful when calling free_ops and XFREE here.  Some pseudo file
213      * systems like nfsx call this function (mf_mounted), even though it
214      * would be called by the lower-level amd file system functions.  nfsx
215      * needs to call this function because of the other actions it takes.
216      * So we pass a boolean from the caller (yes, not so clean workaround)
217      * to determine if we should free or not.  If we're not freeing (often
218      * because we're called from a callback function), then just to be sure,
219      * we'll zero out the am_opts structure and set the pointer to NULL.
220      * The parent mntfs node owns this memory and is going to free it with a
221      * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code).
222      */
223     if (call_free_opts) {
224       free_opts(mf->mf_fo);	/* this free is needed to prevent leaks */
225       XFREE(mf->mf_fo);		/* (also this one) */
226     } else {
227       memset(mf->mf_fo, 0, sizeof(am_opts));
228       mf->mf_fo = NULL;
229     }
230   }
231 
232   if (mf->mf_flags & MFF_RESTART) {
233     mf->mf_flags &= ~MFF_RESTART;
234     dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags);
235   }
236 
237   /*
238    * Log message
239    */
240   quoted = strchr(mf->mf_info, ' ') != 0;
241   plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
242        quoted ? "\"" : "",
243        mf->mf_info,
244        quoted ? "\"" : "",
245        wasmounted ? "referenced" : "mounted",
246        mf->mf_ops->fs_type, mf->mf_mount);
247 }
248 
249 
250 void
251 am_mounted(am_node *mp)
252 {
253   int notimeout = 0;		/* assume normal timeouts initially */
254   mntfs *mf = mp->am_mnt;
255 
256   /*
257    * This is the parent mntfs which does the mf->mf_fo (am_opts type), and
258    * we're passing TRUE here to tell mf_mounted to actually free the
259    * am_opts.  See a related comment in mf_mounted().
260    */
261   mf_mounted(mf, TRUE);
262 
263 #ifdef HAVE_FS_AUTOFS
264   if (mf->mf_flags & MFF_IS_AUTOFS)
265     autofs_mounted(mp);
266 #endif /* HAVE_FS_AUTOFS */
267 
268   /*
269    * Patch up path for direct mounts
270    */
271   if (mp->am_parent && mp->am_parent->am_mnt->mf_fsflags & FS_DIRECT)
272     mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
273 
274   /*
275    * Check whether this mount should be cached permanently or not,
276    * and handle user-requested timeouts.
277    */
278   /* first check if file system was set to never timeout */
279   if (mf->mf_fsflags & FS_NOTIMEOUT)
280     notimeout = 1;
281   /* next, alter that decision by map flags */
282   if (mf->mf_mopts) {
283     mntent_t mnt;
284     mnt.mnt_opts = mf->mf_mopts;
285 
286     /* umount option: user wants to unmount this entry */
287     if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount"))
288       notimeout = 0;
289     /* noumount option: user does NOT want to unmount this entry */
290     if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount"))
291       notimeout = 1;
292     /* utimeout=N option: user wants to unmount this option AND set timeout */
293     if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
294       mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */
295     else
296       notimeout = 0;
297     /* special case: don't try to unmount "/" (it can never succeed) */
298     if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0')
299       notimeout = 1;
300   }
301   /* finally set actual flags */
302   if (notimeout) {
303     mp->am_flags |= AMF_NOTIMEOUT;
304     plog(XLOG_INFO, "%s set to never timeout", mp->am_path);
305   } else {
306     mp->am_flags &= ~AMF_NOTIMEOUT;
307     plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo);
308   }
309 
310   /*
311    * If this node is a symlink then
312    * compute the length of the returned string.
313    */
314   if (mp->am_fattr.na_type == NFLNK)
315     mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount);
316 
317   /*
318    * Record mount time, and update am_stats at the same time.
319    */
320   mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime);
321   new_ttl(mp);
322 
323   /*
324    * Update mtime of parent node (copying "struct nfstime" in '=' below)
325    */
326   if (mp->am_parent && mp->am_parent->am_mnt)
327     mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime;
328 
329   /*
330    * This is ugly, but essentially unavoidable
331    * Sublinks must be treated separately as type==link
332    * when the base type is different.
333    */
334   if (mp->am_link && mf->mf_ops != &amfs_link_ops)
335     amfs_link_ops.mount_fs(mp, mf);
336 
337   /*
338    * Now, if we can, do a reply to our client here
339    * to speed things up.
340    */
341 #ifdef HAVE_FS_AUTOFS
342   if (mp->am_flags & AMF_AUTOFS)
343     autofs_mount_succeeded(mp);
344   else
345 #endif /* HAVE_FS_AUTOFS */
346     nfs_quick_reply(mp, 0);
347 
348   /*
349    * Update stats
350    */
351   amd_stats.d_mok++;
352 }
353 
354 
355 /*
356  * Replace mount point with a reference to an error filesystem.
357  * The mount point (struct mntfs) is NOT discarded,
358  * the caller must do it if it wants to _before_ calling this function.
359  */
360 void
361 assign_error_mntfs(am_node *mp)
362 {
363   int error;
364   dlog("assign_error_mntfs");
365   /*
366    * Save the old error code
367    */
368   error = mp->am_error;
369   if (error <= 0)
370     error = mp->am_mnt->mf_error;
371   /*
372    * Allocate a new error reference
373    */
374   mp->am_mnt = new_mntfs();
375   /*
376    * Put back the error code
377    */
378   mp->am_mnt->mf_error = error;
379   mp->am_mnt->mf_flags |= MFF_ERROR;
380   /*
381    * Zero the error in the mount point
382    */
383   mp->am_error = 0;
384 }
385 
386 
387 /*
388  * Build a new map cache for this node, or re-use
389  * an existing cache for the same map.
390  */
391 void
392 amfs_mkcacheref(mntfs *mf)
393 {
394   char *cache;
395 
396   if (mf->mf_fo && mf->mf_fo->opt_cache)
397     cache = mf->mf_fo->opt_cache;
398   else
399     cache = "none";
400   mf->mf_private = (opaque_t) mapc_find(mf->mf_info,
401 					cache,
402 					(mf->mf_fo ? mf->mf_fo->opt_maptype : NULL),
403 					mf->mf_mount);
404   mf->mf_prfree = mapc_free;
405 }
406 
407 
408 /*
409  * Locate next node in sibling list which is mounted
410  * and is not an error node.
411  */
412 am_node *
413 next_nonerror_node(am_node *xp)
414 {
415   mntfs *mf;
416 
417   /*
418    * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
419    * Fixes a race condition when mounting direct automounts.
420    * Also fixes a problem when doing a readdir on a directory
421    * containing hung automounts.
422    */
423   while (xp &&
424 	 (!(mf = xp->am_mnt) ||	/* No mounted filesystem */
425 	  mf->mf_error != 0 ||	/* There was a mntfs error */
426 	  xp->am_error != 0 ||	/* There was a mount error */
427 	  !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
428 	  (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
429 	 )
430     xp = xp->am_osib;
431 
432   return xp;
433 }
434 
435 
436 /*
437  * Mount an automounter directory.
438  * The automounter is connected into the system
439  * as a user-level NFS server.  amfs_mount constructs
440  * the necessary NFS parameters to be given to the
441  * kernel so that it will talk back to us.
442  *
443  * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP).
444  *
445  * NEW: on certain systems, mounting can be done using the
446  * kernel-level automount (autofs) support. In that case,
447  * we don't need NFS at all here.
448  */
449 int
450 amfs_mount(am_node *mp, mntfs *mf, char *opts)
451 {
452   char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
453   int retry, error = 0, genflags;
454   int on_autofs = mf->mf_flags & MFF_ON_AUTOFS;
455   char *dir = mf->mf_mount;
456   mntent_t mnt;
457   MTYPE_TYPE type;
458   int forced_unmount = 0;	/* are we using forced unmounts? */
459 
460   memset((voidp) &mnt, 0, sizeof(mnt));
461   mnt.mnt_dir = dir;
462   mnt.mnt_fsname = pid_fsname;
463   mnt.mnt_opts = opts;
464 
465 #ifdef HAVE_FS_AUTOFS
466   if (mf->mf_flags & MFF_IS_AUTOFS) {
467     type = MOUNT_TYPE_AUTOFS;
468     /*
469      * Make sure that amd's top-level autofs mounts are hidden by default
470      * from df.
471      * XXX: It works ok on Linux, might not work on other systems.
472      */
473     mnt.mnt_type = "autofs";
474   } else
475 #endif /* HAVE_FS_AUTOFS */
476   {
477     type = MOUNT_TYPE_NFS;
478     /*
479      * Make sure that amd's top-level NFS mounts are hidden by default
480      * from df.
481      * If they don't appear to support the either the "ignore" mnttab
482      * option entry, or the "auto" one, set the mount type to "nfs".
483      */
484     mnt.mnt_type = HIDE_MOUNT_TYPE;
485   }
486 
487   retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
488   if (retry <= 0)
489     retry = 2;			/* XXX: default to 2 retries */
490 
491   /*
492    * SET MOUNT ARGS
493    */
494 
495   /*
496    * Make a ``hostname'' string for the kernel
497    */
498   xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s",
499 	    get_server_pid(), am_get_hostname(), dir);
500   /*
501    * Most kernels have a name length restriction (64 bytes)...
502    */
503   if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
504     xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..",
505 	     sizeof(fs_hostname) - MAXHOSTNAMELEN + 3);
506 #ifdef HOSTNAMESZ
507   /*
508    * ... and some of these restrictions are 32 bytes (HOSTNAMESZ)
509    * If you need to get the definition for HOSTNAMESZ found, you may
510    * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file.
511    */
512   if (strlen(fs_hostname) >= HOSTNAMESZ)
513     xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..",
514 	     sizeof(fs_hostname) - HOSTNAMESZ + 3);
515 #endif /* HOSTNAMESZ */
516 
517   /*
518    * Finally we can compute the mount genflags set above,
519    * and add any automounter specific flags.
520    */
521   genflags = compute_mount_flags(&mnt);
522 #ifdef HAVE_FS_AUTOFS
523   if (on_autofs)
524     genflags |= autofs_compute_mount_flags(&mnt);
525 #endif /* HAVE_FS_AUTOFS */
526   genflags |= compute_automounter_mount_flags(&mnt);
527 
528 again:
529   if (!(mf->mf_flags & MFF_IS_AUTOFS)) {
530     nfs_args_t nfs_args;
531     am_nfs_fh *fhp;
532     am_nfs_handle_t anh;
533 #ifndef HAVE_TRANSPORT_TYPE_TLI
534     u_short port;
535     struct sockaddr_in sin;
536 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
537 
538     /*
539      * get fhandle of remote path for automount point
540      */
541     fhp = get_root_nfs_fh(dir);
542     if (!fhp) {
543       plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
544       return EINVAL;
545     }
546 
547 #ifndef HAVE_TRANSPORT_TYPE_TLI
548     /*
549      * Create sockaddr to point to the local machine.
550      */
551     memset((voidp) &sin, 0, sizeof(sin));
552     /* as per POSIX, sin_len need not be set (used internally by kernel) */
553     sin.sin_family = AF_INET;
554     sin.sin_addr = myipaddr;
555     port = hasmntval(&mnt, MNTTAB_OPT_PORT);
556     if (port) {
557       sin.sin_port = htons(port);
558     } else {
559       plog(XLOG_ERROR, "no port number specified for %s", dir);
560       return EINVAL;
561     }
562 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
563 
564     /* setup the many fields and flags within nfs_args */
565     memmove(&anh.v2, fhp, sizeof(*fhp));
566 #ifdef HAVE_TRANSPORT_TYPE_TLI
567     compute_nfs_args(&nfs_args,
568 		     &mnt,
569 		     genflags,
570 		     nfsncp,
571 		     NULL,	/* remote host IP addr is set below */
572 		     NFS_VERSION,	/* version 2 */
573 		     "udp",
574 		     &anh,
575 		     fs_hostname,
576 		     pid_fsname);
577     /*
578      * IMPORTANT: set the correct IP address AFTERWARDS.  It cannot
579      * be done using the normal mechanism of compute_nfs_args(), because
580      * that one will allocate a new address and use NFS_SA_DREF() to copy
581      * parts to it, while assuming that the ip_addr passed is always
582      * a "struct sockaddr_in".  That assumption is incorrect on TLI systems,
583      * because they define a special macro HOST_SELF which is DIFFERENT
584      * than localhost (127.0.0.1)!
585      */
586     nfs_args.addr = &nfsxprt->xp_ltaddr;
587 #else /* not HAVE_TRANSPORT_TYPE_TLI */
588     compute_nfs_args(&nfs_args,
589 		     &mnt,
590 		     genflags,
591 		     NULL,
592 		     &sin,
593 		     NFS_VERSION,	/* version 2 */
594 		     "udp",
595 		     &anh,
596 		     fs_hostname,
597 		     pid_fsname);
598 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
599 
600     /*************************************************************************
601      * NOTE: while compute_nfs_args() works ok for regular NFS mounts	     *
602      * the toplvl one is not quite regular, and so some options must be      *
603      * corrected by hand more carefully, *after* compute_nfs_args() runs.    *
604      *************************************************************************/
605     compute_automounter_nfs_args(&nfs_args, &mnt);
606 
607     if (amuDebug(D_TRACE)) {
608       print_nfs_args(&nfs_args, 0);
609       plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags);
610     }
611 
612     /* This is it!  Here we try to mount amd on its mount points */
613     error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args,
614 		     retry, type, 0, NULL, mnttab_file_name, on_autofs);
615 
616 #ifdef HAVE_TRANSPORT_TYPE_TLI
617     free_knetconfig(nfs_args.knconf);
618     /*
619      * local automounter mounts do not allocate a special address, so
620      * no need to XFREE(nfs_args.addr) under TLI.
621      */
622 #endif /* HAVE_TRANSPORT_TYPE_TLI */
623 
624 #ifdef HAVE_FS_AUTOFS
625   } else {
626     /* This is it!  Here we try to mount amd on its mount points */
627     error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh,
628 		     retry, type, 0, NULL, mnttab_file_name, on_autofs);
629 #endif /* HAVE_FS_AUTOFS */
630   }
631   if (error == 0 || forced_unmount)
632      return error;
633 
634   /*
635    * If user wants forced/lazy unmount semantics, then try it iff the
636    * current mount failed with EIO or ESTALE.
637    */
638   if (gopt.flags & CFM_FORCED_UNMOUNTS) {
639     switch (errno) {
640     case ESTALE:
641     case EIO:
642       forced_unmount = errno;
643       plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path);
644       if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name,
645 			     AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) {
646 	plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path);
647 	errno = forced_unmount;
648       } else
649 	goto again;
650     default:
651       break;
652     }
653   }
654 
655   return error;
656 }
657 
658 
659 void
660 am_unmounted(am_node *mp)
661 {
662   mntfs *mf = mp->am_mnt;
663 
664   if (!foreground)		/* firewall - should never happen */
665     return;
666 
667   /*
668    * Do unmounted callback
669    */
670   if (mf->mf_ops->umounted)
671     mf->mf_ops->umounted(mf);
672 
673   /*
674    * This is ugly, but essentially unavoidable.
675    * Sublinks must be treated separately as type==link
676    * when the base type is different.
677    */
678   if (mp->am_link && mf->mf_ops != &amfs_link_ops)
679     amfs_link_ops.umount_fs(mp, mf);
680 
681 #ifdef HAVE_FS_AUTOFS
682   if (mf->mf_flags & MFF_IS_AUTOFS)
683     autofs_release_fh(mp);
684   if (mp->am_flags & AMF_AUTOFS)
685     autofs_umount_succeeded(mp);
686 #endif /* HAVE_FS_AUTOFS */
687 
688   /*
689    * Clean up any directories that were made
690    *
691    * If we remove the mount point of a pending mount, any queued access
692    * to it will fail. So don't do it in that case.
693    * Also don't do it if the refcount is > 1.
694    */
695   if (mf->mf_flags & MFF_MKMNT &&
696       mf->mf_refc == 1 &&
697       !(mp->am_flags & AMF_REMOUNT)) {
698     plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount);
699     rmdirs(mf->mf_mount);
700     mf->mf_flags &= ~MFF_MKMNT;
701   }
702 
703   /*
704    * If this is a pseudo-directory then adjust the link count
705    * in the parent
706    */
707   if (mp->am_parent && mp->am_fattr.na_type == NFDIR)
708     --mp->am_parent->am_fattr.na_nlink;
709 
710   /*
711    * Update mtime of parent node
712    */
713   if (mp->am_parent && mp->am_parent->am_mnt)
714     clocktime(&mp->am_parent->am_fattr.na_mtime);
715 
716   if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) {
717     char *fname = strdup(mp->am_name);
718     am_node *mp_parent = mp->am_parent;
719     mntfs *mf_parent = mp_parent->am_mnt;
720     int error = 0;
721 
722     free_map(mp);
723     plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
724     mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
725     if (mp && error < 0)
726       mp = mf_parent->mf_ops->mount_child(mp, &error);
727     if (error > 0) {
728       errno = error;
729       plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
730     }
731     XFREE(fname);
732   } else
733     /*
734      * We have a race here.
735      * If this node has a pending mount and amd is going down (unmounting
736      * everything in the process), then we could potentially free it here
737      * while a struct continuation still has a reference to it. So when
738      * amfs_cont is called, it blows up.
739      * We avoid the race by refusing to free any nodes that have
740      * pending mounts (defined as having a non-NULL am_mfarray).
741      */
742     if (!mp->am_mfarray)
743       free_map(mp);
744 }
745 
746 
747 /*
748  * Fork the automounter
749  *
750  * TODO: Need a better strategy for handling errors
751  */
752 static int
753 dofork(void)
754 {
755   int pid;
756 
757 top:
758   pid = fork();
759 
760   if (pid < 0) {		/* fork error, retry in 1 second */
761     sleep(1);
762     goto top;
763   }
764   if (pid == 0) {		/* child process (foreground==false) */
765     am_set_mypid();
766     foreground = 0;
767   } else {			/* parent process, has one more child */
768     NumChildren++;
769   }
770 
771   return pid;
772 }
773 
774 
775 int
776 background(void)
777 {
778   int pid = dofork();
779 
780   if (pid == 0) {
781     dlog("backgrounded");
782     foreground = 0;
783   } else
784     dlog("forked process %d", pid);
785   return pid;
786 }
787