Blame view
block/ioctl.c
8.22 KB
c59ede7b7
|
1 |
#include <linux/capability.h> |
1da177e4c
|
2 3 |
#include <linux/blkdev.h> #include <linux/blkpg.h> |
a885c8c43
|
4 |
#include <linux/hdreg.h> |
1da177e4c
|
5 6 7 |
#include <linux/backing-dev.h> #include <linux/buffer_head.h> #include <linux/smp_lock.h> |
2056a782f
|
8 |
#include <linux/blktrace_api.h> |
1da177e4c
|
9 10 11 12 13 14 |
#include <asm/uaccess.h> static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg) { struct block_device *bdevp; struct gendisk *disk; |
e71bf0d0e
|
15 |
struct hd_struct *part; |
1da177e4c
|
16 17 |
struct blkpg_ioctl_arg a; struct blkpg_partition p; |
e71bf0d0e
|
18 |
struct disk_part_iter piter; |
1da177e4c
|
19 |
long long start, length; |
cf771cb5a
|
20 |
int partno; |
1da177e4c
|
21 22 23 24 25 26 27 28 29 30 |
if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) return -EFAULT; if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) return -EFAULT; disk = bdev->bd_disk; if (bdev != bdev->bd_contains) return -EINVAL; |
cf771cb5a
|
31 |
partno = p.pno; |
540eed563
|
32 |
if (partno <= 0) |
1da177e4c
|
33 34 35 36 37 38 39 40 41 42 43 44 45 |
return -EINVAL; switch (a.op) { case BLKPG_ADD_PARTITION: start = p.start >> 9; length = p.length >> 9; /* check for fit in a hd_struct */ if (sizeof(sector_t) == sizeof(long) && sizeof(long long) > sizeof(long)) { long pstart = start, plength = length; if (pstart != start || plength != length || pstart < 0 || plength < 0) return -EINVAL; } |
88e341261
|
46 |
|
c039e3134
|
47 |
mutex_lock(&bdev->bd_mutex); |
88e341261
|
48 |
|
1da177e4c
|
49 |
/* overlap? */ |
e71bf0d0e
|
50 51 52 53 54 55 |
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY); while ((part = disk_part_iter_next(&piter))) { if (!(start + length <= part->start_sect || start >= part->start_sect + part->nr_sects)) { disk_part_iter_exit(&piter); |
c039e3134
|
56 |
mutex_unlock(&bdev->bd_mutex); |
1da177e4c
|
57 58 59 |
return -EBUSY; } } |
e71bf0d0e
|
60 |
disk_part_iter_exit(&piter); |
1da177e4c
|
61 |
/* all seems OK */ |
ba32929a9
|
62 63 |
part = add_partition(disk, partno, start, length, ADDPART_FLAG_NONE); |
c039e3134
|
64 |
mutex_unlock(&bdev->bd_mutex); |
ba32929a9
|
65 |
return IS_ERR(part) ? PTR_ERR(part) : 0; |
1da177e4c
|
66 |
case BLKPG_DEL_PARTITION: |
e71bf0d0e
|
67 68 |
part = disk_get_part(disk, partno); if (!part) |
1da177e4c
|
69 |
return -ENXIO; |
e71bf0d0e
|
70 71 72 |
bdevp = bdget(part_devt(part)); disk_put_part(part); |
1da177e4c
|
73 74 |
if (!bdevp) return -ENOMEM; |
e71bf0d0e
|
75 |
|
2e7b651df
|
76 |
mutex_lock(&bdevp->bd_mutex); |
1da177e4c
|
77 |
if (bdevp->bd_openers) { |
c039e3134
|
78 |
mutex_unlock(&bdevp->bd_mutex); |
1da177e4c
|
79 80 81 82 83 |
bdput(bdevp); return -EBUSY; } /* all seems OK */ fsync_bdev(bdevp); |
f98393a64
|
84 |
invalidate_bdev(bdevp); |
1da177e4c
|
85 |
|
6d740cd5b
|
86 |
mutex_lock_nested(&bdev->bd_mutex, 1); |
cf771cb5a
|
87 |
delete_partition(disk, partno); |
c039e3134
|
88 89 |
mutex_unlock(&bdev->bd_mutex); mutex_unlock(&bdevp->bd_mutex); |
1da177e4c
|
90 91 92 93 94 95 96 97 98 99 100 101 |
bdput(bdevp); return 0; default: return -EINVAL; } } static int blkdev_reread_part(struct block_device *bdev) { struct gendisk *disk = bdev->bd_disk; int res; |
b5d0b9df0
|
102 |
if (!disk_partitionable(disk) || bdev != bdev->bd_contains) |
1da177e4c
|
103 104 105 |
return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; |
c039e3134
|
106 |
if (!mutex_trylock(&bdev->bd_mutex)) |
1da177e4c
|
107 108 |
return -EBUSY; res = rescan_partitions(disk, bdev); |
c039e3134
|
109 |
mutex_unlock(&bdev->bd_mutex); |
1da177e4c
|
110 111 |
return res; } |
d30a2605b
|
112 113 114 |
static int blk_ioctl_discard(struct block_device *bdev, uint64_t start, uint64_t len) { |
d30a2605b
|
115 116 117 118 119 120 121 122 123 |
if (start & 511) return -EINVAL; if (len & 511) return -EINVAL; start >>= 9; len >>= 9; if (start + len > (bdev->bd_inode->i_size >> 9)) return -EINVAL; |
746cd1e7e
|
124 125 |
return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, DISCARD_FL_WAIT); |
d30a2605b
|
126 |
} |
1da177e4c
|
127 128 129 130 131 132 133 134 135 |
static int put_ushort(unsigned long arg, unsigned short val) { return put_user(val, (unsigned short __user *)arg); } static int put_int(unsigned long arg, int val) { return put_user(val, (int __user *)arg); } |
ac481c20e
|
136 137 138 139 |
static int put_uint(unsigned long arg, unsigned int val) { return put_user(val, (unsigned int __user *)arg); } |
1da177e4c
|
140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
static int put_long(unsigned long arg, long val) { return put_user(val, (long __user *)arg); } static int put_ulong(unsigned long arg, unsigned long val) { return put_user(val, (unsigned long __user *)arg); } static int put_u64(unsigned long arg, u64 val) { return put_user(val, (u64 __user *)arg); } |
633a08b81
|
154 155 156 157 158 |
int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, unsigned long arg) { struct gendisk *disk = bdev->bd_disk; int ret; |
d4430d62f
|
159 160 161 |
if (disk->fops->ioctl) return disk->fops->ioctl(bdev, mode, cmd, arg); |
633a08b81
|
162 |
|
d4430d62f
|
163 |
if (disk->fops->locked_ioctl) { |
633a08b81
|
164 |
lock_kernel(); |
d4430d62f
|
165 |
ret = disk->fops->locked_ioctl(bdev, mode, cmd, arg); |
633a08b81
|
166 167 168 169 170 171 172 173 174 175 176 177 |
unlock_kernel(); return ret; } return -ENOTTY; } /* * For the record: _GPL here is only because somebody decided to slap it * on the previous export. Sheer idiocy, since it wasn't copyrightable * at all and could be open-coded without any exports by anybody who cares. */ EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl); |
f58c4c0a1
|
178 179 180 181 |
/* * always keep this in sync with compat_blkdev_ioctl() and * compat_blkdev_locked_ioctl() */ |
56b26add0
|
182 |
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, |
bb93e3a52
|
183 184 |
unsigned long arg) { |
bb93e3a52
|
185 |
struct gendisk *disk = bdev->bd_disk; |
45048d096
|
186 187 |
struct backing_dev_info *bdi; loff_t size; |
bb93e3a52
|
188 189 190 |
int ret, n; switch(cmd) { |
1da177e4c
|
191 192 193 |
case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES; |
bb93e3a52
|
194 |
|
e436fdae7
|
195 |
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); |
bb93e3a52
|
196 197 198 199 200 |
/* -EINVAL to handle old uncorrected drivers */ if (ret != -EINVAL && ret != -ENOTTY) return ret; lock_kernel(); |
1da177e4c
|
201 |
fsync_bdev(bdev); |
f98393a64
|
202 |
invalidate_bdev(bdev); |
bb93e3a52
|
203 |
unlock_kernel(); |
1da177e4c
|
204 |
return 0; |
bb93e3a52
|
205 |
|
1da177e4c
|
206 |
case BLKROSET: |
e436fdae7
|
207 |
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); |
bb93e3a52
|
208 209 210 |
/* -EINVAL to handle old uncorrected drivers */ if (ret != -EINVAL && ret != -ENOTTY) return ret; |
1da177e4c
|
211 212 213 214 |
if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (get_user(n, (int __user *)(arg))) return -EFAULT; |
bb93e3a52
|
215 |
lock_kernel(); |
1da177e4c
|
216 |
set_device_ro(bdev, n); |
bb93e3a52
|
217 |
unlock_kernel(); |
1da177e4c
|
218 |
return 0; |
d30a2605b
|
219 220 221 |
case BLKDISCARD: { uint64_t range[2]; |
e436fdae7
|
222 |
if (!(mode & FMODE_WRITE)) |
d30a2605b
|
223 224 225 226 227 228 229 |
return -EBADF; if (copy_from_user(range, (void __user *)arg, sizeof(range))) return -EFAULT; return blk_ioctl_discard(bdev, range[0], range[1]); } |
a885c8c43
|
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
case HDIO_GETGEO: { struct hd_geometry geo; if (!arg) return -EINVAL; if (!disk->fops->getgeo) return -ENOTTY; /* * We need to set the startsect first, the driver may * want to override it. */ geo.start = get_start_sect(bdev); ret = disk->fops->getgeo(bdev, &geo); if (ret) return ret; if (copy_to_user((struct hd_geometry __user *)arg, &geo, sizeof(geo))) return -EFAULT; return 0; } |
45048d096
|
251 252 253 254 255 256 257 258 259 260 |
case BLKRAGET: case BLKFRAGET: if (!arg) return -EINVAL; bdi = blk_get_backing_dev_info(bdev); if (bdi == NULL) return -ENOTTY; return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512); case BLKROGET: return put_int(arg, bdev_read_only(bdev) != 0); |
ac481c20e
|
261 |
case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */ |
45048d096
|
262 |
return put_int(arg, block_size(bdev)); |
ac481c20e
|
263 |
case BLKSSZGET: /* get block device logical block size */ |
e1defc4ff
|
264 |
return put_int(arg, bdev_logical_block_size(bdev)); |
ac481c20e
|
265 266 267 268 269 270 271 272 |
case BLKPBSZGET: /* get block device physical block size */ return put_uint(arg, bdev_physical_block_size(bdev)); case BLKIOMIN: return put_uint(arg, bdev_io_min(bdev)); case BLKIOOPT: return put_uint(arg, bdev_io_opt(bdev)); case BLKALIGNOFF: return put_int(arg, bdev_alignment_offset(bdev)); |
98262f276
|
273 274 |
case BLKDISCARDZEROES: return put_uint(arg, bdev_discard_zeroes_data(bdev)); |
45048d096
|
275 |
case BLKSECTGET: |
ae03bf639
|
276 |
return put_ushort(arg, queue_max_sectors(bdev_get_queue(bdev))); |
45048d096
|
277 278 279 280 281 282 283 |
case BLKRASET: case BLKFRASET: if(!capable(CAP_SYS_ADMIN)) return -EACCES; bdi = blk_get_backing_dev_info(bdev); if (bdi == NULL) return -ENOTTY; |
45048d096
|
284 |
bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; |
45048d096
|
285 286 287 288 289 290 291 292 293 |
return 0; case BLKBSZSET: /* set the logical block size */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (!arg) return -EINVAL; if (get_user(n, (int __user *) arg)) return -EFAULT; |
6af3a56e1
|
294 |
if (!(mode & FMODE_EXCL) && bd_claim(bdev, &bdev) < 0) |
45048d096
|
295 296 |
return -EBUSY; ret = set_blocksize(bdev, n); |
6af3a56e1
|
297 298 |
if (!(mode & FMODE_EXCL)) bd_release(bdev); |
bb93e3a52
|
299 |
return ret; |
45048d096
|
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
case BLKPG: lock_kernel(); ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg); unlock_kernel(); break; case BLKRRPART: lock_kernel(); ret = blkdev_reread_part(bdev); unlock_kernel(); break; case BLKGETSIZE: size = bdev->bd_inode->i_size; if ((size >> 9) > ~0UL) return -EFBIG; return put_ulong(arg, size >> 9); case BLKGETSIZE64: return put_u64(arg, bdev->bd_inode->i_size); case BLKTRACESTART: case BLKTRACESTOP: case BLKTRACESETUP: case BLKTRACETEARDOWN: lock_kernel(); ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg); unlock_kernel(); break; default: ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); } return ret; |
1da177e4c
|
329 |
} |
68f66feb3
|
330 |
EXPORT_SYMBOL_GPL(blkdev_ioctl); |