feat(usb_mass_storage): mount as disk (#333)

* feat(usb_mass_storage): mount as disk

* chore: try to set initial virtual media state from sysfs

* chore(usb-mass-storage): fix inquiry_string
This commit is contained in:
Aveline
2025-05-12 19:07:27 +02:00
committed by GitHub
parent 8ee0532f0e
commit 63c2272c45
6 changed files with 137 additions and 27 deletions

View File

@@ -26,6 +26,19 @@ func writeFile(path string, data string) error {
return os.WriteFile(path, []byte(data), 0644)
}
func getMassStorageImage() (string, error) {
massStorageFunctionPath, err := gadget.GetPath("mass_storage_lun0")
if err != nil {
return "", fmt.Errorf("failed to get mass storage path: %w", err)
}
imagePath, err := os.ReadFile(path.Join(massStorageFunctionPath, "file"))
if err != nil {
return "", fmt.Errorf("failed to get mass storage image path: %w", err)
}
return strings.TrimSpace(string(imagePath)), nil
}
func setMassStorageImage(imagePath string) error {
massStorageFunctionPath, err := gadget.GetPath("mass_storage_lun0")
if err != nil {
@@ -39,19 +52,21 @@ func setMassStorageImage(imagePath string) error {
}
func setMassStorageMode(cdrom bool) error {
massStorageFunctionPath, err := gadget.GetPath("mass_storage_lun0")
if err != nil {
return fmt.Errorf("failed to get mass storage path: %w", err)
}
mode := "0"
if cdrom {
mode = "1"
}
if err := writeFile(path.Join(massStorageFunctionPath, "lun.0", "cdrom"), mode); err != nil {
err, changed := gadget.OverrideGadgetConfig("mass_storage_lun0", "cdrom", mode)
if err != nil {
return fmt.Errorf("failed to set cdrom mode: %w", err)
}
return nil
if !changed {
return nil
}
return gadget.UpdateGadgetConfig()
}
func onDiskMessage(msg webrtc.DataChannelMessage) {
@@ -113,20 +128,17 @@ func rpcMountBuiltInImage(filename string) error {
return mountImage(imagePath)
}
func getMassStorageMode() (bool, error) {
func getMassStorageCDROMEnabled() (bool, error) {
massStorageFunctionPath, err := gadget.GetPath("mass_storage_lun0")
if err != nil {
return false, fmt.Errorf("failed to get mass storage path: %w", err)
}
data, err := os.ReadFile(path.Join(massStorageFunctionPath, "lun.0", "cdrom"))
data, err := os.ReadFile(path.Join(massStorageFunctionPath, "cdrom"))
if err != nil {
return false, fmt.Errorf("failed to read cdrom mode: %w", err)
}
// Trim any whitespace characters. It has a newline at the end
trimmedData := strings.TrimSpace(string(data))
return trimmedData == "1", nil
}
@@ -191,6 +203,60 @@ func rpcUnmountImage() error {
var httpRangeReader *httpreadat.RangeReader
func getInitialVirtualMediaState() (*VirtualMediaState, error) {
cdromEnabled, err := getMassStorageCDROMEnabled()
if err != nil {
return nil, fmt.Errorf("failed to get mass storage cdrom enabled: %w", err)
}
diskPath, err := getMassStorageImage()
if err != nil {
return nil, fmt.Errorf("failed to get mass storage image: %w", err)
}
initialState := &VirtualMediaState{
Source: Storage,
Mode: Disk,
}
if cdromEnabled {
initialState.Mode = CDROM
}
// TODO: check if it's WebRTC or HTTP
if diskPath == "" {
return nil, nil
} else if diskPath == "/dev/nbd0" {
initialState.Source = HTTP
initialState.URL = "/"
initialState.Size = 1
} else {
initialState.Filename = filepath.Base(diskPath)
// get size from file
logger.Info().Str("diskPath", diskPath).Msg("getting file size")
info, err := os.Stat(diskPath)
if err != nil {
return nil, fmt.Errorf("failed to get file info: %w", err)
}
initialState.Size = info.Size()
}
return initialState, nil
}
func setInitialVirtualMediaState() error {
virtualMediaStateMutex.Lock()
defer virtualMediaStateMutex.Unlock()
initialState, err := getInitialVirtualMediaState()
if err != nil {
return fmt.Errorf("failed to get initial virtual media state: %w", err)
}
currentVirtualMediaState = initialState
logger.Info().Interface("initial_virtual_media_state", initialState).Msg("initial virtual media state set")
return nil
}
func rpcMountWithHTTP(url string, mode VirtualMediaMode) error {
virtualMediaStateMutex.Lock()
if currentVirtualMediaState != nil {
@@ -204,6 +270,11 @@ func rpcMountWithHTTP(url string, mode VirtualMediaMode) error {
return fmt.Errorf("failed to use http url: %w", err)
}
logger.Info().Str("url", url).Int64("size", n).Msg("using remote url")
if err := setMassStorageMode(mode == CDROM); err != nil {
return fmt.Errorf("failed to set mass storage mode: %w", err)
}
currentVirtualMediaState = &VirtualMediaState{
Source: HTTP,
Mode: mode,
@@ -243,6 +314,11 @@ func rpcMountWithWebRTC(filename string, size int64, mode VirtualMediaMode) erro
Size: size,
}
virtualMediaStateMutex.Unlock()
if err := setMassStorageMode(mode == CDROM); err != nil {
return fmt.Errorf("failed to set mass storage mode: %w", err)
}
logger.Debug().Interface("currentVirtualMediaState", currentVirtualMediaState).Msg("currentVirtualMediaState")
logger.Debug().Msg("Starting nbd device")
nbdDevice = NewNBDDevice()
@@ -280,6 +356,10 @@ func rpcMountWithStorage(filename string, mode VirtualMediaMode) error {
return fmt.Errorf("failed to get file info: %w", err)
}
if err := setMassStorageMode(mode == CDROM); err != nil {
return fmt.Errorf("failed to set mass storage mode: %w", err)
}
err = setMassStorageImage(fullPath)
if err != nil {
return fmt.Errorf("failed to set mass storage image: %w", err)