Blame daemon/indirect.c

Packit 8480eb
/* ----------------------------------------------------------------------- *
Packit 8480eb
 *
Packit 8480eb
 *  indirect.c - Linux automounter indirect mount handling
Packit 8480eb
 *   
Packit 8480eb
 *   Copyright 1997 Transmeta Corporation - All Rights Reserved
Packit 8480eb
 *   Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
Packit 8480eb
 *   Copyright 2001-2005 Ian Kent <raven@themaw.net>
Packit 8480eb
 *
Packit 8480eb
 *   This program is free software; you can redistribute it and/or modify
Packit 8480eb
 *   it under the terms of the GNU General Public License as published by
Packit 8480eb
 *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
Packit 8480eb
 *   USA; either version 2 of the License, or (at your option) any later
Packit 8480eb
 *   version.
Packit 8480eb
 *   
Packit 8480eb
 *   This program is distributed in the hope that it will be useful,
Packit 8480eb
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8480eb
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8480eb
 *   GNU General Public License for more details.
Packit 8480eb
 *
Packit 8480eb
 * ----------------------------------------------------------------------- */
Packit 8480eb
Packit 8480eb
#include <dirent.h>
Packit 8480eb
#include <libgen.h>
Packit 8480eb
#include <signal.h>
Packit 8480eb
#include <stdio.h>
Packit 8480eb
#include <stdlib.h>
Packit 8480eb
#include <string.h>
Packit 8480eb
#include <sys/ioctl.h>
Packit 8480eb
#include <sys/types.h>
Packit 8480eb
#include <sys/wait.h>
Packit 8480eb
#include <sys/stat.h>
Packit 8480eb
#include <sys/time.h>
Packit 8480eb
#include <sys/mount.h>
Packit 8480eb
#include <sys/vfs.h>
Packit 8480eb
#include <sched.h>
Packit 8480eb
Packit 8480eb
#define INCLUDE_PENDING_FUNCTIONS
Packit 8480eb
#include "automount.h"
Packit 8480eb
Packit 8480eb
/* Attribute to create detached thread */
Packit 8480eb
extern pthread_attr_t th_attr_detached;
Packit 8480eb
Packit 8480eb
static int do_mount_autofs_indirect(struct autofs_point *ap, const char *root)
Packit 8480eb
{
Packit 8480eb
	const char *str_indirect = mount_type_str(t_indirect);
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	time_t timeout = get_exp_timeout(ap, ap->entry->maps);
Packit 8480eb
	char *options = NULL;
Packit 8480eb
	const char *hosts_map_name = "-hosts";
Packit 8480eb
	const char *map_name = hosts_map_name;
Packit 8480eb
	const char *type;
Packit 8480eb
	struct stat st;
Packit Service 145c60
	struct mnt_list *mnts;
Packit 8480eb
	int ret;
Packit 8480eb
	int err;
Packit 8480eb
Packit 8480eb
	/* If the map is being shared the exp_timeout can't be inherited
Packit 8480eb
	 * from the map source since it may be different so the autofs
Packit 8480eb
	 * point exp_runfreq must have already been set.
Packit 8480eb
	 */
Packit 8480eb
	if (ap->entry->maps->ref <= 1)
Packit 8480eb
		ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO;
Packit 8480eb
Packit 8480eb
	if (ops->version && !do_force_unlink) {
Packit 8480eb
		ap->flags |= MOUNT_FLAG_REMOUNT;
Packit 8480eb
		ret = try_remount(ap, NULL, t_indirect);
Packit 8480eb
		ap->flags &= ~MOUNT_FLAG_REMOUNT;
Packit 8480eb
		if (ret == 1)
Packit 8480eb
			return 0;
Packit 8480eb
		if (ret == 0)
Packit 8480eb
			return -1;
Packit 8480eb
	} else {
Packit Service 4213a1
		mnts = get_mnt_list(ap->path, 1);
Packit Service 145c60
		if (mnts) {
Packit Service 145c60
			ret = unlink_mount_tree(ap, mnts);
Packit Service 145c60
			free_mnt_list(mnts);
Packit Service 145c60
			if (!ret) {
Packit Service 145c60
				error(ap->logopt,
Packit Service 145c60
				      "already mounted as other than autofs "
Packit Service 145c60
				      "or failed to unlink entry in tree");
Packit Service 145c60
				goto out_err;
Packit Service 145c60
			}
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
Packit Service 145c60
	options = make_options_string(ap->path, ap->kpipefd, str_indirect);
Packit 8480eb
	if (!options) {
Packit 8480eb
		error(ap->logopt, "options string error");
Packit 8480eb
		goto out_err;
Packit 8480eb
	}
Packit 8480eb
Packit Service 870bb4
	if ((ap->flags & MOUNT_FLAG_STRICTEXPIRE) &&
Packit Service 870bb4
	    ((get_kver_major() == 5 && get_kver_minor() > 3) ||
Packit Service 870bb4
	     (get_kver_major() > 5))) {
Packit Service 870bb4
		char *tmp = realloc(options, strlen(options) + 12);
Packit Service 870bb4
		if (tmp) {
Packit Service 870bb4
			strcat(tmp, ",strictexpire");
Packit Service 870bb4
			options = tmp;
Packit Service 870bb4
		}
Packit Service 870bb4
	}
Packit Service 870bb4
Packit Service 5e3179
	if ((ap->flags & MOUNT_FLAG_IGNORE) &&
Packit Service 5e3179
	    ((get_kver_major() == 5 && get_kver_minor() > 4) ||
Packit Service 5e3179
	     (get_kver_major() > 5))) {
Packit Service 5e3179
		char *tmp = realloc(options, strlen(options) + 7);
Packit Service 5e3179
		if (tmp) {
Packit Service 5e3179
			strcat(tmp, ",ignore");
Packit Service 5e3179
			options = tmp;
Packit Service 5e3179
		}
Packit Service 5e3179
	}
Packit Service 5e3179
Packit 8480eb
	/* In case the directory doesn't exist, try to mkdir it */
Packit Service 2798b2
	if (mkdir_path(root, mp_mode) < 0) {
Packit 8480eb
		if (errno != EEXIST && errno != EROFS) {
Packit 8480eb
			crit(ap->logopt,
Packit 8480eb
			     "failed to create autofs directory %s",
Packit 8480eb
			     root);
Packit 8480eb
			goto out_err;
Packit 8480eb
		}
Packit 8480eb
		/* If we recieve an error, and it's EEXIST or EROFS we know
Packit 8480eb
		   the directory was not created. */
Packit 8480eb
		ap->flags &= ~MOUNT_FLAG_DIR_CREATED;
Packit 8480eb
	} else {
Packit 8480eb
		/* No errors so the directory was successfully created */
Packit 8480eb
		ap->flags |= MOUNT_FLAG_DIR_CREATED;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	type = ap->entry->maps->type;
Packit 8480eb
	if (!type || strcmp(ap->entry->maps->type, "hosts"))
Packit 8480eb
		map_name = ap->entry->maps->argv[0];
Packit 8480eb
Packit 8480eb
	ret = mount(map_name, root, "autofs", MS_MGC_VAL, options);
Packit 8480eb
	if (ret) {
Packit 8480eb
		crit(ap->logopt,
Packit 8480eb
		     "failed to mount autofs path %s at %s", ap->path, root);
Packit 8480eb
		goto out_rmdir;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	free(options);
Packit 8480eb
	options = NULL;
Packit 8480eb
Packit 8480eb
	ret = stat(root, &st);
Packit 8480eb
	if (ret == -1) {
Packit 8480eb
		crit(ap->logopt,
Packit 8480eb
		     "failed to stat mount for autofs path %s", ap->path);
Packit 8480eb
		goto out_umount;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	if (ap->mode && (err = chmod(root, ap->mode)))
Packit 8480eb
		warn(ap->logopt, "failed to change mode of %s", ap->path);
Packit 8480eb
Packit 8480eb
	if (ops->open(ap->logopt, &ap->ioctlfd, st.st_dev, root)) {
Packit 8480eb
		crit(ap->logopt,
Packit 8480eb
		     "failed to create ioctl fd for autofs path %s", ap->path);
Packit 8480eb
		goto out_umount;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	ap->dev = st.st_dev;	/* Device number for mount point checks */
Packit 8480eb
Packit 8480eb
	ops->timeout(ap->logopt, ap->ioctlfd, timeout);
Packit 8480eb
	if (ap->logopt & LOGOPT_DEBUG)
Packit 8480eb
		notify_mount_result(ap, root, timeout, str_indirect);
Packit 8480eb
	else
Packit 8480eb
		notify_mount_result(ap, ap->path, timeout, str_indirect);
Packit 8480eb
Packit 8480eb
	return 0;
Packit 8480eb
Packit 8480eb
out_umount:
Packit 8480eb
	umount(root);
Packit 8480eb
out_rmdir:
Packit 8480eb
	if (ap->flags & MOUNT_FLAG_DIR_CREATED)
Packit 8480eb
		rmdir(root);
Packit 8480eb
out_err:
Packit 8480eb
	if (options)
Packit 8480eb
		free(options);
Packit 8480eb
	close(ap->state_pipe[0]);
Packit 8480eb
	close(ap->state_pipe[1]);
Packit 8480eb
	close(ap->pipefd);
Packit 8480eb
	close(ap->kpipefd);
Packit 8480eb
Packit 8480eb
	return -1;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int mount_autofs_indirect(struct autofs_point *ap, const char *root)
Packit 8480eb
{
Packit 8480eb
	time_t now = monotonic_time(NULL);
Packit 8480eb
	int status;
Packit 8480eb
	int map;
Packit 8480eb
Packit 8480eb
	/* TODO: read map, determine map type is OK */
Packit Service 145c60
	if (lookup_nss_read_map(ap, NULL, now))
Packit Service 145c60
		lookup_prune_cache(ap, now);
Packit Service 145c60
	else {
Packit Service 145c60
		error(ap->logopt, "failed to read map for %s", ap->path);
Packit Service 145c60
		return -1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	status = do_mount_autofs_indirect(ap, root);
Packit 8480eb
	if (status < 0)
Packit 8480eb
		return -1;
Packit 8480eb
Packit 8480eb
	map = lookup_ghost(ap, root);
Packit 8480eb
	if (map & LKP_FAIL) {
Packit 8480eb
		if (map & LKP_DIRECT) {
Packit 8480eb
			error(ap->logopt,
Packit 8480eb
			      "bad map format,found direct, "
Packit 8480eb
			      "expected indirect exiting");
Packit 8480eb
		} else {
Packit 8480eb
			error(ap->logopt, "failed to load map, exiting");
Packit 8480eb
		}
Packit 8480eb
		/* TODO: Process cleanup ?? */
Packit 8480eb
		return -1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	if (map & LKP_NOTSUP)
Packit 8480eb
		ap->flags &= ~MOUNT_FLAG_GHOST;
Packit 8480eb
Packit 8480eb
	return 0;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
void close_mount_fds(struct autofs_point *ap)
Packit 8480eb
{
Packit 8480eb
	/*
Packit 8480eb
	 * Since submounts look after themselves the parent never knows
Packit 8480eb
	 * it needs to close the ioctlfd for offset mounts so we have
Packit 8480eb
	 * to do it here. If the cache entry isn't found then there aren't
Packit 8480eb
	 * any offset mounts.
Packit 8480eb
	 */
Packit 8480eb
	if (ap->submount)
Packit 8480eb
		lookup_source_close_ioctlfd(ap->parent, ap->path);
Packit 8480eb
Packit 8480eb
	close(ap->state_pipe[0]);
Packit 8480eb
	close(ap->state_pipe[1]);
Packit 8480eb
	ap->state_pipe[0] = -1;
Packit 8480eb
	ap->state_pipe[1] = -1;
Packit 8480eb
Packit 8480eb
	if (ap->pipefd >= 0)
Packit 8480eb
		close(ap->pipefd);
Packit 8480eb
Packit 8480eb
	if (ap->kpipefd >= 0)
Packit 8480eb
		close(ap->kpipefd);
Packit 8480eb
Packit 8480eb
	return;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int umount_autofs_indirect(struct autofs_point *ap, const char *root)
Packit 8480eb
{
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	char buf[MAX_ERR_BUF];
Packit 8480eb
	char mountpoint[PATH_MAX + 1];
Packit 8480eb
	int rv, retries;
Packit 8480eb
	unsigned int unused;
Packit 8480eb
Packit 8480eb
	if (root)
Packit 8480eb
		strcpy(mountpoint, root);
Packit 8480eb
	else
Packit 8480eb
		strcpy(mountpoint, ap->path);
Packit 8480eb
Packit 8480eb
	/* If we are trying to shutdown make sure we can umount */
Packit 8480eb
	rv = ops->askumount(ap->logopt, ap->ioctlfd, &unused);
Packit 8480eb
	if (rv == -1) {
Packit 8480eb
		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit 8480eb
		logerr("ioctl failed: %s", estr);
Packit 8480eb
		return 1;
Packit 8480eb
	} else if (!unused) {
Packit 8480eb
#if defined(ENABLE_IGNORE_BUSY_MOUNTS) || defined(ENABLE_FORCED_SHUTDOWN)
Packit 8480eb
		if (!ap->shutdown)
Packit 8480eb
			return 1;
Packit 8480eb
		error(ap->logopt, "ask umount returned busy %s", ap->path);
Packit 8480eb
#else
Packit 8480eb
		return 1;
Packit 8480eb
#endif
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	ops->close(ap->logopt, ap->ioctlfd);
Packit 8480eb
	ap->ioctlfd = -1;
Packit 8480eb
	sched_yield();
Packit 8480eb
Packit 8480eb
	retries = UMOUNT_RETRIES;
Packit 8480eb
	while ((rv = umount(mountpoint)) == -1 && retries--) {
Packit 8480eb
		struct timespec tm = {0, 200000000};
Packit 8480eb
		if (errno != EBUSY)
Packit 8480eb
			break;
Packit 8480eb
		nanosleep(&tm, NULL);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	if (rv == -1) {
Packit 8480eb
		switch (errno) {
Packit 8480eb
		case ENOENT:
Packit 8480eb
		case EINVAL:
Packit 8480eb
			error(ap->logopt,
Packit 8480eb
			      "mount point %s does not exist", mountpoint);
Packit 8480eb
			close_mount_fds(ap);
Packit 8480eb
			return 0;
Packit 8480eb
			break;
Packit 8480eb
		case EBUSY:
Packit 8480eb
			debug(ap->logopt,
Packit 8480eb
			      "mount point %s is in use", mountpoint);
Packit 8480eb
			if (ap->state == ST_SHUTDOWN_FORCE) {
Packit 8480eb
				close_mount_fds(ap);
Packit 8480eb
				goto force_umount;
Packit 8480eb
			} else {
Packit 8480eb
				/*
Packit 8480eb
				 * If the umount returns EBUSY there may be
Packit 8480eb
				 * a mount request in progress so we need to
Packit 8480eb
				 * recover unless we have been explicitly
Packit 8480eb
				 * asked to shutdown and configure option
Packit 8480eb
				 * ENABLE_IGNORE_BUSY_MOUNTS is enabled.
Packit 8480eb
				 */
Packit 8480eb
#ifdef ENABLE_IGNORE_BUSY_MOUNTS
Packit 8480eb
				if (ap->shutdown) {
Packit 8480eb
					close_mount_fds(ap);
Packit 8480eb
					return 0;
Packit 8480eb
				}
Packit 8480eb
#endif
Packit 8480eb
				ops->open(ap->logopt,
Packit 8480eb
					  &ap->ioctlfd, ap->dev, mountpoint);
Packit 8480eb
				if (ap->ioctlfd < 0) {
Packit 8480eb
					warn(ap->logopt,
Packit 8480eb
					     "could not recover autofs path %s",
Packit 8480eb
					     mountpoint);
Packit 8480eb
					close_mount_fds(ap);
Packit 8480eb
					return 0;
Packit 8480eb
				}
Packit 8480eb
			}
Packit 8480eb
			break;
Packit 8480eb
		case ENOTDIR:
Packit 8480eb
			error(ap->logopt, "mount point is not a directory");
Packit 8480eb
			close_mount_fds(ap);
Packit 8480eb
			return 0;
Packit 8480eb
			break;
Packit 8480eb
		}
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	/*
Packit 8480eb
	 * We have successfully umounted the mount so we now close
Packit 8480eb
	 * the descriptors. The kernel end of the kernel pipe will
Packit 8480eb
	 * have been put during the umount super block cleanup.
Packit 8480eb
	 */
Packit 8480eb
	close_mount_fds(ap);
Packit 8480eb
Packit 8480eb
force_umount:
Packit 8480eb
	if (rv != 0) {
Packit 8480eb
		warn(ap->logopt,
Packit 8480eb
		     "forcing umount of indirect mount %s", mountpoint);
Packit 8480eb
		rv = umount2(mountpoint, MNT_DETACH);
Packit 8480eb
	} else {
Packit 8480eb
		info(ap->logopt, "umounted indirect mount %s", mountpoint);
Packit 8480eb
		if (ap->submount)
Packit 8480eb
			rm_unwanted(ap, mountpoint, 1);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	return rv;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static void mnts_cleanup(void *arg)
Packit 8480eb
{
Packit 8480eb
	struct mnt_list *mnts = (struct mnt_list *) arg;
Packit 8480eb
	free_mnt_list(mnts);
Packit 8480eb
	return;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
void *expire_proc_indirect(void *arg)
Packit 8480eb
{
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	struct autofs_point *ap;
Packit 8480eb
	struct mapent *me = NULL;
Packit 8480eb
	struct mnt_list *mnts = NULL, *next;
Packit 8480eb
	struct expire_args *ea;
Packit 8480eb
	struct expire_args ec;
Packit Service dce2e8
	unsigned int how;
Packit 8480eb
	int offsets, submnts, count;
Packit 8480eb
	int retries;
Packit 8480eb
	int ioctlfd, cur_state;
Packit 8480eb
	int status, ret, left;
Packit 8480eb
Packit 8480eb
	ea = (struct expire_args *) arg;
Packit 8480eb
Packit 8480eb
	status = pthread_mutex_lock(&ea->mutex);
Packit 8480eb
	if (status)
Packit 8480eb
		fatal(status);
Packit 8480eb
Packit 8480eb
	ap = ec.ap = ea->ap;
Packit Service dce2e8
	how = ea->how;
Packit 8480eb
	ec.status = -1;
Packit 8480eb
Packit 8480eb
	ea->signaled = 1;
Packit 8480eb
	status = pthread_cond_signal(&ea->cond);
Packit 8480eb
	if (status)
Packit 8480eb
		fatal(status);
Packit 8480eb
Packit 8480eb
	status = pthread_mutex_unlock(&ea->mutex);
Packit 8480eb
	if (status)
Packit 8480eb
		fatal(status);
Packit 8480eb
Packit 8480eb
	pthread_cleanup_push(expire_cleanup, &ec);
Packit 8480eb
Packit 8480eb
	left = 0;
Packit 8480eb
Packit 8480eb
	/* Get a list of real mounts and expire them if possible */
Packit Service 4213a1
	mnts = get_mnt_list(ap->path, 0);
Packit 8480eb
	pthread_cleanup_push(mnts_cleanup, mnts);
Packit 8480eb
	for (next = mnts; next; next = next->next) {
Packit 8480eb
		char *ind_key;
Packit 8480eb
		int ret;
Packit 8480eb
Packit Service 97a3c9
		if (next->flags & MNTS_AUTOFS) {
Packit 8480eb
			/*
Packit 8480eb
			 * If we have submounts check if this path lives below
Packit 8480eb
			 * one of them and pass on the state change.
Packit 8480eb
			 */
Packit 8480eb
			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
Packit Service 97a3c9
			if (next->flags & MNTS_INDIRECT)
Packit Service 1d8123
				master_notify_submount(ap, next->mp, ap->state);
Packit Service 97a3c9
			else if (next->flags & MNTS_OFFSET) {
Packit 8480eb
				struct map_source *map;
Packit 8480eb
				struct mapent_cache *mc = NULL;
Packit 8480eb
				struct mapent *me = NULL;
Packit 8480eb
				struct stat st;
Packit 8480eb
Packit 8480eb
				/* It's got a mount, deal with in the outer loop */
Packit Service 4213a1
				if (is_mounted(next->mp, MNTS_REAL)) {
Packit 8480eb
					pthread_setcancelstate(cur_state, NULL);
Packit 8480eb
					continue;
Packit 8480eb
				}
Packit 8480eb
Packit 8480eb
				/* Don't touch submounts */
Packit Service 1d8123
				if (master_find_submount(ap, next->mp)) {
Packit 8480eb
					pthread_setcancelstate(cur_state, NULL);
Packit 8480eb
					continue;
Packit 8480eb
				}
Packit 8480eb
Packit 8480eb
				master_source_writelock(ap->entry);
Packit 8480eb
Packit 8480eb
				map = ap->entry->maps;
Packit 8480eb
				while (map) {
Packit 8480eb
					mc = map->mc;
Packit 8480eb
					cache_writelock(mc);
Packit Service 1d8123
					me = cache_lookup_distinct(mc, next->mp);
Packit 8480eb
					if (me)
Packit 8480eb
						break;
Packit 8480eb
					cache_unlock(mc);
Packit 8480eb
					map = map->next;
Packit 8480eb
				}
Packit 8480eb
Packit 8480eb
				if (!mc || !me) {
Packit 8480eb
					master_source_unlock(ap->entry);
Packit 8480eb
					pthread_setcancelstate(cur_state, NULL);
Packit 8480eb
					continue;
Packit 8480eb
				}
Packit 8480eb
Packit 8480eb
				if (me->ioctlfd == -1) {
Packit 8480eb
					cache_unlock(mc);
Packit 8480eb
					master_source_unlock(ap->entry);
Packit 8480eb
					pthread_setcancelstate(cur_state, NULL);
Packit 8480eb
					continue;
Packit 8480eb
				}
Packit 8480eb
Packit 8480eb
				/* Check for manual umount */
Packit 8480eb
				if (fstat(me->ioctlfd, &st) == -1 ||
Packit 8480eb
				    !count_mounts(ap, me->key, st.st_dev)) {
Packit 8480eb
					ops->close(ap->logopt, me->ioctlfd);
Packit 8480eb
					me->ioctlfd = -1;
Packit 8480eb
				}
Packit 8480eb
Packit 8480eb
				cache_unlock(mc);
Packit 8480eb
				master_source_unlock(ap->entry);
Packit 8480eb
			}
Packit 8480eb
Packit 8480eb
			pthread_setcancelstate(cur_state, NULL);
Packit 8480eb
			continue;
Packit 8480eb
		}
Packit 8480eb
Packit 8480eb
		if (ap->state == ST_EXPIRE || ap->state == ST_PRUNE)
Packit 8480eb
			pthread_testcancel();
Packit 8480eb
Packit 8480eb
		/*
Packit 8480eb
		 * If the mount corresponds to an offset trigger then
Packit 8480eb
		 * the key is the path, otherwise it's the last component.
Packit 8480eb
		 */
Packit Service 1d8123
		ind_key = strrchr(next->mp, '/');
Packit 8480eb
		if (ind_key)
Packit 8480eb
			ind_key++;
Packit 8480eb
Packit 8480eb
		/*
Packit 8480eb
		 * If me->key starts with a '/' and it's not an autofs
Packit 8480eb
		 * filesystem it's a nested mount and we need to use
Packit 8480eb
		 * the ioctlfd of the mount to send the expire.
Packit 8480eb
		 * Otherwise it's a top level indirect mount (possibly
Packit 8480eb
		 * with offsets in it) and we use the usual ioctlfd.
Packit 8480eb
		 */
Packit 8480eb
		pthread_cleanup_push(master_source_lock_cleanup, ap->entry);
Packit 8480eb
		master_source_readlock(ap->entry);
Packit Service 1d8123
		me = lookup_source_mapent(ap, next->mp, LKP_DISTINCT);
Packit 8480eb
		if (!me && ind_key)
Packit 8480eb
			me = lookup_source_mapent(ap, ind_key, LKP_NORMAL);
Packit 8480eb
		pthread_cleanup_pop(1);
Packit 8480eb
Packit 8480eb
		ioctlfd = ap->ioctlfd;
Packit 8480eb
		if (me) {
Packit 8480eb
			if (*me->key == '/')
Packit 8480eb
				ioctlfd = me->ioctlfd;
Packit 8480eb
			cache_unlock(me->mc);
Packit 8480eb
		}
Packit 8480eb
Packit Service 1d8123
		debug(ap->logopt, "expire %s", next->mp);
Packit 8480eb
Packit 8480eb
		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
Packit Service 1d8123
		ret = ops->expire(ap->logopt, ioctlfd, next->mp, how);
Packit 8480eb
		if (ret)
Packit 8480eb
			left++;
Packit 8480eb
		pthread_setcancelstate(cur_state, NULL);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	/*
Packit 8480eb
	 * If there are no more real mounts left we could still
Packit 8480eb
	 * have some offset mounts with no '/' offset or symlinks
Packit 8480eb
	 * so we need to umount or unlink them here.
Packit 8480eb
	 */
Packit 8480eb
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
Packit 8480eb
	retries = (count_mounts(ap, ap->path, ap->dev) + 1);
Packit 8480eb
	while (retries--) {
Packit Service dce2e8
		ret = ops->expire(ap->logopt, ap->ioctlfd, ap->path, how);
Packit 8480eb
		if (ret)
Packit 8480eb
			left++;
Packit 8480eb
	}
Packit 8480eb
	pthread_setcancelstate(cur_state, NULL);
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
Packit 8480eb
	count = offsets = submnts = 0;
Packit Service 4213a1
	mnts = get_mnt_list(ap->path, 0);
Packit 8480eb
	pthread_cleanup_push(mnts_cleanup, mnts);
Packit 8480eb
	/* Are there any real mounts left */
Packit 8480eb
	for (next = mnts; next; next = next->next) {
Packit Service 97a3c9
		if (!(next->flags & MNTS_AUTOFS))
Packit 8480eb
			count++;
Packit 8480eb
		else {
Packit Service 97a3c9
			if (next->flags & MNTS_INDIRECT)
Packit 8480eb
				submnts++;
Packit 8480eb
			else
Packit 8480eb
				offsets++;
Packit 8480eb
		}
Packit 8480eb
	}
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
Packit 8480eb
	if (submnts)
Packit Service e65602
		debug(ap->logopt,
Packit 8480eb
		     "%d submounts remaining in %s", submnts, ap->path);
Packit 8480eb
Packit 8480eb
	/* 
Packit 8480eb
	 * EXPIRE_MULTI is synchronous, so we can be sure (famous last
Packit 8480eb
	 * words) the umounts are done by the time we reach here
Packit 8480eb
	 */
Packit 8480eb
	if (count)
Packit Service e65602
		debug(ap->logopt, "%d remaining in %s", count, ap->path);
Packit 8480eb
Packit 8480eb
	ec.status = left;
Packit 8480eb
Packit 8480eb
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
	pthread_setcancelstate(cur_state, NULL);
Packit 8480eb
Packit 8480eb
	return NULL;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static void expire_send_fail(void *arg)
Packit 8480eb
{
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	struct pending_args *mt = arg;
Packit 8480eb
	struct autofs_point *ap = mt->ap;
Packit 8480eb
	ops->send_fail(ap->logopt,
Packit 8480eb
		       ap->ioctlfd, mt->wait_queue_token, -ENOENT);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static void *do_expire_indirect(void *arg)
Packit 8480eb
{
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	struct pending_args *args, mt;
Packit 8480eb
	struct autofs_point *ap;
Packit 8480eb
	int status, state;
Packit 8480eb
Packit 8480eb
	args = (struct pending_args *) arg;
Packit 8480eb
Packit 8480eb
	pending_mutex_lock(args);
Packit 8480eb
Packit 8480eb
	memcpy(&mt, args, sizeof(struct pending_args));
Packit 8480eb
Packit 8480eb
	ap = mt.ap;
Packit 8480eb
Packit 8480eb
	args->signaled = 1;
Packit 8480eb
	status = pthread_cond_signal(&args->cond);
Packit 8480eb
	if (status)
Packit 8480eb
		fatal(status);
Packit 8480eb
Packit 8480eb
	pending_mutex_unlock(args);
Packit 8480eb
Packit 8480eb
	pthread_cleanup_push(expire_send_fail, &mt;;
Packit 8480eb
Packit 8480eb
	status = do_expire(mt.ap, mt.name, mt.len);
Packit 8480eb
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
Packit 8480eb
	if (status)
Packit 8480eb
		ops->send_fail(ap->logopt,
Packit 8480eb
			       ap->ioctlfd, mt.wait_queue_token, -status);
Packit 8480eb
	else
Packit 8480eb
		ops->send_ready(ap->logopt,
Packit 8480eb
				ap->ioctlfd, mt.wait_queue_token);
Packit 8480eb
	pthread_setcancelstate(state, NULL);
Packit 8480eb
Packit 8480eb
	pthread_cleanup_pop(0);
Packit 8480eb
Packit 8480eb
	return NULL;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int handle_packet_expire_indirect(struct autofs_point *ap, autofs_packet_expire_indirect_t *pkt)
Packit 8480eb
{
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	struct pending_args *mt;
Packit 8480eb
	char buf[MAX_ERR_BUF];
Packit 8480eb
	pthread_t thid;
Packit 8480eb
	struct timespec wait;
Packit 8480eb
	int status, state;
Packit 8480eb
Packit 8480eb
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
Packit 8480eb
Packit 8480eb
	debug(ap->logopt, "token %ld, name %s",
Packit 8480eb
		  (unsigned long) pkt->wait_queue_token, pkt->name);
Packit 8480eb
Packit 8480eb
	mt = malloc(sizeof(struct pending_args));
Packit 8480eb
	if (!mt) {
Packit 8480eb
		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit 8480eb
		logerr("malloc: %s", estr);
Packit 8480eb
		ops->send_fail(ap->logopt,
Packit 8480eb
			       ap->ioctlfd, pkt->wait_queue_token, -ENOMEM);
Packit 8480eb
		pthread_setcancelstate(state, NULL);
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	pending_cond_init(mt);
Packit 8480eb
Packit 8480eb
	status = pthread_mutex_init(&mt->mutex, NULL);
Packit 8480eb
	if (status)
Packit 8480eb
		fatal(status);
Packit 8480eb
Packit 8480eb
	mt->ap = ap;
Packit 8480eb
	strncpy(mt->name, pkt->name, pkt->len);
Packit 8480eb
	mt->name[pkt->len] = '\0';
Packit 8480eb
	mt->len = pkt->len;
Packit 8480eb
	mt->wait_queue_token = pkt->wait_queue_token;
Packit 8480eb
Packit 8480eb
	pending_mutex_lock(mt);
Packit 8480eb
Packit 8480eb
	status = pthread_create(&thid, &th_attr_detached, do_expire_indirect, mt);
Packit 8480eb
	if (status) {
Packit 8480eb
		error(ap->logopt, "expire thread create failed");
Packit 8480eb
		ops->send_fail(ap->logopt,
Packit 8480eb
			       ap->ioctlfd, pkt->wait_queue_token, -status);
Packit 8480eb
		pending_mutex_unlock(mt);
Packit 8480eb
		pending_cond_destroy(mt);
Packit 8480eb
		pending_mutex_destroy(mt);
Packit 8480eb
		free_pending_args(mt);
Packit 8480eb
		pthread_setcancelstate(state, NULL);
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	pthread_cleanup_push(free_pending_args, mt);
Packit 8480eb
	pthread_cleanup_push(pending_mutex_destroy, mt);
Packit 8480eb
	pthread_cleanup_push(pending_cond_destroy, mt);
Packit 8480eb
	pthread_cleanup_push(pending_mutex_unlock, mt);
Packit 8480eb
	pthread_setcancelstate(state, NULL);
Packit 8480eb
Packit 8480eb
	mt->signaled = 0;
Packit 8480eb
	while (!mt->signaled) {
Packit 8480eb
		clock_gettime(CLOCK_MONOTONIC, &wait);
Packit 8480eb
		wait.tv_sec += 2;
Packit 8480eb
		status = pthread_cond_timedwait(&mt->cond, &mt->mutex, &wait);
Packit 8480eb
		if (status && status != ETIMEDOUT)
Packit 8480eb
			fatal(status);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
Packit 8480eb
	return 0;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static void mount_send_fail(void *arg)
Packit 8480eb
{
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	struct pending_args *mt = arg;
Packit 8480eb
	struct autofs_point *ap = mt->ap;
Packit 8480eb
	ops->send_fail(ap->logopt,
Packit 8480eb
		       ap->ioctlfd, mt->wait_queue_token, -ENOENT);
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
static void *do_mount_indirect(void *arg)
Packit 8480eb
{
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	struct pending_args *args, mt;
Packit 8480eb
	struct autofs_point *ap;
Packit 8480eb
	char buf[PATH_MAX + 1];
Packit 8480eb
	struct stat st;
Packit 8480eb
	int len, status, state;
Packit 8480eb
Packit 8480eb
	args = (struct pending_args *) arg;
Packit 8480eb
Packit 8480eb
	pending_mutex_lock(args);
Packit 8480eb
Packit 8480eb
	memcpy(&mt, args, sizeof(struct pending_args));
Packit 8480eb
Packit 8480eb
	ap = mt.ap;
Packit 8480eb
Packit 8480eb
	set_thread_mount_request_log_id(&mt;;
Packit 8480eb
Packit 8480eb
	args->signaled = 1;
Packit 8480eb
	status = pthread_cond_signal(&args->cond);
Packit 8480eb
	if (status)
Packit 8480eb
		fatal(status);
Packit 8480eb
Packit 8480eb
	pending_mutex_unlock(args);
Packit 8480eb
Packit 8480eb
	pthread_cleanup_push(mount_send_fail, &mt;;
Packit 8480eb
Packit 8480eb
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
Packit 8480eb
Packit Service 35697a
	if (defaults_get_mount_verbose()) {
Packit Service 35697a
		pid_t ppid = log_pidinfo(ap, mt.pid, "requestor");
Packit Service 35697a
		if (ppid > 0)
Packit Service 35697a
			log_pidinfo(ap, ppid, "parent");
Packit Service 35697a
	}
Packit Service 35697a
Packit 8480eb
	len = ncat_path(buf, sizeof(buf), ap->path, mt.name, mt.len);
Packit 8480eb
	if (!len) {
Packit 8480eb
		crit(ap->logopt, "path to be mounted is to long");
Packit 8480eb
		ops->send_fail(ap->logopt,
Packit 8480eb
			       ap->ioctlfd, mt.wait_queue_token,
Packit 8480eb
			      -ENAMETOOLONG);
Packit 8480eb
		pthread_setcancelstate(state, NULL);
Packit 8480eb
		pthread_exit(NULL);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	status = lstat(buf, &st);
Packit 8480eb
	if (status != -1 && !(S_ISDIR(st.st_mode) && st.st_dev == mt.dev)) {
Packit 8480eb
		error(ap->logopt,
Packit 8480eb
		      "indirect trigger not valid or already mounted %s", buf);
Packit 8480eb
		ops->send_ready(ap->logopt, ap->ioctlfd, mt.wait_queue_token);
Packit 8480eb
		pthread_setcancelstate(state, NULL);
Packit 8480eb
		pthread_exit(NULL);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	pthread_setcancelstate(state, NULL);
Packit 8480eb
Packit 8480eb
	info(ap->logopt, "attempting to mount entry %s", buf);
Packit 8480eb
Packit 8480eb
	set_tsd_user_vars(ap->logopt, mt.uid, mt.gid);
Packit 8480eb
Packit 8480eb
	status = lookup_nss_mount(ap, NULL, mt.name, mt.len);
Packit 8480eb
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
Packit 8480eb
	if (status) {
Packit 8480eb
		ops->send_ready(ap->logopt,
Packit 8480eb
				ap->ioctlfd, mt.wait_queue_token);
Packit 8480eb
		info(ap->logopt, "mounted %s", buf);
Packit 8480eb
	} else {
Packit 8480eb
		/* TODO: get mount return status from lookup_nss_mount */
Packit 8480eb
		ops->send_fail(ap->logopt,
Packit 8480eb
			       ap->ioctlfd, mt.wait_queue_token, -ENOENT);
Packit 8480eb
		info(ap->logopt, "failed to mount %s", buf);
Packit 8480eb
	}
Packit 8480eb
	pthread_setcancelstate(state, NULL);
Packit 8480eb
Packit 8480eb
	pthread_cleanup_pop(0);
Packit 8480eb
Packit 8480eb
	return NULL;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missing_indirect_t *pkt)
Packit 8480eb
{
Packit 8480eb
	struct ioctl_ops *ops = get_ioctl_ops();
Packit 8480eb
	pthread_t thid;
Packit 8480eb
	char buf[MAX_ERR_BUF];
Packit 8480eb
	struct pending_args *mt;
Packit 8480eb
	struct timespec wait;
Packit 8480eb
	struct mapent *me;
Packit 8480eb
	int status, state;
Packit 8480eb
Packit 8480eb
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
Packit 8480eb
Packit 8480eb
	master_mutex_lock();
Packit 8480eb
Packit 8480eb
	debug(ap->logopt, "token %ld, name %s, request pid %u",
Packit 8480eb
		(unsigned long) pkt->wait_queue_token, pkt->name, pkt->pid);
Packit 8480eb
Packit 8480eb
	/* Ignore packet if we're trying to shut down */
Packit 8480eb
	if (ap->shutdown || ap->state == ST_SHUTDOWN_FORCE) {
Packit 8480eb
		ops->send_fail(ap->logopt,
Packit 8480eb
			       ap->ioctlfd, pkt->wait_queue_token, -ENOENT);
Packit 8480eb
		master_mutex_unlock();
Packit 8480eb
		pthread_setcancelstate(state, NULL);
Packit 8480eb
		return 0;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	/* Check if we recorded a mount fail for this key anywhere */
Packit 8480eb
	me = lookup_source_mapent(ap, pkt->name, LKP_DISTINCT);
Packit 8480eb
	if (me) {
Packit 8480eb
		if (me->status >= monotonic_time(NULL)) {
Packit 8480eb
			ops->send_fail(ap->logopt, ap->ioctlfd,
Packit 8480eb
				       pkt->wait_queue_token, -ENOENT);
Packit 8480eb
			cache_unlock(me->mc);
Packit 8480eb
			master_mutex_unlock();
Packit 8480eb
			pthread_setcancelstate(state, NULL);
Packit 8480eb
			return 0;
Packit 8480eb
		}
Packit 8480eb
		cache_unlock(me->mc);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	mt = malloc(sizeof(struct pending_args));
Packit 8480eb
	if (!mt) {
Packit 8480eb
		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit 8480eb
		logerr("malloc: %s", estr);
Packit 8480eb
		ops->send_fail(ap->logopt,
Packit 8480eb
			       ap->ioctlfd, pkt->wait_queue_token, -ENOMEM);
Packit 8480eb
		master_mutex_unlock();
Packit 8480eb
		pthread_setcancelstate(state, NULL);
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
	memset(mt, 0, sizeof(struct pending_args));
Packit 8480eb
Packit 8480eb
	pending_cond_init(mt);
Packit 8480eb
Packit 8480eb
	status = pthread_mutex_init(&mt->mutex, NULL);
Packit 8480eb
	if (status)
Packit 8480eb
		fatal(status);
Packit 8480eb
Packit 8480eb
	pending_mutex_lock(mt);
Packit 8480eb
Packit 8480eb
	mt->ap = ap;
Packit 8480eb
	strncpy(mt->name, pkt->name, pkt->len);
Packit 8480eb
	mt->name[pkt->len] = '\0';
Packit 8480eb
	mt->len = pkt->len;
Packit 8480eb
	mt->dev = pkt->dev;
Packit 8480eb
	mt->uid = pkt->uid;
Packit 8480eb
	mt->gid = pkt->gid;
Packit 8480eb
	mt->pid = pkt->pid;
Packit 8480eb
	mt->wait_queue_token = pkt->wait_queue_token;
Packit 8480eb
Packit 8480eb
	status = pthread_create(&thid, &th_attr_detached, do_mount_indirect, mt);
Packit 8480eb
	if (status) {
Packit 8480eb
		error(ap->logopt, "expire thread create failed");
Packit 8480eb
		ops->send_fail(ap->logopt,
Packit 8480eb
			       ap->ioctlfd, pkt->wait_queue_token, -status);
Packit 8480eb
		master_mutex_unlock();
Packit 8480eb
		pending_mutex_unlock(mt);
Packit 8480eb
		pending_cond_destroy(mt);
Packit 8480eb
		pending_mutex_destroy(mt);
Packit 8480eb
		free_pending_args(mt);
Packit 8480eb
		pthread_setcancelstate(state, NULL);
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	master_mutex_unlock();
Packit 8480eb
Packit 8480eb
	pthread_cleanup_push(free_pending_args, mt);
Packit 8480eb
	pthread_cleanup_push(pending_mutex_destroy, mt);
Packit 8480eb
	pthread_cleanup_push(pending_cond_destroy, mt);
Packit 8480eb
	pthread_cleanup_push(pending_mutex_unlock, mt);
Packit 8480eb
	pthread_setcancelstate(state, NULL);
Packit 8480eb
Packit 8480eb
	mt->signaled = 0;
Packit 8480eb
	while (!mt->signaled) {
Packit 8480eb
		clock_gettime(CLOCK_MONOTONIC, &wait);
Packit 8480eb
		wait.tv_sec += 2;
Packit 8480eb
		status = pthread_cond_timedwait(&mt->cond, &mt->mutex, &wait);
Packit 8480eb
		if (status && status != ETIMEDOUT)
Packit 8480eb
			fatal(status);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
	pthread_cleanup_pop(1);
Packit 8480eb
Packit 8480eb
	return 0;
Packit 8480eb
}
Packit 8480eb