xref: /openbmc/linux/block/ioctl.c (revision 87c2ce3b)
1 #include <linux/sched.h>		/* for capable() */
2 #include <linux/blkdev.h>
3 #include <linux/blkpg.h>
4 #include <linux/hdreg.h>
5 #include <linux/backing-dev.h>
6 #include <linux/buffer_head.h>
7 #include <linux/smp_lock.h>
8 #include <asm/uaccess.h>
9 
10 static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg)
11 {
12 	struct block_device *bdevp;
13 	struct gendisk *disk;
14 	struct blkpg_ioctl_arg a;
15 	struct blkpg_partition p;
16 	long long start, length;
17 	int part;
18 	int i;
19 
20 	if (!capable(CAP_SYS_ADMIN))
21 		return -EACCES;
22 	if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
23 		return -EFAULT;
24 	if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
25 		return -EFAULT;
26 	disk = bdev->bd_disk;
27 	if (bdev != bdev->bd_contains)
28 		return -EINVAL;
29 	part = p.pno;
30 	if (part <= 0 || part >= disk->minors)
31 		return -EINVAL;
32 	switch (a.op) {
33 		case BLKPG_ADD_PARTITION:
34 			start = p.start >> 9;
35 			length = p.length >> 9;
36 			/* check for fit in a hd_struct */
37 			if (sizeof(sector_t) == sizeof(long) &&
38 			    sizeof(long long) > sizeof(long)) {
39 				long pstart = start, plength = length;
40 				if (pstart != start || plength != length
41 				    || pstart < 0 || plength < 0)
42 					return -EINVAL;
43 			}
44 			/* partition number in use? */
45 			down(&bdev->bd_sem);
46 			if (disk->part[part - 1]) {
47 				up(&bdev->bd_sem);
48 				return -EBUSY;
49 			}
50 			/* overlap? */
51 			for (i = 0; i < disk->minors - 1; i++) {
52 				struct hd_struct *s = disk->part[i];
53 
54 				if (!s)
55 					continue;
56 				if (!(start+length <= s->start_sect ||
57 				      start >= s->start_sect + s->nr_sects)) {
58 					up(&bdev->bd_sem);
59 					return -EBUSY;
60 				}
61 			}
62 			/* all seems OK */
63 			add_partition(disk, part, start, length);
64 			up(&bdev->bd_sem);
65 			return 0;
66 		case BLKPG_DEL_PARTITION:
67 			if (!disk->part[part-1])
68 				return -ENXIO;
69 			if (disk->part[part - 1]->nr_sects == 0)
70 				return -ENXIO;
71 			bdevp = bdget_disk(disk, part);
72 			if (!bdevp)
73 				return -ENOMEM;
74 			down(&bdevp->bd_sem);
75 			if (bdevp->bd_openers) {
76 				up(&bdevp->bd_sem);
77 				bdput(bdevp);
78 				return -EBUSY;
79 			}
80 			/* all seems OK */
81 			fsync_bdev(bdevp);
82 			invalidate_bdev(bdevp, 0);
83 
84 			down(&bdev->bd_sem);
85 			delete_partition(disk, part);
86 			up(&bdev->bd_sem);
87 			up(&bdevp->bd_sem);
88 			bdput(bdevp);
89 
90 			return 0;
91 		default:
92 			return -EINVAL;
93 	}
94 }
95 
96 static int blkdev_reread_part(struct block_device *bdev)
97 {
98 	struct gendisk *disk = bdev->bd_disk;
99 	int res;
100 
101 	if (disk->minors == 1 || bdev != bdev->bd_contains)
102 		return -EINVAL;
103 	if (!capable(CAP_SYS_ADMIN))
104 		return -EACCES;
105 	if (down_trylock(&bdev->bd_sem))
106 		return -EBUSY;
107 	res = rescan_partitions(disk, bdev);
108 	up(&bdev->bd_sem);
109 	return res;
110 }
111 
112 static int put_ushort(unsigned long arg, unsigned short val)
113 {
114 	return put_user(val, (unsigned short __user *)arg);
115 }
116 
117 static int put_int(unsigned long arg, int val)
118 {
119 	return put_user(val, (int __user *)arg);
120 }
121 
122 static int put_long(unsigned long arg, long val)
123 {
124 	return put_user(val, (long __user *)arg);
125 }
126 
127 static int put_ulong(unsigned long arg, unsigned long val)
128 {
129 	return put_user(val, (unsigned long __user *)arg);
130 }
131 
132 static int put_u64(unsigned long arg, u64 val)
133 {
134 	return put_user(val, (u64 __user *)arg);
135 }
136 
137 static int blkdev_locked_ioctl(struct file *file, struct block_device *bdev,
138 				unsigned cmd, unsigned long arg)
139 {
140 	struct backing_dev_info *bdi;
141 	int ret, n;
142 
143 	switch (cmd) {
144 	case BLKRAGET:
145 	case BLKFRAGET:
146 		if (!arg)
147 			return -EINVAL;
148 		bdi = blk_get_backing_dev_info(bdev);
149 		if (bdi == NULL)
150 			return -ENOTTY;
151 		return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512);
152 	case BLKROGET:
153 		return put_int(arg, bdev_read_only(bdev) != 0);
154 	case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */
155 		return put_int(arg, block_size(bdev));
156 	case BLKSSZGET: /* get block device hardware sector size */
157 		return put_int(arg, bdev_hardsect_size(bdev));
158 	case BLKSECTGET:
159 		return put_ushort(arg, bdev_get_queue(bdev)->max_sectors);
160 	case BLKRASET:
161 	case BLKFRASET:
162 		if(!capable(CAP_SYS_ADMIN))
163 			return -EACCES;
164 		bdi = blk_get_backing_dev_info(bdev);
165 		if (bdi == NULL)
166 			return -ENOTTY;
167 		bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
168 		return 0;
169 	case BLKBSZSET:
170 		/* set the logical block size */
171 		if (!capable(CAP_SYS_ADMIN))
172 			return -EACCES;
173 		if (!arg)
174 			return -EINVAL;
175 		if (get_user(n, (int __user *) arg))
176 			return -EFAULT;
177 		if (bd_claim(bdev, file) < 0)
178 			return -EBUSY;
179 		ret = set_blocksize(bdev, n);
180 		bd_release(bdev);
181 		return ret;
182 	case BLKPG:
183 		return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
184 	case BLKRRPART:
185 		return blkdev_reread_part(bdev);
186 	case BLKGETSIZE:
187 		if ((bdev->bd_inode->i_size >> 9) > ~0UL)
188 			return -EFBIG;
189 		return put_ulong(arg, bdev->bd_inode->i_size >> 9);
190 	case BLKGETSIZE64:
191 		return put_u64(arg, bdev->bd_inode->i_size);
192 	}
193 	return -ENOIOCTLCMD;
194 }
195 
196 static int blkdev_driver_ioctl(struct inode *inode, struct file *file,
197 		struct gendisk *disk, unsigned cmd, unsigned long arg)
198 {
199 	int ret;
200 	if (disk->fops->unlocked_ioctl)
201 		return disk->fops->unlocked_ioctl(file, cmd, arg);
202 
203 	if (disk->fops->ioctl) {
204 		lock_kernel();
205 		ret = disk->fops->ioctl(inode, file, cmd, arg);
206 		unlock_kernel();
207 		return ret;
208 	}
209 
210 	return -ENOTTY;
211 }
212 
213 int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
214 			unsigned long arg)
215 {
216 	struct block_device *bdev = inode->i_bdev;
217 	struct gendisk *disk = bdev->bd_disk;
218 	int ret, n;
219 
220 	switch(cmd) {
221 	case BLKFLSBUF:
222 		if (!capable(CAP_SYS_ADMIN))
223 			return -EACCES;
224 
225 		ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg);
226 		/* -EINVAL to handle old uncorrected drivers */
227 		if (ret != -EINVAL && ret != -ENOTTY)
228 			return ret;
229 
230 		lock_kernel();
231 		fsync_bdev(bdev);
232 		invalidate_bdev(bdev, 0);
233 		unlock_kernel();
234 		return 0;
235 
236 	case BLKROSET:
237 		ret = blkdev_driver_ioctl(inode, file, disk, cmd, arg);
238 		/* -EINVAL to handle old uncorrected drivers */
239 		if (ret != -EINVAL && ret != -ENOTTY)
240 			return ret;
241 		if (!capable(CAP_SYS_ADMIN))
242 			return -EACCES;
243 		if (get_user(n, (int __user *)(arg)))
244 			return -EFAULT;
245 		lock_kernel();
246 		set_device_ro(bdev, n);
247 		unlock_kernel();
248 		return 0;
249 	case HDIO_GETGEO: {
250 		struct hd_geometry geo;
251 
252 		if (!arg)
253 			return -EINVAL;
254 		if (!disk->fops->getgeo)
255 			return -ENOTTY;
256 
257 		/*
258 		 * We need to set the startsect first, the driver may
259 		 * want to override it.
260 		 */
261 		geo.start = get_start_sect(bdev);
262 		ret = disk->fops->getgeo(bdev, &geo);
263 		if (ret)
264 			return ret;
265 		if (copy_to_user((struct hd_geometry __user *)arg, &geo,
266 					sizeof(geo)))
267 			return -EFAULT;
268 		return 0;
269 	}
270 	}
271 
272 	lock_kernel();
273 	ret = blkdev_locked_ioctl(file, bdev, cmd, arg);
274 	unlock_kernel();
275 	if (ret != -ENOIOCTLCMD)
276 		return ret;
277 
278 	return blkdev_driver_ioctl(inode, file, disk, cmd, arg);
279 }
280 
281 /* Most of the generic ioctls are handled in the normal fallback path.
282    This assumes the blkdev's low level compat_ioctl always returns
283    ENOIOCTLCMD for unknown ioctls. */
284 long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
285 {
286 	struct block_device *bdev = file->f_dentry->d_inode->i_bdev;
287 	struct gendisk *disk = bdev->bd_disk;
288 	int ret = -ENOIOCTLCMD;
289 	if (disk->fops->compat_ioctl) {
290 		lock_kernel();
291 		ret = disk->fops->compat_ioctl(file, cmd, arg);
292 		unlock_kernel();
293 	}
294 	return ret;
295 }
296 
297 EXPORT_SYMBOL_GPL(blkdev_ioctl);
298