/** * ioctl_probe -- return host identification * @host: host to identify * @buffer: userspace buffer for identification * * Return: * * if successful, %1 and an identifying string at @buffer, if @buffer * is non-NULL, filling to the length stored at * (int *) @buffer. * * <0 error code on failure.
*/ staticint ioctl_probe(struct Scsi_Host *host, void __user *buffer)
{ unsignedint len, slen; constchar *string;
if (buffer) { if (get_user(len, (unsignedint __user *) buffer)) return -EFAULT;
if (host->hostt->info)
string = host->hostt->info(host); else
string = host->hostt->name; if (string) {
slen = strlen(string); if (len > slen)
len = slen + 1; if (copy_to_user(buffer, string, len)) return -EFAULT;
}
} return 1;
}
staticint ioctl_internal_command(struct scsi_device *sdev, char *cmd, int timeout, int retries)
{ int result; struct scsi_sense_hdr sshdr; conststruct scsi_exec_args exec_args = {
.sshdr = &sshdr,
};
SCSI_LOG_IOCTL(1, sdev_printk(KERN_INFO, sdev, "Trying ioctl with scsi command %d\n", *cmd));
result = scsi_execute_cmd(sdev, cmd, REQ_OP_DRV_IN, NULL, 0, timeout,
retries, &exec_args);
SCSI_LOG_IOCTL(2, sdev_printk(KERN_INFO, sdev, "Ioctl returned 0x%x\n", result));
if (result < 0) goto out; if (scsi_sense_valid(&sshdr)) { switch (sshdr.sense_key) { case ILLEGAL_REQUEST: if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
sdev->lockable = 0; else
sdev_printk(KERN_INFO, sdev, "ioctl_internal_command: " "ILLEGAL REQUEST " "asc=0x%x ascq=0x%x\n",
sshdr.asc, sshdr.ascq); break; case NOT_READY: /* This happens if there is no disc in drive */ if (sdev->removable) break;
fallthrough; case UNIT_ATTENTION: if (sdev->removable) {
sdev->changed = 1;
result = 0; /* This is no longer considered an error */ break;
}
fallthrough; /* for non-removable media */ default:
sdev_printk(KERN_INFO, sdev, "ioctl_internal_command return code = %x\n",
result);
scsi_print_sense_hdr(sdev, NULL, &sshdr); break;
}
}
out:
SCSI_LOG_IOCTL(2, sdev_printk(KERN_INFO, sdev, "IOCTL Releasing command\n")); return result;
}
/** * scsi_set_medium_removal() - send command to allow or prevent medium removal * @sdev: target scsi device * @state: removal state to set (prevent or allow) * * Returns: * * %0 if @sdev is not removable or not lockable or successful. * * non-%0 is a SCSI result code if > 0 or kernel error code if < 0. * * Sets @sdev->locked to the new state on success.
*/ int scsi_set_medium_removal(struct scsi_device *sdev, char state)
{ char scsi_cmd[MAX_COMMAND_SIZE]; int ret;
if (!sdev->removable || !sdev->lockable) return 0;
ret = ioctl_internal_command(sdev, scsi_cmd,
IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES); if (ret == 0)
sdev->locked = (state == SCSI_REMOVAL_PREVENT); return ret;
}
EXPORT_SYMBOL(scsi_set_medium_removal);
/* * The scsi_ioctl_get_pci() function places into arg the value * pci_dev::slot_name (8 characters) for the PCI device (if any). * Returns: 0 on success * -ENXIO if there isn't a PCI device pointer * (could be because the SCSI driver hasn't been * updated yet, or because it isn't a SCSI * device) * any copy_to_user() error on failure there
*/ staticint scsi_ioctl_get_pci(struct scsi_device *sdev, void __user *arg)
{ struct device *dev = scsi_get_device(sdev->host); constchar *name;
if (!dev) return -ENXIO;
name = dev_name(dev);
/* compatibility with old ioctl which only returned
* 20 characters */ return copy_to_user(arg, name, min(strlen(name), (size_t)20))
? -EFAULT: 0;
}
/* * will always return that we are ATAPI even for a real SCSI drive, I'm not * so sure this is worth doing anything about (why would you care??)
*/ staticint sg_emulated_host(struct request_queue *q, int __user *p)
{ return put_user(1, p);
}
/** * scsi_cmd_allowed() - Check if the given command is allowed. * @cmd: SCSI command to check * @open_for_write: is the file / block device opened for writing? * * Only a subset of commands are allowed for unprivileged users. Commands used * to format the media, update the firmware, etc. are not permitted. * * Return: %true if the cmd is allowed, otherwise @false.
*/ bool scsi_cmd_allowed(unsignedchar *cmd, bool open_for_write)
{ /* root can do any command. */ if (capable(CAP_SYS_RAWIO)) returntrue;
/* Anybody who can open the device can do a read-safe command */ switch (cmd[0]) { /* Basic read-only commands */ case TEST_UNIT_READY: case REQUEST_SENSE: case READ_6: case READ_10: case READ_12: case READ_16: case READ_BUFFER: case READ_DEFECT_DATA: case READ_CAPACITY: /* also GPCMD_READ_CDVD_CAPACITY */ case READ_LONG: case INQUIRY: case MODE_SENSE: case MODE_SENSE_10: case LOG_SENSE: case START_STOP: case GPCMD_VERIFY_10: case VERIFY_16: case REPORT_LUNS: case SERVICE_ACTION_IN_16: case RECEIVE_DIAGNOSTIC: case MAINTENANCE_IN: /* also GPCMD_SEND_KEY, which is a write command */ case GPCMD_READ_BUFFER_CAPACITY: /* Audio CD commands */ case GPCMD_PLAY_CD: case GPCMD_PLAY_AUDIO_10: case GPCMD_PLAY_AUDIO_MSF: case GPCMD_PLAY_AUDIO_TI: case GPCMD_PAUSE_RESUME: /* CD/DVD data reading */ case GPCMD_READ_CD: case GPCMD_READ_CD_MSF: case GPCMD_READ_DISC_INFO: case GPCMD_READ_DVD_STRUCTURE: case GPCMD_READ_HEADER: case GPCMD_READ_TRACK_RZONE_INFO: case GPCMD_READ_SUBCHANNEL: case GPCMD_READ_TOC_PMA_ATIP: case GPCMD_REPORT_KEY: case GPCMD_SCAN: case GPCMD_GET_CONFIGURATION: case GPCMD_READ_FORMAT_CAPACITIES: case GPCMD_GET_EVENT_STATUS_NOTIFICATION: case GPCMD_GET_PERFORMANCE: case GPCMD_SEEK: case GPCMD_STOP_PLAY_SCAN: /* ZBC */ case ZBC_IN: returntrue; /* Basic writing commands */ case WRITE_6: case WRITE_10: case WRITE_VERIFY: case WRITE_12: case WRITE_VERIFY_12: case WRITE_16: case WRITE_LONG: case WRITE_LONG_2: case WRITE_SAME: case WRITE_SAME_16: case WRITE_SAME_32: case ERASE: case GPCMD_MODE_SELECT_10: case MODE_SELECT: case LOG_SELECT: case GPCMD_BLANK: case GPCMD_CLOSE_TRACK: case GPCMD_FLUSH_CACHE: case GPCMD_FORMAT_UNIT: case GPCMD_REPAIR_RZONE_TRACK: case GPCMD_RESERVE_RZONE_TRACK: case GPCMD_SEND_DVD_STRUCTURE: case GPCMD_SEND_EVENT: case GPCMD_SEND_OPC: case GPCMD_SEND_CUE_SHEET: case GPCMD_SET_SPEED: case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL: case GPCMD_LOAD_UNLOAD: case GPCMD_SET_STREAMING: case GPCMD_SET_READ_AHEAD: /* ZBC */ case ZBC_OUT: return open_for_write; default: returnfalse;
}
}
EXPORT_SYMBOL(scsi_cmd_allowed);
/** * sg_scsi_ioctl -- handle deprecated SCSI_IOCTL_SEND_COMMAND ioctl * @q: request queue to send scsi commands down * @open_for_write: is the file / block device opened for writing? * @sic: userspace structure describing the command to perform * * Send down the scsi command described by @sic to the device below * the request queue @q. * * 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.
*/ staticint sg_scsi_ioctl(struct request_queue *q, bool open_for_write, struct scsi_ioctl_command __user *sic)
{ struct request *rq; int err; unsignedint in_len, out_len, bytes, opcode, cmdlen; struct scsi_cmnd *scmd; char *buffer = NULL;
if (!sic) return -EINVAL;
/* * get in an out lengths, verify they don't exceed a page worth of data
*/ if (get_user(in_len, &sic->inlen)) return -EFAULT; if (get_user(out_len, &sic->outlen)) return -EFAULT; if (in_len > PAGE_SIZE || out_len > PAGE_SIZE) return -EINVAL; if (get_user(opcode, &sic->data[0])) return -EFAULT;
bytes = max(in_len, out_len); if (bytes) {
buffer = kzalloc(bytes, GFP_NOIO | GFP_USER | __GFP_NOWARN); if (!buffer) return -ENOMEM;
/* * get command and data to send to device, if any
*/
err = -EFAULT;
scmd->cmd_len = cmdlen; if (copy_from_user(scmd->cmnd, sic->data, cmdlen)) goto error;
if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) goto error;
err = -EPERM; if (!scsi_cmd_allowed(scmd->cmnd, open_for_write)) goto error;
/* default. possible overridden later */
scmd->allowed = 5;
switch (opcode) { case SEND_DIAGNOSTIC: case FORMAT_UNIT:
rq->timeout = FORMAT_UNIT_TIMEOUT;
scmd->allowed = 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;
scmd->allowed = 1; break; default:
rq->timeout = BLK_DEFAULT_SG_TIMEOUT; break;
}
if (bytes) {
err = blk_rq_map_kern(rq, buffer, bytes, GFP_NOIO); if (err) goto error;
}
blk_execute_rq(rq, false);
err = scmd->result & 0xff; /* only 8 bit SCSI status */ if (err) { if (scmd->sense_len && scmd->sense_buffer) { /* limit sense len for backward compatibility */ if (copy_to_user(sic->data, scmd->sense_buffer,
min(scmd->sense_len, 16U)))
err = -EFAULT;
}
} else { if (copy_to_user(sic->data, buffer, out_len))
err = -EFAULT;
}
error = get_sg_io_hdr(&hdr, argp); if (error) return error;
error = sg_io(sdev, &hdr, open_for_write); if (error == -EFAULT) return error; if (put_sg_io_hdr(&hdr, argp)) return -EFAULT; return error;
}
/** * scsi_ioctl - Dispatch ioctl to scsi device * @sdev: scsi device receiving ioctl * @open_for_write: is the file / block device opened for writing? * @cmd: which ioctl is it * @arg: data associated with ioctl * * Description: The scsi_ioctl() function differs from most ioctls in that it * does not take a major/minor number as the dev field. Rather, it takes * a pointer to a &struct scsi_device. * * Return: varies depending on the @cmd
*/ int scsi_ioctl(struct scsi_device *sdev, bool open_for_write, int cmd, void __user *arg)
{ struct request_queue *q = sdev->request_queue; struct scsi_sense_hdr sense_hdr;
/* Check for deprecated ioctls ... all the ioctls which don't
* follow the new unique numbering scheme are deprecated */ switch (cmd) { case SCSI_IOCTL_SEND_COMMAND: case SCSI_IOCTL_TEST_UNIT_READY: case SCSI_IOCTL_BENCHMARK_COMMAND: case SCSI_IOCTL_SYNC: case SCSI_IOCTL_START_UNIT: case SCSI_IOCTL_STOP_UNIT:
printk(KERN_WARNING "program %s is using a deprecated SCSI " "ioctl, please convert it to SG_IO\n", current->comm); break; default: break;
}
switch (cmd) { case SG_GET_VERSION_NUM: return sg_get_version(arg); case SG_SET_TIMEOUT: return sg_set_timeout(sdev, arg); case SG_GET_TIMEOUT: return jiffies_to_clock_t(sdev->sg_timeout); case SG_GET_RESERVED_SIZE: return sg_get_reserved_size(sdev, arg); case SG_SET_RESERVED_SIZE: return sg_set_reserved_size(sdev, arg); case SG_EMULATED_HOST: return sg_emulated_host(q, arg); case SG_IO: return scsi_ioctl_sg_io(sdev, open_for_write, arg); case SCSI_IOCTL_SEND_COMMAND: return sg_scsi_ioctl(q, open_for_write, arg); case CDROM_SEND_PACKET: return scsi_cdrom_send_packet(sdev, open_for_write, arg); case CDROMCLOSETRAY: return scsi_send_start_stop(sdev, 3); case CDROMEJECT: return scsi_send_start_stop(sdev, 2); case SCSI_IOCTL_GET_IDLUN: return scsi_get_idlun(sdev, arg); case SCSI_IOCTL_GET_BUS_NUMBER: return put_user(sdev->host->host_no, (int __user *)arg); case SCSI_IOCTL_PROBE_HOST: return ioctl_probe(sdev->host, arg); case SCSI_IOCTL_DOORLOCK: return scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT); case SCSI_IOCTL_DOORUNLOCK: return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); case SCSI_IOCTL_TEST_UNIT_READY: return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT,
NORMAL_RETRIES, &sense_hdr); case SCSI_IOCTL_START_UNIT: return scsi_send_start_stop(sdev, 1); case SCSI_IOCTL_STOP_UNIT: return scsi_send_start_stop(sdev, 0); case SCSI_IOCTL_GET_PCI: return scsi_ioctl_get_pci(sdev, arg); case SG_SCSI_RESET: return scsi_ioctl_reset(sdev, arg);
}
#ifdef CONFIG_COMPAT if (in_compat_syscall()) { if (!sdev->host->hostt->compat_ioctl) return -EINVAL; return sdev->host->hostt->compat_ioctl(sdev, cmd, arg);
} #endif if (!sdev->host->hostt->ioctl) return -EINVAL; return sdev->host->hostt->ioctl(sdev, cmd, arg);
}
EXPORT_SYMBOL(scsi_ioctl);
/** * scsi_ioctl_block_when_processing_errors - prevent commands from being queued * @sdev: target scsi device * @cmd: which ioctl is it * @ndelay: no delay (non-blocking) * * We can process a reset even when a device isn't fully operable. * * Return: %0 on success, <0 error code.
*/ int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev, int cmd, bool ndelay)
{ if (cmd == SG_SCSI_RESET && ndelay) { if (scsi_host_in_recovery(sdev->host)) return -EAGAIN;
} else { if (!scsi_block_when_processing_errors(sdev)) return -ENODEV;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.