extractedLnx/linux-2.6.12/drivers/scsi/st.c_st_ioctl.c
/* The ioctl command */
static int st_ioctl(struct inode *inode, struct file *file,
unsigned int cmd_in, unsigned long arg)
{
int i, cmd_nr, cmd_type, bt;
int retval = 0;
unsigned int blk;
struct scsi_tape *STp = file->private_data;
struct st_modedef *STm;
struct st_partstat *STps;
char *name = tape_name(STp);
void __user *p = (void __user *)arg;
if (down_interruptible(&STp->lock))
return -ERESTARTSYS;
DEB(
if (debugging && !STp->in_use) {
printk(ST_DEB_MSG "%s: Incorrect device.\n", name);
retval = (-EIO);
goto out;
} ) /* end DEB */
STm = &(STp->modes[STp->current_mode]);
STps = &(STp->ps[STp->partition]);
/*
* If we are in the middle of error recovery, don't let anyone
* else try and use this device. Also, if error recovery fails, it
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
retval = scsi_nonblockable_ioctl(STp->device, cmd_in, p, file);
if (!scsi_block_when_processing_errors(STp->device) || retval != -ENODEV)
goto out;
retval = 0;
cmd_type = _IOC_TYPE(cmd_in);
cmd_nr = _IOC_NR(cmd_in);
if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
struct mtop mtc;
if (_IOC_SIZE(cmd_in) != sizeof(mtc)) {
retval = (-EINVAL);
goto out;
}
i = copy_from_user(&mtc, p, sizeof(struct mtop));
if (i) {
retval = (-EFAULT);
goto out;
}
if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
printk(KERN_WARNING
"%s: MTSETDRVBUFFER only allowed for root.\n", name);
retval = (-EPERM);
goto out;
}
if (!STm->defined &&
(mtc.mt_op != MTSETDRVBUFFER &&
(mtc.mt_count & MT_ST_OPTIONS) == 0)) {
retval = (-ENXIO);
goto out;
}
if (!STp->pos_unknown) {
if (STps->eof == ST_FM_HIT) {
if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
mtc.mt_op == MTEOM) {
mtc.mt_count -= 1;
if (STps->drv_file >= 0)
STps->drv_file += 1;
} else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
mtc.mt_count += 1;
if (STps->drv_file >= 0)
STps->drv_file += 1;
}
}
if (mtc.mt_op == MTSEEK) {
/* Old position must be restored if partition will be
changed */
i = !STp->can_partitions ||
(STp->new_partition != STp->partition);
} else {
i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM ||
mtc.mt_op == MTCOMPRESSION;
}
i = flush_buffer(STp, i);
if (i < 0) {
retval = i;
goto out;
}
if (STps->rw == ST_WRITING &&
(mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
mtc.mt_op == MTSEEK ||
mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM)) {
i = st_int_ioctl(STp, MTWEOF, 1);
if (i < 0) {
retval = i;
goto out;
}
if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM)
mtc.mt_count++;
STps->rw = ST_IDLE;
}
} else {
/*
* If there was a bus reset, block further access
* to this device. If the user wants to rewind the tape,
* then reset the flag and allow access again.
*/
if (mtc.mt_op != MTREW &&
mtc.mt_op != MTOFFL &&
mtc.mt_op != MTRETEN &&
mtc.mt_op != MTERASE &&
mtc.mt_op != MTSEEK &&
mtc.mt_op != MTEOM) {
retval = (-EIO);
goto out;
}
reset_state(STp);
/* remove this when the midlevel properly clears was_reset */
STp->device->was_reset = 0;
}
if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */
if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
do_door_lock(STp, 0); /* Ignore result! */
if (mtc.mt_op == MTSETDRVBUFFER &&
(mtc.mt_count & MT_ST_OPTIONS) != 0) {
retval = st_set_options(STp, mtc.mt_count);
goto out;
}
if (mtc.mt_op == MTSETPART) {
if (!STp->can_partitions ||
mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS) {
retval = (-EINVAL);
goto out;
}
if (mtc.mt_count >= STp->nbr_partitions &&
(STp->nbr_partitions = nbr_partitions(STp)) < 0) {
retval = (-EIO);
goto out;
}
if (mtc.mt_count >= STp->nbr_partitions) {
retval = (-EINVAL);
goto out;
}
STp->new_partition = mtc.mt_count;
retval = 0;
goto out;
}
if (mtc.mt_op == MTMKPART) {
if (!STp->can_partitions) {
retval = (-EINVAL);
goto out;
}
if ((i = st_int_ioctl(STp, MTREW, 0)) < 0 ||
(i = partition_tape(STp, mtc.mt_count)) < 0) {
retval = i;
goto out;
}
for (i = 0; i < ST_NBR_PARTITIONS; i++) {
STp->ps[i].rw = ST_IDLE;
STp->ps[i].at_sm = 0;
STp->ps[i].last_block_valid = 0;
}
STp->partition = STp->new_partition = 0;
STp->nbr_partitions = 1; /* Bad guess ?-) */
STps->drv_block = STps->drv_file = 0;
retval = 0;
goto out;
}
if (mtc.mt_op == MTSEEK) {
i = set_location(STp, mtc.mt_count, STp->new_partition, 0);
if (!STp->can_partitions)
STp->ps[0].rw = ST_IDLE;
retval = i;
goto out;
}
if (mtc.mt_op == MTUNLOAD || mtc.mt_op == MTOFFL) {
retval = do_load_unload(STp, file, 0);
goto out;
}
if (mtc.mt_op == MTLOAD) {
retval = do_load_unload(STp, file, max(1, mtc.mt_count));
goto out;
}
if (mtc.mt_op == MTLOCK || mtc.mt_op == MTUNLOCK) {
retval = do_door_lock(STp, (mtc.mt_op == MTLOCK));
goto out;
}
if (STp->can_partitions && STp->ready == ST_READY &&
(i = switch_partition(STp)) < 0) {
retval = i;
goto out;
}
if (mtc.mt_op == MTCOMPRESSION)
retval = st_compression(STp, (mtc.mt_count & 1));
else
retval = st_int_ioctl(STp, mtc.mt_op, mtc.mt_count);
goto out;
}
if (!STm->defined) {
retval = (-ENXIO);
goto out;
}
if ((i = flush_buffer(STp, 0)) < 0) {
retval = i;
goto out;
}
if (STp->can_partitions &&
(i = switch_partition(STp)) < 0) {
retval = i;
goto out;
}
if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
struct mtget mt_status;
if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) {
retval = (-EINVAL);
goto out;
}
mt_status.mt_type = STp->tape_type;
mt_status.mt_dsreg =
((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
mt_status.mt_blkno = STps->drv_block;
mt_status.mt_fileno = STps->drv_file;
if (STp->block_size != 0) {
if (STps->rw == ST_WRITING)
mt_status.mt_blkno +=
(STp->buffer)->buffer_bytes / STp->block_size;
else if (STps->rw == ST_READING)
mt_status.mt_blkno -=
((STp->buffer)->buffer_bytes +
STp->block_size - 1) / STp->block_size;
}
mt_status.mt_gstat = 0;
if (STp->drv_write_prot)
mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
if (mt_status.mt_blkno == 0) {
if (mt_status.mt_fileno == 0)
mt_status.mt_gstat |= GMT_BOT(0xffffffff);
else
mt_status.mt_gstat |= GMT_EOF(0xffffffff);
}
mt_status.mt_erreg = (STp->recover_reg << MT_ST_SOFTERR_SHIFT);
mt_status.mt_resid = STp->partition;
if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
mt_status.mt_gstat |= GMT_EOT(0xffffffff);
else if (STps->eof >= ST_EOM_OK)
mt_status.mt_gstat |= GMT_EOD(0xffffffff);
if (STp->density == 1)
mt_status.mt_gstat |= GMT_D_800(0xffffffff);
else if (STp->density == 2)
mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
else if (STp->density == 3)
mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
if (STp->ready == ST_READY)
mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
if (STp->ready == ST_NO_TAPE)
mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
if (STps->at_sm)
mt_status.mt_gstat |= GMT_SM(0xffffffff);
if (STm->do_async_writes ||
(STm->do_buffer_writes && STp->block_size != 0) ||
STp->drv_buffer != 0)
mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
if (STp->cleaning_req)
mt_status.mt_gstat |= GMT_CLN(0xffffffff);
i = copy_to_user(p, &mt_status, sizeof(struct mtget));
if (i) {
retval = (-EFAULT);
goto out;
}
STp->recover_reg = 0; /* Clear after read */
retval = 0;
goto out;
} /* End of MTIOCGET */
if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
struct mtpos mt_pos;
if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos)) {
retval = (-EINVAL);
goto out;
}
if ((i = get_location(STp, &blk, &bt, 0)) < 0) {
retval = i;
goto out;
}
mt_pos.mt_blkno = blk;
i = copy_to_user(p, &mt_pos, sizeof(struct mtpos));
if (i)
retval = (-EFAULT);
goto out;
}
up(&STp->lock);
switch (cmd_in) {
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
break;
default:
if (!capable(CAP_SYS_ADMIN))
i = -EPERM;
else
i = scsi_cmd_ioctl(file, STp->disk, cmd_in, p);
if (i != -ENOTTY)
return i;
break;
}
if (!capable(CAP_SYS_ADMIN) &&
(cmd_in == SCSI_IOCTL_START_UNIT || cmd_in == SCSI_IOCTL_STOP_UNIT))
return -EPERM;
return scsi_ioctl(STp->device, cmd_in, p);
out:
up(&STp->lock);
return retval;
}
Generated by GNU enscript 1.6.4.