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