package module
Version: v0.0.0-...-923e3cf Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2016 License: Apache-2.0 Imports: 21 Imported by: 0




Go bindings to attach Go Readers and Writers to the Linux kernel via SCSI.

It connects to the TCM Userspace kernel API, and provides a loopback device that responds to SCSI commands. This project is based on open-iscsi/tcmu-runner, but in pure Go.


This package creates two types of Handlers (much like net/http) for SCSI block device commands. It wraps the implementation details of the kernel API, and sets up (a) a TCMU SCSI device and connect that to (b) a loopback SCSI target.

From here, the Linux IO Target kernel stack can expose the SCSI target however it likes. This includes iSCSI, vHost, etc. For further details, see the LIO wiki.


First, to use this package at all, you'll need the appropriate kernel module:

sudo modprobe target_core_user

Now that that's settled, there's tcmufile.go for a quick example binary that serves an image file under /dev/tcmufile/myfile.

For creating your custom SCSI targets based on a ReadWriterAt:

handler := &tcmu.SCSIHandler{
        HBA: 30, // Choose a virtual HBA number. 30 is fine.
        LUN: 0,  // The LUN attached to this HBA. Multiple LUNs can work on the same HBA, this differentiates them.
        WWN: tcmu.NaaWWN{
                OUI:      "000000",                    // Or provide your OUI
                VendorID: GenerateSerial("foobar"), // Or provide a vendor id/serial number
                // Optional: Provide further information for your WWN
                // VendorIDExt: "0123456789abcdef", 
        VolumeName: "myVolName", // The name of your volume.
        DataSizes: tcmu.DataSizes{
                VolumeSize: 5 * 1024 * 1024, // Size in bytes, eg, 5GiB
                BlockSize:  1024,            // Size of logical blocks, eg, 1K
        DevReady: tcmu.SingleThreadedDevReady(
                ReadWriterAtCmdHandler{      // Or replace with your own handler
                        RW: rw,
d, _ := tcmu.OpenTCMUDevice("/dev/myDevDirectory", handler)
defer d.Close()

This will create a device named /dev/myDevDirectory/myVolName with the mentioned details. It is now ready for formatting and treating like a block device.

If you wish to handle more SCSI commands, you can implement a replacement for the ReadWriterAtCmdHandler following the interface:

type SCSICmdHandler interface {
	HandleCommand(cmd *SCSICmd) (SCSIResponse, error)

If the default functionality was acceptable, the library contains a number of helpful Emulate functions that you can call to achieve the basic functionality.



tcmu is a package that connects to the TCM in Userspace kernel module, a part of the LIO stack. It provides the ability to emulate a SCSI storage device in pure Go.



This section is empty.


This section is empty.


func CachingModePage

func CachingModePage(w io.Writer, wce bool)

func FixedString

func FixedString(s string, length int) []byte

func GenerateSerial

func GenerateSerial(name string) string


type DataSizes

type DataSizes struct {
	VolumeSize int64
	BlockSize  int64

type DevReadyFunc

type DevReadyFunc func(chan *SCSICmd, chan SCSIResponse) error

func MultiThreadedDevReady

func MultiThreadedDevReady(h SCSICmdHandler, threads int) DevReadyFunc

func SingleThreadedDevReady

func SingleThreadedDevReady(h SCSICmdHandler) DevReadyFunc

type Device

type Device struct {
	// contains filtered or unexported fields

func OpenTCMUDevice

func OpenTCMUDevice(devPath string, scsi *SCSIHandler) (*Device, error)

OpenTCMUDevice creates the virtual device based on the details in the SCSIHandler, eventually creating a device under devPath (eg, "/dev") with the file name scsi.VolumeName. The returned Device represents the open device connection to the kernel, and must be closed.

func (*Device) Close

func (d *Device) Close() error

func (*Device) GetDevConfig

func (d *Device) GetDevConfig() string

func (*Device) Sizes

func (d *Device) Sizes() DataSizes

type InquiryInfo

type InquiryInfo struct {
	VendorID   string
	ProductID  string
	ProductRev string

InquiryInfo holds the general vendor information for the emulated SCSI Device. Fields used from this will be padded or trunacted to meet the spec.

type NaaWWN

type NaaWWN struct {
	// OUI is the first three bytes (six hex digits), in ASCII, of your
	// IEEE Organizationally Unique Identifier, eg, "05abcd".
	OUI string
	// The VendorID is the first four bytes (eight hex digits), in ASCII, of
	// the device's vendor-specific ID (perhaps a serial number), eg, "2416c05f".
	VendorID string
	// The VendorIDExt is an optional eight more bytes (16 hex digits) in the same format
	// as the above, if necessary.
	VendorIDExt string

NaaWWN represents the World Wide Name of the SCSI device we are emulating, using the Network Address Authority standard.

func (NaaWWN) DeviceID

func (n NaaWWN) DeviceID() string

func (NaaWWN) NexusID

func (n NaaWWN) NexusID() string

type ReadWriterAt

type ReadWriterAt interface {

type ReadWriterAtCmdHandler

type ReadWriterAtCmdHandler struct {
	RW  ReadWriterAt
	Inq *InquiryInfo

func (ReadWriterAtCmdHandler) HandleCommand

func (h ReadWriterAtCmdHandler) HandleCommand(cmd *SCSICmd) (SCSIResponse, error)

type SCSICmd

type SCSICmd struct {

	// Buf, if provided, may be used as a scratch buffer for copying data to and from the kernel.
	Buf []byte
	// contains filtered or unexported fields

SCSICmd represents a single SCSI command recieved from the kernel to the virtual target.

func (*SCSICmd) CdbLen

func (c *SCSICmd) CdbLen() int

CdbLen returns the length of the command, in bytes.

func (*SCSICmd) CheckCondition

func (c *SCSICmd) CheckCondition(key byte, asc uint16) SCSIResponse

CheckCondition returns a response providing extra sense data. Takes a Sense Key and an Additional Sense Code.

func (*SCSICmd) Command

func (c *SCSICmd) Command() byte

Command returns the SCSI command byte for the command. Useful when used as a comparison to the constants in the scsi package: c.Command() == scsi.Read6

func (*SCSICmd) Device

func (c *SCSICmd) Device() *Device

Device accesses the details of the SCSI device this command is handling.

func (*SCSICmd) GetCDB

func (c *SCSICmd) GetCDB(index int) byte

GetCDB returns the byte at `index` inside the command.

func (*SCSICmd) IllegalRequest

func (c *SCSICmd) IllegalRequest() SCSIResponse

IllegalRequest is a preset response for a request that is malformed or unexpected.

func (*SCSICmd) LBA

func (c *SCSICmd) LBA() uint64

LBA returns the block address that this command wishes to access.

func (*SCSICmd) MediumError

func (c *SCSICmd) MediumError() SCSIResponse

MediumError is a preset response for a read error condition from the device

func (*SCSICmd) NotHandled

func (c *SCSICmd) NotHandled() SCSIResponse

NotHandled creates a response and sense data that tells the kernel this device does not emulate this command.

func (*SCSICmd) Ok

func (c *SCSICmd) Ok() SCSIResponse

Ok creates a SCSIResponse to this command with SAM_STAT_GOOD, the common case for commands that succeed.

func (*SCSICmd) Read

func (c *SCSICmd) Read(b []byte) (n int, err error)

Read, for a SCSICmd, is a io.Reader from the data buffer attached to this SCSI command. If there's data to be written to the virtual device, this is the way to access it.

func (*SCSICmd) RespondSenseData

func (c *SCSICmd) RespondSenseData(status byte, sense []byte) SCSIResponse

RespondSenseData returns a SCSIResponse with the given status byte set and takes a byte array representing the SCSI sense data to be written.

func (*SCSICmd) RespondStatus

func (c *SCSICmd) RespondStatus(status byte) SCSIResponse

RespondStatus returns a SCSIResponse with the given status byte set. Ok() is equivalent to RespondStatus(scsi.SamStatGood).

func (*SCSICmd) TargetFailure

func (c *SCSICmd) TargetFailure() SCSIResponse

TargetFailure is a preset response for returning a hardware error.

func (*SCSICmd) Write

func (c *SCSICmd) Write(b []byte) (n int, err error)

Write, for a SCSICmd, is a io.Writer to the data buffer attached to this SCSI command. It's writing *to* the buffer, which happens most commonly when responding to Read commands (take data and write it back to the kernel buffer)

func (*SCSICmd) XferLen

func (c *SCSICmd) XferLen() uint32

XferLen returns the length of the data buffer this command provides for transfering data to/from the kernel.

type SCSICmdHandler

type SCSICmdHandler interface {
	HandleCommand(cmd *SCSICmd) (SCSIResponse, error)

SCSICmdHandler is a simple request/response handler for SCSI commands coming to TCMU. A SCSI error is reported as an SCSIResponse with an error bit set, while returning a Go error is for flagrant, process-ending errors (OOM, perhaps).

type SCSIHandler

type SCSIHandler struct {
	// The volume name and resultant device name.
	VolumeName string
	// The size of the device and the blocksize for the device.
	DataSizes DataSizes
	// The loopback HBA for the emulated SCSI device
	HBA int
	// The LUN for the emulated HBA
	LUN int
	// The SCSI World Wide Identifer for the device
	// Called once the device is ready. Should spawn a goroutine (or several)
	// to handle commands coming in the first channel, and send their associated
	// responses down the second channel, ordering optional.
	DevReady DevReadyFunc

SCSIHandler is the high-level data for the emulated SCSI device.

func BasicSCSIHandler

func BasicSCSIHandler(rw ReadWriterAt) *SCSIHandler

type SCSIResponse

type SCSIResponse struct {
	// contains filtered or unexported fields

A SCSIResponse is generated from methods on SCSICmd.

func EmulateEvpdInquiry

func EmulateEvpdInquiry(cmd *SCSICmd, inq *InquiryInfo) (SCSIResponse, error)

func EmulateInquiry

func EmulateInquiry(cmd *SCSICmd, inq *InquiryInfo) (SCSIResponse, error)

func EmulateModeSelect

func EmulateModeSelect(cmd *SCSICmd, wce bool) (SCSIResponse, error)

EmulateModeSelect checks that the only mode selected is the static one returned from EmulateModeSense. `wce` should match the Write Cache Enabled of the EmulateModeSense call.

func EmulateModeSense

func EmulateModeSense(cmd *SCSICmd, wce bool) (SCSIResponse, error)

EmulateModeSense responds to a static Mode Sense command. `wce` enables or diables the SCSI "Write Cache Enabled" flag.

func EmulateRead

func EmulateRead(cmd *SCSICmd, r io.ReaderAt) (SCSIResponse, error)

func EmulateReadCapacity16

func EmulateReadCapacity16(cmd *SCSICmd) (SCSIResponse, error)

func EmulateServiceActionIn

func EmulateServiceActionIn(cmd *SCSICmd) (SCSIResponse, error)

func EmulateStdInquiry

func EmulateStdInquiry(cmd *SCSICmd, inq *InquiryInfo) (SCSIResponse, error)

func EmulateTestUnitReady

func EmulateTestUnitReady(cmd *SCSICmd) (SCSIResponse, error)

func EmulateWrite

func EmulateWrite(cmd *SCSICmd, r io.WriterAt) (SCSIResponse, error)

type WWN

type WWN interface {
	DeviceID() string
	NexusID() string

WWN provides two WWNs, one for the device itself and one for the loopback device created for the kernel.

func GenerateTestWWN

func GenerateTestWWN() WWN


Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL