diff --git a/pkg/scsi/cmd.go b/pkg/scsi/cmd.go index 1d22add..0c80d5d 100644 --- a/pkg/scsi/cmd.go +++ b/pkg/scsi/cmd.go @@ -16,6 +16,8 @@ limitations under the License. package scsi +import "bytes" + type SCSICommandType byte var ( @@ -185,8 +187,8 @@ const ( ) type SCSIDataBuffer struct { - Buffer uint64 - Length uint64 + Buffer *bytes.Buffer + Length uint32 TransferLength uint32 Resid int32 } @@ -197,20 +199,22 @@ type SCSICommand struct { Device *SCSILu State uint64 Direction SCSIDataDirection - InSDBBuffer *SCSIDataBuffer - OutSDBBuffer *SCSIDataBuffer + 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 + CommandITNID uint64 + Offset uint64 + TL uint32 + SCB *bytes.Buffer + SCBLength int + Lun []uint8 + Attribute int + Tag uint64 + Result int + SenseBuffer *bytes.Buffer + SenseLength uint32 + ITNexus *ITNexus + ITNexusLuInfo *ITNexusLuInfo } func SCSICDBGroupID(opcode byte) byte { diff --git a/pkg/scsi/drivers.go b/pkg/scsi/drivers.go index 595d674..61c2428 100644 --- a/pkg/scsi/drivers.go +++ b/pkg/scsi/drivers.go @@ -47,7 +47,7 @@ type SCSITargetDriverOps interface { DestroyPortal(name string) error CreateLu(lu *SCSILu) error - GetLun(lun uint8) (uint64, error) + GetLu(lun uint8) (uint64, error) CommandNotify(nid uint64, result int, cmd *SCSICommand) error } diff --git a/pkg/scsi/error.go b/pkg/scsi/error.go index a2b10af..e2b4d70 100644 --- a/pkg/scsi/error.go +++ b/pkg/scsi/error.go @@ -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 diff --git a/pkg/scsi/lun.go b/pkg/scsi/lun.go index d85dfe8..ce6724b 100644 --- a/pkg/scsi/lun.go +++ b/pkg/scsi/lun.go @@ -16,14 +16,58 @@ 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 +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 +} + +type SCSILu struct { + FD int + Address uint64 + Size uint64 + Lun uint64 + Path string + BsoFlags int + BlockShift uint + ReserveID uint64 + DeviceProtocol SCSIDeviceProtocol + Storage *BackingStore + Target *SCSITarget + Attrs SCSILuPhyAttribute + + // function handler for command performing and finishing + PerformCommand CommandFunc + FinishCommand func(*SCSITarget, *SCSICommand) +} + +func luPreventRemoval(lu *SCSILu) bool { + // TODO + return false } diff --git a/pkg/scsi/sbc.go b/pkg/scsi/sbc.go index 3e75230..721244f 100644 --- a/pkg/scsi/sbc.go +++ b/pkg/scsi/sbc.go @@ -17,6 +17,8 @@ limitations under the License. // SCSI block command processing package scsi +import "encoding/binary" + type SBCSCSIDeviceProtocol struct { BaseSCSIDeviceProtocol } @@ -37,10 +39,12 @@ func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *SCSILu) error { return nil } -func NewSBCDevice() (SBCSCSIDeviceProtocol, error) { +func NewSBCDevice() SBCSCSIDeviceProtocol { var sbc = SBCSCSIDeviceProtocol{ - BaseSCSIDeviceProtocol{Type: TYPE_DISK, - SCSIDeviceOps: make([]SCSIDeviceOperation, 256)}, + BaseSCSIDeviceProtocol{ + Type: TYPE_DISK, + SCSIDeviceOps: make([]SCSIDeviceOperation, 256), + }, } for i := 0; i <= 256; i++ { sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)) @@ -95,57 +99,201 @@ func NewSBCDevice() (SBCSCSIDeviceProtocol, error) { 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) - return sbc, nil + return sbc } -func SBCModeSelect(host int, cmd *SCSICommand) error { - return nil +func SBCModeSelect(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SBCModeSense(host int, cmd *SCSICommand) error { - return nil +func SBCModeSense(host int, cmd *SCSICommand) 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 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 *SCSICommand) SAMStat { + var ( + key = ILLEGAL_REQUEST + asc = ASC_INVALID_FIELD_IN_CDB + ) + + if err := deviceReserve(cmd); err != nil { + return 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 SAMStatGood +sense: + BuildSenseData(cmd, key, asc) + return SAMStatCheckCondition } -func SBCUnmap(host int, cmd *SCSICommand) error { - return nil +func SBCUnmap(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SBCReadWrite(host int, cmd *SCSICommand) error { - return nil +func SBCReadWrite(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SBCReserve(host int, cmd *SCSICommand) error { - return nil +func SBCReserve(host int, cmd *SCSICommand) SAMStat { + if err := deviceReserve(cmd); err != nil { + return SAMStatReservationConflict + } + return SAMStatGood } -func SBCRelease(host int, cmd *SCSICommand) error { - return nil +func SBCRelease(host int, cmd *SCSICommand) SAMStat { + if err := deviceRelease(cmd.Target.TID, cmd.CommandITNID, cmd.Device.Lun, false); err != nil { + return SAMStatReservationConflict + } + + return 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 *SCSICommand) 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<> 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 + } +} diff --git a/pkg/scsi/spc.go b/pkg/scsi/spc.go index 9504bc2..7bcbb4c 100644 --- a/pkg/scsi/spc.go +++ b/pkg/scsi/spc.go @@ -17,6 +17,14 @@ limitations under the License. // SCSI primary command processing package scsi +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/gostor/gotgt/pkg/util" +) + /* * Protocol Identifier Values * @@ -100,78 +108,275 @@ const ( DESG_SCSI ) -func SPCIllegalOp(host int, cmd *SCSICommand) error { +func SPCIllegalOp(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood +} + +func SPCLuOffline(lu *SCSILu) error { + lu.Attrs.Online = true return nil } -func SPCInquiry(host int, cmd *SCSICommand) error { +func SPCLuOnline(lu *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 *SCSICommand) SAMStat { + return SAMStatGood } -func SPCStartStop(host int, cmd *SCSICommand) error { - return nil +func SPCReportLuns(host int, cmd *SCSICommand) 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 SAMStatGood +sense: + cmd.InSDBBuffer.Resid = 0 + BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) + return SAMStatCheckCondition } -func SPCTestUnit(host int, cmd *SCSICommand) error { - return nil +func SPCStartStop(host int, cmd *SCSICommand) SAMStat { + var ( + pwrcnd, loej, start byte + ) + if err := deviceReserve(cmd); err != nil { + return SAMStatReservationConflict + } + + cmd.InSDBBuffer.Resid = 0 + scb := cmd.SCB.Bytes() + pwrcnd = scb[4] & 0xf0 + if pwrcnd != 0 { + return 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 SAMStatCheckCondition + } + SPCLuOffline(cmd.Device) + } + if loej != 0 && start != 0 && cmd.Device.Attrs.Removable { + SPCLuOnline(cmd.Device) + } + + return SAMStatGood } -func SPCPreventAllowMediaRemoval(host int, cmd *SCSICommand) error { - return nil +func SPCTestUnit(host int, cmd *SCSICommand) SAMStat { + if err := deviceReserve(cmd); err != nil { + return SAMStatReservationConflict + } + if cmd.Device.Attrs.Online { + return SAMStatGood + } + if cmd.Device.Attrs.Removable { + BuildSenseData(cmd, NOT_READY, ASC_MEDIUM_NOT_PRESENT) + } else { + BuildSenseData(cmd, NOT_READY, ASC_BECOMING_READY) + } + + return SAMStatCheckCondition } -func SPCModeSense(host int, cmd *SCSICommand) error { - return nil +func SPCPreventAllowMediaRemoval(host int, cmd *SCSICommand) SAMStat { + if err := deviceReserve(cmd); err != nil { + return SAMStatReservationConflict + } + // PREVENT_MASK = 0x03 + cmd.ITNexusLuInfo.Prevent = int(cmd.SCB.Bytes()[4] & 0x03) + return 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 *SCSICommand) SAMStat { + return SAMStatGood } -func SPCPRReadKeys(host int, cmd *SCSICommand) error { - return nil +func SPCSendDiagnostics(host int, cmd *SCSICommand) SAMStat { + // we only support SELF-TEST==1 + if cmd.SCB.Bytes()[1]&0x04 == 0 { + goto sense + } + + return SAMStatGood +sense: + cmd.InSDBBuffer.Resid = 0 + BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) + return 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 *SCSICommand) SAMStat { + // TODO + return SAMStatGood } -func SPCPRReportCapabilities(host int, cmd *SCSICommand) error { - return nil +func SPCPRReadKeys(host int, cmd *SCSICommand) 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 SAMStatCheckCondition } -func SPCPRRegister(host int, cmd *SCSICommand) error { - return nil +func SPCPRReadReservation(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SPCPRReserve(host int, cmd *SCSICommand) error { - return nil +func SPCPRReportCapabilities(host int, cmd *SCSICommand) 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 SAMStatGood +sense: + cmd.InSDBBuffer.Resid = 0 + BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) + return SAMStatCheckCondition } -func SPCPRRelease(host int, cmd *SCSICommand) error { - return nil +func SPCPRRegister(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SPCPRClear(host int, cmd *SCSICommand) error { - return nil +func SPCPRReserve(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SPCPRPreempt(host int, cmd *SCSICommand) error { - return nil +func SPCPRRelease(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SPCPRRegisterAndMove(host int, cmd *SCSICommand) error { - return nil +func SPCPRClear(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SPCRequestSense(host int, cmd *SCSICommand) error { - return nil +func SPCPRPreempt(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood } -func SPCSendDiagnostics(host int, cmd *SCSICommand) error { - return nil +func SPCPRRegisterAndMove(host int, cmd *SCSICommand) SAMStat { + return SAMStatGood +} + +func SPCRequestSense(host int, cmd *SCSICommand) 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 SAMStatGood } diff --git a/pkg/scsi/spc_test.go b/pkg/scsi/spc_test.go new file mode 100644 index 0000000..6ef6095 --- /dev/null +++ b/pkg/scsi/spc_test.go @@ -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) { +} diff --git a/pkg/scsi/target.go b/pkg/scsi/target.go index 30267c0..1a76a49 100644 --- a/pkg/scsi/target.go +++ b/pkg/scsi/target.go @@ -16,6 +16,12 @@ limitations under the License. package scsi +import ( + "fmt" + + "github.com/golang/glog" +) + type SCSITargetState int var ( @@ -32,9 +38,52 @@ const ( PR_EA_FN = (1 << 0) ) -type SCSITarget struct { - Name string - TID int - LID int - State SCSITargetState +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 +} + +func deviceReserve(cmd *SCSICommand) error { + var lu *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 } diff --git a/pkg/util/util.go b/pkg/util/util.go index afdbae3..33b5bce 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -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)