From 40a64a37fe9e61189bf69c815e3d8535604e28ed Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Fri, 12 Sep 2025 15:13:48 +0200 Subject: Fixed iSCSI SCSI emulation and implemented adding target port to iSCSI device, QEMU now tries to boot from PXE: Important milestone. Finally, some code refactoring was done again. --- src/server/iscsi.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++------ src/server/iscsi.h | 154 ++++++++++++++++++++++++++++++++--- 2 files changed, 355 insertions(+), 34 deletions(-) diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 9c44f04..0ab6f1e 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -18,6 +18,7 @@ * */ +#include "image.h" #include #include #include @@ -2636,7 +2637,6 @@ iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_ task->sub_tasks = NULL; task->conn = conn; task->pdu = NULL; - task->buf = NULL; task->pos = 0U; task->len = 0U; task->id = 0ULL; @@ -3289,7 +3289,7 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task response_pdu->data_digest_size = conn->data_digest; } - memcpy( response_pdu->ds_cmd_data, task->buf, len ); + memcpy( response_pdu->ds_cmd_data, task->scsi_task.buf, len ); response_pdu->task = task; task->scsi_task.ref++; @@ -4025,7 +4025,7 @@ iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id) iscsi_scsi_lun *lun = (iscsi_scsi_lun *) malloc( sizeof(struct iscsi_scsi_lun) ); if ( lun == NULL ) { - logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN" ); + logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN" ); return NULL; } @@ -4033,7 +4033,7 @@ iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id) lun->tasks = iscsi_hashmap_create( 0U ); if ( lun->tasks == NULL ) { - logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN tasks hash map" ); + logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN tasks hash map" ); free( lun ); @@ -4043,7 +4043,7 @@ iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id) lun->tasks_pending = iscsi_hashmap_create( 0U ); if ( lun->tasks_pending == NULL ) { - logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN pending tasks hash map" ); + logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN pending tasks hash map" ); iscsi_hashmap_destroy( lun->tasks ); free( lun ); @@ -4054,7 +4054,7 @@ iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id) lun->tasks_mgmt = iscsi_hashmap_create( 0U ); if ( lun->tasks_mgmt == NULL ) { - logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN management tasks hash map" ); + logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN management tasks hash map" ); iscsi_hashmap_destroy( lun->tasks_pending ); iscsi_hashmap_destroy( lun->tasks ); @@ -4066,7 +4066,7 @@ iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id) lun->tasks_mgmt_pending = iscsi_hashmap_create( 0U ); if ( lun->tasks_mgmt_pending == NULL ) { - logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN pending management tasks hash map" ); + logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN pending management tasks hash map" ); iscsi_hashmap_destroy( lun->tasks_mgmt ); iscsi_hashmap_destroy( lun->tasks_pending ); @@ -4079,7 +4079,7 @@ iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id) lun->pr_regs = iscsi_hashmap_create( 0U ); if ( lun->pr_regs == NULL ) { - logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN Persistent Reservation (PR) registrant for I_T nexus hash map" ); + logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN Persistent Reservation (PR) registrant for I_T nexus hash map" ); iscsi_hashmap_destroy( lun->tasks_mgmt_pending ); iscsi_hashmap_destroy( lun->tasks_mgmt ); @@ -4383,7 +4383,7 @@ void iscsi_scsi_lun_task_run(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task) void iscsi_scsi_lun_task_complete(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task) { if ( lun != NULL ) - iscsi_hashmap_remove( lun->tasks, (uint8_t *) &scsi_task->id, sizeof(uint64_t) ); + iscsi_hashmap_remove( lun->tasks, (uint8_t *) &scsi_task->id, sizeof(scsi_task->id) ); scsi_task->xfer_complete_callback( scsi_task ); } @@ -5111,6 +5111,50 @@ static int iscsi_scsi_emu_block_write_same(dnbd3_image_t *image, iscsi_scsi_task return 0; } +/** + * @brief Initializes a DNBD3 image for an iSCSI SCSI LUN retrieved from its iSCSI SCSI task and optionally check for read access. + * + * This function also sets the SCSI + * status result code accordingly. + * + * @param[in] scsi_task Pointer to iSCSI SCSI task + * to retrieve the iSCSI SCSI LUN + * from in order to initialize the + * DNBD3 image and also set the SCSI + * error code. May NOT be NULL, so + * be careful. + * @param[in] access Check if read access for DNBD3 + * image is working. + * @retval true The DNBD3 image has been initialized + * successfully and is readable. + * @retval false The DNBD3 image has NOT been + * successfully and is read is not possible. + */ +static bool iscsi_scsi_emu_image_init(iscsi_scsi_task *scsi_task, const bool access) +{ + // TODO: Handle server and proxy stuff. + + iscsi_scsi_lun *lun = scsi_task->lun; + + if ( lun->image == NULL ) { + lun->image = image_getOrLoad( lun->device->name, (uint16_t) lun->id ); + + if ( lun->image == NULL ) { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED ); + + return false; + } + } + + if ( access && (!image_ensureOpen( lun->image ) || lun->image->problem.read || (lun->image->virtualFilesize == 0ULL)) ) { + iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED ); + + return false; + } + + return true; +} + /** * @brief Executes SCSI block emulation on a DNBD3 image. * @@ -5134,6 +5178,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_WRITE6 : { const iscsi_scsi_cdb_read_write_6 *cdb_read_write_6 = (iscsi_scsi_cdb_read_write_6 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + lba = iscsi_get_be24(cdb_read_write_6->lba); xfer_len = cdb_read_write_6->xfer_len; @@ -5148,6 +5195,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_WRITE10 : { const iscsi_scsi_cdb_read_write_10 *cdb_read_write_10 = (iscsi_scsi_cdb_read_write_10 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + lba = iscsi_get_be32(cdb_read_write_10->lba); xfer_len = iscsi_get_be16(cdb_read_write_10->xfer_len); @@ -5159,6 +5209,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_WRITE12 : { const iscsi_scsi_cdb_read_write_12 *cdb_read_write_12 = (iscsi_scsi_cdb_read_write_12 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + lba = iscsi_get_be32(cdb_read_write_12->lba); xfer_len = iscsi_get_be32(cdb_read_write_12->xfer_len); @@ -5170,6 +5223,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_WRITE16 : { const iscsi_scsi_cdb_read_write_16 *cdb_read_write_16 = (iscsi_scsi_cdb_read_write_16 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + lba = iscsi_get_be64(cdb_read_write_16->lba); xfer_len = iscsi_get_be32(cdb_read_write_16->xfer_len); @@ -5195,11 +5251,17 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) return ISCSI_SCSI_TASK_RUN_COMPLETE; } + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, (ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE | ISCSI_SCSI_EMU_BLOCK_FLAGS_VERIFY) ); break; } case ISCSI_SCSI_OPCODE_READCAPACITY10 : { + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + iscsi_scsi_read_capacity_10_parameter_data_packet *buf = (iscsi_scsi_read_capacity_10_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet) ); if ( buf == NULL ) { @@ -5235,6 +5297,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) switch ( ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_GET_ACTION(cdb_servce_in_action_16->action) ) { case ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 : { + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + iscsi_scsi_service_action_in_16_parameter_data_packet *buf = (iscsi_scsi_service_action_in_16_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) ); if ( buf == NULL ) { @@ -5286,6 +5351,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE10 : { const iscsi_scsi_cdb_sync_cache_10 *cdb_sync_cache_10 = (iscsi_scsi_cdb_sync_cache_10 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + lba = iscsi_get_be32(cdb_sync_cache_10->lba); xfer_len = iscsi_get_be16(cdb_sync_cache_10->xfer_len); @@ -5299,6 +5367,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE16 : { const iscsi_scsi_cdb_sync_cache_16 *cdb_sync_cache_16 = (iscsi_scsi_cdb_sync_cache_16 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + lba = iscsi_get_be64(cdb_sync_cache_16->lba); xfer_len = iscsi_get_be32(cdb_sync_cache_16->xfer_len); @@ -5310,6 +5381,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) break; } case ISCSI_SCSI_OPCODE_UNMAP : { + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + return iscsi_scsi_emu_block_unmap( lun->image, scsi_task ); break; @@ -5317,6 +5391,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_WRITE_SAME10 : { const iscsi_scsi_cdb_write_same_10 *cdb_write_same_10 = (iscsi_scsi_cdb_write_same_10 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + lba = iscsi_get_be32(cdb_write_same_10->lba); xfer_len = iscsi_get_be16(cdb_write_same_10->xfer_len); @@ -5327,6 +5404,9 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_WRITE_SAME16 : { const iscsi_scsi_cdb_write_same_16 *cdb_write_same_16 = (iscsi_scsi_cdb_write_same_16 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + lba = iscsi_get_be64(cdb_write_same_16->lba); xfer_len = iscsi_get_be32(cdb_write_same_16->xfer_len); @@ -5596,7 +5676,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet)); // Target Port Group alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet)); // Logical Unit Group - if ( (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) > len ) { + if ( len < (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); return -1; @@ -5691,7 +5771,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task alloc_len = (sizeof(iscsi_scsi_vpd_page_ext_inquiry_data_packet) - sizeof(iscsi_scsi_vpd_page_inquiry_data_packet)); - if ( (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) > len ) { + if ( len < (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); return -1; @@ -5758,7 +5838,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_LIMITS : { iscsi_scsi_vpd_page_block_limits_inquiry_data_packet *vpd_page_block_limits_inquiry_data_pkt = (iscsi_scsi_vpd_page_block_limits_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params; - if ( (sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_block_limits_inquiry_data_packet)) > len ) { + if ( len < (sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_block_limits_inquiry_data_packet)) ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); return -1; @@ -5805,7 +5885,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_DEV_CHARS : { iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet *vpd_page_block_dev_chars_inquiry_data_pkt = (iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params; - if ( (sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet)) > len ) { + if ( len < (sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet)) ) { iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE ); return -1; @@ -5882,7 +5962,7 @@ static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task char image_rev[sizeof(std_inquiry_data_pkt->product_rev_level) + 1]; - sprintf( "%04X", image_rev, image->rid ); + sprintf( image_rev, "%04X", image->rid ); iscsi_strcpy_pad( image_rev, (char *) std_inquiry_data_pkt->product_rev_level, sizeof(std_inquiry_data_pkt->product_rev_level), ' ' ); uint add_len = (sizeof(struct iscsi_scsi_std_inquiry_data_packet) - sizeof(struct iscsi_scsi_basic_inquiry_data_packet)); @@ -6111,11 +6191,14 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_INQUIRY : { const iscsi_scsi_cdb_inquiry *cdb_inquiry = (iscsi_scsi_cdb_inquiry *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + alloc_len = iscsi_get_be16(cdb_inquiry->alloc_len); len = alloc_len; - if ( len > 4096U ) - len = 4096U; + if ( len < ISCSI_DEFAULT_RECV_DS_LEN ) + len = ISCSI_DEFAULT_RECV_DS_LEN; iscsi_scsi_std_inquiry_data_packet *std_inquiry_data_pkt = NULL; @@ -6157,8 +6240,8 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) len = alloc_len; - if ( len > 4096U ) - len = 4096U; + if ( len < ISCSI_DEFAULT_RECV_DS_LEN ) + len = ISCSI_DEFAULT_RECV_DS_LEN; iscsi_scsi_report_luns_parameter_data_lun_list_packet *report_luns_parameter_data_pkt = NULL; @@ -6254,6 +6337,9 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_MODESENSE6 : { const iscsi_scsi_cdb_mode_sense_6 *cdb_mode_sense_6 = (iscsi_scsi_cdb_mode_sense_6 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + alloc_len = cdb_mode_sense_6->alloc_len; const int dbd = (cdb_mode_sense_6->flags & ISCSI_SCSI_CDB_MODE_SENSE_6_FLAGS_DBD); @@ -6303,6 +6389,9 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) case ISCSI_SCSI_OPCODE_MODESENSE10 : { const iscsi_scsi_cdb_mode_sense_10 *cdb_mode_sense_10 = (iscsi_scsi_cdb_mode_sense_10 *) scsi_task->cdb; + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + alloc_len = iscsi_get_be16(cdb_mode_sense_10->alloc_len); const int dbd10 = (cdb_mode_sense_10->flags & ISCSI_SCSI_CDB_MODE_SENSE_10_FLAGS_DBD); @@ -6393,8 +6482,21 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task) break; } - case ISCSI_SCSI_OPCODE_TESTUNITREADY : + case ISCSI_SCSI_OPCODE_TESTUNITREADY : { + if ( !iscsi_scsi_emu_image_init( scsi_task, false ) ) + break; + + scsi_task->pos = 0U; + scsi_task->status = ISCSI_SCSI_STATUS_GOOD; + + break; + } case ISCSI_SCSI_OPCODE_STARTSTOPUNIT : { + // TODO: Handle eject image and power saving (suspend and standby) modes. + + if ( !iscsi_scsi_emu_image_init( scsi_task, true ) ) + break; + scsi_task->pos = 0U; scsi_task->status = ISCSI_SCSI_STATUS_GOOD; @@ -6795,7 +6897,6 @@ iscsi_device *iscsi_device_create(const uint8_t *name, const uint luns, const ui device->id = 0; device->flags = 0; - device->num_ports = 0U; device->protocol_id = protocol_id; return device; @@ -6867,7 +6968,7 @@ iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *devic * @brief Finds an iSCSI LUN by LUN identifier. * * Callback function for each element while iterating - * through the iSCSI device.\n + * through the iSCSI device hash map.\n * LUNs about to be removed are not returned by this * callback. * @@ -6906,7 +7007,8 @@ int iscsi_device_find_lun_callback(uint8_t *key, const size_t key_size, uint8_t * hash map. * * @param[in] device Pointer to iSCSI device to - * search in the LUN hash map. + * search in the LUN hash map. May NOT be + * NULL, so be careful. * @param[in] lun_id LUN identifier to be searched * for. * @return Pointer to found iSCSI LUN or NULL in @@ -6922,6 +7024,45 @@ iscsi_scsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id) return lun_find.lun; } +/** + * @brief Creates, initializes and adds an iSCSI target port to an iSCSI device. + * + * This function checks whether the iSCSI + * target port already exists for the + * device. + * + * @param[in] device Pointer to iSCSI device to + * add the port for. May NOT be NULL, so + * be careful. + * @param[in] name Pointer to string containing + * the name for the iSCSI target port. + * NULL is NOT allowed here, take caution. + * @param[in] id Unique iSCSI target port + * identifier to be used. + * @return 0 on successful operation, a + * negative error code otherwise. + */ +int iscsi_device_port_add(iscsi_device *device, const uint8_t *name, const uint64_t id) +{ + if ( iscsi_hashmap_contains( device->ports, (uint8_t *) &id, sizeof(id) ) ) + return -1; + + iscsi_port *port = iscsi_port_create( name, id, (uint16_t) iscsi_hashmap_size( device->ports ) ); + + if ( port == NULL ) + return -1; + + const int rc = iscsi_hashmap_put( device->ports, (uint8_t *) &port->id, sizeof(port->id), (uint8_t *) port ); + + if ( rc < 0 ) { + iscsi_port_destroy( port ); + + return -1; + } + + return 0; +} + /** * @brief Enqueues an iSCSI SCSI task to the first LUN of an iSCSI device. * @@ -7001,6 +7142,38 @@ static int iscsi_target_node_check_flags(const int flags, const int32_t chap_gro return -1; } +/** + * @brief Creates, initializes and adds a portal group to an iSCSI target node. + * + * Callback function for each element while iterating + * through the iSCSI global vector portal group + * hash map. + * + * @param[in] key Pointer to zero padded key. NULL is + * an invalid pointer here, so be careful. + * @param[in] key_size Number of bytes for the key. + * @param[in] value Value of the key, NULL creates an + * empty key assignment. + * @param[in,out] user_data Pointer to the iSCSI target + * node to be added and may NOT be NULL, so be + * careful. + * @retval -1 An error occured during adding the + * iSCSI portal group to the iSCSI target node. + * @retval 0 The iSCSI portal group has been + * added successfully. + */ +int iscsi_target_node_create_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) +{ + iscsi_target_node *target = (iscsi_target_node *) user_data; + iscsi_portal_group *portal_group = (iscsi_portal_group *) value; + uint8_t *port_name = iscsi_sprintf_alloc( "%s,t,0x%4.4x", target->device->name, portal_group->tag ); + + if ( port_name == NULL ) + return -1; + + return iscsi_device_port_add( target->device, port_name, (uint64_t) portal_group->tag ); +} + /** * @brief Creates and initializes an iSCSI target node. * @@ -7078,6 +7251,20 @@ iscsi_target_node *iscsi_target_node_create(const uint8_t *name, const uint8_t * return NULL; } + const int rc = iscsi_hashmap_iterate( iscsi_globvec->portal_groups, iscsi_target_node_create_callback, (uint8_t *) target ); + + if ( rc < 0 ) { + iscsi_device_destroy( target->device ); + + if ( target->alias != NULL ) + free( target->alias ); + + free( target->name ); + free( target ); + + return NULL; + } + target->num = index; target->queue_depth = queue_depth; target->flags = flags; @@ -10005,8 +10192,8 @@ static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *co return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR ); // TODO: Implement dif ctx stuff. - if ( task->buf != NULL ) { - pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (task->buf + task->len); + if ( task->scsi_task.buf != NULL ) { + pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (task->scsi_task.buf + task->len); pdu->ds_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN; } else { // TODO: Implement dif ctx stuff. diff --git a/src/server/iscsi.h b/src/server/iscsi.h index 91cb447..bd6e0b7 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -1334,6 +1334,140 @@ typedef struct __attribute__((packed)) iscsi_scsi_cdb_req_sense { } iscsi_scsi_cdb_req_sense; +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags: Reply immediately after CDB check (IMMED). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_EXEC_FLAGS_IMMED (1 << 0) + + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Process the START and LOEJ bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_PROC_START_LOEJ_BITS 0x0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the active power condition (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_ACTIVE 0x0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the idle_a power condition (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_IDLE_A 0x0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the idle_b power condition (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_IDLE_B 0x1 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the idle_c power condition (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_IDLE_C 0x2 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the standby_z power condition (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_STANDBY_Z 0x0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the standby_b power condition (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_STANDBY_Y 0x1 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Initialize and start all of the idle and standby condition timers that are enabled (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LU_CONTROL 0x0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the idle_a condition timer to be set to zero (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_IDLE_A_0 0x0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the idle_b condition timer to be set to zero (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_IDLE_B_0 0x1 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the idle_c condition timer to be set to zero (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_IDLE_C_0 0x2 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the standby_z condition timer to be set to zero (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_STANDBY_Z_0 0x0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the standby_y condition timer to be set to zero (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_STANDBY_Y_0 0x1 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: First bit of the four bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT 0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Last bit of the four bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LAST_BIT ((ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT) + 4 - 1) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Bit mask. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LAST_BIT)) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Extracts the power condition modifier bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_GET_POWER_COND_MOD(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LAST_BIT)) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Stores into the power condition modifier bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_PUT_POWER_COND_MOD(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LAST_BIT)) + + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags: START. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_START (1 << 0) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags: LOad EJect (LOEJ). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_LOEJ (1 << 1) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags: Do not flush caches until a power condition that prevents accessing the medium is entered (NO_FLUSH). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_NO_FLUSH (1 << 2) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Process the START and LOEJ bits (START_VALID). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_START_VALID 0x0 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Cause the logical unit to transition to the active power condition (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_ACTIVE 0x1 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Cause the logical unit to transition to the idle_a to idle_c power conditions (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_IDLE 0x2 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Cause the logical unit to transition to the standby_z and standby_y power conditions (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_STANDBY 0x3 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Obselete. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_OBSELETE 0x5 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Initialize and start all of the idle and standby condition timers that are enabled (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LU_CONTROL 0x7 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Force the idle_a to idle_c condition timers to be set to zero (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FORCE_IDLE_0 0xA + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Force the standby_z and standby_y condition timers to be set to zero (see SPC5). +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FORCE_STANDBY_0 0xB + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: First bit of the four bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT 4 + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Last bit of the four bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LAST_BIT ((ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT) + 8 - 1) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Bit mask. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LAST_BIT)) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Extracts the power condition bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_GET_POWER_COND(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LAST_BIT)) + +/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Stores into the power condition bits. +#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_PUT_POWER_COND(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LAST_BIT)) + + +/** + * @brief iSCSI SCSI CDB packet data structure for SCSI START STOP UNIT command. + * + * There are 6 bytes in the CDB field for this command. + */ +typedef struct __attribute__((packed)) iscsi_scsi_cdb_start_stop_unit { + /// SCSI opcode. + iscsi_scsi_cdb cdb; + + /// Execution flags. + int8_t exec_flags; + + /// Reserved for future usage (always MUST be 0 for now). + uint8_t reserved; + + /// Power condition modifier. + uint8_t power_cond_mod; + + /// Flags. + int8_t flags; + + /// Control. + uint8_t control; +} iscsi_scsi_cdb_start_stop_unit; + + /// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Register - Register a reservation key without making a reservation. #define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_REGISTER 0x00 @@ -8746,11 +8880,11 @@ typedef struct iscsi_portal_group { /// Hash map containing all portals associated with this iSCSI group. iscsi_hashmap *portals; - /// Reference count. - int ref_count; - /// Tag value for this portal group. - int tag; + uint64_t tag; + + /// Reference count. + int ref_count; /// Portal group flags. int flags; @@ -8942,6 +9076,9 @@ void iscsi_portal_destroy(iscsi_portal *portal); /// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): No access rights. #define ISCSI_SCSI_ASCQ_NO_ACCESS_RIGHTS 0x02 +/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Manual intervention required. +#define ISCSI_SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED 0x03 + /// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block reference tag check failed. #define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_REF_TAG_CHECK_FAIL 0x03 @@ -9393,9 +9530,6 @@ typedef struct iscsi_device { /// Flags. int flags; - /// Number of ports. - uint num_ports; - /// Protocol identifier. uint8_t protocol_id; } iscsi_device; @@ -9962,9 +10096,6 @@ typedef struct iscsi_task { /// Associated iSCSI PDU. iscsi_pdu *pdu; - /// Buffer to send to initiator. - uint8_t *buf; - /// Buffer position in bytes. uint pos; @@ -10106,8 +10237,11 @@ iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *devic int iscsi_device_find_lun_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI LUN by LUN identifier iscsi_scsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id); // Searches an iSCSI LUN by LUN identifier +int iscsi_device_port_add(iscsi_device *device, const uint8_t *name, const uint64_t id); // Creates, initializes and adds an iSCSI target port to an iSCSI device + void iscsi_device_scsi_task_queue(iscsi_device *device, iscsi_scsi_task *scsi_task); // Enqueues an iSCSI SCSI task to the first LUN of an iSCSI device +int iscsi_target_node_create_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Creates, initializes and adds a portal group to an iSCSI target node iscsi_target_node *iscsi_target_node_create(const uint8_t *name, const uint8_t *alias, const int index, const uint luns, const uint queue_depth, const int flags, const int32_t chap_group, const int header_digest, const int data_digest); // Creates and initializes an iSCSI target node void iscsi_target_node_destroy(iscsi_target_node *target); // Deallocates all resources acquired by iscsi_target_node_create -- cgit v1.2.3-55-g7522