Blame view

drivers/char/raw.c 8.43 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
15
16
17
18
19
20
  #include <linux/major.h>
  #include <linux/blkdev.h>
  #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...
21
  #include <linux/mutex.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
22
  #include <linux/gfp.h>
c4a047272   Al Viro   fix rawctl compat...
23
  #include <linux/compat.h>
0078bff52   Jan Kara   Allow setting of ...
24
  #include <linux/vmalloc.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
31
  
  #include <asm/uaccess.h>
  
  struct raw_device_data {
  	struct block_device *binding;
  	int inuse;
  };
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
32
  static struct class *raw_class;
0078bff52   Jan Kara   Allow setting of ...
33
  static struct raw_device_data *raw_devices;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
34
  static DEFINE_MUTEX(raw_mutex);
62322d255   Arjan van de Ven   [PATCH] make more...
35
  static const struct file_operations raw_ctl_fops; /* forward declaration */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

0078bff52   Jan Kara   Allow setting of ...
37
38
39
40
  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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  /*
   * 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...
61
  	mutex_lock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
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;
  	igrab(bdev->bd_inode);
e525fd89d   Tejun Heo   block: make blkde...
71
  	err = blkdev_get(bdev, filp->f_mode | FMODE_EXCL, raw_open);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
  	if (err)
  		goto out;
e1defc4ff   Martin K. Petersen   block: Do away wi...
74
  	err = set_blocksize(bdev, bdev_logical_block_size(bdev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  	if (err)
e525fd89d   Tejun Heo   block: make blkde...
76
  		goto out1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
  	filp->f_flags |= O_DIRECT;
  	filp->f_mapping = bdev->bd_inode->i_mapping;
  	if (++raw_devices[minor].inuse == 1)
a7113a966   Josef Sipek   [PATCH] struct pa...
80
  		filp->f_path.dentry->d_inode->i_mapping =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
  			bdev->bd_inode->i_mapping;
  	filp->private_data = bdev;
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
83
  	mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  out1:
e525fd89d   Tejun Heo   block: make blkde...
86
  	blkdev_put(bdev, filp->f_mode | FMODE_EXCL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  out:
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
88
  	mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
91
92
93
94
95
96
97
98
99
  	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...
100
  	mutex_lock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
106
  	bdev = raw_devices[minor].binding;
  	if (--raw_devices[minor].inuse == 0) {
  		/* Here  inode->i_mapping == bdev->bd_inode->i_mapping  */
  		inode->i_mapping = &inode->i_data;
  		inode->i_mapping->backing_dev_info = &default_backing_dev_info;
  	}
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
107
  	mutex_unlock(&raw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108

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

c4a047272   Al Viro   fix rawctl compat...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  	/*
  	 * 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...
152

c4a047272   Al Viro   fix rawctl compat...
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
179
  	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
180
  }
c4a047272   Al Viro   fix rawctl compat...
181
  static int bind_get(int number, dev_t *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
  {
c4a047272   Al Viro   fix rawctl compat...
183
184
185
186
187
188
189
190
191
192
193
194
195
  	struct raw_device_data *rawdev;
  	struct block_device *bdev;
  
  	if (number <= 0 || number >= MAX_RAW_MINORS)
  		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
196
197
198
199
200
201
  }
  
  /*
   * Deal with ioctls against the raw-device control interface, to bind
   * and unbind other raw devices.
   */
55929332c   Arnd Bergmann   drivers: Push dow...
202
203
  static long raw_ctl_ioctl(struct file *filp, unsigned int command,
  			  unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
  {
  	struct raw_config_request rq;
c4a047272   Al Viro   fix rawctl compat...
206
207
  	dev_t dev;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
  
  	switch (command) {
  	case RAW_SETBIND:
c4a047272   Al Viro   fix rawctl compat...
211
212
213
214
  		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
215
  	case RAW_GETBIND:
c4a047272   Al Viro   fix rawctl compat...
216
217
  		if (copy_from_user(&rq, (void __user *) arg, sizeof(rq)))
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218

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

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

c4a047272   Al Viro   fix rawctl compat...
226
227
228
229
  		if (copy_to_user((void __user *)arg, &rq, sizeof(rq)))
  			return -EFAULT;
  
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  	}
c4a047272   Al Viro   fix rawctl compat...
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
273
274
  
  	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
275
  }
c4a047272   Al Viro   fix rawctl compat...
276
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277

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

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

0078bff52   Jan Kara   Allow setting of ...
310
311
312
313
314
315
  	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...
316
  	raw_devices = vzalloc(sizeof(struct raw_device_data) * max_raw_minors);
0078bff52   Jan Kara   Allow setting of ...
317
318
319
320
321
322
  	if (!raw_devices) {
  		printk(KERN_ERR "Not enough memory for raw device structures
  ");
  		ret = -ENOMEM;
  		goto error;
  	}
0078bff52   Jan Kara   Allow setting of ...
323
324
  
  	ret = register_chrdev_region(dev, max_raw_minors, "raw");
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
325
  	if (ret)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
  		goto error;
  
  	cdev_init(&raw_cdev, &raw_fops);
0078bff52   Jan Kara   Allow setting of ...
329
  	ret = cdev_add(&raw_cdev, dev, max_raw_minors);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
330
  	if (ret) {
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
331
  		goto error_region;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  	}
ca8eca688   Greg Kroah-Hartman   [PATCH] class: co...
333
  	raw_class = class_create(THIS_MODULE, "raw");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
  	if (IS_ERR(raw_class)) {
  		printk(KERN_ERR "Error creating raw class.
  ");
  		cdev_del(&raw_cdev);
3e26a423e   Rolf Eike Beer   [PATCH] Return be...
338
339
  		ret = PTR_ERR(raw_class);
  		goto error_region;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  	}
e454cea20   Kay Sievers   Driver-Core: exte...
341
  	raw_class->devnode = raw_devnode;
03457cd45   Greg Kroah-Hartman   device create: ch...
342
  	device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343

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