xref: /netbsd-src/external/bsd/am-utils/dist/amd/autil.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: autil.c,v 1.1.1.2 2009/03/20 20:26:48 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2009 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     /*
172      * We aren't going to schedule a timeout, so we need to notify the
173      * child here unless we are already unmounting, in which case that
174      * process is responsible for notifying the child.
175      */
176     if (mf->mf_flags & MFF_UNMOUNTING)
177       plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path);
178     else {
179       plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
180       notify_child(mp, AMQ_UMNT_FAILED, EBUSY, 0);
181     }
182   } else {
183     plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
184     mp->am_flags &= ~AMF_NOTIMEOUT;
185     mp->am_ttl = clocktime(NULL);
186     /*
187      * Force mtime update of parent dir, to prevent DNLC/dcache from caching
188      * the old entry, which could result in ESTALE errors, bad symlinks, and
189      * more.
190      */
191     clocktime(&mp->am_parent->am_fattr.na_mtime);
192     reschedule_timeout_mp();
193   }
194 }
195 
196 
197 void
198 mf_mounted(mntfs *mf, bool_t call_free_opts)
199 {
200   int quoted;
201   int wasmounted = mf->mf_flags & MFF_MOUNTED;
202 
203   if (!wasmounted) {
204     /*
205      * If this is a freshly mounted
206      * filesystem then update the
207      * mntfs structure...
208      */
209     mf->mf_flags |= MFF_MOUNTED;
210     mf->mf_error = 0;
211 
212     /*
213      * Do mounted callback
214      */
215     if (mf->mf_ops->mounted)
216       mf->mf_ops->mounted(mf);
217 
218     /*
219      * Be careful when calling free_ops and XFREE here.  Some pseudo file
220      * systems like nfsx call this function (mf_mounted), even though it
221      * would be called by the lower-level amd file system functions.  nfsx
222      * needs to call this function because of the other actions it takes.
223      * So we pass a boolean from the caller (yes, not so clean workaround)
224      * to determine if we should free or not.  If we're not freeing (often
225      * because we're called from a callback function), then just to be sure,
226      * we'll zero out the am_opts structure and set the pointer to NULL.
227      * The parent mntfs node owns this memory and is going to free it with a
228      * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code).
229      */
230     if (call_free_opts) {
231       free_opts(mf->mf_fo);	/* this free is needed to prevent leaks */
232       XFREE(mf->mf_fo);		/* (also this one) */
233     } else {
234       memset(mf->mf_fo, 0, sizeof(am_opts));
235       mf->mf_fo = NULL;
236     }
237   }
238 
239   if (mf->mf_flags & MFF_RESTART) {
240     mf->mf_flags &= ~MFF_RESTART;
241     dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags);
242   }
243 
244   /*
245    * Log message
246    */
247   quoted = strchr(mf->mf_info, ' ') != 0;
248   plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
249        quoted ? "\"" : "",
250        mf->mf_info,
251        quoted ? "\"" : "",
252        wasmounted ? "referenced" : "mounted",
253        mf->mf_ops->fs_type, mf->mf_mount);
254 }
255 
256 
257 void
258 am_mounted(am_node *mp)
259 {
260   int notimeout = 0;		/* assume normal timeouts initially */
261   mntfs *mf = mp->am_mnt;
262 
263   /*
264    * This is the parent mntfs which does the mf->mf_fo (am_opts type), and
265    * we're passing TRUE here to tell mf_mounted to actually free the
266    * am_opts.  See a related comment in mf_mounted().
267    */
268   mf_mounted(mf, TRUE);
269 
270 #ifdef HAVE_FS_AUTOFS
271   if (mf->mf_flags & MFF_IS_AUTOFS)
272     autofs_mounted(mp);
273 #endif /* HAVE_FS_AUTOFS */
274 
275   /*
276    * Patch up path for direct mounts
277    */
278   if (mp->am_parent && mp->am_parent->am_mnt->mf_fsflags & FS_DIRECT)
279     mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
280 
281   /*
282    * Check whether this mount should be cached permanently or not,
283    * and handle user-requested timeouts.
284    */
285   /* first check if file system was set to never timeout */
286   if (mf->mf_fsflags & FS_NOTIMEOUT)
287     notimeout = 1;
288   /* next, alter that decision by map flags */
289   if (mf->mf_mopts) {
290     mntent_t mnt;
291     mnt.mnt_opts = mf->mf_mopts;
292 
293     /* umount option: user wants to unmount this entry */
294     if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount"))
295       notimeout = 0;
296     /* noumount option: user does NOT want to unmount this entry */
297     if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount"))
298       notimeout = 1;
299     /* utimeout=N option: user wants to unmount this option AND set timeout */
300     if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
301       mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */
302     else
303       notimeout = 0;
304     /* special case: don't try to unmount "/" (it can never succeed) */
305     if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0')
306       notimeout = 1;
307   }
308   /* finally set actual flags */
309   if (notimeout) {
310     mp->am_flags |= AMF_NOTIMEOUT;
311     plog(XLOG_INFO, "%s set to never timeout", mp->am_path);
312   } else {
313     mp->am_flags &= ~AMF_NOTIMEOUT;
314     plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo);
315   }
316 
317   /*
318    * If this node is a symlink then
319    * compute the length of the returned string.
320    */
321   if (mp->am_fattr.na_type == NFLNK)
322     mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount);
323 
324   /*
325    * Record mount time, and update am_stats at the same time.
326    */
327   mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime);
328   new_ttl(mp);
329 
330   /*
331    * Update mtime of parent node (copying "struct nfstime" in '=' below)
332    */
333   if (mp->am_parent && mp->am_parent->am_mnt)
334     mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime;
335 
336   /*
337    * This is ugly, but essentially unavoidable
338    * Sublinks must be treated separately as type==link
339    * when the base type is different.
340    */
341   if (mp->am_link && mf->mf_ops != &amfs_link_ops)
342     amfs_link_ops.mount_fs(mp, mf);
343 
344   /*
345    * Now, if we can, do a reply to our client here
346    * to speed things up.
347    */
348 #ifdef HAVE_FS_AUTOFS
349   if (mp->am_flags & AMF_AUTOFS)
350     autofs_mount_succeeded(mp);
351   else
352 #endif /* HAVE_FS_AUTOFS */
353     nfs_quick_reply(mp, 0);
354 
355   /*
356    * Update stats
357    */
358   amd_stats.d_mok++;
359 }
360 
361 
362 /*
363  * Replace mount point with a reference to an error filesystem.
364  * The mount point (struct mntfs) is NOT discarded,
365  * the caller must do it if it wants to _before_ calling this function.
366  */
367 void
368 assign_error_mntfs(am_node *mp)
369 {
370   int error;
371   dlog("assign_error_mntfs");
372   /*
373    * Save the old error code
374    */
375   error = mp->am_error;
376   if (error <= 0)
377     error = mp->am_mnt->mf_error;
378   /*
379    * Allocate a new error reference
380    */
381   mp->am_mnt = new_mntfs();
382   /*
383    * Put back the error code
384    */
385   mp->am_mnt->mf_error = error;
386   mp->am_mnt->mf_flags |= MFF_ERROR;
387   /*
388    * Zero the error in the mount point
389    */
390   mp->am_error = 0;
391 }
392 
393 
394 /*
395  * Build a new map cache for this node, or re-use
396  * an existing cache for the same map.
397  */
398 void
399 amfs_mkcacheref(mntfs *mf)
400 {
401   char *cache;
402 
403   if (mf->mf_fo && mf->mf_fo->opt_cache)
404     cache = mf->mf_fo->opt_cache;
405   else
406     cache = "none";
407   mf->mf_private = (opaque_t) mapc_find(mf->mf_info,
408 					cache,
409 					(mf->mf_fo ? mf->mf_fo->opt_maptype : NULL),
410 					mf->mf_mount);
411   mf->mf_prfree = mapc_free;
412 }
413 
414 
415 /*
416  * Locate next node in sibling list which is mounted
417  * and is not an error node.
418  */
419 am_node *
420 next_nonerror_node(am_node *xp)
421 {
422   mntfs *mf;
423 
424   /*
425    * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
426    * Fixes a race condition when mounting direct automounts.
427    * Also fixes a problem when doing a readdir on a directory
428    * containing hung automounts.
429    */
430   while (xp &&
431 	 (!(mf = xp->am_mnt) ||	/* No mounted filesystem */
432 	  mf->mf_error != 0 ||	/* There was a mntfs error */
433 	  xp->am_error != 0 ||	/* There was a mount error */
434 	  !(mf->mf_flags & MFF_MOUNTED) ||	/* The fs is not mounted */
435 	  (mf->mf_server->fs_flags & FSF_DOWN))	/* The fs may be down */
436 	 )
437     xp = xp->am_osib;
438 
439   return xp;
440 }
441 
442 
443 /*
444  * Mount an automounter directory.
445  * The automounter is connected into the system
446  * as a user-level NFS server.  amfs_mount constructs
447  * the necessary NFS parameters to be given to the
448  * kernel so that it will talk back to us.
449  *
450  * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP).
451  *
452  * NEW: on certain systems, mounting can be done using the
453  * kernel-level automount (autofs) support. In that case,
454  * we don't need NFS at all here.
455  */
456 int
457 amfs_mount(am_node *mp, mntfs *mf, char *opts)
458 {
459   char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
460   int retry, error = 0, genflags;
461   int on_autofs = mf->mf_flags & MFF_ON_AUTOFS;
462   char *dir = mf->mf_mount;
463   mntent_t mnt;
464   MTYPE_TYPE type;
465   int forced_unmount = 0;	/* are we using forced unmounts? */
466 
467   memset((voidp) &mnt, 0, sizeof(mnt));
468   mnt.mnt_dir = dir;
469   mnt.mnt_fsname = pid_fsname;
470   mnt.mnt_opts = opts;
471 
472 #ifdef HAVE_FS_AUTOFS
473   if (mf->mf_flags & MFF_IS_AUTOFS) {
474     type = MOUNT_TYPE_AUTOFS;
475     /*
476      * Make sure that amd's top-level autofs mounts are hidden by default
477      * from df.
478      * XXX: It works ok on Linux, might not work on other systems.
479      */
480     mnt.mnt_type = "autofs";
481   } else
482 #endif /* HAVE_FS_AUTOFS */
483   {
484     type = MOUNT_TYPE_NFS;
485     /*
486      * Make sure that amd's top-level NFS mounts are hidden by default
487      * from df.
488      * If they don't appear to support the either the "ignore" mnttab
489      * option entry, or the "auto" one, set the mount type to "nfs".
490      */
491     mnt.mnt_type = HIDE_MOUNT_TYPE;
492   }
493 
494   retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
495   if (retry <= 0)
496     retry = 2;			/* XXX: default to 2 retries */
497 
498   /*
499    * SET MOUNT ARGS
500    */
501 
502   /*
503    * Make a ``hostname'' string for the kernel
504    */
505   xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s",
506 	    get_server_pid(), am_get_hostname(), dir);
507   /*
508    * Most kernels have a name length restriction (64 bytes)...
509    */
510   if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
511     xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..",
512 	     sizeof(fs_hostname) - MAXHOSTNAMELEN + 3);
513 #ifdef HOSTNAMESZ
514   /*
515    * ... and some of these restrictions are 32 bytes (HOSTNAMESZ)
516    * If you need to get the definition for HOSTNAMESZ found, you may
517    * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file.
518    */
519   if (strlen(fs_hostname) >= HOSTNAMESZ)
520     xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..",
521 	     sizeof(fs_hostname) - HOSTNAMESZ + 3);
522 #endif /* HOSTNAMESZ */
523 
524   /*
525    * Finally we can compute the mount genflags set above,
526    * and add any automounter specific flags.
527    */
528   genflags = compute_mount_flags(&mnt);
529 #ifdef HAVE_FS_AUTOFS
530   if (on_autofs)
531     genflags |= autofs_compute_mount_flags(&mnt);
532 #endif /* HAVE_FS_AUTOFS */
533   genflags |= compute_automounter_mount_flags(&mnt);
534 
535 again:
536   if (!(mf->mf_flags & MFF_IS_AUTOFS)) {
537     nfs_args_t nfs_args;
538     am_nfs_fh *fhp;
539     am_nfs_handle_t anh;
540 #ifndef HAVE_TRANSPORT_TYPE_TLI
541     u_short port;
542     struct sockaddr_in sin;
543 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
544 
545     /*
546      * get fhandle of remote path for automount point
547      */
548     fhp = get_root_nfs_fh(dir);
549     if (!fhp) {
550       plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
551       return EINVAL;
552     }
553 
554 #ifndef HAVE_TRANSPORT_TYPE_TLI
555     /*
556      * Create sockaddr to point to the local machine.
557      */
558     memset((voidp) &sin, 0, sizeof(sin));
559     /* as per POSIX, sin_len need not be set (used internally by kernel) */
560     sin.sin_family = AF_INET;
561     sin.sin_addr = myipaddr;
562     port = hasmntval(&mnt, MNTTAB_OPT_PORT);
563     if (port) {
564       sin.sin_port = htons(port);
565     } else {
566       plog(XLOG_ERROR, "no port number specified for %s", dir);
567       return EINVAL;
568     }
569 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
570 
571     /* setup the many fields and flags within nfs_args */
572     memmove(&anh.v2, fhp, sizeof(*fhp));
573 #ifdef HAVE_TRANSPORT_TYPE_TLI
574     compute_nfs_args(&nfs_args,
575 		     &mnt,
576 		     genflags,
577 		     nfsncp,
578 		     NULL,	/* remote host IP addr is set below */
579 		     NFS_VERSION,	/* version 2 */
580 		     "udp",
581 		     &anh,
582 		     fs_hostname,
583 		     pid_fsname);
584     /*
585      * IMPORTANT: set the correct IP address AFTERWARDS.  It cannot
586      * be done using the normal mechanism of compute_nfs_args(), because
587      * that one will allocate a new address and use NFS_SA_DREF() to copy
588      * parts to it, while assuming that the ip_addr passed is always
589      * a "struct sockaddr_in".  That assumption is incorrect on TLI systems,
590      * because they define a special macro HOST_SELF which is DIFFERENT
591      * than localhost (127.0.0.1)!
592      */
593     nfs_args.addr = &nfsxprt->xp_ltaddr;
594 #else /* not HAVE_TRANSPORT_TYPE_TLI */
595     compute_nfs_args(&nfs_args,
596 		     &mnt,
597 		     genflags,
598 		     NULL,
599 		     &sin,
600 		     NFS_VERSION,	/* version 2 */
601 		     "udp",
602 		     &anh,
603 		     fs_hostname,
604 		     pid_fsname);
605 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
606 
607     /*************************************************************************
608      * NOTE: while compute_nfs_args() works ok for regular NFS mounts	     *
609      * the toplvl one is not quite regular, and so some options must be      *
610      * corrected by hand more carefully, *after* compute_nfs_args() runs.    *
611      *************************************************************************/
612     compute_automounter_nfs_args(&nfs_args, &mnt);
613 
614     if (amuDebug(D_TRACE)) {
615       print_nfs_args(&nfs_args, 0);
616       plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags);
617     }
618 
619     /* This is it!  Here we try to mount amd on its mount points */
620     error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args,
621 		     retry, type, 0, NULL, mnttab_file_name, on_autofs);
622 
623 #ifdef HAVE_TRANSPORT_TYPE_TLI
624     free_knetconfig(nfs_args.knconf);
625     /*
626      * local automounter mounts do not allocate a special address, so
627      * no need to XFREE(nfs_args.addr) under TLI.
628      */
629 #endif /* HAVE_TRANSPORT_TYPE_TLI */
630 
631 #ifdef HAVE_FS_AUTOFS
632   } else {
633     /* This is it!  Here we try to mount amd on its mount points */
634     error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh,
635 		     retry, type, 0, NULL, mnttab_file_name, on_autofs);
636 #endif /* HAVE_FS_AUTOFS */
637   }
638   if (error == 0 || forced_unmount)
639      return error;
640 
641   /*
642    * If user wants forced/lazy unmount semantics, then try it iff the
643    * current mount failed with EIO or ESTALE.
644    */
645   if (gopt.flags & CFM_FORCED_UNMOUNTS) {
646     switch (errno) {
647     case ESTALE:
648     case EIO:
649       forced_unmount = errno;
650       plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path);
651       if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name,
652 			     AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) {
653 	plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path);
654 	errno = forced_unmount;
655       } else
656 	goto again;
657     default:
658       break;
659     }
660   }
661 
662   return error;
663 }
664 
665 
666 void
667 am_unmounted(am_node *mp)
668 {
669   mntfs *mf = mp->am_mnt;
670 
671   if (!foreground) {		/* firewall - should never happen */
672     /*
673      * This is a coding error.  Make sure we hear about it!
674      */
675     plog(XLOG_FATAL, "am_unmounted: illegal use in background (%s)",
676 	mp->am_name);
677     notify_child(mp, AMQ_UMNT_OK, 0, 0);	/* XXX - be safe? */
678     return;
679   }
680 
681   /*
682    * Do unmounted callback
683    */
684   if (mf->mf_ops->umounted)
685     mf->mf_ops->umounted(mf);
686 
687   /*
688    * This is ugly, but essentially unavoidable.
689    * Sublinks must be treated separately as type==link
690    * when the base type is different.
691    */
692   if (mp->am_link && mf->mf_ops != &amfs_link_ops)
693     amfs_link_ops.umount_fs(mp, mf);
694 
695 #ifdef HAVE_FS_AUTOFS
696   if (mf->mf_flags & MFF_IS_AUTOFS)
697     autofs_release_fh(mp);
698   if (mp->am_flags & AMF_AUTOFS)
699     autofs_umount_succeeded(mp);
700 #endif /* HAVE_FS_AUTOFS */
701 
702   /*
703    * Clean up any directories that were made
704    *
705    * If we remove the mount point of a pending mount, any queued access
706    * to it will fail. So don't do it in that case.
707    * Also don't do it if the refcount is > 1.
708    */
709   if (mf->mf_flags & MFF_MKMNT &&
710       mf->mf_refc == 1 &&
711       !(mp->am_flags & AMF_REMOUNT)) {
712     plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount);
713     rmdirs(mf->mf_mount);
714     mf->mf_flags &= ~MFF_MKMNT;
715   }
716 
717   /*
718    * If this is a pseudo-directory then adjust the link count
719    * in the parent
720    */
721   if (mp->am_parent && mp->am_fattr.na_type == NFDIR)
722     --mp->am_parent->am_fattr.na_nlink;
723 
724   /*
725    * Update mtime of parent node
726    */
727   if (mp->am_parent && mp->am_parent->am_mnt)
728     clocktime(&mp->am_parent->am_fattr.na_mtime);
729 
730   if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) {
731     char *fname = strdup(mp->am_name);
732     am_node *mp_parent = mp->am_parent;
733     mntfs *mf_parent = mp_parent->am_mnt;
734     am_node fake_mp;
735     int error = 0;
736 
737     /*
738      * We need to use notify_child() after free_map(), so save enough
739      * to do that in fake_mp.
740      */
741     fake_mp.am_fd[1] = mp->am_fd[1];
742     mp->am_fd[1] = -1;
743 
744     free_map(mp);
745     plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
746     mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
747     if (mp && error < 0)
748       mp = mf_parent->mf_ops->mount_child(mp, &error);
749     if (error > 0) {
750       errno = error;
751       plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
752       notify_child(&fake_mp, AMQ_UMNT_OK, 0, 0);
753     } else {
754       notify_child(&fake_mp, AMQ_UMNT_FAILED, EBUSY, 0);
755     }
756     XFREE(fname);
757   } else {
758     /*
759      * We have a race here.
760      * If this node has a pending mount and amd is going down (unmounting
761      * everything in the process), then we could potentially free it here
762      * while a struct continuation still has a reference to it. So when
763      * amfs_cont is called, it blows up.
764      * We avoid the race by refusing to free any nodes that have
765      * pending mounts (defined as having a non-NULL am_mfarray).
766      */
767     notify_child(mp, AMQ_UMNT_OK, 0, 0);	/* do this regardless */
768     if (!mp->am_mfarray)
769       free_map(mp);
770   }
771 }
772 
773 
774 /*
775  * Fork the automounter
776  *
777  * TODO: Need a better strategy for handling errors
778  */
779 static int
780 dofork(void)
781 {
782   int pid;
783 
784 top:
785   pid = fork();
786 
787   if (pid < 0) {		/* fork error, retry in 1 second */
788     sleep(1);
789     goto top;
790   }
791   if (pid == 0) {		/* child process (foreground==false) */
792     am_set_mypid();
793     foreground = 0;
794   } else {			/* parent process, has one more child */
795     NumChildren++;
796   }
797 
798   return pid;
799 }
800 
801 
802 int
803 background(void)
804 {
805   int pid = dofork();
806 
807   if (pid == 0) {
808     dlog("backgrounded");
809     foreground = 0;
810   } else
811     dlog("forked process %d", pid);
812   return pid;
813 }
814