- Guard uint32 underflow in CSW residue calculation - Use net.JoinHostPort for IPv6 compatibility - Add LBA bounds checking in Read10/Read12 - Clean up redundant byte writes in ReadFormatCapacities Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
2.4 KiB
Go
102 lines
2.4 KiB
Go
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")
|
|
}
|