/*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. By downloading, copying, installing or
* using the software you agree to this license. If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Intel License Agreement
*
* Copyright (c) 2000, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*
* -Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
*
* -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
*
* -The name of Intel Corporation may not be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <unistd.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <unistd.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#include "scsi_cmd_codes.h"
#include "iscsi.h"
#include "iscsiutil.h"
#include "device.h"
#include "osd.h"
/*
* Globals
*/
static int osd_luns = CONFIG_OSD_LUNS_DFLT;
static uint64_t osd_capacity = CONFIG_OSD_CAPACITY_DFLT * 1048576;
static char base_dir[64] = CONFIG_OSD_BASEDIR_DFLT;
#ifndef __KERNEL__
void
device_set_var(const char *var, char *arg)
{
if (strcmp(var, "capacity") == 0) {
osd_capacity = strtoll(arg, (char **) NULL, 10) * 1048576;
} else if (strcmp(var, "luns") == 0) {
osd_luns = atoi(arg);
} else if (strcmp(var, "directory") == 0) {
(void) strlcpy(base_dir, arg, sizeof(base_dir));
} else {
(void) fprintf(stderr, "Unrecognised variable: `%s'\n", var);
}
}
#endif
int
device_init(globals_t *gp, char *dev)
{
struct stat st;
char FileName[1024];
int i;
if (stat(base_dir, &st) < 0) {
/* Create directory for OSD */
if (mkdir(base_dir, 0755) != 0) {
if (errno != EEXIST) {
iscsi_err(__FILE__, __LINE__, "error creating directory \"%s\" for OSD: errno %d\n", base_dir, errno);
return -1;
}
}
/* Create directory for LU */
for (i = 0; i < osd_luns; i++) {
sprintf(FileName, "%s/lun_%d", base_dir, i);
if (mkdir(FileName, 0755) != 0) {
if (errno != EEXIST) {
iscsi_err(__FILE__, __LINE__, "error creating \"%s\" for LU %d: errno %d\n", FileName, i, errno);
return -1;
}
}
}
}
/* Display LU info */
return 0;
}
int
osd_read_callback(void *arg)
{
struct iovec *sg = (struct iovec *) arg;
int i = 0;
while (sg[i].iov_base != NULL) {
iscsi_free_atomic(sg[i].iov_base);
i++;
}
return 0;
}
int
device_command(target_session_t * sess, target_cmd_t * cmd)
{
iscsi_scsi_cmd_args_t *args = cmd->scsi_cmd;
uint8_t *data;
char FileName[1024];
uint8_t *write_data = NULL;
uint8_t *read_data = NULL;
uint8_t *set_list = NULL;
uint8_t *get_list = NULL;
struct iovec sg[3];
int sg_len = 0;
int rc;
osd_args_t osd_args;
uint32_t GroupID = 0;
uint64_t UserID = 0;
char string[1024];
uint8_t *get_data = NULL;
uint32_t page = 0;
uint32_t index = 0;
int attr_len = 0;
iscsi_trace(TRACE_SCSI_CMD, "SCSI op 0x%x (lun %llu)\n", args->cdb[0], args->lun);
if (args->lun >= osd_luns) {
iscsi_trace(TRACE_SCSI_DEBUG, "invalid lun: %llu\n", args->lun);
args->status = 0x01;
return 0;
}
args->status = 1;
switch (args->cdb[0]) {
case TEST_UNIT_READY:
iscsi_trace(TRACE_SCSI_CMD, "TEST_UNIT_READY(lun %llu)\n", args->lun);
args->status = 0;
args->length = 0;
break;
case INQUIRY:
iscsi_trace(TRACE_SCSI_CMD, "INQUIRY(lun %llu)\n", args->lun);
data = args->send_data;
memset(data, 0, args->cdb[4]); /* Clear allocated buffer */
data[0] = 0x0e; /* Peripheral Device Type */
/* data[1] |= 0x80; // Removable Bit */
data[2] |= 0x02;/* ANSI-approved version */
/* data[3] |= 0x80; // AENC */
/* data[3] |= 0x40; // TrmIOP */
/* data[3] |= 0x20; // NormACA */
data[4] = args->cdb[4] - 4; /* Additional length */
/*
* data[7] |= 0x80; // Relative
* addressing
*/
data[7] |= 0x40;/* WBus32 */
data[7] |= 0x20;/* WBus16 */
/* data[7] |= 0x10; // Sync */
/* data[7] |= 0x08; // Linked Commands */
/* data[7] |= 0x04; // TransDis */
/*
* data[7] |= 0x02; // Tagged Command
* Queueing
*/
/* data[7] |= 0x01; // SftRe */
(void) memset(data + 8, 0x0, 32);
strlcpy(data + 8, OSD_VENDOR, 8); /* Vendor */
strlcpy(data + 16, OSD_PRODUCT, 16); /* Product ID */
(void) snprintf(data + 32, 8, "%d", OSD_VERSION); /* Product Revision */
args->input = 1;
args->length = args->cdb[4] + 1;
args->status = 0;
break;
case 0x7F:
OSD_DECAP_CDB(args->cdb, args->ext_cdb, &osd_args);
/* OSD_PRINT_CDB(args->cdb, args->ext_cdb); */
GroupID = osd_args.GroupID;
UserID = osd_args.UserID;
/*
* Transfer all data
*/
if (osd_args.set_attributes_list_length) {
if ((set_list = iscsi_malloc_atomic(osd_args.set_attributes_list_length)) == NULL) {
iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
goto done;
}
sg[sg_len].iov_base = set_list;
sg[sg_len].iov_len = osd_args.set_attributes_list_length;
sg_len++;
}
if (osd_args.get_attributes_list_length) {
if ((get_list = iscsi_malloc_atomic(osd_args.get_attributes_list_length)) == NULL) {
iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
goto done;
}
sg[sg_len].iov_base = get_list;
sg[sg_len].iov_len = osd_args.get_attributes_list_length;
sg_len++;
}
if (osd_args.service_action == OSD_WRITE) {
if ((write_data = iscsi_malloc_atomic(osd_args.length)) == NULL) {
iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
goto done;
}
sg[sg_len].iov_base = write_data;
sg[sg_len].iov_len = osd_args.length;
sg_len++;
}
if (sg_len) {
if (target_transfer_data(sess, args, sg, sg_len) != 0) {
iscsi_err(__FILE__, __LINE__, "target_transfer_data() failed\n");
goto done;
}
}
/*
* Set any attributes
*/
if (osd_args.set_attributes_list_length) {
uint32_t page, attr;
uint16_t len;
int i;
iscsi_trace(TRACE_OSD, "OSD_SET_ATTR(lun %llu, GroupID 0x%x, UserID 0x%llx)\n", args->lun, osd_args.GroupID, osd_args.UserID);
for (i = 0; i < osd_args.set_attributes_list_length;) {
page = ISCSI_NTOHL(*((uint32_t *) (&(set_list[i]))));
i += 4;
attr = ISCSI_NTOHL(*((uint32_t *) (&(set_list[i]))));
i += 4;
len = ISCSI_NTOHS(*((uint16_t *) (&(set_list[i]))));
i += 2;
sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx.0x%x.%u",
base_dir, args->lun, osd_args.GroupID, osd_args.UserID, page, attr);
if ((rc = open(FileName, O_WRONLY | O_CREAT, 0644)) == -1) {
iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
goto done;
}
if (write(rc, set_list + i, len) != len) {
iscsi_err(__FILE__, __LINE__, "write() failed\n");
}
close(rc);
i += len;
iscsi_trace(TRACE_OSD, "SET(0x%x,%u,%u>\n", page, attr, len);
}
}
args->send_sg_len = 0;
sg_len = 0;
switch (osd_args.service_action) {
case OSD_CREATE_GROUP:
do {
GroupID = rand() % 1048576 * 1024 + 1;
sprintf(FileName, "%s/lun_%llu/0x%x", base_dir, args->lun, GroupID);
rc = mkdir(FileName, 0755);
} while (rc == -1 && errno == EEXIST);
iscsi_trace(TRACE_OSD, "OSD_CREATE_GROUP(lun %llu) --> 0x%x\n", args->lun, GroupID);
args->status = 0;
break;
case OSD_REMOVE_GROUP:
iscsi_trace(TRACE_OSD, "OSD_REMOVE_GROUP(lun %llu, 0x%x)\n", args->lun, osd_args.GroupID);
sprintf(FileName, "%s/lun_%llu/0x%x", base_dir, args->lun, osd_args.GroupID);
if ((rc = rmdir(FileName)) == -1) {
iscsi_err(__FILE__, __LINE__, "rmdir(\"%s\") failed: errno %d\n", FileName, errno);
goto done;
}
args->status = 0;
break;
case OSD_CREATE:
UserID = rand() % 1048576 * 1024 + 1;
create_user_again:
sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx",
base_dir, args->lun, osd_args.GroupID, UserID);
rc = open(FileName, O_CREAT | O_EXCL | O_RDWR, 0644);
if ((rc == -1) && (errno == EEXIST)) {
UserID = rand() % 1048576 * 1024 + 1;
goto create_user_again;
}
close(rc);
iscsi_trace(TRACE_OSD, "OSD_CREATE(lun %llu, GroupID 0x%x) --> 0x%llx\n", args->lun, osd_args.GroupID, UserID);
args->status = 0;
break;
case OSD_REMOVE:
iscsi_trace(TRACE_OSD, "OSD_REMOVE(lun %llu, 0x%llx)\n", args->lun, osd_args.UserID);
sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx",
base_dir, args->lun, osd_args.GroupID, osd_args.UserID);
if ((rc = unlink(FileName)) == -1) {
iscsi_err(__FILE__, __LINE__, "unlink(\"%s\") failed: errno %d\n", FileName, errno);
goto done;
}
sprintf(string, "rm -f %s/lun_%llu/0x%x/0x%llx.*", base_dir, args->lun, osd_args.GroupID, osd_args.UserID);
if (system(string) != 0) {
iscsi_err(__FILE__, __LINE__, "\"%s\" failed\n", string);
return -1;
}
args->status = 0;
break;
case OSD_WRITE:
iscsi_trace(TRACE_OSD, "OSD_WRITE(lun %llu, GroupID 0x%x, UserID 0x%llx, length %llu, offset %llu)\n",
args->lun, osd_args.GroupID, osd_args.UserID, osd_args.length, osd_args.offset);
sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx",
base_dir, args->lun, osd_args.GroupID, osd_args.UserID);
if ((rc = open(FileName, O_WRONLY, 0644)) == -1) {
iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
goto write_done;
}
if (lseek(rc, osd_args.offset, SEEK_SET) == -1) {
iscsi_err(__FILE__, __LINE__, "error seeking \"%s\": errno %d\n", FileName, errno);
goto write_done;
}
if (write(rc, write_data, osd_args.length) != osd_args.length) {
iscsi_err(__FILE__, __LINE__, "write() failed\n");
goto write_done;
}
close(rc);
args->status = 0;
write_done:
break;
case OSD_READ:
iscsi_trace(TRACE_OSD, "OSD_READ(lun %llu, GroupID 0x%x, UserID 0x%llx, length %llu, offset %llu)\n",
args->lun, osd_args.GroupID, osd_args.UserID, osd_args.length, osd_args.offset);
sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx",
base_dir, args->lun, osd_args.GroupID, osd_args.UserID);
if ((rc = open(FileName, O_RDONLY, 0644)) == -1) {
iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
goto read_done;
}
if ((read_data = iscsi_malloc_atomic(osd_args.length)) == NULL) {
iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
goto read_done;
}
if (lseek(rc, osd_args.offset, SEEK_SET) == -1) {
iscsi_err(__FILE__, __LINE__, "error seeking \"%s\": errno %d\n", FileName, errno);
goto read_done;
}
if (read(rc, read_data, osd_args.length) != osd_args.length) {
iscsi_err(__FILE__, __LINE__, "read() failed\n");
goto read_done;
}
close(rc);
args->status = 0;
read_done:
if (args->status == 0) {
args->input = 1;
sg[0].iov_base = read_data;
sg[0].iov_len = osd_args.length;
sg[1].iov_base = NULL;
sg[1].iov_len = 0;
args->send_data = (void *) sg;
args->send_sg_len = 1;
sg_len++;
cmd->callback = osd_read_callback;
cmd->callback_arg = sg;
} else {
if (read_data)
iscsi_free_atomic(read_data);
args->length = 0; /* Need a better way of
* specifying an error.. */
}
break;
case OSD_GET_ATTR:
iscsi_trace(TRACE_OSD, "OSD_GET_ATTR(lun %llu, GroupID 0x%x, UserID 0x%llx)\n",
args->lun, osd_args.GroupID, osd_args.UserID);
args->status = 0;
break;
case OSD_SET_ATTR:
args->status = 0;
break;
}
if (args->status)
goto done;
/*
* Send back requested attributes
*/
if (osd_args.get_attributes_list_length || osd_args.get_attributes_page) {
if ((get_data = iscsi_malloc_atomic(osd_args.get_attributes_allocation_length)) == NULL) {
iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
goto done;
}
}
if (osd_args.get_attributes_list_length) {
int i;
for (i = 0; i < osd_args.get_attributes_list_length;) {
page = ISCSI_NTOHL(*((uint32_t *) (&(get_list[i]))));
i += 4;
index = ISCSI_NTOHL(*((uint32_t *) (&(get_list[i]))));
i += 4;
iscsi_trace(TRACE_OSD, "GET(0x%x,%u)\n", page, index);
switch (page) {
case 0x40000001:
switch (index) {
case 0x1:
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
attr_len += 4;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
attr_len += 4;
*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(4);
attr_len += 2;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(GroupID);
attr_len += 4;
break;
default:
iscsi_err(__FILE__, __LINE__, "unknown attr index %u\n", index);
goto done;
}
break;
case 0x00000001:
switch (index) {
case 0x1:
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
attr_len += 4;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
attr_len += 4;
*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(4);
attr_len += 2;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(GroupID);
attr_len += 4;
break;
case 0x2:
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
attr_len += 4;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
attr_len += 4;
*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(8);
attr_len += 2;
*((uint64_t *) & get_data[attr_len]) = ISCSI_HTONLL(UserID);
attr_len += 8;
break;
default:
iscsi_err(__FILE__, __LINE__, "unknown attr index %u\n", index);
goto done;
}
break;
/* Vendor-specific */
case 0x30000000:
switch (index) {
case 0x1:
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
attr_len += 4;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
attr_len += 4;
*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(480);
attr_len += 2;
sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx.0x%x.%u",
base_dir, args->lun, osd_args.GroupID, osd_args.UserID, page, index);
if ((rc = open(FileName, O_RDONLY, 0644)) == -1) {
iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
}
if (read(rc, get_data + attr_len, 480) != 480) {
iscsi_err(__FILE__, __LINE__, "read() failed\n");
goto done;
}
close(rc);
attr_len += 480;
break;
default:
iscsi_err(__FILE__, __LINE__, "unknown vendor attr index %u\n", index);
goto done;
}
break;
default:
iscsi_err(__FILE__, __LINE__, "unknown page 0x%x\n", page);
goto done;
}
}
}
if (osd_args.get_attributes_page) {
/*
* Right now, if we get a request for an entire page,
* we return only one attribute.
*/
page = osd_args.get_attributes_page;
switch (osd_args.get_attributes_page) {
case 0x40000001:
index = 1;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
attr_len += 4;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
attr_len += 4;
*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(4);
attr_len += 2;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(GroupID);
attr_len += 4;
break;
case 0x00000001:
index = 2;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
attr_len += 4;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
attr_len += 4;
*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(8);
attr_len += 2;
*((uint64_t *) & get_data[attr_len]) = ISCSI_HTONLL(UserID);
attr_len += 8;
break;
case 0x30000000:
index = 1;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
attr_len += 4;
*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
attr_len += 4;
*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(480);
attr_len += 2;
sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx.0x%x.%u",
base_dir, args->lun, osd_args.GroupID, osd_args.UserID, page, index);
if ((rc = open(FileName, O_RDONLY, 0644)) == -1) {
iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
}
if (read(rc, get_data + attr_len, 480) != 480) {
iscsi_err(__FILE__, __LINE__, "read() failed\n");
goto done;
}
close(rc);
attr_len += 480;
break;
default:
iscsi_err(__FILE__, __LINE__, "page not yet supported\n");
goto done;
}
}
if (attr_len) {
if (attr_len != osd_args.get_attributes_allocation_length) {
iscsi_err(__FILE__, __LINE__, "allocation lengths differ: got %u, expected %u\n",
osd_args.get_attributes_allocation_length, attr_len);
goto done;
}
if (!args->status) {
args->input = 1;
sg[sg_len].iov_base = get_data;
sg[sg_len].iov_len = osd_args.get_attributes_allocation_length;
sg_len++;
sg[sg_len].iov_base = NULL;
sg[sg_len].iov_len = 0;
args->send_data = (void *) sg;
args->send_sg_len++;
cmd->callback = osd_read_callback;
cmd->callback_arg = sg;
} else {
if (get_data)
iscsi_free_atomic(get_data);
}
}
break;
default:
iscsi_err(__FILE__, __LINE__, "UNKNOWN OPCODE 0x%x\n", args->cdb[0]);
args->status = 0x01;
break;
}
done:
iscsi_trace(TRACE_SCSI_DEBUG, "SCSI op 0x%x: done (status 0x%x)\n", args->cdb[0], args->status);
if (set_list) {
iscsi_free_atomic(set_list);
}
if (get_list) {
iscsi_free_atomic(get_list);
}
if (write_data) {
iscsi_free_atomic(write_data);
}
return 0;
}
/* ARGSUSED */
int
device_shutdown(target_session_t *sess)
{
return 0;
}