2 * Copyright (c) 1997-2014 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.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * File: am-utils/amd/autil.c
41 * utilities specified to amd, taken out of the older amd/util.c.
46 #endif /* HAVE_CONFIG_H */
50 int NumChildren = 0; /* number of children of primary amd */
51 static char invalid_keys[] = "\"'!;@ \t\n";
53 /****************************************************************************
55 ****************************************************************************/
57 #ifdef HAVE_TRANSPORT_TYPE_TLI
58 # define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */
59 #endif /* HAVE_TRANSPORT_TYPE_TLI */
62 /****************************************************************************
63 *** FORWARD DEFINITIONS ***
64 ****************************************************************************/
65 static void domain_strip(char *otherdom, char *localdom);
66 static int dofork(void);
69 /****************************************************************************
71 ****************************************************************************/
74 * Copy s into p, reallocating p if necessary
77 strealloc(char *p, char *s)
79 size_t len = strlen(s) + 1;
81 p = (char *) xrealloc((voidp) p, len);
85 # if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY)
87 # endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */
88 #endif /* DEBUG_MEM */
94 * Strip off the trailing part of a domain
95 * to produce a short-form domain relative
96 * to the local host domain.
97 * Note that this has no effect if the domain
98 * names do not have the same number of
99 * components. If that restriction proves
100 * to be a problem then the loop needs recoding
101 * to skip from right to left and do partial
102 * matches along the way -- ie more expensive.
105 domain_strip(char *otherdom, char *localdom)
109 if ((p1 = strchr(otherdom, '.')) &&
110 (p2 = strchr(localdom, '.')) &&
111 STREQ(p1 + 1, p2 + 1))
117 * Normalize a host name: replace cnames with real names, and decide if to
118 * strip domain name or not.
121 host_normalize(char **chp)
124 * Normalize hosts is used to resolve host name aliases
125 * and replace them with the standard-form name.
126 * Invoked with "-n" command line option.
128 if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) {
130 hp = gethostbyname(*chp);
131 if (hp && hp->h_addrtype == AF_INET) {
132 dlog("Hostname %s normalized to %s", *chp, hp->h_name);
133 *chp = strealloc(*chp, (char *) hp->h_name);
136 if (gopt.flags & CFM_DOMAIN_STRIP) {
137 domain_strip(*chp, hostd);
143 * Keys are not allowed to contain " ' ! or ; to avoid
144 * problems with macro expansions.
150 if (strchr(invalid_keys, *key++))
157 forcibly_timeout_mp(am_node *mp)
159 mntfs *mf = mp->am_al->al_mnt;
161 * Arrange to timeout this node
163 if (mf && ((mp->am_flags & AMF_ROOT) ||
164 (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) {
166 * We aren't going to schedule a timeout, so we need to notify the
167 * child here unless we are already unmounting, in which case that
168 * process is responsible for notifying the child.
170 if (mf->mf_flags & MFF_UNMOUNTING)
171 plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path);
173 plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
174 notify_child(mp, AMQ_UMNT_FAILED, EBUSY, 0);
177 plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
178 mp->am_flags &= ~AMF_NOTIMEOUT;
179 mp->am_ttl = clocktime(NULL);
181 * Force mtime update of parent dir, to prevent DNLC/dcache from caching
182 * the old entry, which could result in ESTALE errors, bad symlinks, and
185 clocktime(&mp->am_parent->am_fattr.na_mtime);
186 reschedule_timeout_mp();
192 mf_mounted(mntfs *mf, bool_t call_free_opts)
195 int wasmounted = mf->mf_flags & MFF_MOUNTED;
199 * If this is a freshly mounted
200 * filesystem then update the
203 mf->mf_flags |= MFF_MOUNTED;
207 * Do mounted callback
209 if (mf->mf_ops->mounted)
210 mf->mf_ops->mounted(mf);
213 * We used to free the mf_mo (options) here, however they're now stored
214 * and managed with the mntfs and do not need to be free'd here (this ensures
215 * that we use the same options to monitor/unmount the system as we used
220 if (mf->mf_flags & MFF_RESTART) {
221 mf->mf_flags &= ~MFF_RESTART;
222 dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags);
228 quoted = strchr(mf->mf_info, ' ') != 0;
229 plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
233 wasmounted ? "referenced" : "mounted",
234 mf->mf_ops->fs_type, mf->mf_mount);
239 am_mounted(am_node *mp)
241 int notimeout = 0; /* assume normal timeouts initially */
242 mntfs *mf = mp->am_al->al_mnt;
245 * This is the parent mntfs which does the mf->mf_fo (am_opts type), and
246 * we're passing TRUE here to tell mf_mounted to actually free the
247 * am_opts. See a related comment in mf_mounted().
249 mf_mounted(mf, TRUE);
251 #ifdef HAVE_FS_AUTOFS
252 if (mf->mf_flags & MFF_IS_AUTOFS)
254 #endif /* HAVE_FS_AUTOFS */
257 * Patch up path for direct mounts
259 if (mp->am_parent && mp->am_parent->am_al->al_mnt->mf_fsflags & FS_DIRECT)
260 mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
263 * Check whether this mount should be cached permanently or not,
264 * and handle user-requested timeouts.
266 /* first check if file system was set to never timeout */
267 if (mf->mf_fsflags & FS_NOTIMEOUT)
269 /* next, alter that decision by map flags */
273 mnt.mnt_opts = mf->mf_mopts;
275 /* umount option: user wants to unmount this entry */
276 if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount"))
278 /* noumount option: user does NOT want to unmount this entry */
279 if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount"))
281 /* utimeout=N option: user wants to unmount this option AND set timeout */
282 if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
283 mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */
286 /* special case: don't try to unmount "/" (it can never succeed) */
287 if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0')
290 /* finally set actual flags */
292 mp->am_flags |= AMF_NOTIMEOUT;
293 plog(XLOG_INFO, "%s set to never timeout", mp->am_path);
295 mp->am_flags &= ~AMF_NOTIMEOUT;
296 plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo);
300 * If this node is a symlink then
301 * compute the length of the returned string.
303 if (mp->am_fattr.na_type == NFLNK)
304 mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount);
307 * Record mount time, and update am_stats at the same time.
309 mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime);
313 * Update mtime of parent node (copying "struct nfstime" in '=' below)
315 if (mp->am_parent && mp->am_parent->am_al->al_mnt)
316 mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime;
319 * This is ugly, but essentially unavoidable
320 * Sublinks must be treated separately as type==link
321 * when the base type is different.
323 if (mp->am_link && mf->mf_ops != &amfs_link_ops)
324 amfs_link_ops.mount_fs(mp, mf);
327 * Now, if we can, do a reply to our client here
328 * to speed things up.
330 #ifdef HAVE_FS_AUTOFS
331 if (mp->am_flags & AMF_AUTOFS)
332 autofs_mount_succeeded(mp);
334 #endif /* HAVE_FS_AUTOFS */
335 nfs_quick_reply(mp, 0);
345 * Replace mount point with a reference to an error filesystem.
346 * The mount point (struct mntfs) is NOT discarded,
347 * the caller must do it if it wants to _before_ calling this function.
350 assign_error_mntfs(am_node *mp)
353 dlog("assign_error_mntfs");
355 if (mp->am_al == NULL) {
356 plog(XLOG_ERROR, "%s: Can't assign error", __func__);
360 * Save the old error code
362 error = mp->am_error;
364 error = mp->am_al->al_mnt->mf_error;
366 * Allocate a new error reference
369 mp->am_al = new_loc();
371 * Put back the error code
373 mp->am_al->al_mnt->mf_error = error;
374 mp->am_al->al_mnt->mf_flags |= MFF_ERROR;
376 * Zero the error in the mount point
383 * Build a new map cache for this node, or re-use
384 * an existing cache for the same map.
387 amfs_mkcacheref(mntfs *mf)
391 if (mf->mf_fo && mf->mf_fo->opt_cache)
392 cache = mf->mf_fo->opt_cache;
395 mf->mf_private = (opaque_t) mapc_find(mf->mf_info,
397 (mf->mf_fo ? mf->mf_fo->opt_maptype : NULL),
399 mf->mf_prfree = mapc_free;
404 * Locate next node in sibling list which is mounted
405 * and is not an error node.
408 next_nonerror_node(am_node *xp)
413 * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
414 * Fixes a race condition when mounting direct automounts.
415 * Also fixes a problem when doing a readdir on a directory
416 * containing hung automounts.
419 (!(mf = xp->am_al->al_mnt) || /* No mounted filesystem */
420 mf->mf_error != 0 || /* There was a mntfs error */
421 xp->am_error != 0 || /* There was a mount error */
422 !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */
423 (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */
432 * Mount an automounter directory.
433 * The automounter is connected into the system
434 * as a user-level NFS server. amfs_mount constructs
435 * the necessary NFS parameters to be given to the
436 * kernel so that it will talk back to us.
438 * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP).
440 * NEW: on certain systems, mounting can be done using the
441 * kernel-level automount (autofs) support. In that case,
442 * we don't need NFS at all here.
445 amfs_mount(am_node *mp, mntfs *mf, char *opts)
447 char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
448 int retry, error = 0, genflags;
449 int on_autofs = mf->mf_flags & MFF_ON_AUTOFS;
450 char *dir = mf->mf_mount;
453 int forced_unmount = 0; /* are we using forced unmounts? */
454 u_long nfs_version = get_nfs_dispatcher_version(nfs_dispatcher);
456 memset(&mnt, 0, sizeof(mnt));
458 mnt.mnt_fsname = pid_fsname;
461 #ifdef HAVE_FS_AUTOFS
462 if (mf->mf_flags & MFF_IS_AUTOFS) {
463 type = MOUNT_TYPE_AUTOFS;
465 * Make sure that amd's top-level autofs mounts are hidden by default
467 * XXX: It works ok on Linux, might not work on other systems.
469 mnt.mnt_type = "autofs";
471 #endif /* HAVE_FS_AUTOFS */
473 type = MOUNT_TYPE_NFS;
475 * Make sure that amd's top-level NFS mounts are hidden by default
477 * If they don't appear to support the either the "ignore" mnttab
478 * option entry, or the "auto" one, set the mount type to "nfs".
480 mnt.mnt_type = HIDE_MOUNT_TYPE;
483 retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
485 retry = 2; /* XXX: default to 2 retries */
492 * Make a ``hostname'' string for the kernel
494 xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s",
495 get_server_pid(), am_get_hostname(), dir);
497 * Most kernels have a name length restriction (64 bytes)...
499 if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
500 xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..",
501 sizeof(fs_hostname) - MAXHOSTNAMELEN + 3);
504 * ... and some of these restrictions are 32 bytes (HOSTNAMESZ)
505 * If you need to get the definition for HOSTNAMESZ found, you may
506 * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file.
508 if (strlen(fs_hostname) >= HOSTNAMESZ)
509 xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..",
510 sizeof(fs_hostname) - HOSTNAMESZ + 3);
511 #endif /* HOSTNAMESZ */
514 * Finally we can compute the mount genflags set above,
515 * and add any automounter specific flags.
517 genflags = compute_mount_flags(&mnt);
518 #ifdef HAVE_FS_AUTOFS
520 genflags |= autofs_compute_mount_flags(&mnt);
521 #endif /* HAVE_FS_AUTOFS */
522 genflags |= compute_automounter_mount_flags(&mnt);
525 if (!(mf->mf_flags & MFF_IS_AUTOFS)) {
527 am_nfs_handle_t *fhp, anh;
528 #ifndef HAVE_TRANSPORT_TYPE_TLI
530 struct sockaddr_in sin;
531 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
534 * get fhandle of remote path for automount point
536 fhp = get_root_nfs_fh(dir, &anh);
538 plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
542 #ifndef HAVE_TRANSPORT_TYPE_TLI
544 * Create sockaddr to point to the local machine.
546 memset(&sin, 0, sizeof(sin));
547 /* as per POSIX, sin_len need not be set (used internally by kernel) */
548 sin.sin_family = AF_INET;
549 sin.sin_addr = myipaddr;
550 port = hasmntval(&mnt, MNTTAB_OPT_PORT);
552 sin.sin_port = htons(port);
554 plog(XLOG_ERROR, "no port number specified for %s", dir);
557 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
559 /* setup the many fields and flags within nfs_args */
560 #ifdef HAVE_TRANSPORT_TYPE_TLI
561 compute_nfs_args(&nfs_args,
565 NULL, /* remote host IP addr is set below */
572 * IMPORTANT: set the correct IP address AFTERWARDS. It cannot
573 * be done using the normal mechanism of compute_nfs_args(), because
574 * that one will allocate a new address and use NFS_SA_DREF() to copy
575 * parts to it, while assuming that the ip_addr passed is always
576 * a "struct sockaddr_in". That assumption is incorrect on TLI systems,
577 * because they define a special macro HOST_SELF which is DIFFERENT
578 * than localhost (127.0.0.1)!
580 nfs_args.addr = &nfsxprt->xp_ltaddr;
581 #else /* not HAVE_TRANSPORT_TYPE_TLI */
582 compute_nfs_args(&nfs_args,
592 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
594 /*************************************************************************
595 * NOTE: while compute_nfs_args() works ok for regular NFS mounts *
596 * the toplvl one is not quite regular, and so some options must be *
597 * corrected by hand more carefully, *after* compute_nfs_args() runs. *
598 *************************************************************************/
599 compute_automounter_nfs_args(&nfs_args, &mnt);
601 if (amuDebug(D_TRACE)) {
602 print_nfs_args(&nfs_args, 0);
603 plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags);
606 /* This is it! Here we try to mount amd on its mount points */
607 error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args,
608 retry, type, 0, NULL, mnttab_file_name, on_autofs);
610 #ifdef HAVE_TRANSPORT_TYPE_TLI
611 free_knetconfig(nfs_args.knconf);
613 * local automounter mounts do not allocate a special address, so
614 * no need to XFREE(nfs_args.addr) under TLI.
616 #endif /* HAVE_TRANSPORT_TYPE_TLI */
618 #ifdef HAVE_FS_AUTOFS
620 /* This is it! Here we try to mount amd on its mount points */
621 error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh,
622 retry, type, 0, NULL, mnttab_file_name, on_autofs);
623 #endif /* HAVE_FS_AUTOFS */
625 if (error == 0 || forced_unmount)
629 * If user wants forced/lazy unmount semantics, then try it iff the
630 * current mount failed with EIO or ESTALE.
632 if (gopt.flags & CFM_FORCED_UNMOUNTS) {
636 forced_unmount = errno;
637 plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path);
638 if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name,
639 AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) {
640 plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path);
641 errno = forced_unmount;
654 am_unmounted(am_node *mp)
656 mntfs *mf = mp->am_al->al_mnt;
658 if (!foreground) { /* firewall - should never happen */
660 * This is a coding error. Make sure we hear about it!
662 plog(XLOG_FATAL, "am_unmounted: illegal use in background (%s)",
664 notify_child(mp, AMQ_UMNT_OK, 0, 0); /* XXX - be safe? */
669 * Do unmounted callback
671 if (mf->mf_ops->umounted)
672 mf->mf_ops->umounted(mf);
675 * This is ugly, but essentially unavoidable.
676 * Sublinks must be treated separately as type==link
677 * when the base type is different.
679 if (mp->am_link && mf->mf_ops != &amfs_link_ops)
680 amfs_link_ops.umount_fs(mp, mf);
682 #ifdef HAVE_FS_AUTOFS
683 if (mf->mf_flags & MFF_IS_AUTOFS)
684 autofs_release_fh(mp);
685 if (mp->am_flags & AMF_AUTOFS)
686 autofs_umount_succeeded(mp);
687 #endif /* HAVE_FS_AUTOFS */
690 * Clean up any directories that were made
692 * If we remove the mount point of a pending mount, any queued access
693 * to it will fail. So don't do it in that case.
694 * Also don't do it if the refcount is > 1.
696 if (mf->mf_flags & MFF_MKMNT &&
698 !(mp->am_flags & AMF_REMOUNT)) {
699 plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount);
700 rmdirs(mf->mf_mount);
701 mf->mf_flags &= ~MFF_MKMNT;
705 * If this is a pseudo-directory then adjust the link count
708 if (mp->am_parent && mp->am_fattr.na_type == NFDIR)
709 --mp->am_parent->am_fattr.na_nlink;
712 * Update mtime of parent node
714 if (mp->am_parent && mp->am_parent->am_al->al_mnt)
715 clocktime(&mp->am_parent->am_fattr.na_mtime);
717 if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) {
718 char *fname = xstrdup(mp->am_name);
719 am_node *mp_parent = mp->am_parent;
720 mntfs *mf_parent = mp_parent->am_al->al_mnt;
725 * We need to use notify_child() after free_map(), so save enough
726 * to do that in fake_mp.
728 fake_mp.am_fd[1] = mp->am_fd[1];
732 plog(XLOG_INFO, "am_unmounted: remounting %s", fname);
733 mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE);
735 (void)mf_parent->mf_ops->mount_child(mp, &error);
738 plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname);
739 notify_child(&fake_mp, AMQ_UMNT_OK, 0, 0);
741 notify_child(&fake_mp, AMQ_UMNT_FAILED, EBUSY, 0);
746 * We have a race here.
747 * If this node has a pending mount and amd is going down (unmounting
748 * everything in the process), then we could potentially free it here
749 * while a struct continuation still has a reference to it. So when
750 * amfs_cont is called, it blows up.
751 * We avoid the race by refusing to free any nodes that have
752 * pending mounts (defined as having a non-NULL am_alarray).
754 notify_child(mp, AMQ_UMNT_OK, 0, 0); /* do this regardless */
762 * Fork the automounter
764 * TODO: Need a better strategy for handling errors
774 if (pid < 0) { /* fork error, retry in 1 second */
778 if (pid == 0) { /* child process (foreground==false) */
781 } else { /* parent process, has one more child */
795 dlog("backgrounded");
798 dlog("forked process %d", pid);