diff --git a/internal/usbgadget/config.go b/internal/usbgadget/config.go
index b73d392..5c287da 100644
--- a/internal/usbgadget/config.go
+++ b/internal/usbgadget/config.go
@@ -137,6 +137,29 @@ func (u *UsbGadget) GetPath(itemKey string) (string, error) {
return joinPath(u.kvmGadgetPath, item.path), nil
}
+// OverrideGadgetConfig overrides the gadget config for the given item and attribute.
+// It returns an error if the item is not found or the attribute is not found.
+// It returns true if the attribute is overridden, false otherwise.
+func (u *UsbGadget) OverrideGadgetConfig(itemKey string, itemAttr string, value string) (error, bool) {
+ u.configLock.Lock()
+ defer u.configLock.Unlock()
+
+ // get it as a pointer
+ _, ok := u.configMap[itemKey]
+ if !ok {
+ return fmt.Errorf("config item %s not found", itemKey), false
+ }
+
+ if u.configMap[itemKey].attrs[itemAttr] == value {
+ return nil, false
+ }
+
+ u.configMap[itemKey].attrs[itemAttr] = value
+ u.log.Info().Str("itemKey", itemKey).Str("itemAttr", itemAttr).Str("value", value).Msg("overriding gadget config")
+
+ return nil, true
+}
+
func mountConfigFS() error {
_, err := os.Stat(gadgetPath)
// TODO: check if it's mounted properly
diff --git a/jsonrpc.go b/jsonrpc.go
index 05db3d5..3c805e4 100644
--- a/jsonrpc.go
+++ b/jsonrpc.go
@@ -566,9 +566,12 @@ type RPCHandler struct {
func rpcSetMassStorageMode(mode string) (string, error) {
logger.Info().Str("mode", mode).Msg("Setting mass storage mode")
var cdrom bool
- if mode == "cdrom" {
+ switch mode {
+ case "cdrom":
cdrom = true
- } else if mode != "file" {
+ case "file":
+ cdrom = false
+ default:
logger.Info().Str("mode", mode).Msg("Invalid mode provided")
return "", fmt.Errorf("invalid mode: %s", mode)
}
@@ -587,7 +590,7 @@ func rpcSetMassStorageMode(mode string) (string, error) {
}
func rpcGetMassStorageMode() (string, error) {
- cdrom, err := getMassStorageMode()
+ cdrom, err := getMassStorageCDROMEnabled()
if err != nil {
return "", fmt.Errorf("failed to get mass storage mode: %w", err)
}
diff --git a/ui/src/routes/devices.$id.mount.tsx b/ui/src/routes/devices.$id.mount.tsx
index 74fcae2..4d3369a 100644
--- a/ui/src/routes/devices.$id.mount.tsx
+++ b/ui/src/routes/devices.$id.mount.tsx
@@ -414,7 +414,7 @@ function BrowserFileView({
if (file?.name.endsWith(".iso")) {
setUsbMode("CDROM");
} else if (file?.name.endsWith(".img")) {
- setUsbMode("CDROM");
+ setUsbMode("Disk");
}
};
@@ -566,7 +566,7 @@ function UrlView({
if (url.endsWith(".iso")) {
setUsbMode("CDROM");
} else if (url.endsWith(".img")) {
- setUsbMode("CDROM");
+ setUsbMode("Disk");
}
}
@@ -773,7 +773,7 @@ function DeviceFileView({
if (file.name.endsWith(".iso")) {
setUsbMode("CDROM");
} else if (file.name.endsWith(".img")) {
- setUsbMode("CDROM");
+ setUsbMode("Disk");
}
}
@@ -1579,7 +1579,6 @@ function UsbModeSelector({
type="radio"
id="disk"
name="mountType"
- disabled
checked={usbMode === "Disk"}
onChange={() => setUsbMode("Disk")}
className="h-3 w-3 border-slate-800/30 bg-white text-blue-700 transition-opacity focus:ring-blue-500 disabled:opacity-30 dark:bg-slate-800"
@@ -1588,9 +1587,6 @@ function UsbModeSelector({
Disk
-
- Coming soon
-
diff --git a/usb_mass_storage.go b/usb_mass_storage.go
index 79a05d1..8d4b1f1 100644
--- a/usb_mass_storage.go
+++ b/usb_mass_storage.go
@@ -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"))
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,36 @@ 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)
+ }
+
+ source := Storage
+ // TODO: check if it's WebRTC or HTTP
+ if diskPath == "/dev/nbd0" {
+ source = HTTP
+ }
+
+ mode := Disk
+ if cdromEnabled {
+ mode = CDROM
+ }
+
+ return &VirtualMediaState{
+ Source: source,
+ Mode: mode,
+ URL: "",
+ Size: 0,
+ }, nil
+}
+
func rpcMountWithHTTP(url string, mode VirtualMediaMode) error {
virtualMediaStateMutex.Lock()
if currentVirtualMediaState != nil {
@@ -204,6 +246,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 +290,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 +332,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)