Blob Blame History Raw
diff -up autofs-5.0.3/daemon/indirect.c.device-node-control autofs-5.0.3/daemon/indirect.c
--- autofs-5.0.3/daemon/indirect.c.device-node-control	2008-02-25 08:58:46.000000000 +0900
+++ autofs-5.0.3/daemon/indirect.c	2008-02-25 09:03:12.000000000 +0900
@@ -43,6 +43,8 @@ extern pthread_attr_t thread_attr;
 static pthread_mutex_t ma_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t ea_mutex = PTHREAD_MUTEX_INITIALIZER;
 
+static const unsigned int indirect = AUTOFS_TYPE_INDIRECT;
+
 static int unlink_mount_tree(struct autofs_point *ap, struct mnt_list *mnts)
 {
 	struct mnt_list *this;
@@ -88,19 +90,20 @@ static int unlink_mount_tree(struct auto
 
 static int do_mount_autofs_indirect(struct autofs_point *ap)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	time_t timeout = ap->exp_timeout;
 	char *options = NULL;
 	const char *type, *map_name = NULL;
 	struct stat st;
 	struct mnt_list *mnts;
-	int cl_flags, ret;
+	int ret;
 
 	mnts = get_mnt_list(_PROC_MOUNTS, ap->path, 1);
 	if (mnts) {
 		ret = unlink_mount_tree(ap, mnts);
 		free_mnt_list(mnts);
 		if (!ret) {
-			debug(ap->logopt,
+			error(ap->logopt,
 			      "already mounted as other than autofs "
 			      "or failed to unlink entry in tree");
 			goto out_err;
@@ -108,8 +111,10 @@ static int do_mount_autofs_indirect(stru
 	}
 
 	options = make_options_string(ap->path, ap->kpipefd, NULL);
-	if (!options)
+	if (!options) {
+		error(ap->logopt, "options string error");
 		goto out_err;
+	}
 
 	/* In case the directory doesn't exist, try to mkdir it */
 	if (mkdir_path(ap->path, 0555) < 0) {
@@ -147,22 +152,15 @@ static int do_mount_autofs_indirect(stru
 
 	options = NULL;
 
-	/* Root directory for ioctl()'s */
-	ap->ioctlfd = open(ap->path, O_RDONLY);
-	if (ap->ioctlfd < 0) {
+	if (ops->open(ap->logopt, &ap->ioctlfd, -1, ap->path, indirect)) {
 		crit(ap->logopt,
 		     "failed to create ioctl fd for autofs path %s", ap->path);
 		goto out_umount;
 	}
 
-	if ((cl_flags = fcntl(ap->ioctlfd, F_GETFD, 0)) != -1) {
-		cl_flags |= FD_CLOEXEC;
-		fcntl(ap->ioctlfd, F_SETFD, cl_flags);
-	}
-
 	ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO;
 
-	ioctl(ap->ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout);
+	ops->timeout(ap->logopt, ap->ioctlfd, &timeout);
 
 	if (ap->exp_timeout)
 		info(ap->logopt,
@@ -235,8 +233,10 @@ int mount_autofs_indirect(struct autofs_
 
 int umount_autofs_indirect(struct autofs_point *ap)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	char buf[MAX_ERR_BUF];
-	int ret, rv, retries;
+	int rv, retries;
+	unsigned int ret;
 
 	/*
 	 * Since submounts look after themselves the parent never knows
@@ -248,7 +248,7 @@ int umount_autofs_indirect(struct autofs
 		lookup_source_close_ioctlfd(ap->parent, ap->path);
 
 	/* If we are trying to shutdown make sure we can umount */
-	rv = ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &ret);
+	rv = ops->askumount(ap->logopt, ap->ioctlfd, &ret);
 	if (rv == -1) {
 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 		logerr("ioctl failed: %s", estr);
@@ -258,8 +258,8 @@ int umount_autofs_indirect(struct autofs
 		return 1;
 	}
 
-	ioctl(ap->ioctlfd, AUTOFS_IOC_CATATONIC, 0);
-	close(ap->ioctlfd);
+	ops->catatonic(ap->logopt, ap->ioctlfd);
+	ops->close(ap->logopt, ap->ioctlfd);
 	ap->ioctlfd = -1;
 	close(ap->state_pipe[0]);
 	close(ap->state_pipe[1]);
@@ -320,48 +320,6 @@ force_umount:
 	return rv;
 }
 
-static int expire_indirect(struct autofs_point *ap, int ioctlfd, const char *path, unsigned int when)
-{
-	char buf[MAX_ERR_BUF];
-	int ret, retries;
-	struct stat st;
-
-	if (fstat(ioctlfd, &st) == -1) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		debug(ap->logopt, "fstat failed: %s", estr);
-		return 0;
-	}
-
-	retries = (count_mounts(ap->logopt, path, st.st_dev) + 1) * EXPIRE_RETRIES;
-
-	while (retries--) {
-		struct timespec tm = {0, 100000000};
-
-		/* Ggenerate expire message for the mount. */
-		ret = ioctl(ioctlfd, AUTOFS_IOC_EXPIRE_DIRECT, &when);
-		if (ret == -1) {
-			/* Mount has gone away */
-			if (errno == EBADF || errno == EINVAL)
-				return 1;
-
-			/*
-			 * Other than EAGAIN is an expire error so continue.
-			 * Kernel will try the next mount.
-			 */
-			if (errno == EAGAIN)
-				break;
-		}
-		nanosleep(&tm, NULL);
-	}
-
-	if (!ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &ret)) {
-		if (!ret)
-			return 0;
-	}
-
-	return 1;
-}
-
 static void mnts_cleanup(void *arg)
 {
 	struct mnt_list *mnts = (struct mnt_list *) arg;
@@ -371,6 +329,7 @@ static void mnts_cleanup(void *arg)
 
 void *expire_proc_indirect(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct autofs_point *ap;
 	struct mapent *me = NULL;
 	struct mnt_list *mnts = NULL, *next;
@@ -458,8 +417,8 @@ void *expire_proc_indirect(void *arg)
 		debug(ap->logopt, "expire %s", next->path);
 
 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
-		ret = expire_indirect(ap, ioctlfd, next->path, now);
-		if (!ret)
+		ret = ops->expire(ap->logopt, ioctlfd, next->path, now);
+		if (ret)
 			left++;
 		pthread_setcancelstate(cur_state, NULL);
 	}
@@ -472,8 +431,8 @@ void *expire_proc_indirect(void *arg)
 	 */
 	if (mnts) {
 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
-		ret = expire_indirect(ap, ap->ioctlfd, ap->path, now);
-		if (!ret)
+		ret = ops->expire(ap->logopt, ap->ioctlfd, ap->path, now);
+		if (ret)
 			left++;
 		pthread_setcancelstate(cur_state, NULL);
 	}
@@ -495,8 +454,8 @@ void *expire_proc_indirect(void *arg)
 	pthread_cleanup_pop(1);
 
 	if (submnts)
-		debug(ap->logopt,
-		      "%d submounts remaining in %s", submnts, ap->path);
+		info(ap->logopt,
+		     "%d submounts remaining in %s", submnts, ap->path);
 
 	/* 
 	 * EXPIRE_MULTI is synchronous, so we can be sure (famous last
@@ -527,8 +486,11 @@ static void pending_cond_destroy(void *a
 
 static void expire_send_fail(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt = arg;
-	send_fail(mt->ap->logopt, mt->ap->ioctlfd, mt->wait_queue_token);
+	struct autofs_point *ap = mt->ap;
+	ops->send_fail(ap->logopt,
+		       ap->ioctlfd, mt->wait_queue_token, -ENOENT);
 }
 
 static void free_pending_args(void *arg)
@@ -546,6 +508,7 @@ static void expire_mutex_unlock(void *ar
 
 static void *do_expire_indirect(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt;
 	struct autofs_point *ap;
 	int status, state;
@@ -572,9 +535,11 @@ static void *do_expire_indirect(void *ar
 	status = do_expire(mt->ap, mt->name, mt->len);
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
 	if (status)
-		send_fail(ap->logopt, ap->ioctlfd, mt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       ap->ioctlfd, mt->wait_queue_token, -status);
 	else
-		send_ready(ap->logopt, ap->ioctlfd, mt->wait_queue_token);
+		ops->send_ready(ap->logopt,
+				ap->ioctlfd, mt->wait_queue_token);
 	pthread_setcancelstate(state, NULL);
 
 	pthread_cleanup_pop(0);
@@ -586,6 +551,7 @@ static void *do_expire_indirect(void *ar
 
 int handle_packet_expire_indirect(struct autofs_point *ap, autofs_packet_expire_indirect_t *pkt)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt;
 	char buf[MAX_ERR_BUF];
 	pthread_t thid;
@@ -600,7 +566,8 @@ int handle_packet_expire_indirect(struct
 	if (!mt) {
 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 		logerr("malloc: %s", estr);
-		send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       ap->ioctlfd, pkt->wait_queue_token, -ENOMEM);
 		pthread_setcancelstate(state, NULL);
 		return 1;
 	}
@@ -622,7 +589,8 @@ int handle_packet_expire_indirect(struct
 	status = pthread_create(&thid, &thread_attr, do_expire_indirect, mt);
 	if (status) {
 		error(ap->logopt, "expire thread create failed");
-		send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       ap->ioctlfd, pkt->wait_queue_token, -status);
 		expire_mutex_unlock(NULL);
 		pending_cond_destroy(mt);
 		free_pending_args(mt);
@@ -647,8 +615,11 @@ int handle_packet_expire_indirect(struct
 
 static void mount_send_fail(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt = arg;
-	send_fail(mt->ap->logopt, mt->ap->ioctlfd, mt->wait_queue_token);
+	struct autofs_point *ap = mt->ap;
+	ops->send_fail(ap->logopt,
+		       ap->ioctlfd, mt->wait_queue_token, -ENOENT);
 }
 
 static void mount_mutex_unlock(void *arg)
@@ -660,6 +631,7 @@ static void mount_mutex_unlock(void *arg
 
 static void *do_mount_indirect(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt;
 	struct autofs_point *ap;
 	char buf[PATH_MAX + 1];
@@ -837,10 +809,13 @@ cont:
 	status = lookup_nss_mount(ap, NULL, mt->name, mt->len);
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
 	if (status) {
-		send_ready(ap->logopt, ap->ioctlfd, mt->wait_queue_token);
+		ops->send_ready(ap->logopt,
+				ap->ioctlfd, mt->wait_queue_token);
 		info(ap->logopt, "mounted %s", buf);
 	} else {
-		send_fail(ap->logopt, ap->ioctlfd, mt->wait_queue_token);
+		/* TODO: get mount return status from lookup_nss_mount */
+		ops->send_fail(ap->logopt,
+			       ap->ioctlfd, mt->wait_queue_token, -ENOENT);
 		info(ap->logopt, "failed to mount %s", buf);
 	}
 	pthread_setcancelstate(state, NULL);
@@ -854,6 +829,7 @@ cont:
 
 int handle_packet_missing_indirect(struct autofs_point *ap, autofs_packet_missing_indirect_t *pkt)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	pthread_t thid;
 	char buf[MAX_ERR_BUF];
 	struct pending_args *mt;
@@ -868,7 +844,8 @@ int handle_packet_missing_indirect(struc
 	if (ap->shutdown ||
 	    ap->state == ST_SHUTDOWN_FORCE ||
 	    ap->state == ST_SHUTDOWN) {
-		send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       ap->ioctlfd, pkt->wait_queue_token, -ENOENT);
 		pthread_setcancelstate(state, NULL);
 		return 0;
 	}
@@ -877,7 +854,8 @@ int handle_packet_missing_indirect(struc
 	if (!mt) {
 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 		logerr("malloc: %s", estr);
-		send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       ap->ioctlfd, pkt->wait_queue_token, -ENOMEM);
 		pthread_setcancelstate(state, NULL);
 		return 1;
 	}
@@ -903,7 +881,8 @@ int handle_packet_missing_indirect(struc
 	status = pthread_create(&thid, &thread_attr, do_mount_indirect, mt);
 	if (status) {
 		error(ap->logopt, "expire thread create failed");
-		send_fail(ap->logopt, ap->ioctlfd, pkt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       ap->ioctlfd, pkt->wait_queue_token, -status);
 		mount_mutex_unlock(NULL);
 		pending_cond_destroy(mt);
 		free_pending_args(mt);
diff -up autofs-5.0.3/daemon/state.c.device-node-control autofs-5.0.3/daemon/state.c
--- autofs-5.0.3/daemon/state.c.device-node-control	2008-01-14 13:39:16.000000000 +0900
+++ autofs-5.0.3/daemon/state.c	2008-02-25 09:03:12.000000000 +0900
@@ -93,6 +93,7 @@ void nextstate(int statefd, enum states 
  */
 void expire_cleanup(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	pthread_t thid = pthread_self();
 	struct expire_args *ec;
 	struct autofs_point *ap;
@@ -113,7 +114,8 @@ void expire_cleanup(void *arg)
 
 	/* Check to see if expire process finished */
 	if (thid == ap->exp_thread) {
-		int rv, idle;
+		unsigned int idle;
+		int rv;
 
 		ap->exp_thread = 0;
 
@@ -135,7 +137,7 @@ void expire_cleanup(void *arg)
 			 * allowing it to shutdown.
 			 */
 			if (ap->submount && !success) {
-				rv = ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &idle);
+				rv = ops->askumount(ap->logopt, ap->ioctlfd, &idle);
 				if (!rv && idle && ap->submount > 1) {
 					next = ST_SHUTDOWN_PENDING;
 					break;
@@ -160,7 +162,7 @@ void expire_cleanup(void *arg)
 			 * shutdown return to ready state unless we have
 			 * been signaled to shutdown.
 			 */
-			rv = ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &idle);
+			rv = ops->askumount(ap->logopt, ap->ioctlfd, &idle);
 			if (!idle && !ap->shutdown) {
 				next = ST_READY;
 				if (!ap->submount)
diff -up autofs-5.0.3/daemon/lookup.c.device-node-control autofs-5.0.3/daemon/lookup.c
--- autofs-5.0.3/daemon/lookup.c.device-node-control	2008-01-14 13:39:16.000000000 +0900
+++ autofs-5.0.3/daemon/lookup.c	2008-02-25 09:03:12.000000000 +0900
@@ -1148,7 +1148,8 @@ int lookup_source_close_ioctlfd(struct a
 		me = cache_lookup_distinct(mc, key);
 		if (me) {
 			if (me->ioctlfd != -1) {
-				close(me->ioctlfd);
+				struct ioctl_ops *ops = get_ioctl_ops();
+				ops->close(ap->logopt, me->ioctlfd);
 				me->ioctlfd = -1;
 			}
 			cache_unlock(mc);
diff -up autofs-5.0.3/daemon/direct.c.device-node-control autofs-5.0.3/daemon/direct.c
--- autofs-5.0.3/daemon/direct.c.device-node-control	2008-02-25 08:58:46.000000000 +0900
+++ autofs-5.0.3/daemon/direct.c	2008-02-25 09:03:12.000000000 +0900
@@ -42,6 +42,9 @@
 
 extern pthread_attr_t thread_attr;
 
+static const unsigned int direct = AUTOFS_TYPE_DIRECT;
+static const unsigned int offset = AUTOFS_TYPE_OFFSET;
+
 struct mnt_params {
 	char *options;
 };
@@ -88,6 +91,7 @@ static void mnts_cleanup(void *arg)
 
 int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	char buf[MAX_ERR_BUF];
 	int ioctlfd, rv, left, retries;
 
@@ -106,23 +110,13 @@ int do_umount_autofs_direct(struct autof
 			return 1;
 		}
 		ioctlfd = me->ioctlfd;
-	} else {
-		int cl_flags;
-
-		ioctlfd = open(me->key, O_RDONLY);
-		if (ioctlfd != -1) {
-			if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) {
-				cl_flags |= FD_CLOEXEC;
-				fcntl(ioctlfd, F_SETFD, cl_flags);
-			}
-		}
-	}
-
+	} else
+		ops->open(ap->logopt, &ioctlfd, -1, me->key, direct);
 
 	if (ioctlfd >= 0) {
-		int status = 1;
+		unsigned int status = 1;
 
-		rv = ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status);
+		rv = ops->askumount(ap->logopt, ioctlfd, &status);
 		if (rv) {
 			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 			error(ap->logopt, "ioctl failed: %s", estr);
@@ -135,19 +129,17 @@ int do_umount_autofs_direct(struct autof
 				return 1;
 			} else {
 				me->ioctlfd = -1;
-				ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0);
-				close(ioctlfd);
+				ops->catatonic(ap->logopt, ioctlfd);
+				ops->close(ap->logopt, ioctlfd);
 				goto force_umount;
 			}
 		}
 		me->ioctlfd = -1;
-		ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0);
-		close(ioctlfd);
+		ops->catatonic(ap->logopt, ioctlfd);
+		ops->close(ap->logopt, ioctlfd);
 	} else {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 		error(ap->logopt,
 		      "couldn't get ioctl fd for direct mount %s", me->key);
-		debug(ap->logopt, "open: %s", estr);
 		return 1;
 	}
 
@@ -297,10 +289,11 @@ static int unlink_mount_tree(struct auto
 
 int do_mount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct mnt_params *mp;
 	time_t timeout = ap->exp_timeout;
 	struct stat st;
-	int status, ret, ioctlfd, cl_flags;
+	int status, ret, ioctlfd;
 	struct list_head list;
 	const char *map_name;
 
@@ -313,16 +306,8 @@ int do_mount_autofs_direct(struct autofs
 
 			save_ioctlfd = ioctlfd = me->ioctlfd;
 
-			if (ioctlfd == -1) {
-				ioctlfd = open(me->key, O_RDONLY);
-				if (ioctlfd != -1) {
-					cl_flags = fcntl(ioctlfd, F_GETFD, 0);
-					if (cl_flags != -1) {
-						cl_flags |= FD_CLOEXEC;
-						fcntl(ioctlfd, F_SETFD, cl_flags);
-					}
-				}
-			}
+			if (ioctlfd == -1)
+				ops->open(ap->logopt, &ioctlfd, -1, me->key, direct);
 
 			if (ioctlfd < 0) {
 				error(ap->logopt,
@@ -331,13 +316,14 @@ int do_mount_autofs_direct(struct autofs
 				return 0;
 			}
 
-			ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, &tout);
+			ops->timeout(ap->logopt, ioctlfd, &tout);
 
 			if (save_ioctlfd == -1)
-				close(ioctlfd);
+				ops->close(ap->logopt, ioctlfd);
 
 			return 0;
 		}
+
 		if (!unlink_mount_tree(ap, &list)) {
 			debug(ap->logopt,
 			      "already mounted as other than autofs "
@@ -402,22 +388,16 @@ int do_mount_autofs_direct(struct autofs
 		goto out_err;
 	}
 
-	/* Root directory for ioctl()'s */
-	ioctlfd = open(me->key, O_RDONLY);
+	ops->open(ap->logopt, &ioctlfd, -1, me->key, direct);
 	if (ioctlfd < 0) {
 		crit(ap->logopt, "failed to create ioctl fd for %s", me->key);
 		goto out_umount;
 	}
 
-	if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) {
-		cl_flags |= FD_CLOEXEC;
-		fcntl(ioctlfd, F_SETFD, cl_flags);
-	}
-
 	/* Calculate the timeouts */
 	ap->exp_runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO;
 
-	ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout);
+	ops->timeout(ap->logopt, ioctlfd, &timeout);
 
 	if (ap->exp_timeout)
 		info(ap->logopt,
@@ -438,14 +418,14 @@ int do_mount_autofs_direct(struct autofs
 	}
 	cache_set_ino_index(me->mc, me->key, st.st_dev, st.st_ino);
 
-	close(ioctlfd);
+	ops->close(ap->logopt, ioctlfd);
 
 	debug(ap->logopt, "mounted trigger %s", me->key);
 
 	return 0;
 
 out_close:
-	close(ioctlfd);
+	ops->close(ap->logopt, ioctlfd);
 out_umount:
 	/* TODO: maybe force umount (-l) */
 	umount(me->key);
@@ -537,8 +517,9 @@ int mount_autofs_direct(struct autofs_po
 
 int umount_autofs_offset(struct autofs_point *ap, struct mapent *me)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	char buf[MAX_ERR_BUF];
-	int ioctlfd, cl_flags, rv = 1, retries;
+	int ioctlfd, rv = 1, retries;
 
 	if (me->ioctlfd != -1) {
 		if (is_mounted(_PATH_MOUNTED, me->key, MNTS_REAL)) {
@@ -555,20 +536,13 @@ int umount_autofs_offset(struct autofs_p
 			      me->key);
 			return 0;
 		}
-
-		ioctlfd = open(me->key, O_RDONLY);
-		if (ioctlfd != -1) {
-			if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) {
-				cl_flags |= FD_CLOEXEC;
-				fcntl(ioctlfd, F_SETFD, cl_flags);
-			}
-		}
+		ops->open(ap->logopt, &ioctlfd, -1, me->key, offset);
 	}
 
 	if (ioctlfd >= 0) {
-		int status = 1;
+		unsigned int status = 1;
 
-		rv = ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &status);
+		rv = ops->askumount(ap->logopt, ioctlfd, &status);
 		if (rv) {
 			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 			logerr("ioctl failed: %s", estr);
@@ -581,14 +555,14 @@ int umount_autofs_offset(struct autofs_p
 				return 1;
 			} else {
 				me->ioctlfd = -1;
-				ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0);
-				close(ioctlfd);
+				ops->catatonic(ap->logopt, ioctlfd);
+				ops->close(ap->logopt, ioctlfd);
 				goto force_umount;
 			}
 		}
 		me->ioctlfd = -1;
-		ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0);
-		close(ioctlfd);
+		ops->catatonic(ap->logopt, ioctlfd);
+		ops->close(ap->logopt, ioctlfd);
 	} else {
 		struct stat st;
 		char *estr;
@@ -653,11 +627,12 @@ force_umount:
 
 int mount_autofs_offset(struct autofs_point *ap, struct mapent *me)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	char buf[MAX_ERR_BUF];
 	struct mnt_params *mp;
 	time_t timeout = ap->exp_timeout;
 	struct stat st;
-	int ioctlfd, cl_flags, status, ret;
+	int ioctlfd, status, ret;
 	const char *type, *map_name = NULL;
 
 	if (is_mounted(_PROC_MOUNTS, me->key, MNTS_AUTOFS)) {
@@ -760,19 +735,13 @@ int mount_autofs_offset(struct autofs_po
 		goto out_err;
 	}
 
-	/* Root directory for ioctl()'s */
-	ioctlfd = open(me->key, O_RDONLY);
+	ops->open(ap->logopt, &ioctlfd, -1, me->key, offset);
 	if (ioctlfd < 0) {
 		crit(ap->logopt, "failed to create ioctl fd for %s", me->key);
 		goto out_umount;
 	}
 
-	if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) {
-		cl_flags |= FD_CLOEXEC;
-		fcntl(ioctlfd, F_SETFD, cl_flags);
-	}
-
-	ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout);
+	ops->timeout(ap->logopt, ioctlfd, &timeout);
 
 	ret = fstat(ioctlfd, &st);
 	if (ret == -1) {
@@ -783,14 +752,14 @@ int mount_autofs_offset(struct autofs_po
 
 	cache_set_ino_index(me->mc, me->key, st.st_dev, st.st_ino);
 
-	close(ioctlfd);
+	ops->close(ap->logopt, ioctlfd);
 
 	debug(ap->logopt, "mounted trigger %s", me->key);
 
 	return 0;
 
 out_close:
-	close(ioctlfd);
+	ops->close(ap->logopt, ioctlfd);
 out_umount:
 	umount(me->key);
 out_err:
@@ -800,53 +769,9 @@ out_err:
 	return -1;
 }
 
-static int expire_direct(int ioctlfd, const char *path, unsigned int when, unsigned int logopt)
-{
-	char buf[MAX_ERR_BUF];
-	int ret, retries;
-	struct stat st;
-
-	if (fstat(ioctlfd, &st) == -1) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		debug(logopt, "fstat failed: %s", estr);
-		return 0;
-	}
-
-	retries = (count_mounts(logopt, path, st.st_dev) + 1) * EXPIRE_RETRIES;
-
-	while (retries--) {
-		struct timespec tm = {0, 100000000};
-
-		/* Ggenerate expire message for the mount. */
-		ret = ioctl(ioctlfd, AUTOFS_IOC_EXPIRE_DIRECT, &when);
-		if (ret == -1) {
-			/* Mount has gone away */
-			if (errno == EBADF || errno == EINVAL)
-				return 1;
-
-			/*
-			 * Other than EAGAIN is an expire error so continue.
-			 * Kernel try the same mount again, limited by
-			 * retries (ie. number of mounts directly under
-			 * mount point, should always be one for direct
-			 * mounts).
-			 */
-			if (errno == EAGAIN)
-				break;
-		}
-		nanosleep(&tm, NULL);
-	}
-
-	if (!ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, &ret)) {
-		if (!ret)
-			return 0;
-	}
-
-	return 1;
-}
-
 void *expire_proc_direct(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct mnt_list *mnts = NULL, *next;
 	struct list_head list, *p;
 	struct expire_args *ea;
@@ -958,8 +883,8 @@ void *expire_proc_direct(void *arg)
 
 			ioctlfd = me->ioctlfd;
 
-			ret = expire_direct(ioctlfd, next->path, now, ap->logopt);
-			if (!ret) {
+			ret = ops->expire(ap->logopt, ioctlfd, next->path, now);
+			if (ret) {
 				left++;
 				pthread_setcancelstate(cur_state, NULL);
 				continue;
@@ -991,8 +916,8 @@ void *expire_proc_direct(void *arg)
 		debug(ap->logopt, "send expire to trigger %s", next->path);
 
 		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
-		ret = expire_direct(ioctlfd, next->path, now, ap->logopt);
-		if (!ret)
+		ret = ops->expire(ap->logopt, ioctlfd, next->path, now);
+		if (ret)
 			left++;
 		pthread_setcancelstate(cur_state, NULL);
 	}
@@ -1023,8 +948,11 @@ static void pending_cond_destroy(void *a
 
 static void expire_send_fail(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt = arg;
-	send_fail(mt->ap->logopt, mt->ioctlfd, mt->wait_queue_token);
+	struct autofs_point *ap = mt->ap;
+	ops->send_fail(ap->logopt,
+		       mt->ioctlfd, mt->wait_queue_token, -ENOENT);
 }
 
 static void free_pending_args(void *arg)
@@ -1042,6 +970,7 @@ static void expire_mutex_unlock(void *ar
 
 static void *do_expire_direct(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt;
 	struct autofs_point *ap;
 	size_t len;
@@ -1076,15 +1005,16 @@ static void *do_expire_direct(void *arg)
 	status = do_expire(ap, mt->name, len);
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
 	if (status)
-		send_fail(ap->logopt, mt->ioctlfd, mt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       mt->ioctlfd, mt->wait_queue_token, -ENOENT);
 	else {
 		struct mapent *me;
 		cache_readlock(mt->mc);
 		me = cache_lookup_distinct(mt->mc, mt->name);
 		me->ioctlfd = -1;
 		cache_unlock(mt->mc);
-		send_ready(ap->logopt, mt->ioctlfd, mt->wait_queue_token);
-		close(mt->ioctlfd);
+		ops->send_ready(ap->logopt, mt->ioctlfd, mt->wait_queue_token);
+		ops->close(ap->logopt, mt->ioctlfd);
 	}
 	pthread_setcancelstate(state, NULL);
 
@@ -1097,6 +1027,7 @@ static void *do_expire_direct(void *arg)
 
 int handle_packet_expire_direct(struct autofs_point *ap, autofs_packet_expire_direct_t *pkt)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct map_source *map;
 	struct mapent_cache *mc = NULL;
 	struct mapent *me = NULL;
@@ -1146,7 +1077,8 @@ int handle_packet_expire_direct(struct a
 	if (!mt) {
 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 		error(ap->logopt, "malloc: %s", estr);
-		send_fail(ap->logopt, me->ioctlfd, pkt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       me->ioctlfd, pkt->wait_queue_token, -ENOMEM);
 		cache_unlock(mc);
 		pthread_setcancelstate(state, NULL);
 		return 1;
@@ -1175,7 +1107,8 @@ int handle_packet_expire_direct(struct a
 	status = pthread_create(&thid, &thread_attr, do_expire_direct, mt);
 	if (status) {
 		error(ap->logopt, "expire thread create failed");
-		send_fail(ap->logopt, mt->ioctlfd, pkt->wait_queue_token);
+		ops->send_fail(ap->logopt,
+			       mt->ioctlfd, pkt->wait_queue_token, -status);
 		cache_unlock(mc);
 		expire_mutex_unlock(NULL);
 		pending_cond_destroy(mt);
@@ -1203,9 +1136,11 @@ int handle_packet_expire_direct(struct a
 
 static void mount_send_fail(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt = arg;
-	send_fail(mt->ap->logopt, mt->ioctlfd, mt->wait_queue_token);
-	close(mt->ioctlfd);
+	struct autofs_point *ap = mt->ap;
+	ops->send_fail(ap->logopt, mt->ioctlfd, mt->wait_queue_token, -ENOENT);
+	ops->close(ap->logopt, mt->ioctlfd);
 }
 
 static void mount_mutex_unlock(void *arg)
@@ -1217,6 +1152,7 @@ static void mount_mutex_unlock(void *arg
 
 static void *do_mount_direct(void *arg)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct pending_args *mt;
 	struct autofs_point *ap;
 	struct passwd pw;
@@ -1409,15 +1345,19 @@ cont:
 		cache_unlock(mt->mc);
 		if (set_fd) {
 			me->ioctlfd = mt->ioctlfd;
-			send_ready(ap->logopt, mt->ioctlfd, mt->wait_queue_token);
+			ops->send_ready(ap->logopt,
+					mt->ioctlfd, mt->wait_queue_token);
 		} else {
-			send_ready(ap->logopt, mt->ioctlfd, mt->wait_queue_token);
-			close(mt->ioctlfd);
+			ops->send_ready(ap->logopt,
+					mt->ioctlfd, mt->wait_queue_token);
+			ops->close(ap->logopt, mt->ioctlfd);
 		}
 		info(ap->logopt, "mounted %s", mt->name);
 	} else {
-		send_fail(mt->ap->logopt, mt->ioctlfd, mt->wait_queue_token);
-		close(mt->ioctlfd);
+		/* TODO: get mount return status from lookup_nss_mount */
+		ops->send_fail(ap->logopt,
+			       mt->ioctlfd, mt->wait_queue_token, -ENOENT);
+		ops->close(ap->logopt, mt->ioctlfd);
 		info(ap->logopt, "failed to mount %s", mt->name);
 	}
 	pthread_setcancelstate(state, NULL);
@@ -1431,6 +1371,7 @@ cont:
 
 int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_direct_t *pkt)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	struct map_source *map;
 	struct mapent_cache *mc = NULL;
 	struct mapent *me = NULL;
@@ -1438,7 +1379,8 @@ int handle_packet_missing_direct(struct 
 	struct pending_args *mt;
 	char buf[MAX_ERR_BUF];
 	int status = 0;
-	int ioctlfd, cl_flags, state;
+	int ioctlfd, state;
+	unsigned int type;
 
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
 
@@ -1475,12 +1417,16 @@ int handle_packet_missing_direct(struct 
 		return 1;
 	}
 
+	type = direct;
+	if (me->multi != me && !list_empty(&me->multi_list))
+		type = offset;
+
 	if (me->ioctlfd != -1) {
 		/* Maybe someone did a manual umount, clean up ! */
 		ioctlfd = me->ioctlfd;
 		me->ioctlfd = -1;
 	} else
-		ioctlfd = open(me->key, O_RDONLY);
+		ops->open(ap->logopt, &ioctlfd, -1, me->key, type);
 
 	if (ioctlfd == -1) {
 		cache_unlock(mc);
@@ -1490,11 +1436,6 @@ int handle_packet_missing_direct(struct 
 		return 1;
 	}
 
-	if ((cl_flags = fcntl(ioctlfd, F_GETFD, 0)) != -1) {
-		cl_flags |= FD_CLOEXEC;
-		fcntl(ioctlfd, F_SETFD, cl_flags);
-	}
-
 	debug(ap->logopt, "token %ld, name %s, request pid %u",
 		  (unsigned long) pkt->wait_queue_token, me->key, pkt->pid);
 
@@ -1502,8 +1443,9 @@ int handle_packet_missing_direct(struct 
 	if (ap->shutdown ||
 	    ap->state == ST_SHUTDOWN_FORCE ||
 	    ap->state == ST_SHUTDOWN) {
-		send_fail(ap->logopt, ioctlfd, pkt->wait_queue_token);
-		close(ioctlfd);
+		ops->send_fail(ap->logopt,
+			       ioctlfd, pkt->wait_queue_token, -ENOENT);
+		ops->close(ap->logopt, ioctlfd);
 		cache_unlock(mc);
 		pthread_setcancelstate(state, NULL);
 		return 1;
@@ -1513,8 +1455,9 @@ int handle_packet_missing_direct(struct 
 	if (!mt) {
 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
 		error(ap->logopt, "malloc: %s", estr);
-		send_fail(ap->logopt, ioctlfd, pkt->wait_queue_token);
-		close(ioctlfd);
+		ops->send_fail(ap->logopt,
+			       ioctlfd, pkt->wait_queue_token, -ENOMEM);
+		ops->close(ap->logopt, ioctlfd);
 		cache_unlock(mc);
 		pthread_setcancelstate(state, NULL);
 		return 1;
@@ -1542,8 +1485,9 @@ int handle_packet_missing_direct(struct 
 	status = pthread_create(&thid, &thread_attr, do_mount_direct, mt);
 	if (status) {
 		error(ap->logopt, "missing mount thread create failed");
-		send_fail(ap->logopt, ioctlfd, pkt->wait_queue_token);
-		close(ioctlfd);
+		ops->send_fail(ap->logopt,
+			       ioctlfd, pkt->wait_queue_token, -status);
+		ops->close(ap->logopt, ioctlfd);
 		cache_unlock(mc);
 		mount_mutex_unlock(NULL);
 		pending_cond_destroy(mt);
diff -up autofs-5.0.3/daemon/automount.c.device-node-control autofs-5.0.3/daemon/automount.c
--- autofs-5.0.3/daemon/automount.c.device-node-control	2008-01-14 13:39:16.000000000 +0900
+++ autofs-5.0.3/daemon/automount.c	2008-02-25 09:03:12.000000000 +0900
@@ -579,40 +579,6 @@ int umount_autofs(struct autofs_point *a
 	return ret;
 }
 
-int send_ready(unsigned logopt, int ioctlfd, unsigned int wait_queue_token)
-{
-	char buf[MAX_ERR_BUF];
-
-	if (wait_queue_token == 0)
-		return 0;
-
-	debug(logopt, "token = %d", wait_queue_token);
-
-	if (ioctl(ioctlfd, AUTOFS_IOC_READY, wait_queue_token) < 0) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		logerr("AUTOFS_IOC_READY: error %s", estr);
-		return 1;
-	}
-	return 0;
-}
-
-int send_fail(unsigned logopt, int ioctlfd, unsigned int wait_queue_token)
-{
-	char buf[MAX_ERR_BUF];
-
-	if (wait_queue_token == 0)
-		return 0;
-
-	debug(logopt, "token = %d", wait_queue_token);
-
-	if (ioctl(ioctlfd, AUTOFS_IOC_FAIL, wait_queue_token) < 0) {
-		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
-		logerr("AUTOFS_IOC_FAIL: error %s", estr);
-		return 1;
-	}
-	return 0;
-}
-
 static size_t get_kpkt_len(void)
 {
 	size_t pkt_len = sizeof(struct autofs_v5_packet);
@@ -1540,7 +1506,9 @@ void *handle_mounts(void *arg)
 
 	while (ap->state != ST_SHUTDOWN) {
 		if (handle_packet(ap)) {
-			int ret, result;
+			struct ioctl_ops *ops = get_ioctl_ops();
+			unsigned int result;
+			int ret;
 
 			state_mutex_lock(ap);
 			/*
@@ -1557,7 +1525,7 @@ void *handle_mounts(void *arg)
 			 * If the ioctl fails assume the kernel doesn't have
 			 * AUTOFS_IOC_ASKUMOUNT and just continue.
 			 */
-			ret = ioctl(ap->ioctlfd, AUTOFS_IOC_ASKUMOUNT, &result);
+			ret = ops->askumount(ap->logopt, ap->ioctlfd, &result);
 			if (ret == -1) {
 				state_mutex_unlock(ap);
 				break;
@@ -2055,6 +2023,8 @@ int main(int argc, char *argv[])
 		exit(1);
 	}
 
+	init_ioctl_ctl();
+
 	if (!alarm_start_handler()) {
 		logerr("%s: failed to create alarm handler thread!", program);
 		master_kill(master_list);
@@ -2099,6 +2069,8 @@ int main(int argc, char *argv[])
 	if (dh)
 		dlclose(dh);
 #endif
+	close_ioctl_ctl();
+
 	info(logging, "autofs stopped");
 
 	exit(0);
diff -up autofs-5.0.3/include/automount.h.device-node-control autofs-5.0.3/include/automount.h
--- autofs-5.0.3/include/automount.h.device-node-control	2008-01-14 13:39:16.000000000 +0900
+++ autofs-5.0.3/include/automount.h	2008-02-25 09:03:12.000000000 +0900
@@ -29,6 +29,7 @@
 #include "log.h"
 #include "rpc_subs.h"
 #include "parse_subs.h"
+#include "dev-ioctl-lib.h"
 
 #ifdef WITH_DMALLOC
 #include <dmalloc.h>
@@ -70,6 +71,10 @@ int load_autofs4_module(void);
 #define SMB_SUPER_MAGIC    0x0000517BL
 #define CIFS_MAGIC_NUMBER  0xFF534D42L
 
+#define AUTOFS_TYPE_INDIRECT     0x0001
+#define AUTOFS_TYPE_DIRECT       0x0002
+#define AUTOFS_TYPE_OFFSET       0x0004
+
 /* This sould be enough for at least 20 host aliases */
 #define HOST_ENT_BUF_SIZE	2048
 
@@ -367,7 +372,7 @@ void free_mnt_list(struct mnt_list *list
 int contained_in_local_fs(const char *path);
 int is_mounted(const char *table, const char *path, unsigned int type);
 int has_fstab_option(const char *opt);
-char *find_mnt_ino(const char *table, dev_t dev, ino_t ino);
+int find_mnt_devid(const char *table, const char *path, char *devid, unsigned int type);
 char *get_offset(const char *prefix, char *offset,
                  struct list_head *head, struct list_head **pos);
 void add_ordered_list(struct mnt_list *ent, struct list_head *head);
@@ -377,6 +382,7 @@ int tree_get_mnt_list(struct mnt_list *m
 int tree_get_mnt_sublist(struct mnt_list *mnts, struct list_head *list, const char *path, int include);
 int tree_find_mnt_ents(struct mnt_list *mnts, struct list_head *list, const char *path);
 int tree_is_mounted(struct mnt_list *mnts, const char *path, unsigned int type);
+int tree_find_mnt_devid(struct mnt_list *mnts, const char *path, char *devid, unsigned int type);
 
 /* Core automount definitions */
 
@@ -470,8 +476,6 @@ struct autofs_point {
 
 void *handle_mounts(void *arg);
 int umount_multi(struct autofs_point *ap, const char *path, int incl);
-int send_ready(unsigned logopt, int ioctlfd, unsigned int wait_queue_token);
-int send_fail(unsigned logopt, int ioctlfd, unsigned int wait_queue_token);
 int do_expire(struct autofs_point *ap, const char *name, int namelen);
 void *expire_proc_indirect(void *);
 void *expire_proc_direct(void *);
diff -up /dev/null autofs-5.0.3/include/linux/auto_dev-ioctl.h
--- /dev/null	2008-02-25 17:16:11.149000952 +0900
+++ autofs-5.0.3/include/linux/auto_dev-ioctl.h	2008-02-25 09:03:44.000000000 +0900
@@ -0,0 +1,113 @@
+/*
+ * linux/include/linux/auto_dev-ioctl.h
+ *
+ * Copyright 2008 Red Hat, Inc. All rights reserved.
+ * Copyright 2008 Ian Kent <raven@themaw.net>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#ifndef _LINUX_AUTO_DEV_IOCTL_H
+#define _LINUX_AUTO_DEV_IOCTL_H
+
+#include <linux/types.h>
+
+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR	1
+#define AUTOFS_DEV_IOCTL_VERSION_MINOR	0
+
+#define AUTOFS_DEVID_LEN		16
+
+/*
+ * An ioctl interface for autofs mount point control.
+ */
+
+/*
+ * All theioctls use this structure.
+ * When sending a path size must account for the total length
+ * of the chunk of memory.
+ */
+
+struct autofs_dev_ioctl {
+	__u32 ver_major;
+	__u32 ver_minor;
+	__u32 size;		/* total size of data passed in
+				 * including this struct */
+	__s32 ioctlfd;		/* automount command fd */
+
+	__u32 arg1;		/* Command parameters */
+	__u32 arg2;
+
+	char path[0];
+};
+
+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in)
+{
+	in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
+	in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
+	in->size = sizeof(struct autofs_dev_ioctl);
+	in->ioctlfd = -1;
+	in->arg1 = 0;
+	in->arg2 = 0;
+	return;
+}
+
+/*
+ * If you change this make sure you make the corresponding change
+ * to autofs-dev-ioctl.c:lookup_ioctl()
+ */
+enum {
+	/* Get various version info */
+	AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71,
+	AUTOFS_DEV_IOCTL_PROTOVER_CMD,
+	AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD,
+
+	/* Set mount we're acting on */
+	AUTOFS_DEV_IOCTL_OPENMOUNT_CMD,
+
+	/* Close mount ioctl fd */
+	AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD,
+
+	/* Mount/expire status returns */
+	AUTOFS_DEV_IOCTL_READY_CMD,
+	AUTOFS_DEV_IOCTL_FAIL_CMD,
+
+	/* Automount active/inactive */
+	AUTOFS_DEV_IOCTL_SETPIPEFD_CMD,
+	AUTOFS_DEV_IOCTL_CATATONIC_CMD,
+
+	/* Expiry timeout */
+	AUTOFS_DEV_IOCTL_TIMEOUT_CMD,
+
+	/* Get mount last requesting uid and gid */
+	AUTOFS_DEV_IOCTL_REQUESTOR_CMD,
+
+	/* Check for eligible expire candidates */
+	AUTOFS_DEV_IOCTL_EXPIRE_CMD,
+
+	/* Request busy status */
+	AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD,
+
+	/* Check if path is a mountpoint */
+	AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD,
+};
+
+#define AUTOFS_IOCTL 0x93
+
+#define AUTOFS_DEV_IOCTL_VERSION	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_PROTOVER	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_PROTOSUBVER	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_OPENMOUNT	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_READY		_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_FAIL		_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_SETPIPEFD	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_CATATONIC	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_TIMEOUT	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_REQUESTOR	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_REQUESTOR_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_EXPIRE		_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_ASKUMOUNT	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl)
+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT	_IOWR(AUTOFS_IOCTL, AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl)
+
+#endif	/* _LINUX_AUTO_DEV_IOCTL_H */
diff -up /dev/null autofs-5.0.3/include/mounts.h
--- /dev/null	2008-02-25 17:16:11.149000952 +0900
+++ autofs-5.0.3/include/mounts.h	2008-02-25 09:03:44.000000000 +0900
@@ -0,0 +1,96 @@
+/* ----------------------------------------------------------------------- *
+ *   
+ *  mounts.h - header file for mount utilities module.
+ * 
+ *   Copyright 2008 Red Hat, Inc. All rights reserved.
+ *   Copyright 2004-2006 Ian Kent <raven@themaw.net> - All Rights Reserved.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef MOUNTS_H
+#define MOUNTS_H
+
+#define AUTOFS_TYPE_INDIRECT     0x0001
+#define AUTOFS_TYPE_DIRECT       0x0002
+#define AUTOFS_TYPE_OFFSET       0x0004
+
+#define MNTS_ALL	0x0001
+#define MNTS_REAL	0x0002
+#define MNTS_AUTOFS	0x0004
+
+#define REMOUNT_SUCCESS		0x0000
+#define REMOUNT_OPEN_FAIL	0x0001
+#define REMOUNT_STAT_FAIL	0x0002
+#define REMOUNT_READ_MAP	0x0004
+
+extern const unsigned int indirect;
+extern const unsigned int direct;
+extern const unsigned int offset;
+
+struct mapent;
+
+struct mnt_list {
+	char *path;
+	char *fs_name;
+	char *fs_type;
+	char *opts;
+	pid_t owner;
+	/*
+	 * List operations ie. get_mnt_list.
+	 */
+	struct mnt_list *next;
+	/*
+	 * Tree operations ie. tree_make_tree,
+	 * tree_get_mnt_list etc.
+	 */
+	struct mnt_list *left;
+	struct mnt_list *right;
+	struct list_head self;
+	struct list_head list;
+	struct list_head entries;
+	struct list_head sublist;
+	/*
+	 * Offset mount handling ie. add_ordered_list
+	 * and get_offset.
+	 */
+	struct list_head ordered;
+};
+
+unsigned int query_kproto_ver(void);
+unsigned int get_kver_major(void);
+unsigned int get_kver_minor(void);
+char *make_options_string(char *path, int kernel_pipefd, char *extra);
+char *make_mnt_name_string(char *path);
+struct mnt_list *get_mnt_list(const char *table, const char *path, int include);
+struct mnt_list *reverse_mnt_list(struct mnt_list *list);
+void free_mnt_list(struct mnt_list *list);
+int contained_in_local_fs(const char *path);
+int is_mounted(const char *table, const char *path, unsigned int type);
+int has_fstab_option(const char *opt);
+int find_mnt_devid(const char *table, const char *path, char *devid, const unsigned int type);
+char *get_offset(const char *prefix, char *offset,
+                 struct list_head *head, struct list_head **pos);
+void add_ordered_list(struct mnt_list *ent, struct list_head *head);
+void tree_free_mnt_tree(struct mnt_list *tree);
+struct mnt_list *tree_make_mnt_tree(const char *table, const char *path);
+int tree_get_mnt_list(struct mnt_list *mnts, struct list_head *list, const char *path, int include);
+int tree_get_mnt_sublist(struct mnt_list *mnts, struct list_head *list, const char *path, int include);
+int tree_find_mnt_ents(struct mnt_list *mnts, struct list_head *list, const char *path);
+int tree_is_mounted(struct mnt_list *mnts, const char *path, unsigned int type);
+int tree_find_mnt_devid(struct mnt_list *mnts, const char *path, char *devid, const unsigned int type);
+void set_tsd_user_vars(unsigned int, uid_t, gid_t);
+const char *mount_type_str(unsigned int);
+void notify_mount_result(struct autofs_point *, const char *, const char *);
+int remount_active_mount(struct autofs_point *, struct mapent_cache *,
+			 const char *, dev_t devid, const unsigned int, int *);
+int umount_ent(struct autofs_point *, const char *);
+int mount_multi_triggers(struct autofs_point *, char *, struct mapent *, const char *);
+int umount_multi_triggers(struct autofs_point *, char *, struct mapent *, const char *);
+
+#endif
diff -up /dev/null autofs-5.0.3/include/dev-ioctl-lib.h
--- /dev/null	2008-02-25 17:16:11.149000952 +0900
+++ autofs-5.0.3/include/dev-ioctl-lib.h	2008-02-25 09:03:44.000000000 +0900
@@ -0,0 +1,62 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *  dev-ioctl-lib.h - autofs device control.
+ *
+ *   Copyright 2008 Red Hat, Inc. All rights reserved.
+ *   Copyright 2008 Ian Kent <raven@themaw.net>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef AUTOFS_DEV_IOCTL_LIB_H
+#define AUTOFS_DEV_IOCTL_LIB_H
+
+#include <sys/types.h>
+#include "linux/auto_dev-ioctl.h"
+
+#define CONTROL_DEVICE  "/dev/autofs"
+
+#define DEV_IOCTL_IS_MOUNTED	0x0001
+#define DEV_IOCTL_IS_AUTOFS	0x0002
+#define DEV_IOCTL_IS_OTHER	0x0004
+
+struct ioctl_ctl {
+	int devfd;
+	struct ioctl_ops *ops;
+};
+
+struct ioctl_ops {
+	int (*version)(unsigned int, int, struct autofs_dev_ioctl *);
+	int (*protover)(unsigned int, int, unsigned int *);
+	int (*protosubver)(unsigned int, int, unsigned int *);
+	int (*open)(unsigned int, int *, dev_t, const char *, unsigned int);
+	int (*close)(unsigned int, int);
+	int (*send_ready)(unsigned int, int, unsigned int);
+	int (*send_fail)(unsigned int, int, unsigned int, int);
+	int (*setpipefd)(unsigned int, int, int);
+	int (*catatonic)(unsigned int, int);
+	int (*timeout)(unsigned int, int, time_t *);
+	int (*requestor)(unsigned int, int, const char *, uid_t *, gid_t *);
+	int (*expire)(unsigned int, int, const char *, unsigned int);
+	int (*askumount)(unsigned int, int, unsigned int *);
+	int (*ismountpoint)(unsigned int, int, const char *, unsigned int *);
+};
+
+void init_ioctl_ctl(void);
+void close_ioctl_ctl(void);
+struct ioctl_ops *get_ioctl_ops(void);
+struct autofs_dev_ioctl *alloc_ioctl_ctl_open(const char *, unsigned int);
+void free_ioctl_ctl_open(struct autofs_dev_ioctl *);
+
+#endif
+
diff -up autofs-5.0.3/CHANGELOG.device-node-control autofs-5.0.3/CHANGELOG
--- autofs-5.0.3/CHANGELOG.device-node-control	2008-02-25 08:58:46.000000000 +0900
+++ autofs-5.0.3/CHANGELOG	2008-02-25 09:07:32.000000000 +0900
@@ -8,6 +8,7 @@
 - another fix for don't fail on empty master map.
 - fix expire working harder than needed.
 - fix unlink of mount tree incorrectly causing autofs mount fail.
+- add miscellaneous device node interface library.
  
 14/01/2008 autofs-5.0.3
 -----------------------
diff -up autofs-5.0.3/redhat/autofs.init.in.device-node-control autofs-5.0.3/redhat/autofs.init.in
--- autofs-5.0.3/redhat/autofs.init.in.device-node-control	2008-01-14 13:39:16.000000000 +0900
+++ autofs-5.0.3/redhat/autofs.init.in	2008-02-25 09:03:12.000000000 +0900
@@ -13,6 +13,7 @@
 DAEMON=@@sbindir@@/automount
 prog=`basename $DAEMON`
 MODULE="autofs4"
+DEVICE="autofs"
 initdir=@@initdir@@
 confdir=@@autofsconfdir@@
 
@@ -56,6 +57,15 @@ function start() {
 		echo
 		return $RETVAL
 	fi
+
+	# Check misc device
+	if [ -e "/proc/misc" ]; then
+		MINOR=`awk "/$DEVICE/ {print \\$1}" /proc/misc`
+		if [ -n "$MINOR" -a ! -c "/dev/$DEVICE" ]; then
+			mknod -m 0600 /dev/$DEVICE c 10 $MINOR
+		fi
+	fi
+
 	echo -n $"Starting $prog: "
 	$prog $OPTIONS 
 	RETVAL=$?
diff -up autofs-5.0.3/lib/master_parse.y.device-node-control autofs-5.0.3/lib/master_parse.y
--- autofs-5.0.3/lib/master_parse.y.device-node-control	2008-02-25 08:58:46.000000000 +0900
+++ autofs-5.0.3/lib/master_parse.y	2008-02-25 09:03:12.000000000 +0900
@@ -796,6 +796,7 @@ int master_parse_entry(const char *buffe
 			return 0;
 		}
 	} else {
+		struct ioctl_ops *ops = get_ioctl_ops();
 		struct autofs_point *ap = entry->ap;
 		time_t tout = timeout;
 
@@ -807,7 +808,7 @@ int master_parse_entry(const char *buffe
 			ap->exp_timeout = timeout;
 			ap->exp_runfreq = (ap->exp_timeout + CHECK_RATIO - 1) / CHECK_RATIO;
 			if (ap->ioctlfd != -1 && ap->type == LKP_INDIRECT)
-				ioctl(ap->ioctlfd, AUTOFS_IOC_SETTIMEOUT, &tout);
+				ops->timeout(ap->logopt, ap->ioctlfd, &tout);
 		}
 	}
 	entry->ap->random_selection = random_selection;
diff -up autofs-5.0.3/lib/mounts.c.device-node-control autofs-5.0.3/lib/mounts.c
--- autofs-5.0.3/lib/mounts.c.device-node-control	2008-01-14 13:39:16.000000000 +0900
+++ autofs-5.0.3/lib/mounts.c	2008-02-25 09:03:12.000000000 +0900
@@ -38,6 +38,7 @@ static const char kver_options_template[
 
 unsigned int query_kproto_ver(void)
 {
+	struct ioctl_ops *ops = get_ioctl_ops();
 	char dir[] = "/tmp/autoXXXXXX", *t_dir;
 	char options[MAX_OPTIONS_LEN + 1];
 	pid_t pgrp = getpgrp();
@@ -70,7 +71,7 @@ unsigned int query_kproto_ver(void)
 
 	close(pipefd[1]);
 
-	ioctlfd = open(t_dir, O_RDONLY);
+	ops->open(LOGOPT_NONE, &ioctlfd, -1, t_dir, AUTOFS_TYPE_INDIRECT);
 	if (ioctlfd == -1) {
 		umount(t_dir);
 		close(pipefd[0]);
@@ -78,11 +79,11 @@ unsigned int query_kproto_ver(void)
 		return 0;
 	}
 
-	ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0);
+	ops->catatonic(LOGOPT_NONE, ioctlfd);
 
 	/* If this ioctl() doesn't work, it is kernel version 2 */
-	if (ioctl(ioctlfd, AUTOFS_IOC_PROTOVER, &kver.major) == -1) {
-		close(ioctlfd);
+	if (ops->protover(LOGOPT_NONE, ioctlfd, &kver.major)) {
+		ops->close(LOGOPT_NONE, ioctlfd);
 		umount(t_dir);
 		close(pipefd[0]);
 		rmdir(t_dir);
@@ -90,15 +91,15 @@ unsigned int query_kproto_ver(void)
 	}
 
 	/* If this ioctl() doesn't work, version is 4 or less */
-	if (ioctl(ioctlfd, AUTOFS_IOC_PROTOSUBVER, &kver.minor) == -1) {
-		close(ioctlfd);
+	if (ops->protosubver(LOGOPT_NONE, ioctlfd, &kver.minor)) {
+		ops->close(LOGOPT_NONE, ioctlfd);
 		umount(t_dir);
 		close(pipefd[0]);
 		rmdir(t_dir);
 		return 0;
 	}
 
-	close(ioctlfd);
+	ops->close(LOGOPT_NONE, ioctlfd);
 	umount(t_dir);
 	close(pipefd[0]);
 	rmdir(t_dir);
@@ -456,49 +457,75 @@ int has_fstab_option(const char *opt)
 	return ret;
 }
 
-char *find_mnt_ino(const char *table, dev_t dev, ino_t ino)
+/*
+ * Find the device number of an autofs mount with given path and
+ * type (eg..AUTOFS_TYPE_DIRECT). An autofs display mount option
+ * "dev=<device number>" is provided by the kernel module for this.
+ *
+ * The device number is used by the kernel to identify the autofs
+ * super block when searching for the mount.
+ */
+int find_mnt_devid(const char *table,
+		   const char *path, char *devid, unsigned int type)
 {
-	struct mntent mnt_wrk;
 	struct mntent *mnt;
+	struct mntent mnt_wrk;
 	char buf[PATH_MAX * 3];
-	char *path = NULL;
-	unsigned long l_dev = (unsigned long) dev;
-	unsigned long l_ino = (unsigned long) ino;
 	FILE *tab;
+	char *dev;
 
 	tab = setmntent(table, "r");
 	if (!tab) {
-		char *estr = strerror_r(errno, buf, (size_t) PATH_MAX - 1);
-		logerr("setmntent: %s", estr);
+		printf("failed to open mount table\n");
 		return 0;
 	}
 
+	dev = NULL;
 	while ((mnt = getmntent_r(tab, &mnt_wrk, buf, PATH_MAX * 3))) {
-		char *p_dev, *p_ino;
-		unsigned long m_dev, m_ino;
-
 		if (strcmp(mnt->mnt_type, "autofs"))
 			continue;
 
-		p_dev = strstr(mnt->mnt_opts, "dev=");
-		if (!p_dev)
-			continue;
-		sscanf(p_dev, "dev=%lu", &m_dev);
-		if (m_dev != l_dev)
+		if (strcmp(mnt->mnt_dir, path))
 			continue;
 
-		p_ino = strstr(mnt->mnt_opts, "ino=");
-		if (!p_ino)
-			continue;
-		sscanf(p_ino, "ino=%lu", &m_ino);
-		if (m_ino == l_ino) {
-			path = strdup(mnt->mnt_dir);
+		switch (type) {
+		case AUTOFS_TYPE_INDIRECT:
+			if (!hasmntopt(mnt, "indirect"))
+				continue;
+			break;
+
+		case AUTOFS_TYPE_DIRECT:
+			if (!hasmntopt(mnt, "direct"))
+				continue;
+			break;
+
+		case AUTOFS_TYPE_OFFSET:
+			if (!hasmntopt(mnt, "offset"))
+				continue;
+			break;
+		}
+
+		dev = hasmntopt(mnt, "dev");
+		if (dev) {
+			char *start = strchr(dev, '=') + 1;
+			char *end = strchr(start, ',');
+			if (end)
+				*end = '\0';
+			if (start) {
+				int len = strlen(start);
+				memcpy(devid, start, len);
+				devid[len] = '\0';
+			}
 			break;
 		}
 	}
+
 	endmntent(tab);
 
-	return path;
+	if (!dev)
+		return 0;
+
+	return 1;
 }
 
 char *get_offset(const char *prefix, char *offset,
@@ -976,3 +1003,70 @@ int tree_is_mounted(struct mnt_list *mnt
 	return mounted;
 }
 
+int tree_find_mnt_devid(struct mnt_list *mnts,
+			const char *path, char *devid, unsigned int type)
+{
+	struct list_head *p;
+	struct list_head list;
+	size_t len = strlen(path);
+	char *dev;
+
+	INIT_LIST_HEAD(&list);
+
+	if (!tree_find_mnt_ents(mnts, &list, path))
+		return 0;
+
+	dev = NULL;
+	list_for_each(p, &list) {
+		struct mnt_list *mptr;
+
+		mptr = list_entry(p, struct mnt_list, entries);
+
+		if (strcmp(mptr->fs_type, "autofs"))
+			continue;
+
+		if (strlen(mptr->path) < len)
+			return 0;
+
+		if (strcmp(mptr->path, path))
+			continue;
+
+		switch (type) {
+		case AUTOFS_TYPE_INDIRECT:
+			if (!strstr(mptr->opts, "indirect"))
+				continue;
+			break;
+
+		case AUTOFS_TYPE_DIRECT:
+			if (!strstr(mptr->opts, "direct"))
+				continue;
+			break;
+
+		case AUTOFS_TYPE_OFFSET:
+			if (!strstr(mptr->opts, "offset"))
+				continue;
+			break;
+		}
+
+		dev = strstr(mptr->opts, "dev");
+		if (dev) {
+			char *start = strchr(dev, '=') + 1;
+			char *end = strchr(start, ',');
+			if (end)
+				*end = '\0';
+			if (start) {
+				int len = strlen(start);
+				memcpy(devid, start, len);
+				devid[len] = '\0';
+			}
+			*end = ',';
+			break;
+		}
+	}
+
+	if (!dev)
+		return 0;
+
+	return 1;
+}
+
diff -up autofs-5.0.3/lib/Makefile.device-node-control autofs-5.0.3/lib/Makefile
--- autofs-5.0.3/lib/Makefile.device-node-control	2008-01-14 13:39:16.000000000 +0900
+++ autofs-5.0.3/lib/Makefile	2008-02-25 09:03:12.000000000 +0900
@@ -7,12 +7,13 @@ include ../Makefile.rules
 
 SRCS = cache.c cat_path.c rpc_subs.c mounts.c log.c nsswitch.c \
 	master_tok.l master_parse.y nss_tok.c nss_parse.tab.c \
-	args.c alarm.c macros.c master.c defaults.c parse_subs.c
+	args.c alarm.c macros.c master.c defaults.c parse_subs.c \
+	dev-ioctl-lib.c
 RPCS = mount.h mount_clnt.c mount_xdr.c
 OBJS = cache.o mount_clnt.o mount_xdr.o cat_path.o rpc_subs.o \
 	mounts.o log.o nsswitch.o master_tok.o master_parse.tab.o \
 	nss_tok.o nss_parse.tab.o args.o alarm.o macros.o master.o \
-	defaults.o parse_subs.o
+	defaults.o parse_subs.o dev-ioctl-lib.o
 
 YACCSRC = nss_tok.c nss_parse.tab.c nss_parse.tab.h \
 	  master_tok.c master_parse.tab.c master_parse.tab.h
diff -up /dev/null autofs-5.0.3/lib/dev-ioctl-lib.c
--- /dev/null	2008-02-25 17:16:11.149000952 +0900
+++ autofs-5.0.3/lib/dev-ioctl-lib.c	2008-02-25 09:03:44.000000000 +0900
@@ -0,0 +1,747 @@
+/* ----------------------------------------------------------------------- *
+ *   
+ *  ctl-dev-lib.c - module for Linux automount mount table lookup functions
+ *
+ *   Copyright 2008 Red Hat, Inc. All rights reserved.
+ *   Copyright 2008 Ian Kent <raven@themaw.net> - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ *   USA; either version 2 of the License, or (at your option) any later
+ *   version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <errno.h>
+#include <sys/vfs.h>
+
+#include "automount.h"
+
+/* ioctld control function interface */
+static struct ioctl_ctl ctl = { -1, NULL };
+
+#ifndef AUTOFS_SUPER_MAGIC
+#define AUTOFS_SUPER_MAGIC      0x0187
+#endif
+
+/*
+ * Define functions for autofs ioctl control.
+ *
+ * We provide two interfaces. One which routes ioctls via a
+ * miscelaneous device node and can be used to obtain an ioctl
+ * file descriptor for autofs mounts that are covered by an
+ * active mount (eg. active direct or multi-mount offsets).
+ * The other provides the traditional autofs ioctl implementation.
+ *
+ * The miscielaneous device control functions are prefixed with
+ * dev_ctl_ and the traditional ones are prefixed with ioctl_.
+ */
+static int dev_ioctl_version(unsigned int, int, struct autofs_dev_ioctl *);
+static int dev_ioctl_protover(unsigned int, int, unsigned int *);
+static int dev_ioctl_protosubver(unsigned int, int, unsigned int *);
+static int dev_ioctl_open(unsigned int, int *, dev_t, const char *, unsigned int);
+static int dev_ioctl_close(unsigned int, int);
+static int dev_ioctl_send_ready(unsigned int, int, unsigned int);
+static int dev_ioctl_send_fail(unsigned int, int, unsigned int, int);
+static int dev_ioctl_setpipefd(unsigned int, int, int);
+static int dev_ioctl_catatonic(unsigned int, int);
+static int dev_ioctl_timeout(unsigned int, int, time_t *);
+static int dev_ioctl_requestor(unsigned int, int, const char *, uid_t *, gid_t *);
+static int dev_ioctl_expire(unsigned int, int, const char *, unsigned int);
+static int dev_ioctl_askumount(unsigned int, int, unsigned int *);
+static int dev_ioctl_ismountpoint(unsigned int, int, const char *, unsigned int *);
+
+static int ioctl_protover(unsigned int, int, unsigned int *);
+static int ioctl_protosubver(unsigned int, int, unsigned int *);
+static int ioctl_open(unsigned int, int *, dev_t, const char *, unsigned int);
+static int ioctl_close(unsigned int, int);
+static int ioctl_send_ready(unsigned int, int, unsigned int);
+static int ioctl_send_fail(unsigned int, int, unsigned int, int);
+static int ioctl_catatonic(unsigned int, int);
+static int ioctl_timeout(unsigned int, int, time_t *);
+static int ioctl_expire(unsigned int, int, const char *, unsigned int);
+static int ioctl_askumount(unsigned int, int, unsigned int *);
+
+static struct ioctl_ops dev_ioctl_ops = {
+	.version	= dev_ioctl_version,
+	.protover	= dev_ioctl_protover,
+	.protosubver	= dev_ioctl_protosubver,
+	.open		= dev_ioctl_open,
+	.close		= dev_ioctl_close,
+	.send_ready	= dev_ioctl_send_ready,
+	.send_fail	= dev_ioctl_send_fail,
+	.setpipefd	= dev_ioctl_setpipefd,
+	.catatonic	= dev_ioctl_catatonic,
+	.timeout	= dev_ioctl_timeout,
+	.requestor	= dev_ioctl_requestor,
+	.expire		= dev_ioctl_expire,
+	.askumount	= dev_ioctl_askumount,
+	.ismountpoint	= dev_ioctl_ismountpoint
+};
+
+static struct ioctl_ops ioctl_ops = {
+	.version	= NULL,
+	.protover	= ioctl_protover,
+	.protosubver	= ioctl_protosubver,
+	.open		= ioctl_open,
+	.close		= ioctl_close,
+	.send_ready	= ioctl_send_ready,
+	.send_fail	= ioctl_send_fail,
+	.setpipefd	= NULL,
+	.catatonic	= ioctl_catatonic,
+	.timeout	= ioctl_timeout,
+	.requestor	= NULL,
+	.expire		= ioctl_expire,
+	.askumount	= ioctl_askumount,
+	.ismountpoint	= NULL
+};
+
+/*
+ * Allocate the control struct that holds the misc device file
+ * descriptor and operation despatcher table.
+ */
+void init_ioctl_ctl(void)
+{
+	int devfd;
+
+	if (ctl.ops)
+		return;
+
+	devfd = open(CONTROL_DEVICE, O_RDONLY);
+	if (devfd == -1)
+		ctl.ops = &ioctl_ops;
+	else {
+		int cl_flags = fcntl(devfd, F_GETFD, 0);
+		if (cl_flags != -1) {
+			cl_flags |= FD_CLOEXEC;
+			fcntl(devfd, F_SETFD, cl_flags);
+		}
+		ctl.devfd = devfd;
+		ctl.ops = &dev_ioctl_ops;
+	}
+	return;
+}
+
+void close_ioctl_ctl(void)
+{
+	if (ctl.devfd != -1) {
+		close(ctl.devfd);
+		ctl.devfd = -1;
+	}
+	ctl.ops = NULL;
+	return;
+}
+
+/* Return a pointer to the operations control struct */
+struct ioctl_ops *get_ioctl_ops(void)
+{
+	if (!ctl.ops)
+		init_ioctl_ctl();
+	return ctl.ops;
+}
+
+/* Get kenrel version of misc device code */
+static int dev_ioctl_version(unsigned int logopt,
+			     int ioctlfd, struct autofs_dev_ioctl *param)
+{
+	param->ioctlfd = ioctlfd;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_VERSION, param) == -1)
+		return -1;
+
+	return 0;
+}
+
+/* Get major version of autofs kernel module mount protocol */
+static int dev_ioctl_protover(unsigned int logopt,
+			      int ioctlfd, unsigned int *major)
+{
+	struct autofs_dev_ioctl param;
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_PROTOVER, &param) == -1)
+		return -1;
+
+	*major = param.arg1;
+
+	return 0;
+}
+
+static int ioctl_protover(unsigned int logopt,
+			  int ioctlfd, unsigned int *major)
+{
+	return ioctl(ioctlfd, AUTOFS_IOC_PROTOVER, major);
+}
+
+/* Get minor version of autofs kernel module mount protocol */
+static int dev_ioctl_protosubver(unsigned int logopt,
+				 int ioctlfd, unsigned int *minor)
+{
+	struct autofs_dev_ioctl param;
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_PROTOSUBVER, &param) == -1)
+		return -1;
+
+	*minor = param.arg1;
+
+	return 0;
+}
+
+static int ioctl_protosubver(unsigned int logopt,
+			     int ioctlfd, unsigned int *minor)
+{
+	return ioctl(ioctlfd, AUTOFS_IOC_PROTOSUBVER, minor);
+}
+
+/*
+ * Allocate a parameter struct for misc device ioctl used when
+ * opening an autofs mount point. Attach the path to the end
+ * of the struct. and lookup the device number if not given.
+ * Locating the device number relies on the mount option
+ * "dev=<device number>" being present in the autofs fs mount
+ * options.
+ */
+static struct autofs_dev_ioctl *alloc_dev_ioctl_open(const char *path, dev_t devid, unsigned int type)
+{
+	struct autofs_dev_ioctl *ioctl;
+	size_t size, p_len;
+	dev_t devno = devid;
+
+	if (!path)
+		return NULL;
+
+	if (devno == -1) {
+		char device[AUTOFS_DEVID_LEN];
+
+		if (!find_mnt_devid(_PROC_MOUNTS, path, device, type)) {
+			errno = ENOENT;
+			return NULL;
+		}
+		devno = strtoul(device, NULL, 0);
+	}
+
+	p_len = strlen(path);
+	size = sizeof(struct autofs_dev_ioctl) + p_len + 1;
+	ioctl = malloc(size);
+	if (!ioctl) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	init_autofs_dev_ioctl(ioctl);
+	ioctl->size = size;
+	memcpy(ioctl->path, path, p_len);
+	ioctl->path[p_len] = '\0';
+	ioctl->arg1 = devno;
+
+	return ioctl;
+}
+
+static void free_dev_ioctl_open(struct autofs_dev_ioctl *ioctl)
+{
+	free(ioctl);
+	return;
+}
+
+/*
+ * Allocate a parameter struct for misc device ioctl which includes
+ * a path. This is used when getting the last mount requestor uid
+ * and gid and when checking if a path within the autofs filesystem
+ * is a mount point. We add the path to the end of the struct.
+ */
+static struct autofs_dev_ioctl *alloc_dev_ioctl_path(int ioctlfd, const char *path)
+{
+	struct autofs_dev_ioctl *ioctl;
+	size_t size, p_len;
+
+	if (!path)
+		return NULL;
+
+	p_len = strlen(path);
+	size = sizeof(struct autofs_dev_ioctl) + p_len + 1;
+	ioctl = malloc(size);
+	if (!ioctl) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	init_autofs_dev_ioctl(ioctl);
+	ioctl->ioctlfd = ioctlfd;
+	ioctl->size = size;
+	memcpy(ioctl->path, path, p_len);
+	ioctl->path[p_len] = '\0';
+
+	return ioctl;
+}
+
+static void free_dev_ioctl_path(struct autofs_dev_ioctl *ioctl)
+{
+	free(ioctl);
+	return;
+}
+
+/* Get a file descriptor for control operations */
+static int dev_ioctl_open(unsigned int logopt, int *ioctlfd,
+			  dev_t devid, const char *path, unsigned int type)
+{
+	struct autofs_dev_ioctl *param;
+
+	*ioctlfd = -1;
+
+	param = alloc_dev_ioctl_open(path, devid, type);
+	if (!param)
+		return -1;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) == -1) {
+		int save_errno = errno;
+		free_dev_ioctl_open(param);
+		errno = save_errno;
+		return -1;
+	}
+
+	*ioctlfd = param->ioctlfd;
+
+	free_dev_ioctl_open(param);
+
+	return 0;
+}
+
+static int ioctl_open(unsigned int logopt, int *ioctlfd,
+		      dev_t devid, const char *path, unsigned int type)
+{
+	struct statfs sfs;
+	int save_errno, fd, cl_flags;
+
+	*ioctlfd = -1;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return -1;
+
+	cl_flags = fcntl(fd, F_GETFD, 0);
+	if (cl_flags != -1) {
+		cl_flags |= FD_CLOEXEC;
+		fcntl(fd, F_SETFD, cl_flags);
+	}
+
+	if (fstatfs(fd, &sfs) == -1) {
+		save_errno = errno;
+		goto err;
+	}
+
+	if (sfs.f_type != AUTOFS_SUPER_MAGIC) {
+		save_errno = ENOENT;
+		goto err;
+	}
+
+	*ioctlfd = fd;
+
+	return 0;
+err:
+	close(fd);
+	errno = save_errno;
+	return -1;
+}
+
+/* Close */
+static int dev_ioctl_close(unsigned int logopt, int ioctlfd)
+{
+	struct autofs_dev_ioctl param;
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_CLOSEMOUNT, &param) == -1)
+		return -1;
+
+	return 0;
+}
+
+static int ioctl_close(unsigned int logopt, int ioctlfd)
+{
+	return close(ioctlfd);
+}
+
+/* Send ready status for given token */
+static int dev_ioctl_send_ready(unsigned int logopt,
+				int ioctlfd, unsigned int token)
+{
+	struct autofs_dev_ioctl param;
+
+	if (token == 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	debug(logopt, "token = %d", token);
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+	param.arg1 = token;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_READY, &param) == -1) {
+		char *estr, buf[MAX_ERR_BUF];
+		int save_errno = errno;
+		estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		logerr("AUTOFS_DEV_IOCTL_READY: error %s", estr);
+		errno = save_errno;
+		return -1;
+	}
+	return 0;
+}
+
+static int ioctl_send_ready(unsigned int logopt,
+			    int ioctlfd, unsigned int token)
+{
+	if (token == 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	debug(logopt, "token = %d", token);
+
+	if (ioctl(ioctlfd, AUTOFS_IOC_READY, token) == -1) {
+		char *estr, buf[MAX_ERR_BUF];
+		int save_errno = errno;
+		estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		logerr("AUTOFS_IOC_READY: error %s", estr);
+		errno = save_errno;
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * Send ready status for given token.
+ *
+ * The device node ioctl implementation allows for sending a status
+ * of other than ENOENT, unlike the tradional interface.
+ */
+static int dev_ioctl_send_fail(unsigned int logopt,
+			       int ioctlfd, unsigned int token, int status)
+{
+	struct autofs_dev_ioctl param;
+
+	if (token == 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	debug(logopt, "token = %d", token);
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+	param.arg1 = token;
+	param.arg2 = status;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_FAIL, &param) == -1) {
+		char *estr, buf[MAX_ERR_BUF];
+		int save_errno = errno;
+		estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		logerr("AUTOFS_DEV_IOCTL_FAIL: error %s", estr);
+		errno = save_errno;
+		return -1;
+	}
+	return 0;
+}
+
+static int ioctl_send_fail(unsigned int logopt,
+			   int ioctlfd, unsigned int token, int status)
+{
+	if (token == 0) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	debug(logopt, "token = %d", token);
+
+	if (ioctl(ioctlfd, AUTOFS_IOC_FAIL, token) == -1) {
+		char *estr, buf[MAX_ERR_BUF];
+		int save_errno = errno;
+		estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		logerr("AUTOFS_IOC_FAIL: error %s", estr);
+		errno = save_errno;
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * Set the pipe fd for kernel communication.
+ *
+ * Normally this is set at mount using an option but if we
+ * are reconnecting to a busy mount then we need to use this
+ * to tell the autofs kernel module about the new pipe fd. In
+ * order to protect mounts against incorrectly setting the
+ * pipefd we also require that the autofs mount be catatonic.
+ *
+ * If successful this also sets the process group id used to
+ * identify the controlling process to the process group of
+ * the caller.
+ */
+static int dev_ioctl_setpipefd(unsigned int logopt, int ioctlfd, int pipefd)
+{
+	struct autofs_dev_ioctl param;
+
+	if (pipefd == -1) {
+		errno = EBADF;
+		return -1;
+	}
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+	param.arg1 = pipefd;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_SETPIPEFD, &param) == -1)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * Make the autofs mount point catatonic, no longer responsive to
+ * mount requests. Also closes the kernel pipe file descriptor.
+ */
+static int dev_ioctl_catatonic(unsigned int logopt, int ioctlfd)
+{
+	struct autofs_dev_ioctl param;
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_CATATONIC, &param) == -1)
+		return -1;
+
+	return 0;
+}
+
+static int ioctl_catatonic(unsigned int logopt, int ioctlfd)
+{
+	return ioctl(ioctlfd, AUTOFS_IOC_CATATONIC, 0);
+}
+
+/* Set the autofs mount timeout */
+static int dev_ioctl_timeout(unsigned int logopt, int ioctlfd, time_t *timeout)
+{
+	struct autofs_dev_ioctl param;
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+	param.arg1 = *timeout;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_TIMEOUT, &param) == -1)
+		return -1;
+
+	*timeout = param.arg1;
+
+	return 0;
+}
+
+static int ioctl_timeout(unsigned int logopt, int ioctlfd, time_t *timeout)
+{
+	return ioctl(ioctlfd, AUTOFS_IOC_SETTIMEOUT, timeout);
+}
+
+/*
+ * Get the uid and gid of the last request for the mountpoint, path.
+ *
+ * When reconstructing an autofs mount tree with active mounts
+ * we need to re-connect to mounts that may have used the original
+ * process uid and gid (or string variations of them) for mount
+ * lookups within the map entry.
+ */
+static int dev_ioctl_requestor(unsigned int logopt,
+			       int ioctlfd, const char *path,
+			       uid_t *uid, gid_t *gid)
+{
+	struct autofs_dev_ioctl *param;
+	int err;
+
+	if (!path)
+		errno = EINVAL;
+
+	*uid = -1;
+	*gid = -1;
+
+
+	param = alloc_dev_ioctl_path(ioctlfd, path);
+	if (!param) 
+		return -1;
+
+	err = ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_REQUESTOR, param);
+	if (err == -1) {
+		int save_errno = errno;
+		free_dev_ioctl_open(param);
+		errno = save_errno;
+		return -1;
+	}
+
+	*uid = param->arg1;
+	*gid = param->arg2;
+
+	free_dev_ioctl_path(param);
+
+	return 0;
+}
+
+/*
+ * Call repeatedly until it returns EAGAIN, meaning there's nothing
+ * more that can be done.
+ */
+static int expire(unsigned int logopt, int cmd, int fd,
+		  int ioctlfd, const char *path, void *arg)
+{
+	char buf[MAX_ERR_BUF];
+	struct stat st;
+	unsigned int retries;
+	unsigned int ret;
+
+	if (fstat(ioctlfd, &st) == -1) {
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+		debug(logopt, "fstat failed: %s", estr);
+		return 0;
+	}
+
+	retries = (count_mounts(logopt, path, st.st_dev) + 1) * EXPIRE_RETRIES;
+
+	while (retries--) {
+		struct timespec tm = {0, 100000000};
+
+		ret = ioctl(fd, cmd, arg);
+		if (ret == -1) {
+			/* Mount has gone away */
+			if (errno == EBADF || errno == EINVAL)
+				return 0;
+
+			/*
+			 *  Other than EAGAIN is an expire error so continue.
+			 *  Kernel will try the next mount for indirect
+			 *  mounts. For direct mounts it will just try the
+			 *  same mount again, limited by retries (ie. number
+			 *  of mounts directly under mount point, should
+			 *  always be one for direct mounts).
+			 */
+			if (errno == EAGAIN)
+				break;
+		}
+		nanosleep(&tm, NULL);
+	}
+
+	if (ctl.ops->askumount(logopt, ioctlfd, &ret))
+		return -1;
+
+	if (!ret)
+		return -1;
+
+	return 0;
+}
+
+static int dev_ioctl_expire(unsigned int logopt,
+			    int ioctlfd, const char *path, unsigned int when)
+{
+	struct autofs_dev_ioctl param;
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+	param.arg1 = when;
+
+	return expire(logopt, AUTOFS_DEV_IOCTL_EXPIRE,
+		      ctl.devfd, ioctlfd, path, (void *) &param);
+}
+
+static int ioctl_expire(unsigned int logopt,
+		        int ioctlfd, const char *path, unsigned int when)
+{
+	return expire(logopt, AUTOFS_IOC_EXPIRE_MULTI,
+		      ioctlfd, ioctlfd, path, (void *) &when);
+}
+
+/* Check if autofs mount point is in use */
+static int dev_ioctl_askumount(unsigned int logopt,
+			       int ioctlfd, unsigned int *busy)
+{
+	struct autofs_dev_ioctl param;
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = ioctlfd;
+
+	if (ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_ASKUMOUNT, &param) == -1)
+		return -1;
+
+	*busy = param.arg1;
+
+	return 0;
+}
+
+static int ioctl_askumount(unsigned int logopt,
+			   int ioctlfd, unsigned int *busy)
+{
+	return ioctl(ioctlfd, AUTOFS_IOC_ASKUMOUNT, busy);
+}
+
+/*
+ * Check if the given path is a mountpoint.
+ *
+ * The path is considered a mountpoint if it is itself a mountpoint
+ * or contains a mount, such as a multi-mount without a root mount.
+ * In addition, if the path is itself a mountpoint we return whether
+ * the mounted file system is an autofs filesystem or other file
+ * system.
+ */
+static int dev_ioctl_ismountpoint(unsigned int logopt,
+				  int ioctlfd, const char *path,
+				  unsigned int *mountpoint)
+{
+	struct autofs_dev_ioctl *param;
+	int err;
+
+	*mountpoint = 0;
+
+	if (!path) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	param = alloc_dev_ioctl_path(ioctlfd, path);
+	if (!param) 
+		return -1;
+
+	err = ioctl(ctl.devfd, AUTOFS_DEV_IOCTL_ISMOUNTPOINT, param);
+	if (err == -1) {
+		int save_errno = errno;
+		free_dev_ioctl_open(param);
+		errno = save_errno;
+		return -1;
+	}
+
+	if (param->arg1) {
+		*mountpoint = DEV_IOCTL_IS_MOUNTED;
+
+		if (param->arg2) {
+			if (param->arg2 == AUTOFS_SUPER_MAGIC)
+				*mountpoint |= DEV_IOCTL_IS_AUTOFS;
+			else
+				*mountpoint |= DEV_IOCTL_IS_OTHER;
+		}
+	}
+
+	free_dev_ioctl_path(param);
+
+	return 0;
+}
diff -up autofs-5.0.3/samples/rc.autofs.in.device-node-control autofs-5.0.3/samples/rc.autofs.in
--- autofs-5.0.3/samples/rc.autofs.in.device-node-control	2008-01-14 13:39:16.000000000 +0900
+++ autofs-5.0.3/samples/rc.autofs.in	2008-02-25 09:03:12.000000000 +0900
@@ -12,6 +12,7 @@
 DAEMON=@@sbindir@@/automount
 prog=`basename $DAEMON`
 MODULE="autofs4"
+DEVICE="autofs"
 confdir=@@autofsconfdir@@
 
 test -e $DAEMON || exit 0
@@ -47,6 +48,14 @@ function start() {
 		return 1
 	fi
 
+	# Check misc device
+	if [ -e "/proc/misc" ]; then
+		MINOR=`awk "/$DEVICE/ {print \\$1}" /proc/misc`
+		if [ -n "$MINOR" -a ! -c "/dev/$DEVICE" ]; then
+			mknod -m 0600 /dev/$DEVICE c 10 $MINOR
+		fi
+	fi
+
 	$prog $OPTIONS 
 	RETVAL=$?
 	if [ $RETVAL -eq 0 ] ; then