1 /*
2 * Copyright (c) 1997-2006 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 *
40 * File: am-utils/amd/autil.c
41 *
42 */
43
44 /*
45 * utilities specified to amd, taken out of the older amd/util.c.
46 */
47
48 #ifdef HAVE_CONFIG_H
49 # include <config.h>
50 #endif /* HAVE_CONFIG_H */
51 #include <am_defs.h>
52 #include <amd.h>
53
54 int NumChildren = 0; /* number of children of primary amd */
55 static char invalid_keys[] = "\"'!;@ \t\n";
56
57 /****************************************************************************
58 *** MACROS ***
59 ****************************************************************************/
60
61 #ifdef HAVE_TRANSPORT_TYPE_TLI
62 # define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */
63 #endif /* HAVE_TRANSPORT_TYPE_TLI */
64
65
66 /****************************************************************************
67 *** FORWARD DEFINITIONS ***
68 ****************************************************************************/
69 static void domain_strip(char *otherdom, char *localdom);
70 static int dofork(void);
71
72
73 /****************************************************************************
74 *** FUNCTIONS ***
75 ****************************************************************************/
76
77 /*
78 * Copy s into p, reallocating p if necessary
79 */
80 char *
strealloc(char * p,char * s)81 strealloc(char *p, char *s)
82 {
83 size_t len = strlen(s) + 1;
84
85 p = (char *) xrealloc((voidp) p, len);
86
87 xstrlcpy(p, s, len);
88 #ifdef DEBUG_MEM
89 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
90 malloc_verify();
91 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
92 #endif /* DEBUG_MEM */
93 return p;
94 }
95
96
97 /*
98 * Strip off the trailing part of a domain
99 * to produce a short-form domain relative
100 * to the local host domain.
101 * Note that this has no effect if the domain
102 * names do not have the same number of
103 * components. If that restriction proves
104 * to be a problem then the loop needs recoding
105 * to skip from right to left and do partial
106 * matches along the way -- ie more expensive.
107 */
108 static void
domain_strip(char * otherdom,char * localdom)109 domain_strip(char *otherdom, char *localdom)
110 {
111 char *p1, *p2;
112
113 if ((p1 = strchr(otherdom, '.')) &&
114 (p2 = strchr(localdom, '.')) &&
115 STREQ(p1 + 1, p2 + 1))
116 *p1 = '\0';
117 }
118
119
120 /*
121 * Normalize a host name: replace cnames with real names, and decide if to
122 * strip domain name or not.
123 */
124 void
host_normalize(char ** chp)125 host_normalize(char **chp)
126 {
127 /*
128 * Normalize hosts is used to resolve host name aliases
129 * and replace them with the standard-form name.
130 * Invoked with "-n" command line option.
131 */
132 if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) {
133 struct hostent *hp;
134 hp = gethostbyname(*chp);
135 if (hp && hp->h_addrtype == AF_INET) {
136 dlog("Hostname %s normalized to %s", *chp, hp->h_name);
137 *chp = strealloc(*chp, (char *) hp->h_name);
138 }
139 }
140 if (gopt.flags & CFM_DOMAIN_STRIP) {
141 domain_strip(*chp, hostd);
142 }
143 }
144
145
146 /*
147 * Keys are not allowed to contain " ' ! or ; to avoid
148 * problems with macro expansions.
149 */
150 int
valid_key(char * key)151 valid_key(char *key)
152 {
153 while (*key)
154 if (strchr(invalid_keys, *key++))
155 return FALSE;
156 return TRUE;
157 }
158
159
160 void
forcibly_timeout_mp(am_node * mp)161 forcibly_timeout_mp(am_node *mp)
162 {
163 mntfs *mf = mp->am_mnt;
164 /*
165 * Arrange to timeout this node
166 */
167 if (mf && ((mp->am_flags & AMF_ROOT) ||
168 (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) {
169 if (mf->mf_flags & MFF_UNMOUNTING)
170 plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path);
171 else
172 plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
173 } else {
174 plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
175 mp->am_flags &= ~AMF_NOTIMEOUT;
176 mp->am_ttl = clocktime(NULL);
177 /*
178 * Force mtime update of parent dir, to prevent DNLC/dcache from caching
179 * the old entry, which could result in ESTALE errors, bad symlinks, and
180 * more.
181 */
182 clocktime(&mp->am_parent->am_fattr.na_mtime);
183 reschedule_timeout_mp();
184 }
185 }
186
187
188 void
mf_mounted(mntfs * mf,bool_t call_free_opts)189 mf_mounted(mntfs *mf, bool_t call_free_opts)
190 {
191 int quoted;
192 int wasmounted = mf->mf_flags & MFF_MOUNTED;
193
194 if (!wasmounted) {
195 /*
196 * If this is a freshly mounted
197 * filesystem then update the
198 * mntfs structure...
199 */
200 mf->mf_flags |= MFF_MOUNTED;
201 mf->mf_error = 0;
202
203 /*
204 * Do mounted callback
205 */
206 if (mf->mf_ops->mounted)
207 mf->mf_ops->mounted(mf);
208
209 /*
210 * Be careful when calling free_ops and XFREE here. Some pseudo file
211 * systems like nfsx call this function (mf_mounted), even though it
212 * would be called by the lower-level amd file system functions. nfsx
213 * needs to call this function because of the other actions it takes.
214 * So we pass a boolean from the caller (yes, not so clean workaround)
215 * to determine if we should free or not. If we're not freeing (often
216 * because we're called from a callback function), then just to be sure,
217 * we'll zero out the am_opts structure and set the pointer to NULL.
218 * The parent mntfs node owns this memory and is going to free it with a
219 * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code).
220 */
221 if (call_free_opts) {
222 free_opts(mf->mf_fo); /* this free is needed to prevent leaks */
223 XFREE(mf->mf_fo); /* (also this one) */
224 } else {
225 memset(mf->mf_fo, 0, sizeof(am_opts));
226 mf->mf_fo = NULL;
227 }
228 }
229
230 if (mf->mf_flags & MFF_RESTART) {
231 mf->mf_flags &= ~MFF_RESTART;
232 dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags);
233 }
234
235 /*
236 * Log message
237 */
238 quoted = strchr(mf->mf_info, ' ') != 0;
239 plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
240 quoted ? "\"" : "",
241 mf->mf_info,
242 quoted ? "\"" : "",
243 wasmounted ? "referenced" : "mounted",
244 mf->mf_ops->fs_type, mf->mf_mount);
245 }
246
247
248 void
am_mounted(am_node * mp)249 am_mounted(am_node *mp)
250 {
251 int notimeout = 0; /* assume normal timeouts initially */
252 mntfs *mf = mp->am_mnt;
253
254 /*
255 * This is the parent mntfs which does the mf->mf_fo (am_opts type), and
256 * we're passing TRUE here to tell mf_mounted to actually free the
257 * am_opts. See a related comment in mf_mounted().
258 */
259 mf_mounted(mf, TRUE);
260
261 #ifdef HAVE_FS_AUTOFS
262 if (mf->mf_flags & MFF_IS_AUTOFS)
263 autofs_mounted(mp);
264 #endif /* HAVE_FS_AUTOFS */
265
266 /*
267 * Patch up path for direct mounts
268 */
269 if (mp->am_parent && mp->am_parent->am_mnt->mf_fsflags & FS_DIRECT)
270 mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
271
272 /*
273 * Check whether this mount should be cached permanently or not,
274 * and handle user-requested timeouts.
275 */
276 /* first check if file system was set to never timeout */
277 if (mf->mf_fsflags & FS_NOTIMEOUT)
278 notimeout = 1;
279 /* next, alter that decision by map flags */
280 if (mf->mf_mopts) {
281 mntent_t mnt;
282 mnt.mnt_opts = mf->mf_mopts;
283
284 /* umount option: user wants to unmount this entry */
285 if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount"))
286 notimeout = 0;
287 /* noumount option: user does NOT want to unmount this entry */
288 if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount"))
289 notimeout = 1;
290 /* utimeout=N option: user wants to unmount this option AND set timeout */
291 if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
292 mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */
293 else
294 notimeout = 0;
295 /* special case: don't try to unmount "/" (it can never succeed) */
296 if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0')
297 notimeout = 1;
298 }
299 /* finally set actual flags */
300 if (notimeout) {
301 mp->am_flags |= AMF_NOTIMEOUT;
302 plog(XLOG_INFO, "%s set to never timeout", mp->am_path);
303 } else {
304 mp->am_flags &= ~AMF_NOTIMEOUT;
305 plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo);
306 }
307
308 /*
309 * If this node is a symlink then
310 * compute the length of the returned string.
311 */
312 if (mp->am_fattr.na_type == NFLNK)
313 mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount);
314
315 /*
316 * Record mount time, and update am_stats at the same time.
317 */
318 mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime);
319 new_ttl(mp);
320
321 /*
322 * Update mtime of parent node (copying "struct nfstime" in '=' below)
323 */
324 if (mp->am_parent && mp->am_parent->am_mnt)
325 mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime;
326
327 /*
328 * This is ugly, but essentially unavoidable
329 * Sublinks must be treated separately as type==link
330 * when the base type is different.
331 */
332 if (mp->am_link && mf->mf_ops != &amfs_link_ops)
333 amfs_link_ops.mount_fs(mp, mf);
334
335 /*
336 * Now, if we can, do a reply to our client here
337 * to speed things up.
338 */
339 #ifdef HAVE_FS_AUTOFS
340 if (mp->am_flags & AMF_AUTOFS)
341 autofs_mount_succeeded(mp);
342 else
343 #endif /* HAVE_FS_AUTOFS */
344 nfs_quick_reply(mp, 0);
345
346 /*
347 * Update stats
348 */
349 amd_stats.d_mok++;
350 }
351
352
353 /*
354 * Replace mount point with a reference to an error filesystem.
355 * The mount point (struct mntfs) is NOT discarded,
356 * the caller must do it if it wants to _before_ calling this function.
357 */
358 void
assign_error_mntfs(am_node * mp)359 assign_error_mntfs(am_node *mp)
360 {
361 int error;
362 dlog("assign_error_mntfs");
363 /*
364 * Save the old error code
365 */
366 error = mp->am_error;
367 if (error <= 0)
368 error = mp->am_mnt->mf_error;
369 /*
370 * Allocate a new error reference
371 */
372 mp->am_mnt = new_mntfs();
373 /*
374 * Put back the error code
375 */
376 mp->am_mnt->mf_error = error;
377 mp->am_mnt->mf_flags |= MFF_ERROR;
378 /*
379 * Zero the error in the mount point
380 */
381 mp->am_error = 0;
382 }
383
384
385 /*
386 * Build a new map cache for this node, or re-use
387 * an existing cache for the same map.
388 */
389 void
amfs_mkcacheref(mntfs * mf)390 amfs_mkcacheref(mntfs *mf)
391 {
392 char *cache;
393
394 if (mf->mf_fo && mf->mf_fo->opt_cache)
395 cache = mf->mf_fo->opt_cache;
396 else
397 cache = "none";
398 mf->mf_private = (opaque_t) mapc_find(mf->mf_info,
399 cache,
400 (mf->mf_fo ? mf->mf_fo->opt_maptype : NULL));
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_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
457 memset((voidp) &mnt, 0, sizeof(mnt));
458 mnt.mnt_dir = dir;
459 mnt.mnt_fsname = pid_fsname;
460 mnt.mnt_opts = opts;
461
462 #ifdef HAVE_FS_AUTOFS
463 if (mf->mf_flags & MFF_IS_AUTOFS) {
464 type = MOUNT_TYPE_AUTOFS;
465 /*
466 * Make sure that amd's top-level autofs mounts are hidden by default
467 * from df.
468 * XXX: It works ok on Linux, might not work on other systems.
469 */
470 mnt.mnt_type = "autofs";
471 } else
472 #endif /* HAVE_FS_AUTOFS */
473 {
474 type = MOUNT_TYPE_NFS;
475 /*
476 * Make sure that amd's top-level NFS mounts are hidden by default
477 * from df.
478 * If they don't appear to support the either the "ignore" mnttab
479 * option entry, or the "auto" one, set the mount type to "nfs".
480 */
481 mnt.mnt_type = HIDE_MOUNT_TYPE;
482 }
483
484 retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
485 if (retry <= 0)
486 retry = 2; /* XXX: default to 2 retries */
487
488 /*
489 * SET MOUNT ARGS
490 */
491
492 /*
493 * Make a ``hostname'' string for the kernel
494 */
495 xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s",
496 get_server_pid(), am_get_hostname(), dir);
497 /*
498 * Most kernels have a name length restriction (64 bytes)...
499 */
500 if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
501 xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..",
502 sizeof(fs_hostname) - MAXHOSTNAMELEN + 3);
503 #ifdef HOSTNAMESZ
504 /*
505 * ... and some of these restrictions are 32 bytes (HOSTNAMESZ)
506 * If you need to get the definition for HOSTNAMESZ found, you may
507 * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file.
508 */
509 if (strlen(fs_hostname) >= HOSTNAMESZ)
510 xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..",
511 sizeof(fs_hostname) - HOSTNAMESZ + 3);
512 #endif /* HOSTNAMESZ */
513
514 /*
515 * Finally we can compute the mount genflags set above,
516 * and add any automounter specific flags.
517 */
518 genflags = compute_mount_flags(&mnt);
519 #ifdef HAVE_FS_AUTOFS
520 if (on_autofs)
521 genflags |= autofs_compute_mount_flags(&mnt);
522 #endif /* HAVE_FS_AUTOFS */
523 genflags |= compute_automounter_mount_flags(&mnt);
524
525 again:
526 if (!(mf->mf_flags & MFF_IS_AUTOFS)) {
527 nfs_args_t nfs_args;
528 am_nfs_fh *fhp;
529 am_nfs_handle_t 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);
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((voidp) &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 memmove(&anh.v2, fhp, sizeof(*fhp));
563 #ifdef HAVE_TRANSPORT_TYPE_TLI
564 compute_nfs_args(&nfs_args,
565 &mnt,
566 genflags,
567 nfsncp,
568 NULL, /* remote host IP addr is set below */
569 NFS_VERSION, /* version 2 */
570 "udp",
571 &anh,
572 fs_hostname,
573 pid_fsname);
574 /*
575 * IMPORTANT: set the correct IP address AFTERWARDS. It cannot
576 * be done using the normal mechanism of compute_nfs_args(), because
577 * that one will allocate a new address and use NFS_SA_DREF() to copy
578 * parts to it, while assuming that the ip_addr passed is always
579 * a "struct sockaddr_in". That assumption is incorrect on TLI systems,
580 * because they define a special macro HOST_SELF which is DIFFERENT
581 * than localhost (127.0.0.1)!
582 */
583 nfs_args.addr = &nfsxprt->xp_ltaddr;
584 #else /* not HAVE_TRANSPORT_TYPE_TLI */
585 compute_nfs_args(&nfs_args,
586 &mnt,
587 genflags,
588 NULL,
589 &sin,
590 NFS_VERSION, /* version 2 */
591 "udp",
592 &anh,
593 fs_hostname,
594 pid_fsname);
595 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
596
597 /*************************************************************************
598 * NOTE: while compute_nfs_args() works ok for regular NFS mounts *
599 * the toplvl one is not quite regular, and so some options must be *
600 * corrected by hand more carefully, *after* compute_nfs_args() runs. *
601 *************************************************************************/
602 compute_automounter_nfs_args(&nfs_args, &mnt);
603
604 if (amuDebug(D_TRACE)) {
605 print_nfs_args(&nfs_args, 0);
606 plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags);
607 }
608
609 /* This is it! Here we try to mount amd on its mount points */
610 error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args,
611 retry, type, 0, NULL, mnttab_file_name, on_autofs);
612
613 #ifdef HAVE_TRANSPORT_TYPE_TLI
614 free_knetconfig(nfs_args.knconf);
615 /*
616 * local automounter mounts do not allocate a special address, so
617 * no need to XFREE(nfs_args.addr) under TLI.
618 */
619 #endif /* HAVE_TRANSPORT_TYPE_TLI */
620
621 #ifdef HAVE_FS_AUTOFS
622 } else {
623 /* This is it! Here we try to mount amd on its mount points */
624 error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh,
625 retry, type, 0, NULL, mnttab_file_name, on_autofs);
626 #endif /* HAVE_FS_AUTOFS */
627 }
628 if (error == 0 || forced_unmount)
629 return error;
630
631 /*
632 * If user wants forced/lazy unmount semantics, then try it iff the
633 * current mount failed with EIO or ESTALE.
634 */
635 if (gopt.flags & CFM_FORCED_UNMOUNTS) {
636 switch (errno) {
637 case ESTALE:
638 case EIO:
639 forced_unmount = errno;
640 plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path);
641 if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name,
642 AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) {
643 plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path);
644 errno = forced_unmount;
645 } else
646 goto again;
647 default:
648 break;
649 }
650 }
651
652 return error;
653 }
654
655
656 void
am_unmounted(am_node * mp)657 am_unmounted(am_node *mp)
658 {
659 mntfs *mf = mp->am_mnt;
660
661 if (!foreground) /* firewall - should never happen */
662 return;
663
664 /*
665 * Do unmounted callback
666 */
667 if (mf->mf_ops->umounted)
668 mf->mf_ops->umounted(mf);
669
670 /*
671 * This is ugly, but essentially unavoidable.
672 * Sublinks must be treated separately as type==link
673 * when the base type is different.
674 */
675 if (mp->am_link && mf->mf_ops != &amfs_link_ops)
676 amfs_link_ops.umount_fs(mp, mf);
677
678 #ifdef HAVE_FS_AUTOFS
679 if (mf->mf_flags & MFF_IS_AUTOFS)
680 autofs_release_fh(mp);
681 if (mp->am_flags & AMF_AUTOFS)
682 autofs_umount_succeeded(mp);
683 #endif /* HAVE_FS_AUTOFS */
684
685 /*
686 * Clean up any directories that were made
687 *
688 * If we remove the mount point of a pending mount, any queued access
689 * to it will fail. So don't do it in that case.
690 * Also don't do it if the refcount is > 1.
691 */
692 if (mf->mf_flags & MFF_MKMNT &&
693 mf->mf_refc == 1 &&
694 !(mp->am_flags & AMF_REMOUNT)) {
695 plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount);
696 rmdirs(mf->mf_mount);
697 mf->mf_flags &= ~MFF_MKMNT;
698 }
699
700 /*
701 * If this is a pseudo-directory then adjust the link count
702 * in the parent
703 */
704 if (mp->am_parent && mp->am_fattr.na_type == NFDIR)
705 --mp->am_parent->am_fattr.na_nlink;
706
707 /*
708 * Update mtime of parent node
709 */
710 if (mp->am_parent && mp->am_parent->am_mnt)
711 clocktime(&mp->am_parent->am_fattr.na_mtime);
712
713 if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) {
714 char *fname = strdup(mp->am_name);
715 am_node *mp_parent = mp->am_parent;
716 mntfs *mf_parent = mp_parent->am_mnt;
717 int error = 0;
718
719 free_map(mp);
720 plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
721 mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
722 if (mp && error < 0)
723 mp = mf_parent->mf_ops->mount_child(mp, &error);
724 if (error > 0) {
725 errno = error;
726 plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
727 }
728 XFREE(fname);
729 } else
730 /*
731 * We have a race here.
732 * If this node has a pending mount and amd is going down (unmounting
733 * everything in the process), then we could potentially free it here
734 * while a struct continuation still has a reference to it. So when
735 * amfs_cont is called, it blows up.
736 * We avoid the race by refusing to free any nodes that have
737 * pending mounts (defined as having a non-NULL am_mfarray).
738 */
739 if (!mp->am_mfarray)
740 free_map(mp);
741 }
742
743
744 /*
745 * Fork the automounter
746 *
747 * TODO: Need a better strategy for handling errors
748 */
749 static int
dofork(void)750 dofork(void)
751 {
752 int pid;
753
754 top:
755 pid = fork();
756
757 if (pid < 0) { /* fork error, retry in 1 second */
758 sleep(1);
759 goto top;
760 }
761 if (pid == 0) { /* child process (foreground==false) */
762 am_set_mypid();
763 foreground = 0;
764 } else { /* parent process, has one more child */
765 NumChildren++;
766 }
767
768 return pid;
769 }
770
771
772 int
background(void)773 background(void)
774 {
775 int pid = dofork();
776
777 if (pid == 0) {
778 dlog("backgrounded");
779 foreground = 0;
780 } else
781 dlog("forked process %d", pid);
782 return pid;
783 }
784