summary refs log tree commit diff
path: root/block/scsi_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/scsi_ioctl.c')
-rw-r--r--block/scsi_ioctl.c101
1 files changed, 72 insertions, 29 deletions
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index 24f7af9d0abc..b33eda26e205 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -350,16 +350,51 @@ out:
 	return ret;
 }
 
+/**
+ * sg_scsi_ioctl  --  handle deprecated SCSI_IOCTL_SEND_COMMAND ioctl
+ * @file:	file this ioctl operates on (optional)
+ * @q:		request queue to send scsi commands down
+ * @disk:	gendisk to operate on (option)
+ * @sic:	userspace structure describing the command to perform
+ *
+ * Send down the scsi command described by @sic to the device below
+ * the request queue @q.  If @file is non-NULL it's used to perform
+ * fine-grained permission checks that allow users to send down
+ * non-destructive SCSI commands.  If the caller has a struct gendisk
+ * available it should be passed in as @disk to allow the low level
+ * driver to use the information contained in it.  A non-NULL @disk
+ * is only allowed if the caller knows that the low level driver doesn't
+ * need it (e.g. in the scsi subsystem).
+ *
+ * Notes:
+ *   -  This interface is deprecated - users should use the SG_IO
+ *      interface instead, as this is a more flexible approach to
+ *      performing SCSI commands on a device.
+ *   -  The SCSI command length is determined by examining the 1st byte
+ *      of the given command. There is no way to override this.
+ *   -  Data transfers are limited to PAGE_SIZE
+ *   -  The length (x + y) must be at least OMAX_SB_LEN bytes long to
+ *      accommodate the sense buffer when an error occurs.
+ *      The sense buffer is truncated to OMAX_SB_LEN (16) bytes so that
+ *      old code will not be surprised.
+ *   -  If a Unix error occurs (e.g. ENOMEM) then the user will receive
+ *      a negative return and the Unix error code in 'errno'.
+ *      If the SCSI command succeeds then 0 is returned.
+ *      Positive numbers returned are the compacted SCSI error codes (4
+ *      bytes in one int) where the lowest byte is the SCSI status.
+ */
 #define OMAX_SB_LEN 16          /* For backward compatibility */
-
-static int sg_scsi_ioctl(struct file *file, request_queue_t *q,
-			 struct gendisk *bd_disk, Scsi_Ioctl_Command __user *sic)
+int sg_scsi_ioctl(struct file *file, struct request_queue *q,
+		  struct gendisk *disk, struct scsi_ioctl_command __user *sic)
 {
 	struct request *rq;
 	int err;
 	unsigned int in_len, out_len, bytes, opcode, cmdlen;
 	char *buffer = NULL, sense[SCSI_SENSE_BUFFERSIZE];
 
+	if (!sic)
+		return -EINVAL;
+
 	/*
 	 * get in an out lengths, verify they don't exceed a page worth of data
 	 */
@@ -393,45 +428,53 @@ static int sg_scsi_ioctl(struct file *file, request_queue_t *q,
 	if (copy_from_user(rq->cmd, sic->data, cmdlen))
 		goto error;
 
-	if (copy_from_user(buffer, sic->data + cmdlen, in_len))
+	if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len))
 		goto error;
 
 	err = verify_command(file, rq->cmd);
 	if (err)
 		goto error;
 
+	/* default.  possible overriden later */
+	rq->retries = 5;
+
 	switch (opcode) {
-		case SEND_DIAGNOSTIC:
-		case FORMAT_UNIT:
-			rq->timeout = FORMAT_UNIT_TIMEOUT;
-			break;
-		case START_STOP:
-			rq->timeout = START_STOP_TIMEOUT;
-			break;
-		case MOVE_MEDIUM:
-			rq->timeout = MOVE_MEDIUM_TIMEOUT;
-			break;
-		case READ_ELEMENT_STATUS:
-			rq->timeout = READ_ELEMENT_STATUS_TIMEOUT;
-			break;
-		case READ_DEFECT_DATA:
-			rq->timeout = READ_DEFECT_DATA_TIMEOUT;
-			break;
-		default:
-			rq->timeout = BLK_DEFAULT_TIMEOUT;
-			break;
+	case SEND_DIAGNOSTIC:
+	case FORMAT_UNIT:
+		rq->timeout = FORMAT_UNIT_TIMEOUT;
+		rq->retries = 1;
+		break;
+	case START_STOP:
+		rq->timeout = START_STOP_TIMEOUT;
+		break;
+	case MOVE_MEDIUM:
+		rq->timeout = MOVE_MEDIUM_TIMEOUT;
+		break;
+	case READ_ELEMENT_STATUS:
+		rq->timeout = READ_ELEMENT_STATUS_TIMEOUT;
+		break;
+	case READ_DEFECT_DATA:
+		rq->timeout = READ_DEFECT_DATA_TIMEOUT;
+		rq->retries = 1;
+		break;
+	default:
+		rq->timeout = BLK_DEFAULT_TIMEOUT;
+		break;
+	}
+
+	if (bytes && blk_rq_map_kern(q, rq, buffer, bytes, __GFP_WAIT)) {
+		err = DRIVER_ERROR << 24;
+		goto out;
 	}
 
 	memset(sense, 0, sizeof(sense));
 	rq->sense = sense;
 	rq->sense_len = 0;
-
-	rq->data = buffer;
-	rq->data_len = bytes;
 	rq->flags |= REQ_BLOCK_PC;
-	rq->retries = 0;
 
-	blk_execute_rq(q, bd_disk, rq, 0);
+	blk_execute_rq(q, disk, rq, 0);
+
+out:
 	err = rq->errors & 0xff;	/* only 8 bit SCSI status */
 	if (err) {
 		if (rq->sense_len && rq->sense) {
@@ -450,7 +493,7 @@ error:
 	blk_put_request(rq);
 	return err;
 }
-
+EXPORT_SYMBOL_GPL(sg_scsi_ioctl);
 
 /* Send basic block requests */
 static int __blk_send_generic(request_queue_t *q, struct gendisk *bd_disk, int cmd, int data)