gid_t gid;
umode_t mode;
umode_t ptmxmode;
+ int newinstance;
};
enum {
- Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode,
+ Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance,
Opt_err
};
{Opt_mode, "mode=%o"},
#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
{Opt_ptmxmode, "ptmxmode=%o"},
+ {Opt_newinstance, "newinstance"},
#endif
{Opt_err, NULL}
};
static inline struct super_block *pts_sb_from_inode(struct inode *inode)
{
+#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
if (inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)
return inode->i_sb;
-
+#endif
return devpts_mnt->mnt_sb;
}
-static int parse_mount_options(char *data, struct pts_mount_opts *opts)
+#define PARSE_MOUNT 0
+#define PARSE_REMOUNT 1
+
+static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts)
{
char *p;
opts->mode = DEVPTS_DEFAULT_MODE;
opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE;
+ /* newinstance makes sense only on initial mount */
+ if (op == PARSE_MOUNT)
+ opts->newinstance = 0;
+
while ((p = strsep(&data, ",")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int token;
return -EINVAL;
opts->ptmxmode = option & S_IALLUGO;
break;
+ case Opt_newinstance:
+ /* newinstance makes sense only on initial mount */
+ if (op == PARSE_MOUNT)
+ opts->newinstance = 1;
+ break;
#endif
default:
printk(KERN_ERR "devpts: called with bogus options\n");
}
inode->i_ino = 2;
- inode->i_uid = inode->i_gid = 0;
- inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
mode = S_IFCHR|opts->ptmxmode;
fsi->ptmx_dentry = dentry;
rc = 0;
-
- printk(KERN_DEBUG "Created ptmx node in devpts ino %lu\n",
- inode->i_ino);
out:
mutex_unlock(&root->d_inode->i_mutex);
return rc;
struct pts_fs_info *fsi = DEVPTS_SB(sb);
struct pts_mount_opts *opts = &fsi->mount_opts;
- err = parse_mount_options(data, opts);
+ err = parse_mount_options(data, PARSE_REMOUNT, opts);
/*
* parse_mount_options() restores options to default values
goto free_fsi;
inode->i_ino = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->i_blocks = 0;
- inode->i_uid = inode->i_gid = 0;
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
if (s->s_root)
return 0;
- printk("devpts: get root dentry failed\n");
+ printk(KERN_ERR "devpts: get root dentry failed\n");
iput(inode);
free_fsi:
return -ENOMEM;
}
+#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
+static int compare_init_pts_sb(struct super_block *s, void *p)
+{
+ if (devpts_mnt)
+ return devpts_mnt->mnt_sb == s;
+ return 0;
+}
+
+/*
+ * devpts_get_sb()
+ *
+ * If the '-o newinstance' mount option was specified, mount a new
+ * (private) instance of devpts. PTYs created in this instance are
+ * independent of the PTYs in other devpts instances.
+ *
+ * If the '-o newinstance' option was not specified, mount/remount the
+ * initial kernel mount of devpts. This type of mount gives the
+ * legacy, single-instance semantics.
+ *
+ * The 'newinstance' option is needed to support multiple namespace
+ * semantics in devpts while preserving backward compatibility of the
+ * current 'single-namespace' semantics. i.e all mounts of devpts
+ * without the 'newinstance' mount option should bind to the initial
+ * kernel mount, like get_sb_single().
+ *
+ * Mounts with 'newinstance' option create a new, private namespace.
+ *
+ * NOTE:
+ *
+ * For single-mount semantics, devpts cannot use get_sb_single(),
+ * because get_sb_single()/sget() find and use the super-block from
+ * the most recent mount of devpts. But that recent mount may be a
+ * 'newinstance' mount and get_sb_single() would pick the newinstance
+ * super-block instead of the initial super-block.
+ */
static int devpts_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
+ int error;
+ struct pts_mount_opts opts;
+ struct super_block *s;
+
+ memset(&opts, 0, sizeof(opts));
+ if (data) {
+ error = parse_mount_options(data, PARSE_MOUNT, &opts);
+ if (error)
+ return error;
+ }
+
+ if (opts.newinstance)
+ s = sget(fs_type, NULL, set_anon_super, NULL);
+ else
+ s = sget(fs_type, compare_init_pts_sb, set_anon_super, NULL);
+
+ if (IS_ERR(s))
+ return PTR_ERR(s);
+
+ if (!s->s_root) {
+ s->s_flags = flags;
+ error = devpts_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
+ if (error)
+ goto out_undo_sget;
+ s->s_flags |= MS_ACTIVE;
+ }
+
+ simple_set_mnt(mnt, s);
+
+ memcpy(&(DEVPTS_SB(s))->mount_opts, &opts, sizeof(opts));
+
+ error = mknod_ptmx(s);
+ if (error)
+ goto out_dput;
+
+ return 0;
+
+out_dput:
+ dput(s->s_root); /* undo dget() in simple_set_mnt() */
+
+out_undo_sget:
+ deactivate_locked_super(s);
+ return error;
+}
+
+#else
+/*
+ * This supports only the legacy single-instance semantics (no
+ * multiple-instance semantics)
+ */
+static int devpts_get_sb(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data, struct vfsmount *mnt)
+{
return get_sb_single(fs_type, flags, data, devpts_fill_super, mnt);
}
+#endif
static void devpts_kill_sb(struct super_block *sb)
{
int ida_ret;
retry:
- if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL)) {
+ if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL))
return -ENOMEM;
- }
mutex_lock(&allocated_ptys_lock);
ida_ret = ida_get_new(&fsi->allocated_ptys, &index);
int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty)
{
- int number = tty->index; /* tty layer puts index from devpts_new_index() in here */
+ /* tty layer puts index from devpts_new_index() in here */
+ int number = tty->index;
struct tty_driver *driver = tty->driver;
dev_t device = MKDEV(driver->major, driver->minor_start+number);
struct dentry *dentry;
if (!inode)
return -ENOMEM;
- inode->i_ino = number+2;
- inode->i_uid = config.setuid ? config.uid : current_fsuid();
- inode->i_gid = config.setgid ? config.gid : current_fsgid();
+ inode->i_ino = number + 3;
+ inode->i_uid = opts->setuid ? opts->uid : current_fsuid();
+ inode->i_gid = opts->setgid ? opts->gid : current_fsgid();
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR|opts->mode, device);
inode->i_private = tty;
mutex_lock(&root->d_inode->i_mutex);
dentry = d_find_alias(inode);
- if (dentry && !IS_ERR(dentry)) {
+ if (IS_ERR(dentry))
+ goto out;
+
+ if (dentry) {
inode->i_nlink--;
d_delete(dentry);
- dput(dentry);
+ dput(dentry); /* d_alloc_name() in devpts_pty_new() */
}
+ dput(dentry); /* d_find_alias above */
+out:
mutex_unlock(&root->d_inode->i_mutex);
}