[CRIU] [PATCH 5/5] tty: Use regular files engine to save paths to the peers, v3

Cyrill Gorcunov gorcunov at openvz.org
Mon Oct 13 08:51:52 PDT 2014


Currently we're using predefined format for master/slave pty peers:
masters are always /dev/ptmx, while slaves are /dev/pts/$index,
where $index is the peer number.

While fitting most of distros this is not always correct and slave peers
might be mounted to an arbitrary place, so that we need to somehow
carry paths with ourself in image.

Instead of bloating current tty image lets use regular file engine instead
and on checkpoint stage save a path to the link in regfiles set, then on
restore simply fetch it from the image.

Such approach will help in future when we need to support multiple
instances of devpts filesystem.

To support backward compatibility with images where no regfile
records are present we generate new "fake" one on the fly in
pty_alloc_fake_reg_d() helper.

Because of the need to restore dead slave peers and restore of
the controlling terminal we need to generate that named "fake
inverted" ptys in pty_alloc_fake() helper: in particular if
we need to open dead slave peer we generate fake master peer,
open it and the close out. Almost the same situation in
restoring contolling terminal -- we get master peer, generate
appropriate fake slave tty_info object, open it, manipulate,
the close out and free.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 files-reg.c |   8 ++
 tty.c       | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 238 insertions(+), 28 deletions(-)

diff --git a/files-reg.c b/files-reg.c
index 98c71e9f9096..86b8783f3ff2 100644
--- a/files-reg.c
+++ b/files-reg.c
@@ -664,6 +664,14 @@ static int check_path_remap(char *rpath, int plen, const struct fd_parms *parms,
 		}
 
 		return 0;
+	} else if (parms->fs_type == DEVPTS_SUPER_MAGIC) {
+		/*
+		 * Devpts devices/files are generated by the
+		 * kernel itself so we should not try to generate
+		 * any kind of ghost files here even if file is
+		 * no longer exist.
+		 */
+		return 0;
 	}
 
 	if (ost->st_nlink == 0)
diff --git a/tty.c b/tty.c
index 003fc2b4a9a6..e96317af4518 100644
--- a/tty.c
+++ b/tty.c
@@ -27,6 +27,7 @@
 #include "util-pie.h"
 #include "proc_parse.h"
 #include "file-ids.h"
+#include "files-reg.h"
 
 #include "protobuf.h"
 #include "protobuf/tty.pb-c.h"
@@ -80,6 +81,8 @@ struct tty_info {
 	struct list_head		list;
 	struct file_desc		d;
 
+	struct file_desc		*reg_d;
+
 	TtyFileEntry			*tfe;
 	TtyInfoEntry			*tie;
 
@@ -121,6 +124,8 @@ static DECLARE_BITMAP(tty_active_pairs, (MAX_TTYS << 1));
  */
 static mutex_t *tty_mutex;
 
+static bool tty_is_master(struct tty_info *info);
+
 int prepare_shared_tty(void)
 {
 	tty_mutex = shmalloc(sizeof(*tty_mutex));
@@ -242,18 +247,182 @@ static int tty_test_and_set(int bit, unsigned long *bitmap)
 	return ret;
 }
 
-static int pty_open_ptmx_index(int flags, int index)
+/*
+ * Generate a regular file object in case if such is missed
+ * in the image file, ie obsolete interface has been used on
+ * checkpoint.
+ */
+static struct file_desc *pty_alloc_fake_reg_d(struct tty_info *info, bool add)
+{
+	TtyFileEntry *tfe = info->tfe;
+	struct reg_file_info *r;
+
+	r = xzalloc(sizeof(*r) + sizeof(*r->rfe) + 64);
+	if (!r)
+		return NULL;
+
+	r->rfe = (void *)r + sizeof(*r);
+	reg_file_entry__init(r->rfe);
+
+	r->rfe->name = (void *)r + sizeof(*r) + sizeof(*r->rfe);
+	if (tty_is_master(info))
+		strcpy(r->rfe->name, "/dev/ptmx");
+	else
+		snprintf(r->rfe->name, 64, "/dev/pts/%u",
+			 info->tie->pty->index);
+
+	if (add)
+		file_desc_add(&r->d, tfe->id, NULL);
+	else {
+		r->d.id = tfe->id;
+		INIT_LIST_HEAD(&r->d.fd_info_head);
+		INIT_HLIST_NODE(&r->d.hash);
+	}
+
+	r->rfe->id	= tfe->id;
+	r->rfe->flags	= tfe->flags;
+	r->rfe->fown	= tfe->fown;
+	r->path		= &r->rfe->name[1];
+
+	return &r->d;
+}
+
+static struct tty_info *pty_alloc_fake(struct tty_info *info, int type)
+{
+	struct reg_file_info *new, *orig;
+	struct tty_info *p;
+
+	pr_debug("Allocating fake for %#x (reg_d %p)\n",
+		 info->tfe->id, info->reg_d);
+
+	BUG_ON(!info->reg_d);
+	orig = container_of(info->reg_d, struct reg_file_info, d);
+
+	p = xzalloc(sizeof(*p)		+
+		    sizeof(*p->tfe)	+
+		    sizeof(*p->tie)	+
+		    sizeof(*p->tie->pty));
+	if (!p)
+		return NULL;
+
+	p->tfe = (void *)p + sizeof(*p);
+	tty_file_entry__init(p->tfe);
+
+	p->tie = (void *)p->tfe + sizeof(*p->tfe);
+	tty_info_entry__init(p->tie);
+
+	p->tie->pty = (void *)p->tie + sizeof(*p->tie);
+	tty_pty_entry__init(p->tie->pty);
+
+	p->tfe->id	= info->tfe->id;
+	p->tfe->flags	= info->tfe->flags;
+	p->tfe->fown	= info->tfe->fown;
+
+	p->tie->pty->index = info->tie->pty->index;
+
+	p->type		= type;
+	p->reg_d = pty_alloc_fake_reg_d(p, false);
+	if (!p->reg_d) {
+		xfree(p);
+		return NULL;
+	}
+
+	new = container_of(p->reg_d, struct reg_file_info, d);
+
+	if ((type == TTY_TYPE_PTM && tty_is_master(info)) ||
+	    (type == TTY_TYPE_PTS && !tty_is_master(info))) {
+		new->path = xstrdup(orig->path);
+		new->rfe->name = &new->path[1];
+	} else {
+		char *pos = strrchr(orig->rfe->name, '/');
+		size_t len = strlen(orig->rfe->name) + 1;
+		size_t slash_at = pos - orig->rfe->name;
+		char *inverted_path = xmalloc(len + 32);
+
+		BUG_ON(!pos || !inverted_path);
+
+		memcpy(inverted_path, orig->rfe->name, slash_at + 1);
+		if (type == TTY_TYPE_PTM)
+			strcat(inverted_path, "ptmx");
+		else {
+			if (slash_at >= 4 && strcmp(&inverted_path[slash_at - 4], "pts"))
+				snprintf(&inverted_path[slash_at + 1], 10, "pts/%u", p->tie->pty->index);
+			else
+				snprintf(&inverted_path[slash_at + 1], 10, "%u", p->tie->pty->index);
+		}
+
+		new->rfe->name = inverted_path;
+		new->path = &inverted_path[1];
+	}
+
+	return p;
+}
+
+#define pty_alloc_fake_master(info)	pty_alloc_fake(info, TTY_TYPE_PTM)
+#define pty_alloc_fake_slave(info)	pty_alloc_fake(info, TTY_TYPE_PTS)
+
+static void pty_free_fake(struct tty_info **info)
+{
+	if (info) {
+		struct tty_info *p = *info;
+		struct reg_file_info *r;
+
+		r = container_of(p->reg_d, struct reg_file_info, d);
+		xfree(r->rfe->name);
+		xfree(p->reg_d);
+		xfree(p);
+		*info = NULL;
+	}
+}
+
+struct pty_open_args {
+	struct tty_info	*info;
+	unsigned int	flags;
+};
+
+static int do_open_pty_reg(int ns_root_fd, struct reg_file_info *rfi, void *v)
+{
+	struct pty_open_args *args = v;
+	int fd;
+
+	fd = openat(ns_root_fd, rfi->path, args->flags);
+	if (fd < 0) {
+		pr_err("Can't open pty %#x [%s]\n", args->info->tfe->id, rfi->path);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int open_pty_reg(struct tty_info *info, unsigned int flags)
+{
+	struct pty_open_args args = {
+		.info	= info,
+		.flags	= flags,
+	};
+
+	return open_path(info->reg_d, do_open_pty_reg, &args);
+}
+
+static char *path_from_reg(struct file_desc *d)
+{
+	struct reg_file_info *rfi = container_of(d, struct reg_file_info, d);
+	return rfi->path;
+}
+
+static int pty_open_ptmx_index(struct tty_info *info, int flags)
 {
 	int fds[32], i, ret = -1, cur_idx;
+	int index = info->tie->pty->index;
 
 	memset(fds, 0xff, sizeof(fds));
 
 	mutex_lock(tty_mutex);
 
 	for (i = 0; i < ARRAY_SIZE(fds); i++) {
-		fds[i] = open(PTMX_PATH, flags);
+		fds[i] = open_pty_reg(info, flags);
 		if (fds[i] < 0) {
-			pr_perror("Can't open %s", PTMX_PATH);
+			pr_perror("Can't open %s", path_from_reg(info->reg_d));
 			break;
 		}
 
@@ -343,17 +512,19 @@ static int tty_set_prgp(int fd, int group)
 static int tty_restore_ctl_terminal(struct file_desc *d, int fd)
 {
 	struct tty_info *info = container_of(d, struct tty_info, d);
+	struct tty_info *fake;
 	int slave, ret = -1;
-	char pts_name[64];
 
 	if (!is_service_fd(fd, CTL_TTY_OFF))
 		return 0;
 
-	snprintf(pts_name, sizeof(pts_name), PTS_FMT, info->tie->pty->index);
-	slave = open(pts_name, O_RDONLY);
+	fake = pty_alloc_fake_slave(info);
+	if (!fake)
+		goto err;
+	slave = open_pty_reg(fake, O_RDONLY);
 	if (slave < 0) {
-		pr_perror("Can't open %s", pts_name);
-		return -1;
+		pr_perror("Can't open %s", path_from_reg(fake->reg_d));
+		goto err;
 	}
 
 	pr_info("Restore session %d by %d tty (index %d)\n",
@@ -365,8 +536,9 @@ static int tty_restore_ctl_terminal(struct file_desc *d, int fd)
 		ret = tty_set_prgp(slave, info->tie->pgrp);
 
 	close(slave);
+err:
 	close(fd);
-
+	pty_free_fake(&fake);
 	return ret;
 }
 
@@ -467,9 +639,6 @@ static int pty_open_slaves(struct tty_info *info)
 	int sock = -1, fd = -1, ret = -1;
 	struct fdinfo_list_entry *fle;
 	struct tty_info *slave;
-	char pts_name[64];
-
-	snprintf(pts_name, sizeof(pts_name), PTS_FMT, info->tie->pty->index);
 
 	sock = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (sock < 0) {
@@ -480,9 +649,9 @@ static int pty_open_slaves(struct tty_info *info)
 	list_for_each_entry(slave, &info->sibling, sibling) {
 		BUG_ON(tty_is_master(slave));
 
-		fd = open(pts_name, slave->tfe->flags | O_NOCTTY);
+		fd = open_pty_reg(slave, slave->tfe->flags | O_NOCTTY);
 		if (fd < 0) {
-			pr_perror("Can't open slave %s", pts_name);
+			pr_perror("Can't open slave %s", path_from_reg(slave->reg_d));
 			goto err;
 		}
 
@@ -492,7 +661,7 @@ static int pty_open_slaves(struct tty_info *info)
 		fle = file_master(&slave->d);
 
 		pr_debug("send slave %#x fd %d connected on %s (pid %d)\n",
-			 slave->tfe->id, fd, pts_name, fle->pid);
+			 slave->tfe->id, fd, path_from_reg(slave->reg_d), fle->pid);
 
 		if (send_fd_to_peer(fd, fle, sock)) {
 			pr_perror("Can't send file descriptor");
@@ -534,6 +703,7 @@ static int receive_tty(struct tty_info *info)
 static int pty_open_unpaired_slave(struct file_desc *d, struct tty_info *slave)
 {
 	int master = -1, ret = -1, fd = -1;
+	struct tty_info *fake = NULL;
 
 	/*
 	 * We may have 2 cases here: the slave either need to
@@ -549,25 +719,23 @@ static int pty_open_unpaired_slave(struct file_desc *d, struct tty_info *slave)
 		pr_info("Migrated slave peer %x -> to fd %d\n",
 			slave->tfe->id, fd);
 	} else {
-		char pts_name[64];
-
-		snprintf(pts_name, sizeof(pts_name), PTS_FMT, slave->tie->pty->index);
-
-		master = pty_open_ptmx_index(O_RDONLY, slave->tie->pty->index);
+		fake = pty_alloc_fake_master(slave);
+		if (!fake)
+			goto err;
+		master = pty_open_ptmx_index(fake, O_RDONLY);
 		if (master < 0) {
 			pr_perror("Can't open fale %x (index %d)",
 				  slave->tfe->id, slave->tie->pty->index);
-			return -1;
+			goto err;
 		}
 
 		unlock_pty(master);
 
-		fd = open(pts_name, slave->tfe->flags | O_NOCTTY);
+		fd = open_pty_reg(slave, slave->tfe->flags | O_NOCTTY);
 		if (fd < 0) {
-			pr_perror("Can't open slave %s", pts_name);
+			pr_perror("Can't open slave %s", path_from_reg(slave->reg_d));
 			goto err;
 		}
-
 	}
 
 	if (restore_tty_params(fd, slave))
@@ -606,6 +774,7 @@ static int pty_open_unpaired_slave(struct file_desc *d, struct tty_info *slave)
 err:
 	close_safe(&master);
 	close_safe(&fd);
+	pty_free_fake(&fake);
 	return ret;
 }
 
@@ -613,7 +782,7 @@ static int pty_open_ptmx(struct tty_info *info)
 {
 	int master = -1;
 
-	master = pty_open_ptmx_index(info->tfe->flags, info->tie->pty->index);
+	master = pty_open_ptmx_index(info, info->tfe->flags);
 	if (master < 0) {
 		pr_perror("Can't open %x (index %d)",
 			  info->tfe->id, info->tie->pty->index);
@@ -622,9 +791,6 @@ static int pty_open_ptmx(struct tty_info *info)
 
 	unlock_pty(master);
 
-	if (rst_file_params(master, info->tfe->fown, info->tfe->flags))
-		goto err;
-
 	if (restore_tty_params(master, info))
 		goto err;
 
@@ -967,6 +1133,20 @@ struct collect_image_info tty_info_cinfo = {
 	.flags		= COLLECT_OPTIONAL,
 };
 
+static void pty_undelete_reg(struct tty_info *info)
+{
+	struct reg_file_info *rfi = container_of(info->reg_d, struct reg_file_info, d);
+	const char postfix[] = " (deleted)";
+	const size_t plen = strlen(postfix);
+	size_t len = strlen(rfi->path);
+
+	if (len < plen ||
+	    strcmp(postfix, &rfi->path[len - plen]))
+		return;
+
+	rfi->path[len - plen] = '\0';
+}
+
 static int collect_one_tty(void *obj, ProtobufCMessage *msg)
 {
 	struct tty_info *info = obj;
@@ -988,6 +1168,25 @@ static int collect_one_tty(void *obj, ProtobufCMessage *msg)
 	if (verify_info(info))
 		return -1;
 
+	info->reg_d = find_file_desc_raw(FD_TYPES__REG, info->tfe->id);
+	if (!info->reg_d) {
+		info->reg_d = pty_alloc_fake_reg_d(info, true);
+		if (!info->reg_d) {
+			pr_err("Can't generate new reg_d descriptor for id %#x\n",
+			       info->tfe->id);
+			return -1;
+		}
+	}
+
+	/*
+	 * Slave PTYs without master might have
+	 * been postfixed as deleted, strip this
+	 * part, we will need a valid path on their
+	 * opening.
+	 */
+	if (info->type == TTY_TYPE_PTS)
+		pty_undelete_reg(info);
+
 	/*
 	 * The tty peers which have no @termios are hung up,
 	 * so don't mark them as active, we create them with
@@ -1181,6 +1380,9 @@ static int dump_one_pty(int lfd, u32 id, const struct fd_parms *p)
 
 	pr_info("Dumping tty %d with id %#x\n", lfd, id);
 
+	if (dump_one_reg_file(lfd, id, p))
+		return -1;
+
 	type = tty_type(major(p->stat.st_rdev), minor(p->stat.st_rdev));
 	index = parse_pty_index(id, lfd, p, type);
 	if (index < 0)
-- 
1.9.3



More information about the CRIU mailing list