Lorenz Brun | bffdda8 | 2023-01-10 05:00:36 +0000 | [diff] [blame] | 1 | //go:build linux |
| 2 | |
| 3 | package scsi |
| 4 | |
| 5 | import ( |
| 6 | "encoding/binary" |
| 7 | "errors" |
| 8 | "fmt" |
| 9 | "math" |
| 10 | "runtime" |
| 11 | "unsafe" |
| 12 | |
| 13 | "golang.org/x/sys/unix" |
| 14 | ) |
| 15 | |
| 16 | // RawCommand issues a raw command against the device. |
| 17 | func (d *Device) RawCommand(c *CommandDataBuffer) error { |
| 18 | cdb, err := c.Bytes() |
| 19 | if err != nil { |
| 20 | return fmt.Errorf("error encoding CDB: %w", err) |
| 21 | } |
| 22 | conn, err := d.fd.SyscallConn() |
| 23 | if err != nil { |
| 24 | return fmt.Errorf("unable to get RawConn: %w", err) |
| 25 | } |
| 26 | var dxferDir int32 |
| 27 | switch c.DataTransferDirection { |
| 28 | case DataTransferNone: |
| 29 | dxferDir = SG_DXFER_NONE |
| 30 | case DataTransferFromDevice: |
| 31 | dxferDir = SG_DXFER_FROM_DEV |
| 32 | case DataTransferToDevice: |
| 33 | dxferDir = SG_DXFER_TO_DEV |
| 34 | case DataTransferBidirectional: |
| 35 | dxferDir = SG_DXFER_TO_FROM_DEV |
| 36 | default: |
| 37 | return errors.New("invalid DataTransferDirection") |
| 38 | } |
| 39 | var timeout uint32 |
| 40 | if c.Timeout.Milliseconds() > math.MaxUint32 { |
| 41 | timeout = math.MaxUint32 |
| 42 | } |
| 43 | if len(c.Data) > math.MaxUint32 { |
| 44 | return errors.New("payload larger than 2^32 bytes, unable to issue") |
| 45 | } |
| 46 | if len(cdb) > math.MaxUint8 { |
| 47 | return errors.New("CDB larger than 2^8 bytes, unable to issue") |
| 48 | } |
| 49 | var senseBuf [32]byte |
| 50 | cmdRaw := sgIOHdr{ |
| 51 | Interface_id: 'S', |
| 52 | Dxfer_direction: dxferDir, |
| 53 | Dxfer_len: uint32(len(c.Data)), |
| 54 | Dxferp: uintptr(unsafe.Pointer(&c.Data[0])), |
| 55 | Cmd_len: uint8(len(cdb)), |
| 56 | Cmdp: uintptr(unsafe.Pointer(&cdb[0])), |
| 57 | Mx_sb_len: uint8(len(senseBuf)), |
| 58 | Sbp: uintptr(unsafe.Pointer(&senseBuf[0])), |
| 59 | Timeout: timeout, |
| 60 | } |
| 61 | var errno unix.Errno |
| 62 | err = conn.Control(func(fd uintptr) { |
| 63 | _, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, SG_IO, uintptr(unsafe.Pointer(&cmdRaw))) |
| 64 | }) |
| 65 | runtime.KeepAlive(cmdRaw) |
| 66 | runtime.KeepAlive(c.Data) |
| 67 | runtime.KeepAlive(senseBuf) |
| 68 | runtime.KeepAlive(cdb) |
| 69 | if err != nil { |
| 70 | return fmt.Errorf("unable to get fd: %w", err) |
| 71 | } |
| 72 | if errno != 0 { |
| 73 | return errno |
| 74 | } |
| 75 | if cmdRaw.Masked_status != 0 { |
| 76 | if senseBuf[0] == 0x70 || senseBuf[0] == 0x71 { |
| 77 | err := &FixedError{ |
| 78 | Deferred: senseBuf[0] == 0x71, |
| 79 | SenseKey: SenseKey(senseBuf[2] & 0b1111), |
| 80 | Information: binary.BigEndian.Uint32(senseBuf[3:7]), |
| 81 | } |
| 82 | length := int(senseBuf[7]) |
| 83 | if length >= 4 { |
| 84 | err.CommandSpecificInformation = binary.BigEndian.Uint32(senseBuf[8:12]) |
| 85 | if length >= 6 { |
| 86 | err.AdditionalSenseCode = AdditionalSenseCode(uint16(senseBuf[12])<<8 | uint16(senseBuf[13])) |
| 87 | } |
| 88 | } |
| 89 | return err |
| 90 | } |
| 91 | return &UnknownError{ |
| 92 | RawSenseData: senseBuf[:], |
| 93 | } |
| 94 | } |
| 95 | return nil |
| 96 | } |