Add qcow2.xz file format support
This commit is contained in:
		@@ -137,6 +137,9 @@ pub const PROGRAM_COPY: &str = "/bin/cp";
 | 
			
		||||
/// Gzip program path
 | 
			
		||||
pub const PROGRAM_GZIP: &str = "/usr/bin/gzip";
 | 
			
		||||
 | 
			
		||||
/// XZ program path
 | 
			
		||||
pub const PROGRAM_XZ: &str = "/usr/bin/xz";
 | 
			
		||||
 | 
			
		||||
/// Bash program
 | 
			
		||||
pub const PROGRAM_BASH: &str = "/usr/bin/bash";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,10 @@ pub enum DiskFileFormat {
 | 
			
		||||
        #[serde(default)]
 | 
			
		||||
        virtual_size: FileSize,
 | 
			
		||||
    },
 | 
			
		||||
    CompressedRaw,
 | 
			
		||||
    CompressedQCow2,
 | 
			
		||||
    GzCompressedRaw,
 | 
			
		||||
    GzCompressedQCow2,
 | 
			
		||||
    XzCompressedRaw,
 | 
			
		||||
    XzCompressedQCow2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DiskFileFormat {
 | 
			
		||||
@@ -37,8 +39,10 @@ impl DiskFileFormat {
 | 
			
		||||
        match self {
 | 
			
		||||
            DiskFileFormat::Raw { .. } => &["raw", ""],
 | 
			
		||||
            DiskFileFormat::QCow2 { .. } => &["qcow2"],
 | 
			
		||||
            DiskFileFormat::CompressedRaw => &["raw.gz"],
 | 
			
		||||
            DiskFileFormat::CompressedQCow2 => &["qcow2.gz"],
 | 
			
		||||
            DiskFileFormat::GzCompressedRaw => &["raw.gz"],
 | 
			
		||||
            DiskFileFormat::GzCompressedQCow2 => &["qcow2.gz"],
 | 
			
		||||
            DiskFileFormat::XzCompressedRaw => &["raw.xz"],
 | 
			
		||||
            DiskFileFormat::XzCompressedQCow2 => &["qcow2.xz"],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -81,9 +85,14 @@ impl DiskFileInfo {
 | 
			
		||||
            },
 | 
			
		||||
            "gz" if name.ends_with(".qcow2") => {
 | 
			
		||||
                name = name.strip_suffix(".qcow2").unwrap_or(&name).to_string();
 | 
			
		||||
                DiskFileFormat::CompressedQCow2
 | 
			
		||||
                DiskFileFormat::GzCompressedQCow2
 | 
			
		||||
            }
 | 
			
		||||
            "gz" => DiskFileFormat::CompressedRaw,
 | 
			
		||||
            "gz" => DiskFileFormat::GzCompressedRaw,
 | 
			
		||||
            "xz" if name.ends_with(".qcow2") => {
 | 
			
		||||
                name = name.strip_suffix(".qcow2").unwrap_or(&name).to_string();
 | 
			
		||||
                DiskFileFormat::XzCompressedQCow2
 | 
			
		||||
            }
 | 
			
		||||
            "xz" => DiskFileFormat::XzCompressedRaw,
 | 
			
		||||
            _ => anyhow::bail!("Unsupported disk extension: {ext}!"),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -159,8 +168,8 @@ impl DiskFileInfo {
 | 
			
		||||
 | 
			
		||||
        // Prepare the conversion
 | 
			
		||||
        let mut cmd = match (self.format, dest_format) {
 | 
			
		||||
            // Decompress QCow2
 | 
			
		||||
            (DiskFileFormat::CompressedQCow2, DiskFileFormat::QCow2 { .. }) => {
 | 
			
		||||
            // Decompress QCow2 (GZIP)
 | 
			
		||||
            (DiskFileFormat::GzCompressedQCow2, DiskFileFormat::QCow2 { .. }) => {
 | 
			
		||||
                let mut cmd = Command::new(constants::PROGRAM_GZIP);
 | 
			
		||||
                cmd.arg("--keep")
 | 
			
		||||
                    .arg("--decompress")
 | 
			
		||||
@@ -170,8 +179,19 @@ impl DiskFileInfo {
 | 
			
		||||
                cmd
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Compress QCow2
 | 
			
		||||
            (DiskFileFormat::QCow2 { .. }, DiskFileFormat::CompressedQCow2) => {
 | 
			
		||||
            // Decompress QCow2 (XZ)
 | 
			
		||||
            (DiskFileFormat::XzCompressedQCow2, DiskFileFormat::QCow2 { .. }) => {
 | 
			
		||||
                let mut cmd = Command::new(constants::PROGRAM_XZ);
 | 
			
		||||
                cmd.arg("--stdout")
 | 
			
		||||
                    .arg("--keep")
 | 
			
		||||
                    .arg("--decompress")
 | 
			
		||||
                    .arg(&self.file_path)
 | 
			
		||||
                    .stdout(File::create(&temp_file)?);
 | 
			
		||||
                cmd
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Compress QCow2 (Gzip)
 | 
			
		||||
            (DiskFileFormat::QCow2 { .. }, DiskFileFormat::GzCompressedQCow2) => {
 | 
			
		||||
                let mut cmd = Command::new(constants::PROGRAM_GZIP);
 | 
			
		||||
                cmd.arg("--keep")
 | 
			
		||||
                    .arg("--to-stdout")
 | 
			
		||||
@@ -180,6 +200,16 @@ impl DiskFileInfo {
 | 
			
		||||
                cmd
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Compress QCow2 (Xz)
 | 
			
		||||
            (DiskFileFormat::QCow2 { .. }, DiskFileFormat::XzCompressedQCow2) => {
 | 
			
		||||
                let mut cmd = Command::new(constants::PROGRAM_XZ);
 | 
			
		||||
                cmd.arg("--keep")
 | 
			
		||||
                    .arg("--to-stdout")
 | 
			
		||||
                    .arg(&self.file_path)
 | 
			
		||||
                    .stdout(File::create(&temp_file)?);
 | 
			
		||||
                cmd
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Convert QCow2 to Raw file
 | 
			
		||||
            (DiskFileFormat::QCow2 { .. }, DiskFileFormat::Raw { is_sparse }) => {
 | 
			
		||||
                let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE);
 | 
			
		||||
@@ -245,7 +275,7 @@ impl DiskFileInfo {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Compress Raw
 | 
			
		||||
            (DiskFileFormat::Raw { .. }, DiskFileFormat::CompressedRaw) => {
 | 
			
		||||
            (DiskFileFormat::Raw { .. }, DiskFileFormat::GzCompressedRaw) => {
 | 
			
		||||
                let mut cmd = Command::new(constants::PROGRAM_GZIP);
 | 
			
		||||
                cmd.arg("--keep")
 | 
			
		||||
                    .arg("--to-stdout")
 | 
			
		||||
@@ -255,7 +285,7 @@ impl DiskFileInfo {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Decompress Raw to not sparse file
 | 
			
		||||
            (DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: false }) => {
 | 
			
		||||
            (DiskFileFormat::GzCompressedRaw, DiskFileFormat::Raw { is_sparse: false }) => {
 | 
			
		||||
                let mut cmd = Command::new(constants::PROGRAM_GZIP);
 | 
			
		||||
                cmd.arg("--keep")
 | 
			
		||||
                    .arg("--decompress")
 | 
			
		||||
@@ -267,7 +297,7 @@ impl DiskFileInfo {
 | 
			
		||||
 | 
			
		||||
            // Decompress Raw to sparse file
 | 
			
		||||
            // https://benou.fr/www/ben/decompressing-sparse-files.html
 | 
			
		||||
            (DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: true }) => {
 | 
			
		||||
            (DiskFileFormat::GzCompressedRaw, DiskFileFormat::Raw { is_sparse: true }) => {
 | 
			
		||||
                let mut cmd = Command::new(constants::PROGRAM_BASH);
 | 
			
		||||
                cmd.arg("-c").arg(format!(
 | 
			
		||||
                    "{} -d -c {} | {} conv=sparse of={}",
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@ import { VMFileDisk, VMInfo } from "./VMApi";
 | 
			
		||||
export type DiskImageFormat =
 | 
			
		||||
  | { format: "Raw"; is_sparse: boolean }
 | 
			
		||||
  | { format: "QCow2"; virtual_size?: number }
 | 
			
		||||
  | { format: "CompressedQCow2" }
 | 
			
		||||
  | { format: "CompressedRaw" };
 | 
			
		||||
  | { format: "GzCompressedQCow2" }
 | 
			
		||||
  | { format: "GzCompressedRaw" };
 | 
			
		||||
 | 
			
		||||
export type DiskImage = {
 | 
			
		||||
  file_size: number;
 | 
			
		||||
 
 | 
			
		||||
@@ -42,13 +42,15 @@ export function ConvertDiskImageDialog(
 | 
			
		||||
    setFormat({ format: value ?? ("QCow2" as any) });
 | 
			
		||||
 | 
			
		||||
    if (value === "QCow2") setFilename(`${origFilename}.qcow2`);
 | 
			
		||||
    if (value === "CompressedQCow2") setFilename(`${origFilename}.qcow2.gz`);
 | 
			
		||||
    if (value === "GzCompressedQCow2") setFilename(`${origFilename}.qcow2.gz`);
 | 
			
		||||
    if (value === "XzCompressedQCow2") setFilename(`${origFilename}.qcow2.xz`);
 | 
			
		||||
    if (value === "Raw") {
 | 
			
		||||
      setFilename(`${origFilename}.raw`);
 | 
			
		||||
      // Check sparse checkbox by default
 | 
			
		||||
      setFormat({ format: "Raw", is_sparse: true });
 | 
			
		||||
    }
 | 
			
		||||
    if (value === "CompressedRaw") setFilename(`${origFilename}.raw.gz`);
 | 
			
		||||
    if (value === "GzCompressedRaw") setFilename(`${origFilename}.raw.gz`);
 | 
			
		||||
    if (value === "XzCompressedRaw") setFilename(`${origFilename}.raw.xz`);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleSubmit = async () => {
 | 
			
		||||
@@ -104,8 +106,10 @@ export function ConvertDiskImageDialog(
 | 
			
		||||
          options={[
 | 
			
		||||
            { value: "QCow2" },
 | 
			
		||||
            { value: "Raw" },
 | 
			
		||||
            { value: "CompressedRaw" },
 | 
			
		||||
            { value: "CompressedQCow2" },
 | 
			
		||||
            { value: "GzCompressedRaw" },
 | 
			
		||||
            { value: "XzCompressedRaw" },
 | 
			
		||||
            { value: "GzCompressedQCow2" },
 | 
			
		||||
            { value: "XzCompressedQCow2" },
 | 
			
		||||
          ]}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user