All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] cr: pty: don't use required_id, change ptmx_open()
@ 2009-09-02 23:31 Serge E. Hallyn
       [not found] ` <20090902233111.GA27125-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
  0 siblings, 1 reply; 4+ messages in thread
From: Serge E. Hallyn @ 2009-09-02 23:31 UTC (permalink / raw)
  To: Oren Laadan; +Cc: Linux Containers

Ok so here is the patch to do unix98 pty restore by making
invasive changes to ptmx_open() instead of using
task_struct->required_id to pass an index.  Patch generated
against the cr tree with the original pty patches, as I
assume that will be easier to read.

Break ptmx_open() into a ptmx_create() which is used both
by ptmx_open itself, and fs/devpts/inode.c:open_create_pty().
The latter function is used only during sys_restart() when
a unix98 pty must be restored.

Either with or without this patch, the pty test at
	git://git.sr71.net/~hallyn/cr_tests.git
under the pty/ directory.  The question then is which is
a more likely approach to be acceptable upstream.  The
task->required_id approach is imo 'hacky', but it actually
does a much better job of re-using existing helpers, and
therefore is more maintainable.  So part of me DOES prefer
the original required_id approach.

This is purely RFC, so comments very much appreciated.

Signed-off-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
--
 checkpoint/sys.c           |    8 ---
 drivers/char/pty.c         |   16 +++---
 drivers/char/tty_io.c      |  112 +++++++++++++++++++++++++++++++++++----------
 fs/devpts/inode.c          |   55 ++++++++++++++++++++--
 include/linux/checkpoint.h |    2 
 include/linux/devpts_fs.h  |    2 
 include/linux/sched.h      |    1 
 7 files changed, 151 insertions(+), 45 deletions(-)

diff --git a/checkpoint/sys.c b/checkpoint/sys.c
index 3db18f7..b1fdbd7 100644
--- a/checkpoint/sys.c
+++ b/checkpoint/sys.c
@@ -339,14 +339,6 @@ SYSCALL_DEFINE3(restart, pid_t, pid, int, fd, unsigned long, flags)
 	return ret;
 }
 
-static int __init checkpoint_init(void)
-{
-	init_task.required_id = CKPT_REQUIRED_NONE;
-	return 0;
-}
-__initcall(checkpoint_init);
-
-
 /* 'ckpt_debug_level' controls the verbosity level of c/r code */
 #ifdef CONFIG_CHECKPOINT_DEBUG
 
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index afdab5e..79e86e4 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -28,6 +28,8 @@
 #include <linux/uaccess.h>
 #include <linux/bitops.h>
 #include <linux/devpts_fs.h>
+#include <linux/file.h>
+#include <linux/mount.h>
 
 #include <asm/system.h>
 
@@ -629,20 +631,13 @@ static const struct tty_operations pty_unix98_ops = {
  *		allocated_ptys_lock handles the list of free pty numbers
  */
 
-static int __ptmx_open(struct inode *inode, struct file *filp)
+int ptmx_create(struct inode *inode, struct file *filp, int index)
 {
 	struct tty_struct *tty;
 	int retval;
-	int index = -1;
 
 	nonseekable_open(inode, filp);
 
-#ifdef CONFIG_CHECKPOINT
-	/* when restarting, request specific index */
-	if (current->flags & PF_RESTARTING)
-		index = (int) current->required_id;
-#endif
-	/* find a device that is not in use. */
 	index = devpts_new_index(inode, index);
 	if (index < 0)
 		return index;
@@ -675,6 +670,11 @@ out:
 	return retval;
 }
 
+static int __ptmx_open(struct inode *inode, struct file *filp)
+{
+	return ptmx_create(inode, filp, UNSPECIFIED_PTY_INDEX);
+}
+
 static int ptmx_open(struct inode *inode, struct file *filp)
 {
 	int ret;
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index b8f8d79..d27ec36 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -79,6 +79,7 @@
 #include <linux/devpts_fs.h>
 #include <linux/file.h>
 #include <linux/fdtable.h>
+#include <linux/namei.h>
 #include <linux/console.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
@@ -2982,13 +2983,97 @@ static int restore_tty_ldisc(struct ckpt_ctx *ctx,
 	return 0;
 }
 
-#define CKPT_PTMX_PATH  "/dev/ptmx"
+#define PTMXNAME "ptmx"
+struct tty_struct *pty_open_by_master_in_ns(struct ckpt_ctx *ctx,
+		struct ckpt_hdr_tty *h, struct file *fdir)
+{
+	struct tty_struct *tty;
+	struct file *file;
+	int ret;
+	struct qstr ptmxname;
+	struct dentry *ptmxdentry;
+	struct nameidata nd;
+
+	ret = file_permission(fdir, MAY_EXEC);
+	if (ret)
+		return ERR_PTR(ret);
+	nd.path = fdir->f_path;
+	path_get(&nd.path);
+	ptmxname.name = PTMXNAME;
+	ptmxname.len = strlen(ptmxname.name);
+	mutex_lock(&fdir->f_dentry->d_inode->i_mutex);
+	ptmxdentry = d_hash_and_lookup(fdir->f_dentry, &ptmxname);
+	mutex_unlock(&fdir->f_dentry->d_inode->i_mutex);
+	if (!ptmxdentry) {
+		path_put(&nd.path);
+		return ERR_PTR(-ENOENT);
+	}
+
+	/* should get mode from the file handle... */
+	file = alloc_file(nd.path.mnt, ptmxdentry, FMODE_READ|FMODE_WRITE, &tty_fops);
+	path_put(&nd.path);
+	if (!file) {
+		dput(ptmxdentry);
+		return ERR_PTR(-EINVAL);
+	}
+
+	ret = security_dentry_open(file, current_cred());
+	if (ret) {
+		fput(file);
+		return ERR_PTR(ret);
+	}
+	/* check write access perms to file */
+
+	lock_kernel();  /* tty_open does it... */
+	ret = open_create_pty(fdir, h->index, file);
+	unlock_kernel();
+	if (ret) {
+		fput(file);
+		return ERR_PTR(ret);
+	}
+	ckpt_debug("master file %p (obj %d)\n", file, h->file_objref);
+
+	/*
+	 * Add file to objhash to ensure proper cleanup later
+	 * (it isn't referenced elsewhere). Use h->file_objref
+	 * which was explicitly during checkpoint for this.
+	 */
+	ret = ckpt_obj_insert(ctx, file, h->file_objref, CKPT_OBJ_FILE);
+	if (ret < 0) {
+		fput(file);
+		return ERR_PTR(ret);
+	}
+
+	tty = file->private_data;
+	fput(file);   /* objhash took a ref */
+
+	return tty;
+}
+
+struct tty_struct *pty_open_by_master(struct ckpt_ctx *ctx,
+		struct ckpt_hdr_tty *h)
+{
+	struct file *fdir;
+	struct tty_struct *tty;
+	/*
+	 * we need to pick a way to specify which devpts
+	 * mountpoint to use.  For now, we'll just use
+	 * whatever /dev/ptmx points to
+	 */
+	fdir = filp_open("/dev/pts", O_RDONLY, 0);
+	if (IS_ERR(fdir))
+		return ERR_PTR(PTR_ERR(fdir));
+
+	tty = pty_open_by_master_in_ns(ctx, h, fdir);
+	fput(fdir);
+
+	return tty;
+}
 
 static struct tty_struct *do_restore_tty(struct ckpt_ctx *ctx)
 {
 	struct ckpt_hdr_tty *h;
 	struct tty_struct *tty = ERR_PTR(-EINVAL);
-	struct file *file = NULL;
 	int master, ret;
 
 	h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_TTY);
@@ -3025,28 +3110,9 @@ static struct tty_struct *do_restore_tty(struct ckpt_ctx *ctx)
 	 * specific (and probably called via tty_operations).
 	 */
 	if (master) {
-		current->required_id = h->index;
-		file = filp_open(CKPT_PTMX_PATH, O_RDWR | O_NOCTTY, 0);
-		current->required_id = CKPT_REQUIRED_NONE;
-		ckpt_debug("master file %p (obj %d)\n", file, h->file_objref);
-		if (IS_ERR(file)) {
-			tty = (struct tty_struct *) file;
-			goto out;
-		}
-
-		/*
-		 * Add file to objhash to ensure proper cleanup later
-		 * (it isn't referenced elsewhere). Use h->file_objref
-		 * which was explicitly during checkpoint for this.
-		 */
-		ret = ckpt_obj_insert(ctx, file, h->file_objref, CKPT_OBJ_FILE);
-		if (ret < 0) {
-			tty = ERR_PTR(ret);
-			fput(file);
+		tty = pty_open_by_master(ctx, h);
+		if (IS_ERR(tty))
 			goto out;
-		}
-
-		tty = file->private_data;
 	} else {
 		tty = ckpt_obj_fetch(ctx, h->link_objref, CKPT_OBJ_TTY);
 		if (IS_ERR(tty))
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 8921726..f76083f 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -14,6 +14,9 @@
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
 #include <linux/tty.h>
@@ -431,13 +434,18 @@ static struct file_system_type devpts_fs_type = {
 /*
  * The normal naming convention is simply /dev/pts/<number>; this conforms
  * to the System V naming convention
+ *
+ * @ptmx_inode: inode of the /dev/ptmx file - each pty namepace has its
+ * own
+ * @req_idx: requested specific index (i.e. 5 for /dev/pts/5).  If -1,
+ * then return first available.
  */
 
 int devpts_new_index(struct inode *ptmx_inode, int req_idx)
 {
 	struct super_block *sb = pts_sb_from_inode(ptmx_inode);
 	struct pts_fs_info *fsi = DEVPTS_SB(sb);
-	int index;
+	int index = req_idx;
 	int ida_ret;
 
 retry:
@@ -445,7 +453,8 @@ retry:
 		return -ENOMEM;
 
 	mutex_lock(&allocated_ptys_lock);
-	index = (req_idx >= 0 ? req_idx : 0);
+	if (index == UNSPECIFIED_PTY_INDEX)
+		index = 0;
 	ida_ret = ida_get_new_above(&fsi->allocated_ptys, index, &index);
 	if (ida_ret < 0) {
 		mutex_unlock(&allocated_ptys_lock);
@@ -454,7 +463,7 @@ retry:
 		return -EIO;
 	}
 
-	if (req_idx >= 0 && index != req_idx) {
+	if (req_idx != UNSPECIFIED_PTY_INDEX && index != req_idx) {
 		ida_remove(&fsi->allocated_ptys, index);
 		mutex_unlock(&allocated_ptys_lock);
 		return -EBUSY;
@@ -557,6 +566,46 @@ out:
 	mutex_unlock(&root->d_inode->i_mutex);
 }
 
+struct inode *get_ptmx_inode(struct super_block *sb)
+{
+	struct pts_fs_info *fsi = DEVPTS_SB(sb);
+	struct dentry *dentry = fsi->ptmx_dentry;
+	struct inode *inode = dentry->d_inode;
+
+	return igrab(inode);
+}
+
+/* defined in drivers/char/pty.c */
+extern int ptmx_create(struct inode *inode, struct file *filp, int index);
+
+/*
+ * fn caller (in restart code) has checked for the rights on the
+ * part of the userspace task to open the ptmx file.
+ * @pdir: an open file handle to the /dev/pts directory, which we
+ * will use to determine the devpts namespace, and to confirm that
+ * the resulting file is in the right directory.
+ * @file is a file for /dev/ptmx
+ */
+int open_create_pty(struct file *fdir, int idx, struct file *file)
+{
+	struct inode *ptmx_ino;
+	struct dentry *pdir = fdir->f_dentry;
+	struct super_block *sb;
+	int ret;
+
+	if (!pdir || !pdir->d_inode)
+		return -EINVAL;
+
+	sb = pts_sb_from_inode(pdir->d_inode);
+	ptmx_ino = get_ptmx_inode(sb);
+	if (!ptmx_ino)
+		return -EINVAL;
+
+	ret = ptmx_create(ptmx_ino, file, idx);
+	iput(ptmx_ino);
+	return ret;
+}
+
 static int __init init_devpts_fs(void)
 {
 	int err = register_filesystem(&devpts_fs_type);
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index 5e90ef9..0cbe8c4 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -46,8 +46,6 @@
 #define CHECKPOINT_USER_FLAGS		CHECKPOINT_SUBTREE
 #define RESTART_USER_FLAGS		(RESTART_TASKSELF | RESTART_FROZEN)
 
-#define CKPT_REQUIRED_NONE  ((unsigned long) -1L)
-
 extern void exit_checkpoint(struct task_struct *tsk);
 
 extern int ckpt_kwrite(struct ckpt_ctx *ctx, void *buf, int count);
diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h
index 14948ee..a15d23c 100644
--- a/include/linux/devpts_fs.h
+++ b/include/linux/devpts_fs.h
@@ -15,10 +15,12 @@
 
 #include <linux/errno.h>
 
+#define UNSPECIFIED_PTY_INDEX -1
 #ifdef CONFIG_UNIX98_PTYS
 
 int devpts_new_index(struct inode *ptmx_inode, int req_idx);
 void devpts_kill_index(struct inode *ptmx_inode, int idx);
+int open_create_pty(struct file *fdir, int idx, struct file *file);
 /* mknod in devpts */
 int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty);
 /* get tty structure */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index f6f1350..0e67de7 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1481,7 +1481,6 @@ struct task_struct {
 #endif /* CONFIG_TRACING */
 #ifdef CONFIG_CHECKPOINT
 	struct ckpt_ctx *checkpoint_ctx;
-	unsigned long required_id;
 #endif
 };

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RFC] cr: pty: don't use required_id, change ptmx_open()
       [not found] ` <20090902233111.GA27125-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
@ 2009-09-08 18:14   ` Oren Laadan
       [not found]     ` <4AA69EEB.9040706-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
  0 siblings, 1 reply; 4+ messages in thread
From: Oren Laadan @ 2009-09-08 18:14 UTC (permalink / raw)
  To: Serge E. Hallyn; +Cc: Linux Containers


I assume this will be the preferred approach. See comments
below.

Serge E. Hallyn wrote:
> Ok so here is the patch to do unix98 pty restore by making
> invasive changes to ptmx_open() instead of using
> task_struct->required_id to pass an index.  Patch generated
> against the cr tree with the original pty patches, as I
> assume that will be easier to read.
> 
> Break ptmx_open() into a ptmx_create() which is used both
> by ptmx_open itself, and fs/devpts/inode.c:open_create_pty().
> The latter function is used only during sys_restart() when
> a unix98 pty must be restored.
> 
> Either with or without this patch, the pty test at
> 	git://git.sr71.net/~hallyn/cr_tests.git
> under the pty/ directory.  The question then is which is
> a more likely approach to be acceptable upstream.  The
> task->required_id approach is imo 'hacky', but it actually
> does a much better job of re-using existing helpers, and
> therefore is more maintainable.  So part of me DOES prefer
> the original required_id approach.
> 
> This is purely RFC, so comments very much appreciated.
> 
> Signed-off-by: Serge Hallyn <serue-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
> --
>  checkpoint/sys.c           |    8 ---
>  drivers/char/pty.c         |   16 +++---
>  drivers/char/tty_io.c      |  112 +++++++++++++++++++++++++++++++++++----------
>  fs/devpts/inode.c          |   55 ++++++++++++++++++++--
>  include/linux/checkpoint.h |    2 
>  include/linux/devpts_fs.h  |    2 
>  include/linux/sched.h      |    1 
>  7 files changed, 151 insertions(+), 45 deletions(-)
> 
> diff --git a/checkpoint/sys.c b/checkpoint/sys.c
> index 3db18f7..b1fdbd7 100644
> --- a/checkpoint/sys.c
> +++ b/checkpoint/sys.c
> @@ -339,14 +339,6 @@ SYSCALL_DEFINE3(restart, pid_t, pid, int, fd, unsigned long, flags)
>  	return ret;
>  }
>  
> -static int __init checkpoint_init(void)
> -{
> -	init_task.required_id = CKPT_REQUIRED_NONE;
> -	return 0;
> -}
> -__initcall(checkpoint_init);
> -
> -
>  /* 'ckpt_debug_level' controls the verbosity level of c/r code */
>  #ifdef CONFIG_CHECKPOINT_DEBUG
>  
> diff --git a/drivers/char/pty.c b/drivers/char/pty.c
> index afdab5e..79e86e4 100644
> --- a/drivers/char/pty.c
> +++ b/drivers/char/pty.c
> @@ -28,6 +28,8 @@
>  #include <linux/uaccess.h>
>  #include <linux/bitops.h>
>  #include <linux/devpts_fs.h>
> +#include <linux/file.h>
> +#include <linux/mount.h>
>  
>  #include <asm/system.h>
>  
> @@ -629,20 +631,13 @@ static const struct tty_operations pty_unix98_ops = {
>   *		allocated_ptys_lock handles the list of free pty numbers
>   */
>  
> -static int __ptmx_open(struct inode *inode, struct file *filp)
> +int ptmx_create(struct inode *inode, struct file *filp, int index)
>  {
>  	struct tty_struct *tty;
>  	int retval;
> -	int index = -1;
>  
>  	nonseekable_open(inode, filp);
>  
> -#ifdef CONFIG_CHECKPOINT
> -	/* when restarting, request specific index */
> -	if (current->flags & PF_RESTARTING)
> -		index = (int) current->required_id;
> -#endif
> -	/* find a device that is not in use. */
>  	index = devpts_new_index(inode, index);
>  	if (index < 0)
>  		return index;
> @@ -675,6 +670,11 @@ out:
>  	return retval;
>  }
>  
> +static int __ptmx_open(struct inode *inode, struct file *filp)
> +{
> +	return ptmx_create(inode, filp, UNSPECIFIED_PTY_INDEX);
> +}
> +
>  static int ptmx_open(struct inode *inode, struct file *filp)
>  {
>  	int ret;
> diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
> index b8f8d79..d27ec36 100644
> --- a/drivers/char/tty_io.c
> +++ b/drivers/char/tty_io.c
> @@ -79,6 +79,7 @@
>  #include <linux/devpts_fs.h>
>  #include <linux/file.h>
>  #include <linux/fdtable.h>
> +#include <linux/namei.h>
>  #include <linux/console.h>
>  #include <linux/timer.h>
>  #include <linux/ctype.h>
> @@ -2982,13 +2983,97 @@ static int restore_tty_ldisc(struct ckpt_ctx *ctx,
>  	return 0;
>  }
>  
> -#define CKPT_PTMX_PATH  "/dev/ptmx"
> +#define PTMXNAME "ptmx"
> +struct tty_struct *pty_open_by_master_in_ns(struct ckpt_ctx *ctx,
> +		struct ckpt_hdr_tty *h, struct file *fdir)

I assume you split this to two functions so that in the future you
could feed a different "base" directory (for a different devpts mount).
To simplify this patch, perhaps we can defer it until we do that ?

Nit: s/pty_open_by_master_in_ns/pty_open_by_index/ ?

Instead of passing ckpt_hdr_tty, maybe instead make it return the
file pointer, and pass the desired index instead:
	struct file *pty_open_by_index(char *path, int index)

By letting the caller decide on the path, and what to do with the
resulting file pointer, I hope the c/r code will be more separate
and therefore easier to follow ?

> +{
> +	struct tty_struct *tty;
> +	struct file *file;
> +	int ret;
> +	struct qstr ptmxname;
> +	struct dentry *ptmxdentry;
> +	struct nameidata nd;
> +
> +	ret = file_permission(fdir, MAY_EXEC);
> +	if (ret)
> +		return ERR_PTR(ret);
> +	nd.path = fdir->f_path;
> +	path_get(&nd.path);
> +	ptmxname.name = PTMXNAME;
> +	ptmxname.len = strlen(ptmxname.name);
> +	mutex_lock(&fdir->f_dentry->d_inode->i_mutex);
> +	ptmxdentry = d_hash_and_lookup(fdir->f_dentry, &ptmxname);
> +	mutex_unlock(&fdir->f_dentry->d_inode->i_mutex);
> +	if (!ptmxdentry) {
> +		path_put(&nd.path);
> +		return ERR_PTR(-ENOENT);
> +	}

Need to verify that the resulting dentry points to a ptmx device.
(With current code, user can arrange for something else in /dev/pts)

> +
> +	/* should get mode from the file handle... */

Should be ok, since restore_file_common() should restore the mode...

> +	file = alloc_file(nd.path.mnt, ptmxdentry, FMODE_READ|FMODE_WRITE, &tty_fops);
> +	path_put(&nd.path);
> +	if (!file) {
> +		dput(ptmxdentry);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	ret = security_dentry_open(file, current_cred());
> +	if (ret) {
> +		fput(file);
> +		return ERR_PTR(ret);
> +	}
> +	/* check write access perms to file */

I bet the above can be done by reusing lookup and __dentry_open()
functions from fs/open.c and fs/namei.c ...

> +
> +	lock_kernel();  /* tty_open does it... */
> +	ret = open_create_pty(fdir, h->index, file);
> +	unlock_kernel();
> +	if (ret) {
> +		fput(file);
> +		return ERR_PTR(ret);
> +	}
> +	ckpt_debug("master file %p (obj %d)\n", file, h->file_objref);
> +
> +	/*
> +	 * Add file to objhash to ensure proper cleanup later
> +	 * (it isn't referenced elsewhere). Use h->file_objref
> +	 * which was explicitly during checkpoint for this.
> +	 */
> +	ret = ckpt_obj_insert(ctx, file, h->file_objref, CKPT_OBJ_FILE);
> +	if (ret < 0) {
> +		fput(file);
> +		return ERR_PTR(ret);
> +	}

Can do this in caller. So

> +
> +	tty = file->private_data;
> +	fput(file);   /* objhash took a ref */
> +
> +	return tty;
> +}
> +
> +struct tty_struct *pty_open_by_master(struct ckpt_ctx *ctx,
> +		struct ckpt_hdr_tty *h)
> +{
> +	struct file *fdir;
> +	struct tty_struct *tty;
> +	/*
> +	 * we need to pick a way to specify which devpts
> +	 * mountpoint to use.  For now, we'll just use
> +	 * whatever /dev/ptmx points to
> +	 */
> +	fdir = filp_open("/dev/pts", O_RDONLY, 0);
> +	if (IS_ERR(fdir))
> +		return ERR_PTR(PTR_ERR(fdir));

Does this mean that to restore a subtree on a system without
devpts-ns it won't work ?  (no /dev/pts/ptmx)

[...]

Oren.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC] cr: pty: don't use required_id, change ptmx_open()
       [not found]     ` <4AA69EEB.9040706-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
@ 2009-09-08 19:51       ` Serge E. Hallyn
  2009-09-10  2:28       ` Serge E. Hallyn
  1 sibling, 0 replies; 4+ messages in thread
From: Serge E. Hallyn @ 2009-09-08 19:51 UTC (permalink / raw)
  To: Oren Laadan; +Cc: Linux Containers

Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
> > +	file = alloc_file(nd.path.mnt, ptmxdentry, FMODE_READ|FMODE_WRITE, &tty_fops);
> > +	path_put(&nd.path);
> > +	if (!file) {
> > +		dput(ptmxdentry);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	ret = security_dentry_open(file, current_cred());
> > +	if (ret) {
> > +		fput(file);
> > +		return ERR_PTR(ret);
> > +	}
> > +	/* check write access perms to file */
> 
> I bet the above can be done by reusing lookup and __dentry_open()
> functions from fs/open.c and fs/namei.c ...

With lookup_create()?  Hmm, yeah - I think there are reasons I can't do
that, but I'll just try and see where it gets us.

> > +
> > +	lock_kernel();  /* tty_open does it... */
> > +	ret = open_create_pty(fdir, h->index, file);
> > +	unlock_kernel();
> > +	if (ret) {
> > +		fput(file);
> > +		return ERR_PTR(ret);
> > +	}
> > +	ckpt_debug("master file %p (obj %d)\n", file, h->file_objref);
> > +
> > +	/*
> > +	 * Add file to objhash to ensure proper cleanup later
> > +	 * (it isn't referenced elsewhere). Use h->file_objref
> > +	 * which was explicitly during checkpoint for this.
> > +	 */
> > +	ret = ckpt_obj_insert(ctx, file, h->file_objref, CKPT_OBJ_FILE);
> > +	if (ret < 0) {
> > +		fput(file);
> > +		return ERR_PTR(ret);
> > +	}
> 
> Can do this in caller. So
> 
> > +
> > +	tty = file->private_data;
> > +	fput(file);   /* objhash took a ref */
> > +
> > +	return tty;
> > +}
> > +
> > +struct tty_struct *pty_open_by_master(struct ckpt_ctx *ctx,
> > +		struct ckpt_hdr_tty *h)
> > +{
> > +	struct file *fdir;
> > +	struct tty_struct *tty;
> > +	/*
> > +	 * we need to pick a way to specify which devpts
> > +	 * mountpoint to use.  For now, we'll just use
> > +	 * whatever /dev/ptmx points to
> > +	 */
> > +	fdir = filp_open("/dev/pts", O_RDONLY, 0);
> > +	if (IS_ERR(fdir))
> > +		return ERR_PTR(PTR_ERR(fdir));
> 
> Does this mean that to restore a subtree on a system without
> devpts-ns it won't work ?  (no /dev/pts/ptmx)

Yes.  I figured once we settle on devpts ns handling, we can
trivially special-case the DEVPTS_MULTIPLE_INSTANCES=n case
to use /dev/ptmx.

Since we're punting on devpts namespaces anyway I guess the
pty_open_by_master() can just open /dev/ptmx directly for
now.

Or, maybe we're better off handling the namespaces.  Because
without doing that, we are hard-coding pathnames in the kernel
left and right, and that's Bad(tm).

thanks,
-serge

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC] cr: pty: don't use required_id, change ptmx_open()
       [not found]     ` <4AA69EEB.9040706-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
  2009-09-08 19:51       ` Serge E. Hallyn
@ 2009-09-10  2:28       ` Serge E. Hallyn
  1 sibling, 0 replies; 4+ messages in thread
From: Serge E. Hallyn @ 2009-09-10  2:28 UTC (permalink / raw)
  To: Oren Laadan; +Cc: Linux Containers

Quoting Oren Laadan (orenl-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org):
> > +	file = alloc_file(nd.path.mnt, ptmxdentry, FMODE_READ|FMODE_WRITE, &tty_fops);
> > +	path_put(&nd.path);
> > +	if (!file) {
> > +		dput(ptmxdentry);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	ret = security_dentry_open(file, current_cred());
> > +	if (ret) {
> > +		fput(file);
> > +		return ERR_PTR(ret);
> > +	}
> > +	/* check write access perms to file */
> 
> I bet the above can be done by reusing lookup and __dentry_open()
> functions from fs/open.c and fs/namei.c ...

All right, so I tried the below patch (lots of debugging left in) but
the master end doesn't seem to end up right.  After restart, pty_write()
continues to succeed on the re-opened /dev/pts/N, but no reads happen
on the master.

What I'm doing is passing a new flag LOOKUP_CALLER_OPEN to filp_open(),
to tell it not to actually call the fops->open() routine (ptmx_open).
Then I call ptmx_create() (the renamed ptmx_open) by hand.  Seems like
it certainly ought to be working...  But I'm getting nowhere figuring
out what's happening to the major file, so putting it aside for a bit.

Following is the patch against the tree with Oren's original pty patches.

-serge

---
 checkpoint/sys.c           |    8 -----
 drivers/char/pty.c         |   43 ++++++++++++++++++++--------
 drivers/char/tty_io.c      |   69 ++++++++++++++++++++++++++++++++++++---------
 fs/devpts/inode.c          |   60 +++++++++++++++++++++++++++++++++++++--
 fs/open.c                  |   19 +++++++++++-
 include/linux/checkpoint.h |    2 -
 include/linux/devpts_fs.h  |    3 +
 include/linux/namei.h      |    1 
 include/linux/sched.h      |    1 
 9 files changed, 167 insertions(+), 39 deletions(-)

diff --git a/checkpoint/sys.c b/checkpoint/sys.c
index 3db18f7..b1fdbd7 100644
--- a/checkpoint/sys.c
+++ b/checkpoint/sys.c
@@ -339,14 +339,6 @@ SYSCALL_DEFINE3(restart, pid_t, pid, int, fd, unsigned long, flags)
 	return ret;
 }
 
-static int __init checkpoint_init(void)
-{
-	init_task.required_id = CKPT_REQUIRED_NONE;
-	return 0;
-}
-__initcall(checkpoint_init);
-
-
 /* 'ckpt_debug_level' controls the verbosity level of c/r code */
 #ifdef CONFIG_CHECKPOINT_DEBUG
 
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index afdab5e..6ea8afb 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -28,6 +28,8 @@
 #include <linux/uaccess.h>
 #include <linux/bitops.h>
 #include <linux/devpts_fs.h>
+#include <linux/file.h>
+#include <linux/mount.h>
 
 #include <asm/system.h>
 
@@ -114,6 +116,13 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf,
 {
 	struct tty_struct *to = tty->link;
 	int c;
+	int debug = (tty->index > 0);
+
+	if (debug)
+		printk(KERN_NOTICE "%s: called on %s idx %d buf .%s. count %d\n",
+			__func__,
+			tty->driver->subtype == PTY_TYPE_MASTER ? "master" : "slave",
+			tty->index, buf, count);
 
 	if (tty->stopped)
 		return 0;
@@ -122,6 +131,8 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf,
 	   big deal */
 
 	c = pty_space(to);
+	if (debug)
+		printk(KERN_NOTICE "%s: space was %d\n", __func__, c);
 	if (c > count)
 		c = count;
 	if (c > 0) {
@@ -131,6 +142,8 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf,
 		tty_flip_buffer_push(to);
 		tty_wakeup(tty);
 	}
+	if (debug)
+		printk(KERN_NOTICE "%s: returning %d\n", __func__, c);
 	return c;
 }
 
@@ -196,13 +209,21 @@ static int pty_open(struct tty_struct *tty, struct file *filp)
 	if (!tty || !tty->link)
 		goto out;
 
+	printk(KERN_NOTICE "%s: tty is %pS link %pS\n",
+		__func__, tty, tty->link);
 	retval = -EIO;
-	if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+	if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
+		printk(KERN_NOTICE "%s: other end closed\n", __func__);
 		goto out;
-	if (test_bit(TTY_PTY_LOCK, &tty->link->flags))
+	}
+	if (test_bit(TTY_PTY_LOCK, &tty->link->flags)) {
+		printk(KERN_NOTICE "%s: ptylock\n", __func__);
 		goto out;
-	if (tty->link->count != 1)
+	}
+	if (tty->link->count != 1) {
+		printk(KERN_NOTICE "%s: count is %d (!=1)\n", __func__, tty->link->count);
 		goto out;
+	}
 
 	clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
 	set_bit(TTY_THROTTLED, &tty->flags);
@@ -629,27 +650,22 @@ static const struct tty_operations pty_unix98_ops = {
  *		allocated_ptys_lock handles the list of free pty numbers
  */
 
-static int __ptmx_open(struct inode *inode, struct file *filp)
+int ptmx_create(struct inode *inode, struct file *filp, int index)
 {
 	struct tty_struct *tty;
 	int retval;
-	int index = -1;
 
 	nonseekable_open(inode, filp);
 
-#ifdef CONFIG_CHECKPOINT
-	/* when restarting, request specific index */
-	if (current->flags & PF_RESTARTING)
-		index = (int) current->required_id;
-#endif
-	/* find a device that is not in use. */
 	index = devpts_new_index(inode, index);
+	printk(KERN_NOTICE "%s: index %d\n", __func__, index);
 	if (index < 0)
 		return index;
 
 	mutex_lock(&tty_mutex);
 	tty = tty_init_dev(ptm_driver, index, 1);
 	mutex_unlock(&tty_mutex);
+	printk(KERN_NOTICE "%s: tty is %pS\n", __func__, tty);
 
 	if (IS_ERR(tty)) {
 		retval = PTR_ERR(tty);
@@ -675,6 +691,11 @@ out:
 	return retval;
 }
 
+static int __ptmx_open(struct inode *inode, struct file *filp)
+{
+	return ptmx_create(inode, filp, UNSPECIFIED_PTY_INDEX);
+}
+
 static int ptmx_open(struct inode *inode, struct file *filp)
 {
 	int ret;
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index b8f8d79..d363c0e 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -79,6 +79,7 @@
 #include <linux/devpts_fs.h>
 #include <linux/file.h>
 #include <linux/fdtable.h>
+#include <linux/namei.h>
 #include <linux/console.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
@@ -891,8 +892,19 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
 	struct tty_struct *tty;
 	struct inode *inode;
 	struct tty_ldisc *ld;
+	int debug = 0;
 
 	tty = (struct tty_struct *)file->private_data;
+	if ((tty->driver->subtype == PTY_TYPE_MASTER ||
+			tty->driver->subtype == PTY_TYPE_SLAVE) &&
+			tty->index > 0)
+		debug = 1;
+
+	if (debug)
+		printk(KERN_NOTICE "%s: called on %s idx %d count %ld\n",
+			__func__,
+			tty->driver->subtype == PTY_TYPE_MASTER ? "master" : "slave",
+			tty->index, count);
 	inode = file->f_path.dentry->d_inode;
 	if (tty_paranoia_check(tty, inode, "tty_read"))
 		return -EIO;
@@ -909,6 +921,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
 	tty_ldisc_deref(ld);
 	if (i > 0)
 		inode->i_atime = current_fs_time(inode->i_sb);
+	if (debug)
+		printk(KERN_NOTICE "%s: returning %d\n", __func__, i);
 	return i;
 }
 
@@ -2907,6 +2921,7 @@ struct file *tty_file_restore(struct ckpt_ctx *ctx, struct ckpt_hdr_file *ptr)
 		clear_bit(TTY_PTY_LOCK, &tty->link->flags);
 		file = filp_open(slavepath, O_RDWR | O_NOCTTY, 0);
 		ckpt_debug("slave file %p (idnex %d)\n", file, tty->index);
+		ckpt_debug("slavelock was %d\n", slavelock);
 		if (IS_ERR(file))
 			return file;
 		if (slavelock)
@@ -2982,13 +2997,43 @@ static int restore_tty_ldisc(struct ckpt_ctx *ctx,
 	return 0;
 }
 
-#define CKPT_PTMX_PATH  "/dev/ptmx"
+struct file *pty_open_by_index(char *ptmxpath, int index)
+{
+	struct file *ptmxfile;
+	int ret;
+
+	/*
+	 * we need to pick a way to specify which devpts
+	 * mountpoint to use.  For now, we'll just use
+	 * whatever /dev/ptmx points to
+	 */
+	ptmxfile = filp_open(ptmxpath, O_RDWR|O_NOCTTY|LOOKUP_CALLER_OPEN, 0);
+	if (IS_ERR(ptmxfile))
+		return ptmxfile;
+
+	if (!file_is_ptmx(ptmxfile)) {
+		printk(KERN_NOTICE "%s: dentry was not ptmx\n", __func__);
+		fput(ptmxfile);
+		return ERR_PTR(-ENOENT);
+	}
+
+	lock_kernel();
+	ret = open_create_pty(ptmxfile, index);
+	unlock_kernel();
+	printk(KERN_NOTICE "%s: open_create_pty returned %d\n", __func__, ret);
+	if (ret) {
+		fput(ptmxfile);
+		return ERR_PTR(ret);
+	}
+	printk(KERN_NOTICE "%s: done\n", __func__);
+
+	return ptmxfile;
+}
 
 static struct tty_struct *do_restore_tty(struct ckpt_ctx *ctx)
 {
 	struct ckpt_hdr_tty *h;
 	struct tty_struct *tty = ERR_PTR(-EINVAL);
-	struct file *file = NULL;
 	int master, ret;
 
 	h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_TTY);
@@ -3025,30 +3070,25 @@ static struct tty_struct *do_restore_tty(struct ckpt_ctx *ctx)
 	 * specific (and probably called via tty_operations).
 	 */
 	if (master) {
-		current->required_id = h->index;
-		file = filp_open(CKPT_PTMX_PATH, O_RDWR | O_NOCTTY, 0);
-		current->required_id = CKPT_REQUIRED_NONE;
-		ckpt_debug("master file %p (obj %d)\n", file, h->file_objref);
-		if (IS_ERR(file)) {
-			tty = (struct tty_struct *) file;
+		struct file *f = pty_open_by_index("/dev/ptmx", h->index);
+		if (IS_ERR(f))
 			goto out;
-		}
+		tty = f->private_data;
 
 		/*
 		 * Add file to objhash to ensure proper cleanup later
 		 * (it isn't referenced elsewhere). Use h->file_objref
 		 * which was explicitly during checkpoint for this.
 		 */
-		ret = ckpt_obj_insert(ctx, file, h->file_objref, CKPT_OBJ_FILE);
+		ret = ckpt_obj_insert(ctx, f, h->file_objref, CKPT_OBJ_FILE);
+		fput(f);  /* objhash took an extra reference */
 		if (ret < 0) {
 			tty = ERR_PTR(ret);
-			fput(file);
 			goto out;
 		}
-
-		tty = file->private_data;
 	} else {
 		tty = ckpt_obj_fetch(ctx, h->link_objref, CKPT_OBJ_TTY);
+		ckpt_debug("restoring slave");
 		if (IS_ERR(tty))
 			goto out;
 		tty = tty->link;
@@ -3077,15 +3117,18 @@ static struct tty_struct *do_restore_tty(struct ckpt_ctx *ctx)
 	tty->winsize.ws_xpixel = h->winsize.ws_xpixel;
 	mutex_unlock(&tty->termios_mutex);
 
+	ckpt_debug("after mutex_unlock");
 	if (test_bit(TTY_HUPPED, (unsigned long *) &h->flags))
 		tty_vhangup(tty);
 	else {
 		ret = restore_tty_ldisc(ctx, tty, h);
+		ckpt_debug("restore_tty_ldisc returnd %d\n", ret);
 		if (ret < 0) {
 			tty = ERR_PTR(ret);
 			goto out;
 		}
 	}
+	ckpt_debug("at end tty is %pS", tty);
 
 	tty_kref_get(tty);
  out:
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 8921726..cfadb22 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -14,6 +14,9 @@
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
 #include <linux/tty.h>
@@ -431,13 +434,18 @@ static struct file_system_type devpts_fs_type = {
 /*
  * The normal naming convention is simply /dev/pts/<number>; this conforms
  * to the System V naming convention
+ *
+ * @ptmx_inode: inode of the /dev/ptmx file - each pty namepace has its
+ * own
+ * @req_idx: requested specific index (i.e. 5 for /dev/pts/5).  If -1,
+ * then return first available.
  */
 
 int devpts_new_index(struct inode *ptmx_inode, int req_idx)
 {
 	struct super_block *sb = pts_sb_from_inode(ptmx_inode);
 	struct pts_fs_info *fsi = DEVPTS_SB(sb);
-	int index;
+	int index = req_idx;
 	int ida_ret;
 
 retry:
@@ -445,7 +453,8 @@ retry:
 		return -ENOMEM;
 
 	mutex_lock(&allocated_ptys_lock);
-	index = (req_idx >= 0 ? req_idx : 0);
+	if (index == UNSPECIFIED_PTY_INDEX)
+		index = 0;
 	ida_ret = ida_get_new_above(&fsi->allocated_ptys, index, &index);
 	if (ida_ret < 0) {
 		mutex_unlock(&allocated_ptys_lock);
@@ -454,7 +463,7 @@ retry:
 		return -EIO;
 	}
 
-	if (req_idx >= 0 && index != req_idx) {
+	if (req_idx != UNSPECIFIED_PTY_INDEX && index != req_idx) {
 		ida_remove(&fsi->allocated_ptys, index);
 		mutex_unlock(&allocated_ptys_lock);
 		return -EBUSY;
@@ -557,6 +566,51 @@ out:
 	mutex_unlock(&root->d_inode->i_mutex);
 }
 
+struct inode *get_ptmx_inode(struct super_block *sb)
+{
+	struct pts_fs_info *fsi = DEVPTS_SB(sb);
+	struct dentry *dentry = fsi->ptmx_dentry;
+	struct inode *inode = dentry->d_inode;
+
+	return igrab(inode);
+}
+
+/* defined in drivers/char/pty.c */
+extern int ptmx_create(struct inode *inode, struct file *filp, int index);
+
+/*
+ * fn caller (in restart code) has checked for the rights on the
+ * part of the userspace task to open the ptmx file.
+ * @pdir: an open file handle to the /dev/pts directory, which we
+ * will use to determine the devpts namespace, and to confirm that
+ * the resulting file is in the right directory.
+ * @file is a file for /dev/ptmx
+ */
+int open_create_pty(struct file *ptmxfile, int idx)
+{
+	return  ptmx_create(ptmxfile->f_dentry->d_inode, ptmxfile, idx);
+}
+
+int file_is_ptmx(struct file *file)
+{
+	dev_t device;
+	struct inode *inode;
+
+	if (!file->f_dentry)
+		return 0;
+	inode = file->f_dentry->d_inode;
+	if (!inode)
+		return 0;
+	device = inode->i_rdev;
+	if (!device)
+		return 0;
+	if (imajor(inode) != TTYAUX_MAJOR)
+		return 0;
+	if (iminor(inode) != 2)
+		return 0;
+	return 1;
+}
+
 static int __init init_devpts_fs(void)
 {
 	int err = register_filesystem(&devpts_fs_type);
diff --git a/fs/open.c b/fs/open.c
index dd98e80..c65229a 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -924,6 +924,17 @@ out_err:
 }
 EXPORT_SYMBOL_GPL(lookup_instantiate_filp);
 
+/*
+ * Passed to nameidata_to_filp to keep it from running
+ * the file-specified open routine.
+ * We pass this along if LOOKUP_CALLER_OPEN was passed to
+ * filp_open, meaning the caller will open the file by hand
+ */
+static int __dummy_open(struct inode *inode, struct file *filp)
+{
+	return 0;
+}
+
 /**
  * nameidata_to_filp - convert a nameidata to an open filp.
  * @nd: pointer to nameidata
@@ -935,13 +946,19 @@ struct file *nameidata_to_filp(struct nameidata *nd, int flags)
 {
 	const struct cred *cred = current_cred();
 	struct file *filp;
+	int (*open)(struct inode *, struct file *) = NULL;
+
+	if (flags & LOOKUP_CALLER_OPEN) {
+		printk(KERN_NOTICE "%s: I was passed LOOKUP_CALLER_OPEN\n", __func__);
+		open = __dummy_open;
+	}
 
 	/* Pick up the filp from the open intent */
 	filp = nd->intent.open.file;
 	/* Has the filesystem initialised the file for us? */
 	if (filp->f_path.dentry == NULL)
 		filp = __dentry_open(nd->path.dentry, nd->path.mnt, flags, filp,
-				     NULL, cred);
+				     open, cred);
 	else
 		path_put(&nd->path);
 	return filp;
diff --git a/include/linux/checkpoint.h b/include/linux/checkpoint.h
index 5e90ef9..0cbe8c4 100644
--- a/include/linux/checkpoint.h
+++ b/include/linux/checkpoint.h
@@ -46,8 +46,6 @@
 #define CHECKPOINT_USER_FLAGS		CHECKPOINT_SUBTREE
 #define RESTART_USER_FLAGS		(RESTART_TASKSELF | RESTART_FROZEN)
 
-#define CKPT_REQUIRED_NONE  ((unsigned long) -1L)
-
 extern void exit_checkpoint(struct task_struct *tsk);
 
 extern int ckpt_kwrite(struct ckpt_ctx *ctx, void *buf, int count);
diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h
index 14948ee..ddb418d 100644
--- a/include/linux/devpts_fs.h
+++ b/include/linux/devpts_fs.h
@@ -15,10 +15,13 @@
 
 #include <linux/errno.h>
 
+#define UNSPECIFIED_PTY_INDEX -1
 #ifdef CONFIG_UNIX98_PTYS
 
 int devpts_new_index(struct inode *ptmx_inode, int req_idx);
 void devpts_kill_index(struct inode *ptmx_inode, int idx);
+int open_create_pty(struct file *ptmxfile, int idx);
+int file_is_ptmx(struct file *file);
 /* mknod in devpts */
 int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty);
 /* get tty structure */
diff --git a/include/linux/namei.h b/include/linux/namei.h
index d870ae2..fae5159 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -56,6 +56,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_CREATE		0x0200
 #define LOOKUP_EXCL		0x0400
 #define LOOKUP_RENAME_TARGET	0x0800
+#define LOOKUP_CALLER_OPEN	0x1000
 
 extern int user_path_at(int, const char __user *, unsigned, struct path *);
 
diff --git a/include/linux/sched.h b/include/linux/sched.h
index f6f1350..0e67de7 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1481,7 +1481,6 @@ struct task_struct {
 #endif /* CONFIG_TRACING */
 #ifdef CONFIG_CHECKPOINT
 	struct ckpt_ctx *checkpoint_ctx;
-	unsigned long required_id;
 #endif
 };

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2009-09-10  2:28 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-09-02 23:31 [RFC] cr: pty: don't use required_id, change ptmx_open() Serge E. Hallyn
     [not found] ` <20090902233111.GA27125-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
2009-09-08 18:14   ` Oren Laadan
     [not found]     ` <4AA69EEB.9040706-RdfvBDnrOixBDgjK7y7TUQ@public.gmane.org>
2009-09-08 19:51       ` Serge E. Hallyn
2009-09-10  2:28       ` Serge E. Hallyn

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.