Merge tag '5.6-rc-small-smb3-fix-for-stable' of git://git.samba.org/sfrench/cifs-2.6
[linux-2.6-microblaze.git] / block / scsi_ioctl.c
index 650bade..b4e73d5 100644 (file)
@@ -20,6 +20,7 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_ioctl.h>
 #include <scsi/scsi_cmnd.h>
+#include <scsi/sg.h>
 
 struct blk_cmd_filter {
        unsigned long read_ok[BLK_SCSI_CMD_PER_LONG];
@@ -550,34 +551,6 @@ static inline int blk_send_start_stop(struct request_queue *q,
        return __blk_send_generic(q, bd_disk, GPCMD_START_STOP_UNIT, data);
 }
 
-#ifdef CONFIG_COMPAT
-struct compat_sg_io_hdr {
-       compat_int_t interface_id;      /* [i] 'S' for SCSI generic (required) */
-       compat_int_t dxfer_direction;   /* [i] data transfer direction  */
-       unsigned char cmd_len;          /* [i] SCSI command length ( <= 16 bytes) */
-       unsigned char mx_sb_len;        /* [i] max length to write to sbp */
-       unsigned short iovec_count;     /* [i] 0 implies no scatter gather */
-       compat_uint_t dxfer_len;        /* [i] byte count of data transfer */
-       compat_uint_t dxferp;           /* [i], [*io] points to data transfer memory
-                                               or scatter gather list */
-       compat_uptr_t cmdp;             /* [i], [*i] points to command to perform */
-       compat_uptr_t sbp;              /* [i], [*o] points to sense_buffer memory */
-       compat_uint_t timeout;          /* [i] MAX_UINT->no timeout (unit: millisec) */
-       compat_uint_t flags;            /* [i] 0 -> default, see SG_FLAG... */
-       compat_int_t pack_id;           /* [i->o] unused internally (normally) */
-       compat_uptr_t usr_ptr;          /* [i->o] unused internally */
-       unsigned char status;           /* [o] scsi status */
-       unsigned char masked_status;    /* [o] shifted, masked scsi status */
-       unsigned char msg_status;       /* [o] messaging level data (optional) */
-       unsigned char sb_len_wr;        /* [o] byte count actually written to sbp */
-       unsigned short host_status;     /* [o] errors from host adapter */
-       unsigned short driver_status;   /* [o] errors from software driver */
-       compat_int_t resid;             /* [o] dxfer_len - actual_transferred */
-       compat_uint_t duration;         /* [o] time taken by cmd (unit: millisec) */
-       compat_uint_t info;             /* [o] auxiliary information */
-};
-#endif
-
 int put_sg_io_hdr(const struct sg_io_hdr *hdr, void __user *argp)
 {
 #ifdef CONFIG_COMPAT
@@ -666,6 +639,136 @@ int get_sg_io_hdr(struct sg_io_hdr *hdr, const void __user *argp)
 }
 EXPORT_SYMBOL(get_sg_io_hdr);
 
+#ifdef CONFIG_COMPAT
+struct compat_cdrom_generic_command {
+       unsigned char   cmd[CDROM_PACKET_SIZE];
+       compat_caddr_t  buffer;
+       compat_uint_t   buflen;
+       compat_int_t    stat;
+       compat_caddr_t  sense;
+       unsigned char   data_direction;
+       compat_int_t    quiet;
+       compat_int_t    timeout;
+       compat_caddr_t  reserved[1];
+};
+#endif
+
+static int scsi_get_cdrom_generic_arg(struct cdrom_generic_command *cgc,
+                                     const void __user *arg)
+{
+#ifdef CONFIG_COMPAT
+       if (in_compat_syscall()) {
+               struct compat_cdrom_generic_command cgc32;
+
+               if (copy_from_user(&cgc32, arg, sizeof(cgc32)))
+                       return -EFAULT;
+
+               *cgc = (struct cdrom_generic_command) {
+                       .buffer         = compat_ptr(cgc32.buffer),
+                       .buflen         = cgc32.buflen,
+                       .stat           = cgc32.stat,
+                       .sense          = compat_ptr(cgc32.sense),
+                       .data_direction = cgc32.data_direction,
+                       .quiet          = cgc32.quiet,
+                       .timeout        = cgc32.timeout,
+                       .reserved[0]    = compat_ptr(cgc32.reserved[0]),
+               };
+               memcpy(&cgc->cmd, &cgc32.cmd, CDROM_PACKET_SIZE);
+               return 0;
+       }
+#endif
+       if (copy_from_user(cgc, arg, sizeof(*cgc)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int scsi_put_cdrom_generic_arg(const struct cdrom_generic_command *cgc,
+                                     void __user *arg)
+{
+#ifdef CONFIG_COMPAT
+       if (in_compat_syscall()) {
+               struct compat_cdrom_generic_command cgc32 = {
+                       .buffer         = (uintptr_t)(cgc->buffer),
+                       .buflen         = cgc->buflen,
+                       .stat           = cgc->stat,
+                       .sense          = (uintptr_t)(cgc->sense),
+                       .data_direction = cgc->data_direction,
+                       .quiet          = cgc->quiet,
+                       .timeout        = cgc->timeout,
+                       .reserved[0]    = (uintptr_t)(cgc->reserved[0]),
+               };
+               memcpy(&cgc32.cmd, &cgc->cmd, CDROM_PACKET_SIZE);
+
+               if (copy_to_user(arg, &cgc32, sizeof(cgc32)))
+                       return -EFAULT;
+
+               return 0;
+       }
+#endif
+       if (copy_to_user(arg, cgc, sizeof(*cgc)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int scsi_cdrom_send_packet(struct request_queue *q,
+                                 struct gendisk *bd_disk,
+                                 fmode_t mode, void __user *arg)
+{
+       struct cdrom_generic_command cgc;
+       struct sg_io_hdr hdr;
+       int err;
+
+       err = scsi_get_cdrom_generic_arg(&cgc, arg);
+       if (err)
+               return err;
+
+       cgc.timeout = clock_t_to_jiffies(cgc.timeout);
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.interface_id = 'S';
+       hdr.cmd_len = sizeof(cgc.cmd);
+       hdr.dxfer_len = cgc.buflen;
+       switch (cgc.data_direction) {
+               case CGC_DATA_UNKNOWN:
+                       hdr.dxfer_direction = SG_DXFER_UNKNOWN;
+                       break;
+               case CGC_DATA_WRITE:
+                       hdr.dxfer_direction = SG_DXFER_TO_DEV;
+                       break;
+               case CGC_DATA_READ:
+                       hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+                       break;
+               case CGC_DATA_NONE:
+                       hdr.dxfer_direction = SG_DXFER_NONE;
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       hdr.dxferp = cgc.buffer;
+       hdr.sbp = cgc.sense;
+       if (hdr.sbp)
+               hdr.mx_sb_len = sizeof(struct request_sense);
+       hdr.timeout = jiffies_to_msecs(cgc.timeout);
+       hdr.cmdp = ((struct cdrom_generic_command __user*) arg)->cmd;
+       hdr.cmd_len = sizeof(cgc.cmd);
+
+       err = sg_io(q, bd_disk, &hdr, mode);
+       if (err == -EFAULT)
+               return -EFAULT;
+
+       if (hdr.status)
+               return -EIO;
+
+       cgc.stat = err;
+       cgc.buflen = hdr.resid;
+       if (scsi_put_cdrom_generic_arg(&cgc, arg))
+               return -EFAULT;
+
+       return err;
+}
+
 int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mode,
                   unsigned int cmd, void __user *arg)
 {
@@ -716,60 +819,9 @@ int scsi_cmd_ioctl(struct request_queue *q, struct gendisk *bd_disk, fmode_t mod
                                err = -EFAULT;
                        break;
                }
-               case CDROM_SEND_PACKET: {
-                       struct cdrom_generic_command cgc;
-                       struct sg_io_hdr hdr;
-
-                       err = -EFAULT;
-                       if (copy_from_user(&cgc, arg, sizeof(cgc)))
-                               break;
-                       cgc.timeout = clock_t_to_jiffies(cgc.timeout);
-                       memset(&hdr, 0, sizeof(hdr));
-                       hdr.interface_id = 'S';
-                       hdr.cmd_len = sizeof(cgc.cmd);
-                       hdr.dxfer_len = cgc.buflen;
-                       err = 0;
-                       switch (cgc.data_direction) {
-                               case CGC_DATA_UNKNOWN:
-                                       hdr.dxfer_direction = SG_DXFER_UNKNOWN;
-                                       break;
-                               case CGC_DATA_WRITE:
-                                       hdr.dxfer_direction = SG_DXFER_TO_DEV;
-                                       break;
-                               case CGC_DATA_READ:
-                                       hdr.dxfer_direction = SG_DXFER_FROM_DEV;
-                                       break;
-                               case CGC_DATA_NONE:
-                                       hdr.dxfer_direction = SG_DXFER_NONE;
-                                       break;
-                               default:
-                                       err = -EINVAL;
-                       }
-                       if (err)
-                               break;
-
-                       hdr.dxferp = cgc.buffer;
-                       hdr.sbp = cgc.sense;
-                       if (hdr.sbp)
-                               hdr.mx_sb_len = sizeof(struct request_sense);
-                       hdr.timeout = jiffies_to_msecs(cgc.timeout);
-                       hdr.cmdp = ((struct cdrom_generic_command __user*) arg)->cmd;
-                       hdr.cmd_len = sizeof(cgc.cmd);
-
-                       err = sg_io(q, bd_disk, &hdr, mode);
-                       if (err == -EFAULT)
-                               break;
-
-                       if (hdr.status)
-                               err = -EIO;
-
-                       cgc.stat = err;
-                       cgc.buflen = hdr.resid;
-                       if (copy_to_user(arg, &cgc, sizeof(cgc)))
-                               err = -EFAULT;
-
+               case CDROM_SEND_PACKET:
+                       err = scsi_cdrom_send_packet(q, bd_disk, mode, arg);
                        break;
-               }
 
                /*
                 * old junk scsi send command ioctl