162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* -*- linux-c -*- --------------------------------------------------------- * 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * linux/fs/devpts/inode.c 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright 1998-2004 H. Peter Anvin -- All Rights Reserved 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * ------------------------------------------------------------------------- */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/fs.h> 1562306a36Sopenharmony_ci#include <linux/sched.h> 1662306a36Sopenharmony_ci#include <linux/namei.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/mount.h> 1962306a36Sopenharmony_ci#include <linux/tty.h> 2062306a36Sopenharmony_ci#include <linux/mutex.h> 2162306a36Sopenharmony_ci#include <linux/magic.h> 2262306a36Sopenharmony_ci#include <linux/idr.h> 2362306a36Sopenharmony_ci#include <linux/devpts_fs.h> 2462306a36Sopenharmony_ci#include <linux/parser.h> 2562306a36Sopenharmony_ci#include <linux/fsnotify.h> 2662306a36Sopenharmony_ci#include <linux/seq_file.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define DEVPTS_DEFAULT_MODE 0600 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * ptmx is a new node in /dev/pts and will be unused in legacy (single- 3162306a36Sopenharmony_ci * instance) mode. To prevent surprises in user space, set permissions of 3262306a36Sopenharmony_ci * ptmx to 0. Use 'chmod' or remount with '-o ptmxmode' to set meaningful 3362306a36Sopenharmony_ci * permissions. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci#define DEVPTS_DEFAULT_PTMX_MODE 0000 3662306a36Sopenharmony_ci#define PTMX_MINOR 2 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * sysctl support for setting limits on the number of Unix98 ptys allocated. 4062306a36Sopenharmony_ci * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_cistatic int pty_limit = NR_UNIX98_PTY_DEFAULT; 4362306a36Sopenharmony_cistatic int pty_reserve = NR_UNIX98_PTY_RESERVE; 4462306a36Sopenharmony_cistatic int pty_limit_min; 4562306a36Sopenharmony_cistatic int pty_limit_max = INT_MAX; 4662306a36Sopenharmony_cistatic atomic_t pty_count = ATOMIC_INIT(0); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct ctl_table pty_table[] = { 4962306a36Sopenharmony_ci { 5062306a36Sopenharmony_ci .procname = "max", 5162306a36Sopenharmony_ci .maxlen = sizeof(int), 5262306a36Sopenharmony_ci .mode = 0644, 5362306a36Sopenharmony_ci .data = &pty_limit, 5462306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 5562306a36Sopenharmony_ci .extra1 = &pty_limit_min, 5662306a36Sopenharmony_ci .extra2 = &pty_limit_max, 5762306a36Sopenharmony_ci }, { 5862306a36Sopenharmony_ci .procname = "reserve", 5962306a36Sopenharmony_ci .maxlen = sizeof(int), 6062306a36Sopenharmony_ci .mode = 0644, 6162306a36Sopenharmony_ci .data = &pty_reserve, 6262306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 6362306a36Sopenharmony_ci .extra1 = &pty_limit_min, 6462306a36Sopenharmony_ci .extra2 = &pty_limit_max, 6562306a36Sopenharmony_ci }, { 6662306a36Sopenharmony_ci .procname = "nr", 6762306a36Sopenharmony_ci .maxlen = sizeof(int), 6862306a36Sopenharmony_ci .mode = 0444, 6962306a36Sopenharmony_ci .data = &pty_count, 7062306a36Sopenharmony_ci .proc_handler = proc_dointvec, 7162306a36Sopenharmony_ci }, 7262306a36Sopenharmony_ci {} 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct pts_mount_opts { 7662306a36Sopenharmony_ci int setuid; 7762306a36Sopenharmony_ci int setgid; 7862306a36Sopenharmony_ci kuid_t uid; 7962306a36Sopenharmony_ci kgid_t gid; 8062306a36Sopenharmony_ci umode_t mode; 8162306a36Sopenharmony_ci umode_t ptmxmode; 8262306a36Sopenharmony_ci int reserve; 8362306a36Sopenharmony_ci int max; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cienum { 8762306a36Sopenharmony_ci Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance, Opt_max, 8862306a36Sopenharmony_ci Opt_err 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const match_table_t tokens = { 9262306a36Sopenharmony_ci {Opt_uid, "uid=%u"}, 9362306a36Sopenharmony_ci {Opt_gid, "gid=%u"}, 9462306a36Sopenharmony_ci {Opt_mode, "mode=%o"}, 9562306a36Sopenharmony_ci {Opt_ptmxmode, "ptmxmode=%o"}, 9662306a36Sopenharmony_ci {Opt_newinstance, "newinstance"}, 9762306a36Sopenharmony_ci {Opt_max, "max=%d"}, 9862306a36Sopenharmony_ci {Opt_err, NULL} 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct pts_fs_info { 10262306a36Sopenharmony_ci struct ida allocated_ptys; 10362306a36Sopenharmony_ci struct pts_mount_opts mount_opts; 10462306a36Sopenharmony_ci struct super_block *sb; 10562306a36Sopenharmony_ci struct dentry *ptmx_dentry; 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return sb->s_fs_info; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int devpts_ptmx_path(struct path *path) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct super_block *sb; 11662306a36Sopenharmony_ci int err; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Is a devpts filesystem at "pts" in the same directory? */ 11962306a36Sopenharmony_ci err = path_pts(path); 12062306a36Sopenharmony_ci if (err) 12162306a36Sopenharmony_ci return err; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Is the path the root of a devpts filesystem? */ 12462306a36Sopenharmony_ci sb = path->mnt->mnt_sb; 12562306a36Sopenharmony_ci if ((sb->s_magic != DEVPTS_SUPER_MAGIC) || 12662306a36Sopenharmony_ci (path->mnt->mnt_root != sb->s_root)) 12762306a36Sopenharmony_ci return -ENODEV; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Try to find a suitable devpts filesystem. We support the following 13462306a36Sopenharmony_ci * scenarios: 13562306a36Sopenharmony_ci * - The ptmx device node is located in the same directory as the devpts 13662306a36Sopenharmony_ci * mount where the pts device nodes are located. 13762306a36Sopenharmony_ci * This is e.g. the case when calling open on the /dev/pts/ptmx device 13862306a36Sopenharmony_ci * node when the devpts filesystem is mounted at /dev/pts. 13962306a36Sopenharmony_ci * - The ptmx device node is located outside the devpts filesystem mount 14062306a36Sopenharmony_ci * where the pts device nodes are located. For example, the ptmx device 14162306a36Sopenharmony_ci * is a symlink, separate device node, or bind-mount. 14262306a36Sopenharmony_ci * A supported scenario is bind-mounting /dev/pts/ptmx to /dev/ptmx and 14362306a36Sopenharmony_ci * then calling open on /dev/ptmx. In this case a suitable pts 14462306a36Sopenharmony_ci * subdirectory can be found in the common parent directory /dev of the 14562306a36Sopenharmony_ci * devpts mount and the ptmx bind-mount, after resolving the /dev/ptmx 14662306a36Sopenharmony_ci * bind-mount. 14762306a36Sopenharmony_ci * If no suitable pts subdirectory can be found this function will fail. 14862306a36Sopenharmony_ci * This is e.g. the case when bind-mounting /dev/pts/ptmx to /ptmx. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistruct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct path path; 15362306a36Sopenharmony_ci int err = 0; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci path = filp->f_path; 15662306a36Sopenharmony_ci path_get(&path); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Walk upward while the start point is a bind mount of 15962306a36Sopenharmony_ci * a single file. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci while (path.mnt->mnt_root == path.dentry) 16262306a36Sopenharmony_ci if (follow_up(&path) == 0) 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* devpts_ptmx_path() finds a devpts fs or returns an error. */ 16662306a36Sopenharmony_ci if ((path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) || 16762306a36Sopenharmony_ci (DEVPTS_SB(path.mnt->mnt_sb) != fsi)) 16862306a36Sopenharmony_ci err = devpts_ptmx_path(&path); 16962306a36Sopenharmony_ci dput(path.dentry); 17062306a36Sopenharmony_ci if (!err) { 17162306a36Sopenharmony_ci if (DEVPTS_SB(path.mnt->mnt_sb) == fsi) 17262306a36Sopenharmony_ci return path.mnt; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci err = -ENODEV; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mntput(path.mnt); 17862306a36Sopenharmony_ci return ERR_PTR(err); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistruct pts_fs_info *devpts_acquire(struct file *filp) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct pts_fs_info *result; 18462306a36Sopenharmony_ci struct path path; 18562306a36Sopenharmony_ci struct super_block *sb; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci path = filp->f_path; 18862306a36Sopenharmony_ci path_get(&path); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Has the devpts filesystem already been found? */ 19162306a36Sopenharmony_ci if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) { 19262306a36Sopenharmony_ci int err; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci err = devpts_ptmx_path(&path); 19562306a36Sopenharmony_ci if (err) { 19662306a36Sopenharmony_ci result = ERR_PTR(err); 19762306a36Sopenharmony_ci goto out; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * pty code needs to hold extra references in case of last /dev/tty close 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci sb = path.mnt->mnt_sb; 20562306a36Sopenharmony_ci atomic_inc(&sb->s_active); 20662306a36Sopenharmony_ci result = DEVPTS_SB(sb); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciout: 20962306a36Sopenharmony_ci path_put(&path); 21062306a36Sopenharmony_ci return result; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_civoid devpts_release(struct pts_fs_info *fsi) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci deactivate_super(fsi->sb); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci#define PARSE_MOUNT 0 21962306a36Sopenharmony_ci#define PARSE_REMOUNT 1 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* 22262306a36Sopenharmony_ci * parse_mount_options(): 22362306a36Sopenharmony_ci * Set @opts to mount options specified in @data. If an option is not 22462306a36Sopenharmony_ci * specified in @data, set it to its default value. 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * Note: @data may be NULL (in which case all options are set to default). 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci char *p; 23162306a36Sopenharmony_ci kuid_t uid; 23262306a36Sopenharmony_ci kgid_t gid; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci opts->setuid = 0; 23562306a36Sopenharmony_ci opts->setgid = 0; 23662306a36Sopenharmony_ci opts->uid = GLOBAL_ROOT_UID; 23762306a36Sopenharmony_ci opts->gid = GLOBAL_ROOT_GID; 23862306a36Sopenharmony_ci opts->mode = DEVPTS_DEFAULT_MODE; 23962306a36Sopenharmony_ci opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; 24062306a36Sopenharmony_ci opts->max = NR_UNIX98_PTY_MAX; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Only allow instances mounted from the initial mount 24362306a36Sopenharmony_ci * namespace to tap the reserve pool of ptys. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci if (op == PARSE_MOUNT) 24662306a36Sopenharmony_ci opts->reserve = 24762306a36Sopenharmony_ci (current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci while ((p = strsep(&data, ",")) != NULL) { 25062306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 25162306a36Sopenharmony_ci int token; 25262306a36Sopenharmony_ci int option; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (!*p) 25562306a36Sopenharmony_ci continue; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci token = match_token(p, tokens, args); 25862306a36Sopenharmony_ci switch (token) { 25962306a36Sopenharmony_ci case Opt_uid: 26062306a36Sopenharmony_ci if (match_int(&args[0], &option)) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci uid = make_kuid(current_user_ns(), option); 26362306a36Sopenharmony_ci if (!uid_valid(uid)) 26462306a36Sopenharmony_ci return -EINVAL; 26562306a36Sopenharmony_ci opts->uid = uid; 26662306a36Sopenharmony_ci opts->setuid = 1; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case Opt_gid: 26962306a36Sopenharmony_ci if (match_int(&args[0], &option)) 27062306a36Sopenharmony_ci return -EINVAL; 27162306a36Sopenharmony_ci gid = make_kgid(current_user_ns(), option); 27262306a36Sopenharmony_ci if (!gid_valid(gid)) 27362306a36Sopenharmony_ci return -EINVAL; 27462306a36Sopenharmony_ci opts->gid = gid; 27562306a36Sopenharmony_ci opts->setgid = 1; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci case Opt_mode: 27862306a36Sopenharmony_ci if (match_octal(&args[0], &option)) 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci opts->mode = option & S_IALLUGO; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci case Opt_ptmxmode: 28362306a36Sopenharmony_ci if (match_octal(&args[0], &option)) 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci opts->ptmxmode = option & S_IALLUGO; 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci case Opt_newinstance: 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci case Opt_max: 29062306a36Sopenharmony_ci if (match_int(&args[0], &option) || 29162306a36Sopenharmony_ci option < 0 || option > NR_UNIX98_PTY_MAX) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci opts->max = option; 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci default: 29662306a36Sopenharmony_ci pr_err("called with bogus options\n"); 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int mknod_ptmx(struct super_block *sb) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci int mode; 30762306a36Sopenharmony_ci int rc = -ENOMEM; 30862306a36Sopenharmony_ci struct dentry *dentry; 30962306a36Sopenharmony_ci struct inode *inode; 31062306a36Sopenharmony_ci struct dentry *root = sb->s_root; 31162306a36Sopenharmony_ci struct pts_fs_info *fsi = DEVPTS_SB(sb); 31262306a36Sopenharmony_ci struct pts_mount_opts *opts = &fsi->mount_opts; 31362306a36Sopenharmony_ci kuid_t ptmx_uid = current_fsuid(); 31462306a36Sopenharmony_ci kgid_t ptmx_gid = current_fsgid(); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci inode_lock(d_inode(root)); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* If we have already created ptmx node, return */ 31962306a36Sopenharmony_ci if (fsi->ptmx_dentry) { 32062306a36Sopenharmony_ci rc = 0; 32162306a36Sopenharmony_ci goto out; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci dentry = d_alloc_name(root, "ptmx"); 32562306a36Sopenharmony_ci if (!dentry) { 32662306a36Sopenharmony_ci pr_err("Unable to alloc dentry for ptmx node\n"); 32762306a36Sopenharmony_ci goto out; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * Create a new 'ptmx' node in this mount of devpts. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ci inode = new_inode(sb); 33462306a36Sopenharmony_ci if (!inode) { 33562306a36Sopenharmony_ci pr_err("Unable to alloc inode for ptmx node\n"); 33662306a36Sopenharmony_ci dput(dentry); 33762306a36Sopenharmony_ci goto out; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci inode->i_ino = 2; 34162306a36Sopenharmony_ci inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci mode = S_IFCHR|opts->ptmxmode; 34462306a36Sopenharmony_ci init_special_inode(inode, mode, MKDEV(TTYAUX_MAJOR, 2)); 34562306a36Sopenharmony_ci inode->i_uid = ptmx_uid; 34662306a36Sopenharmony_ci inode->i_gid = ptmx_gid; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci d_add(dentry, inode); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci fsi->ptmx_dentry = dentry; 35162306a36Sopenharmony_ci rc = 0; 35262306a36Sopenharmony_ciout: 35362306a36Sopenharmony_ci inode_unlock(d_inode(root)); 35462306a36Sopenharmony_ci return rc; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void update_ptmx_mode(struct pts_fs_info *fsi) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct inode *inode; 36062306a36Sopenharmony_ci if (fsi->ptmx_dentry) { 36162306a36Sopenharmony_ci inode = d_inode(fsi->ptmx_dentry); 36262306a36Sopenharmony_ci inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int devpts_remount(struct super_block *sb, int *flags, char *data) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci int err; 36962306a36Sopenharmony_ci struct pts_fs_info *fsi = DEVPTS_SB(sb); 37062306a36Sopenharmony_ci struct pts_mount_opts *opts = &fsi->mount_opts; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci err = parse_mount_options(data, PARSE_REMOUNT, opts); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * parse_mount_options() restores options to default values 37662306a36Sopenharmony_ci * before parsing and may have changed ptmxmode. So, update the 37762306a36Sopenharmony_ci * mode in the inode too. Bogus options don't fail the remount, 37862306a36Sopenharmony_ci * so do this even on error return. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci update_ptmx_mode(fsi); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return err; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int devpts_show_options(struct seq_file *seq, struct dentry *root) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct pts_fs_info *fsi = DEVPTS_SB(root->d_sb); 38862306a36Sopenharmony_ci struct pts_mount_opts *opts = &fsi->mount_opts; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (opts->setuid) 39162306a36Sopenharmony_ci seq_printf(seq, ",uid=%u", 39262306a36Sopenharmony_ci from_kuid_munged(&init_user_ns, opts->uid)); 39362306a36Sopenharmony_ci if (opts->setgid) 39462306a36Sopenharmony_ci seq_printf(seq, ",gid=%u", 39562306a36Sopenharmony_ci from_kgid_munged(&init_user_ns, opts->gid)); 39662306a36Sopenharmony_ci seq_printf(seq, ",mode=%03o", opts->mode); 39762306a36Sopenharmony_ci seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode); 39862306a36Sopenharmony_ci if (opts->max < NR_UNIX98_PTY_MAX) 39962306a36Sopenharmony_ci seq_printf(seq, ",max=%d", opts->max); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic const struct super_operations devpts_sops = { 40562306a36Sopenharmony_ci .statfs = simple_statfs, 40662306a36Sopenharmony_ci .remount_fs = devpts_remount, 40762306a36Sopenharmony_ci .show_options = devpts_show_options, 40862306a36Sopenharmony_ci}; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void *new_pts_fs_info(struct super_block *sb) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct pts_fs_info *fsi; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL); 41562306a36Sopenharmony_ci if (!fsi) 41662306a36Sopenharmony_ci return NULL; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ida_init(&fsi->allocated_ptys); 41962306a36Sopenharmony_ci fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE; 42062306a36Sopenharmony_ci fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; 42162306a36Sopenharmony_ci fsi->sb = sb; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return fsi; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int 42762306a36Sopenharmony_cidevpts_fill_super(struct super_block *s, void *data, int silent) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct inode *inode; 43062306a36Sopenharmony_ci int error; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci s->s_iflags &= ~SB_I_NODEV; 43362306a36Sopenharmony_ci s->s_blocksize = 1024; 43462306a36Sopenharmony_ci s->s_blocksize_bits = 10; 43562306a36Sopenharmony_ci s->s_magic = DEVPTS_SUPER_MAGIC; 43662306a36Sopenharmony_ci s->s_op = &devpts_sops; 43762306a36Sopenharmony_ci s->s_d_op = &simple_dentry_operations; 43862306a36Sopenharmony_ci s->s_time_gran = 1; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci error = -ENOMEM; 44162306a36Sopenharmony_ci s->s_fs_info = new_pts_fs_info(s); 44262306a36Sopenharmony_ci if (!s->s_fs_info) 44362306a36Sopenharmony_ci goto fail; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci error = parse_mount_options(data, PARSE_MOUNT, &DEVPTS_SB(s)->mount_opts); 44662306a36Sopenharmony_ci if (error) 44762306a36Sopenharmony_ci goto fail; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci error = -ENOMEM; 45062306a36Sopenharmony_ci inode = new_inode(s); 45162306a36Sopenharmony_ci if (!inode) 45262306a36Sopenharmony_ci goto fail; 45362306a36Sopenharmony_ci inode->i_ino = 1; 45462306a36Sopenharmony_ci inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode); 45562306a36Sopenharmony_ci inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; 45662306a36Sopenharmony_ci inode->i_op = &simple_dir_inode_operations; 45762306a36Sopenharmony_ci inode->i_fop = &simple_dir_operations; 45862306a36Sopenharmony_ci set_nlink(inode, 2); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci s->s_root = d_make_root(inode); 46162306a36Sopenharmony_ci if (!s->s_root) { 46262306a36Sopenharmony_ci pr_err("get root dentry failed\n"); 46362306a36Sopenharmony_ci goto fail; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci error = mknod_ptmx(s); 46762306a36Sopenharmony_ci if (error) 46862306a36Sopenharmony_ci goto fail_dput; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_cifail_dput: 47262306a36Sopenharmony_ci dput(s->s_root); 47362306a36Sopenharmony_ci s->s_root = NULL; 47462306a36Sopenharmony_cifail: 47562306a36Sopenharmony_ci return error; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* 47962306a36Sopenharmony_ci * devpts_mount() 48062306a36Sopenharmony_ci * 48162306a36Sopenharmony_ci * Mount a new (private) instance of devpts. PTYs created in this 48262306a36Sopenharmony_ci * instance are independent of the PTYs in other devpts instances. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_cistatic struct dentry *devpts_mount(struct file_system_type *fs_type, 48562306a36Sopenharmony_ci int flags, const char *dev_name, void *data) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci return mount_nodev(fs_type, flags, data, devpts_fill_super); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void devpts_kill_sb(struct super_block *sb) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct pts_fs_info *fsi = DEVPTS_SB(sb); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (fsi) 49562306a36Sopenharmony_ci ida_destroy(&fsi->allocated_ptys); 49662306a36Sopenharmony_ci kfree(fsi); 49762306a36Sopenharmony_ci kill_litter_super(sb); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic struct file_system_type devpts_fs_type = { 50162306a36Sopenharmony_ci .name = "devpts", 50262306a36Sopenharmony_ci .mount = devpts_mount, 50362306a36Sopenharmony_ci .kill_sb = devpts_kill_sb, 50462306a36Sopenharmony_ci .fs_flags = FS_USERNS_MOUNT, 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/* 50862306a36Sopenharmony_ci * The normal naming convention is simply /dev/pts/<number>; this conforms 50962306a36Sopenharmony_ci * to the System V naming convention 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ciint devpts_new_index(struct pts_fs_info *fsi) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci int index = -ENOSPC; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (atomic_inc_return(&pty_count) >= (pty_limit - 51762306a36Sopenharmony_ci (fsi->mount_opts.reserve ? 0 : pty_reserve))) 51862306a36Sopenharmony_ci goto out; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci index = ida_alloc_max(&fsi->allocated_ptys, fsi->mount_opts.max - 1, 52162306a36Sopenharmony_ci GFP_KERNEL); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ciout: 52462306a36Sopenharmony_ci if (index < 0) 52562306a36Sopenharmony_ci atomic_dec(&pty_count); 52662306a36Sopenharmony_ci return index; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_civoid devpts_kill_index(struct pts_fs_info *fsi, int idx) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci ida_free(&fsi->allocated_ptys, idx); 53262306a36Sopenharmony_ci atomic_dec(&pty_count); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/** 53662306a36Sopenharmony_ci * devpts_pty_new -- create a new inode in /dev/pts/ 53762306a36Sopenharmony_ci * @fsi: Filesystem info for this instance. 53862306a36Sopenharmony_ci * @index: used as a name of the node 53962306a36Sopenharmony_ci * @priv: what's given back by devpts_get_priv 54062306a36Sopenharmony_ci * 54162306a36Sopenharmony_ci * The dentry for the created inode is returned. 54262306a36Sopenharmony_ci * Remove it from /dev/pts/ with devpts_pty_kill(). 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_cistruct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct dentry *dentry; 54762306a36Sopenharmony_ci struct super_block *sb = fsi->sb; 54862306a36Sopenharmony_ci struct inode *inode; 54962306a36Sopenharmony_ci struct dentry *root; 55062306a36Sopenharmony_ci struct pts_mount_opts *opts; 55162306a36Sopenharmony_ci char s[12]; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci root = sb->s_root; 55462306a36Sopenharmony_ci opts = &fsi->mount_opts; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci inode = new_inode(sb); 55762306a36Sopenharmony_ci if (!inode) 55862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci inode->i_ino = index + 3; 56162306a36Sopenharmony_ci inode->i_uid = opts->setuid ? opts->uid : current_fsuid(); 56262306a36Sopenharmony_ci inode->i_gid = opts->setgid ? opts->gid : current_fsgid(); 56362306a36Sopenharmony_ci inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode); 56462306a36Sopenharmony_ci init_special_inode(inode, S_IFCHR|opts->mode, MKDEV(UNIX98_PTY_SLAVE_MAJOR, index)); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci sprintf(s, "%d", index); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci dentry = d_alloc_name(root, s); 56962306a36Sopenharmony_ci if (dentry) { 57062306a36Sopenharmony_ci dentry->d_fsdata = priv; 57162306a36Sopenharmony_ci d_add(dentry, inode); 57262306a36Sopenharmony_ci fsnotify_create(d_inode(root), dentry); 57362306a36Sopenharmony_ci } else { 57462306a36Sopenharmony_ci iput(inode); 57562306a36Sopenharmony_ci dentry = ERR_PTR(-ENOMEM); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return dentry; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci/** 58262306a36Sopenharmony_ci * devpts_get_priv -- get private data for a slave 58362306a36Sopenharmony_ci * @dentry: dentry of the slave 58462306a36Sopenharmony_ci * 58562306a36Sopenharmony_ci * Returns whatever was passed as priv in devpts_pty_new for a given inode. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_civoid *devpts_get_priv(struct dentry *dentry) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci if (dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC) 59062306a36Sopenharmony_ci return NULL; 59162306a36Sopenharmony_ci return dentry->d_fsdata; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci/** 59562306a36Sopenharmony_ci * devpts_pty_kill -- remove inode form /dev/pts/ 59662306a36Sopenharmony_ci * @dentry: dentry of the slave to be removed 59762306a36Sopenharmony_ci * 59862306a36Sopenharmony_ci * This is an inverse operation of devpts_pty_new. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_civoid devpts_pty_kill(struct dentry *dentry) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci WARN_ON_ONCE(dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci dentry->d_fsdata = NULL; 60562306a36Sopenharmony_ci drop_nlink(dentry->d_inode); 60662306a36Sopenharmony_ci d_drop(dentry); 60762306a36Sopenharmony_ci fsnotify_unlink(d_inode(dentry->d_parent), dentry); 60862306a36Sopenharmony_ci dput(dentry); /* d_alloc_name() in devpts_pty_new() */ 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic int __init init_devpts_fs(void) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci int err = register_filesystem(&devpts_fs_type); 61462306a36Sopenharmony_ci if (!err) { 61562306a36Sopenharmony_ci register_sysctl("kernel/pty", pty_table); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci return err; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_cimodule_init(init_devpts_fs) 620