Blame view

drivers/char/raw.c 8.31 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * linux/drivers/char/raw.c
   *
   * Front-end raw character devices.  These can be bound to any block
   * devices to provide genuine Unix raw character device semantics.
   *
   * We reserve minor number 0 for a control interface.  ioctl()s on this
   * device are used to bind the other minor numbers to block devices.
   */
  
  #include <linux/init.h>
  #include <linux/fs.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
  #include <linux/major.h>
  #include <linux/blkdev.h>
66114cad6   Tejun Heo   writeback: separa...
15
  #include <linux/backing-dev.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
21
  #include <linux/module.h>
  #include <linux/raw.h>
  #include <linux/capability.h>
  #include <linux/uio.h>
  #include <linux/cdev.h>
  #include <linux/device.h>
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
22
  #include <linux/mutex.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
23
  #include <linux/gfp.h>
c4a047272   Al Viro   fix rawctl compat...
24
  #include <linux/compat.h>
0078bff52   Jan Kara   Allow setting of ...
25
  #include <linux/vmalloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
27
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
32
  
  struct raw_device_data {
  	struct block_device *binding;
  	int inuse;
  };
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
33
  static struct class *raw_class;
0078bff52   Jan Kara   Allow setting of ...
34
  static struct raw_device_data *raw_devices;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
35
  static DEFINE_MUTEX(raw_mutex);
62322d255   Arjan van de Ven   [PATCH] make more...
36
  static const struct file_operations raw_ctl_fops; /* forward declaration */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37

0078bff52   Jan Kara   Allow setting of ...
38
39
40
41
  static int max_raw_minors = MAX_RAW_MINORS;
  
  module_param(max_raw_minors, int, 0);
  MODULE_PARM_DESC(max_raw_minors, "Maximum number of raw devices (1-65536)");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  /*
   * Open/close code for raw IO.
   *
   * We just rewrite the i_mapping for the /dev/raw/rawN file descriptor to
   * point at the blockdev's address_space and set the file handle to use
   * O_DIRECT.
   *
   * Set the device's soft blocksize to the minimum possible.  This gives the
   * finest possible alignment and has no adverse impact on performance.
   */
  static int raw_open(struct inode *inode, struct file *filp)
  {
  	const int minor = iminor(inode);
  	struct block_device *bdev;
  	int err;
  
  	if (minor == 0) {	/* It is the control device */
  		filp->f_op = &raw_ctl_fops;
  		return 0;
  	}
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
62
  	mutex_lock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
68
69
70
  
  	/*
  	 * All we need to do on open is check that the device is bound.
  	 */
  	bdev = raw_devices[minor].binding;
  	err = -ENODEV;
  	if (!bdev)
  		goto out;
ed8a9d2c8   Ilya Dryomov   block: use bd{gra...
71
  	bdgrab(bdev);
e525fd89d   Tejun Heo   block: make blkde...
72
  	err = blkdev_get(bdev, filp->f_mode | FMODE_EXCL, raw_open);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
  	if (err)
  		goto out;
e1defc4ff   Martin K. Petersen   block: Do away wi...
75
  	err = set_blocksize(bdev, bdev_logical_block_size(bdev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  	if (err)
e525fd89d   Tejun Heo   block: make blkde...
77
  		goto out1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
  	filp->f_flags |= O_DIRECT;
  	filp->f_mapping = bdev->bd_inode->i_mapping;
  	if (++raw_devices[minor].inuse == 1)
496ad9aa8   Al Viro   new helper: file_...
81
  		file_inode(filp)->i_mapping =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
  			bdev->bd_inode->i_mapping;
  	filp->private_data = bdev;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
84
  	mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  out1:
e525fd89d   Tejun Heo   block: make blkde...
87
  	blkdev_put(bdev, filp->f_mode | FMODE_EXCL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  out:
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
89
  	mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
95
96
97
98
99
100
  	return err;
  }
  
  /*
   * When the final fd which refers to this character-special node is closed, we
   * make its ->mapping point back at its own i_data.
   */
  static int raw_release(struct inode *inode, struct file *filp)
  {
  	const int minor= iminor(inode);
  	struct block_device *bdev;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
101
  	mutex_lock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  	bdev = raw_devices[minor].binding;
b83ae6d42   Christoph Hellwig   fs: remove mappin...
103
  	if (--raw_devices[minor].inuse == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
  		/* Here  inode->i_mapping == bdev->bd_inode->i_mapping  */
  		inode->i_mapping = &inode->i_data;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
106
  	mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107

e525fd89d   Tejun Heo   block: make blkde...
108
  	blkdev_put(bdev, filp->f_mode | FMODE_EXCL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
  	return 0;
  }
  
  /*
   * Forward ioctls to the underlying block device.
   */
55929332c   Arnd Bergmann   drivers: Push dow...
115
116
  static long
  raw_ioctl(struct file *filp, unsigned int command, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
  {
  	struct block_device *bdev = filp->private_data;
c4a047272   Al Viro   fix rawctl compat...
119
120
121
122
123
124
125
126
  	return blkdev_ioctl(bdev, 0, command, arg);
  }
  
  static int bind_set(int number, u64 major, u64 minor)
  {
  	dev_t dev = MKDEV(major, minor);
  	struct raw_device_data *rawdev;
  	int err = 0;
0078bff52   Jan Kara   Allow setting of ...
127
  	if (number <= 0 || number >= max_raw_minors)
c4a047272   Al Viro   fix rawctl compat...
128
129
130
131
132
133
  		return -EINVAL;
  
  	if (MAJOR(dev) != major || MINOR(dev) != minor)
  		return -EINVAL;
  
  	rawdev = &raw_devices[number];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

c4a047272   Al Viro   fix rawctl compat...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  	/*
  	 * This is like making block devices, so demand the
  	 * same capability
  	 */
  	if (!capable(CAP_SYS_ADMIN))
  		return -EPERM;
  
  	/*
  	 * For now, we don't need to check that the underlying
  	 * block device is present or not: we can do that when
  	 * the raw device is opened.  Just check that the
  	 * major/minor numbers make sense.
  	 */
  
  	if (MAJOR(dev) == 0 && dev != 0)
  		return -EINVAL;
55929332c   Arnd Bergmann   drivers: Push dow...
151

c4a047272   Al Viro   fix rawctl compat...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  	mutex_lock(&raw_mutex);
  	if (rawdev->inuse) {
  		mutex_unlock(&raw_mutex);
  		return -EBUSY;
  	}
  	if (rawdev->binding) {
  		bdput(rawdev->binding);
  		module_put(THIS_MODULE);
  	}
  	if (!dev) {
  		/* unbind */
  		rawdev->binding = NULL;
  		device_destroy(raw_class, MKDEV(RAW_MAJOR, number));
  	} else {
  		rawdev->binding = bdget(dev);
  		if (rawdev->binding == NULL) {
  			err = -ENOMEM;
  		} else {
  			dev_t raw = MKDEV(RAW_MAJOR, number);
  			__module_get(THIS_MODULE);
  			device_destroy(raw_class, raw);
  			device_create(raw_class, NULL, raw, NULL,
  				      "raw%d", number);
  		}
  	}
  	mutex_unlock(&raw_mutex);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  }
c4a047272   Al Viro   fix rawctl compat...
180
  static int bind_get(int number, dev_t *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  {
c4a047272   Al Viro   fix rawctl compat...
182
183
  	struct raw_device_data *rawdev;
  	struct block_device *bdev;
5bbb2ae3d   Paul Bolle   raw: test against...
184
  	if (number <= 0 || number >= max_raw_minors)
c4a047272   Al Viro   fix rawctl compat...
185
186
187
188
189
190
191
192
193
  		return -EINVAL;
  
  	rawdev = &raw_devices[number];
  
  	mutex_lock(&raw_mutex);
  	bdev = rawdev->binding;
  	*dev = bdev ? bdev->bd_dev : 0;
  	mutex_unlock(&raw_mutex);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
198
199
  }
  
  /*
   * Deal with ioctls against the raw-device control interface, to bind
   * and unbind other raw devices.
   */
55929332c   Arnd Bergmann   drivers: Push dow...
200
201
  static long raw_ctl_ioctl(struct file *filp, unsigned int command,
  			  unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
  {
  	struct raw_config_request rq;
c4a047272   Al Viro   fix rawctl compat...
204
205
  	dev_t dev;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
  
  	switch (command) {
  	case RAW_SETBIND:
c4a047272   Al Viro   fix rawctl compat...
209
210
211
212
  		if (copy_from_user(&rq, (void __user *) arg, sizeof(rq)))
  			return -EFAULT;
  
  		return bind_set(rq.raw_minor, rq.block_major, rq.block_minor);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
  	case RAW_GETBIND:
c4a047272   Al Viro   fix rawctl compat...
214
215
  		if (copy_from_user(&rq, (void __user *) arg, sizeof(rq)))
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216

c4a047272   Al Viro   fix rawctl compat...
217
218
219
  		err = bind_get(rq.raw_minor, &dev);
  		if (err)
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220

c4a047272   Al Viro   fix rawctl compat...
221
222
  		rq.block_major = MAJOR(dev);
  		rq.block_minor = MINOR(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223

c4a047272   Al Viro   fix rawctl compat...
224
225
226
227
  		if (copy_to_user((void __user *)arg, &rq, sizeof(rq)))
  			return -EFAULT;
  
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  	}
c4a047272   Al Viro   fix rawctl compat...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  
  	return -EINVAL;
  }
  
  #ifdef CONFIG_COMPAT
  struct raw32_config_request {
  	compat_int_t	raw_minor;
  	compat_u64	block_major;
  	compat_u64	block_minor;
  };
  
  static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd,
  				unsigned long arg)
  {
  	struct raw32_config_request __user *user_req = compat_ptr(arg);
  	struct raw32_config_request rq;
  	dev_t dev;
  	int err = 0;
  
  	switch (cmd) {
  	case RAW_SETBIND:
  		if (copy_from_user(&rq, user_req, sizeof(rq)))
  			return -EFAULT;
  
  		return bind_set(rq.raw_minor, rq.block_major, rq.block_minor);
  
  	case RAW_GETBIND:
  		if (copy_from_user(&rq, user_req, sizeof(rq)))
  			return -EFAULT;
  
  		err = bind_get(rq.raw_minor, &dev);
  		if (err)
  			return err;
  
  		rq.block_major = MAJOR(dev);
  		rq.block_minor = MINOR(dev);
  
  		if (copy_to_user(user_req, &rq, sizeof(rq)))
  			return -EFAULT;
  
  		return 0;
  	}
  
  	return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  }
c4a047272   Al Viro   fix rawctl compat...
274
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275

62322d255   Arjan van de Ven   [PATCH] make more...
276
  static const struct file_operations raw_fops = {
b2de525f0   David Jeffery   Return short read...
277
  	.read_iter	= blkdev_read_iter,
1456c0a87   Al Viro   blkdev_aio_write(...
278
  	.write_iter	= blkdev_write_iter,
55929332c   Arnd Bergmann   drivers: Push dow...
279
280
281
282
  	.fsync		= blkdev_fsync,
  	.open		= raw_open,
  	.release	= raw_release,
  	.unlocked_ioctl = raw_ioctl,
cb3b9cf81   Arnd Bergmann   raw: use explicit...
283
  	.llseek		= default_llseek,
55929332c   Arnd Bergmann   drivers: Push dow...
284
  	.owner		= THIS_MODULE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  };
62322d255   Arjan van de Ven   [PATCH] make more...
286
  static const struct file_operations raw_ctl_fops = {
55929332c   Arnd Bergmann   drivers: Push dow...
287
  	.unlocked_ioctl = raw_ctl_ioctl,
c4a047272   Al Viro   fix rawctl compat...
288
289
290
  #ifdef CONFIG_COMPAT
  	.compat_ioctl	= raw_ctl_compat_ioctl,
  #endif
55929332c   Arnd Bergmann   drivers: Push dow...
291
292
  	.open		= raw_open,
  	.owner		= THIS_MODULE,
cb3b9cf81   Arnd Bergmann   raw: use explicit...
293
  	.llseek		= noop_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  };
7e7654a92   Greg Kroah-Hartman   cdev: remove unne...
295
  static struct cdev raw_cdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296

2c9ede55e   Al Viro   switch device_get...
297
  static char *raw_devnode(struct device *dev, umode_t *mode)
6fd469337   Kay Sievers   Driver Core: raw:...
298
299
300
  {
  	return kasprintf(GFP_KERNEL, "raw/%s", dev_name(dev));
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
  static int __init raw_init(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
  	dev_t dev = MKDEV(RAW_MAJOR, 0);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
304
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305

0078bff52   Jan Kara   Allow setting of ...
306
307
308
309
310
311
  	if (max_raw_minors < 1 || max_raw_minors > 65536) {
  		printk(KERN_WARNING "raw: invalid max_raw_minors (must be"
  			" between 1 and 65536), using %d
  ", MAX_RAW_MINORS);
  		max_raw_minors = MAX_RAW_MINORS;
  	}
8e03bd652   Joe Perches   char: Convert vma...
312
  	raw_devices = vzalloc(sizeof(struct raw_device_data) * max_raw_minors);
0078bff52   Jan Kara   Allow setting of ...
313
314
315
316
317
318
  	if (!raw_devices) {
  		printk(KERN_ERR "Not enough memory for raw device structures
  ");
  		ret = -ENOMEM;
  		goto error;
  	}
0078bff52   Jan Kara   Allow setting of ...
319
320
  
  	ret = register_chrdev_region(dev, max_raw_minors, "raw");
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
321
  	if (ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
324
  		goto error;
  
  	cdev_init(&raw_cdev, &raw_fops);
0078bff52   Jan Kara   Allow setting of ...
325
  	ret = cdev_add(&raw_cdev, dev, max_raw_minors);
202cdb6f8   Bhumika Goyal   drivers: char: ra...
326
  	if (ret)
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
327
  		goto error_region;
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
328
  	raw_class = class_create(THIS_MODULE, "raw");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
330
331
332
  	if (IS_ERR(raw_class)) {
  		printk(KERN_ERR "Error creating raw class.
  ");
  		cdev_del(&raw_cdev);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
333
334
  		ret = PTR_ERR(raw_class);
  		goto error_region;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
  	}
e454cea20   Kay Sievers   Driver-Core: exte...
336
  	raw_class->devnode = raw_devnode;
03457cd45   Greg Kroah-Hartman   device create: ch...
337
  	device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  	return 0;
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
340
  error_region:
0078bff52   Jan Kara   Allow setting of ...
341
  	unregister_chrdev_region(dev, max_raw_minors);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
  error:
0078bff52   Jan Kara   Allow setting of ...
343
  	vfree(raw_devices);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
344
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
348
  }
  
  static void __exit raw_exit(void)
  {
38ca6c34d   Greg Kroah-Hartman   Driver core: conv...
349
  	device_destroy(raw_class, MKDEV(RAW_MAJOR, 0));
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
350
  	class_destroy(raw_class);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  	cdev_del(&raw_cdev);
0078bff52   Jan Kara   Allow setting of ...
352
  	unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), max_raw_minors);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
  }
  
  module_init(raw_init);
  module_exit(raw_exit);
  MODULE_LICENSE("GPL");