diff options
Diffstat (limited to 'block/scsi_ioctl.c')
-rw-r--r-- | block/scsi_ioctl.c | 163 |
1 files changed, 95 insertions, 68 deletions
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index e83f1dbf7c29..a26ba07955fe 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c | |||
@@ -41,8 +41,6 @@ const unsigned char scsi_command_size[8] = | |||
41 | 41 | ||
42 | EXPORT_SYMBOL(scsi_command_size); | 42 | EXPORT_SYMBOL(scsi_command_size); |
43 | 43 | ||
44 | #define BLK_DEFAULT_TIMEOUT (60 * HZ) | ||
45 | |||
46 | #include <scsi/sg.h> | 44 | #include <scsi/sg.h> |
47 | 45 | ||
48 | static int sg_get_version(int __user *p) | 46 | static int sg_get_version(int __user *p) |
@@ -114,7 +112,7 @@ static int sg_emulated_host(request_queue_t *q, int __user *p) | |||
114 | #define safe_for_read(cmd) [cmd] = CMD_READ_SAFE | 112 | #define safe_for_read(cmd) [cmd] = CMD_READ_SAFE |
115 | #define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE | 113 | #define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE |
116 | 114 | ||
117 | static int verify_command(struct file *file, unsigned char *cmd) | 115 | int blk_verify_command(unsigned char *cmd, int has_write_perm) |
118 | { | 116 | { |
119 | static unsigned char cmd_type[256] = { | 117 | static unsigned char cmd_type[256] = { |
120 | 118 | ||
@@ -193,18 +191,11 @@ static int verify_command(struct file *file, unsigned char *cmd) | |||
193 | safe_for_write(GPCMD_SET_STREAMING), | 191 | safe_for_write(GPCMD_SET_STREAMING), |
194 | }; | 192 | }; |
195 | unsigned char type = cmd_type[cmd[0]]; | 193 | unsigned char type = cmd_type[cmd[0]]; |
196 | int has_write_perm = 0; | ||
197 | 194 | ||
198 | /* Anybody who can open the device can do a read-safe command */ | 195 | /* Anybody who can open the device can do a read-safe command */ |
199 | if (type & CMD_READ_SAFE) | 196 | if (type & CMD_READ_SAFE) |
200 | return 0; | 197 | return 0; |
201 | 198 | ||
202 | /* | ||
203 | * file can be NULL from ioctl_by_bdev()... | ||
204 | */ | ||
205 | if (file) | ||
206 | has_write_perm = file->f_mode & FMODE_WRITE; | ||
207 | |||
208 | /* Write-safe commands just require a writable open.. */ | 199 | /* Write-safe commands just require a writable open.. */ |
209 | if ((type & CMD_WRITE_SAFE) && has_write_perm) | 200 | if ((type & CMD_WRITE_SAFE) && has_write_perm) |
210 | return 0; | 201 | return 0; |
@@ -221,25 +212,96 @@ static int verify_command(struct file *file, unsigned char *cmd) | |||
221 | /* Otherwise fail it with an "Operation not permitted" */ | 212 | /* Otherwise fail it with an "Operation not permitted" */ |
222 | return -EPERM; | 213 | return -EPERM; |
223 | } | 214 | } |
215 | EXPORT_SYMBOL_GPL(blk_verify_command); | ||
216 | |||
217 | int blk_fill_sghdr_rq(request_queue_t *q, struct request *rq, | ||
218 | struct sg_io_hdr *hdr, int has_write_perm) | ||
219 | { | ||
220 | memset(rq->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */ | ||
221 | |||
222 | if (copy_from_user(rq->cmd, hdr->cmdp, hdr->cmd_len)) | ||
223 | return -EFAULT; | ||
224 | if (blk_verify_command(rq->cmd, has_write_perm)) | ||
225 | return -EPERM; | ||
226 | |||
227 | /* | ||
228 | * fill in request structure | ||
229 | */ | ||
230 | rq->cmd_len = hdr->cmd_len; | ||
231 | rq->cmd_type = REQ_TYPE_BLOCK_PC; | ||
232 | |||
233 | rq->timeout = (hdr->timeout * HZ) / 1000; | ||
234 | if (!rq->timeout) | ||
235 | rq->timeout = q->sg_timeout; | ||
236 | if (!rq->timeout) | ||
237 | rq->timeout = BLK_DEFAULT_SG_TIMEOUT; | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | EXPORT_SYMBOL_GPL(blk_fill_sghdr_rq); | ||
242 | |||
243 | /* | ||
244 | * unmap a request that was previously mapped to this sg_io_hdr. handles | ||
245 | * both sg and non-sg sg_io_hdr. | ||
246 | */ | ||
247 | int blk_unmap_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr) | ||
248 | { | ||
249 | blk_rq_unmap_user(rq->bio); | ||
250 | blk_put_request(rq); | ||
251 | return 0; | ||
252 | } | ||
253 | EXPORT_SYMBOL_GPL(blk_unmap_sghdr_rq); | ||
254 | |||
255 | int blk_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr, | ||
256 | struct bio *bio) | ||
257 | { | ||
258 | int r, ret = 0; | ||
259 | |||
260 | /* | ||
261 | * fill in all the output members | ||
262 | */ | ||
263 | hdr->status = rq->errors & 0xff; | ||
264 | hdr->masked_status = status_byte(rq->errors); | ||
265 | hdr->msg_status = msg_byte(rq->errors); | ||
266 | hdr->host_status = host_byte(rq->errors); | ||
267 | hdr->driver_status = driver_byte(rq->errors); | ||
268 | hdr->info = 0; | ||
269 | if (hdr->masked_status || hdr->host_status || hdr->driver_status) | ||
270 | hdr->info |= SG_INFO_CHECK; | ||
271 | hdr->resid = rq->data_len; | ||
272 | hdr->sb_len_wr = 0; | ||
273 | |||
274 | if (rq->sense_len && hdr->sbp) { | ||
275 | int len = min((unsigned int) hdr->mx_sb_len, rq->sense_len); | ||
276 | |||
277 | if (!copy_to_user(hdr->sbp, rq->sense, len)) | ||
278 | hdr->sb_len_wr = len; | ||
279 | else | ||
280 | ret = -EFAULT; | ||
281 | } | ||
282 | |||
283 | rq->bio = bio; | ||
284 | r = blk_unmap_sghdr_rq(rq, hdr); | ||
285 | if (ret) | ||
286 | r = ret; | ||
287 | |||
288 | return r; | ||
289 | } | ||
290 | EXPORT_SYMBOL_GPL(blk_complete_sghdr_rq); | ||
224 | 291 | ||
225 | static int sg_io(struct file *file, request_queue_t *q, | 292 | static int sg_io(struct file *file, request_queue_t *q, |
226 | struct gendisk *bd_disk, struct sg_io_hdr *hdr) | 293 | struct gendisk *bd_disk, struct sg_io_hdr *hdr) |
227 | { | 294 | { |
228 | unsigned long start_time, timeout; | 295 | unsigned long start_time; |
229 | int writing = 0, ret = 0; | 296 | int writing = 0, ret = 0, has_write_perm = 0; |
230 | struct request *rq; | 297 | struct request *rq; |
231 | char sense[SCSI_SENSE_BUFFERSIZE]; | 298 | char sense[SCSI_SENSE_BUFFERSIZE]; |
232 | unsigned char cmd[BLK_MAX_CDB]; | ||
233 | struct bio *bio; | 299 | struct bio *bio; |
234 | 300 | ||
235 | if (hdr->interface_id != 'S') | 301 | if (hdr->interface_id != 'S') |
236 | return -EINVAL; | 302 | return -EINVAL; |
237 | if (hdr->cmd_len > BLK_MAX_CDB) | 303 | if (hdr->cmd_len > BLK_MAX_CDB) |
238 | return -EINVAL; | 304 | return -EINVAL; |
239 | if (copy_from_user(cmd, hdr->cmdp, hdr->cmd_len)) | ||
240 | return -EFAULT; | ||
241 | if (verify_command(file, cmd)) | ||
242 | return -EPERM; | ||
243 | 305 | ||
244 | if (hdr->dxfer_len > (q->max_hw_sectors << 9)) | 306 | if (hdr->dxfer_len > (q->max_hw_sectors << 9)) |
245 | return -EIO; | 307 | return -EIO; |
@@ -260,25 +322,13 @@ static int sg_io(struct file *file, request_queue_t *q, | |||
260 | if (!rq) | 322 | if (!rq) |
261 | return -ENOMEM; | 323 | return -ENOMEM; |
262 | 324 | ||
263 | /* | 325 | if (file) |
264 | * fill in request structure | 326 | has_write_perm = file->f_mode & FMODE_WRITE; |
265 | */ | ||
266 | rq->cmd_len = hdr->cmd_len; | ||
267 | memset(rq->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */ | ||
268 | memcpy(rq->cmd, cmd, hdr->cmd_len); | ||
269 | |||
270 | memset(sense, 0, sizeof(sense)); | ||
271 | rq->sense = sense; | ||
272 | rq->sense_len = 0; | ||
273 | |||
274 | rq->cmd_type = REQ_TYPE_BLOCK_PC; | ||
275 | 327 | ||
276 | timeout = msecs_to_jiffies(hdr->timeout); | 328 | if (blk_fill_sghdr_rq(q, rq, hdr, has_write_perm)) { |
277 | rq->timeout = (timeout < INT_MAX) ? timeout : INT_MAX; | 329 | blk_put_request(rq); |
278 | if (!rq->timeout) | 330 | return -EFAULT; |
279 | rq->timeout = q->sg_timeout; | 331 | } |
280 | if (!rq->timeout) | ||
281 | rq->timeout = BLK_DEFAULT_TIMEOUT; | ||
282 | 332 | ||
283 | if (hdr->iovec_count) { | 333 | if (hdr->iovec_count) { |
284 | const int size = sizeof(struct sg_iovec) * hdr->iovec_count; | 334 | const int size = sizeof(struct sg_iovec) * hdr->iovec_count; |
@@ -306,6 +356,9 @@ static int sg_io(struct file *file, request_queue_t *q, | |||
306 | goto out; | 356 | goto out; |
307 | 357 | ||
308 | bio = rq->bio; | 358 | bio = rq->bio; |
359 | memset(sense, 0, sizeof(sense)); | ||
360 | rq->sense = sense; | ||
361 | rq->sense_len = 0; | ||
309 | rq->retries = 0; | 362 | rq->retries = 0; |
310 | 363 | ||
311 | start_time = jiffies; | 364 | start_time = jiffies; |
@@ -316,31 +369,9 @@ static int sg_io(struct file *file, request_queue_t *q, | |||
316 | */ | 369 | */ |
317 | blk_execute_rq(q, bd_disk, rq, 0); | 370 | blk_execute_rq(q, bd_disk, rq, 0); |
318 | 371 | ||
319 | /* write to all output members */ | ||
320 | hdr->status = 0xff & rq->errors; | ||
321 | hdr->masked_status = status_byte(rq->errors); | ||
322 | hdr->msg_status = msg_byte(rq->errors); | ||
323 | hdr->host_status = host_byte(rq->errors); | ||
324 | hdr->driver_status = driver_byte(rq->errors); | ||
325 | hdr->info = 0; | ||
326 | if (hdr->masked_status || hdr->host_status || hdr->driver_status) | ||
327 | hdr->info |= SG_INFO_CHECK; | ||
328 | hdr->resid = rq->data_len; | ||
329 | hdr->duration = ((jiffies - start_time) * 1000) / HZ; | 372 | hdr->duration = ((jiffies - start_time) * 1000) / HZ; |
330 | hdr->sb_len_wr = 0; | ||
331 | |||
332 | if (rq->sense_len && hdr->sbp) { | ||
333 | int len = min((unsigned int) hdr->mx_sb_len, rq->sense_len); | ||
334 | |||
335 | if (!copy_to_user(hdr->sbp, rq->sense, len)) | ||
336 | hdr->sb_len_wr = len; | ||
337 | } | ||
338 | 373 | ||
339 | if (blk_rq_unmap_user(bio)) | 374 | return blk_complete_sghdr_rq(rq, hdr, bio); |
340 | ret = -EFAULT; | ||
341 | |||
342 | /* may not have succeeded, but output values written to control | ||
343 | * structure (struct sg_io_hdr). */ | ||
344 | out: | 375 | out: |
345 | blk_put_request(rq); | 376 | blk_put_request(rq); |
346 | return ret; | 377 | return ret; |
@@ -427,7 +458,7 @@ int sg_scsi_ioctl(struct file *file, struct request_queue *q, | |||
427 | if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) | 458 | if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) |
428 | goto error; | 459 | goto error; |
429 | 460 | ||
430 | err = verify_command(file, rq->cmd); | 461 | err = blk_verify_command(rq->cmd, file->f_mode & FMODE_WRITE); |
431 | if (err) | 462 | if (err) |
432 | goto error; | 463 | goto error; |
433 | 464 | ||
@@ -454,7 +485,7 @@ int sg_scsi_ioctl(struct file *file, struct request_queue *q, | |||
454 | rq->retries = 1; | 485 | rq->retries = 1; |
455 | break; | 486 | break; |
456 | default: | 487 | default: |
457 | rq->timeout = BLK_DEFAULT_TIMEOUT; | 488 | rq->timeout = BLK_DEFAULT_SG_TIMEOUT; |
458 | break; | 489 | break; |
459 | } | 490 | } |
460 | 491 | ||
@@ -501,7 +532,7 @@ static int __blk_send_generic(request_queue_t *q, struct gendisk *bd_disk, int c | |||
501 | rq->cmd_type = REQ_TYPE_BLOCK_PC; | 532 | rq->cmd_type = REQ_TYPE_BLOCK_PC; |
502 | rq->data = NULL; | 533 | rq->data = NULL; |
503 | rq->data_len = 0; | 534 | rq->data_len = 0; |
504 | rq->timeout = BLK_DEFAULT_TIMEOUT; | 535 | rq->timeout = BLK_DEFAULT_SG_TIMEOUT; |
505 | memset(rq->cmd, 0, sizeof(rq->cmd)); | 536 | memset(rq->cmd, 0, sizeof(rq->cmd)); |
506 | rq->cmd[0] = cmd; | 537 | rq->cmd[0] = cmd; |
507 | rq->cmd[4] = data; | 538 | rq->cmd[4] = data; |
@@ -517,16 +548,12 @@ static inline int blk_send_start_stop(request_queue_t *q, struct gendisk *bd_dis | |||
517 | return __blk_send_generic(q, bd_disk, GPCMD_START_STOP_UNIT, data); | 548 | return __blk_send_generic(q, bd_disk, GPCMD_START_STOP_UNIT, data); |
518 | } | 549 | } |
519 | 550 | ||
520 | int scsi_cmd_ioctl(struct file *file, struct gendisk *bd_disk, unsigned int cmd, void __user *arg) | 551 | int scsi_cmd_ioctl(struct file *file, struct request_queue *q, |
552 | struct gendisk *bd_disk, unsigned int cmd, void __user *arg) | ||
521 | { | 553 | { |
522 | request_queue_t *q; | ||
523 | int err; | 554 | int err; |
524 | 555 | ||
525 | q = bd_disk->queue; | 556 | if (!q || blk_get_queue(q)) |
526 | if (!q) | ||
527 | return -ENXIO; | ||
528 | |||
529 | if (blk_get_queue(q)) | ||
530 | return -ENXIO; | 557 | return -ENXIO; |
531 | 558 | ||
532 | switch (cmd) { | 559 | switch (cmd) { |