Initial import of libiscsi
This commit is contained in:
127
lib/connect.c
Normal file
127
lib/connect.c
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
struct connect_task {
|
||||
iscsi_command_cb cb;
|
||||
void *private_data;
|
||||
int lun;
|
||||
};
|
||||
|
||||
static void
|
||||
iscsi_testunitready_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *private_data)
|
||||
{
|
||||
struct connect_task *ct = private_data;
|
||||
|
||||
if (status != 0) {
|
||||
struct scsi_task *scsi = command_data;
|
||||
|
||||
if (scsi->sense.key == SCSI_SENSE_UNIT_ATTENTION
|
||||
&& scsi->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) {
|
||||
/* This is just the normal unitattention/busreset
|
||||
* you always get just after a fresh login. Try
|
||||
* again.
|
||||
*/
|
||||
if (iscsi_testunitready_async(iscsi, ct->lun,
|
||||
iscsi_testunitready_cb,
|
||||
ct) != 0) {
|
||||
iscsi_set_error(iscsi, "iscsi_testunitready "
|
||||
"failed.");
|
||||
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
ct->private_data);
|
||||
free(ct);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ct->cb(iscsi, status?SCSI_STATUS_ERROR:SCSI_STATUS_GOOD, NULL,
|
||||
ct->private_data);
|
||||
free(ct);
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data _U_,
|
||||
void *private_data)
|
||||
{
|
||||
struct connect_task *ct = private_data;
|
||||
|
||||
if (status != 0) {
|
||||
iscsi_set_error(iscsi, "Failed to login to iSCSI target. "
|
||||
"%s", iscsi_get_error(iscsi));
|
||||
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
|
||||
free(ct);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iscsi_testunitready_async(iscsi, ct->lun,
|
||||
iscsi_testunitready_cb, ct) != 0) {
|
||||
iscsi_set_error(iscsi, "iscsi_testunitready_async failed.");
|
||||
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
|
||||
free(ct);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_,
|
||||
void *private_data)
|
||||
{
|
||||
struct connect_task *ct = private_data;
|
||||
|
||||
if (status != 0) {
|
||||
iscsi_set_error(iscsi, "Failed to connect to iSCSI socket. "
|
||||
"%s", iscsi_get_error(iscsi));
|
||||
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
|
||||
free(ct);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iscsi_login_async(iscsi, iscsi_login_cb, ct) != 0) {
|
||||
iscsi_set_error(iscsi, "iscsi_login_async failed.");
|
||||
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
|
||||
free(ct);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal,
|
||||
int lun, iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct connect_task *ct;
|
||||
|
||||
ct = malloc(sizeof(struct connect_task));
|
||||
if (ct == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory. Failed to allocate "
|
||||
"connect_task structure.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ct->cb = cb;
|
||||
ct->lun = lun;
|
||||
ct->private_data = private_data;
|
||||
if (iscsi_connect_async(iscsi, portal, iscsi_connect_cb, ct) != 0) {
|
||||
free(ct);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
99
lib/crc32c.c
Normal file
99
lib/crc32c.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
|
||||
/*****************************************************************/
|
||||
/* */
|
||||
/* CRC LOOKUP TABLE */
|
||||
/* ================ */
|
||||
/* The following CRC lookup table was generated automagically */
|
||||
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
|
||||
/* Program V1.0 using the following model parameters: */
|
||||
/* */
|
||||
/* Width : 4 bytes. */
|
||||
/* Poly : 0x1EDC6F41L */
|
||||
/* Reverse : TRUE. */
|
||||
/* */
|
||||
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
|
||||
/* see the document titled "A Painless Guide to CRC Error */
|
||||
/* Detection Algorithms" by Ross Williams */
|
||||
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
|
||||
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
static unsigned long crctable[256] = {
|
||||
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
|
||||
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
|
||||
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
|
||||
0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
|
||||
0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
|
||||
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
|
||||
0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
|
||||
0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
|
||||
0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
|
||||
0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
|
||||
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
|
||||
0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
|
||||
0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
|
||||
0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
|
||||
0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
|
||||
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
|
||||
0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
|
||||
0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
|
||||
0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
|
||||
0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
|
||||
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
|
||||
0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
|
||||
0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
|
||||
0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
|
||||
0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
|
||||
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
|
||||
0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
|
||||
0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
|
||||
0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
|
||||
0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
|
||||
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
|
||||
0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
|
||||
0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
|
||||
0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
|
||||
0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
|
||||
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
|
||||
0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
|
||||
0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
|
||||
0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
|
||||
0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
|
||||
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
|
||||
0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
|
||||
0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
|
||||
0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
|
||||
0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
|
||||
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
|
||||
0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
|
||||
0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
|
||||
0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
|
||||
0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
|
||||
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
|
||||
0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
|
||||
0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
|
||||
0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
|
||||
0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
|
||||
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
|
||||
0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
|
||||
0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
|
||||
0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
|
||||
0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
|
||||
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
|
||||
0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
|
||||
0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
|
||||
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
|
||||
};
|
||||
|
||||
unsigned long crc32c(char *buf, int len)
|
||||
{
|
||||
unsigned long crc = 0xffffffff;
|
||||
while (len-- > 0) {
|
||||
crc = (crc>>8) ^ crctable[(crc ^ (*buf++)) & 0xFF];
|
||||
}
|
||||
return crc^0xffffffff;
|
||||
}
|
||||
|
||||
189
lib/discovery.c
Normal file
189
lib/discovery.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
|
||||
int
|
||||
iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
||||
void *private_data)
|
||||
{
|
||||
struct iscsi_pdu *pdu;
|
||||
char *str;
|
||||
|
||||
if (iscsi->session_type != ISCSI_SESSION_DISCOVERY) {
|
||||
iscsi_set_error(iscsi, "Trying to do discovery on "
|
||||
"non-discovery session.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_TEXT_REQUEST,
|
||||
ISCSI_PDU_TEXT_RESPONSE);
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
|
||||
"text pdu.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* immediate */
|
||||
iscsi_pdu_set_immediate(pdu);
|
||||
|
||||
/* flags */
|
||||
iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_TEXT_FINAL);
|
||||
|
||||
/* target transfer tag */
|
||||
iscsi_pdu_set_ttt(pdu, 0xffffffff);
|
||||
|
||||
/* sendtargets */
|
||||
str = (char *)"SendTargets=All";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdu->callback = cb;
|
||||
pdu->private_data = private_data;
|
||||
|
||||
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
|
||||
"text pdu.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_free_discovery_addresses(struct iscsi_discovery_address *addresses)
|
||||
{
|
||||
while (addresses != NULL) {
|
||||
struct iscsi_discovery_address *next = addresses->next;
|
||||
|
||||
free(discard_const(addresses->target_name));
|
||||
addresses->target_name = NULL;
|
||||
|
||||
free(discard_const(addresses->target_address));
|
||||
addresses->target_address = NULL;
|
||||
|
||||
addresses->next = NULL;
|
||||
free(addresses);
|
||||
addresses = next;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size)
|
||||
{
|
||||
struct iscsi_discovery_address *targets = NULL;
|
||||
|
||||
/* verify the response looks sane */
|
||||
if (hdr[1] != ISCSI_PDU_TEXT_FINAL) {
|
||||
iscsi_set_error(iscsi, "unsupported flags in text "
|
||||
"reply %02x", hdr[1]);
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
pdu->private_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* skip past the header */
|
||||
hdr += ISCSI_HEADER_SIZE;
|
||||
size -= ISCSI_HEADER_SIZE;
|
||||
|
||||
while (size > 0) {
|
||||
int len;
|
||||
|
||||
len = strlen((char *)hdr);
|
||||
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len > size) {
|
||||
iscsi_set_error(iscsi, "len > size when parsing "
|
||||
"discovery data %d>%d", len, size);
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
pdu->private_data);
|
||||
iscsi_free_discovery_addresses(targets);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parse the strings */
|
||||
if (!strncmp((char *)hdr, "TargetName=", 11)) {
|
||||
struct iscsi_discovery_address *target;
|
||||
|
||||
target = malloc(sizeof(struct iscsi_discovery_address));
|
||||
if (target == NULL) {
|
||||
iscsi_set_error(iscsi, "Failed to allocate "
|
||||
"data for new discovered "
|
||||
"target");
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
pdu->private_data);
|
||||
iscsi_free_discovery_addresses(targets);
|
||||
return -1;
|
||||
}
|
||||
bzero(target, sizeof(struct iscsi_discovery_address));
|
||||
target->target_name = strdup((char *)hdr+11);
|
||||
if (target->target_name == NULL) {
|
||||
iscsi_set_error(iscsi, "Failed to allocate "
|
||||
"data for new discovered "
|
||||
"target name");
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
pdu->private_data);
|
||||
free(target);
|
||||
target = NULL;
|
||||
iscsi_free_discovery_addresses(targets);
|
||||
return -1;
|
||||
}
|
||||
target->next = targets;
|
||||
targets = target;
|
||||
} else if (!strncmp((char *)hdr, "TargetAddress=", 14)) {
|
||||
targets->target_address = strdup((char *)hdr+14);
|
||||
if (targets->target_address == NULL) {
|
||||
iscsi_set_error(iscsi, "Failed to allocate "
|
||||
"data for new discovered "
|
||||
"target address");
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
pdu->private_data);
|
||||
iscsi_free_discovery_addresses(targets);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
iscsi_set_error(iscsi, "Dont know how to handle "
|
||||
"discovery string : %s", hdr);
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
pdu->private_data);
|
||||
iscsi_free_discovery_addresses(targets);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr += len + 1;
|
||||
size -= len + 1;
|
||||
}
|
||||
|
||||
pdu->callback(iscsi, SCSI_STATUS_GOOD, targets, pdu->private_data);
|
||||
iscsi_free_discovery_addresses(targets);
|
||||
|
||||
return 0;
|
||||
}
|
||||
207
lib/init.c
Normal file
207
lib/init.c
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "slist.h"
|
||||
|
||||
|
||||
struct iscsi_context *
|
||||
iscsi_create_context(const char *initiator_name)
|
||||
{
|
||||
struct iscsi_context *iscsi;
|
||||
|
||||
iscsi = malloc(sizeof(struct iscsi_context));
|
||||
if (iscsi == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(iscsi, sizeof(struct iscsi_context));
|
||||
|
||||
iscsi->initiator_name = strdup(initiator_name);
|
||||
if (iscsi->initiator_name == NULL) {
|
||||
free(iscsi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iscsi->fd = -1;
|
||||
|
||||
/* initialize to a "random" isid */
|
||||
iscsi_set_isid_random(iscsi, getpid() ^ time(NULL));
|
||||
|
||||
return iscsi;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_set_isid_random(struct iscsi_context *iscsi, int rnd)
|
||||
{
|
||||
iscsi->isid[0] = 0x80;
|
||||
iscsi->isid[1] = rnd&0xff;
|
||||
iscsi->isid[2] = rnd&0xff;
|
||||
iscsi->isid[3] = rnd&0xff;
|
||||
iscsi->isid[4] = 0;
|
||||
iscsi->isid[5] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_set_alias(struct iscsi_context *iscsi, const char *alias)
|
||||
{
|
||||
if (iscsi->is_loggedin != 0) {
|
||||
iscsi_set_error(iscsi, "Already logged in when adding alias");
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(discard_const(iscsi->alias));
|
||||
|
||||
iscsi->alias = strdup(alias);
|
||||
if (iscsi->alias == NULL) {
|
||||
iscsi_set_error(iscsi, "Failed to allocate alias name");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
|
||||
{
|
||||
if (iscsi->is_loggedin != 0) {
|
||||
iscsi_set_error(iscsi, "Already logged in when adding "
|
||||
"targetname");
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(discard_const(iscsi->target_name));
|
||||
|
||||
iscsi->target_name = strdup(target_name);
|
||||
if (iscsi->target_name == NULL) {
|
||||
iscsi_set_error(iscsi, "Failed to allocate target name");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_destroy_context(struct iscsi_context *iscsi)
|
||||
{
|
||||
struct iscsi_pdu *pdu;
|
||||
|
||||
if (iscsi == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iscsi->fd != -1) {
|
||||
iscsi_disconnect(iscsi);
|
||||
}
|
||||
|
||||
while ((pdu = iscsi->outqueue)) {
|
||||
SLIST_REMOVE(&iscsi->outqueue, pdu);
|
||||
pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
|
||||
pdu->private_data);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
}
|
||||
while ((pdu = iscsi->waitpdu)) {
|
||||
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
||||
pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
|
||||
pdu->private_data);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
}
|
||||
|
||||
free(discard_const(iscsi->initiator_name));
|
||||
iscsi->initiator_name = NULL;
|
||||
|
||||
free(discard_const(iscsi->target_name));
|
||||
iscsi->target_name = NULL;
|
||||
|
||||
free(discard_const(iscsi->alias));
|
||||
iscsi->alias = NULL;
|
||||
|
||||
if (iscsi->inbuf != NULL) {
|
||||
free(iscsi->inbuf);
|
||||
iscsi->inbuf = NULL;
|
||||
iscsi->insize = 0;
|
||||
iscsi->inpos = 0;
|
||||
}
|
||||
|
||||
free(iscsi->error_string);
|
||||
iscsi->error_string = NULL;
|
||||
|
||||
free(iscsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *str;
|
||||
|
||||
va_start(ap, error_string);
|
||||
if (vasprintf(&str, error_string, ap) < 0) {
|
||||
/* not much we can do here */
|
||||
str = NULL;
|
||||
}
|
||||
|
||||
free(iscsi->error_string);
|
||||
|
||||
iscsi->error_string = str;
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
iscsi_get_error(struct iscsi_context *iscsi)
|
||||
{
|
||||
return iscsi->error_string;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_set_header_digest(struct iscsi_context *iscsi,
|
||||
enum iscsi_header_digest header_digest)
|
||||
{
|
||||
if (iscsi->is_loggedin) {
|
||||
iscsi_set_error(iscsi, "trying to set header digest while "
|
||||
"logged in");
|
||||
return -1;
|
||||
}
|
||||
|
||||
iscsi->want_header_digest = header_digest;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_is_logged_in(struct iscsi_context *iscsi)
|
||||
{
|
||||
return iscsi->is_loggedin;
|
||||
}
|
||||
378
lib/login.c
Normal file
378
lib/login.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
|
||||
int
|
||||
iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
||||
void *private_data)
|
||||
{
|
||||
struct iscsi_pdu *pdu;
|
||||
char *str;
|
||||
|
||||
if (iscsi->is_loggedin != 0) {
|
||||
iscsi_set_error(iscsi, "Trying to login while already logged "
|
||||
"in.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (iscsi->session_type) {
|
||||
case ISCSI_SESSION_DISCOVERY:
|
||||
case ISCSI_SESSION_NORMAL:
|
||||
break;
|
||||
default:
|
||||
iscsi_set_error(iscsi, "trying to login without setting "
|
||||
"session type.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGIN_REQUEST,
|
||||
ISCSI_PDU_LOGIN_RESPONSE);
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
|
||||
"login pdu.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* login request */
|
||||
iscsi_pdu_set_immediate(pdu);
|
||||
|
||||
/* flags */
|
||||
iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_LOGIN_TRANSIT
|
||||
| ISCSI_PDU_LOGIN_CSG_OPNEG
|
||||
| ISCSI_PDU_LOGIN_NSG_FF);
|
||||
|
||||
|
||||
/* initiator name */
|
||||
if (iscsi_pdu_add_data(iscsi, pdu,
|
||||
(unsigned char *)"InitiatorName=",
|
||||
14) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
||||
"failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
if (iscsi_pdu_add_data(iscsi, pdu,
|
||||
(unsigned char *)iscsi->initiator_name,
|
||||
strlen(iscsi->initiator_name) +1) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
||||
"failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* optional alias */
|
||||
if (iscsi->alias) {
|
||||
if (iscsi_pdu_add_data(iscsi, pdu,
|
||||
(unsigned char *)"InitiatorAlias=",
|
||||
15) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
||||
"failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
if (iscsi_pdu_add_data(iscsi, pdu,
|
||||
(unsigned char *)iscsi->alias,
|
||||
strlen(iscsi->alias) +1) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
||||
"failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* target name */
|
||||
if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
|
||||
if (iscsi->target_name == NULL) {
|
||||
iscsi_set_error(iscsi, "Trying normal connect but "
|
||||
"target name not set.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi_pdu_add_data(iscsi, pdu,
|
||||
(unsigned char *)"TargetName=",
|
||||
11) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
||||
"failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
if (iscsi_pdu_add_data(iscsi, pdu,
|
||||
(unsigned char *)iscsi->target_name,
|
||||
strlen(iscsi->target_name) +1) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
|
||||
"failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* session type */
|
||||
switch (iscsi->session_type) {
|
||||
case ISCSI_SESSION_DISCOVERY:
|
||||
str = (char *)"SessionType=Discovery";
|
||||
break;
|
||||
case ISCSI_SESSION_NORMAL:
|
||||
str = (char *)"SessionType=Normal";
|
||||
break;
|
||||
default:
|
||||
iscsi_set_error(iscsi, "Can not handle sessions %d yet.",
|
||||
iscsi->session_type);
|
||||
return -1;
|
||||
}
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (iscsi->want_header_digest) {
|
||||
case ISCSI_HEADER_DIGEST_NONE:
|
||||
str = (char *)"HeaderDigest=None";
|
||||
break;
|
||||
case ISCSI_HEADER_DIGEST_NONE_CRC32C:
|
||||
str = (char *)"HeaderDigest=None,CRC32C";
|
||||
break;
|
||||
case ISCSI_HEADER_DIGEST_CRC32C_NONE:
|
||||
str = (char *)"HeaderDigest=CRC32C,None";
|
||||
break;
|
||||
case ISCSI_HEADER_DIGEST_CRC32C:
|
||||
str = (char *)"HeaderDigest=CRC32C";
|
||||
break;
|
||||
}
|
||||
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
str = (char *)"DataDigest=None";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
str = (char *)"InitialR2T=Yes";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
str = (char *)"ImmediateData=Yes";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
str = (char *)"MaxBurstLength=262144";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
str = (char *)"FirstBurstLength=262144";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
str = (char *)"MaxRecvDataSegmentLength=262144";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
str = (char *)"DataPDUInOrder=Yes";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
str = (char *)"DataSequenceInOrder=Yes";
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
pdu->callback = cb;
|
||||
pdu->private_data = private_data;
|
||||
|
||||
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
|
||||
"pdu.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (size < ISCSI_HEADER_SIZE) {
|
||||
iscsi_set_error(iscsi, "dont have enough data to read status "
|
||||
"from login reply");
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = ntohs(*(uint16_t *)&hdr[36]);
|
||||
if (status != 0) {
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
pdu->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iscsi->statsn = ntohs(*(uint16_t *)&hdr[24]);
|
||||
|
||||
/* XXX here we should parse the data returned in case the target
|
||||
* renegotiated some some parameters.
|
||||
* we should also do proper handshaking if the target is not yet
|
||||
* prepared to transition to the next stage
|
||||
*/
|
||||
/* skip past the header */
|
||||
hdr += ISCSI_HEADER_SIZE;
|
||||
size -= ISCSI_HEADER_SIZE;
|
||||
|
||||
while (size > 0) {
|
||||
int len;
|
||||
|
||||
len = strlen((char *)hdr);
|
||||
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len > size) {
|
||||
iscsi_set_error(iscsi, "len > size when parsing "
|
||||
"login data %d>%d", len, size);
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
pdu->private_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parse the strings */
|
||||
if (!strncmp((char *)hdr, "HeaderDigest=", 13)) {
|
||||
if (!strcmp((char *)hdr + 13, "CRC32C")) {
|
||||
iscsi->header_digest
|
||||
= ISCSI_HEADER_DIGEST_CRC32C;
|
||||
} else {
|
||||
iscsi->header_digest
|
||||
= ISCSI_HEADER_DIGEST_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
hdr += len + 1;
|
||||
size -= len + 1;
|
||||
}
|
||||
|
||||
|
||||
iscsi->is_loggedin = 1;
|
||||
pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
||||
void *private_data)
|
||||
{
|
||||
struct iscsi_pdu *pdu;
|
||||
|
||||
if (iscsi->is_loggedin == 0) {
|
||||
iscsi_set_error(iscsi, "Trying to logout while not logged in.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGOUT_REQUEST,
|
||||
ISCSI_PDU_LOGOUT_RESPONSE);
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
|
||||
"logout pdu.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* logout request has the immediate flag set */
|
||||
iscsi_pdu_set_immediate(pdu);
|
||||
|
||||
/* flags : close the session */
|
||||
iscsi_pdu_set_pduflags(pdu, 0x80);
|
||||
|
||||
|
||||
pdu->callback = cb;
|
||||
pdu->private_data = private_data;
|
||||
|
||||
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
|
||||
"logout pdu.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr _U_, int size _U_)
|
||||
{
|
||||
iscsi->is_loggedin = 0;
|
||||
pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_set_session_type(struct iscsi_context *iscsi,
|
||||
enum iscsi_session_type session_type)
|
||||
{
|
||||
if (iscsi->is_loggedin) {
|
||||
iscsi_set_error(iscsi, "trying to set session type while "
|
||||
"logged in");
|
||||
return -1;
|
||||
}
|
||||
|
||||
iscsi->session_type = session_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
90
lib/nop.c
Normal file
90
lib/nop.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
|
||||
int
|
||||
iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
||||
unsigned char *data, int len, void *private_data)
|
||||
{
|
||||
struct iscsi_pdu *pdu;
|
||||
|
||||
if (iscsi->is_loggedin == 0) {
|
||||
iscsi_set_error(iscsi, "trying send nop-out while not logged "
|
||||
"in");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NOP_IN);
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "Failed to allocate nop-out pdu");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* immediate flag */
|
||||
iscsi_pdu_set_immediate(pdu);
|
||||
|
||||
/* flags */
|
||||
iscsi_pdu_set_pduflags(pdu, 0x80);
|
||||
|
||||
/* ttt */
|
||||
iscsi_pdu_set_ttt(pdu, 0xffffffff);
|
||||
|
||||
/* lun */
|
||||
iscsi_pdu_set_lun(pdu, 2);
|
||||
|
||||
/* cmdsn is not increased if Immediate delivery*/
|
||||
iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
|
||||
pdu->cmdsn = iscsi->cmdsn;
|
||||
|
||||
pdu->callback = cb;
|
||||
pdu->private_data = private_data;
|
||||
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) {
|
||||
iscsi_set_error(iscsi, "Failed to add outdata to nop-out");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
||||
iscsi_set_error(iscsi, "failed to queue iscsi nop-out pdu");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size)
|
||||
{
|
||||
struct iscsi_data data;
|
||||
|
||||
data.data = NULL;
|
||||
data.size = 0;
|
||||
|
||||
if (size > ISCSI_HEADER_SIZE) {
|
||||
data.data = discard_const(&hdr[ISCSI_HEADER_SIZE]);
|
||||
data.size = size - ISCSI_HEADER_SIZE;
|
||||
}
|
||||
pdu->callback(iscsi, SCSI_STATUS_GOOD, &data, pdu->private_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
336
lib/pdu.c
Normal file
336
lib/pdu.c
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
#include "slist.h"
|
||||
|
||||
struct iscsi_pdu *
|
||||
iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode,
|
||||
enum iscsi_opcode response_opcode)
|
||||
{
|
||||
struct iscsi_pdu *pdu;
|
||||
|
||||
pdu = malloc(sizeof(struct iscsi_pdu));
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "failed to allocate pdu");
|
||||
return NULL;
|
||||
}
|
||||
bzero(pdu, sizeof(struct iscsi_pdu));
|
||||
|
||||
pdu->outdata.size = ISCSI_HEADER_SIZE;
|
||||
pdu->outdata.data = malloc(pdu->outdata.size);
|
||||
|
||||
if (pdu->outdata.data == NULL) {
|
||||
iscsi_set_error(iscsi, "failed to allocate pdu header");
|
||||
free(pdu);
|
||||
return NULL;
|
||||
}
|
||||
bzero(pdu->outdata.data, pdu->outdata.size);
|
||||
|
||||
/* opcode */
|
||||
pdu->outdata.data[0] = opcode;
|
||||
pdu->response_opcode = response_opcode;
|
||||
|
||||
/* isid */
|
||||
if (opcode == ISCSI_PDU_LOGIN_REQUEST) {
|
||||
memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6);
|
||||
}
|
||||
|
||||
/* itt */
|
||||
*(uint32_t *)&pdu->outdata.data[16] = htonl(iscsi->itt);
|
||||
pdu->itt = iscsi->itt;
|
||||
|
||||
iscsi->itt++;
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
||||
{
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "trying to free NULL pdu");
|
||||
return;
|
||||
}
|
||||
|
||||
free(pdu->outdata.data);
|
||||
pdu->outdata.data = NULL;
|
||||
|
||||
free(pdu->indata.data);
|
||||
pdu->indata.data = NULL;
|
||||
|
||||
if (pdu->scsi_cbdata) {
|
||||
iscsi_free_scsi_cbdata(pdu->scsi_cbdata);
|
||||
pdu->scsi_cbdata = NULL;
|
||||
}
|
||||
|
||||
free(pdu);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data,
|
||||
unsigned char *dptr, int dsize, int pdualignment)
|
||||
{
|
||||
int len, aligned;
|
||||
unsigned char *buf;
|
||||
|
||||
if (dsize == 0) {
|
||||
iscsi_set_error(iscsi, "Trying to append zero size data to "
|
||||
"iscsi_data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = data->size + dsize;
|
||||
aligned = len;
|
||||
if (pdualignment) {
|
||||
aligned = (aligned+3)&0xfffffffc;
|
||||
}
|
||||
buf = malloc(aligned);
|
||||
if (buf == NULL) {
|
||||
iscsi_set_error(iscsi, "failed to allocate buffer for %d "
|
||||
"bytes", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data->size > 0) {
|
||||
memcpy(buf, data->data, data->size);
|
||||
}
|
||||
memcpy(buf + data->size, dptr, dsize);
|
||||
if (len != aligned) {
|
||||
/* zero out any padding at the end */
|
||||
bzero(buf+len, aligned-len);
|
||||
}
|
||||
|
||||
free(data->data);
|
||||
|
||||
data->data = buf;
|
||||
data->size = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
unsigned char *dptr, int dsize)
|
||||
{
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "trying to add data to NULL pdu");
|
||||
return -1;
|
||||
}
|
||||
if (dsize == 0) {
|
||||
iscsi_set_error(iscsi, "Trying to append zero size data to "
|
||||
"pdu");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi_add_data(iscsi, &pdu->outdata, dptr, dsize, 1) != 0) {
|
||||
iscsi_set_error(iscsi, "failed to add data to pdu buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* update data segment length */
|
||||
*(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size
|
||||
- ISCSI_HEADER_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_get_pdu_size(struct iscsi_context *iscsi, const unsigned char *hdr)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = (ntohl(*(uint32_t *)&hdr[4])&0x00ffffff) + ISCSI_HEADER_SIZE;
|
||||
size = (size+3)&0xfffffffc;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr,
|
||||
int size)
|
||||
{
|
||||
uint32_t itt;
|
||||
enum iscsi_opcode opcode;
|
||||
struct iscsi_pdu *pdu;
|
||||
uint8_t ahslen;
|
||||
|
||||
opcode = hdr[0] & 0x3f;
|
||||
ahslen = hdr[4];
|
||||
itt = ntohl(*(uint32_t *)&hdr[16]);
|
||||
|
||||
if (ahslen != 0) {
|
||||
iscsi_set_error(iscsi, "cant handle expanded headers yet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
|
||||
enum iscsi_opcode expected_response = pdu->response_opcode;
|
||||
int is_finished = 1;
|
||||
|
||||
if (pdu->itt != itt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we have a special case with scsi-command opcodes,
|
||||
* they are replied to by either a scsi-response
|
||||
* or a data-in, or a combination of both.
|
||||
*/
|
||||
if (opcode == ISCSI_PDU_DATA_IN
|
||||
&& expected_response == ISCSI_PDU_SCSI_RESPONSE) {
|
||||
expected_response = ISCSI_PDU_DATA_IN;
|
||||
}
|
||||
|
||||
if (opcode != expected_response) {
|
||||
iscsi_set_error(iscsi, "Got wrong opcode back for "
|
||||
"itt:%d got:%d expected %d",
|
||||
itt, opcode, pdu->response_opcode);
|
||||
return -1;
|
||||
}
|
||||
switch (opcode) {
|
||||
case ISCSI_PDU_LOGIN_RESPONSE:
|
||||
if (iscsi_process_login_reply(iscsi, pdu, hdr, size)
|
||||
!= 0) {
|
||||
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
iscsi_set_error(iscsi, "iscsi login reply "
|
||||
"failed");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ISCSI_PDU_TEXT_RESPONSE:
|
||||
if (iscsi_process_text_reply(iscsi, pdu, hdr, size)
|
||||
!= 0) {
|
||||
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
iscsi_set_error(iscsi, "iscsi text reply "
|
||||
"failed");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ISCSI_PDU_LOGOUT_RESPONSE:
|
||||
if (iscsi_process_logout_reply(iscsi, pdu, hdr, size)
|
||||
!= 0) {
|
||||
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
iscsi_set_error(iscsi, "iscsi logout reply "
|
||||
"failed");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ISCSI_PDU_SCSI_RESPONSE:
|
||||
if (iscsi_process_scsi_reply(iscsi, pdu, hdr, size)
|
||||
!= 0) {
|
||||
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
iscsi_set_error(iscsi, "iscsi response reply "
|
||||
"failed");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ISCSI_PDU_DATA_IN:
|
||||
if (iscsi_process_scsi_data_in(iscsi, pdu, hdr, size,
|
||||
&is_finished) != 0) {
|
||||
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
iscsi_set_error(iscsi, "iscsi data in "
|
||||
"failed");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case ISCSI_PDU_NOP_IN:
|
||||
if (iscsi_process_nop_out_reply(iscsi, pdu, hdr, size)
|
||||
!= 0) {
|
||||
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
iscsi_set_error(iscsi, "iscsi nop-in failed");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
iscsi_set_error(iscsi, "Dont know how to handle "
|
||||
"opcode %d", opcode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_finished) {
|
||||
SLIST_REMOVE(&iscsi->waitpdu, pdu);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags)
|
||||
{
|
||||
pdu->outdata.data[1] = flags;
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_pdu_set_immediate(struct iscsi_pdu *pdu)
|
||||
{
|
||||
pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE;
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt)
|
||||
{
|
||||
*(uint32_t *)&pdu->outdata.data[20] = htonl(ttt);
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn)
|
||||
{
|
||||
*(uint32_t *)&pdu->outdata.data[24] = htonl(cmdsn);
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn)
|
||||
{
|
||||
*(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn);
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task)
|
||||
{
|
||||
bzero(&pdu->outdata.data[32], 16);
|
||||
memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size);
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun)
|
||||
{
|
||||
pdu->outdata.data[9] = lun;
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen)
|
||||
{
|
||||
*(uint32_t *)&pdu->outdata.data[20] = htonl(expxferlen);
|
||||
}
|
||||
528
lib/scsi-command.c
Normal file
528
lib/scsi-command.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
struct iscsi_scsi_cbdata {
|
||||
struct iscsi_scsi_cbdata *prev, *next;
|
||||
iscsi_command_cb callback;
|
||||
void *private_data;
|
||||
struct scsi_task *task;
|
||||
};
|
||||
|
||||
void
|
||||
iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata)
|
||||
{
|
||||
if (scsi_cbdata == NULL) {
|
||||
return;
|
||||
}
|
||||
if (scsi_cbdata->task != NULL) {
|
||||
scsi_free_scsi_task(scsi_cbdata->task);
|
||||
scsi_cbdata->task = NULL;
|
||||
}
|
||||
free(scsi_cbdata);
|
||||
}
|
||||
|
||||
void
|
||||
iscsi_cbdata_steal_scsi_task(struct scsi_task *task)
|
||||
{
|
||||
struct iscsi_scsi_cbdata *scsi_cbdata =
|
||||
scsi_get_task_private_ptr(task);
|
||||
|
||||
if (scsi_cbdata != NULL) {
|
||||
scsi_cbdata->task = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *private_data)
|
||||
{
|
||||
struct iscsi_scsi_cbdata *scsi_cbdata =
|
||||
(struct iscsi_scsi_cbdata *)private_data;
|
||||
struct scsi_task *task = command_data;
|
||||
|
||||
switch (status) {
|
||||
case SCSI_STATUS_GOOD:
|
||||
scsi_cbdata->callback(iscsi, SCSI_STATUS_GOOD, task,
|
||||
scsi_cbdata->private_data);
|
||||
return;
|
||||
case SCSI_STATUS_CHECK_CONDITION:
|
||||
scsi_cbdata->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task,
|
||||
scsi_cbdata->private_data);
|
||||
return;
|
||||
default:
|
||||
iscsi_set_error(iscsi, "Cant handle scsi status %d yet.",
|
||||
status);
|
||||
scsi_cbdata->callback(iscsi, SCSI_STATUS_ERROR, task,
|
||||
scsi_cbdata->private_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun,
|
||||
struct scsi_task *task, iscsi_command_cb cb,
|
||||
struct iscsi_data *data, void *private_data)
|
||||
{
|
||||
struct iscsi_pdu *pdu;
|
||||
struct iscsi_scsi_cbdata *scsi_cbdata;
|
||||
int flags;
|
||||
|
||||
if (iscsi->session_type != ISCSI_SESSION_NORMAL) {
|
||||
iscsi_set_error(iscsi, "Trying to send command on "
|
||||
"discovery session.");
|
||||
scsi_free_scsi_task(task);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi->is_loggedin == 0) {
|
||||
iscsi_set_error(iscsi, "Trying to send command while "
|
||||
"not logged in.");
|
||||
scsi_free_scsi_task(task);
|
||||
return -1;
|
||||
}
|
||||
|
||||
scsi_cbdata = malloc(sizeof(struct iscsi_scsi_cbdata));
|
||||
if (scsi_cbdata == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to allocate "
|
||||
"scsi cbdata.");
|
||||
scsi_free_scsi_task(task);
|
||||
return -1;
|
||||
}
|
||||
bzero(scsi_cbdata, sizeof(struct iscsi_scsi_cbdata));
|
||||
scsi_cbdata->task = task;
|
||||
scsi_cbdata->callback = cb;
|
||||
scsi_cbdata->private_data = private_data;
|
||||
|
||||
scsi_set_task_private_ptr(task, scsi_cbdata);
|
||||
|
||||
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_REQUEST,
|
||||
ISCSI_PDU_SCSI_RESPONSE);
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory, Failed to allocate "
|
||||
"scsi pdu.");
|
||||
iscsi_free_scsi_cbdata(scsi_cbdata);
|
||||
return -1;
|
||||
}
|
||||
pdu->scsi_cbdata = scsi_cbdata;
|
||||
|
||||
/* flags */
|
||||
flags = ISCSI_PDU_SCSI_FINAL|ISCSI_PDU_SCSI_ATTR_SIMPLE;
|
||||
switch (task->xfer_dir) {
|
||||
case SCSI_XFER_NONE:
|
||||
break;
|
||||
case SCSI_XFER_READ:
|
||||
flags |= ISCSI_PDU_SCSI_READ;
|
||||
break;
|
||||
case SCSI_XFER_WRITE:
|
||||
flags |= ISCSI_PDU_SCSI_WRITE;
|
||||
if (data == NULL) {
|
||||
iscsi_set_error(iscsi, "DATA-OUT command but data "
|
||||
"== NULL.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
if (data->size != task->expxferlen) {
|
||||
iscsi_set_error(iscsi, "Data size:%d is not same as "
|
||||
"expected data transfer "
|
||||
"length:%d.", data->size,
|
||||
task->expxferlen);
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
if (iscsi_pdu_add_data(iscsi, pdu, data->data, data->size)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to "
|
||||
"add outdata to the pdu.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
iscsi_pdu_set_pduflags(pdu, flags);
|
||||
|
||||
/* lun */
|
||||
iscsi_pdu_set_lun(pdu, lun);
|
||||
|
||||
/* expxferlen */
|
||||
iscsi_pdu_set_expxferlen(pdu, task->expxferlen);
|
||||
|
||||
/* cmdsn */
|
||||
iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
|
||||
pdu->cmdsn = iscsi->cmdsn;
|
||||
iscsi->cmdsn++;
|
||||
|
||||
/* exp statsn */
|
||||
iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1);
|
||||
|
||||
/* cdb */
|
||||
iscsi_pdu_set_cdb(pdu, task);
|
||||
|
||||
pdu->callback = iscsi_scsi_response_cb;
|
||||
pdu->private_data = scsi_cbdata;
|
||||
|
||||
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
|
||||
"scsi pdu.");
|
||||
iscsi_free_pdu(iscsi, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size)
|
||||
{
|
||||
int statsn, flags, response, status;
|
||||
struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
|
||||
struct scsi_task *task = scsi_cbdata->task;
|
||||
|
||||
statsn = ntohl(*(uint32_t *)&hdr[24]);
|
||||
if (statsn > (int)iscsi->statsn) {
|
||||
iscsi->statsn = statsn;
|
||||
}
|
||||
|
||||
flags = hdr[1];
|
||||
if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
|
||||
iscsi_set_error(iscsi, "scsi response pdu but Final bit is "
|
||||
"not set: 0x%02x.", flags);
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
|
||||
pdu->private_data);
|
||||
return -1;
|
||||
}
|
||||
if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
|
||||
iscsi_set_error(iscsi, "scsi response asked for ACK "
|
||||
"0x%02x.", flags);
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
|
||||
pdu->private_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
response = hdr[2];
|
||||
|
||||
status = hdr[3];
|
||||
|
||||
switch (status) {
|
||||
case SCSI_STATUS_GOOD:
|
||||
task->datain.data = pdu->indata.data;
|
||||
task->datain.size = pdu->indata.size;
|
||||
|
||||
pdu->indata.data = NULL;
|
||||
pdu->indata.size = 0;
|
||||
|
||||
pdu->callback(iscsi, SCSI_STATUS_GOOD, task,
|
||||
pdu->private_data);
|
||||
break;
|
||||
case SCSI_STATUS_CHECK_CONDITION:
|
||||
task->datain.size = size - ISCSI_HEADER_SIZE;
|
||||
task->datain.data = malloc(task->datain.size);
|
||||
if (task->datain.data == NULL) {
|
||||
iscsi_set_error(iscsi, "failed to allocate blob for "
|
||||
"sense data");
|
||||
}
|
||||
memcpy(task->datain.data, hdr + ISCSI_HEADER_SIZE,
|
||||
task->datain.size);
|
||||
|
||||
task->sense.error_type = task->datain.data[2] & 0x7f;
|
||||
task->sense.key = task->datain.data[4] & 0x0f;
|
||||
task->sense.ascq = ntohs(*(uint16_t *)
|
||||
&(task->datain.data[14]));
|
||||
|
||||
iscsi_set_error(iscsi, "SENSE KEY:%s(%d) ASCQ:%s(0x%04x)",
|
||||
scsi_sense_key_str(task->sense.key),
|
||||
task->sense.key,
|
||||
scsi_sense_ascq_str(task->sense.ascq),
|
||||
task->sense.ascq);
|
||||
pdu->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task,
|
||||
pdu->private_data);
|
||||
break;
|
||||
default:
|
||||
iscsi_set_error(iscsi, "Unknown SCSI status :%d.", status);
|
||||
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
|
||||
pdu->private_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size _U_, int *is_finished)
|
||||
{
|
||||
int statsn, flags, status;
|
||||
struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
|
||||
struct scsi_task *task = scsi_cbdata->task;
|
||||
int dsl;
|
||||
|
||||
statsn = ntohl(*(uint32_t *)&hdr[24]);
|
||||
if (statsn > (int)iscsi->statsn) {
|
||||
iscsi->statsn = statsn;
|
||||
}
|
||||
|
||||
flags = hdr[1];
|
||||
if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
|
||||
iscsi_set_error(iscsi, "scsi response asked for ACK "
|
||||
"0x%02x.", flags);
|
||||
pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
|
||||
pdu->private_data);
|
||||
return -1;
|
||||
}
|
||||
dsl = ntohl(*(uint32_t *)&hdr[4])&0x00ffffff;
|
||||
|
||||
if (iscsi_add_data(iscsi, &pdu->indata,
|
||||
discard_const(hdr + ISCSI_HEADER_SIZE), dsl, 0)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to add data "
|
||||
"to pdu in buffer.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
|
||||
*is_finished = 0;
|
||||
}
|
||||
if ((flags&ISCSI_PDU_DATA_CONTAINS_STATUS) == 0) {
|
||||
*is_finished = 0;
|
||||
}
|
||||
|
||||
if (*is_finished == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* this was the final data-in packet in the sequence and it has
|
||||
* the s-bit set, so invoke the callback.
|
||||
*/
|
||||
status = hdr[3];
|
||||
task->datain.data = pdu->indata.data;
|
||||
task->datain.size = pdu->indata.size;
|
||||
|
||||
pdu->indata.data = NULL;
|
||||
pdu->indata.size = 0;
|
||||
|
||||
pdu->callback(iscsi, status, task, pdu->private_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* SCSI commands
|
||||
*/
|
||||
|
||||
int
|
||||
iscsi_testunitready_async(struct iscsi_context *iscsi, int lun,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int ret;
|
||||
|
||||
task = scsi_cdb_testunitready();
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"testunitready cdb.");
|
||||
return -1;
|
||||
}
|
||||
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
|
||||
private_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iscsi_reportluns_async(struct iscsi_context *iscsi, int report_type,
|
||||
int alloc_len, iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int ret;
|
||||
|
||||
if (alloc_len < 16) {
|
||||
iscsi_set_error(iscsi, "Minimum allowed alloc len for "
|
||||
"reportluns is 16. You specified %d.",
|
||||
alloc_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
task = scsi_reportluns_cdb(report_type, alloc_len);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"reportluns cdb.");
|
||||
return -1;
|
||||
}
|
||||
/* report luns are always sent to lun 0 */
|
||||
ret = iscsi_scsi_command_async(iscsi, 0, task, cb, NULL,
|
||||
private_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, int evpd,
|
||||
int page_code, int maxsize,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int ret;
|
||||
|
||||
task = scsi_cdb_inquiry(evpd, page_code, maxsize);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"inquiry cdb.");
|
||||
return -1;
|
||||
}
|
||||
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
|
||||
private_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int pmi, iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int ret;
|
||||
|
||||
task = scsi_cdb_readcapacity10(lba, pmi);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"readcapacity10 cdb.");
|
||||
return -1;
|
||||
}
|
||||
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
|
||||
private_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_read10_async(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int datalen, int blocksize,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int ret;
|
||||
|
||||
if (datalen % blocksize != 0) {
|
||||
iscsi_set_error(iscsi, "Datalen:%d is not a multiple of "
|
||||
"the blocksize:%d.", datalen, blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
task = scsi_cdb_read10(lba, datalen, blocksize);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"read10 cdb.");
|
||||
return -1;
|
||||
}
|
||||
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
|
||||
private_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
iscsi_write10_async(struct iscsi_context *iscsi, int lun, unsigned char *data,
|
||||
int datalen, int lba, int fua, int fuanv, int blocksize,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
struct iscsi_data outdata;
|
||||
int ret;
|
||||
|
||||
if (datalen % blocksize != 0) {
|
||||
iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the "
|
||||
"blocksize:%d.", datalen, blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
task = scsi_cdb_write10(lba, datalen, fua, fuanv, blocksize);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"read10 cdb.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
outdata.data = data;
|
||||
outdata.size = datalen;
|
||||
|
||||
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata,
|
||||
private_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, int dbd, int pc,
|
||||
int page_code, int sub_page_code,
|
||||
unsigned char alloc_len,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int ret;
|
||||
|
||||
task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code,
|
||||
alloc_len);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"modesense6 cdb.");
|
||||
return -1;
|
||||
}
|
||||
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
|
||||
private_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_synchronizecache10_async(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int num_blocks, int syncnv, int immed,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int ret;
|
||||
|
||||
task = scsi_cdb_synchronizecache10(lba, num_blocks, syncnv,
|
||||
immed);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"synchronizecache10 cdb.");
|
||||
return -1;
|
||||
}
|
||||
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
|
||||
private_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
876
lib/scsi-lowlevel.c
Normal file
876
lib/scsi-lowlevel.c
Normal file
@@ -0,0 +1,876 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*
|
||||
* would be nice if this could grow into a full blown library for scsi to
|
||||
* 1, build a CDB
|
||||
* 2, check how big a complete data-in structure needs to be
|
||||
* 3, unmarshall data-in into a real structure
|
||||
* 4, marshall a real structure into a data-out blob
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "scsi-lowlevel.h"
|
||||
#include "slist.h"
|
||||
|
||||
|
||||
void
|
||||
scsi_free_scsi_task(struct scsi_task *task)
|
||||
{
|
||||
struct scsi_allocated_memory *mem;
|
||||
|
||||
while ((mem = task->mem)) {
|
||||
SLIST_REMOVE(&task->mem, mem);
|
||||
free(mem->ptr);
|
||||
free(mem);
|
||||
}
|
||||
|
||||
free(task->datain.data);
|
||||
free(task);
|
||||
}
|
||||
|
||||
static void *
|
||||
scsi_malloc(struct scsi_task *task, size_t size)
|
||||
{
|
||||
struct scsi_allocated_memory *mem;
|
||||
|
||||
mem = malloc(sizeof(struct scsi_allocated_memory));
|
||||
if (mem == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
bzero(mem, sizeof(struct scsi_allocated_memory));
|
||||
mem->ptr = malloc(size);
|
||||
if (mem->ptr == NULL) {
|
||||
free(mem);
|
||||
return NULL;
|
||||
}
|
||||
bzero(mem->ptr, size);
|
||||
SLIST_ADD(&task->mem, mem);
|
||||
return mem->ptr;
|
||||
}
|
||||
|
||||
struct value_string {
|
||||
int value;
|
||||
const char *string;
|
||||
};
|
||||
|
||||
static const char *
|
||||
value_string_find(struct value_string *values, int value)
|
||||
{
|
||||
for (; values->value; values++) {
|
||||
if (value == values->value) {
|
||||
return values->string;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
scsi_sense_key_str(int key)
|
||||
{
|
||||
struct value_string keys[] = {
|
||||
{SCSI_SENSE_ILLEGAL_REQUEST,
|
||||
"ILLEGAL_REQUEST"},
|
||||
{SCSI_SENSE_UNIT_ATTENTION,
|
||||
"UNIT_ATTENTION"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
return value_string_find(keys, key);
|
||||
}
|
||||
|
||||
const char *
|
||||
scsi_sense_ascq_str(int ascq)
|
||||
{
|
||||
struct value_string ascqs[] = {
|
||||
{SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB,
|
||||
"INVALID_FIELD_IN_CDB"},
|
||||
{SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED,
|
||||
"LOGICAL_UNIT_NOT_SUPPORTED"},
|
||||
{SCSI_SENSE_ASCQ_BUS_RESET,
|
||||
"BUS_RESET"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
return value_string_find(ascqs, ascq);
|
||||
}
|
||||
|
||||
/*
|
||||
* TESTUNITREADY
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_cdb_testunitready(void)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(task, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_TESTUNITREADY;
|
||||
|
||||
task->cdb_size = 6;
|
||||
task->xfer_dir = SCSI_XFER_NONE;
|
||||
task->expxferlen = 0;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* REPORTLUNS
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_reportluns_cdb(int report_type, int alloc_len)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(task, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_REPORTLUNS;
|
||||
task->cdb[2] = report_type;
|
||||
*(uint32_t *)&task->cdb[6] = htonl(alloc_len);
|
||||
|
||||
task->cdb_size = 12;
|
||||
task->xfer_dir = SCSI_XFER_READ;
|
||||
task->expxferlen = alloc_len;
|
||||
|
||||
task->params.reportluns.report_type = report_type;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse the data in blob and calcualte the size of a full report luns
|
||||
* datain structure
|
||||
*/
|
||||
static int
|
||||
scsi_reportluns_datain_getfullsize(struct scsi_task *task)
|
||||
{
|
||||
uint32_t list_size;
|
||||
|
||||
list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
|
||||
|
||||
return list_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* unmarshall the data in blob for reportluns into a structure
|
||||
*/
|
||||
static struct scsi_reportluns_list *
|
||||
scsi_reportluns_datain_unmarshall(struct scsi_task *task)
|
||||
{
|
||||
struct scsi_reportluns_list *list;
|
||||
int list_size;
|
||||
int i, num_luns;
|
||||
|
||||
if (task->datain.size < 4) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
|
||||
if (list_size < task->datain.size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
num_luns = list_size / 8 - 1;
|
||||
list = scsi_malloc(task, offsetof(struct scsi_reportluns_list, luns)
|
||||
+ sizeof(uint16_t) * num_luns);
|
||||
if (list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list->num = num_luns;
|
||||
for (i = 0; i < num_luns; i++) {
|
||||
list->luns[i] = htons(*(uint16_t *)
|
||||
&(task->datain.data[i*8+8]));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* READCAPACITY10
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_cdb_readcapacity10(int lba, int pmi)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(task, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_READCAPACITY10;
|
||||
|
||||
*(uint32_t *)&task->cdb[2] = htonl(lba);
|
||||
|
||||
if (pmi) {
|
||||
task->cdb[8] |= 0x01;
|
||||
}
|
||||
|
||||
task->cdb_size = 10;
|
||||
task->xfer_dir = SCSI_XFER_READ;
|
||||
task->expxferlen = 8;
|
||||
|
||||
task->params.readcapacity10.lba = lba;
|
||||
task->params.readcapacity10.pmi = pmi;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse the data in blob and calcualte the size of a full
|
||||
* readcapacity10 datain structure
|
||||
*/
|
||||
static int
|
||||
scsi_readcapacity10_datain_getfullsize(struct scsi_task *task _U_)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* unmarshall the data in blob for readcapacity10 into a structure
|
||||
*/
|
||||
static struct scsi_readcapacity10 *
|
||||
scsi_readcapacity10_datain_unmarshall(struct scsi_task *task)
|
||||
{
|
||||
struct scsi_readcapacity10 *rc10;
|
||||
|
||||
if (task->datain.size < 8) {
|
||||
return NULL;
|
||||
}
|
||||
rc10 = scsi_malloc(task, sizeof(struct scsi_readcapacity10));
|
||||
if (rc10 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc10->lba = htonl(*(uint32_t *)&(task->datain.data[0]));
|
||||
rc10->block_size = htonl(*(uint32_t *)&(task->datain.data[4]));
|
||||
|
||||
return rc10;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* INQUIRY
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_cdb_inquiry(int evpd, int page_code, int alloc_len)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(task, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_INQUIRY;
|
||||
|
||||
if (evpd) {
|
||||
task->cdb[1] |= 0x01;
|
||||
}
|
||||
|
||||
task->cdb[2] = page_code;
|
||||
|
||||
*(uint16_t *)&task->cdb[3] = htons(alloc_len);
|
||||
|
||||
task->cdb_size = 6;
|
||||
task->xfer_dir = SCSI_XFER_READ;
|
||||
task->expxferlen = alloc_len;
|
||||
|
||||
task->params.inquiry.evpd = evpd;
|
||||
task->params.inquiry.page_code = page_code;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse the data in blob and calcualte the size of a full
|
||||
* inquiry datain structure
|
||||
*/
|
||||
static int
|
||||
scsi_inquiry_datain_getfullsize(struct scsi_task *task)
|
||||
{
|
||||
if (task->params.inquiry.evpd == 0) {
|
||||
return task->datain.data[4] + 3;
|
||||
}
|
||||
|
||||
switch (task->params.inquiry.page_code) {
|
||||
case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
|
||||
return task->datain.data[3] + 4;
|
||||
case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
|
||||
return task->datain.data[3] + 4;
|
||||
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
|
||||
return ntohs(*(uint16_t *)&task->datain.data[2]) + 4;
|
||||
case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
|
||||
return task->datain.data[3] + 4;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* unmarshall the data in blob for inquiry into a structure
|
||||
*/
|
||||
static void *
|
||||
scsi_inquiry_datain_unmarshall(struct scsi_task *task)
|
||||
{
|
||||
if (task->params.inquiry.evpd == 0) {
|
||||
struct scsi_inquiry_standard *inq;
|
||||
|
||||
/* standard inquiry */
|
||||
inq = scsi_malloc(task, sizeof(struct scsi_inquiry_standard));
|
||||
if (inq == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
|
||||
inq->periperal_device_type = task->datain.data[0]&0x1f;
|
||||
inq->rmb = !!(task->datain.data[1]&0x80);
|
||||
inq->version = task->datain.data[2];
|
||||
inq->normaca = !!(task->datain.data[3]&0x20);
|
||||
inq->hisup = !!(task->datain.data[3]&0x10);
|
||||
inq->response_data_format = task->datain.data[3]&0x0f;
|
||||
|
||||
inq->sccs = !!(task->datain.data[5]&0x80);
|
||||
inq->acc = !!(task->datain.data[5]&0x40);
|
||||
inq->tpgs = (task->datain.data[5]>>4)&0x03;
|
||||
inq->threepc = !!(task->datain.data[5]&0x08);
|
||||
inq->protect = !!(task->datain.data[5]&0x01);
|
||||
|
||||
inq->encserv = !!(task->datain.data[6]&0x40);
|
||||
inq->multip = !!(task->datain.data[6]&0x10);
|
||||
inq->addr16 = !!(task->datain.data[6]&0x01);
|
||||
inq->wbus16 = !!(task->datain.data[7]&0x20);
|
||||
inq->sync = !!(task->datain.data[7]&0x10);
|
||||
inq->cmdque = !!(task->datain.data[7]&0x02);
|
||||
|
||||
memcpy(&inq->vendor_identification[0],
|
||||
&task->datain.data[8], 8);
|
||||
memcpy(&inq->product_identification[0],
|
||||
&task->datain.data[16], 16);
|
||||
memcpy(&inq->product_revision_level[0],
|
||||
&task->datain.data[32], 4);
|
||||
|
||||
inq->clocking = (task->datain.data[56]>>2)&0x03;
|
||||
inq->qas = !!(task->datain.data[56]&0x02);
|
||||
inq->ius = !!(task->datain.data[56]&0x01);
|
||||
|
||||
return inq;
|
||||
}
|
||||
|
||||
if (task->params.inquiry.page_code
|
||||
== SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES) {
|
||||
struct scsi_inquiry_supported_pages *inq;
|
||||
|
||||
inq = scsi_malloc(task,
|
||||
sizeof(struct scsi_inquiry_supported_pages));
|
||||
if (inq == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
|
||||
inq->periperal_device_type = task->datain.data[0]&0x1f;
|
||||
inq->pagecode = task->datain.data[1];
|
||||
|
||||
inq->num_pages = task->datain.data[3];
|
||||
inq->pages = scsi_malloc(task, inq->num_pages);
|
||||
if (inq->pages == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(inq->pages, &task->datain.data[4], inq->num_pages);
|
||||
return inq;
|
||||
} else if (task->params.inquiry.page_code
|
||||
== SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER) {
|
||||
struct scsi_inquiry_unit_serial_number *inq;
|
||||
|
||||
inq = scsi_malloc(task,
|
||||
sizeof(struct scsi_inquiry_unit_serial_number));
|
||||
if (inq == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
|
||||
inq->periperal_device_type = task->datain.data[0]&0x1f;
|
||||
inq->pagecode = task->datain.data[1];
|
||||
|
||||
inq->usn = scsi_malloc(task, task->datain.data[3]+1);
|
||||
if (inq->usn == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(inq->usn, &task->datain.data[4], task->datain.data[3]);
|
||||
inq->usn[task->datain.data[3]] = 0;
|
||||
return inq;
|
||||
} else if (task->params.inquiry.page_code
|
||||
== SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION) {
|
||||
struct scsi_inquiry_device_identification *inq;
|
||||
int remaining = ntohs(*(uint16_t *)&task->datain.data[2]);
|
||||
unsigned char *dptr;
|
||||
|
||||
inq = scsi_malloc(task,
|
||||
sizeof(struct scsi_inquiry_device_identification));
|
||||
if (inq == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
|
||||
inq->periperal_device_type = task->datain.data[0]&0x1f;
|
||||
inq->pagecode = task->datain.data[1];
|
||||
|
||||
dptr = &task->datain.data[4];
|
||||
while (remaining > 0) {
|
||||
struct scsi_inquiry_device_designator *dev;
|
||||
|
||||
dev = scsi_malloc(task,
|
||||
sizeof(struct scsi_inquiry_device_designator));
|
||||
if (dev == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->next = inq->designators;
|
||||
inq->designators = dev;
|
||||
|
||||
dev->protocol_identifier = (dptr[0]>>4) & 0x0f;
|
||||
dev->code_set = dptr[0] & 0x0f;
|
||||
dev->piv = !!(dptr[1]&0x80);
|
||||
dev->association = (dptr[1]>>4)&0x03;
|
||||
dev->designator_type = dptr[1]&0x0f;
|
||||
|
||||
dev->designator_length = dptr[3];
|
||||
dev->designator = scsi_malloc(task,
|
||||
dev->designator_length+1);
|
||||
if (dev->designator == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
dev->designator[dev->designator_length] = 0;
|
||||
memcpy(dev->designator, &dptr[4],
|
||||
dev->designator_length);
|
||||
|
||||
remaining -= 4;
|
||||
remaining -= dev->designator_length;
|
||||
|
||||
dptr += dev->designator_length + 4;
|
||||
}
|
||||
return inq;
|
||||
} else if (task->params.inquiry.page_code
|
||||
== SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS) {
|
||||
struct scsi_inquiry_block_device_characteristics *inq;
|
||||
|
||||
inq = scsi_malloc(task,
|
||||
sizeof(struct scsi_inquiry_block_device_characteristics));
|
||||
if (inq == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
|
||||
inq->periperal_device_type = task->datain.data[0]&0x1f;
|
||||
inq->pagecode = task->datain.data[1];
|
||||
|
||||
inq->medium_rotation_rate = ntohs(*(uint16_t *)
|
||||
&task->datain.data[4]);
|
||||
return inq;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* READ10
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_cdb_read10(int lba, int xferlen, int blocksize)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(task, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_READ10;
|
||||
|
||||
*(uint32_t *)&task->cdb[2] = htonl(lba);
|
||||
*(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
|
||||
|
||||
task->cdb_size = 10;
|
||||
task->xfer_dir = SCSI_XFER_READ;
|
||||
task->expxferlen = xferlen;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
* WRITE10
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(task, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_WRITE10;
|
||||
|
||||
if (fua) {
|
||||
task->cdb[1] |= 0x08;
|
||||
}
|
||||
if (fuanv) {
|
||||
task->cdb[1] |= 0x02;
|
||||
}
|
||||
|
||||
*(uint32_t *)&task->cdb[2] = htonl(lba);
|
||||
*(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
|
||||
|
||||
task->cdb_size = 10;
|
||||
task->xfer_dir = SCSI_XFER_WRITE;
|
||||
task->expxferlen = xferlen;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* MODESENSE6
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc,
|
||||
enum scsi_modesense_page_code page_code,
|
||||
int sub_page_code, unsigned char alloc_len)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(task, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_MODESENSE6;
|
||||
|
||||
if (dbd) {
|
||||
task->cdb[1] |= 0x08;
|
||||
}
|
||||
task->cdb[2] = pc<<6 | page_code;
|
||||
task->cdb[3] = sub_page_code;
|
||||
task->cdb[4] = alloc_len;
|
||||
|
||||
task->cdb_size = 6;
|
||||
task->xfer_dir = SCSI_XFER_READ;
|
||||
task->expxferlen = alloc_len;
|
||||
|
||||
task->params.modesense6.dbd = dbd;
|
||||
task->params.modesense6.pc = pc;
|
||||
task->params.modesense6.page_code = page_code;
|
||||
task->params.modesense6.sub_page_code = sub_page_code;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse the data in blob and calcualte the size of a full
|
||||
* modesense6 datain structure
|
||||
*/
|
||||
static int
|
||||
scsi_modesense6_datain_getfullsize(struct scsi_task *task)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = task->datain.data[0] + 1;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SYNCHRONIZECACHE10
|
||||
*/
|
||||
struct scsi_task *
|
||||
scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = malloc(sizeof(struct scsi_task));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bzero(task, sizeof(struct scsi_task));
|
||||
task->cdb[0] = SCSI_OPCODE_SYNCHRONIZECACHE10;
|
||||
|
||||
if (syncnv) {
|
||||
task->cdb[1] |= 0x04;
|
||||
}
|
||||
if (immed) {
|
||||
task->cdb[1] |= 0x02;
|
||||
}
|
||||
*(uint32_t *)&task->cdb[2] = htonl(lba);
|
||||
*(uint16_t *)&task->cdb[7] = htons(num_blocks);
|
||||
|
||||
task->cdb_size = 10;
|
||||
task->xfer_dir = SCSI_XFER_NONE;
|
||||
task->expxferlen = 0;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
scsi_datain_getfullsize(struct scsi_task *task)
|
||||
{
|
||||
switch (task->cdb[0]) {
|
||||
case SCSI_OPCODE_TESTUNITREADY:
|
||||
return 0;
|
||||
case SCSI_OPCODE_INQUIRY:
|
||||
return scsi_inquiry_datain_getfullsize(task);
|
||||
case SCSI_OPCODE_MODESENSE6:
|
||||
return scsi_modesense6_datain_getfullsize(task);
|
||||
case SCSI_OPCODE_READCAPACITY10:
|
||||
return scsi_readcapacity10_datain_getfullsize(task);
|
||||
case SCSI_OPCODE_SYNCHRONIZECACHE10:
|
||||
return 0;
|
||||
case SCSI_OPCODE_REPORTLUNS:
|
||||
return scsi_reportluns_datain_getfullsize(task);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *
|
||||
scsi_datain_unmarshall(struct scsi_task *task)
|
||||
{
|
||||
switch (task->cdb[0]) {
|
||||
case SCSI_OPCODE_TESTUNITREADY:
|
||||
return NULL;
|
||||
case SCSI_OPCODE_INQUIRY:
|
||||
return scsi_inquiry_datain_unmarshall(task);
|
||||
case SCSI_OPCODE_READCAPACITY10:
|
||||
return scsi_readcapacity10_datain_unmarshall(task);
|
||||
case SCSI_OPCODE_SYNCHRONIZECACHE10:
|
||||
return NULL;
|
||||
case SCSI_OPCODE_REPORTLUNS:
|
||||
return scsi_reportluns_datain_unmarshall(task);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
scsi_devtype_to_str(enum scsi_inquiry_peripheral_device_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS:
|
||||
return "DIRECT_ACCESS";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS:
|
||||
return "SEQUENTIAL_ACCESS";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER:
|
||||
return "PRINTER";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR:
|
||||
return "PROCESSOR";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE:
|
||||
return "WRITE_ONCE";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC:
|
||||
return "MMC";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER:
|
||||
return "SCANNER";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY:
|
||||
return "OPTICAL_MEMORY";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER:
|
||||
return "MEDIA_CHANGER";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS:
|
||||
return "COMMUNICATIONS";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER:
|
||||
return "STORAGE_ARRAY_CONTROLLER";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES:
|
||||
return "ENCLOSURE_SERVICES";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS:
|
||||
return "SIMPLIFIED_DIRECT_ACCESS";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER:
|
||||
return "OPTICAL_CARD_READER";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER:
|
||||
return "BRIDGE_CONTROLLER";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD:
|
||||
return "OSD";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION:
|
||||
return "AUTOMATION";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER:
|
||||
return "SEQURITY_MANAGER";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN:
|
||||
return "WELL_KNOWN_LUN";
|
||||
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *
|
||||
scsi_devqualifier_to_str(enum scsi_inquiry_peripheral_qualifier qualifier)
|
||||
{
|
||||
switch (qualifier) {
|
||||
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED:
|
||||
return "CONNECTED";
|
||||
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED:
|
||||
return "DISCONNECTED";
|
||||
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED:
|
||||
return "NOT_SUPPORTED";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *
|
||||
scsi_version_to_str(enum scsi_version version)
|
||||
{
|
||||
switch (version) {
|
||||
case SCSI_VERSION_SPC:
|
||||
return "ANSI INCITS 301-1997 (SPC)";
|
||||
case SCSI_VERSION_SPC2:
|
||||
return "ANSI INCITS 351-2001 (SPC-2)";
|
||||
case SCSI_VERSION_SPC3:
|
||||
return "ANSI INCITS 408-2005 (SPC-3)";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
scsi_inquiry_pagecode_to_str(int pagecode)
|
||||
{
|
||||
switch (pagecode) {
|
||||
case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
|
||||
return "SUPPORTED_VPD_PAGES";
|
||||
case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
|
||||
return "UNIT_SERIAL_NUMBER";
|
||||
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
|
||||
return "DEVICE_IDENTIFICATION";
|
||||
case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
|
||||
return "BLOCK_DEVICE_CHARACTERISTICS";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
scsi_protocol_identifier_to_str(int identifier)
|
||||
{
|
||||
switch (identifier) {
|
||||
case SCSI_PROTOCOL_IDENTIFIER_FIBRE_CHANNEL:
|
||||
return "FIBRE_CHANNEL";
|
||||
case SCSI_PROTOCOL_IDENTIFIER_PARALLEL_SCSI:
|
||||
return "PARALLEL_SCSI";
|
||||
case SCSI_PROTOCOL_IDENTIFIER_SSA:
|
||||
return "SSA";
|
||||
case SCSI_PROTOCOL_IDENTIFIER_IEEE_1394:
|
||||
return "IEEE_1394";
|
||||
case SCSI_PROTOCOL_IDENTIFIER_RDMA:
|
||||
return "RDMA";
|
||||
case SCSI_PROTOCOL_IDENTIFIER_ISCSI:
|
||||
return "ISCSI";
|
||||
case SCSI_PROTOCOL_IDENTIFIER_SAS:
|
||||
return "SAS";
|
||||
case SCSI_PROTOCOL_IDENTIFIER_ADT:
|
||||
return "ADT";
|
||||
case SCSI_PROTOCOL_IDENTIFIER_ATA:
|
||||
return "ATA";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *
|
||||
scsi_codeset_to_str(int codeset)
|
||||
{
|
||||
switch (codeset) {
|
||||
case SCSI_CODESET_BINARY:
|
||||
return "BINARY";
|
||||
case SCSI_CODESET_ASCII:
|
||||
return "ASCII";
|
||||
case SCSI_CODESET_UTF8:
|
||||
return "UTF8";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *
|
||||
scsi_association_to_str(int association)
|
||||
{
|
||||
switch (association) {
|
||||
case SCSI_ASSOCIATION_LOGICAL_UNIT:
|
||||
return "LOGICAL_UNIT";
|
||||
case SCSI_ASSOCIATION_TARGET_PORT:
|
||||
return "TARGET_PORT";
|
||||
case SCSI_ASSOCIATION_TARGET_DEVICE:
|
||||
return "TARGET_DEVICE";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char *
|
||||
scsi_designator_type_to_str(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC:
|
||||
return "VENDOR_SPECIFIC";
|
||||
case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID:
|
||||
return "T10_VENDORT_ID";
|
||||
case SCSI_DESIGNATOR_TYPE_EUI_64:
|
||||
return "EUI_64";
|
||||
case SCSI_DESIGNATOR_TYPE_NAA:
|
||||
return "NAA";
|
||||
case SCSI_DESIGNATOR_TYPE_RELATIVE_TARGET_PORT:
|
||||
return "RELATIVE_TARGET_PORT";
|
||||
case SCSI_DESIGNATOR_TYPE_TARGET_PORT_GROUP:
|
||||
return "TARGET_PORT_GROUP";
|
||||
case SCSI_DESIGNATOR_TYPE_LOGICAL_UNIT_GROUP:
|
||||
return "LOGICAL_UNIT_GROUP";
|
||||
case SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER:
|
||||
return "MD5_LOGICAL_UNIT_IDENTIFIER";
|
||||
case SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING:
|
||||
return "SCSI_NAME_STRING";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void
|
||||
scsi_set_task_private_ptr(struct scsi_task *task, void *ptr)
|
||||
{
|
||||
task->ptr = ptr;
|
||||
}
|
||||
|
||||
void *
|
||||
scsi_get_task_private_ptr(struct scsi_task *task)
|
||||
{
|
||||
return task->ptr;
|
||||
}
|
||||
351
lib/socket.c
Normal file
351
lib/socket.c
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "slist.h"
|
||||
|
||||
static void set_nonblocking(int fd)
|
||||
{
|
||||
unsigned v;
|
||||
v = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, v | O_NONBLOCK);
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
int tpgt = -1;
|
||||
int port = 3260;
|
||||
char *str;
|
||||
char *addr;
|
||||
struct sockaddr_storage s;
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)&s;
|
||||
int socksize;
|
||||
|
||||
if (iscsi->fd != -1) {
|
||||
iscsi_set_error(iscsi,
|
||||
"Trying to connect but already connected.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = strdup(portal);
|
||||
if (addr == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: "
|
||||
"Failed to strdup portal address.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check if we have a target portal group tag */
|
||||
str = rindex(addr, ',');
|
||||
if (str != NULL) {
|
||||
tpgt = atoi(str+1);
|
||||
str[0] = 0;
|
||||
}
|
||||
|
||||
/* XXX need handling for {ipv6 addresses} */
|
||||
/* for now, assume all is ipv4 */
|
||||
str = rindex(addr, ':');
|
||||
if (str != NULL) {
|
||||
port = atoi(str+1);
|
||||
str[0] = 0;
|
||||
}
|
||||
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, addr, &sin->sin_addr) != 1) {
|
||||
iscsi_set_error(iscsi, "Invalid target:%s "
|
||||
"Failed to convert to ip address.", addr);
|
||||
free(addr);
|
||||
return -1;
|
||||
}
|
||||
free(addr);
|
||||
|
||||
switch (s.ss_family) {
|
||||
case AF_INET:
|
||||
iscsi->fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
socksize = sizeof(struct sockaddr_in);
|
||||
break;
|
||||
default:
|
||||
iscsi_set_error(iscsi, "Unknown address family :%d. "
|
||||
"Only IPv4 supported so far.", s.ss_family);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
if (iscsi->fd == -1) {
|
||||
iscsi_set_error(iscsi, "Failed to open iscsi socket. "
|
||||
"Errno:%s(%d).", strerror(errno), errno);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
iscsi->socket_status_cb = cb;
|
||||
iscsi->connect_data = private_data;
|
||||
|
||||
set_nonblocking(iscsi->fd);
|
||||
|
||||
if (connect(iscsi->fd, (struct sockaddr *)&s, socksize) != 0
|
||||
&& errno != EINPROGRESS) {
|
||||
iscsi_set_error(iscsi, "Connect failed with errno : "
|
||||
"%s(%d)", strerror(errno), errno);
|
||||
close(iscsi->fd);
|
||||
iscsi->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_disconnect(struct iscsi_context *iscsi)
|
||||
{
|
||||
if (iscsi->fd == -1) {
|
||||
iscsi_set_error(iscsi, "Trying to disconnect "
|
||||
"but not connected");
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(iscsi->fd);
|
||||
iscsi->fd = -1;
|
||||
iscsi->is_connected = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_get_fd(struct iscsi_context *iscsi)
|
||||
{
|
||||
return iscsi->fd;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_which_events(struct iscsi_context *iscsi)
|
||||
{
|
||||
int events = POLLIN;
|
||||
|
||||
if (iscsi->is_connected == 0) {
|
||||
events |= POLLOUT;
|
||||
}
|
||||
|
||||
if (iscsi->outqueue) {
|
||||
events |= POLLOUT;
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_read_from_socket(struct iscsi_context *iscsi)
|
||||
{
|
||||
int available;
|
||||
int size;
|
||||
unsigned char *buf;
|
||||
ssize_t count;
|
||||
|
||||
if (ioctl(iscsi->fd, FIONREAD, &available) != 0) {
|
||||
iscsi_set_error(iscsi, "ioctl FIONREAD returned error : "
|
||||
"%d", errno);
|
||||
return -1;
|
||||
}
|
||||
if (available == 0) {
|
||||
iscsi_set_error(iscsi, "no data readable in socket, "
|
||||
"socket is closed");
|
||||
return -1;
|
||||
}
|
||||
size = iscsi->insize - iscsi->inpos + available;
|
||||
buf = malloc(size);
|
||||
if (buf == NULL) {
|
||||
iscsi_set_error(iscsi, "failed to allocate %d bytes for "
|
||||
"input buffer", size);
|
||||
return -1;
|
||||
}
|
||||
if (iscsi->insize > iscsi->inpos) {
|
||||
memcpy(buf, iscsi->inbuf + iscsi->inpos,
|
||||
iscsi->insize - iscsi->inpos);
|
||||
iscsi->insize -= iscsi->inpos;
|
||||
iscsi->inpos = 0;
|
||||
}
|
||||
|
||||
count = read(iscsi->fd, buf + iscsi->insize, available);
|
||||
if (count == -1) {
|
||||
if (errno == EINTR) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
return 0;
|
||||
}
|
||||
iscsi_set_error(iscsi, "read from socket failed, "
|
||||
"errno:%d", errno);
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(iscsi->inbuf);
|
||||
|
||||
iscsi->inbuf = buf;
|
||||
iscsi->insize += count;
|
||||
|
||||
while (1) {
|
||||
if (iscsi->insize - iscsi->inpos < ISCSI_RAW_HEADER_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
count = iscsi_get_pdu_size(iscsi,
|
||||
iscsi->inbuf + iscsi->inpos);
|
||||
if (iscsi->insize + iscsi->inpos < count) {
|
||||
return 0;
|
||||
}
|
||||
if (iscsi_process_pdu(iscsi, iscsi->inbuf + iscsi->inpos,
|
||||
count) != 0) {
|
||||
iscsi->inpos += count;
|
||||
return -1;
|
||||
}
|
||||
iscsi->inpos += count;
|
||||
if (iscsi->inpos == iscsi->insize) {
|
||||
free(iscsi->inbuf);
|
||||
iscsi->inbuf = NULL;
|
||||
iscsi->insize = 0;
|
||||
iscsi->inpos = 0;
|
||||
}
|
||||
if (iscsi->inpos > iscsi->insize) {
|
||||
iscsi_set_error(iscsi, "inpos > insize. bug!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_write_to_socket(struct iscsi_context *iscsi)
|
||||
{
|
||||
ssize_t count;
|
||||
|
||||
if (iscsi->fd == -1) {
|
||||
iscsi_set_error(iscsi, "trying to write but not connected");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (iscsi->outqueue != NULL) {
|
||||
ssize_t total;
|
||||
|
||||
total = iscsi->outqueue->outdata.size;
|
||||
total = (total + 3) & 0xfffffffc;
|
||||
|
||||
count = write(iscsi->fd,
|
||||
iscsi->outqueue->outdata.data
|
||||
+ iscsi->outqueue->written,
|
||||
total - iscsi->outqueue->written);
|
||||
if (count == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
return 0;
|
||||
}
|
||||
iscsi_set_error(iscsi, "Error when writing to "
|
||||
"socket :%d", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iscsi->outqueue->written += count;
|
||||
if (iscsi->outqueue->written == total) {
|
||||
struct iscsi_pdu *pdu = iscsi->outqueue;
|
||||
|
||||
SLIST_REMOVE(&iscsi->outqueue, pdu);
|
||||
SLIST_ADD_END(&iscsi->waitpdu, pdu);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_service(struct iscsi_context *iscsi, int revents)
|
||||
{
|
||||
if (revents & POLLERR) {
|
||||
iscsi_set_error(iscsi, "iscsi_service: POLLERR, "
|
||||
"socket error.");
|
||||
iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
iscsi->connect_data);
|
||||
return -1;
|
||||
}
|
||||
if (revents & POLLHUP) {
|
||||
iscsi_set_error(iscsi, "iscsi_service: POLLHUP, "
|
||||
"socket error.");
|
||||
iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL,
|
||||
iscsi->connect_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
|
||||
iscsi->is_connected = 1;
|
||||
iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL,
|
||||
iscsi->connect_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (revents & POLLOUT && iscsi->outqueue != NULL) {
|
||||
if (iscsi_write_to_socket(iscsi) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (revents & POLLIN) {
|
||||
if (iscsi_read_from_socket(iscsi) != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
||||
{
|
||||
if (pdu == NULL) {
|
||||
iscsi_set_error(iscsi, "trying to queue NULL pdu");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) {
|
||||
unsigned long crc;
|
||||
|
||||
if (pdu->outdata.size < ISCSI_RAW_HEADER_SIZE + 4) {
|
||||
iscsi_set_error(iscsi, "PDU too small (%d) to contain header digest",
|
||||
pdu->outdata.size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
crc = crc32c((char *)pdu->outdata.data, ISCSI_RAW_HEADER_SIZE);
|
||||
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+3] = (crc >> 24)&0xff;
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+2] = (crc >> 16)&0xff;
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+1] = (crc >> 8)&0xff;
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+0] = (crc) &0xff;
|
||||
}
|
||||
|
||||
SLIST_ADD_END(&iscsi->outqueue, pdu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
282
lib/sync.c
Normal file
282
lib/sync.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
struct scsi_sync_state {
|
||||
int finished;
|
||||
struct scsi_task *task;
|
||||
};
|
||||
|
||||
struct iscsi_sync_state {
|
||||
int finished;
|
||||
int status;
|
||||
};
|
||||
|
||||
static void
|
||||
event_loop(struct iscsi_context *iscsi, struct scsi_sync_state *state)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
|
||||
while (state->finished == 0) {
|
||||
pfd.fd = iscsi_get_fd(iscsi);
|
||||
pfd.events = iscsi_which_events(iscsi);
|
||||
|
||||
if (poll(&pfd, 1, -1) < 0) {
|
||||
iscsi_set_error(iscsi, "Poll failed");
|
||||
return;
|
||||
}
|
||||
if (iscsi_service(iscsi, pfd.revents) < 0) {
|
||||
iscsi_set_error(iscsi,
|
||||
"iscsi_service failed with : %s",
|
||||
iscsi_get_error(iscsi));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Synchronous iSCSI commands
|
||||
*/
|
||||
static void
|
||||
iscsi_sync_cb(struct iscsi_context *iscsi _U_, int status,
|
||||
void *command_data _U_, void *private_data)
|
||||
{
|
||||
struct iscsi_sync_state *state = private_data;
|
||||
|
||||
state->status = status;
|
||||
state->finished = 1;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_connect_sync(struct iscsi_context *iscsi, const char *portal)
|
||||
{
|
||||
struct iscsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_connect_async(iscsi, portal,
|
||||
iscsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi,
|
||||
"Failed to start connect() %s",
|
||||
iscsi_get_error(iscsi));
|
||||
return -1;
|
||||
}
|
||||
|
||||
event_loop(iscsi, (struct scsi_sync_state *)&state);
|
||||
|
||||
return state.status;
|
||||
}
|
||||
|
||||
int
|
||||
iscsi_full_connect_sync(struct iscsi_context *iscsi,
|
||||
const char *portal, int lun)
|
||||
{
|
||||
struct iscsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_full_connect_async(iscsi, portal, lun,
|
||||
iscsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi,
|
||||
"Failed to start full connect %s",
|
||||
iscsi_get_error(iscsi));
|
||||
return -1;
|
||||
}
|
||||
|
||||
event_loop(iscsi, (struct scsi_sync_state *)&state);
|
||||
|
||||
return state.status;
|
||||
}
|
||||
|
||||
int iscsi_login_sync(struct iscsi_context *iscsi)
|
||||
{
|
||||
struct iscsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_login_async(iscsi, iscsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi, "Failed to login. %s",
|
||||
iscsi_get_error(iscsi));
|
||||
return -1;
|
||||
}
|
||||
|
||||
event_loop(iscsi, (struct scsi_sync_state *)&state);
|
||||
|
||||
return state.status;
|
||||
}
|
||||
|
||||
int iscsi_logout_sync(struct iscsi_context *iscsi)
|
||||
{
|
||||
struct iscsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_logout_async(iscsi, iscsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi, "Failed to start logout() %s",
|
||||
iscsi_get_error(iscsi));
|
||||
return -1;
|
||||
}
|
||||
|
||||
event_loop(iscsi, (struct scsi_sync_state *)&state);
|
||||
|
||||
return state.status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Synchronous SCSI commands
|
||||
*/
|
||||
static void
|
||||
scsi_sync_cb(struct iscsi_context *iscsi _U_, int status, void *command_data,
|
||||
void *private_data)
|
||||
{
|
||||
struct scsi_task *task = command_data;
|
||||
struct scsi_sync_state *state = private_data;
|
||||
|
||||
task->status = status;
|
||||
state->finished = 1;
|
||||
state->task = task;
|
||||
iscsi_cbdata_steal_scsi_task(task);
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_reportluns_sync(struct iscsi_context *iscsi, int report_type,
|
||||
int alloc_len)
|
||||
{
|
||||
struct scsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_reportluns_async(iscsi, report_type, alloc_len,
|
||||
scsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi, "Failed to send ReportLuns command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
return state.task;
|
||||
}
|
||||
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_testunitready_sync(struct iscsi_context *iscsi, int lun)
|
||||
{
|
||||
struct scsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_testunitready_async(iscsi, lun,
|
||||
scsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi,
|
||||
"Failed to send TestUnitReady command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
return state.task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_inquiry_sync(struct iscsi_context *iscsi, int lun, int evpd,
|
||||
int page_code, int maxsize)
|
||||
{
|
||||
struct scsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_inquiry_async(iscsi, lun, evpd, page_code, maxsize,
|
||||
scsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi, "Failed to send Inquiry command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
return state.task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int pmi)
|
||||
{
|
||||
struct scsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_readcapacity10_async(iscsi, lun, lba, pmi,
|
||||
scsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi,
|
||||
"Failed to send ReadCapacity10 command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
return state.task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int num_blocks, int syncnv, int immed)
|
||||
{
|
||||
struct scsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_synchronizecache10_async(iscsi, lun, lba, num_blocks,
|
||||
syncnv, immed,
|
||||
scsi_sync_cb, &state) != 0) {
|
||||
iscsi_set_error(iscsi,
|
||||
"Failed to send SynchronizeCache10 command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
return state.task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun,
|
||||
struct scsi_task *task, struct iscsi_data *data)
|
||||
{
|
||||
struct scsi_sync_state state;
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
if (iscsi_scsi_command_async(iscsi, lun, task,
|
||||
scsi_sync_cb, data, &state) != 0) {
|
||||
iscsi_set_error(iscsi, "Failed to send SCSI command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
return state.task;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user