[CRIU] [RFC PATCH 4/5] autofs: restore procedure introduced

Stanislav Kinsburskiy skinsbursky at virtuozzo.com
Mon Nov 23 09:22:51 PST 2015


To restore AutoFS we will need a bunch of tricks:

1) Autofs have to be mounted by a specialized routine, because we need to
create a context (see below), remove "timeout=" from mount options and set
proper "pgrp=" and "fd=" options. Also we need to set mount point as catatonic,
if it was so, and adjust timeout. Which is done via mount point ioctls.
To isolate all these from generic code, "mount" callback in fstype operations
was introduced.

2) When all mounts restored, we need replace AutoFS kernel pipe end with the
right one (belonging to the autmount/systemd/etc process). This is performed
via "post_open" callback for pipes. If process belongs to one of automount
contexts, and pipe fd is the right one, then AutoFS mount is set ctatonic and
it's pgrp and fd are changes to right ones via /dev/autofs ioctl.

Note: This patch contains header file auto_dev-ioctl.h, which is used to
easy ioctl calls.

Signed-off-by: Stanislav Kinsburskiy <skinsbursky at virtuozzo.com>
---
 auto_dev-ioctl.h     |  229 +++++++++++++++++++++++++++++++++++++++
 include/proc_parse.h |    3 +
 mount.c              |  291 ++++++++++++++++++++++++++++++++++++++++++++++++--
 pipes.c              |   18 +++
 4 files changed, 527 insertions(+), 14 deletions(-)
 create mode 100644 auto_dev-ioctl.h

diff --git a/auto_dev-ioctl.h b/auto_dev-ioctl.h
new file mode 100644
index 0000000..850f39b
--- /dev/null
+++ b/auto_dev-ioctl.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2008 Red Hat, Inc. All rights reserved.
+ * Copyright 2008 Ian Kent <raven at 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/auto_fs.h>
+
+#ifdef __KERNEL__
+#include <linux/string.h>
+#else
+#include <string.h>
+#endif /* __KERNEL__ */
+
+#define AUTOFS_DEVICE_NAME		"autofs"
+
+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR	1
+#define AUTOFS_DEV_IOCTL_VERSION_MINOR	0
+
+#define AUTOFS_DEVID_LEN		16
+
+#define AUTOFS_DEV_IOCTL_SIZE		sizeof(struct autofs_dev_ioctl)
+
+/*
+ * An ioctl interface for autofs mount point control.
+ */
+
+struct args_protover {
+	__u32	version;
+};
+
+struct args_protosubver {
+	__u32	sub_version;
+};
+
+struct args_openmount {
+	__u32	devid;
+};
+
+struct args_ready {
+	__u32	token;
+};
+
+struct args_fail {
+	__u32	token;
+	__s32	status;
+};
+
+struct args_setpipefd {
+	__s32	pipefd;
+};
+
+struct args_timeout {
+	__u64	timeout;
+};
+
+struct args_requester {
+	__u32	uid;
+	__u32	gid;
+};
+
+struct args_expire {
+	__u32	how;
+};
+
+struct args_askumount {
+	__u32	may_umount;
+};
+
+struct args_ismountpoint {
+	union {
+		struct args_in {
+			__u32	type;
+		} in;
+		struct args_out {
+			__u32	devid;
+			__u32	magic;
+		} out;
+	};
+};
+
+/*
+ * All the ioctls use this structure.
+ * When sending a path size must account for the total length
+ * of the chunk of memory otherwise is is the size of the
+ * structure.
+ */
+
+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 */
+
+	/* Command parameters */
+
+	union {
+		struct args_protover		protover;
+		struct args_protosubver		protosubver;
+		struct args_openmount		openmount;
+		struct args_ready		ready;
+		struct args_fail		fail;
+		struct args_setpipefd		setpipefd;
+		struct args_timeout		timeout;
+		struct args_requester		requester;
+		struct args_expire		expire;
+		struct args_askumount		askumount;
+		struct args_ismountpoint	ismountpoint;
+	};
+
+	char path[0];
+};
+
+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in)
+{
+	memset(in, 0, sizeof(struct autofs_dev_ioctl));
+	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;
+	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,
+
+	/* Open mount ioctl fd */
+	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,
+
+	/* Activate/deactivate autofs mount */
+	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_REQUESTER_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_REQUESTER \
+	_IOWR(AUTOFS_IOCTL, \
+	      AUTOFS_DEV_IOCTL_REQUESTER_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 --git a/include/proc_parse.h b/include/proc_parse.h
index 123f5db..80fda92 100644
--- a/include/proc_parse.h
+++ b/include/proc_parse.h
@@ -106,6 +106,9 @@ struct fstype {
 	int (*dump)(struct mount_info *pm);
 	int (*restore)(struct mount_info *pm);
 	int (*parse)(struct mount_info *pm);
+	int (*mount)(const char *source, const char *target,
+		     const char *filesystemtype, unsigned long mountflags,
+		     const void *data);
 };
 
 struct vm_area_list;
diff --git a/mount.c b/mount.c
index c07d2d1..7a391ca 100644
--- a/mount.c
+++ b/mount.c
@@ -30,12 +30,18 @@
 
 #include "protobuf/mnt.pb-c.h"
 
+#include "auto_dev-ioctl.h"
+
 #define AUTODETECTED_MOUNT "CRIU:AUTOGENERATED"
 #define MS_PROPAGATE (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE | MS_SLAVE)
 
 #undef	LOG_PREFIX
 #define LOG_PREFIX "mnt: "
 
+typedef int (*mount_fn_t)(const char *source, const char *target,
+		const char *filesystemtype, unsigned long mountflags,
+		const void *data);
+
 int ext_mount_add(char *key, char *val)
 {
 	struct ext_mount *em;
@@ -1130,26 +1136,30 @@ out:
 	return -1;
 }
 
-static int attach_option(struct mount_info *pm, char *opt)
+static char *attach_token(char *string, const char *token, char separator)
 {
 	char *buf;
 	int len, olen;
 
-	len = strlen(pm->options);
-	olen = strlen(opt);
-	buf = xrealloc(pm->options, len + olen + 2);
+	len = strlen(string);
+	olen = strlen(token);
+	buf = xrealloc(string, len + olen + 2);
 	if (buf == NULL)
-		return -1;
+		return NULL;
 
-	if (len && buf[len - 1] != ',') {
-		buf[len] = ',';
+	if (len && buf[len - 1] != separator) {
+		buf[len] = separator;
 		len++;
 	}
 
-	memcpy(buf + len, opt, olen + 1);
-	pm->options = buf;
+	memcpy(buf + len, token, olen + 1);
+	return buf;
+}
 
-	return 0;
+static int attach_option(struct mount_info *pm, char *opt)
+{
+	pm->options  = attach_token(pm->options, opt, ',');
+	return (pm->options) ? 0 : -1;
 }
 
 /* Is it mounted w or w/o the newinstance option */
@@ -1405,8 +1415,6 @@ static int autofs_fixup_options(struct mount_info *new)
 	for (i = 0; i < nr_opts; i++) {
 		char *opt = options[i];
 
-		if (!strncmp(opt, "timeout=", strlen("timeout=")))
-			continue;
 		if (!strncmp(opt, "pgrp=", strlen("pgrp="))) {
 			int real_pid, virt_pid = -1;
 			struct pstree_item *item;
@@ -1460,6 +1468,7 @@ static int autofs_dump(struct mount_info *pm)
 	int pipe_fd, pipe_size, steal_pipe[2];
 	ssize_t bytes;
 	int err;
+	struct mount_info *t;
 
 	/* TODO: we are called wthin proper pid namespace. We can't use blobal
 	 * proc and have to mount proc somewhere */
@@ -1512,6 +1521,13 @@ static int autofs_dump(struct mount_info *pm)
 	if (err)
 		goto close_steal;
 
+	list_for_each_entry(t, &pm->mnt_bind, mnt_bind) {
+		xfree(t->options);
+		t->options = xstrdup(pm->options);
+		if (!t->options)
+			goto close_steal;
+	}
+
 	err = 0;
 
 close_steal:
@@ -1522,6 +1538,252 @@ close_pipe:
 	return err;
 }
 
+struct autofs_info_s {
+	int pipe_fd;
+	const char *mnt_path;
+	struct autofs_info_s *next;
+};
+
+struct autofs_service_s {
+	int pgrp;
+	struct autofs_info_s *mnt_info;
+	struct autofs_service_s *next;
+} *automount;
+
+int autofs_fixup_owner(struct autofs_info_s *info)
+{
+	int autofs_fd, mnt_fd, err = -1;
+	struct autofs_dev_ioctl param;
+
+	autofs_fd = open("/dev/autofs", O_RDONLY);
+	if (autofs_fd == -1) {
+		pr_perror("failed to open /dev/autofs");
+		return -1;
+	}
+
+	mnt_fd = open(info->mnt_path, O_RDONLY | O_DIRECTORY);
+	if (mnt_fd == -1) {
+		pr_perror("failed to open %s", info->mnt_path);
+		goto close_fd;
+	}
+
+	init_autofs_dev_ioctl(&param);
+	err = ioctl(mnt_fd, AUTOFS_IOC_CATATONIC, 0);
+	if (err == -1) {
+		pr_perror("failed to set mount point catatonic");
+		goto close_mnt_fd;
+
+	}
+
+	init_autofs_dev_ioctl(&param);
+	param.ioctlfd = mnt_fd;
+	param.setpipefd.pipefd = info->pipe_fd;
+
+	err = ioctl(autofs_fd, AUTOFS_DEV_IOCTL_SETPIPEFD, &param);
+	if (err == -1) {
+		pr_perror("failed to send ioctl to /dev/autofs");
+		goto close_mnt_fd;
+
+	}
+
+	pr_info("autofs mount \"%s\" owner restored: pgrp=%d, fd=%d\n",
+			info->mnt_path, getpid(), mnt_fd);
+
+	err = 0;
+
+close_fd:
+	close(autofs_fd);
+close_mnt_fd:
+	close(mnt_fd);
+	return err;
+}
+
+static struct autofs_service_s *find_automount_service(int pgrp)
+{
+	struct autofs_service_s *service = automount;
+
+	while (service) {
+		if (service->pgrp == pgrp)
+			return service;
+		service = service->next;
+	}
+	return NULL;
+}
+
+struct autofs_info_s *find_automount_info(int pgrp, int pipe_fd)
+{
+	struct autofs_service_s *service;
+	struct autofs_info_s *info;
+
+	service = find_automount_service(pgrp);
+	if (!service)
+		return NULL;
+
+	info = service->mnt_info;
+
+	do {
+		if (info->pipe_fd == pipe_fd)
+			return info;
+		info = info->next;
+	} while (info);
+	return NULL;
+}
+
+static int autofs_add_info(int pgrp, int fd, const char *mnt_path)
+{
+	struct autofs_service_s *service = NULL;
+	struct autofs_info_s *info;
+
+	service = find_automount_service(pgrp);
+	if (!service) {
+		pr_info("adding new autofs service with pid %d\n", pgrp);
+		service = xzalloc(sizeof(*service));
+		if (!service)
+			return -1;
+		service->pgrp = pgrp;
+		service->next = automount;
+		automount = service;
+	}
+
+	info = xzalloc(sizeof(*info));
+	if (!info)
+		return -1;
+
+	info->pipe_fd = fd;
+	info->mnt_path = xstrdup(mnt_path);
+	if (!info->mnt_path)
+		return -1;
+	info->next = service->mnt_info;
+
+	service->mnt_info = info;
+
+	return 0;
+}
+
+static int autofs_set_timeout(const char *mnt_path, int timeout)
+{
+	int mnt_fd;
+	struct autofs_dev_ioctl param;
+
+	mnt_fd = open(mnt_path, O_RDONLY | O_DIRECTORY);
+	if (mnt_fd == -1) {
+		pr_perror("failed to open %s", mnt_path);
+		return -1;
+	}
+
+	init_autofs_dev_ioctl(&param);
+
+	if (ioctl(mnt_fd, AUTOFS_IOC_SETTIMEOUT, &timeout)) {
+		pr_perror("failed to set %s catatonic\n", mnt_path);
+		return -1;
+	}
+
+	close(mnt_fd);
+	return 0;
+}
+
+static int autofs_make_catatonic(const char *mnt_path)
+{
+	int mnt_fd;
+	struct autofs_dev_ioctl param;
+
+	mnt_fd = open(mnt_path, O_RDONLY | O_DIRECTORY);
+	if (mnt_fd == -1) {
+		pr_perror("failed to open %s", mnt_path);
+		return -1;
+	}
+
+	init_autofs_dev_ioctl(&param);
+
+	if (ioctl(mnt_fd, AUTOFS_IOC_CATATONIC, 0)) {
+		pr_perror("failed to set %s catatonic\n", mnt_path);
+		return -1;
+	}
+
+	close(mnt_fd);
+	return 0;
+}
+
+static int autofs_mount(const char *source, const char *target,
+		const char *filesystemtype, unsigned long mountflags,
+		const void *data)
+{
+	char *mount_ops, **options;
+	int nr_opts, i, err = -1;
+	int pgrp = 0, fd = INT_MIN, timeout = 0;
+	int automount_pipe[2];
+
+	mount_ops = xstrdup(data);
+	if (!mount_ops)
+		return -1;
+
+	split(mount_ops, ',', &options, &nr_opts);
+	if (!options)
+		return -1;
+
+	memset(mount_ops, 0, strlen(mount_ops));
+
+	if (pipe(automount_pipe) < 0) {
+		pr_perror("Can't create pipe");
+		return -1;
+	}
+
+	sprintf(mount_ops, "fd=%d,pgrp=%d", automount_pipe[1], getpgrp());
+
+	for (i = 0; i < nr_opts; i++) {
+		char *opt = options[i];
+
+		if (!strncmp(opt, "timeout=", strlen("timeout="))) {
+			timeout = atoi(opt + strlen("timeout="));
+			continue;
+		}
+
+		if (!strncmp(opt, "pgrp=", strlen("pgrp="))) {
+			pgrp = atoi(opt + strlen("pgrp="));
+			continue;
+		}
+
+		if (!strncmp(opt, "fd=", strlen("fd="))) {
+			fd = atoi(opt + strlen("fd="));
+			continue;
+		}
+
+		strcat(mount_ops, ",");
+		strcat(mount_ops, opt);
+	}
+
+	if (fd == INT_MIN) {
+		pr_err("failed to find \"fd=\" option\n");
+		goto close_pipe;
+	}
+
+	if (pgrp == 0) {
+		pr_err("failed to find \"pgrp=\" option\n");
+		goto close_pipe;
+	}
+
+	pr_info("autofs mount: target: %s, pgrp: %d, fd: %d\n", target, pgrp, fd);
+	pr_info("autofs mount: adjusted options: \"%s\"\n", mount_ops);
+
+	if (mount(source, target, filesystemtype, mountflags, mount_ops) < 0) {
+		pr_perror("Can't mount at %s", target);
+		goto close_pipe;
+	}
+
+	if (autofs_set_timeout(target, timeout))
+		goto close_pipe;
+
+	if (fd == -1)
+		err = autofs_make_catatonic(target);
+	else
+		err = autofs_add_info(pgrp, fd, target);
+
+close_pipe:
+	close(automount_pipe[1]);
+	close(automount_pipe[0]);
+	return err;
+}
+
 static struct fstype fstypes[32] = {
 	{
 		.name = "unsupported",
@@ -1594,7 +1856,7 @@ static struct fstype fstypes[32] = {
 		.name = "autofs",
 		.code = FSTYPE__AUTOFS,
 		.dump = autofs_dump,
-		.restore = always_fail,
+		.mount = autofs_mount,
 	},
 };
 
@@ -2096,6 +2358,7 @@ static int do_new_mount(struct mount_info *mi)
 	char *src;
 	struct fstype *tp = mi->fstype;
 	bool remount_ro = (tp->restore && mi->sb_flags & MS_RDONLY);
+	mount_fn_t do_mount = (tp->mount) ? tp->mount: mount;
 
 	src = resolve_source(mi);
 	if (!src)
@@ -2110,7 +2373,7 @@ static int do_new_mount(struct mount_info *mi)
 	if (remount_ro)
 		sflags &= ~MS_RDONLY;
 
-	if (mount(src, mi->mountpoint, tp->name, sflags, mi->options) < 0) {
+	if (do_mount(src, mi->mountpoint, tp->name, sflags, mi->options) < 0) {
 		pr_perror("Can't mount at %s", mi->mountpoint);
 		return -1;
 	}
diff --git a/pipes.c b/pipes.c
index d4f70db..bda1c1d 100644
--- a/pipes.c
+++ b/pipes.c
@@ -380,11 +380,29 @@ static int want_transport(FdinfoEntry *fe, struct file_desc *d)
 	return !pi->create;
 }
 
+struct autofs_info_s;
+struct autofs_info_s *find_automount_info(int pgrp, int pipe_fd);
+int autofs_fixup_owner(struct autofs_info_s *info);
+
+static int pipe_post_open(struct file_desc *d, int fd)
+{
+	struct autofs_info_s *info;
+
+	pr_info("%s: searching info by pid %d and fd %d\n", __func__, getpid(), fd);
+	info = find_automount_info(getpid(), fd);
+	if (!info)
+		return 0;
+
+	pr_info("%s: found info by pid %d and fd %d\n", __func__, getpid(), fd);
+	return autofs_fixup_owner(info);
+}
+
 static struct file_desc_ops pipe_desc_ops = {
 	.type		= FD_TYPES__PIPE,
 	.open		= open_pipe,
 	.want_transport	= want_transport,
 	.name		= pipe_d_name,
+	.post_open	= pipe_post_open,
 };
 
 static int collect_one_pipe(void *o, ProtobufCMessage *base)



More information about the CRIU mailing list