diff --git a/usr/sbc.c b/usr/sbc.c index cf2b609..248a547 100644 --- a/usr/sbc.c +++ b/usr/sbc.c @@ -23,6 +23,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ +#define _FILE_OFFSET_BITS 64 +#define __USE_GNU + #include #include #include @@ -30,6 +33,7 @@ #include #include #include +#include #include "list.h" #include "util.h" @@ -45,6 +49,23 @@ static unsigned int blk_shift = DEFAULT_BLK_SHIFT; +static off_t find_next_data(struct scsi_lu *dev, off_t offset) +{ +#ifdef SEEK_DATA + return lseek64(dev->fd, offset, SEEK_DATA); +#else + return offset; +#endif +} +static off_t find_next_hole(struct scsi_lu *dev, off_t offset) +{ +#ifdef SEEK_HOLE + return lseek64(dev->fd, offset, SEEK_HOLE); +#else + return dev->size; +#endif +} + static int sbc_mode_page_update(struct scsi_cmd *cmd, uint8_t *data, int *changed) { uint8_t pcode = data[0] & 0x3f; @@ -281,10 +302,18 @@ static int sbc_rw(int host_no, struct scsi_cmd *cmd) /* Verify that we are not doing i/o beyond the end-of-lun */ - if (tl && (lba + tl > lu->size)) { - key = ILLEGAL_REQUEST; - asc = ASC_LBA_OUT_OF_RANGE; - goto sense; + if (tl) { + if (lba + tl > lu->size) { + key = ILLEGAL_REQUEST; + asc = ASC_LBA_OUT_OF_RANGE; + goto sense; + } + } else { + if (lba >= lu->size) { + key = ILLEGAL_REQUEST; + asc = ASC_LBA_OUT_OF_RANGE; + goto sense; + } } cmd->offset = lba; @@ -421,7 +450,7 @@ sense: return SAM_STAT_CHECK_CONDITION; } -static int sbc_service_action(int host_no, struct scsi_cmd *cmd) +static int sbc_readcapacity16(int host_no, struct scsi_cmd *cmd) { uint32_t *data; unsigned int bshift; @@ -437,9 +466,6 @@ static int sbc_service_action(int host_no, struct scsi_cmd *cmd) goto sense; } - if (cmd->scb[1] != SAI_READ_CAPACITY_16) - goto sense; - if (scsi_get_in_length(cmd) < 12) goto overflow; @@ -468,6 +494,106 @@ sense: return SAM_STAT_CHECK_CONDITION; } +static int sbc_getlbastatus(int host_no, struct scsi_cmd *cmd) +{ + int len = 32; + uint64_t offset; + uint32_t pdl; + int type; + unsigned char *buf; + unsigned char key = ILLEGAL_REQUEST; + uint16_t asc = ASC_INVALID_OP_CODE; + + if (cmd->dev->attrs.removable && !cmd->dev->attrs.online) { + key = NOT_READY; + asc = ASC_MEDIUM_NOT_PRESENT; + goto sense; + } + + if (scsi_get_in_length(cmd) < 24) + goto overflow; + + len = scsi_get_in_length(cmd); + buf = scsi_get_in_buffer(cmd); + memset(buf, 0, len); + + offset = get_unaligned_be64(&cmd->scb[2]) << cmd->dev->blk_shift; + if (offset >= cmd->dev->size) { + key = ILLEGAL_REQUEST; + asc = ASC_LBA_OUT_OF_RANGE; + goto sense; + } + + pdl = 4; + put_unaligned_be32(pdl, &buf[0]); + + type = 0; + while (len >= 4 + pdl + 16) { + off_t next_offset; + + put_unaligned_be32(pdl + 16, &buf[0]); + + if (offset >= cmd->dev->size) + break; + + next_offset = (type == 0) ? + find_next_hole(cmd->dev, offset) : + find_next_data(cmd->dev, offset); + if (next_offset == offset) { + type = 1 - type; + continue; + } + + put_unaligned_be64(offset >> cmd->dev->blk_shift, + &buf[4 + pdl + 0]); + put_unaligned_be32((next_offset - offset) + >> cmd->dev->blk_shift, + &buf[4 + pdl + 8]); + buf[4 + pdl + 12] = type; + + pdl += 16; + type = 1 - type; + offset = next_offset; + } + len = 4 + pdl; + +overflow: + scsi_set_in_resid_by_actual(cmd, len); + return SAM_STAT_GOOD; + +sense: + sense_data_build(cmd, key, asc); + return SAM_STAT_CHECK_CONDITION; +} + +struct service_action sbc_service_actions[] = { + {SAI_READ_CAPACITY_16, sbc_readcapacity16}, + {SAI_GET_LBA_STATUS, sbc_getlbastatus}, + {0, NULL} +}; + + +static int sbc_service_action(int host_no, struct scsi_cmd *cmd) +{ + uint8_t action; + unsigned char op = cmd->scb[0]; + struct service_action *service_action, *actions; + + action = cmd->scb[1] & 0x1f; + actions = cmd->dev->dev_type_template.ops[op].service_actions; + + service_action = find_service_action(actions, action); + + if (!service_action) { + scsi_set_in_resid_by_actual(cmd, 0); + sense_data_build(cmd, ILLEGAL_REQUEST, + ASC_INVALID_FIELD_IN_CDB); + return SAM_STAT_CHECK_CONDITION; + } + + return service_action->cmd_perform(host_no, cmd); +} + static int sbc_sync_cache(int host_no, struct scsi_cmd *cmd) { int ret; @@ -711,7 +837,7 @@ static struct device_type_template sbc_template = { {spc_illegal_op,}, {spc_illegal_op,}, {spc_illegal_op,}, - {sbc_service_action,}, + {sbc_service_action, sbc_service_actions,}, {spc_illegal_op,}, /* 0xA0 */ diff --git a/usr/scsi.h b/usr/scsi.h index 0a02c36..2b994f9 100644 --- a/usr/scsi.h +++ b/usr/scsi.h @@ -80,6 +80,7 @@ #define WRITE_SAME_16 0x93 #define SERVICE_ACTION_IN 0x9e #define SAI_READ_CAPACITY_16 0x10 +#define SAI_GET_LBA_STATUS 0x12 #define REPORT_LUNS 0xa0 #define MOVE_MEDIUM 0xa5 #define EXCHANGE_MEDIUM 0xa6 diff --git a/usr/spc.c b/usr/spc.c index a7f9a36..117c9f3 100644 --- a/usr/spc.c +++ b/usr/spc.c @@ -794,7 +794,7 @@ struct service_action maint_in_service_actions[] = { {0, NULL} }; -static struct service_action * +struct service_action * find_service_action(struct service_action *service_action, uint32_t action) { while (service_action->cmd_perform) { diff --git a/usr/tgtd.c b/usr/tgtd.c index 4ec6f23..cba2b66 100644 --- a/usr/tgtd.c +++ b/usr/tgtd.c @@ -487,6 +487,7 @@ int main(int argc, char **argv) { struct sigaction sa_old; struct sigaction sa_new; + char *spare_args; int err, ch, longindex, nr_lld = 0; int is_daemon = 1, is_debug = 0; int ret; @@ -549,7 +550,9 @@ int main(int argc, char **argv) exit(1); } - nr_lld = lld_init(argv[optind]); + spare_args = optind < argc ? argv[optind] : NULL; + + nr_lld = lld_init(spare_args); if (!nr_lld) { fprintf(stderr, "No available low level driver!\n"); exit(1); diff --git a/usr/tgtd.h b/usr/tgtd.h index b303e21..aa9b9d5 100644 --- a/usr/tgtd.h +++ b/usr/tgtd.h @@ -353,4 +353,8 @@ int call_program(const char *cmd, void update_lbppbe(struct scsi_lu *lu, int blksize); +struct service_action * +find_service_action(struct service_action *service_action, + uint32_t action); + #endif