Merge pull request #5 from carmark/spc

init commit for SBC/SPC & first run process
This commit is contained in:
Lei Xue
2016-05-03 09:01:07 +08:00
22 changed files with 1562 additions and 610 deletions

View File

@@ -2,65 +2,38 @@
Cloud Integrated SCSI Target framework, this includes two binaries, one is `citadm` which is command line to config and control, the other is `citd` which is a target daemon.
```
# citadm --help
Linux SCSI Target administration utility, version 0.1
Usage: ./citadm [OPTIONS]
Application Options:
--lld <driver> --mode target --op new --tid <id> --targetname <name>
add a new target with <id> and <name>. <id> must not be zero.
--lld <driver> --mode target --op delete [--force] --tid <id>
delete the specific target with <id>.
With force option, the specific target is deleted
even if there is an activity.
--lld <driver> --mode target --op show
show all the targets.
--lld <driver> --mode target --op show --tid <id>
show the specific target's parameters.
--lld <driver> --mode target --op update --tid <id> --name <param> --value <value>
change the target parameters of the target with <id>.
--lld <driver> --mode target --op bind --tid <id> --initiator-address <address>
--lld <driver> --mode target --op bind --tid <id> --initiator-name <name>
enable the target to accept the specific initiators.
--lld <driver> --mode target --op unbind --tid <id> --initiator-address <address>
--lld <driver> --mode target --op unbind --tid <id> --initiator-name <name>
disable the specific permitted initiators.
--lld <driver> --mode logicalunit --op new --tid <id> --lun <lun>
--backing-store <path> --bstype <type> --bsopts <bs options> --bsoflags <options>
add a new logical unit with <lun> to the specific
target with <id>. The logical unit is offered
to the initiators. <path> must be block device files
(including LVM and RAID devices) or regular files.
bstype option is optional.
bsopts are specific to the bstype.
bsoflags supported options are sync and direct
(sync:direct for both).
--lld <driver> --mode logicalunit --op delete --tid <id> --lun <lun>
delete the specific logical unit with <lun> that
the target with <id> has.
--lld <driver> --mode account --op new --user <name> --password <pass>
add a new account with <name> and <pass>.
--lld <driver> --mode account --op delete --user <name>
delete the specific account having <name>.
--lld <driver> --mode account --op bind --tid <id> --user <name> [--outgoing]
add the specific account having <name> to
the specific target with <id>.
<user> could be <IncomingUser> or <OutgoingUser>.
If you use --outgoing option, the account will
be added as an outgoing account.
--lld <driver> --mode account --op unbind --tid <id> --user <name> [--outgoing]
delete the specific account having <name> from specific
target. The --outgoing option must be added if you
delete an outgoing account.
--lld <driver> --mode lld --op start
Start the specified lld without restarting the tgtd process.
--control-port <port> use control port <port>
Help Options:
--help
display this help and exit
Report bugs via <https://github.com/gostor/gotgt/issues>.
## Build
```
$ mkdir $GOPATH/gotstor/
$ cd $GOPATH/gostor/
$ git clone https://github.com/gostor/gotgt gotgt
$ cd gotgt
$ make
```
## Test
You can test this with [libiscsi](https://github.com/gostor/libiscsi).
### build the test tool of libiscsi
```
$ git clone https://github.com/gostor/libiscsi
$ cd libiscsi
$ ./autogen.sh
$ ./configure
$ make
```
### start the gotgt daemon
```
$ ./citd
```
### begin the test
```
$ ./iscsi-test-cu -v iscsi://127.0.0.1:3260/iqn.test.haha/0
```

58
citd.go
View File

@@ -19,13 +19,69 @@ package main
import (
"net"
"os"
"reflect"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/scsi"
)
func main() {
_, err := net.Listen("tcp", ":3260")
l, err := net.Listen("tcp", ":3260")
if err != nil {
glog.Error(err)
os.Exit(1)
}
defer l.Close()
t, err := scsi.NewTarget(0, "iscsi", "test-iscsi-target")
if err != nil {
glog.Error(err)
os.Exit(1)
}
conns := make(map[string]net.Conn)
for {
glog.Info("Listening ...")
conn, err := l.Accept()
checkError(err, "Accept")
glog.Info("Accepting ...")
conns[conn.RemoteAddr().String()] = conn
// start a new thread to do with this command
go Handler(conn, t)
}
}
func checkError(err error, info string) (res bool) {
if err != nil {
glog.Error(info + " " + err.Error())
return false
}
return true
}
func Handler(conn net.Conn, tgt *api.SCSITarget) {
glog.Infof("connection is connected from %s...\n", conn.RemoteAddr().String())
buf := make([]byte, 1024)
for {
lenght, err := conn.Read(buf)
if checkError(err, "Connection") == false {
conn.Close()
break
}
if lenght > 0 {
buf[lenght] = 0
}
v := reflect.ValueOf(tgt.SCSITargetDriver)
iscsit := v.MethodByName("ProcessCommand")
in := make([]reflect.Value, 1)
in[0] = reflect.ValueOf(buf[0:lenght])
res := iscsit.Call(in)[0]
b := res.Bytes()
glog.Infof("%s\n", string(b))
conn.Write(b)
}
}

307
pkg/api/types.go Normal file
View File

@@ -0,0 +1,307 @@
package api
import (
"bytes"
"errors"
)
type SCSICommandType byte
var (
TEST_UNIT_READY SCSICommandType = 0x00
REZERO_UNIT SCSICommandType = 0x01
REQUEST_SENSE SCSICommandType = 0x03
FORMAT_UNIT SCSICommandType = 0x04
READ_BLOCK_LIMITS SCSICommandType = 0x05
REASSIGN_BLOCKS SCSICommandType = 0x07
INITIALIZE_ELEMENT_STATUS SCSICommandType = 0x07
READ_6 SCSICommandType = 0x08
WRITE_6 SCSICommandType = 0x0a
SEEK_6 SCSICommandType = 0x0b
READ_REVERSE SCSICommandType = 0x0f
WRITE_FILEMARKS SCSICommandType = 0x10
SPACE SCSICommandType = 0x11
INQUIRY SCSICommandType = 0x12
RECOVER_BUFFERED_DATA SCSICommandType = 0x14
MODE_SELECT SCSICommandType = 0x15
RESERVE SCSICommandType = 0x16
RELEASE SCSICommandType = 0x17
COPY SCSICommandType = 0x18
ERASE SCSICommandType = 0x19
MODE_SENSE SCSICommandType = 0x1a
START_STOP SCSICommandType = 0x1b
RECEIVE_DIAGNOSTIC SCSICommandType = 0x1c
SEND_DIAGNOSTIC SCSICommandType = 0x1d
ALLOW_MEDIUM_REMOVAL SCSICommandType = 0x1e
SET_WINDOW SCSICommandType = 0x24
READ_CAPACITY SCSICommandType = 0x25
READ_10 SCSICommandType = 0x28
WRITE_10 SCSICommandType = 0x2a
SEEK_10 SCSICommandType = 0x2b
POSITION_TO_ELEMENT SCSICommandType = 0x2b
WRITE_VERIFY SCSICommandType = 0x2e
VERIFY_10 SCSICommandType = 0x2f
SEARCH_HIGH SCSICommandType = 0x30
SEARCH_EQUAL SCSICommandType = 0x31
SEARCH_LOW SCSICommandType = 0x32
SET_LIMITS SCSICommandType = 0x33
PRE_FETCH_10 SCSICommandType = 0x34
READ_POSITION SCSICommandType = 0x34
SYNCHRONIZE_CACHE SCSICommandType = 0x35
LOCK_UNLOCK_CACHE SCSICommandType = 0x36
READ_DEFECT_DATA SCSICommandType = 0x37
INITIALIZE_ELEMENT_STATUS_WITH_RANGE SCSICommandType = 0x37
MEDIUM_SCAN SCSICommandType = 0x38
COMPARE SCSICommandType = 0x39
COPY_VERIFY SCSICommandType = 0x3a
WRITE_BUFFER SCSICommandType = 0x3b
READ_BUFFER SCSICommandType = 0x3c
UPDATE_BLOCK SCSICommandType = 0x3d
READ_LONG SCSICommandType = 0x3e
WRITE_LONG SCSICommandType = 0x3f
CHANGE_DEFINITION SCSICommandType = 0x40
WRITE_SAME SCSICommandType = 0x41
UNMAP SCSICommandType = 0x42
READ_TOC SCSICommandType = 0x43
GET_CONFIGURATION SCSICommandType = 0x46
LOG_SELECT SCSICommandType = 0x4c
LOG_SENSE SCSICommandType = 0x4d
READ_DISK_INFO SCSICommandType = 0x51
READ_TRACK_INFO SCSICommandType = 0x52
MODE_SELECT_10 SCSICommandType = 0x55
RESERVE_10 SCSICommandType = 0x56
RELEASE_10 SCSICommandType = 0x57
MODE_SENSE_10 SCSICommandType = 0x5a
CLOSE_TRACK SCSICommandType = 0x5b
READ_BUFFER_CAP SCSICommandType = 0x5c
PERSISTENT_RESERVE_IN SCSICommandType = 0x5e
PERSISTENT_RESERVE_OUT SCSICommandType = 0x5f
VARLEN_CDB SCSICommandType = 0x7f
READ_16 SCSICommandType = 0x88
COMPARE_AND_WRITE SCSICommandType = 0x89
WRITE_16 SCSICommandType = 0x8a
ORWRITE_16 SCSICommandType = 0x8b
WRITE_VERIFY_16 SCSICommandType = 0x8e
VERIFY_16 SCSICommandType = 0x8f
PRE_FETCH_16 SCSICommandType = 0x90
SYNCHRONIZE_CACHE_16 SCSICommandType = 0x91
WRITE_SAME_16 SCSICommandType = 0x93
SERVICE_ACTION_IN SCSICommandType = 0x9e
SAI_READ_CAPACITY_16 SCSICommandType = 0x10
SAI_GET_LBA_STATUS SCSICommandType = 0x12
REPORT_LUNS SCSICommandType = 0xa0
MAINT_PROTOCOL_IN SCSICommandType = 0xa3
MOVE_MEDIUM SCSICommandType = 0xa5
EXCHANGE_MEDIUM SCSICommandType = 0xa6
READ_12 SCSICommandType = 0xa8
WRITE_12 SCSICommandType = 0xaa
GET_PERFORMACE SCSICommandType = 0xac
READ_DVD_STRUCTURE SCSICommandType = 0xad
WRITE_VERIFY_12 SCSICommandType = 0xae
VERIFY_12 SCSICommandType = 0xaf
SEARCH_HIGH_12 SCSICommandType = 0xb0
SEARCH_EQUAL_12 SCSICommandType = 0xb1
SEARCH_LOW_12 SCSICommandType = 0xb2
READ_ELEMENT_STATUS SCSICommandType = 0xb8
SEND_VOLUME_TAG SCSICommandType = 0xb6
SET_STREAMING SCSICommandType = 0xb6
SET_CD_SPEED SCSICommandType = 0xbb
WRITE_LONG_2 SCSICommandType = 0xea
)
type SCSITargetState int
var (
TargetOnline SCSITargetState = 1
TargetReady SCSITargetState = 2
)
type SCSIDataDirection int
const (
SCSIDataNone = iota
SCSIDataWrite
SCSIDataRead
SCSIDataBidirection
)
type SCSIDataBuffer struct {
Buffer *bytes.Buffer
Length uint32
TransferLength uint32
Resid int32
}
type SCSICommand struct {
Target *SCSITarget
DeviceID uint64
Device *SCSILu
State uint64
Direction SCSIDataDirection
InSDBBuffer SCSIDataBuffer
OutSDBBuffer SCSIDataBuffer
// Command ITN ID
CommandITNID uint64
Offset uint64
TL uint32
SCB *bytes.Buffer
SCBLength int
Lun []uint8
Attribute int
Tag uint64
Result byte
SenseBuffer *bytes.Buffer
SenseLength uint32
ITNexus *ITNexus
ITNexusLuInfo *ITNexusLuInfo
}
type ITNexus struct {
ID uint64
Ctime uint64
Commands []SCSICommand
Target *SCSITarget
Host int
Info string
}
type ITNexusLuInfo struct {
Lu *SCSILu
ID uint64
Prevent int
}
type SCSITarget struct {
Name string
TID int
LID int
State SCSITargetState
Devices []SCSILu
ITNexus []ITNexus
SCSITargetDriver interface{}
}
type SCSITargetDriverState int
const (
// just registered
SCSI_DRIVER_REGD = iota
// initialized ok
SCSI_DRIVER_INIT
// failed to initialize
SCSI_DRIVER_ERR
// exited
SCSI_DRIVER_EXIT
)
type SCSITargetDriverCommon struct {
Name string
State SCSITargetDriverState
DefaultBST string
}
type SCSILuPhyAttribute struct {
SCSIID string
SCSISN string
NumID uint64
VendorID string
ProductID string
ProductRev string
VersionDesction []uint16
// Peripheral device type
DeviceType uint
// Peripheral Qualifier
Qualifier bool
// Removable media
Removable bool
// Read Only media
Readonly bool
// Software Write Protect
SWP bool
// Use thin-provisioning for this LUN
Thinprovisioning bool
// Logical Unit online
Online bool
// Descrptor format sense data supported
SenseFormat bool
// Logical blocks per physical block exponent
Lbppbe int
// Do not update it automatically when the backing file changes
NoLbppbe int
// Lowest aligned LBA
LowestAlignedLBA int
}
var (
DefaultBlockShift int = 9
DefaultSenseBufferSize int = 252
)
var (
SAM_STAT_GOOD byte = 0x00
SAM_STAT_CHECK_CONDITION byte = 0x02
SAM_STAT_CONDITION_MET byte = 0x04
SAM_STAT_BUSY byte = 0x08
SAM_STAT_INTERMEDIATE byte = 0x10
SAM_STAT_INTERMEDIATE_CONDITION_MET byte = 0x14
SAM_STAT_RESERVATION_CONFLICT byte = 0x18
SAM_STAT_COMMAND_TERMINATED byte = 0x22
SAM_STAT_TASK_SET_FULL byte = 0x28
SAM_STAT_ACA_ACTIVE byte = 0x30
SAM_STAT_TASK_ABORTED byte = 0x40
)
type SAMStat struct {
Stat byte
Err error
}
var (
SAMStatGood = SAMStat{SAM_STAT_GOOD, nil}
SAMStatCheckCondition = SAMStat{SAM_STAT_CHECK_CONDITION, errors.New("check condition")}
SAMStatConditionMet = SAMStat{SAM_STAT_CONDITION_MET, errors.New("condition met")}
SAMStatBusy = SAMStat{SAM_STAT_BUSY, errors.New("busy")}
SAMStatIntermediate = SAMStat{SAM_STAT_INTERMEDIATE, errors.New("intermediate")}
SAMStatIntermediateConditionMet = SAMStat{SAM_STAT_INTERMEDIATE_CONDITION_MET, errors.New("intermediate condition met")}
SAMStatReservationConflict = SAMStat{SAM_STAT_RESERVATION_CONFLICT, errors.New("reservation conflict")}
SAMStatCommandTerminated = SAMStat{SAM_STAT_COMMAND_TERMINATED, errors.New("command terminated")}
SAMStatTaskSetFull = SAMStat{SAM_STAT_TASK_SET_FULL, errors.New("task set full")}
SAMStatAcaActive = SAMStat{SAM_STAT_ACA_ACTIVE, errors.New("aca active")}
SAMStatTaskAborted = SAMStat{SAM_STAT_TASK_ABORTED, errors.New("task aborted")}
)
type SCSIDeviceType byte
var (
TYPE_DISK SCSIDeviceType = 0x00
TYPE_TAPE SCSIDeviceType = 0x01
TYPE_PRINTER SCSIDeviceType = 0x02
TYPE_PROCESSOR SCSIDeviceType = 0x03
TYPE_WORM SCSIDeviceType = 0x04
TYPE_MMC SCSIDeviceType = 0x05
TYPE_SCANNER SCSIDeviceType = 0x06
TYPE_MOD SCSIDeviceType = 0x07
TYPE_MEDIUM_CHANGER SCSIDeviceType = 0x08
TYPE_COMM SCSIDeviceType = 0x09
TYPE_RAID SCSIDeviceType = 0x0c
TYPE_ENCLOSURE SCSIDeviceType = 0x0d
TYPE_RBC SCSIDeviceType = 0x0e
TYPE_OSD SCSIDeviceType = 0x11
TYPE_NO_LUN SCSIDeviceType = 0x7f
TYPE_PT SCSIDeviceType = 0xff
)
type SCSILu struct {
FD int
Address uint64
Size uint64
Lun uint64
Path string
BsoFlags int
BlockShift uint
ReserveID uint64
Attrs SCSILuPhyAttribute
}

70
pkg/port/interfaces.go Normal file
View File

@@ -0,0 +1,70 @@
package port
import (
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/port/iscsit"
)
type SCSITargetDriver interface {
Init() error
Exit() error
CreateTarget(target *api.SCSITarget) error
DestroyTarget(target *api.SCSITarget) error
CreatePortal(name string) error
DestroyPortal(name string) error
CreateLu(lu *api.SCSILu) error
GetLu(lun uint8) (uint64, error)
ProcessCommand(buf []byte) ([]byte, error)
CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error
}
type fakeSCSITargetDriver struct {
api.SCSITargetDriverCommon
}
func (fake *fakeSCSITargetDriver) Init() error {
return nil
}
func (fake *fakeSCSITargetDriver) Exit() error {
return nil
}
func (fake *fakeSCSITargetDriver) CreateTarget(target *api.SCSITarget) error {
return nil
}
func (fake *fakeSCSITargetDriver) DestroyTarget(target *api.SCSITarget) error {
return nil
}
func (fake *fakeSCSITargetDriver) CreatePortal(name string) error {
return nil
}
func (fake *fakeSCSITargetDriver) DestroyPortal(name string) error {
return nil
}
func (fake *fakeSCSITargetDriver) CreateLu(lu *api.SCSILu) error {
return nil
}
func (fake *fakeSCSITargetDriver) GetLun(lun uint8) (uint64, error) {
return 0, nil
}
func (fake *fakeSCSITargetDriver) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
return nil
}
func (fake *fakeSCSITargetDriver) ProcessCommand(buf []byte) ([]byte, error) {
return []byte(""), nil
}
func NewTargetDriver(driver string, tgt *api.SCSITarget) SCSITargetDriver {
if driver == "iscsi" {
return iscsit.NewISCSITarget(tgt)
}
return nil
}

View File

@@ -1,26 +1,8 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package packet implements the iSCSI PDU packet format as specified in
// rfc7143 section 11.
package iscsit
import (
"bytes"
"fmt"
"io"
"strings"
)
@@ -70,15 +52,7 @@ var opCodeMap = map[OpCode]string{
OpReject: "Reject",
}
func (c OpCode) String() string {
s := opCodeMap[c]
if s == "" {
s = fmt.Sprintf("Unknown Code: %x", int(c))
}
return s
}
type Message struct {
type ISCSICommand struct {
OpCode OpCode
RawHeader []byte
DataLen int
@@ -109,8 +83,8 @@ type Message struct {
// SCSI commands
ExpectedDataLen uint32
CDB []byte
Status Status
SCSIResponse Response
Status byte
SCSIResponse byte
// Data-In
HasStatus bool
@@ -118,23 +92,23 @@ type Message struct {
BufferOffset uint32
}
func (m *Message) Bytes() []byte {
switch m.OpCode {
func (cmd *ISCSICommand) Bytes() []byte {
switch cmd.OpCode {
case OpLoginResp:
return m.loginRespBytes()
return cmd.loginRespBytes()
case OpLogoutResp:
return m.logoutRespBytes()
return cmd.logoutRespBytes()
case OpSCSIResp:
return m.scsiCmdRespBytes()
return cmd.scsiCmdRespBytes()
case OpSCSIIn:
return m.dataInBytes()
return cmd.dataInBytes()
}
return nil
}
func (m *Message) String() string {
func (m *ISCSICommand) String() string {
var s []string
s = append(s, fmt.Sprintf("Op: %v", m.OpCode))
s = append(s, fmt.Sprintf("Op: %v", opCodeMap[m.OpCode]))
s = append(s, fmt.Sprintf("Final = %v", m.Final))
s = append(s, fmt.Sprintf("Immediate = %v", m.Immediate))
s = append(s, fmt.Sprintf("Data Segment Length = %d", m.DataLen))
@@ -171,37 +145,6 @@ func (m *Message) String() string {
return strings.Join(s, "\n")
}
// Response composes a reply to the given message with the appropriate bits set.
func (m *Message) Response(r *Message) {
r.TaskTag = m.TaskTag
r.ConnID = m.ConnID
r.ISID = m.ISID
}
func Next(r io.Reader) (*Message, error) {
buf := make([]byte, 48) // TODO: sync.Pool
if _, err := io.ReadFull(r, buf); err != nil {
return nil, err
}
m, err := parseHeader(buf)
if err != nil {
return nil, err
}
m.RawHeader = buf
if m.DataLen > 0 {
dl := m.DataLen
for dl%4 > 0 {
dl++
}
data := make([]byte, dl)
if _, err := io.ReadFull(r, data); err != nil {
return nil, err
}
m.RawData = data[:m.DataLen]
}
return m, nil
}
// parseUint parses the given slice as a network-byte-ordered integer. If
// there are more than 8 bytes in data, it overflows.
func ParseUint(data []byte) uint64 {
@@ -220,13 +163,12 @@ func MarshalUint64(i uint64) []byte {
}
return data
}
func parseHeader(data []byte) (*Message, error) {
func parseHeader(data []byte) (*ISCSICommand, error) {
if len(data) != 48 {
return nil, fmt.Errorf("garbled header")
}
// TODO: sync.Pool
m := &Message{}
m := &ISCSICommand{}
m.Immediate = 0x40&data[0] == 0x40
m.OpCode = OpCode(data[0] & 0x3f)
m.Final = 0x80&data[1] == 0x80
@@ -274,3 +216,197 @@ func parseHeader(data []byte) (*Message, error) {
}
return m, nil
}
func (m *ISCSICommand) scsiCmdRespBytes() []byte {
// rfc7143 11.4
buf := &bytes.Buffer{}
buf.WriteByte(byte(OpSCSIResp))
buf.WriteByte(0x80) // 11.4.1 = wtf
buf.WriteByte(byte(m.SCSIResponse))
buf.WriteByte(byte(m.Status))
// Skip through to byte 16
for i := 0; i < 3*4; i++ {
buf.WriteByte(0x00)
}
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
for i := 0; i < 4; i++ {
buf.WriteByte(0x00)
}
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
for i := 0; i < 3*4; i++ {
buf.WriteByte(0x00)
}
return buf.Bytes()
}
func (m *ISCSICommand) dataInBytes() []byte {
// rfc7143 11.7
buf := &bytes.Buffer{}
buf.WriteByte(byte(OpSCSIIn))
var b byte
b = 0x80
if m.HasStatus {
b |= 0x01
}
buf.WriteByte(b)
buf.WriteByte(0x00)
if m.HasStatus {
b = byte(m.Status)
}
buf.WriteByte(b)
buf.WriteByte(0x00) // 4
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
buf.WriteByte(0x00)
buf.WriteByte(byte(m.LUN))
// Skip through to byte 16
for i := 0; i < 6; i++ {
buf.WriteByte(0x00)
}
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
for i := 0; i < 4; i++ {
// 11.7.4
buf.WriteByte(0xff)
}
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.DataSN))[4:])
buf.Write(MarshalUint64(uint64(m.BufferOffset))[4:])
for i := 0; i < 4; i++ {
buf.WriteByte(0x00)
}
buf.Write(m.RawData)
dl := len(m.RawData)
for dl%4 > 0 {
dl++
buf.WriteByte(0x00)
}
return buf.Bytes()
}
type InquiryData struct {
PeripheralQualifier int
PeripheralType int
Removable bool
Version int
SupportsACA bool
Hierarchical bool
SupportsSCC bool
HasACC bool
TargetGroupSupport int
ThirdPartyCopy bool
Protect bool
EnclosureServices bool
Multiport bool
MediaChanger bool
Vendor [8]byte
Product [16]byte
RevisionLevel [4]byte
SerialNumber uint64
}
func (id *InquiryData) bytes() []byte {
buf := &bytes.Buffer{}
var b byte
b = (uint8(id.PeripheralQualifier) << 5) & 0xe0
b |= uint8(id.PeripheralType) & 0x1f
buf.WriteByte(b)
b = 0
if id.Removable {
b = 0x80
}
buf.WriteByte(b)
buf.WriteByte(byte(id.Version))
b = 0x02
if id.SupportsACA {
b |= 0x20
}
if id.Hierarchical {
b |= 0x10
}
buf.WriteByte(b)
buf.WriteByte(0x00)
// byte 5
b = 0
if id.SupportsSCC {
b |= 0x80
}
if id.HasACC {
b |= 0x40
}
b |= byte(id.TargetGroupSupport) << 4 & 0x30
if id.ThirdPartyCopy {
b |= 0x08
}
if id.Protect {
b |= 0x01
}
buf.WriteByte(b)
// byte 6
b = 0
if id.EnclosureServices {
b |= 0x40
}
if id.Multiport {
b |= 0x10
}
if id.MediaChanger {
b |= 0x08
}
buf.WriteByte(b)
buf.WriteByte(0x02)
buf.Write(id.Vendor[:])
buf.Write(id.Product[:])
buf.Write(id.RevisionLevel[:])
buf.Write(MarshalUint64(id.SerialNumber))
for i := 0; i < 12; i++ {
buf.WriteByte(0x00)
}
data := buf.Bytes()
data[4] = byte(len(data) - 4)
return data
}
type Capacity struct {
LBA uint64
Blocksize uint32
ProtectionType uint8
PIExponent uint8
LogicalExponent uint8
ThinProvisioned bool
ThinProvReturnsZeros bool
LowestLBA uint16
}
func (c *Capacity) bytes() []byte {
// table 111
// http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
buf := &bytes.Buffer{}
buf.Write(MarshalUint64(c.LBA))
buf.Write(MarshalUint64(uint64(c.Blocksize))[4:])
var b byte
if c.ProtectionType > 0 {
b |= 0x01
b |= c.ProtectionType << 1
b &= 0x0f
}
buf.WriteByte(b)
b = c.PIExponent << 4
b |= c.LogicalExponent
buf.WriteByte(b)
lowLBA := MarshalUint64(uint64(c.LowestLBA))[6:]
lowLBA[0] &= 0x3f
if c.ThinProvisioned {
lowLBA[0] &= 0x80
}
if c.ThinProvReturnsZeros {
lowLBA[0] &= 0x40
}
return buf.Bytes()
}

View File

@@ -17,6 +17,11 @@ limitations under the License.
// iSCSI Target Driver
package iscsit
import (
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
type ISCSIDiscoveryMethod string
var (
@@ -33,9 +38,11 @@ type ISCSIRedirectInfo struct {
}
type ISCSITarget struct {
*api.SCSITarget
api.SCSITargetDriverCommon
Sessions []*ISCSISession
SessionParam []ISCSISessionParam
TID int
Alias string
MaxSessions int
RedirectInfo ISCSIRedirectInfo
@@ -44,41 +51,128 @@ type ISCSITarget struct {
NopCount int
}
type ISCSITargetDriver struct {
SCSITargetDriver
func NewISCSITarget(target *api.SCSITarget) *ISCSITarget {
return &ISCSITarget{
SCSITarget: target,
}
}
func (tgt *ISCSITargetDriver) Init() error {
func (tgt *ISCSITarget) Init() error {
return nil
}
func (tgt *ISCSITargetDriver) Exit() error {
func (tgt *ISCSITarget) Exit() error {
return nil
}
func (tgt *ISCSITargetDriver) CreateTarget(target *SCSITarget) error {
func (tgt *ISCSITarget) CreateTarget(target *api.SCSITarget) error {
return nil
}
func (tgt *ISCSITargetDriver) DestroyTarget(target *SCSITarget) error {
func (tgt *ISCSITarget) DestroyTarget(target *api.SCSITarget) error {
return nil
}
func (tgt *ISCSITargetDriver) CreatePortal(name string) error {
func (tgt *ISCSITarget) CreatePortal(name string) error {
return nil
}
func (tgt *ISCSITargetDriver) DestroyPortal(name string) error {
func (tgt *ISCSITarget) DestroyPortal(name string) error {
return nil
}
func (tgt *ISCSITargetDriver) CreateLu(lu *SCSILu) error {
func (tgt *ISCSITarget) CreateLu(lu *api.SCSILu) error {
return nil
}
func (tgt *ISCSITargetDriver) GetLun(lun uint8) (uint64, error) {
func (tgt *ISCSITarget) GetLu(lun uint8) (uint64, error) {
return 0, nil
}
func (tgt *ISCSITargetDriver) CommandNotify(nid uint64, result int, cmd *SCSICommand) error {
func (tgt *ISCSITarget) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
return nil
}
func (tgt *ISCSITarget) ProcessCommand(buf []byte) ([]byte, error) {
b := make([]byte, 48) // TODO: sync.Pool
b = buf[0:48]
m, err := parseHeader(b)
if err != nil {
return nil, err
}
m.RawHeader = b
if m.DataLen > 0 {
m.RawData = buf[48:m.DataLen]
}
resp := &ISCSICommand{}
switch m.OpCode {
case OpLoginReq:
resp = &ISCSICommand{
OpCode: OpLoginResp,
Transit: true,
NSG: FullFeaturePhase,
StatSN: m.ExpStatSN,
TaskTag: m.TaskTag,
ExpCmdSN: m.CmdSN,
MaxCmdSN: m.CmdSN,
RawData: util.MarshalKVText(map[string]string{
"HeaderDigest": "None",
"DataDigest": "None",
}),
}
break
case OpSCSICmd:
resp = &ISCSICommand{
OpCode: OpSCSIResp,
Final: true,
StatSN: m.ExpStatSN,
TaskTag: m.TaskTag,
ExpCmdSN: m.CmdSN + 1,
MaxCmdSN: m.CmdSN + 10,
}
switch api.SCSICommandType(m.CDB[0]) {
case api.TEST_UNIT_READY:
// test unit ready
resp.Status = api.SAM_STAT_GOOD
resp.SCSIResponse = 0x01
break
case api.READ_CAPACITY:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
var data []byte
data = append(data, MarshalUint64(uint64(0))[4:]...)
data = append(data, MarshalUint64(uint64(0))[4:]...)
resp.RawData = data
break
case api.SERVICE_ACTION_IN:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
sa := m.CDB[1] & 0x1f
switch sa {
case 0x10:
c := &Capacity{}
resp.RawData = c.bytes()
}
break
case api.INQUIRY:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
alloc := int(ParseUint(m.CDB[3:5]))
inq := &InquiryData{
Vendor: [8]byte{'1', '1', 'c', 'a', 'n', 's'},
Product: [16]byte{'c', 'o', 'f', 'f', 'e', 'e'},
RevisionLevel: [4]byte{'1', '.', '0'},
SerialNumber: 52,
}
if len(inq.bytes()) >= alloc {
resp.RawData = inq.bytes()[:alloc]
} else {
resp.RawData = inq.bytes()
}
break
default:
break
}
}
b1 := resp.Bytes()
return b1, nil
}

View File

@@ -1,24 +1,8 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iscsit
import "bytes"
func (m *Message) loginRespBytes() []byte {
func (m *ISCSICommand) loginRespBytes() []byte {
// rfc7143 11.13
buf := &bytes.Buffer{}
// byte 0

View File

@@ -1,24 +1,8 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iscsit
import "bytes"
func (m *Message) logoutRespBytes() []byte {
func (m *ISCSICommand) logoutRespBytes() []byte {
buf := &bytes.Buffer{}
buf.WriteByte(byte(OpLogoutResp))
buf.WriteByte(0x80)

View File

@@ -42,6 +42,9 @@ type ISCSISession struct {
Rdma int
}
type ISCSIHeader struct {
}
type ISCSIPdu struct {
Bhs ISCSIHeader
AhsSize uint
@@ -78,7 +81,7 @@ func NewISCSISession() (*ISCSISession, error) {
tsih += uint16(b[0]) << 8
tsih += uint16(b[1])
return &Session{
return &ISCSISession{
Tsih: tsih,
}, nil
}

View File

@@ -16,7 +16,11 @@ limitations under the License.
package scsi
import "fmt"
import (
"fmt"
"github.com/gostor/gotgt/pkg/api"
)
type BaseBackingStore struct {
Name string
@@ -25,11 +29,11 @@ type BaseBackingStore struct {
}
type BackingStore interface {
Open(dev *SCSILu, path string, fd *int, size *uint64) error
Close(dev *SCSILu) error
Init(dev *SCSILu, Opts string) error
Exit(dev *SCSILu) error
CommandSubmit(cmd *SCSICommand) error
Open(dev *api.SCSILu, path string, fd *int, size *uint64) error
Close(dev *api.SCSILu) error
Init(dev *api.SCSILu, Opts string) error
Exit(dev *api.SCSILu) error
CommandSubmit(cmd *api.SCSICommand) error
}
type BackingStoreFunc func() (BackingStore, error)
@@ -55,22 +59,22 @@ type fakeBackingStore struct {
BaseBackingStore
}
func (fake *fakeBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error {
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string, fd *int, size *uint64) error {
return nil
}
func (fake *fakeBackingStore) Close(dev *SCSILu) error {
func (fake *fakeBackingStore) Close(dev *api.SCSILu) error {
return nil
}
func (fake *fakeBackingStore) Init(dev *SCSILu, Opts string) error {
func (fake *fakeBackingStore) Init(dev *api.SCSILu, Opts string) error {
return nil
}
func (fake *fakeBackingStore) Exit(dev *SCSILu) error {
func (fake *fakeBackingStore) Exit(dev *api.SCSILu) error {
return nil
}
func (fake *fakeBackingStore) CommandSubmit(cmd *SCSICommand) error {
func (fake *fakeBackingStore) CommandSubmit(cmd *api.SCSICommand) error {
return nil
}

View File

@@ -16,7 +16,11 @@ limitations under the License.
package backingstore
import "github.com/gostor/gotgt/pkg/scsi"
import (
"github.com/gostor/gotgt/pkg/scsi"
"github.com/golang/glog"
)
func init() {
scsi.RegisterBackingStore("null", new)
@@ -35,6 +39,7 @@ func new() (scsi.BackingStore, error) {
}
func (bs *NullBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error {
glog.V(1).Infof("NULL backing store open, size: %d", size)
return nil
}
@@ -51,5 +56,6 @@ func (bs *NullBackingStore) Exit(dev *SCSILu) error {
}
func (bs *NullBackingStore) CommandSubmit(cmd *SCSICommand) error {
cmd.Result = SAM_STAT_GOOD
return nil
}

View File

@@ -16,111 +16,6 @@ limitations under the License.
package scsi
type SCSICommandType byte
var (
TEST_UNIT_READY SCSICommandType = 0x00
REZERO_UNIT SCSICommandType = 0x01
REQUEST_SENSE SCSICommandType = 0x03
FORMAT_UNIT SCSICommandType = 0x04
READ_BLOCK_LIMITS SCSICommandType = 0x05
REASSIGN_BLOCKS SCSICommandType = 0x07
INITIALIZE_ELEMENT_STATUS SCSICommandType = 0x07
READ_6 SCSICommandType = 0x08
WRITE_6 SCSICommandType = 0x0a
SEEK_6 SCSICommandType = 0x0b
READ_REVERSE SCSICommandType = 0x0f
WRITE_FILEMARKS SCSICommandType = 0x10
SPACE SCSICommandType = 0x11
INQUIRY SCSICommandType = 0x12
RECOVER_BUFFERED_DATA SCSICommandType = 0x14
MODE_SELECT SCSICommandType = 0x15
RESERVE SCSICommandType = 0x16
RELEASE SCSICommandType = 0x17
COPY SCSICommandType = 0x18
ERASE SCSICommandType = 0x19
MODE_SENSE SCSICommandType = 0x1a
START_STOP SCSICommandType = 0x1b
RECEIVE_DIAGNOSTIC SCSICommandType = 0x1c
SEND_DIAGNOSTIC SCSICommandType = 0x1d
ALLOW_MEDIUM_REMOVAL SCSICommandType = 0x1e
SET_WINDOW SCSICommandType = 0x24
READ_CAPACITY SCSICommandType = 0x25
READ_10 SCSICommandType = 0x28
WRITE_10 SCSICommandType = 0x2a
SEEK_10 SCSICommandType = 0x2b
POSITION_TO_ELEMENT SCSICommandType = 0x2b
WRITE_VERIFY SCSICommandType = 0x2e
VERIFY_10 SCSICommandType = 0x2f
SEARCH_HIGH SCSICommandType = 0x30
SEARCH_EQUAL SCSICommandType = 0x31
SEARCH_LOW SCSICommandType = 0x32
SET_LIMITS SCSICommandType = 0x33
PRE_FETCH_10 SCSICommandType = 0x34
READ_POSITION SCSICommandType = 0x34
SYNCHRONIZE_CACHE SCSICommandType = 0x35
LOCK_UNLOCK_CACHE SCSICommandType = 0x36
READ_DEFECT_DATA SCSICommandType = 0x37
INITIALIZE_ELEMENT_STATUS_WITH_RANGE SCSICommandType = 0x37
MEDIUM_SCAN SCSICommandType = 0x38
COMPARE SCSICommandType = 0x39
COPY_VERIFY SCSICommandType = 0x3a
WRITE_BUFFER SCSICommandType = 0x3b
READ_BUFFER SCSICommandType = 0x3c
UPDATE_BLOCK SCSICommandType = 0x3d
READ_LONG SCSICommandType = 0x3e
WRITE_LONG SCSICommandType = 0x3f
CHANGE_DEFINITION SCSICommandType = 0x40
WRITE_SAME SCSICommandType = 0x41
UNMAP SCSICommandType = 0x42
READ_TOC SCSICommandType = 0x43
GET_CONFIGURATION SCSICommandType = 0x46
LOG_SELECT SCSICommandType = 0x4c
LOG_SENSE SCSICommandType = 0x4d
READ_DISK_INFO SCSICommandType = 0x51
READ_TRACK_INFO SCSICommandType = 0x52
MODE_SELECT_10 SCSICommandType = 0x55
RESERVE_10 SCSICommandType = 0x56
RELEASE_10 SCSICommandType = 0x57
MODE_SENSE_10 SCSICommandType = 0x5a
CLOSE_TRACK SCSICommandType = 0x5b
READ_BUFFER_CAP SCSICommandType = 0x5c
PERSISTENT_RESERVE_IN SCSICommandType = 0x5e
PERSISTENT_RESERVE_OUT SCSICommandType = 0x5f
VARLEN_CDB SCSICommandType = 0x7f
READ_16 SCSICommandType = 0x88
COMPARE_AND_WRITE SCSICommandType = 0x89
WRITE_16 SCSICommandType = 0x8a
ORWRITE_16 SCSICommandType = 0x8b
WRITE_VERIFY_16 SCSICommandType = 0x8e
VERIFY_16 SCSICommandType = 0x8f
PRE_FETCH_16 SCSICommandType = 0x90
SYNCHRONIZE_CACHE_16 SCSICommandType = 0x91
WRITE_SAME_16 SCSICommandType = 0x93
SERVICE_ACTION_IN SCSICommandType = 0x9e
SAI_READ_CAPACITY_16 SCSICommandType = 0x10
SAI_GET_LBA_STATUS SCSICommandType = 0x12
REPORT_LUNS SCSICommandType = 0xa0
MAINT_PROTOCOL_IN SCSICommandType = 0xa3
MOVE_MEDIUM SCSICommandType = 0xa5
EXCHANGE_MEDIUM SCSICommandType = 0xa6
READ_12 SCSICommandType = 0xa8
WRITE_12 SCSICommandType = 0xaa
GET_PERFORMACE SCSICommandType = 0xac
READ_DVD_STRUCTURE SCSICommandType = 0xad
WRITE_VERIFY_12 SCSICommandType = 0xae
VERIFY_12 SCSICommandType = 0xaf
SEARCH_HIGH_12 SCSICommandType = 0xb0
SEARCH_EQUAL_12 SCSICommandType = 0xb1
SEARCH_LOW_12 SCSICommandType = 0xb2
READ_ELEMENT_STATUS SCSICommandType = 0xb8
SEND_VOLUME_TAG SCSICommandType = 0xb6
SET_STREAMING SCSICommandType = 0xb6
SET_CD_SPEED SCSICommandType = 0xbb
WRITE_LONG_2 SCSICommandType = 0xea
)
type SCSIPRServiceAction byte
type SCSIPRType byte
@@ -153,15 +48,6 @@ var (
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG SCSIPRType = 0x08
)
type SCSIDataDirection int
const (
SCSIDataNone = iota
SCSIDataWrite
SCSIDataRead
SCSIDataBidirection
)
const (
CBD_GROUPID_0 = iota
CBD_GROUPID_1
@@ -184,35 +70,6 @@ const (
CDB_GROUP7 = 0 /* vendor specific */
)
type SCSIDataBuffer struct {
Buffer uint64
Length uint64
TransferLength uint32
Resid int32
}
type SCSICommand struct {
Target *SCSITarget
DeviceID uint64
Device *SCSILu
State uint64
Direction SCSIDataDirection
InSDBBuffer *SCSIDataBuffer
OutSDBBuffer *SCSIDataBuffer
// Command ITN ID
CommandITNID uint64
Offset uint64
TL uint32
SCB *[]byte
SCBLength int
Lun []uint8
Attribute int
Tag uint64
Result int
SenseBuffer []byte
SenseLength int
}
func SCSICDBGroupID(opcode byte) byte {
return ((opcode >> 5) & 0x7)
}

View File

@@ -16,76 +16,3 @@ limitations under the License.
// Target Driver Interface
package scsi
type SCSITargetDriverState int
const (
// just registered
SCSI_DRIVER_REGD = iota
// initialized ok
SCSI_DRIVER_INIT
// failed to initialize
SCSI_DRIVER_ERR
// exited
SCSI_DRIVER_EXIT
)
type SCSITargetDriver struct {
Name string
State SCSITargetDriverState
DefaultBST string
Targets []*SCSITarget
}
type SCSITargetDriverOps interface {
Init() error
Exit() error
CreateTarget(target *SCSITarget) error
DestroyTarget(target *SCSITarget) error
CreatePortal(name string) error
DestroyPortal(name string) error
CreateLu(lu *SCSILu) error
GetLun(lun uint8) (uint64, error)
CommandNotify(nid uint64, result int, cmd *SCSICommand) error
}
type fakeSCSITargetDriver struct {
SCSITargetDriver
}
func (fake *fakeSCSITargetDriver) Init() error {
return nil
}
func (fake *fakeSCSITargetDriver) Exit() error {
return nil
}
func (fake *fakeSCSITargetDriver) CreateTarget(target *SCSITarget) error {
return nil
}
func (fake *fakeSCSITargetDriver) DestroyTarget(target *SCSITarget) error {
return nil
}
func (fake *fakeSCSITargetDriver) CreatePortal(name string) error {
return nil
}
func (fake *fakeSCSITargetDriver) DestroyPortal(name string) error {
return nil
}
func (fake *fakeSCSITargetDriver) CreateLu(lu *SCSILu) error {
return nil
}
func (fake *fakeSCSITargetDriver) GetLun(lun uint8) (uint64, error) {
return 0, nil
}
func (fake *fakeSCSITargetDriver) CommandNotify(nid uint64, result int, cmd *SCSICommand) error {
return nil
}

View File

@@ -16,22 +16,43 @@ limitations under the License.
package scsi
type SCSIError byte
import "errors"
type SCSIError struct {
errno byte
Err error
}
var (
NO_SENSE SCSIError = 0x00
RECOVERED_ERROR SCSIError = 0x01
NOT_READY SCSIError = 0x02
MEDIUM_ERROR SCSIError = 0x03
HARDWARE_ERROR SCSIError = 0x04
ILLEGAL_REQUEST SCSIError = 0x05
UNIT_ATTENTION SCSIError = 0x06
DATA_PROTECT SCSIError = 0x07
BLANK_CHECK SCSIError = 0x08
COPY_ABORTED SCSIError = 0x0a
ABORTED_COMMAND SCSIError = 0x0b
VOLUME_OVERFLOW SCSIError = 0x0d
MISCOMPARE SCSIError = 0x0e
NO_SENSE byte = 0x00
RECOVERED_ERROR byte = 0x01
NOT_READY byte = 0x02
MEDIUM_ERROR byte = 0x03
HARDWARE_ERROR byte = 0x04
ILLEGAL_REQUEST byte = 0x05
UNIT_ATTENTION byte = 0x06
DATA_PROTECT byte = 0x07
BLANK_CHECK byte = 0x08
COPY_ABORTED byte = 0x0a
ABORTED_COMMAND byte = 0x0b
VOLUME_OVERFLOW byte = 0x0d
MISCOMPARE byte = 0x0e
)
var (
NoSenseError = SCSIError{NO_SENSE, errors.New("no sense")}
RecoveredError = SCSIError{RECOVERED_ERROR, errors.New("recovered error")}
NotReadyError = SCSIError{NOT_READY, errors.New("not ready")}
MediumError = SCSIError{MEDIUM_ERROR, errors.New("medium error")}
HardwareError = SCSIError{HARDWARE_ERROR, errors.New("hardware error")}
IllegalRequestError = SCSIError{ILLEGAL_REQUEST, errors.New("illegal request")}
UnitAttentionError = SCSIError{UNIT_ATTENTION, errors.New("unit attention")}
DataProtectError = SCSIError{DATA_PROTECT, errors.New("data protect")}
BlankCheckError = SCSIError{BLANK_CHECK, errors.New("blank check")}
CopyAbortedError = SCSIError{COPY_ABORTED, errors.New("copy aborted")}
AbortedCommandError = SCSIError{ABORTED_COMMAND, errors.New("aborted command")}
VolumeOverflowError = SCSIError{VOLUME_OVERFLOW, errors.New("volume overflow")}
MiscompareError = SCSIError{MISCOMPARE, errors.New("miscompare")}
)
type SCSISubError uint16

View File

@@ -16,14 +16,22 @@ limitations under the License.
package scsi
type SCSILu struct {
FD int
Address uint64
Size uint64
Lun uint64
Path string
BsoFlags int
BlockShift uint
ReserveID uint64
Target *SCSITarget
import "github.com/gostor/gotgt/pkg/api"
type SCSILuOps struct {
*api.SCSILu
DeviceProtocol SCSIDeviceProtocol
Storage *BackingStore
Target *api.SCSITarget
Attrs api.SCSILuPhyAttribute
// function handler for command performing and finishing
PerformCommand CommandFunc
FinishCommand func(*api.SCSITarget, *api.SCSICommand)
}
func luPreventRemoval(lu *api.SCSILu) bool {
// TODO
return false
}

View File

@@ -17,135 +17,296 @@ limitations under the License.
// SCSI block command processing
package scsi
import (
"encoding/binary"
"github.com/gostor/gotgt/pkg/api"
)
const (
PR_SPECIAL = (1 << 5)
PR_WE_FA = (1 << 4)
PR_EA_FA = (1 << 3)
PR_RR_FR = (1 << 2)
PR_WE_FN = (1 << 1)
PR_EA_FN = (1 << 0)
)
type SBCSCSIDeviceProtocol struct {
BaseSCSIDeviceProtocol
}
func (sbc *SBCSCSIDeviceProtocol) InitLu(lu *SCSILu) error {
func (sbc *SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
return nil
}
func (sbc *SBCSCSIDeviceProtocol) ExitLu(lu *SCSILu) error {
func (sbc *SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
return nil
}
func (sbc *SBCSCSIDeviceProtocol) ConfigLu(lu *SCSILu) error {
func (sbc *SBCSCSIDeviceProtocol) ConfigLu(lu *api.SCSILu) error {
return nil
}
func (sbc *SBCSCSIDeviceProtocol) OnlineLu(lu *SCSILu) error {
func (sbc *SBCSCSIDeviceProtocol) OnlineLu(lu *api.SCSILu) error {
return nil
}
func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *SCSILu) error {
func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *api.SCSILu) error {
return nil
}
func NewSBCDevice() (SBCSCSIDeviceProtocol, error) {
func NewSBCDevice() SBCSCSIDeviceProtocol {
var sbc = SBCSCSIDeviceProtocol{
BaseSCSIDeviceProtocol{Type: TYPE_DISK,
SCSIDeviceOps: make([]SCSIDeviceOperation, 256)},
BaseSCSIDeviceProtocol{
Type: api.TYPE_DISK,
SCSIDeviceOps: make([]SCSIDeviceOperation, 256),
},
}
for i := 0; i <= 256; i++ {
sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0))
}
sbc.SCSIDeviceOps[TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0)
sbc.SCSIDeviceOps[REQUEST_SENSE] = NewSCSIDeviceOperation(SPCRequestSense, nil, 0)
sbc.SCSIDeviceOps[FORMAT_UNIT] = NewSCSIDeviceOperation(SBCFormatUnit, nil, 0)
sbc.SCSIDeviceOps[READ_6] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[WRITE_6] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[INQUIRY] = NewSCSIDeviceOperation(SPCInquiry, nil, 0)
sbc.SCSIDeviceOps[MODE_SELECT] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN)
sbc.SCSIDeviceOps[RESERVE] = NewSCSIDeviceOperation(SBCReserve, nil, 0)
sbc.SCSIDeviceOps[RELEASE] = NewSCSIDeviceOperation(SBCRelease, nil, 0)
sbc.SCSIDeviceOps[api.TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0)
sbc.SCSIDeviceOps[api.REQUEST_SENSE] = NewSCSIDeviceOperation(SPCRequestSense, nil, 0)
sbc.SCSIDeviceOps[api.FORMAT_UNIT] = NewSCSIDeviceOperation(SBCFormatUnit, nil, 0)
sbc.SCSIDeviceOps[api.READ_6] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.WRITE_6] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[api.INQUIRY] = NewSCSIDeviceOperation(SPCInquiry, nil, 0)
sbc.SCSIDeviceOps[api.MODE_SELECT] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN)
sbc.SCSIDeviceOps[api.RESERVE] = NewSCSIDeviceOperation(SBCReserve, nil, 0)
sbc.SCSIDeviceOps[api.RELEASE] = NewSCSIDeviceOperation(SBCRelease, nil, 0)
sbc.SCSIDeviceOps[MODE_SENSE] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
sbc.SCSIDeviceOps[START_STOP] = NewSCSIDeviceOperation(SPCStartStop, nil, PR_SPECIAL)
sbc.SCSIDeviceOps[SEND_DIAGNOSTIC] = NewSCSIDeviceOperation(SPCSendDiagnostics, nil, 0)
sbc.SCSIDeviceOps[api.MODE_SENSE] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
sbc.SCSIDeviceOps[api.START_STOP] = NewSCSIDeviceOperation(SPCStartStop, nil, PR_SPECIAL)
sbc.SCSIDeviceOps[api.SEND_DIAGNOSTIC] = NewSCSIDeviceOperation(SPCSendDiagnostics, nil, 0)
sbc.SCSIDeviceOps[ALLOW_MEDIUM_REMOVAL] = NewSCSIDeviceOperation(SPCPreventAllowMediaRemoval, nil, 0)
sbc.SCSIDeviceOps[READ_CAPACITY] = NewSCSIDeviceOperation(SBCReadCapacity, nil, 0)
sbc.SCSIDeviceOps[READ_10] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[WRITE_10] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
sbc.SCSIDeviceOps[WRITE_VERIFY] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[VERIFY_10] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.ALLOW_MEDIUM_REMOVAL] = NewSCSIDeviceOperation(SPCPreventAllowMediaRemoval, nil, 0)
sbc.SCSIDeviceOps[api.READ_CAPACITY] = NewSCSIDeviceOperation(SBCReadCapacity, nil, 0)
sbc.SCSIDeviceOps[api.READ_10] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.WRITE_10] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
sbc.SCSIDeviceOps[api.WRITE_VERIFY] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.VERIFY_10] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[PRE_FETCH_10] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[SYNCHRONIZE_CACHE] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN)
sbc.SCSIDeviceOps[api.PRE_FETCH_10] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.SYNCHRONIZE_CACHE] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_WE_FA|PR_EA_FA|PR_WE_FN|PR_EA_FN)
sbc.SCSIDeviceOps[WRITE_SAME] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
sbc.SCSIDeviceOps[UNMAP] = NewSCSIDeviceOperation(SBCUnmap, nil, 0)
sbc.SCSIDeviceOps[api.WRITE_SAME] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
sbc.SCSIDeviceOps[api.UNMAP] = NewSCSIDeviceOperation(SBCUnmap, nil, 0)
sbc.SCSIDeviceOps[MODE_SELECT_10] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
sbc.SCSIDeviceOps[MODE_SENSE_10] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_WE_FN|PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.MODE_SELECT_10] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
sbc.SCSIDeviceOps[api.MODE_SENSE_10] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_WE_FN|PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[READ_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[WRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[ORWRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[WRITE_VERIFY_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[VERIFY_16] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.READ_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.WRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[api.ORWRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.WRITE_VERIFY_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.VERIFY_16] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[PRE_FETCH_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[SYNCHRONIZE_CACHE_16] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[WRITE_SAME_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
sbc.SCSIDeviceOps[SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.PRE_FETCH_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.SYNCHRONIZE_CACHE_16] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[api.WRITE_SAME_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
sbc.SCSIDeviceOps[EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[WRITE_VERIFY_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[VERIFY_12] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[api.WRITE_VERIFY_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.VERIFY_12] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
return sbc, nil
return sbc
}
func SBCModeSelect(host int, cmd *SCSICommand) error {
return nil
func SBCModeSelect(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SBCModeSense(host int, cmd *SCSICommand) error {
return nil
func SBCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
// DPOFUA = 0x10
var deviceSpecific uint8 = 0x10
if err := SPCModeSense(host, cmd); err.Err != nil {
return err
}
// If this is a read-only lun, we must set the write protect bit
if cmd.Device.Attrs.Readonly || cmd.Device.Attrs.SWP {
deviceSpecific |= 0x80
}
data := cmd.InSDBBuffer.Buffer
data.Next(2)
if cmd.SCB.Bytes()[0] == 0x1a {
data.WriteByte(deviceSpecific)
} else {
data.Next(1)
data.WriteByte(deviceSpecific)
}
return api.SAMStatGood
}
func SBCFormatUnit(host int, cmd *SCSICommand) error {
return nil
// The FORMAT UNIT command requests that the device server format the medium into application client
// accessible logical blocks as specified in the number of blocks and block length values received
// in the last mode parameter block descriptor in a MODE SELECT command (see SPC-3). In addition,
// the device server may certify the medium and create control structures for the management of the medium and defects.
// The degree that the medium is altered by this command is vendor-specific.
func SBCFormatUnit(host int, cmd *api.SCSICommand) api.SAMStat {
var (
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
)
if err := deviceReserve(cmd); err != nil {
return api.SAMStatReservationConflict
}
if !cmd.Device.Attrs.Online {
key = NOT_READY
asc = ASC_MEDIUM_NOT_PRESENT
goto sense
}
if cmd.Device.Attrs.Readonly || cmd.Device.Attrs.SWP {
key = DATA_PROTECT
asc = ASC_WRITE_PROTECT
goto sense
}
if cmd.SCB.Bytes()[1]&0x80 != 0 {
// we dont support format protection information
goto sense
}
if cmd.SCB.Bytes()[1]&0x10 != 0 {
// we dont support format data
goto sense
}
if cmd.SCB.Bytes()[1]&0x07 != 0 {
// defect list format must be 0
goto sense
}
return api.SAMStatGood
sense:
BuildSenseData(cmd, key, asc)
return api.SAMStatCheckCondition
}
func SBCUnmap(host int, cmd *SCSICommand) error {
return nil
func SBCUnmap(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SBCReadWrite(host int, cmd *SCSICommand) error {
return nil
func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SBCReserve(host int, cmd *SCSICommand) error {
return nil
func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
if err := deviceReserve(cmd); err != nil {
return api.SAMStatReservationConflict
}
return api.SAMStatGood
}
func SBCRelease(host int, cmd *SCSICommand) error {
return nil
func SBCRelease(host int, cmd *api.SCSICommand) api.SAMStat {
if err := deviceRelease(cmd.Target.TID, cmd.CommandITNID, cmd.Device.Lun, false); err != nil {
return api.SAMStatReservationConflict
}
return api.SAMStatGood
}
func SBCReadCapacity(host int, cmd *SCSICommand) error {
return nil
// The READ CAPACITY (10) command requests that the device server transfer 8 bytes of parameter data
// describing the capacity and medium format of the direct-access block device to the data-in buffer.
// This command may be processed as if it has a HEAD OF QUEUE task attribute. If the logical unit supports
// protection information, the application client should use the READ CAPACITY (16) command instead of
// the READ CAPACITY (10) command.
func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
var (
scb = cmd.SCB.Bytes()
key = ILLEGAL_REQUEST
asc = ASC_LUN_NOT_SUPPORTED
data = cmd.InSDBBuffer.Buffer
bshift = cmd.Device.BlockShift
size = cmd.Device.Size >> bshift
)
if cmd.Device.Attrs.Removable && !cmd.Device.Attrs.Online {
key = NOT_READY
asc = ASC_MEDIUM_NOT_PRESENT
goto sense
}
if (scb[8]&0x1 == 0) && (scb[2]|scb[3]|scb[4]|scb[5]) != 0 {
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
if cmd.InSDBBuffer.Length < 8 {
goto overflow
}
// data[0] = (size >> 32) ? __cpu_to_be32(0xffffffff) : __cpu_to_be32(size - 1);
if size>>32 != 0 {
binary.Write(data, binary.BigEndian, uint32(0xffffffff))
} else {
binary.Write(data, binary.BigEndian, uint32(size-1))
}
// data[1] = __cpu_to_be32(1U << bshift);
binary.Write(data, binary.BigEndian, uint32(1<<bshift))
overflow:
cmd.InSDBBuffer.Resid = 8
return api.SAMStatGood
sense:
cmd.InSDBBuffer.Resid = 0
BuildSenseData(cmd, key, asc)
return api.SAMStatCheckCondition
}
func SBCVerify(host int, cmd *SCSICommand) error {
return nil
// The VERIFY (10) command requests that the device server verify the specified logical block(s) on the medium.
func SBCVerify(host int, cmd *api.SCSICommand) api.SAMStat {
var (
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
)
if cmd.Device.Attrs.Removable && !cmd.Device.Attrs.Online {
key = NOT_READY
asc = ASC_MEDIUM_NOT_PRESENT
goto sense
}
if cmd.SCB.Bytes()[1]&0xe0 != 0 {
// We only support protection information type 0
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
if cmd.SCB.Bytes()[1]&0x02 == 0 {
// no data compare with the media
return api.SAMStatGood
}
// TODO
sense:
cmd.InSDBBuffer.Resid = 0
BuildSenseData(cmd, key, asc)
return api.SAMStatCheckCondition
}
func SBCReadCapacity16(host int, cmd *SCSICommand) error {
return nil
func SBCReadCapacity16(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SBCGetLbaStatus(host int, cmd *SCSICommand) error {
return nil
func SBCGetLbaStatus(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SBCServiceAction(host int, cmd *SCSICommand) error {
return nil
func SBCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SBCSyncCache(host int, cmd *SCSICommand) error {
return nil
// The SYNCHRONIZE CACHE (10) command requests that the device server ensure that
// the specified logical blocks have their most recent data values recorded in
// non-volatile cache and/or on the medium, based on the SYNC_NV bit.
func SBCSyncCache(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}

56
pkg/scsi/sbc_test.go Normal file
View File

@@ -0,0 +1,56 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// SCSI block command processing
package scsi
import "testing"
func TestSBCModeSelect(t *testing.T) {
}
func TestSBCModeSense(t *testing.T) {
}
func TestSBCFormatUnit(t *testing.T) {
}
func TestSBCUnmap(t *testing.T) {
}
func TestSBCReadWrite(t *testing.T) {
}
func TestSBCReserve(t *testing.T) {
}
func TestSBCRelease(t *testing.T) {
}
func TestSBCReadCapacity(t *testing.T) {
}
func TestSBCVerify(t *testing.T) {
}
func TestSBCReadCapacity16(t *testing.T) {
}
func TestSBCGetLbaStatus(t *testing.T) {
}
func TestSBCSyncCache(t *testing.T) {
}

View File

@@ -16,50 +16,9 @@ limitations under the License.
package scsi
var (
DefaultBlockShift int = 9
DefaultSenseBufferSize int = 252
)
import "github.com/gostor/gotgt/pkg/api"
type SAMStat byte
type SCSIDeviceType byte
var (
SAM_STAT_GOOD SAMStat = 0x00
SAM_STAT_CHECK_CONDITION SAMStat = 0x02
SAM_STAT_CONDITION_MET SAMStat = 0x04
SAM_STAT_BUSY SAMStat = 0x08
SAM_STAT_INTERMEDIATE SAMStat = 0x10
SAM_STAT_INTERMEDIATE_CONDITION_MET SAMStat = 0x14
SAM_STAT_RESERVATION_CONFLICT SAMStat = 0x18
SAM_STAT_COMMAND_TERMINATED SAMStat = 0x22
SAM_STAT_TASK_SET_FULL SAMStat = 0x28
SAM_STAT_ACA_ACTIVE SAMStat = 0x30
SAM_STAT_TASK_ABORTED SAMStat = 0x40
)
var (
TYPE_DISK SCSIDeviceType = 0x00
TYPE_TAPE SCSIDeviceType = 0x01
TYPE_PRINTER SCSIDeviceType = 0x02
TYPE_PROCESSOR SCSIDeviceType = 0x03
TYPE_WORM SCSIDeviceType = 0x04
TYPE_MMC SCSIDeviceType = 0x05
TYPE_SCANNER SCSIDeviceType = 0x06
TYPE_MOD SCSIDeviceType = 0x07
TYPE_MEDIUM_CHANGER SCSIDeviceType = 0x08
TYPE_COMM SCSIDeviceType = 0x09
TYPE_RAID SCSIDeviceType = 0x0c
TYPE_ENCLOSURE SCSIDeviceType = 0x0d
TYPE_RBC SCSIDeviceType = 0x0e
TYPE_OSD SCSIDeviceType = 0x11
TYPE_NO_LUN SCSIDeviceType = 0x7f
TYPE_PT SCSIDeviceType = 0xff
)
type CommandFunc func(host int, cmd *SCSICommand) error
type CommandFunc func(host int, cmd *api.SCSICommand) api.SAMStat
type SCSIServiceAction struct {
ServiceAction uint32
@@ -73,16 +32,16 @@ type SCSIDeviceOperation struct {
}
type BaseSCSIDeviceProtocol struct {
Type SCSIDeviceType
Type api.SCSIDeviceType
SCSIDeviceOps []SCSIDeviceOperation
}
type SCSIDeviceProtocol interface {
InitLu(lu *SCSILu) error
ExitLu(lu *SCSILu) error
ConfigLu(lu *SCSILu) error
OnlineLu(lu *SCSILu) error
OfflineLu(lu *SCSILu) error
InitLu(lu *api.SCSILu) error
ExitLu(lu *api.SCSILu) error
ConfigLu(lu *api.SCSILu) error
OnlineLu(lu *api.SCSILu) error
OfflineLu(lu *api.SCSILu) error
}
func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
@@ -92,3 +51,33 @@ func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCS
PRConflictBits: pr,
}
}
func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
senseBuffer := cmd.SenseBuffer
if cmd.Device.Attrs.SenseFormat {
// descriptor format
// current, not deferred
senseBuffer.WriteByte(0x72)
senseBuffer.WriteByte(key)
senseBuffer.WriteByte((byte(asc) >> 8) & 0xff)
senseBuffer.WriteByte(byte(asc) & 0xff)
cmd.SenseLength = 8
} else {
// fixed format
var length uint32 = 0xa
// current, not deferred
senseBuffer.WriteByte(0x70)
senseBuffer.WriteByte(0x00)
senseBuffer.WriteByte(key)
for i := 0; i < 4; i++ {
senseBuffer.WriteByte(0x00)
}
senseBuffer.WriteByte(byte(length))
for i := 0; i < 4; i++ {
senseBuffer.WriteByte(0x00)
}
senseBuffer.WriteByte((byte(asc) >> 8) & 0xff)
senseBuffer.WriteByte(byte(asc) & 0xff)
cmd.SenseLength = length + 8
}
}

View File

@@ -17,6 +17,15 @@ limitations under the License.
// SCSI primary command processing
package scsi
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
/*
* Protocol Identifier Values
*
@@ -100,78 +109,275 @@ const (
DESG_SCSI
)
func SPCIllegalOp(host int, cmd *SCSICommand) error {
func SPCIllegalOp(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCLuOffline(lu *api.SCSILu) error {
lu.Attrs.Online = true
return nil
}
func SPCInquiry(host int, cmd *SCSICommand) error {
func SPCLuOnline(lu *api.SCSILu) error {
if luPreventRemoval(lu) {
return fmt.Errorf("lu(%s) prevent removal", lu.Lun)
}
lu.Attrs.Online = false
return nil
}
func SPCReportLuns(host int, cmd *SCSICommand) error {
return nil
func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCStartStop(host int, cmd *SCSICommand) error {
return nil
func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
var (
remainLength uint32
actualLength uint32 = 8
availLength uint32 = 0
allocationLength uint32
data *bytes.Buffer
scb *bytes.Buffer = cmd.SCB
)
// Get Allocation Length
allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10])
if allocationLength < 16 {
goto sense
}
if cmd.InSDBBuffer.Length < allocationLength {
goto sense
}
data = cmd.InSDBBuffer.Buffer
remainLength = allocationLength - 8
availLength = 8 * uint32(len(cmd.Target.Devices))
binary.Write(data, binary.BigEndian, availLength)
cmd.InSDBBuffer.Resid = int32(actualLength)
// Skip through to byte 8, Reserved
for i := 0; i < 4; i++ {
data.WriteByte(0x00)
}
for _, lu := range cmd.Target.Devices {
if remainLength > 0 {
lun := lu.Lun
if lun > 0xff {
lun = 0x1 << 30
} else {
lun = 0
}
lun = (0x3fff & lun) << 16
lun = uint64(lun << 32)
binary.Write(data, binary.BigEndian, lun)
remainLength -= 8
}
}
return api.SAMStatGood
sense:
cmd.InSDBBuffer.Resid = 0
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
return api.SAMStatCheckCondition
}
func SPCTestUnit(host int, cmd *SCSICommand) error {
return nil
func SPCStartStop(host int, cmd *api.SCSICommand) api.SAMStat {
var (
pwrcnd, loej, start byte
)
if err := deviceReserve(cmd); err != nil {
return api.SAMStatReservationConflict
}
cmd.InSDBBuffer.Resid = 0
scb := cmd.SCB.Bytes()
pwrcnd = scb[4] & 0xf0
if pwrcnd != 0 {
return api.SAMStatGood
}
loej = scb[4] & 0x02
start = scb[4] & 0x01
if loej != 0 && start == 0 && cmd.Device.Attrs.Removable {
if luPreventRemoval(cmd.Device) {
if cmd.Device.Attrs.Online {
// online == media is present
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_MEDIUM_REMOVAL_PREVENTED)
} else {
// !online == media is not present
BuildSenseData(cmd, NOT_READY, ASC_MEDIUM_REMOVAL_PREVENTED)
}
return api.SAMStatCheckCondition
}
SPCLuOffline(cmd.Device)
}
if loej != 0 && start != 0 && cmd.Device.Attrs.Removable {
SPCLuOnline(cmd.Device)
}
return api.SAMStatGood
}
func SPCPreventAllowMediaRemoval(host int, cmd *SCSICommand) error {
return nil
func SPCTestUnit(host int, cmd *api.SCSICommand) api.SAMStat {
if err := deviceReserve(cmd); err != nil {
return api.SAMStatReservationConflict
}
if cmd.Device.Attrs.Online {
return api.SAMStatGood
}
if cmd.Device.Attrs.Removable {
BuildSenseData(cmd, NOT_READY, ASC_MEDIUM_NOT_PRESENT)
} else {
BuildSenseData(cmd, NOT_READY, ASC_BECOMING_READY)
}
return api.SAMStatCheckCondition
}
func SPCModeSense(host int, cmd *SCSICommand) error {
return nil
func SPCPreventAllowMediaRemoval(host int, cmd *api.SCSICommand) api.SAMStat {
if err := deviceReserve(cmd); err != nil {
return api.SAMStatReservationConflict
}
// PREVENT_MASK = 0x03
cmd.ITNexusLuInfo.Prevent = int(cmd.SCB.Bytes()[4] & 0x03)
return api.SAMStatGood
}
func SPCServiceAction(host int, cmd *SCSICommand) error {
return nil
// SPCModeSense Implement SCSI op MODE SENSE(6) and MODE SENSE(10)
// Reference : SPC4r11
// 6.11 - MODE SENSE(6)
// 6.12 - MODE SENSE(10)
func SPCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCPRReadKeys(host int, cmd *SCSICommand) error {
return nil
func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat {
// we only support SELF-TEST==1
if cmd.SCB.Bytes()[1]&0x04 == 0 {
goto sense
}
return api.SAMStatGood
sense:
cmd.InSDBBuffer.Resid = 0
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
return api.SAMStatCheckCondition
}
func SPCPRReadReservation(host int, cmd *SCSICommand) error {
return nil
// This is useful for the various commands using the SERVICE ACTION format.
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
// TODO
return api.SAMStatGood
}
func SPCPRReportCapabilities(host int, cmd *SCSICommand) error {
return nil
func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
allocationLength := util.GetUnalignedUint32(cmd.SCB.Bytes()[7:9])
if allocationLength < 8 {
goto sense
}
if cmd.InSDBBuffer.Length < allocationLength {
goto sense
}
// TODO
sense:
cmd.InSDBBuffer.Resid = 0
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
return api.SAMStatCheckCondition
}
func SPCPRRegister(host int, cmd *SCSICommand) error {
return nil
func SPCPRReadReservation(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCPRReserve(host int, cmd *SCSICommand) error {
return nil
func SPCPRReportCapabilities(host int, cmd *api.SCSICommand) api.SAMStat {
var (
buf []byte = make([]byte, 8)
availLength uint32 = 8
actualLength uint32 = 0
data *bytes.Buffer = cmd.InSDBBuffer.Buffer
)
allocationLength := util.GetUnalignedUint32(cmd.SCB.Bytes()[7:9])
if allocationLength < 8 {
goto sense
}
if cmd.InSDBBuffer.Length < allocationLength {
goto sense
}
binary.BigEndian.PutUint16(buf[0:2], uint16(8))
// Persistent Reservation Type Mask format
// Type Mask Valid (TMV)
buf[3] |= 0x80
// PR_TYPE_EXCLUSIVE_ACCESS_ALLREG
buf[4] |= 0x80
// PR_TYPE_EXCLUSIVE_ACCESS_REGONLY
buf[4] |= 0x40
// PR_TYPE_WRITE_EXCLUSIVE_REGONLY
buf[4] |= 0x20
// PR_TYPE_EXCLUSIVE_ACCESS
buf[4] |= 0x08
// PR_TYPE_WRITE_EXCLUSIVE
buf[4] |= 0x02
// PR_TYPE_EXCLUSIVE_ACCESS_ALLREG
buf[5] |= 0x01
if err := binary.Write(data, binary.BigEndian, buf); err != nil {
goto sense
} else {
actualLength = availLength
}
cmd.InSDBBuffer.Resid = int32(actualLength)
return api.SAMStatGood
sense:
cmd.InSDBBuffer.Resid = 0
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
return api.SAMStatCheckCondition
}
func SPCPRRelease(host int, cmd *SCSICommand) error {
return nil
func SPCPRRegister(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCPRClear(host int, cmd *SCSICommand) error {
return nil
func SPCPRReserve(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCPRPreempt(host int, cmd *SCSICommand) error {
return nil
func SPCPRRelease(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCPRRegisterAndMove(host int, cmd *SCSICommand) error {
return nil
func SPCPRClear(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCRequestSense(host int, cmd *SCSICommand) error {
return nil
func SPCPRPreempt(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCSendDiagnostics(host int, cmd *SCSICommand) error {
return nil
func SPCPRRegisterAndMove(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
}
func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
var (
allocationLength uint32
actualLength uint32
)
allocationLength = util.GetUnalignedUint32(cmd.SCB.Bytes()[4:8])
if allocationLength > cmd.InSDBBuffer.Length {
allocationLength = cmd.InSDBBuffer.Length
}
BuildSenseData(cmd, NO_SENSE, NO_ADDITIONAL_SENSE)
if cmd.SenseLength < allocationLength {
actualLength = cmd.SenseLength
} else {
actualLength = allocationLength
}
binary.Write(cmd.InSDBBuffer.Buffer, binary.BigEndian, cmd.SenseBuffer.Bytes()[0:actualLength])
cmd.InSDBBuffer.Resid = int32(actualLength)
// reset sense buffer in cmnd
cmd.SenseBuffer = &bytes.Buffer{}
cmd.SenseLength = 0
return api.SAMStatGood
}

73
pkg/scsi/spc_test.go Normal file
View File

@@ -0,0 +1,73 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// SCSI primary command processing test
package scsi
import (
"bytes"
"encoding/binary"
"testing"
)
// Test SPCReportLuns function
func TestSPCReportLuns(t *testing.T) {
// make a fake REPORT_LUNS command
cmd := new(SCSICommand)
device := new(SCSILu)
cmd.Device = device
lu := new(SCSILu)
target := new(SCSITarget)
target.Devices = append(target.Devices, *lu)
cmd.Target = target
cmd.SCB = &bytes.Buffer{}
cmd.SenseBuffer = &bytes.Buffer{}
cmd.InSDBBuffer.Length = 16
cmd.InSDBBuffer.Buffer = &bytes.Buffer{}
cmd.SCB.WriteByte(byte(REPORT_LUNS))
for i := 0; i < 5; i++ {
cmd.SCB.WriteByte(0x00)
}
binary.Write(cmd.SCB, binary.BigEndian, uint32(16))
if err := SPCReportLuns(0, cmd); err.Err != nil {
t.Errorf("Expected not error, but got %v", err)
}
cmd.InSDBBuffer.Length = 10
if err := SPCReportLuns(0, cmd); err.Err == nil {
t.Error("Expected error, but got nothing")
}
cmd.SCB = &bytes.Buffer{}
cmd.SCB.WriteByte(byte(REPORT_LUNS))
for i := 0; i < 5; i++ {
cmd.SCB.WriteByte(0x00)
}
binary.Write(cmd.SCB, binary.BigEndian, uint32(10))
if err := SPCReportLuns(0, cmd); err.Err == nil {
t.Error("Expected error, but got nothing")
}
}
func TestSPCStartStop(t *testing.T) {
}
func TestSPCTestUnit(t *testing.T) {
}
func TestSPCPreventAllowMediaRemoval(t *testing.T) {
}

View File

@@ -16,25 +16,48 @@ limitations under the License.
package scsi
type SCSITargetState int
import (
"fmt"
var (
TargetOnline SCSITargetState = 1
TargetReady SCSITargetState = 2
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/port"
)
const (
PR_SPECIAL = (1 << 5)
PR_WE_FA = (1 << 4)
PR_EA_FA = (1 << 3)
PR_RR_FR = (1 << 2)
PR_WE_FN = (1 << 1)
PR_EA_FN = (1 << 0)
)
func NewTarget(tid int, driverName, name string) (*api.SCSITarget, error) {
// verify the target ID
type SCSITarget struct {
Name string
TID int
LID int
State SCSITargetState
// verify the target's Name
// verify the low level driver
var target = &api.SCSITarget{Name: name, TID: tid}
var tgt = port.NewTargetDriver(driverName, target)
target.SCSITargetDriver = tgt
return target, nil
}
func deviceReserve(cmd *api.SCSICommand) error {
var lu *api.SCSILu
for _, dev := range cmd.Target.Devices {
if dev.Lun == cmd.Device.Lun {
lu = &dev
break
}
}
if lu == nil {
glog.Errorf("invalid target and lun %d %s", cmd.Target.TID, cmd.Device.Lun)
return nil
}
if lu.ReserveID != 0 && lu.ReserveID != cmd.CommandITNID {
glog.Errorf("already reserved %d, %d", lu.ReserveID, cmd.CommandITNID)
return fmt.Errorf("already reserved")
}
lu.ReserveID = cmd.CommandITNID
return nil
}
func deviceRelease(tid int, itn, lun uint64, force bool) error {
// TODO
return nil
}

View File

@@ -16,6 +16,20 @@ limitations under the License.
package util
import "encoding/binary"
func GetUnalignedUint16(u8 []uint8) uint16 {
return binary.BigEndian.Uint16(u8)
}
func GetUnalignedUint32(u8 []uint8) uint32 {
return binary.BigEndian.Uint32(u8)
}
func GetUnalignedUint64(u8 []uint8) uint64 {
return binary.BigEndian.Uint64(u8)
}
// ParseKVText parses iSCSI key value data.
func ParseKVText(txt []byte) map[string]string {
m := make(map[string]string)