+static int compare_init_pts_sb(struct super_block *s, void *p)
+{
+ if (devpts_mnt)
+ return devpts_mnt->mnt_sb == s;
+ return 0;
+}
+
+#ifdef CONFIG_DEVPTS_MULTIPLE_INSTANCES
+/*
+ * Safely parse the mount options in @data and update @opts.
+ *
+ * devpts ends up parsing options two times during mount, due to the
+ * two modes of operation it supports. The first parse occurs in
+ * devpts_get_sb() when determining the mode (single-instance or
+ * multi-instance mode). The second parse happens in devpts_remount()
+ * or new_pts_mount() depending on the mode.
+ *
+ * Parsing of options modifies the @data making subsequent parsing
+ * incorrect. So make a local copy of @data and parse it.
+ *
+ * Return: 0 On success, -errno on error
+ */
+static int safe_parse_mount_options(void *data, struct pts_mount_opts *opts)
+{
+ int rc;
+ void *datacp;
+
+ if (!data)
+ return 0;
+
+ /* Use kstrdup() ? */
+ datacp = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!datacp)
+ return -ENOMEM;
+
+ memcpy(datacp, data, PAGE_SIZE);
+ rc = parse_mount_options((char *)datacp, PARSE_MOUNT, opts);
+ kfree(datacp);
+
+ return rc;
+}
+
+/*
+ * Mount a new (private) instance of devpts. PTYs created in this
+ * instance are independent of the PTYs in other devpts instances.
+ */
+static int new_pts_mount(struct file_system_type *fs_type, int flags,
+ void *data, struct vfsmount *mnt)
+{
+ int err;
+ struct pts_fs_info *fsi;
+ struct pts_mount_opts *opts;
+
+ printk(KERN_NOTICE "devpts: newinstance mount\n");
+
+ err = get_sb_nodev(fs_type, flags, data, devpts_fill_super, mnt);
+ if (err)
+ return err;
+
+ fsi = DEVPTS_SB(mnt->mnt_sb);
+ opts = &fsi->mount_opts;
+
+ err = parse_mount_options(data, PARSE_MOUNT, opts);
+ if (err)
+ goto fail;
+
+ err = mknod_ptmx(mnt->mnt_sb);
+ if (err)
+ goto fail;
+
+ return 0;
+
+fail:
+ dput(mnt->mnt_sb->s_root);
+ deactivate_super(mnt->mnt_sb);
+ return err;
+}
+
+/*
+ * Check if 'newinstance' mount option was specified in @data.
+ *
+ * Return: -errno on error (eg: invalid mount options specified)
+ * : 1 if 'newinstance' mount option was specified
+ * : 0 if 'newinstance' mount option was NOT specified
+ */
+static int is_new_instance_mount(void *data)
+{
+ int rc;
+ struct pts_mount_opts opts;
+
+ if (!data)
+ return 0;
+
+ rc = safe_parse_mount_options(data, &opts);
+ if (!rc)
+ rc = opts.newinstance;
+
+ return rc;
+}
+
+/*
+ * get_init_pts_sb()
+ *
+ * This interface 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.
+ *
+ * But 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.
+ *
+ * This interface is identical to get_sb_single() except that it
+ * consistently selects the 'single-namespace' superblock even in the
+ * presence of the private namespace (i.e 'newinstance') super-blocks.
+ */
+static int get_init_pts_sb(struct file_system_type *fs_type, int flags,
+ void *data, struct vfsmount *mnt)
+{
+ struct super_block *s;
+ int error;
+
+ 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) {
+ up_write(&s->s_umount);
+ deactivate_super(s);
+ return error;
+ }
+ s->s_flags |= MS_ACTIVE;
+ }
+ do_remount_sb(s, flags, data, 0);
+ return simple_set_mnt(mnt, s);
+}
+
+/*
+ * Mount or remount the initial kernel mount of devpts. This type of
+ * mount maintains the legacy, single-instance semantics, while the
+ * kernel still allows multiple-instances.
+ */
+static int init_pts_mount(struct file_system_type *fs_type, int flags,
+ void *data, struct vfsmount *mnt)
+{
+ int err;
+
+ err = get_init_pts_sb(fs_type, flags, data, mnt);
+ if (err)
+ return err;
+
+ err = mknod_ptmx(mnt->mnt_sb);
+ if (err) {
+ dput(mnt->mnt_sb->s_root);
+ deactivate_super(mnt->mnt_sb);
+ }
+
+ return err;
+}
+