This commit is contained in:
duffyduck 2026-02-19 16:51:32 +01:00
parent 14a9e87423
commit 54178dce75
5 changed files with 50 additions and 17 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -42,7 +42,10 @@ func createVHCIAttachment(_ context.Context, granted *protocol.DeviceGranted, _
return nil, -1, fmt.Errorf("VHCI attach: %w", err) return nil, -1, fmt.Errorf("VHCI attach: %w", err)
} }
// The VHCI driver now owns vhciFD, so we don't close it // The VHCI driver holds a kernel reference to the socket via sockfd_lookup,
// so we can close our copy of the fd to avoid leaking it.
unix.Close(vhciFD)
// Create a net.Conn from the tunnel FD // Create a net.Conn from the tunnel FD
tunnelFile := fdToFile(tunnelFD, "usb-tunnel") tunnelFile := fdToFile(tunnelFD, "usb-tunnel")
tunnelConn, err := net.FileConn(tunnelFile) tunnelConn, err := net.FileConn(tunnelFile)
@ -74,9 +77,9 @@ func fdToFile(fd int, name string) *os.File {
} }
// fixVHCIDevicePermissions waits for the VHCI-attached device to create // fixVHCIDevicePermissions waits for the VHCI-attached device to create
// device nodes (e.g. /dev/video*) and sets them to world-accessible. // device nodes (e.g. /dev/video*, /dev/input/event*, /dev/hidraw*) and sets
// VHCI-created devices don't get normal udev rules applied, so they // them to world-accessible. VHCI-created devices don't get normal udev
// default to root-only access. // rules applied, so they default to root-only access.
func fixVHCIDevicePermissions(port int) { func fixVHCIDevicePermissions(port int) {
// Wait for the device to finish enumerating and create device nodes. // Wait for the device to finish enumerating and create device nodes.
// The kernel needs time to enumerate descriptors and bind drivers. // The kernel needs time to enumerate descriptors and bind drivers.
@ -101,12 +104,13 @@ func fixVHCIDevicePermissions(port int) {
if err := os.Chmod(devPath, 0666); err == nil { if err := os.Chmod(devPath, 0666); err == nil {
log.Printf("[use] set permissions 0666 on %s", devPath) log.Printf("[use] set permissions 0666 on %s", devPath)
found = true found = true
} else {
log.Printf("[use] chmod %s failed: %v", devPath, err)
} }
} }
// sound devices → /dev/snd/* // sound devices → /dev/snd/*
if parent == "sound" && strings.HasPrefix(d.Name(), "card") { if parent == "sound" && strings.HasPrefix(d.Name(), "card") {
// For sound cards, chmod all related device nodes
sndDir := filepath.Join(path, "device") sndDir := filepath.Join(path, "device")
if _, err := os.Stat(sndDir); err == nil { if _, err := os.Stat(sndDir); err == nil {
filepath.WalkDir("/dev/snd", func(sndPath string, sd os.DirEntry, err error) error { filepath.WalkDir("/dev/snd", func(sndPath string, sd os.DirEntry, err error) error {
@ -123,6 +127,20 @@ func fixVHCIDevicePermissions(port int) {
devPath := "/dev/input/" + d.Name() devPath := "/dev/input/" + d.Name()
if err := os.Chmod(devPath, 0666); err == nil { if err := os.Chmod(devPath, 0666); err == nil {
log.Printf("[use] set permissions 0666 on %s", devPath) log.Printf("[use] set permissions 0666 on %s", devPath)
found = true
} else {
log.Printf("[use] chmod %s failed: %v", devPath, err)
}
}
// hidraw devices → /dev/hidrawN
if parent == "hidraw" && strings.HasPrefix(d.Name(), "hidraw") {
devPath := "/dev/" + d.Name()
if err := os.Chmod(devPath, 0666); err == nil {
log.Printf("[use] set permissions 0666 on %s", devPath)
found = true
} else {
log.Printf("[use] chmod %s failed: %v", devPath, err)
} }
} }
@ -133,4 +151,6 @@ func fixVHCIDevicePermissions(port int) {
return return
} }
} }
log.Printf("[use] fixVHCIDevicePermissions: no device nodes found after 7.5s (port %d)", port)
} }

View File

@ -414,16 +414,11 @@ func (s *Server) handleCmdSubmit(r io.Reader, hdr *URBHeader, retChan chan<- []b
// device's endpoint state without updating the kernel's internal USB // device's endpoint state without updating the kernel's internal USB
// subsystem, breaking all subsequent SETINTERFACE and SUBMITURB calls // subsystem, breaking all subsequent SETINTERFACE and SUBMITURB calls
// (ESRCH / EHOSTUNREACH). // (ESRCH / EHOSTUNREACH).
// Just reset host-side data toggles to DATA0 (matching the fresh VHCI // Do NOT reset host-side data toggles either: after DisconnectClaimInterface
// state on the use-side) and return success. // the host and device toggles are already in sync. Resetting host-side
log.Printf("[usbip-server] SET_CONFIGURATION(%d) intercepted (device already configured), resetting endpoint toggles", wValue) // toggles to DATA0 would create a mismatch (device still at its current
for epNum := range s.epTypes { // toggle), causing the first interrupt packet to be silently discarded.
if epNum == 0 { log.Printf("[usbip-server] SET_CONFIGURATION(%d) intercepted (device already configured)", wValue)
continue
}
s.handle.ResetEndpoint(uint32(epNum))
s.handle.ResetEndpoint(uint32(epNum) | 0x80)
}
default: default:
// Generic OUT control transfer // Generic OUT control transfer
@ -647,9 +642,27 @@ func (s *Server) reapLoop(retChan chan<- []byte, done <-chan struct{}) {
continue continue
} }
dirStr := "OUT"
if pending.direction == DirIn {
dirStr = "IN"
}
urbType := s.getURBType(uint8(pending.endpoint))
typeNames := map[uint8]string{0: "ISO", 1: "INT", 2: "CTRL", 3: "BULK"}
if urbInfo.Status != 0 { if urbInfo.Status != 0 {
log.Printf("[usbip-server] URB completed: seq=%d ep=%d status=%d actual=%d iso=%v", log.Printf("[usbip-server] URB completed: seq=%d EP%d %s type=%s status=%d actual=%d",
pending.seqNum, pending.endpoint, urbInfo.Status, urbInfo.ActualLength, pending.isISO) pending.seqNum, pending.endpoint, dirStr, typeNames[urbType], urbInfo.Status, urbInfo.ActualLength)
} else if urbType == 1 { // interrupt — always log for HID debugging
hexStr := ""
if pending.direction == DirIn && urbInfo.ActualLength > 0 {
n := int(urbInfo.ActualLength)
if n > 16 {
n = 16
}
hexStr = fmt.Sprintf(" data=%x", pending.buffer[:n])
}
log.Printf("[usbip-server] INT completed: seq=%d EP%d %s actual=%d%s",
pending.seqNum, pending.endpoint, dirStr, urbInfo.ActualLength, hexStr)
} }
var resp []byte var resp []byte