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. 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.
``` ## Build
# 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>.
``` ```
$ 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 ( import (
"net" "net"
"os"
"reflect"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/scsi"
) )
func main() { func main() {
_, err := net.Listen("tcp", ":3260") l, err := net.Listen("tcp", ":3260")
if err != nil { if err != nil {
glog.Error(err) 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 package iscsit
import ( import (
"bytes"
"fmt" "fmt"
"io"
"strings" "strings"
) )
@@ -70,15 +52,7 @@ var opCodeMap = map[OpCode]string{
OpReject: "Reject", OpReject: "Reject",
} }
func (c OpCode) String() string { type ISCSICommand struct {
s := opCodeMap[c]
if s == "" {
s = fmt.Sprintf("Unknown Code: %x", int(c))
}
return s
}
type Message struct {
OpCode OpCode OpCode OpCode
RawHeader []byte RawHeader []byte
DataLen int DataLen int
@@ -109,8 +83,8 @@ type Message struct {
// SCSI commands // SCSI commands
ExpectedDataLen uint32 ExpectedDataLen uint32
CDB []byte CDB []byte
Status Status Status byte
SCSIResponse Response SCSIResponse byte
// Data-In // Data-In
HasStatus bool HasStatus bool
@@ -118,23 +92,23 @@ type Message struct {
BufferOffset uint32 BufferOffset uint32
} }
func (m *Message) Bytes() []byte { func (cmd *ISCSICommand) Bytes() []byte {
switch m.OpCode { switch cmd.OpCode {
case OpLoginResp: case OpLoginResp:
return m.loginRespBytes() return cmd.loginRespBytes()
case OpLogoutResp: case OpLogoutResp:
return m.logoutRespBytes() return cmd.logoutRespBytes()
case OpSCSIResp: case OpSCSIResp:
return m.scsiCmdRespBytes() return cmd.scsiCmdRespBytes()
case OpSCSIIn: case OpSCSIIn:
return m.dataInBytes() return cmd.dataInBytes()
} }
return nil return nil
} }
func (m *Message) String() string { func (m *ISCSICommand) String() string {
var s []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("Final = %v", m.Final))
s = append(s, fmt.Sprintf("Immediate = %v", m.Immediate)) s = append(s, fmt.Sprintf("Immediate = %v", m.Immediate))
s = append(s, fmt.Sprintf("Data Segment Length = %d", m.DataLen)) 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") 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 // parseUint parses the given slice as a network-byte-ordered integer. If
// there are more than 8 bytes in data, it overflows. // there are more than 8 bytes in data, it overflows.
func ParseUint(data []byte) uint64 { func ParseUint(data []byte) uint64 {
@@ -220,13 +163,12 @@ func MarshalUint64(i uint64) []byte {
} }
return data return data
} }
func parseHeader(data []byte) (*ISCSICommand, error) {
func parseHeader(data []byte) (*Message, error) {
if len(data) != 48 { if len(data) != 48 {
return nil, fmt.Errorf("garbled header") return nil, fmt.Errorf("garbled header")
} }
// TODO: sync.Pool // TODO: sync.Pool
m := &Message{} m := &ISCSICommand{}
m.Immediate = 0x40&data[0] == 0x40 m.Immediate = 0x40&data[0] == 0x40
m.OpCode = OpCode(data[0] & 0x3f) m.OpCode = OpCode(data[0] & 0x3f)
m.Final = 0x80&data[1] == 0x80 m.Final = 0x80&data[1] == 0x80
@@ -274,3 +216,197 @@ func parseHeader(data []byte) (*Message, error) {
} }
return m, nil 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 // iSCSI Target Driver
package iscsit package iscsit
import (
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
type ISCSIDiscoveryMethod string type ISCSIDiscoveryMethod string
var ( var (
@@ -33,9 +38,11 @@ type ISCSIRedirectInfo struct {
} }
type ISCSITarget struct { type ISCSITarget struct {
*api.SCSITarget
api.SCSITargetDriverCommon
Sessions []*ISCSISession Sessions []*ISCSISession
SessionParam []ISCSISessionParam SessionParam []ISCSISessionParam
TID int
Alias string Alias string
MaxSessions int MaxSessions int
RedirectInfo ISCSIRedirectInfo RedirectInfo ISCSIRedirectInfo
@@ -44,41 +51,128 @@ type ISCSITarget struct {
NopCount int NopCount int
} }
type ISCSITargetDriver struct { func NewISCSITarget(target *api.SCSITarget) *ISCSITarget {
SCSITargetDriver return &ISCSITarget{
SCSITarget: target,
}
} }
func (tgt *ISCSITargetDriver) Init() error { func (tgt *ISCSITarget) Init() error {
return nil return nil
} }
func (tgt *ISCSITargetDriver) Exit() error { func (tgt *ISCSITarget) Exit() error {
return nil return nil
} }
func (tgt *ISCSITargetDriver) CreateTarget(target *SCSITarget) error { func (tgt *ISCSITarget) CreateTarget(target *api.SCSITarget) error {
return nil return nil
} }
func (tgt *ISCSITargetDriver) DestroyTarget(target *SCSITarget) error { func (tgt *ISCSITarget) DestroyTarget(target *api.SCSITarget) error {
return nil return nil
} }
func (tgt *ISCSITargetDriver) CreatePortal(name string) error { func (tgt *ISCSITarget) CreatePortal(name string) error {
return nil return nil
} }
func (tgt *ISCSITargetDriver) DestroyPortal(name string) error { func (tgt *ISCSITarget) DestroyPortal(name string) error {
return nil return nil
} }
func (tgt *ISCSITargetDriver) CreateLu(lu *SCSILu) error { func (tgt *ISCSITarget) CreateLu(lu *api.SCSILu) error {
return nil return nil
} }
func (tgt *ISCSITargetDriver) GetLun(lun uint8) (uint64, error) { func (tgt *ISCSITarget) GetLu(lun uint8) (uint64, error) {
return 0, nil 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 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 package iscsit
import "bytes" import "bytes"
func (m *Message) loginRespBytes() []byte { func (m *ISCSICommand) loginRespBytes() []byte {
// rfc7143 11.13 // rfc7143 11.13
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
// byte 0 // 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 package iscsit
import "bytes" import "bytes"
func (m *Message) logoutRespBytes() []byte { func (m *ISCSICommand) logoutRespBytes() []byte {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteByte(byte(OpLogoutResp)) buf.WriteByte(byte(OpLogoutResp))
buf.WriteByte(0x80) buf.WriteByte(0x80)

View File

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

View File

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

View File

@@ -16,7 +16,11 @@ limitations under the License.
package backingstore package backingstore
import "github.com/gostor/gotgt/pkg/scsi" import (
"github.com/gostor/gotgt/pkg/scsi"
"github.com/golang/glog"
)
func init() { func init() {
scsi.RegisterBackingStore("null", new) 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 { 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 return nil
} }
@@ -51,5 +56,6 @@ func (bs *NullBackingStore) Exit(dev *SCSILu) error {
} }
func (bs *NullBackingStore) CommandSubmit(cmd *SCSICommand) error { func (bs *NullBackingStore) CommandSubmit(cmd *SCSICommand) error {
cmd.Result = SAM_STAT_GOOD
return nil return nil
} }

View File

@@ -16,111 +16,6 @@ limitations under the License.
package scsi 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 SCSIPRServiceAction byte
type SCSIPRType byte type SCSIPRType byte
@@ -153,15 +48,6 @@ var (
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG SCSIPRType = 0x08 PR_TYPE_EXCLUSIVE_ACCESS_ALLREG SCSIPRType = 0x08
) )
type SCSIDataDirection int
const (
SCSIDataNone = iota
SCSIDataWrite
SCSIDataRead
SCSIDataBidirection
)
const ( const (
CBD_GROUPID_0 = iota CBD_GROUPID_0 = iota
CBD_GROUPID_1 CBD_GROUPID_1
@@ -184,35 +70,6 @@ const (
CDB_GROUP7 = 0 /* vendor specific */ 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 { func SCSICDBGroupID(opcode byte) byte {
return ((opcode >> 5) & 0x7) return ((opcode >> 5) & 0x7)
} }

View File

@@ -16,76 +16,3 @@ limitations under the License.
// Target Driver Interface // Target Driver Interface
package scsi 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 package scsi
type SCSIError byte import "errors"
type SCSIError struct {
errno byte
Err error
}
var ( var (
NO_SENSE SCSIError = 0x00 NO_SENSE byte = 0x00
RECOVERED_ERROR SCSIError = 0x01 RECOVERED_ERROR byte = 0x01
NOT_READY SCSIError = 0x02 NOT_READY byte = 0x02
MEDIUM_ERROR SCSIError = 0x03 MEDIUM_ERROR byte = 0x03
HARDWARE_ERROR SCSIError = 0x04 HARDWARE_ERROR byte = 0x04
ILLEGAL_REQUEST SCSIError = 0x05 ILLEGAL_REQUEST byte = 0x05
UNIT_ATTENTION SCSIError = 0x06 UNIT_ATTENTION byte = 0x06
DATA_PROTECT SCSIError = 0x07 DATA_PROTECT byte = 0x07
BLANK_CHECK SCSIError = 0x08 BLANK_CHECK byte = 0x08
COPY_ABORTED SCSIError = 0x0a COPY_ABORTED byte = 0x0a
ABORTED_COMMAND SCSIError = 0x0b ABORTED_COMMAND byte = 0x0b
VOLUME_OVERFLOW SCSIError = 0x0d VOLUME_OVERFLOW byte = 0x0d
MISCOMPARE SCSIError = 0x0e 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 type SCSISubError uint16

View File

@@ -16,14 +16,22 @@ limitations under the License.
package scsi package scsi
type SCSILu struct { import "github.com/gostor/gotgt/pkg/api"
FD int
Address uint64 type SCSILuOps struct {
Size uint64 *api.SCSILu
Lun uint64
Path string DeviceProtocol SCSIDeviceProtocol
BsoFlags int Storage *BackingStore
BlockShift uint Target *api.SCSITarget
ReserveID uint64 Attrs api.SCSILuPhyAttribute
Target *SCSITarget
// 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 // SCSI block command processing
package scsi 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 { type SBCSCSIDeviceProtocol struct {
BaseSCSIDeviceProtocol BaseSCSIDeviceProtocol
} }
func (sbc *SBCSCSIDeviceProtocol) InitLu(lu *SCSILu) error { func (sbc *SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
return nil return nil
} }
func (sbc *SBCSCSIDeviceProtocol) ExitLu(lu *SCSILu) error { func (sbc *SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
return nil return nil
} }
func (sbc *SBCSCSIDeviceProtocol) ConfigLu(lu *SCSILu) error { func (sbc *SBCSCSIDeviceProtocol) ConfigLu(lu *api.SCSILu) error {
return nil return nil
} }
func (sbc *SBCSCSIDeviceProtocol) OnlineLu(lu *SCSILu) error { func (sbc *SBCSCSIDeviceProtocol) OnlineLu(lu *api.SCSILu) error {
return nil return nil
} }
func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *SCSILu) error { func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *api.SCSILu) error {
return nil return nil
} }
func NewSBCDevice() (SBCSCSIDeviceProtocol, error) { func NewSBCDevice() SBCSCSIDeviceProtocol {
var sbc = SBCSCSIDeviceProtocol{ var sbc = SBCSCSIDeviceProtocol{
BaseSCSIDeviceProtocol{Type: TYPE_DISK, BaseSCSIDeviceProtocol{
SCSIDeviceOps: make([]SCSIDeviceOperation, 256)}, Type: api.TYPE_DISK,
SCSIDeviceOps: make([]SCSIDeviceOperation, 256),
},
} }
for i := 0; i <= 256; i++ { for i := 0; i <= 256; i++ {
sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)) sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0))
} }
sbc.SCSIDeviceOps[TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0) sbc.SCSIDeviceOps[api.TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0)
sbc.SCSIDeviceOps[REQUEST_SENSE] = NewSCSIDeviceOperation(SPCRequestSense, nil, 0) sbc.SCSIDeviceOps[api.REQUEST_SENSE] = NewSCSIDeviceOperation(SPCRequestSense, nil, 0)
sbc.SCSIDeviceOps[FORMAT_UNIT] = NewSCSIDeviceOperation(SBCFormatUnit, nil, 0) sbc.SCSIDeviceOps[api.FORMAT_UNIT] = NewSCSIDeviceOperation(SBCFormatUnit, nil, 0)
sbc.SCSIDeviceOps[READ_6] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) sbc.SCSIDeviceOps[api.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[api.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[api.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[api.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[api.RESERVE] = NewSCSIDeviceOperation(SBCReserve, nil, 0)
sbc.SCSIDeviceOps[RELEASE] = NewSCSIDeviceOperation(SBCRelease, 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[api.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[api.START_STOP] = NewSCSIDeviceOperation(SPCStartStop, nil, PR_SPECIAL)
sbc.SCSIDeviceOps[SEND_DIAGNOSTIC] = NewSCSIDeviceOperation(SPCSendDiagnostics, nil, 0) sbc.SCSIDeviceOps[api.SEND_DIAGNOSTIC] = NewSCSIDeviceOperation(SPCSendDiagnostics, nil, 0)
sbc.SCSIDeviceOps[ALLOW_MEDIUM_REMOVAL] = NewSCSIDeviceOperation(SPCPreventAllowMediaRemoval, nil, 0) sbc.SCSIDeviceOps[api.ALLOW_MEDIUM_REMOVAL] = NewSCSIDeviceOperation(SPCPreventAllowMediaRemoval, nil, 0)
sbc.SCSIDeviceOps[READ_CAPACITY] = NewSCSIDeviceOperation(SBCReadCapacity, nil, 0) sbc.SCSIDeviceOps[api.READ_CAPACITY] = NewSCSIDeviceOperation(SBCReadCapacity, nil, 0)
sbc.SCSIDeviceOps[READ_10] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) sbc.SCSIDeviceOps[api.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[api.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[api.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.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[api.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.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[api.WRITE_SAME] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
sbc.SCSIDeviceOps[UNMAP] = NewSCSIDeviceOperation(SBCUnmap, 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[api.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[api.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[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[PERSISTENT_RESERVE_OUT] = 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[api.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[api.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[api.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[api.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.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[api.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[api.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[api.WRITE_SAME_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
sbc.SCSIDeviceOps[SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0) sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0) sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
sbc.SCSIDeviceOps[EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0) sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) sbc.SCSIDeviceOps[api.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[api.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[api.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.VERIFY_12] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
return sbc, nil return sbc
} }
func SBCModeSelect(host int, cmd *SCSICommand) error { func SBCModeSelect(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SBCModeSense(host int, cmd *SCSICommand) error { func SBCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
return nil // 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 { // The FORMAT UNIT command requests that the device server format the medium into application client
return nil // 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 { func SBCUnmap(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SBCReadWrite(host int, cmd *SCSICommand) error { func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SBCReserve(host int, cmd *SCSICommand) error { func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
return nil if err := deviceReserve(cmd); err != nil {
return api.SAMStatReservationConflict
}
return api.SAMStatGood
} }
func SBCRelease(host int, cmd *SCSICommand) error { func SBCRelease(host int, cmd *api.SCSICommand) api.SAMStat {
return nil 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 { // The READ CAPACITY (10) command requests that the device server transfer 8 bytes of parameter data
return nil // 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 { // The VERIFY (10) command requests that the device server verify the specified logical block(s) on the medium.
return nil 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 { func SBCReadCapacity16(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SBCGetLbaStatus(host int, cmd *SCSICommand) error { func SBCGetLbaStatus(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SBCServiceAction(host int, cmd *SCSICommand) error { func SBCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SBCSyncCache(host int, cmd *SCSICommand) error { // The SYNCHRONIZE CACHE (10) command requests that the device server ensure that
return nil // 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 package scsi
var ( import "github.com/gostor/gotgt/pkg/api"
DefaultBlockShift int = 9
DefaultSenseBufferSize int = 252
)
type SAMStat byte type CommandFunc func(host int, cmd *api.SCSICommand) api.SAMStat
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 SCSIServiceAction struct { type SCSIServiceAction struct {
ServiceAction uint32 ServiceAction uint32
@@ -73,16 +32,16 @@ type SCSIDeviceOperation struct {
} }
type BaseSCSIDeviceProtocol struct { type BaseSCSIDeviceProtocol struct {
Type SCSIDeviceType Type api.SCSIDeviceType
SCSIDeviceOps []SCSIDeviceOperation SCSIDeviceOps []SCSIDeviceOperation
} }
type SCSIDeviceProtocol interface { type SCSIDeviceProtocol interface {
InitLu(lu *SCSILu) error InitLu(lu *api.SCSILu) error
ExitLu(lu *SCSILu) error ExitLu(lu *api.SCSILu) error
ConfigLu(lu *SCSILu) error ConfigLu(lu *api.SCSILu) error
OnlineLu(lu *SCSILu) error OnlineLu(lu *api.SCSILu) error
OfflineLu(lu *SCSILu) error OfflineLu(lu *api.SCSILu) error
} }
func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation { func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
@@ -92,3 +51,33 @@ func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCS
PRConflictBits: pr, 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 // SCSI primary command processing
package scsi package scsi
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
/* /*
* Protocol Identifier Values * Protocol Identifier Values
* *
@@ -100,78 +109,275 @@ const (
DESG_SCSI 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 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 return nil
} }
func SPCReportLuns(host int, cmd *SCSICommand) error { func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SPCStartStop(host int, cmd *SCSICommand) error { func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
return nil 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 { func SPCStartStop(host int, cmd *api.SCSICommand) api.SAMStat {
return nil 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 { func SPCTestUnit(host int, cmd *api.SCSICommand) api.SAMStat {
return nil 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 { func SPCPreventAllowMediaRemoval(host int, cmd *api.SCSICommand) api.SAMStat {
return nil 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 { // SPCModeSense Implement SCSI op MODE SENSE(6) and MODE SENSE(10)
return nil // 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 { func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat {
return nil // 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 { // This is useful for the various commands using the SERVICE ACTION format.
return nil func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
// TODO
return api.SAMStatGood
} }
func SPCPRReportCapabilities(host int, cmd *SCSICommand) error { func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
return nil 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 { func SPCPRReadReservation(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SPCPRReserve(host int, cmd *SCSICommand) error { func SPCPRReportCapabilities(host int, cmd *api.SCSICommand) api.SAMStat {
return nil 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 { func SPCPRRegister(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SPCPRClear(host int, cmd *SCSICommand) error { func SPCPRReserve(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SPCPRPreempt(host int, cmd *SCSICommand) error { func SPCPRRelease(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SPCPRRegisterAndMove(host int, cmd *SCSICommand) error { func SPCPRClear(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SPCRequestSense(host int, cmd *SCSICommand) error { func SPCPRPreempt(host int, cmd *api.SCSICommand) api.SAMStat {
return nil return api.SAMStatGood
} }
func SPCSendDiagnostics(host int, cmd *SCSICommand) error { func SPCPRRegisterAndMove(host int, cmd *api.SCSICommand) api.SAMStat {
return nil 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 package scsi
type SCSITargetState int import (
"fmt"
var ( "github.com/golang/glog"
TargetOnline SCSITargetState = 1 "github.com/gostor/gotgt/pkg/api"
TargetReady SCSITargetState = 2 "github.com/gostor/gotgt/pkg/port"
) )
const ( func NewTarget(tid int, driverName, name string) (*api.SCSITarget, error) {
PR_SPECIAL = (1 << 5) // verify the target ID
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 SCSITarget struct { // verify the target's Name
Name string
TID int // verify the low level driver
LID int var target = &api.SCSITarget{Name: name, TID: tid}
State SCSITargetState 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 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. // ParseKVText parses iSCSI key value data.
func ParseKVText(txt []byte) map[string]string { func ParseKVText(txt []byte) map[string]string {
m := make(map[string]string) m := make(map[string]string)