inode.c 6.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* -*- 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.
 *
 * ------------------------------------------------------------------------- */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/tty.h>
20 21
#include <linux/mutex.h>
#include <linux/idr.h>
L
Linus Torvalds 已提交
22
#include <linux/devpts_fs.h>
23
#include <linux/parser.h>
24
#include <linux/fsnotify.h>
M
Miklos Szeredi 已提交
25
#include <linux/seq_file.h>
L
Linus Torvalds 已提交
26 27 28

#define DEVPTS_SUPER_MAGIC 0x1cd1

M
Miklos Szeredi 已提交
29 30
#define DEVPTS_DEFAULT_MODE 0600

31 32 33 34
extern int pty_limit;			/* Config limit on Unix98 ptys */
static DEFINE_IDR(allocated_ptys);
static DEFINE_MUTEX(allocated_ptys_lock);

L
Linus Torvalds 已提交
35 36 37 38 39 40 41 42 43
static struct vfsmount *devpts_mnt;
static struct dentry *devpts_root;

static struct {
	int setuid;
	int setgid;
	uid_t   uid;
	gid_t   gid;
	umode_t mode;
M
Miklos Szeredi 已提交
44
} config = {.mode = DEVPTS_DEFAULT_MODE};
L
Linus Torvalds 已提交
45

46 47 48 49 50 51 52 53 54 55 56 57
enum {
	Opt_uid, Opt_gid, Opt_mode,
	Opt_err
};

static match_table_t tokens = {
	{Opt_uid, "uid=%u"},
	{Opt_gid, "gid=%u"},
	{Opt_mode, "mode=%o"},
	{Opt_err, NULL}
};

L
Linus Torvalds 已提交
58 59
static int devpts_remount(struct super_block *sb, int *flags, char *data)
{
60 61 62 63 64 65
	char *p;

	config.setuid  = 0;
	config.setgid  = 0;
	config.uid     = 0;
	config.gid     = 0;
M
Miklos Szeredi 已提交
66
	config.mode    = DEVPTS_DEFAULT_MODE;
67 68 69 70 71 72 73

	while ((p = strsep(&data, ",")) != NULL) {
		substring_t args[MAX_OPT_ARGS];
		int token;
		int option;

		if (!*p)
L
Linus Torvalds 已提交
74
			continue;
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

		token = match_token(p, tokens, args);
		switch (token) {
		case Opt_uid:
			if (match_int(&args[0], &option))
				return -EINVAL;
			config.uid = option;
			config.setuid = 1;
			break;
		case Opt_gid:
			if (match_int(&args[0], &option))
				return -EINVAL;
			config.gid = option;
			config.setgid = 1;
			break;
		case Opt_mode:
			if (match_octal(&args[0], &option))
				return -EINVAL;
M
Miklos Szeredi 已提交
93
			config.mode = option & S_IALLUGO;
94 95 96
			break;
		default:
			printk(KERN_ERR "devpts: called with bogus options\n");
L
Linus Torvalds 已提交
97 98 99 100 101 102 103
			return -EINVAL;
		}
	}

	return 0;
}

M
Miklos Szeredi 已提交
104 105 106 107 108 109 110 111 112 113 114
static int devpts_show_options(struct seq_file *seq, struct vfsmount *vfs)
{
	if (config.setuid)
		seq_printf(seq, ",uid=%u", config.uid);
	if (config.setgid)
		seq_printf(seq, ",gid=%u", config.gid);
	seq_printf(seq, ",mode=%03o", config.mode);

	return 0;
}

115
static const struct super_operations devpts_sops = {
L
Linus Torvalds 已提交
116 117
	.statfs		= simple_statfs,
	.remount_fs	= devpts_remount,
M
Miklos Szeredi 已提交
118
	.show_options	= devpts_show_options,
L
Linus Torvalds 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
};

static int
devpts_fill_super(struct super_block *s, void *data, int silent)
{
	struct inode * inode;

	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;

	inode = new_inode(s);
	if (!inode)
		goto fail;
	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;
	inode->i_nlink = 2;

	devpts_root = s->s_root = d_alloc_root(inode);
	if (s->s_root)
		return 0;
	
	printk("devpts: get root dentry failed\n");
	iput(inode);
fail:
	return -ENOMEM;
}

154 155
static int devpts_get_sb(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
L
Linus Torvalds 已提交
156
{
157
	return get_sb_single(fs_type, flags, data, devpts_fill_super, mnt);
L
Linus Torvalds 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
}

static struct file_system_type devpts_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "devpts",
	.get_sb		= devpts_get_sb,
	.kill_sb	= kill_anon_super,
};

/*
 * The normal naming convention is simply /dev/pts/<number>; this conforms
 * to the System V naming convention
 */

static struct dentry *get_node(int num)
{
	char s[12];
	struct dentry *root = devpts_root;
176
	mutex_lock(&root->d_inode->i_mutex);
L
Linus Torvalds 已提交
177 178 179
	return lookup_one_len(s, root, sprintf(s, "%d", num));
}

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
int devpts_new_index(void)
{
	int index;
	int idr_ret;

retry:
	if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
		return -ENOMEM;
	}

	mutex_lock(&allocated_ptys_lock);
	idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
	if (idr_ret < 0) {
		mutex_unlock(&allocated_ptys_lock);
		if (idr_ret == -EAGAIN)
			goto retry;
		return -EIO;
	}

	if (index >= pty_limit) {
		idr_remove(&allocated_ptys, index);
		mutex_unlock(&allocated_ptys_lock);
		return -EIO;
	}
	mutex_unlock(&allocated_ptys_lock);
	return index;
}

void devpts_kill_index(int idx)
{
	mutex_lock(&allocated_ptys_lock);
	idr_remove(&allocated_ptys, idx);
	mutex_unlock(&allocated_ptys_lock);
}

L
Linus Torvalds 已提交
215 216
int devpts_pty_new(struct tty_struct *tty)
{
217
	int number = tty->index; /* tty layer puts index from devpts_new_index() in here */
L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
	struct tty_driver *driver = tty->driver;
	dev_t device = MKDEV(driver->major, driver->minor_start+number);
	struct dentry *dentry;
	struct inode *inode = new_inode(devpts_mnt->mnt_sb);

	/* We're supposed to be given the slave end of a pty */
	BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY);
	BUG_ON(driver->subtype != PTY_TYPE_SLAVE);

	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_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
	init_special_inode(inode, S_IFCHR|config.mode, device);
235
	inode->i_private = tty;
L
Linus Torvalds 已提交
236 237

	dentry = get_node(number);
238
	if (!IS_ERR(dentry) && !dentry->d_inode) {
L
Linus Torvalds 已提交
239
		d_instantiate(dentry, inode);
240 241
		fsnotify_create(devpts_root->d_inode, dentry);
	}
L
Linus Torvalds 已提交
242

243
	mutex_unlock(&devpts_root->d_inode->i_mutex);
L
Linus Torvalds 已提交
244 245 246 247 248 249 250 251 252 253 254 255

	return 0;
}

struct tty_struct *devpts_get_tty(int number)
{
	struct dentry *dentry = get_node(number);
	struct tty_struct *tty;

	tty = NULL;
	if (!IS_ERR(dentry)) {
		if (dentry->d_inode)
256
			tty = dentry->d_inode->i_private;
L
Linus Torvalds 已提交
257 258 259
		dput(dentry);
	}

260
	mutex_unlock(&devpts_root->d_inode->i_mutex);
L
Linus Torvalds 已提交
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

	return tty;
}

void devpts_pty_kill(int number)
{
	struct dentry *dentry = get_node(number);

	if (!IS_ERR(dentry)) {
		struct inode *inode = dentry->d_inode;
		if (inode) {
			inode->i_nlink--;
			d_delete(dentry);
			dput(dentry);
		}
		dput(dentry);
	}
278
	mutex_unlock(&devpts_root->d_inode->i_mutex);
L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
}

static int __init init_devpts_fs(void)
{
	int err = register_filesystem(&devpts_fs_type);
	if (!err) {
		devpts_mnt = kern_mount(&devpts_fs_type);
		if (IS_ERR(devpts_mnt))
			err = PTR_ERR(devpts_mnt);
	}
	return err;
}

static void __exit exit_devpts_fs(void)
{
	unregister_filesystem(&devpts_fs_type);
	mntput(devpts_mnt);
}

module_init(init_devpts_fs)
module_exit(exit_devpts_fs)
MODULE_LICENSE("GPL");