inode.c 6.7 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
#define DEVPTS_DEFAULT_MODE 0600
S
Sukadev Bhattiprolu 已提交
30
#define PTMX_MINOR	2
M
Miklos Szeredi 已提交
31

32
extern int pty_limit;			/* Config limit on Unix98 ptys */
A
Alexey Dobriyan 已提交
33
static DEFINE_IDA(allocated_ptys);
34 35
static DEFINE_MUTEX(allocated_ptys_lock);

L
Linus Torvalds 已提交
36 37 38 39 40 41 42 43 44
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 已提交
45
} config = {.mode = DEVPTS_DEFAULT_MODE};
L
Linus Torvalds 已提交
46

47 48 49 50 51
enum {
	Opt_uid, Opt_gid, Opt_mode,
	Opt_err
};

52
static const match_table_t tokens = {
53 54 55 56 57 58
	{Opt_uid, "uid=%u"},
	{Opt_gid, "gid=%u"},
	{Opt_mode, "mode=%o"},
	{Opt_err, NULL}
};

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

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

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

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

		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 已提交
94
			config.mode = option & S_IALLUGO;
95 96 97
			break;
		default:
			printk(KERN_ERR "devpts: called with bogus options\n");
L
Linus Torvalds 已提交
98 99 100 101 102 103 104
			return -EINVAL;
		}
	}

	return 0;
}

M
Miklos Szeredi 已提交
105 106 107 108 109 110 111 112 113 114 115
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;
}

116
static const struct super_operations devpts_sops = {
L
Linus Torvalds 已提交
117 118
	.statfs		= simple_statfs,
	.remount_fs	= devpts_remount,
M
Miklos Szeredi 已提交
119
	.show_options	= devpts_show_options,
L
Linus Torvalds 已提交
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 154
};

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;
}

155 156
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 已提交
157
{
158
	return get_sb_single(fs_type, flags, data, devpts_fill_super, mnt);
L
Linus Torvalds 已提交
159 160 161 162 163 164 165 166 167 168 169 170 171 172
}

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
 */

173
int devpts_new_index(struct inode *ptmx_inode)
174 175
{
	int index;
A
Alexey Dobriyan 已提交
176
	int ida_ret;
177 178

retry:
A
Alexey Dobriyan 已提交
179
	if (!ida_pre_get(&allocated_ptys, GFP_KERNEL)) {
180 181 182 183
		return -ENOMEM;
	}

	mutex_lock(&allocated_ptys_lock);
A
Alexey Dobriyan 已提交
184 185
	ida_ret = ida_get_new(&allocated_ptys, &index);
	if (ida_ret < 0) {
186
		mutex_unlock(&allocated_ptys_lock);
A
Alexey Dobriyan 已提交
187
		if (ida_ret == -EAGAIN)
188 189 190 191 192
			goto retry;
		return -EIO;
	}

	if (index >= pty_limit) {
A
Alexey Dobriyan 已提交
193
		ida_remove(&allocated_ptys, index);
194 195 196 197 198 199 200
		mutex_unlock(&allocated_ptys_lock);
		return -EIO;
	}
	mutex_unlock(&allocated_ptys_lock);
	return index;
}

201
void devpts_kill_index(struct inode *ptmx_inode, int idx)
202 203
{
	mutex_lock(&allocated_ptys_lock);
A
Alexey Dobriyan 已提交
204
	ida_remove(&allocated_ptys, idx);
205 206 207
	mutex_unlock(&allocated_ptys_lock);
}

208
int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty)
L
Linus Torvalds 已提交
209
{
210
	int number = tty->index; /* tty layer puts index from devpts_new_index() in here */
L
Linus Torvalds 已提交
211 212 213 214
	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);
S
Sukadev Bhattiprolu 已提交
215
	char s[12];
L
Linus Torvalds 已提交
216 217 218 219 220 221 222 223 224

	/* 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;
225 226
	inode->i_uid = config.setuid ? config.uid : current_fsuid();
	inode->i_gid = config.setgid ? config.gid : current_fsgid();
L
Linus Torvalds 已提交
227 228
	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
	init_special_inode(inode, S_IFCHR|config.mode, device);
229
	inode->i_private = tty;
S
Sukadev Bhattiprolu 已提交
230
	tty->driver_data = inode;
L
Linus Torvalds 已提交
231

S
Sukadev Bhattiprolu 已提交
232 233 234 235 236 237 238
	sprintf(s, "%d", number);

	mutex_lock(&devpts_root->d_inode->i_mutex);

	dentry = d_alloc_name(devpts_root, s);
	if (!IS_ERR(dentry)) {
		d_add(dentry, inode);
239 240
		fsnotify_create(devpts_root->d_inode, dentry);
	}
L
Linus Torvalds 已提交
241

242
	mutex_unlock(&devpts_root->d_inode->i_mutex);
L
Linus Torvalds 已提交
243 244 245 246

	return 0;
}

247
struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number)
L
Linus Torvalds 已提交
248
{
S
Sukadev Bhattiprolu 已提交
249
	BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR));
L
Linus Torvalds 已提交
250

S
Sukadev Bhattiprolu 已提交
251 252 253
	if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)
		return (struct tty_struct *)pts_inode->i_private;
	return NULL;
L
Linus Torvalds 已提交
254 255
}

256
void devpts_pty_kill(struct tty_struct *tty)
L
Linus Torvalds 已提交
257
{
S
Sukadev Bhattiprolu 已提交
258 259
	struct inode *inode = tty->driver_data;
	struct dentry *dentry;
L
Linus Torvalds 已提交
260

S
Sukadev Bhattiprolu 已提交
261 262 263 264 265 266 267 268
	BUG_ON(inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR));

	mutex_lock(&devpts_root->d_inode->i_mutex);

	dentry = d_find_alias(inode);
	if (dentry && !IS_ERR(dentry)) {
		inode->i_nlink--;
		d_delete(dentry);
L
Linus Torvalds 已提交
269 270
		dput(dentry);
	}
S
Sukadev Bhattiprolu 已提交
271

272
	mutex_unlock(&devpts_root->d_inode->i_mutex);
L
Linus Torvalds 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
}

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");