Blame view
block/ioctl.c
8.08 KB
c59ede7b7
|
1 |
#include <linux/capability.h> |
1da177e4c
|
2 |
#include <linux/blkdev.h> |
5a0e3ad6a
|
3 |
#include <linux/gfp.h> |
1da177e4c
|
4 |
#include <linux/blkpg.h> |
a885c8c43
|
5 |
#include <linux/hdreg.h> |
1da177e4c
|
6 7 |
#include <linux/backing-dev.h> #include <linux/buffer_head.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 |
part = add_partition(disk, partno, start, length, |
6d1d8050b
|
63 |
ADDPART_FLAG_NONE, NULL); |
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 |
static int blk_ioctl_discard(struct block_device *bdev, uint64_t start, |
8d57a98cc
|
113 |
uint64_t len, int secure) |
d30a2605b
|
114 |
{ |
dd3932edd
|
115 |
unsigned long flags = 0; |
8d57a98cc
|
116 |
|
d30a2605b
|
117 118 119 120 121 122 |
if (start & 511) return -EINVAL; if (len & 511) return -EINVAL; start >>= 9; len >>= 9; |
77304d2ab
|
123 |
if (start + len > (i_size_read(bdev->bd_inode) >> 9)) |
d30a2605b
|
124 |
return -EINVAL; |
8d57a98cc
|
125 |
if (secure) |
dd3932edd
|
126 |
flags |= BLKDEV_DISCARD_SECURE; |
8d57a98cc
|
127 |
return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags); |
d30a2605b
|
128 |
} |
1da177e4c
|
129 130 131 132 133 134 135 136 137 |
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
|
138 139 140 141 |
static int put_uint(unsigned long arg, unsigned int val) { return put_user(val, (unsigned int __user *)arg); } |
1da177e4c
|
142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
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
|
156 157 158 159 |
int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, unsigned long arg) { struct gendisk *disk = bdev->bd_disk; |
d4430d62f
|
160 161 162 |
if (disk->fops->ioctl) return disk->fops->ioctl(bdev, mode, cmd, arg); |
633a08b81
|
163 |
|
633a08b81
|
164 165 166 167 168 169 170 171 |
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
|
172 |
/* |
8a6cfeb6d
|
173 |
* always keep this in sync with compat_blkdev_ioctl() |
f58c4c0a1
|
174 |
*/ |
56b26add0
|
175 |
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, |
bb93e3a52
|
176 177 |
unsigned long arg) { |
bb93e3a52
|
178 |
struct gendisk *disk = bdev->bd_disk; |
45048d096
|
179 180 |
struct backing_dev_info *bdi; loff_t size; |
bb93e3a52
|
181 182 183 |
int ret, n; switch(cmd) { |
1da177e4c
|
184 185 186 |
case BLKFLSBUF: if (!capable(CAP_SYS_ADMIN)) return -EACCES; |
bb93e3a52
|
187 |
|
e436fdae7
|
188 |
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); |
bb93e3a52
|
189 190 191 |
/* -EINVAL to handle old uncorrected drivers */ if (ret != -EINVAL && ret != -ENOTTY) return ret; |
1da177e4c
|
192 |
fsync_bdev(bdev); |
f98393a64
|
193 |
invalidate_bdev(bdev); |
1da177e4c
|
194 |
return 0; |
bb93e3a52
|
195 |
|
1da177e4c
|
196 |
case BLKROSET: |
e436fdae7
|
197 |
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); |
bb93e3a52
|
198 199 200 |
/* -EINVAL to handle old uncorrected drivers */ if (ret != -EINVAL && ret != -ENOTTY) return ret; |
1da177e4c
|
201 202 203 204 205 206 |
if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (get_user(n, (int __user *)(arg))) return -EFAULT; set_device_ro(bdev, n); return 0; |
d30a2605b
|
207 |
|
8d57a98cc
|
208 209 |
case BLKDISCARD: case BLKSECDISCARD: { |
d30a2605b
|
210 |
uint64_t range[2]; |
e436fdae7
|
211 |
if (!(mode & FMODE_WRITE)) |
d30a2605b
|
212 213 214 215 |
return -EBADF; if (copy_from_user(range, (void __user *)arg, sizeof(range))) return -EFAULT; |
8d57a98cc
|
216 217 |
return blk_ioctl_discard(bdev, range[0], range[1], cmd == BLKSECDISCARD); |
d30a2605b
|
218 |
} |
a885c8c43
|
219 220 221 222 223 224 225 226 227 228 229 230 |
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. */ |
a014741c0
|
231 |
memset(&geo, 0, sizeof(geo)); |
a885c8c43
|
232 233 234 235 236 237 238 239 240 |
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
|
241 242 243 244 245 246 247 248 249 250 |
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
|
251 |
case BLKBSZGET: /* get block device soft block size (cf. BLKSSZGET) */ |
45048d096
|
252 |
return put_int(arg, block_size(bdev)); |
ac481c20e
|
253 |
case BLKSSZGET: /* get block device logical block size */ |
e1defc4ff
|
254 |
return put_int(arg, bdev_logical_block_size(bdev)); |
ac481c20e
|
255 256 257 258 259 260 261 262 |
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
|
263 264 |
case BLKDISCARDZEROES: return put_uint(arg, bdev_discard_zeroes_data(bdev)); |
45048d096
|
265 |
case BLKSECTGET: |
ae03bf639
|
266 |
return put_ushort(arg, queue_max_sectors(bdev_get_queue(bdev))); |
45048d096
|
267 268 269 270 271 272 273 |
case BLKRASET: case BLKFRASET: if(!capable(CAP_SYS_ADMIN)) return -EACCES; bdi = blk_get_backing_dev_info(bdev); if (bdi == NULL) return -ENOTTY; |
45048d096
|
274 |
bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; |
45048d096
|
275 276 277 278 279 280 281 282 283 |
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; |
3c522cedb
|
284 285 286 287 288 |
if (!(mode & FMODE_EXCL)) { bdgrab(bdev); if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0) return -EBUSY; } |
45048d096
|
289 |
ret = set_blocksize(bdev, n); |
6af3a56e1
|
290 |
if (!(mode & FMODE_EXCL)) |
e525fd89d
|
291 |
blkdev_put(bdev, mode | FMODE_EXCL); |
bb93e3a52
|
292 |
return ret; |
45048d096
|
293 |
case BLKPG: |
45048d096
|
294 |
ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg); |
45048d096
|
295 296 |
break; case BLKRRPART: |
45048d096
|
297 |
ret = blkdev_reread_part(bdev); |
45048d096
|
298 299 |
break; case BLKGETSIZE: |
77304d2ab
|
300 |
size = i_size_read(bdev->bd_inode); |
45048d096
|
301 302 303 304 |
if ((size >> 9) > ~0UL) return -EFBIG; return put_ulong(arg, size >> 9); case BLKGETSIZE64: |
77304d2ab
|
305 |
return put_u64(arg, i_size_read(bdev->bd_inode)); |
45048d096
|
306 307 308 309 |
case BLKTRACESTART: case BLKTRACESTOP: case BLKTRACESETUP: case BLKTRACETEARDOWN: |
45048d096
|
310 |
ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg); |
45048d096
|
311 312 313 314 315 |
break; default: ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg); } return ret; |
1da177e4c
|
316 |
} |
68f66feb3
|
317 |
EXPORT_SYMBOL_GPL(blkdev_ioctl); |