refactor: extract mount library package, add flake.nix
Move protocol and SCSI code into importable mount/ package with a single public API (Config + Run). Restructure as multi-binary repo with cmd/aten-mount/ CLI wrapper using context-based cancellation. Add Nix flake with go_1_26 for builds and devshell. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2ae20b8827
commit
826edc817a
11 changed files with 255 additions and 168 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
aten-mount
|
||||
/aten-mount
|
||||
/result
|
||||
|
|
|
|||
44
cmd/aten-mount/main.go
Normal file
44
cmd/aten-mount/main.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"git.dsg.is/dsg/aten-ipmi-tools/mount"
|
||||
)
|
||||
|
||||
func main() {
|
||||
user := flag.String("u", "admin", "BMC username")
|
||||
pass := flag.String("p", "admin", "BMC password")
|
||||
port := flag.Int("port", 623, "BMC virtual media TCP port")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [flags] <bmc-host> <image.iso>\n\nFlags:\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 2 {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
cfg := mount.Config{
|
||||
Host: flag.Arg(0),
|
||||
Port: *port,
|
||||
Username: *user,
|
||||
Password: *pass,
|
||||
ISOPath: flag.Arg(1),
|
||||
}
|
||||
|
||||
if err := mount.Run(ctx, cfg); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1773110118,
|
||||
"narHash": "sha256-mPAG8phMbCReKSiKAijjjd3v7uVcJOQ75gSjGJjt/Rk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e607cb5360ff1234862ac9f8839522becb853bb9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
32
flake.nix
Normal file
32
flake.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f {
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
});
|
||||
in
|
||||
{
|
||||
packages = forAllSystems ({ pkgs }: {
|
||||
aten-mount = pkgs.buildGoModule.override { go = pkgs.go_1_26; } {
|
||||
pname = "aten-mount";
|
||||
version = "0.1.0";
|
||||
src = ./.;
|
||||
vendorHash = null;
|
||||
subPackages = [ "cmd/aten-mount" ];
|
||||
};
|
||||
|
||||
default = self.packages.${pkgs.system}.aten-mount;
|
||||
});
|
||||
|
||||
devShells = forAllSystems ({ pkgs }: {
|
||||
default = pkgs.mkShell {
|
||||
packages = [ pkgs.go_1_26 ];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
4
go.mod
4
go.mod
|
|
@ -1,3 +1,3 @@
|
|||
module github.com/example/aten-mount
|
||||
module git.dsg.is/dsg/aten-ipmi-tools
|
||||
|
||||
go 1.25.7
|
||||
go 1.26
|
||||
|
|
|
|||
102
main.go
102
main.go
|
|
@ -1,102 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
user := flag.String("u", "admin", "BMC username")
|
||||
pass := flag.String("p", "admin", "BMC password")
|
||||
port := flag.Int("port", 623, "BMC virtual media TCP port")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [flags] <bmc-host> <image.iso>\n\nFlags:\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 2 {
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
host := flag.Arg(0)
|
||||
isoPath := flag.Arg(1)
|
||||
|
||||
// Open and validate ISO file
|
||||
isoFile, err := os.Open(isoPath)
|
||||
if err != nil {
|
||||
log.Fatalf("open ISO: %v", err)
|
||||
}
|
||||
defer isoFile.Close()
|
||||
|
||||
isoInfo, err := isoFile.Stat()
|
||||
if err != nil {
|
||||
log.Fatalf("stat ISO: %v", err)
|
||||
}
|
||||
isoSize := isoInfo.Size()
|
||||
if isoSize < 0x8054 {
|
||||
log.Fatalf("ISO file too small (%d bytes)", isoSize)
|
||||
}
|
||||
totalSectors := uint32(isoSize / 2048)
|
||||
log.Printf("ISO: %s (%d bytes, %d sectors)", isoPath, isoSize, totalSectors)
|
||||
|
||||
// TCP connect
|
||||
addr := net.JoinHostPort(host, fmt.Sprintf("%d", *port))
|
||||
log.Printf("Connecting to %s...", addr)
|
||||
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
|
||||
if err != nil {
|
||||
log.Fatalf("connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
log.Printf("Connected")
|
||||
|
||||
// Send PlugIn packet
|
||||
pluginPkt := buildPlugInPacket(*user, *pass)
|
||||
if _, err := conn.Write(pluginPkt); err != nil {
|
||||
log.Fatalf("send plugin: %v", err)
|
||||
}
|
||||
log.Printf("Sent PlugIn packet (%d bytes)", len(pluginPkt))
|
||||
|
||||
// Read mount status
|
||||
status, err := readMountStatus(conn)
|
||||
if err != nil {
|
||||
log.Fatalf("mount status: %v", err)
|
||||
}
|
||||
if status != 0x00 {
|
||||
log.Fatalf("mount failed: %s", mountStatusString(status))
|
||||
}
|
||||
log.Printf("Mount OK")
|
||||
|
||||
// Send SetEP command
|
||||
if _, err := conn.Write(buildSetEPPacket()); err != nil {
|
||||
log.Fatalf("send setep: %v", err)
|
||||
}
|
||||
log.Printf("Sent SetEP, entering SCSI command loop")
|
||||
|
||||
// Signal handling for clean unmount
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// SCSI command loop
|
||||
dev := &SCSIDevice{
|
||||
file: isoFile,
|
||||
totalSectors: totalSectors,
|
||||
sense: initialSenseData(),
|
||||
}
|
||||
|
||||
err = commandLoop(conn, dev, sigCh)
|
||||
|
||||
// Clean unmount
|
||||
log.Printf("Sending unmount...")
|
||||
conn.Write(buildUnmountPacket())
|
||||
if err != nil {
|
||||
log.Fatalf("command loop: %v", err)
|
||||
}
|
||||
log.Printf("Unmounted cleanly")
|
||||
}
|
||||
87
mount/mount.go
Normal file
87
mount/mount.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package mount
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config holds the parameters for a virtual media mount session.
|
||||
type Config struct {
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
ISOPath string
|
||||
}
|
||||
|
||||
// Run connects to the BMC, mounts the ISO, and serves SCSI commands
|
||||
// until ctx is cancelled or an error occurs. Sends unmount on return.
|
||||
func Run(ctx context.Context, cfg Config) error {
|
||||
isoFile, err := os.Open(cfg.ISOPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open ISO: %w", err)
|
||||
}
|
||||
defer isoFile.Close()
|
||||
|
||||
isoInfo, err := isoFile.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat ISO: %w", err)
|
||||
}
|
||||
isoSize := isoInfo.Size()
|
||||
if isoSize < 0x8054 {
|
||||
return fmt.Errorf("ISO file too small (%d bytes)", isoSize)
|
||||
}
|
||||
totalSectors := uint32(isoSize / 2048)
|
||||
log.Printf("ISO: %s (%d bytes, %d sectors)", cfg.ISOPath, isoSize, totalSectors)
|
||||
|
||||
addr := net.JoinHostPort(cfg.Host, fmt.Sprintf("%d", cfg.Port))
|
||||
log.Printf("Connecting to %s...", addr)
|
||||
|
||||
var d net.Dialer
|
||||
d.Timeout = 10 * time.Second
|
||||
conn, err := d.DialContext(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("connect: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
log.Printf("Connected")
|
||||
|
||||
if _, err := conn.Write(buildPlugInPacket(cfg.Username, cfg.Password)); err != nil {
|
||||
return fmt.Errorf("send plugin: %w", err)
|
||||
}
|
||||
log.Printf("Sent PlugIn packet")
|
||||
|
||||
status, err := readMountStatus(conn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("mount status: %w", err)
|
||||
}
|
||||
if status != 0x00 {
|
||||
return fmt.Errorf("mount failed: %s", mountStatusString(status))
|
||||
}
|
||||
log.Printf("Mount OK")
|
||||
|
||||
if _, err := conn.Write(buildSetEPPacket()); err != nil {
|
||||
return fmt.Errorf("send setep: %w", err)
|
||||
}
|
||||
log.Printf("Sent SetEP, entering SCSI command loop")
|
||||
|
||||
dev := &scsiDevice{
|
||||
file: isoFile,
|
||||
totalSectors: totalSectors,
|
||||
sense: initialSenseData(),
|
||||
}
|
||||
|
||||
err = commandLoop(ctx, conn, dev)
|
||||
|
||||
log.Printf("Sending unmount...")
|
||||
conn.Write(buildUnmountPacket())
|
||||
if err != nil {
|
||||
return fmt.Errorf("command loop: %w", err)
|
||||
}
|
||||
log.Printf("Unmounted cleanly")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package main
|
||||
package mount
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rc4"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -272,7 +272,7 @@ func buildCSW(ep byte, tag [4]byte, dataTransferLen uint32, actualLen uint32, st
|
|||
pdu[3] = 0xFF
|
||||
binary.LittleEndian.PutUint32(pdu[4:8], 13)
|
||||
// CSW body
|
||||
pdu[8] = 'U' // dCSWSignature "USBS"
|
||||
pdu[8] = 'U' // dCSWSignature "USBS"
|
||||
pdu[9] = 'S'
|
||||
pdu[10] = 'B'
|
||||
pdu[11] = 'S'
|
||||
|
|
@ -282,7 +282,7 @@ func buildCSW(ep byte, tag [4]byte, dataTransferLen uint32, actualLen uint32, st
|
|||
residue = dataTransferLen - actualLen
|
||||
}
|
||||
binary.LittleEndian.PutUint32(pdu[16:20], residue) // dCSWDataResidue
|
||||
pdu[20] = status // bCSWStatus
|
||||
pdu[20] = status // bCSWStatus
|
||||
return pdu
|
||||
}
|
||||
|
||||
|
|
@ -307,22 +307,20 @@ func (h pduHeader) pduType() uint32 {
|
|||
return binary.BigEndian.Uint32(h.typeOrTag[:])
|
||||
}
|
||||
|
||||
// commandLoop reads PDU headers and dispatches commands until signal or error.
|
||||
func commandLoop(conn net.Conn, dev *SCSIDevice, sigCh <-chan os.Signal) error {
|
||||
// commandLoop reads PDU headers and dispatches commands until context
|
||||
// cancellation or error.
|
||||
func commandLoop(ctx context.Context, conn net.Conn, dev *scsiDevice) error {
|
||||
for {
|
||||
// Check for signal (non-blocking)
|
||||
select {
|
||||
case <-sigCh:
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// Set read deadline so we can check signals periodically
|
||||
// Set read deadline so we can check context periodically
|
||||
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
hdr, err := readPDUHeader(conn)
|
||||
if err != nil {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
continue // timeout, loop back to check signal
|
||||
continue // timeout, loop back to check context
|
||||
}
|
||||
return fmt.Errorf("read PDU header: %w", err)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package mount
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package mount
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
|
@ -8,25 +8,25 @@ import (
|
|||
|
||||
// SCSI opcodes
|
||||
const (
|
||||
scsiTestUnitReady = 0x00
|
||||
scsiRequestSense = 0x03
|
||||
scsiInquiry = 0x12
|
||||
scsiStartStopUnit = 0x1B
|
||||
scsiMediumRemoval = 0x1E
|
||||
scsiReadFormatCapacities = 0x23
|
||||
scsiReadCapacity = 0x25
|
||||
scsiRead10 = 0x28
|
||||
scsiReadToc = 0x43
|
||||
scsiGetConfiguration = 0x46
|
||||
scsiGetEventStatusNotif = 0x4A
|
||||
scsiModeSense10 = 0x5A
|
||||
scsiRead12 = 0xA8
|
||||
scsiTestUnitReady = 0x00
|
||||
scsiRequestSense = 0x03
|
||||
scsiInquiry = 0x12
|
||||
scsiStartStopUnit = 0x1B
|
||||
scsiMediumRemoval = 0x1E
|
||||
scsiReadFormatCapacities = 0x23
|
||||
scsiReadCapacity = 0x25
|
||||
scsiRead10 = 0x28
|
||||
scsiReadToc = 0x43
|
||||
scsiGetConfiguration = 0x46
|
||||
scsiGetEventStatusNotif = 0x4A
|
||||
scsiModeSense10 = 0x5A
|
||||
scsiRead12 = 0xA8
|
||||
)
|
||||
|
||||
// CSW status
|
||||
const (
|
||||
cswStatusPassed = 0x00
|
||||
cswStatusFailed = 0x01
|
||||
cswStatusPassed = 0x00
|
||||
cswStatusFailed = 0x01
|
||||
)
|
||||
|
||||
// Sense keys
|
||||
|
|
@ -37,8 +37,8 @@ const (
|
|||
senseIllegalRequest = 0x05
|
||||
)
|
||||
|
||||
// SCSIDevice holds state for the virtual CD-ROM.
|
||||
type SCSIDevice struct {
|
||||
// scsiDevice holds state for the virtual CD-ROM.
|
||||
type scsiDevice struct {
|
||||
file *os.File
|
||||
totalSectors uint32
|
||||
sense [18]byte
|
||||
|
|
@ -46,25 +46,25 @@ type SCSIDevice struct {
|
|||
|
||||
func initialSenseData() [18]byte {
|
||||
var s [18]byte
|
||||
s[0] = 0x70 // Response code (current errors)
|
||||
s[7] = 0x0A // Additional sense length
|
||||
s[0] = 0x70 // Response code (current errors)
|
||||
s[7] = 0x0A // Additional sense length
|
||||
return s
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) setSense(key, asc, ascq byte) {
|
||||
func (d *scsiDevice) setSense(key, asc, ascq byte) {
|
||||
d.sense = initialSenseData()
|
||||
d.sense[2] = key
|
||||
d.sense[12] = asc
|
||||
d.sense[13] = ascq
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) setOK() {
|
||||
func (d *scsiDevice) setOK() {
|
||||
d.sense = initialSenseData()
|
||||
}
|
||||
|
||||
// handleCBW parses a 31-byte CBW and returns the complete TCP response
|
||||
// (data PDU + CSW, or just CSW).
|
||||
func (d *SCSIDevice) handleCBW(ep byte, cbw []byte) []byte {
|
||||
func (d *scsiDevice) handleCBW(ep byte, cbw []byte) []byte {
|
||||
// Parse CBW fields (offsets relative to 31-byte CBW body)
|
||||
// CBW signature at 0-3 ("USBC")
|
||||
var tag [4]byte
|
||||
|
|
@ -96,7 +96,7 @@ func (d *SCSIDevice) handleCBW(ep byte, cbw []byte) []byte {
|
|||
return resp
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) executeSCSI(cdb []byte, transferLen uint32) (data []byte, status byte) {
|
||||
func (d *scsiDevice) executeSCSI(cdb []byte, transferLen uint32) (data []byte, status byte) {
|
||||
if len(cdb) == 0 {
|
||||
d.setSense(senseIllegalRequest, 0x20, 0x00) // Invalid command
|
||||
return nil, cswStatusFailed
|
||||
|
|
@ -137,12 +137,12 @@ func (d *SCSIDevice) executeSCSI(cdb []byte, transferLen uint32) (data []byte, s
|
|||
}
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdTestUnitReady() ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdTestUnitReady() ([]byte, byte) {
|
||||
d.setOK()
|
||||
return nil, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdRequestSense(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdRequestSense(cdb []byte) ([]byte, byte) {
|
||||
allocLen := int(cdb[4])
|
||||
resp := make([]byte, 18)
|
||||
copy(resp, d.sense[:])
|
||||
|
|
@ -153,7 +153,7 @@ func (d *SCSIDevice) cmdRequestSense(cdb []byte) ([]byte, byte) {
|
|||
return resp, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdInquiry(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdInquiry(cdb []byte) ([]byte, byte) {
|
||||
evpd := cdb[1] & 0x01
|
||||
pageCode := cdb[2]
|
||||
allocLen := int(cdb[3])<<8 | int(cdb[4])
|
||||
|
|
@ -216,17 +216,17 @@ func (d *SCSIDevice) cmdInquiry(cdb []byte) ([]byte, byte) {
|
|||
return nil, cswStatusFailed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdStartStopUnit() ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdStartStopUnit() ([]byte, byte) {
|
||||
d.setOK()
|
||||
return nil, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdMediumRemoval() ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdMediumRemoval() ([]byte, byte) {
|
||||
d.setOK()
|
||||
return nil, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdReadCapacity(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdReadCapacity(cdb []byte) ([]byte, byte) {
|
||||
// Validate reserved bytes 1-8 are zero (except byte 2 which is OK)
|
||||
for i := 1; i <= 8; i++ {
|
||||
if i >= 2 && i <= 5 {
|
||||
|
|
@ -246,7 +246,7 @@ func (d *SCSIDevice) cmdReadCapacity(cdb []byte) ([]byte, byte) {
|
|||
return resp, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdReadFormatCapacities(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdReadFormatCapacities(cdb []byte) ([]byte, byte) {
|
||||
allocLen := int(cdb[7])<<8 | int(cdb[8])
|
||||
resp := make([]byte, 12)
|
||||
resp[3] = 0x08 // Capacity list length
|
||||
|
|
@ -263,7 +263,7 @@ func (d *SCSIDevice) cmdReadFormatCapacities(cdb []byte) ([]byte, byte) {
|
|||
return resp, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdRead10(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdRead10(cdb []byte) ([]byte, byte) {
|
||||
lba := binary.BigEndian.Uint32(cdb[2:6])
|
||||
count := uint32(cdb[7])<<8 | uint32(cdb[8])
|
||||
|
||||
|
|
@ -286,7 +286,7 @@ func (d *SCSIDevice) cmdRead10(cdb []byte) ([]byte, byte) {
|
|||
return data[:n], cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdRead12(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdRead12(cdb []byte) ([]byte, byte) {
|
||||
lba := binary.BigEndian.Uint32(cdb[2:6])
|
||||
count := binary.BigEndian.Uint32(cdb[6:10])
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ func (d *SCSIDevice) cmdRead12(cdb []byte) ([]byte, byte) {
|
|||
return data[:n], cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdReadToc(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdReadToc(cdb []byte) ([]byte, byte) {
|
||||
format := cdb[2] & 0x0F
|
||||
if format != 0 {
|
||||
d.setSense(senseIllegalRequest, 0x24, 0x00)
|
||||
|
|
@ -354,7 +354,7 @@ func (d *SCSIDevice) cmdReadToc(cdb []byte) ([]byte, byte) {
|
|||
return resp, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdGetConfiguration(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdGetConfiguration(cdb []byte) ([]byte, byte) {
|
||||
allocLen := int(cdb[7])<<8 | int(cdb[8])
|
||||
if allocLen == 0 {
|
||||
d.setSense(senseIllegalRequest, 0x24, 0x00)
|
||||
|
|
@ -398,7 +398,7 @@ func (d *SCSIDevice) cmdGetConfiguration(cdb []byte) ([]byte, byte) {
|
|||
return resp, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdGetEventStatus(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdGetEventStatus(cdb []byte) ([]byte, byte) {
|
||||
if cdb[1]&0x01 == 0 {
|
||||
// Polled bit not set - async not supported
|
||||
d.setSense(senseIllegalRequest, 0x24, 0x00)
|
||||
|
|
@ -424,7 +424,7 @@ func (d *SCSIDevice) cmdGetEventStatus(cdb []byte) ([]byte, byte) {
|
|||
return resp, cswStatusPassed
|
||||
}
|
||||
|
||||
func (d *SCSIDevice) cmdModeSense10(cdb []byte) ([]byte, byte) {
|
||||
func (d *scsiDevice) cmdModeSense10(cdb []byte) ([]byte, byte) {
|
||||
pageCode := cdb[2] & 0x3F
|
||||
allocLen := int(cdb[7])<<8 | int(cdb[8])
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package mount
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
|
@ -6,16 +6,16 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func newTestDevice(t *testing.T) *SCSIDevice {
|
||||
func newTestDevice(t *testing.T) *scsiDevice {
|
||||
t.Helper()
|
||||
return &SCSIDevice{
|
||||
return &scsiDevice{
|
||||
file: nil, // No file for most tests
|
||||
totalSectors: 1000,
|
||||
sense: initialSenseData(),
|
||||
}
|
||||
}
|
||||
|
||||
func newTestDeviceWithFile(t *testing.T) (*SCSIDevice, func()) {
|
||||
func newTestDeviceWithFile(t *testing.T) (*scsiDevice, func()) {
|
||||
t.Helper()
|
||||
// Create a minimal fake ISO (just enough for reads)
|
||||
f, err := os.CreateTemp("", "test-*.iso")
|
||||
|
|
@ -30,7 +30,7 @@ func newTestDeviceWithFile(t *testing.T) (*SCSIDevice, func()) {
|
|||
f.Write(data)
|
||||
f.Sync()
|
||||
|
||||
dev := &SCSIDevice{
|
||||
dev := &scsiDevice{
|
||||
file: f,
|
||||
totalSectors: 10,
|
||||
sense: initialSenseData(),
|
||||
|
|
@ -173,14 +173,14 @@ func TestHandleCBW(t *testing.T) {
|
|||
dev := newTestDevice(t)
|
||||
// Build a CBW for INQUIRY
|
||||
cbw := make([]byte, 31)
|
||||
copy(cbw[0:4], []byte("USBC")) // Signature
|
||||
copy(cbw[4:8], []byte{1, 2, 3, 4}) // Tag
|
||||
binary.LittleEndian.PutUint32(cbw[8:12], 36) // Transfer length
|
||||
cbw[12] = 0x80 // Flags: IN
|
||||
cbw[14] = 6 // CDB length
|
||||
cbw[15] = 0x12 // INQUIRY opcode
|
||||
cbw[18] = 0x00 // Alloc len MSB (CDB byte 3)
|
||||
cbw[19] = 0x24 // Alloc len LSB (CDB byte 4) = 36
|
||||
copy(cbw[0:4], []byte("USBC")) // Signature
|
||||
copy(cbw[4:8], []byte{1, 2, 3, 4}) // Tag
|
||||
binary.LittleEndian.PutUint32(cbw[8:12], 36) // Transfer length
|
||||
cbw[12] = 0x80 // Flags: IN
|
||||
cbw[14] = 6 // CDB length
|
||||
cbw[15] = 0x12 // INQUIRY opcode
|
||||
cbw[18] = 0x00 // Alloc len MSB (CDB byte 3)
|
||||
cbw[19] = 0x24 // Alloc len LSB (CDB byte 4) = 36
|
||||
|
||||
resp := dev.handleCBW(0x20, cbw)
|
||||
// Should have: 8 data PDU tag + 36 data + 8 CSW tag + 13 CSW body = 65
|
||||
Loading…
Add table
Add a link
Reference in a new issue