Loading block/bsg.c +121 −77 Original line number Diff line number Diff line Loading @@ -103,8 +103,8 @@ struct bsg_command { struct request *rq; struct bio *bio; int err; struct sg_io_hdr hdr; struct sg_io_hdr __user *uhdr; struct sg_io_v4 hdr; struct sg_io_v4 __user *uhdr; char sense[SCSI_SENSE_BUFFERSIZE]; }; Loading Loading @@ -235,57 +235,82 @@ static struct bsg_command *bsg_get_command(struct bsg_device *bd) return bc; } static int blk_fill_sgv4_hdr_rq(request_queue_t *q, struct request *rq, struct sg_io_v4 *hdr, int has_write_perm) { memset(rq->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */ if (copy_from_user(rq->cmd, (void *)(unsigned long)hdr->request, hdr->request_len)) return -EFAULT; if (blk_verify_command(rq->cmd, has_write_perm)) return -EPERM; /* * fill in request structure */ rq->cmd_len = hdr->request_len; rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->timeout = (hdr->timeout * HZ) / 1000; if (!rq->timeout) rq->timeout = q->sg_timeout; if (!rq->timeout) rq->timeout = BLK_DEFAULT_SG_TIMEOUT; return 0; } /* * Check if sg_io_hdr from user is allowed and valid * Check if sg_io_v4 from user is allowed and valid */ static int bsg_validate_sghdr(request_queue_t *q, struct sg_io_hdr *hdr, int *rw) bsg_validate_sgv4_hdr(request_queue_t *q, struct sg_io_v4 *hdr, int *rw) { if (hdr->interface_id != 'S') if (hdr->guard != 'Q') return -EINVAL; if (hdr->cmd_len > BLK_MAX_CDB) if (hdr->request_len > BLK_MAX_CDB) return -EINVAL; if (hdr->dxfer_len > (q->max_sectors << 9)) if (hdr->dout_xfer_len > (q->max_sectors << 9) || hdr->din_xfer_len > (q->max_sectors << 9)) return -EIO; /* not supported currently */ if (hdr->protocol || hdr->subprotocol) return -EINVAL; /* * looks sane, if no data then it should be fine from our POV */ if (!hdr->dxfer_len) if (!hdr->dout_xfer_len && !hdr->din_xfer_len) return 0; switch (hdr->dxfer_direction) { case SG_DXFER_TO_FROM_DEV: case SG_DXFER_FROM_DEV: *rw = READ; break; case SG_DXFER_TO_DEV: *rw = WRITE; break; default: /* not supported currently */ if (hdr->dout_xfer_len && hdr->din_xfer_len) return -EINVAL; } *rw = hdr->dout_xfer_len ? WRITE : READ; return 0; } /* * map sg_io_hdr to a request. for scatter-gather sg_io_hdr, we map * each segment to a bio and string multiple bio's to the request * map sg_io_v4 to a request. */ static struct request * bsg_map_hdr(struct bsg_device *bd, int rw, struct sg_io_hdr *hdr) bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) { request_queue_t *q = bd->queue; struct sg_iovec iov; struct sg_iovec __user *u_iov; struct request *rq; int ret, i = 0; int ret, rw; unsigned int dxfer_len; void *dxferp = NULL; dprintk("map hdr %p/%d/%d\n", hdr->dxferp, hdr->dxfer_len, hdr->iovec_count); dprintk("map hdr %llx/%u %llx/%u\n", (unsigned long long) hdr->dout_xferp, hdr->dout_xfer_len, (unsigned long long) hdr->din_xferp, hdr->din_xfer_len); ret = bsg_validate_sghdr(q, hdr, &rw); ret = bsg_validate_sgv4_hdr(q, hdr, &rw); if (ret) return ERR_PTR(ret); Loading @@ -293,45 +318,30 @@ bsg_map_hdr(struct bsg_device *bd, int rw, struct sg_io_hdr *hdr) * map scatter-gather elements seperately and string them to request */ rq = blk_get_request(q, rw, GFP_KERNEL); ret = blk_fill_sghdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM, ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM, &bd->flags)); if (ret) { blk_put_request(rq); return ERR_PTR(ret); } if (!hdr->iovec_count) { ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len); if (ret) goto out; } u_iov = hdr->dxferp; for (ret = 0, i = 0; i < hdr->iovec_count; i++, u_iov++) { if (copy_from_user(&iov, u_iov, sizeof(iov))) { ret = -EFAULT; break; } if (!iov.iov_len || !iov.iov_base) { ret = -EINVAL; break; } ret = blk_rq_map_user(q, rq, iov.iov_base, iov.iov_len); if (ret) break; } /* * bugger, cleanup */ if (hdr->dout_xfer_len) { dxfer_len = hdr->dout_xfer_len; dxferp = (void*)(unsigned long)hdr->dout_xferp; } else if (hdr->din_xfer_len) { dxfer_len = hdr->din_xfer_len; dxferp = (void*)(unsigned long)hdr->din_xferp; } else dxfer_len = 0; if (dxfer_len) { ret = blk_rq_map_user(q, rq, dxferp, dxfer_len); if (ret) { out: dprintk("failed map at %d: %d\n", i, ret); blk_unmap_sghdr_rq(rq, hdr); dprintk("failed map at %d\n", ret); blk_put_request(rq); rq = ERR_PTR(ret); } } return rq; } Loading @@ -346,7 +356,7 @@ static void bsg_rq_end_io(struct request *rq, int uptodate) struct bsg_device *bd = bc->bd; unsigned long flags; dprintk("%s: finished rq %p bc %p, bio %p offset %d stat %d\n", dprintk("%s: finished rq %p bc %p, bio %p offset %Zd stat %d\n", bd->name, rq, bc, bc->bio, bc - bd->cmd_map, uptodate); bc->hdr.duration = jiffies_to_msecs(jiffies - bc->hdr.duration); Loading Loading @@ -434,6 +444,42 @@ bsg_get_done_cmd_nosignals(struct bsg_device *bd) return __bsg_get_done_cmd(bd, TASK_UNINTERRUPTIBLE); } static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr, struct bio *bio) { int ret = 0; dprintk("rq %p bio %p %u\n", rq, bio, rq->errors); /* * fill in all the output members */ hdr->device_status = status_byte(rq->errors); hdr->transport_status = host_byte(rq->errors); hdr->driver_status = driver_byte(rq->errors); hdr->info = 0; if (hdr->device_status || hdr->transport_status || hdr->driver_status) hdr->info |= SG_INFO_CHECK; hdr->din_resid = rq->data_len; hdr->response_len = 0; if (rq->sense_len && hdr->response) { int len = min((unsigned int) hdr->max_response_len, rq->sense_len); ret = copy_to_user((void*)(unsigned long)hdr->response, rq->sense, len); if (!ret) hdr->response_len = len; else ret = -EFAULT; } blk_rq_unmap_user(bio); blk_put_request(rq); return ret; } static int bsg_complete_all_commands(struct bsg_device *bd) { struct bsg_command *bc; Loading Loading @@ -476,7 +522,7 @@ static int bsg_complete_all_commands(struct bsg_device *bd) break; } tret = blk_complete_sghdr_rq(bc->rq, &bc->hdr, bc->bio); tret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio); if (!ret) ret = tret; Loading @@ -495,11 +541,11 @@ __bsg_read(char __user *buf, size_t count, bsg_command_callback get_bc, struct bsg_command *bc; int nr_commands, ret; if (count % sizeof(struct sg_io_hdr)) if (count % sizeof(struct sg_io_v4)) return -EINVAL; ret = 0; nr_commands = count / sizeof(struct sg_io_hdr); nr_commands = count / sizeof(struct sg_io_v4); while (nr_commands) { bc = get_bc(bd, iov); if (IS_ERR(bc)) { Loading @@ -512,7 +558,7 @@ __bsg_read(char __user *buf, size_t count, bsg_command_callback get_bc, * after completing the request. so do that here, * bsg_complete_work() cannot do that for us */ ret = blk_complete_sghdr_rq(bc->rq, &bc->hdr, bc->bio); ret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio); if (copy_to_user(buf, (char *) &bc->hdr, sizeof(bc->hdr))) ret = -EFAULT; Loading @@ -522,8 +568,8 @@ __bsg_read(char __user *buf, size_t count, bsg_command_callback get_bc, if (ret) break; buf += sizeof(struct sg_io_hdr); *bytes_read += sizeof(struct sg_io_hdr); buf += sizeof(struct sg_io_v4); *bytes_read += sizeof(struct sg_io_v4); nr_commands--; } Loading Loading @@ -582,16 +628,15 @@ static ssize_t __bsg_write(struct bsg_device *bd, const char __user *buf, struct request *rq; int ret, nr_commands; if (count % sizeof(struct sg_io_hdr)) if (count % sizeof(struct sg_io_v4)) return -EINVAL; nr_commands = count / sizeof(struct sg_io_hdr); nr_commands = count / sizeof(struct sg_io_v4); rq = NULL; bc = NULL; ret = 0; while (nr_commands) { request_queue_t *q = bd->queue; int rw = READ; bc = bsg_get_command(bd); if (!bc) Loading @@ -602,7 +647,7 @@ static ssize_t __bsg_write(struct bsg_device *bd, const char __user *buf, break; } bc->uhdr = (struct sg_io_hdr __user *) buf; bc->uhdr = (struct sg_io_v4 __user *) buf; if (copy_from_user(&bc->hdr, buf, sizeof(bc->hdr))) { ret = -EFAULT; break; Loading @@ -611,7 +656,7 @@ static ssize_t __bsg_write(struct bsg_device *bd, const char __user *buf, /* * get a request, fill in the blanks, and add to request queue */ rq = bsg_map_hdr(bd, rw, &bc->hdr); rq = bsg_map_hdr(bd, &bc->hdr); if (IS_ERR(rq)) { ret = PTR_ERR(rq); rq = NULL; Loading @@ -622,12 +667,10 @@ static ssize_t __bsg_write(struct bsg_device *bd, const char __user *buf, bc = NULL; rq = NULL; nr_commands--; buf += sizeof(struct sg_io_hdr); *bytes_read += sizeof(struct sg_io_hdr); buf += sizeof(struct sg_io_v4); *bytes_read += sizeof(struct sg_io_v4); } if (rq) blk_unmap_sghdr_rq(rq, &bc->hdr); if (bc) bsg_free_command(bc); Loading Loading @@ -898,11 +941,12 @@ bsg_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case SG_GET_RESERVED_SIZE: case SG_SET_RESERVED_SIZE: case SG_EMULATED_HOST: case SG_IO: case SCSI_IOCTL_SEND_COMMAND: { void __user *uarg = (void __user *) arg; return scsi_cmd_ioctl(file, bd->disk, cmd, uarg); } case SG_IO: return -EINVAL; /* * block device ioctls */ Loading Loading
block/bsg.c +121 −77 Original line number Diff line number Diff line Loading @@ -103,8 +103,8 @@ struct bsg_command { struct request *rq; struct bio *bio; int err; struct sg_io_hdr hdr; struct sg_io_hdr __user *uhdr; struct sg_io_v4 hdr; struct sg_io_v4 __user *uhdr; char sense[SCSI_SENSE_BUFFERSIZE]; }; Loading Loading @@ -235,57 +235,82 @@ static struct bsg_command *bsg_get_command(struct bsg_device *bd) return bc; } static int blk_fill_sgv4_hdr_rq(request_queue_t *q, struct request *rq, struct sg_io_v4 *hdr, int has_write_perm) { memset(rq->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */ if (copy_from_user(rq->cmd, (void *)(unsigned long)hdr->request, hdr->request_len)) return -EFAULT; if (blk_verify_command(rq->cmd, has_write_perm)) return -EPERM; /* * fill in request structure */ rq->cmd_len = hdr->request_len; rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->timeout = (hdr->timeout * HZ) / 1000; if (!rq->timeout) rq->timeout = q->sg_timeout; if (!rq->timeout) rq->timeout = BLK_DEFAULT_SG_TIMEOUT; return 0; } /* * Check if sg_io_hdr from user is allowed and valid * Check if sg_io_v4 from user is allowed and valid */ static int bsg_validate_sghdr(request_queue_t *q, struct sg_io_hdr *hdr, int *rw) bsg_validate_sgv4_hdr(request_queue_t *q, struct sg_io_v4 *hdr, int *rw) { if (hdr->interface_id != 'S') if (hdr->guard != 'Q') return -EINVAL; if (hdr->cmd_len > BLK_MAX_CDB) if (hdr->request_len > BLK_MAX_CDB) return -EINVAL; if (hdr->dxfer_len > (q->max_sectors << 9)) if (hdr->dout_xfer_len > (q->max_sectors << 9) || hdr->din_xfer_len > (q->max_sectors << 9)) return -EIO; /* not supported currently */ if (hdr->protocol || hdr->subprotocol) return -EINVAL; /* * looks sane, if no data then it should be fine from our POV */ if (!hdr->dxfer_len) if (!hdr->dout_xfer_len && !hdr->din_xfer_len) return 0; switch (hdr->dxfer_direction) { case SG_DXFER_TO_FROM_DEV: case SG_DXFER_FROM_DEV: *rw = READ; break; case SG_DXFER_TO_DEV: *rw = WRITE; break; default: /* not supported currently */ if (hdr->dout_xfer_len && hdr->din_xfer_len) return -EINVAL; } *rw = hdr->dout_xfer_len ? WRITE : READ; return 0; } /* * map sg_io_hdr to a request. for scatter-gather sg_io_hdr, we map * each segment to a bio and string multiple bio's to the request * map sg_io_v4 to a request. */ static struct request * bsg_map_hdr(struct bsg_device *bd, int rw, struct sg_io_hdr *hdr) bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) { request_queue_t *q = bd->queue; struct sg_iovec iov; struct sg_iovec __user *u_iov; struct request *rq; int ret, i = 0; int ret, rw; unsigned int dxfer_len; void *dxferp = NULL; dprintk("map hdr %p/%d/%d\n", hdr->dxferp, hdr->dxfer_len, hdr->iovec_count); dprintk("map hdr %llx/%u %llx/%u\n", (unsigned long long) hdr->dout_xferp, hdr->dout_xfer_len, (unsigned long long) hdr->din_xferp, hdr->din_xfer_len); ret = bsg_validate_sghdr(q, hdr, &rw); ret = bsg_validate_sgv4_hdr(q, hdr, &rw); if (ret) return ERR_PTR(ret); Loading @@ -293,45 +318,30 @@ bsg_map_hdr(struct bsg_device *bd, int rw, struct sg_io_hdr *hdr) * map scatter-gather elements seperately and string them to request */ rq = blk_get_request(q, rw, GFP_KERNEL); ret = blk_fill_sghdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM, ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM, &bd->flags)); if (ret) { blk_put_request(rq); return ERR_PTR(ret); } if (!hdr->iovec_count) { ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len); if (ret) goto out; } u_iov = hdr->dxferp; for (ret = 0, i = 0; i < hdr->iovec_count; i++, u_iov++) { if (copy_from_user(&iov, u_iov, sizeof(iov))) { ret = -EFAULT; break; } if (!iov.iov_len || !iov.iov_base) { ret = -EINVAL; break; } ret = blk_rq_map_user(q, rq, iov.iov_base, iov.iov_len); if (ret) break; } /* * bugger, cleanup */ if (hdr->dout_xfer_len) { dxfer_len = hdr->dout_xfer_len; dxferp = (void*)(unsigned long)hdr->dout_xferp; } else if (hdr->din_xfer_len) { dxfer_len = hdr->din_xfer_len; dxferp = (void*)(unsigned long)hdr->din_xferp; } else dxfer_len = 0; if (dxfer_len) { ret = blk_rq_map_user(q, rq, dxferp, dxfer_len); if (ret) { out: dprintk("failed map at %d: %d\n", i, ret); blk_unmap_sghdr_rq(rq, hdr); dprintk("failed map at %d\n", ret); blk_put_request(rq); rq = ERR_PTR(ret); } } return rq; } Loading @@ -346,7 +356,7 @@ static void bsg_rq_end_io(struct request *rq, int uptodate) struct bsg_device *bd = bc->bd; unsigned long flags; dprintk("%s: finished rq %p bc %p, bio %p offset %d stat %d\n", dprintk("%s: finished rq %p bc %p, bio %p offset %Zd stat %d\n", bd->name, rq, bc, bc->bio, bc - bd->cmd_map, uptodate); bc->hdr.duration = jiffies_to_msecs(jiffies - bc->hdr.duration); Loading Loading @@ -434,6 +444,42 @@ bsg_get_done_cmd_nosignals(struct bsg_device *bd) return __bsg_get_done_cmd(bd, TASK_UNINTERRUPTIBLE); } static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr, struct bio *bio) { int ret = 0; dprintk("rq %p bio %p %u\n", rq, bio, rq->errors); /* * fill in all the output members */ hdr->device_status = status_byte(rq->errors); hdr->transport_status = host_byte(rq->errors); hdr->driver_status = driver_byte(rq->errors); hdr->info = 0; if (hdr->device_status || hdr->transport_status || hdr->driver_status) hdr->info |= SG_INFO_CHECK; hdr->din_resid = rq->data_len; hdr->response_len = 0; if (rq->sense_len && hdr->response) { int len = min((unsigned int) hdr->max_response_len, rq->sense_len); ret = copy_to_user((void*)(unsigned long)hdr->response, rq->sense, len); if (!ret) hdr->response_len = len; else ret = -EFAULT; } blk_rq_unmap_user(bio); blk_put_request(rq); return ret; } static int bsg_complete_all_commands(struct bsg_device *bd) { struct bsg_command *bc; Loading Loading @@ -476,7 +522,7 @@ static int bsg_complete_all_commands(struct bsg_device *bd) break; } tret = blk_complete_sghdr_rq(bc->rq, &bc->hdr, bc->bio); tret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio); if (!ret) ret = tret; Loading @@ -495,11 +541,11 @@ __bsg_read(char __user *buf, size_t count, bsg_command_callback get_bc, struct bsg_command *bc; int nr_commands, ret; if (count % sizeof(struct sg_io_hdr)) if (count % sizeof(struct sg_io_v4)) return -EINVAL; ret = 0; nr_commands = count / sizeof(struct sg_io_hdr); nr_commands = count / sizeof(struct sg_io_v4); while (nr_commands) { bc = get_bc(bd, iov); if (IS_ERR(bc)) { Loading @@ -512,7 +558,7 @@ __bsg_read(char __user *buf, size_t count, bsg_command_callback get_bc, * after completing the request. so do that here, * bsg_complete_work() cannot do that for us */ ret = blk_complete_sghdr_rq(bc->rq, &bc->hdr, bc->bio); ret = blk_complete_sgv4_hdr_rq(bc->rq, &bc->hdr, bc->bio); if (copy_to_user(buf, (char *) &bc->hdr, sizeof(bc->hdr))) ret = -EFAULT; Loading @@ -522,8 +568,8 @@ __bsg_read(char __user *buf, size_t count, bsg_command_callback get_bc, if (ret) break; buf += sizeof(struct sg_io_hdr); *bytes_read += sizeof(struct sg_io_hdr); buf += sizeof(struct sg_io_v4); *bytes_read += sizeof(struct sg_io_v4); nr_commands--; } Loading Loading @@ -582,16 +628,15 @@ static ssize_t __bsg_write(struct bsg_device *bd, const char __user *buf, struct request *rq; int ret, nr_commands; if (count % sizeof(struct sg_io_hdr)) if (count % sizeof(struct sg_io_v4)) return -EINVAL; nr_commands = count / sizeof(struct sg_io_hdr); nr_commands = count / sizeof(struct sg_io_v4); rq = NULL; bc = NULL; ret = 0; while (nr_commands) { request_queue_t *q = bd->queue; int rw = READ; bc = bsg_get_command(bd); if (!bc) Loading @@ -602,7 +647,7 @@ static ssize_t __bsg_write(struct bsg_device *bd, const char __user *buf, break; } bc->uhdr = (struct sg_io_hdr __user *) buf; bc->uhdr = (struct sg_io_v4 __user *) buf; if (copy_from_user(&bc->hdr, buf, sizeof(bc->hdr))) { ret = -EFAULT; break; Loading @@ -611,7 +656,7 @@ static ssize_t __bsg_write(struct bsg_device *bd, const char __user *buf, /* * get a request, fill in the blanks, and add to request queue */ rq = bsg_map_hdr(bd, rw, &bc->hdr); rq = bsg_map_hdr(bd, &bc->hdr); if (IS_ERR(rq)) { ret = PTR_ERR(rq); rq = NULL; Loading @@ -622,12 +667,10 @@ static ssize_t __bsg_write(struct bsg_device *bd, const char __user *buf, bc = NULL; rq = NULL; nr_commands--; buf += sizeof(struct sg_io_hdr); *bytes_read += sizeof(struct sg_io_hdr); buf += sizeof(struct sg_io_v4); *bytes_read += sizeof(struct sg_io_v4); } if (rq) blk_unmap_sghdr_rq(rq, &bc->hdr); if (bc) bsg_free_command(bc); Loading Loading @@ -898,11 +941,12 @@ bsg_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case SG_GET_RESERVED_SIZE: case SG_SET_RESERVED_SIZE: case SG_EMULATED_HOST: case SG_IO: case SCSI_IOCTL_SEND_COMMAND: { void __user *uarg = (void __user *) arg; return scsi_cmd_ioctl(file, bd->disk, cmd, uarg); } case SG_IO: return -EINVAL; /* * block device ioctls */ Loading