cregit-Linux how code gets into the kernel

Release 4.10 fs/devpts/inode.c

Directory: fs/devpts
/* -*- linux-c -*- --------------------------------------------------------- *
 *
 * linux/fs/devpts/inode.c
 *
 *  Copyright 1998-2004 H. Peter Anvin -- All Rights Reserved
 *
 * This file is part of the Linux kernel and is made available under
 * the terms of the GNU General Public License, version 2, or at your
 * option, any later version, incorporated herein by reference.
 *
 * ------------------------------------------------------------------------- */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/mount.h>
#include <linux/tty.h>
#include <linux/mutex.h>
#include <linux/magic.h>
#include <linux/idr.h>
#include <linux/devpts_fs.h>
#include <linux/parser.h>
#include <linux/fsnotify.h>
#include <linux/seq_file.h>


#define DEVPTS_DEFAULT_MODE 0600
/*
 * ptmx is a new node in /dev/pts and will be unused in legacy (single-
 * instance) mode. To prevent surprises in user space, set permissions of
 * ptmx to 0. Use 'chmod' or remount with '-o ptmxmode' to set meaningful
 * permissions.
 */

#define DEVPTS_DEFAULT_PTMX_MODE 0000

#define PTMX_MINOR	2

/*
 * sysctl support for setting limits on the number of Unix98 ptys allocated.
 * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly.
 */

static int pty_limit = NR_UNIX98_PTY_DEFAULT;

static int pty_reserve = NR_UNIX98_PTY_RESERVE;

static int pty_limit_min;

static int pty_limit_max = INT_MAX;

static int pty_count;


static struct ctl_table pty_table[] = {
	{
		.procname	= "max",
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.data		= &pty_limit,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &pty_limit_min,
		.extra2		= &pty_limit_max,
        }, {
		.procname	= "reserve",
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.data		= &pty_reserve,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &pty_limit_min,
		.extra2		= &pty_limit_max,
        }, {
		.procname	= "nr",
		.maxlen		= sizeof(int),
		.mode		= 0444,
		.data		= &pty_count,
		.proc_handler	= proc_dointvec,
        },
	{}
};


static struct ctl_table pty_kern_table[] = {
	{
		.procname	= "pty",
		.mode		= 0555,
		.child		= pty_table,
        },
	{}
};


static struct ctl_table pty_root_table[] = {
	{
		.procname	= "kernel",
		.mode		= 0555,
		.child		= pty_kern_table,
        },
	{}
};

static DEFINE_MUTEX(allocated_ptys_lock);


struct pts_mount_opts {
	
int setuid;
	
int setgid;
	
kuid_t   uid;
	
kgid_t   gid;
	
umode_t mode;
	
umode_t ptmxmode;
	
int reserve;
	
int max;
};

enum {
	





Opt_uid, Opt_gid, Opt_mode, Opt_ptmxmode, Opt_newinstance,  Opt_max,
	
Opt_err
};


static const match_table_t tokens = {
	{Opt_uid, "uid=%u"},
	{Opt_gid, "gid=%u"},
	{Opt_mode, "mode=%o"},
	{Opt_ptmxmode, "ptmxmode=%o"},
	{Opt_newinstance, "newinstance"},
	{Opt_max, "max=%d"},
	{Opt_err, NULL}
};


struct pts_fs_info {
	
struct ida allocated_ptys;
	
struct pts_mount_opts mount_opts;
	
struct super_block *sb;
	
struct dentry *ptmx_dentry;
};


static inline struct pts_fs_info *DEVPTS_SB(struct super_block *sb) { return sb->s_fs_info; }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu19100.00%1100.00%
Total19100.00%1100.00%


struct pts_fs_info *devpts_acquire(struct file *filp) { struct pts_fs_info *result; struct path path; struct super_block *sb; int err; path = filp->f_path; path_get(&path); /* Has the devpts filesystem already been found? */ sb = path.mnt->mnt_sb; if (sb->s_magic != DEVPTS_SUPER_MAGIC) { /* Is a devpts filesystem at "pts" in the same directory? */ err = path_pts(&path); if (err) { result = ERR_PTR(err); goto out; } /* Is the path the root of a devpts filesystem? */ result = ERR_PTR(-ENODEV); sb = path.mnt->mnt_sb; if ((sb->s_magic != DEVPTS_SUPER_MAGIC) || (path.mnt->mnt_root != sb->s_root)) goto out; } /* * pty code needs to hold extra references in case of last /dev/tty close */ atomic_inc(&sb->s_active); result = DEVPTS_SB(sb); out: path_put(&path); return result; }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman13587.66%133.33%
sukadev bhattiprolusukadev bhattiprolu149.09%133.33%
josh triplettjosh triplett53.25%133.33%
Total154100.00%3100.00%


void devpts_release(struct pts_fs_info *fsi) { deactivate_super(fsi->sb); }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman1588.24%150.00%
sukadev bhattiprolusukadev bhattiprolu211.76%150.00%
Total17100.00%2100.00%

#define PARSE_MOUNT 0 #define PARSE_REMOUNT 1 /* * parse_mount_options(): * Set @opts to mount options specified in @data. If an option is not * specified in @data, set it to its default value. * * Note: @data may be NULL (in which case all options are set to default). */
static int parse_mount_options(char *data, int op, struct pts_mount_opts *opts) { char *p; kuid_t uid; kgid_t gid; opts->setuid = 0; opts->setgid = 0; opts->uid = GLOBAL_ROOT_UID; opts->gid = GLOBAL_ROOT_GID; opts->mode = DEVPTS_DEFAULT_MODE; opts->ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; opts->max = NR_UNIX98_PTY_MAX; /* Only allow instances mounted from the initial mount * namespace to tap the reserve pool of ptys. */ if (op == PARSE_MOUNT) opts->reserve = (current->nsproxy->mnt_ns == init_task.nsproxy->mnt_ns); while ((p = strsep(&data, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; int option; if (!*p) continue; token = match_token(p, tokens, args); switch (token) { case Opt_uid: if (match_int(&args[0], &option)) return -EINVAL; uid = make_kuid(current_user_ns(), option); if (!uid_valid(uid)) return -EINVAL; opts->uid = uid; opts->setuid = 1; break; case Opt_gid: if (match_int(&args[0], &option)) return -EINVAL; gid = make_kgid(current_user_ns(), option); if (!gid_valid(gid)) return -EINVAL; opts->gid = gid; opts->setgid = 1; break; case Opt_mode: if (match_octal(&args[0], &option)) return -EINVAL; opts->mode = option & S_IALLUGO; break; case Opt_ptmxmode: if (match_octal(&args[0], &option)) return -EINVAL; opts->ptmxmode = option & S_IALLUGO; break; case Opt_newinstance: break; case Opt_max: if (match_int(&args[0], &option) || option < 0 || option > NR_UNIX98_PTY_MAX) return -EINVAL; opts->max = option; break; default: pr_err("called with bogus options\n"); return -EINVAL; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
domen puncerdomen puncer8923.80%16.67%
sukadev bhattiprolusukadev bhattiprolu7921.12%426.67%
eric w. biedermaneric w. biederman6918.45%213.33%
pre-gitpre-git6216.58%320.00%
konstantin khlebnikovkonstantin khlebnikov4211.23%16.67%
al viroal viro184.81%16.67%
linus torvaldslinus torvalds112.94%16.67%
fabian frederickfabian frederick20.53%16.67%
miklos szeredimiklos szeredi20.53%16.67%
Total374100.00%15100.00%


static int mknod_ptmx(struct super_block *sb) { int mode; int rc = -ENOMEM; struct dentry *dentry; struct inode *inode; struct dentry *root = sb->s_root; struct pts_fs_info *fsi = DEVPTS_SB(sb); struct pts_mount_opts *opts = &fsi->mount_opts; kuid_t ptmx_uid = current_fsuid(); kgid_t ptmx_gid = current_fsgid(); inode_lock(d_inode(root)); /* If we have already created ptmx node, return */ if (fsi->ptmx_dentry) { rc = 0; goto out; } dentry = d_alloc_name(root, "ptmx"); if (!dentry) { pr_err("Unable to alloc dentry for ptmx node\n"); goto out; } /* * Create a new 'ptmx' node in this mount of devpts. */ inode = new_inode(sb); if (!inode) { pr_err("Unable to alloc inode for ptmx node\n"); dput(dentry); goto out; } inode->i_ino = 2; inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); mode = S_IFCHR|opts->ptmxmode; init_special_inode(inode, mode, MKDEV(TTYAUX_MAJOR, 2)); inode->i_uid = ptmx_uid; inode->i_gid = ptmx_gid; d_add(dentry, inode); fsi->ptmx_dentry = dentry; rc = 0; out: inode_unlock(d_inode(root)); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu19683.76%114.29%
eric w. biedermaneric w. biederman2410.26%228.57%
david howellsdavid howells62.56%114.29%
deepa dinamanideepa dinamani41.71%114.29%
al viroal viro20.85%114.29%
fabian frederickfabian frederick20.85%114.29%
Total234100.00%7100.00%


static void update_ptmx_mode(struct pts_fs_info *fsi) { struct inode *inode; if (fsi->ptmx_dentry) { inode = d_inode(fsi->ptmx_dentry); inode->i_mode = S_IFCHR|fsi->mount_opts.ptmxmode; } }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu4293.33%150.00%
david howellsdavid howells36.67%150.00%
Total45100.00%2100.00%


static int devpts_remount(struct super_block *sb, int *flags, char *data) { int err; struct pts_fs_info *fsi = DEVPTS_SB(sb); struct pts_mount_opts *opts = &fsi->mount_opts; err = parse_mount_options(data, PARSE_REMOUNT, opts); /* * parse_mount_options() restores options to default values * before parsing and may have changed ptmxmode. So, update the * mode in the inode too. Bogus options don't fail the remount, * so do this even on error return. */ update_ptmx_mode(fsi); return err; }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu62100.00%3100.00%
Total62100.00%3100.00%


static int devpts_show_options(struct seq_file *seq, struct dentry *root) { struct pts_fs_info *fsi = DEVPTS_SB(root->d_sb); struct pts_mount_opts *opts = &fsi->mount_opts; if (opts->setuid) seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, opts->uid)); if (opts->setgid) seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, opts->gid)); seq_printf(seq, ",mode=%03o", opts->mode); seq_printf(seq, ",ptmxmode=%03o", opts->ptmxmode); if (opts->max < NR_UNIX98_PTY_MAX) seq_printf(seq, ",max=%d", opts->max); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
miklos szeredimiklos szeredi5240.62%116.67%
sukadev bhattiprolusukadev bhattiprolu4132.03%233.33%
konstantin khlebnikovkonstantin khlebnikov1914.84%116.67%
eric w. biedermaneric w. biederman129.38%116.67%
al viroal viro43.12%116.67%
Total128100.00%6100.00%

static const struct super_operations devpts_sops = { .statfs = simple_statfs, .remount_fs = devpts_remount, .show_options = devpts_show_options, };
static void *new_pts_fs_info(struct super_block *sb) { struct pts_fs_info *fsi; fsi = kzalloc(sizeof(struct pts_fs_info), GFP_KERNEL); if (!fsi) return NULL; ida_init(&fsi->allocated_ptys); fsi->mount_opts.mode = DEVPTS_DEFAULT_MODE; fsi->mount_opts.ptmxmode = DEVPTS_DEFAULT_PTMX_MODE; fsi->sb = sb; return fsi; }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu6185.92%375.00%
linus torvaldslinus torvalds1014.08%125.00%
Total71100.00%4100.00%


static int devpts_fill_super(struct super_block *s, void *data, int silent) { struct inode *inode; int error; s->s_iflags &= ~SB_I_NODEV; s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = DEVPTS_SUPER_MAGIC; s->s_op = &devpts_sops; s->s_time_gran = 1; error = -ENOMEM; s->s_fs_info = new_pts_fs_info(s); if (!s->s_fs_info) goto fail; error = parse_mount_options(data, PARSE_MOUNT, &DEVPTS_SB(s)->mount_opts); if (error) goto fail; error = -ENOMEM; inode = new_inode(s); if (!inode) goto fail; inode->i_ino = 1; inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; set_nlink(inode, 2); s->s_root = d_make_root(inode); if (!s->s_root) { pr_err("get root dentry failed\n"); goto fail; } error = mknod_ptmx(s); if (error) goto fail_dput; return 0; fail_dput: dput(s->s_root); s->s_root = NULL; fail: return error; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git8433.47%317.65%
eric w. biedermaneric w. biederman8232.67%317.65%
linus torvaldslinus torvalds2811.16%211.76%
al viroal viro2710.76%423.53%
sukadev bhattiprolusukadev bhattiprolu166.37%15.88%
andi kleenandi kleen41.59%15.88%
miklos szeredimiklos szeredi41.59%15.88%
deepa dinamanideepa dinamani41.59%15.88%
james morrisjames morris20.80%15.88%
Total251100.00%17100.00%

/* * devpts_mount() * * Mount a new (private) instance of devpts. PTYs created in this * instance are independent of the PTYs in other devpts instances. */
static struct dentry *devpts_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, devpts_fill_super); }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu2772.97%457.14%
al viroal viro513.51%114.29%
eric w. biedermaneric w. biederman38.11%114.29%
david howellsdavid howells25.41%114.29%
Total37100.00%7100.00%


static void devpts_kill_sb(struct super_block *sb) { struct pts_fs_info *fsi = DEVPTS_SB(sb); if (fsi) ida_destroy(&fsi->allocated_ptys); kfree(fsi); kill_litter_super(sb); }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu3172.09%250.00%
ilija hadzicilija hadzic818.60%125.00%
eric w. biedermaneric w. biederman49.30%125.00%
Total43100.00%4100.00%

static struct file_system_type devpts_fs_type = { .name = "devpts", .mount = devpts_mount, .kill_sb = devpts_kill_sb, .fs_flags = FS_USERNS_MOUNT, }; /* * The normal naming convention is simply /dev/pts/<number>; this conforms * to the System V naming convention */
int devpts_new_index(struct pts_fs_info *fsi) { int index; int ida_ret; retry: if (!ida_pre_get(&fsi->allocated_ptys, GFP_KERNEL)) return -ENOMEM; mutex_lock(&allocated_ptys_lock); if (pty_count >= (pty_limit - (fsi->mount_opts.reserve ? 0 : pty_reserve))) { mutex_unlock(&allocated_ptys_lock); return -ENOSPC; } ida_ret = ida_get_new(&fsi->allocated_ptys, &index); if (ida_ret < 0) { mutex_unlock(&allocated_ptys_lock); if (ida_ret == -EAGAIN) goto retry; return -EIO; } if (index >= fsi->mount_opts.max) { ida_remove(&fsi->allocated_ptys, index); mutex_unlock(&allocated_ptys_lock); return -ENOSPC; } pty_count++; mutex_unlock(&allocated_ptys_lock); return index; }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu10868.35%337.50%
konstantin khlebnikovkonstantin khlebnikov3622.78%225.00%
alexey dobriyanalexey dobriyan74.43%112.50%
eric w. biedermaneric w. biederman53.16%112.50%
linus torvaldslinus torvalds21.27%112.50%
Total158100.00%8100.00%


void devpts_kill_index(struct pts_fs_info *fsi, int idx) { mutex_lock(&allocated_ptys_lock); ida_remove(&fsi->allocated_ptys, idx); pty_count--; mutex_unlock(&allocated_ptys_lock); }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu3284.21%350.00%
konstantin khlebnikovkonstantin khlebnikov37.89%116.67%
linus torvaldslinus torvalds25.26%116.67%
alexey dobriyanalexey dobriyan12.63%116.67%
Total38100.00%6100.00%

/** * devpts_pty_new -- create a new inode in /dev/pts/ * @ptmx_inode: inode of the master * @device: major+minor of the node to be created * @index: used as a name of the node * @priv: what's given back by devpts_get_priv * * The created inode is returned. Remove it from /dev/pts/ by devpts_pty_kill. */
struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv) { struct dentry *dentry; struct super_block *sb = fsi->sb; struct inode *inode; struct dentry *root; struct pts_mount_opts *opts; char s[12]; root = sb->s_root; opts = &fsi->mount_opts; inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); inode->i_ino = index + 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(inode); init_special_inode(inode, S_IFCHR|opts->mode, MKDEV(UNIX98_PTY_SLAVE_MAJOR, index)); sprintf(s, "%d", index); dentry = d_alloc_name(root, s); if (dentry) { dentry->d_fsdata = priv; d_add(dentry, inode); fsnotify_create(d_inode(root), dentry); } else { iput(inode); dentry = ERR_PTR(-ENOMEM); } return dentry; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git7031.11%313.64%
sukadev bhattiprolusukadev bhattiprolu3917.33%522.73%
jiri slabyjiri slaby229.78%29.09%
linus torvaldslinus torvalds208.89%29.09%
david howellsdavid howells167.11%313.64%
andrey vaginandrey vagin125.33%14.55%
andrew mortonandrew morton104.44%14.55%
josh triplettjosh triplett104.44%14.55%
florin malitaflorin malita94.00%14.55%
al viroal viro94.00%14.55%
deepa dinamanideepa dinamani41.78%14.55%
eric w. biedermaneric w. biederman41.78%14.55%
Total225100.00%22100.00%

/** * devpts_get_priv -- get private data for a slave * @pts_inode: inode of the slave * * Returns whatever was passed as priv in devpts_pty_new for a given inode. */
void *devpts_get_priv(struct dentry *dentry) { if (dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC) return NULL; return dentry->d_fsdata; }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds1344.83%225.00%
sukadev bhattiprolusukadev bhattiprolu724.14%337.50%
andrew mortonandrew morton620.69%112.50%
jiri slabyjiri slaby26.90%112.50%
pre-gitpre-git13.45%112.50%
Total29100.00%8100.00%

/** * devpts_pty_kill -- remove inode form /dev/pts/ * @inode: inode of the slave to be removed * * This is an inverse operation of devpts_pty_new. */
void devpts_pty_kill(struct dentry *dentry) { WARN_ON_ONCE(dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC); dentry->d_fsdata = NULL; drop_nlink(dentry->d_inode); d_delete(dentry); dput(dentry); /* d_alloc_name() in devpts_pty_new() */ }

Contributors

PersonTokensPropCommitsCommitProp
linus torvaldslinus torvalds1328.89%111.11%
sukadev bhattiprolusukadev bhattiprolu1124.44%333.33%
al viroal viro920.00%111.11%
pre-gitpre-git613.33%111.11%
miklos szeredimiklos szeredi36.67%111.11%
jiri slabyjiri slaby24.44%111.11%
alan coxalan cox12.22%111.11%
Total45100.00%9100.00%


static int __init init_devpts_fs(void) { int err = register_filesystem(&devpts_fs_type); if (!err) { register_sysctl_table(pty_root_table); } return err; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2575.76%350.00%
konstantin khlebnikovkonstantin khlebnikov515.15%116.67%
linus torvaldslinus torvalds26.06%116.67%
james morrisjames morris13.03%116.67%
Total33100.00%6100.00%

module_init(init_devpts_fs)

Overall Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu86035.41%1519.74%
eric w. biedermaneric w. biederman36414.99%911.84%
konstantin khlebnikovkonstantin khlebnikov32113.22%22.63%
pre-gitpre-git26410.87%67.89%
domen puncerdomen puncer1335.48%11.32%
al viroal viro1184.86%810.53%
linus torvaldslinus torvalds1104.53%67.89%
miklos szeredimiklos szeredi733.01%33.95%
jiri slabyjiri slaby291.19%45.26%
david howellsdavid howells271.11%45.26%
andrew mortonandrew morton230.95%22.63%
josh triplettjosh triplett150.62%11.32%
andrey vaginandrey vagin120.49%11.32%
florin malitaflorin malita120.49%11.32%
deepa dinamanideepa dinamani120.49%11.32%
fabian frederickfabian frederick110.45%11.32%
art haasart haas100.41%11.32%
ilija hadzicilija hadzic80.33%11.32%
alexey dobriyanalexey dobriyan80.33%11.32%
andi kleenandi kleen40.16%11.32%
nick blacknick black30.12%11.32%
tejun heotejun heo30.12%11.32%
christoph hellwigchristoph hellwig30.12%11.32%
james morrisjames morris30.12%11.32%
alan coxalan cox10.04%11.32%
josef 'jeff' sipekjosef 'jeff' sipek10.04%11.32%
steven whitehousesteven whitehouse10.04%11.32%
andries brouwerandries brouwer0.00%00.00%
Total2429100.00%76100.00%
Directory: fs/devpts
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.