diff --git a/cmd/run.go b/cmd/run.go index cfc95cb..419fe46 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -33,10 +33,33 @@ import ( var runCmd = &cobra.Command{ Use: "run", Short: "Start a VM and expose an FTP file share.", - Args: cobra.ExactArgs(3), + Args: cobra.RangeArgs(1, 3), Run: func(cmd *cobra.Command, args []string) { - vmMountDevName := args[1] - fsType := args[2] + var luksContainerDevice string + + vmMountDevName := "vdb" + + if luksContainerFlag != "" { + if luksContainerEntireDriveFlag { + slog.Error("--luks-container and --luks-container-entire-drive (-c) cannot be both specified at once") + os.Exit(1) + } + + luksContainerDevice = luksContainerFlag + } else if luksContainerEntireDriveFlag { + luksContainerDevice = vmMountDevName + } + + if len(args) > 1 { + vmMountDevName = args[1] + } else if luksContainerDevice != "" { + slog.Error("Cannot use the default (entire) device with a LUKS container. Please specify the in-VM device name to mount as a second positional argument.") + } + + var fsTypeOverride string + if len(args) > 2 { + fsTypeOverride = args[2] + } newBackendFunc := share.GetBackend(shareBackendFlag) if newBackendFunc == nil { @@ -61,7 +84,7 @@ var runCmd = &cobra.Command{ os.Exit(1) } - if (luksFlag || luksContainerFlag != "") && !allowLUKSLowMemoryFlag { + if (luksFlag || luksContainerDevice != "") && !allowLUKSLowMemoryFlag { if vmMemAllocFlag < defaultMemAllocLUKS { if vmMemAllocFlag != defaultMemAlloc { slog.Warn("Enforcing minimum LUKS memory allocation. Please add --allow-luks-low-memory to disable this.", "min", vmMemAllocFlag, "specified", vmMemAllocFlag) @@ -72,13 +95,18 @@ var runCmd = &cobra.Command{ } os.Exit(runVM(args[0], func(ctx context.Context, i *vm.VM, fm *vm.FileManager, tapCtx *share.NetTapRuntimeContext) int { - slog.Info("Mounting the device", "dev", vmMountDevName, "fs", fsType, "luks", luksFlag) + fsToLog := "" + if fsTypeOverride != "" { + fsToLog = fsTypeOverride + } + + slog.Info("Mounting the device", "dev", vmMountDevName, "fs", fsToLog, "luks", luksFlag) err := fm.Mount(vmMountDevName, vm.MountOptions{ - LUKSContainerPreopen: luksContainerFlag, + LUKSContainerPreopen: luksContainerDevice, - FSType: fsType, - LUKS: luksFlag, + FSTypeOverride: fsTypeOverride, + LUKS: luksFlag, }) if err != nil { slog.Error("Failed to mount the disk inside the VM", "error", err.Error()) @@ -129,19 +157,21 @@ var runCmd = &cobra.Command{ } var ( - luksFlag bool - luksContainerFlag string - allowLUKSLowMemoryFlag bool - shareListenIPFlag string - ftpExtIPFlag string - shareBackendFlag string - smbUseExternAddrFlag bool - debugShellFlag bool + luksFlag bool + luksContainerFlag string + luksContainerEntireDriveFlag bool + allowLUKSLowMemoryFlag bool + shareListenIPFlag string + ftpExtIPFlag string + shareBackendFlag string + smbUseExternAddrFlag bool + debugShellFlag bool ) func init() { runCmd.Flags().BoolVarP(&luksFlag, "luks", "l", false, "Use cryptsetup to open a LUKS volume (password will be prompted).") runCmd.Flags().StringVar(&luksContainerFlag, "luks-container", "", `Specifies a device path (without "dev/" prefix) to preopen as a LUKS container (password will be prompted). Useful for accessing LVM partitions behind LUKS.`) + runCmd.Flags().BoolVarP(&luksContainerEntireDriveFlag, "luks-container-entire-drive", "c", false, `Similar to --luks-container, but this assumes that the entire passed-through volume is a LUKS container (password will be prompted).`) runCmd.Flags().BoolVar(&allowLUKSLowMemoryFlag, "allow-luks-low-memory", false, "Allow VM memory allocation lower than 2048 MiB when LUKS is enabled.") runCmd.Flags().BoolVar(&debugShellFlag, "debug-shell", false, "Start a VM shell when the network file share is active.") diff --git a/cmd/utils.go b/cmd/utils.go index c13e395..b87f75e 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -286,8 +286,6 @@ func getDevicePassthroughConfig(val string) (*vm.PassthroughConfig, error) { slog.Warn("RISK WARNING: Skipping device block size detection and using the default of 512 bytes. Please use this ONLY to recover data from disks with filesystem that were locked to emulated 512-byte block size.") - // TODO: Remove the need to specify the FS type manually. - devPath := filepath.Clean(valSplit[1]) err := osspecifics.CheckValidDevicePath(devPath) diff --git a/utils/validations.go b/utils/validations.go index e69b7ad..01752c2 100644 --- a/utils/validations.go +++ b/utils/validations.go @@ -37,6 +37,12 @@ func ClearUnprintableChars(s string, allowNewlines bool) string { }, s) } +var fsTypeRegex = regexp.MustCompile(`^[a-z0-9]+$`) + +func ValidateFsType(s string) bool { + return fsTypeRegex.MatchString(s) +} + var devNameRegexp = regexp.MustCompile(`^[0-9a-z_-]+$`) func ValidateDevName(s string) bool { diff --git a/vm/filemanager.go b/vm/filemanager.go index 73f3fff..61882d0 100644 --- a/vm/filemanager.go +++ b/vm/filemanager.go @@ -85,8 +85,8 @@ func (fm *FileManager) Lsblk() ([]byte, error) { type MountOptions struct { LUKSContainerPreopen string - FSType string - LUKS bool + FSTypeOverride string + LUKS bool } func (fm *FileManager) luksOpen(sc *ssh.Client, fullDevPath string, luksDMName string) error { @@ -186,8 +186,14 @@ func (fm *FileManager) Mount(devName string, mo MountOptions) error { // Windows, but we're targeting a Linux VM.) fullDevPath := "/dev/" + devName - if mo.FSType == "" { - return fmt.Errorf("fs type is empty") + var fsOverride string + + if mo.FSTypeOverride != "" { + if !utils.ValidateFsType(mo.FSTypeOverride) { + return fmt.Errorf("bad fs type override (contains illegal characters)") + } + + fsOverride = mo.FSTypeOverride } sc, err := fm.vm.DialSSH() @@ -228,7 +234,13 @@ func (fm *FileManager) Mount(devName string, mo MountOptions) error { fullDevPath = "/dev/mapper/" + luksDMName } - _, err = sshutil.RunSSHCmd(fm.vm.ctx, sc, "mount -t "+shellescape.Quote(mo.FSType)+" "+shellescape.Quote(fullDevPath)+" /mnt") + cmd := "mount " + if fsOverride != "" { + cmd += "-t " + shellescape.Quote(fsOverride) + " " + } + cmd += shellescape.Quote(fullDevPath) + " /mnt" + + _, err = sshutil.RunSSHCmd(fm.vm.ctx, sc, cmd) if err != nil { return errors.Wrap(err, "run mount cmd") }