From d7796e14594cc616e761c0bf9027863c639d3318 Mon Sep 17 00:00:00 2001
From: Pierre HUBERT <pierre.git@communiquons.org>
Date: Mon, 9 Jun 2025 17:04:35 +0200
Subject: [PATCH] Can decompress XZ files

---
 virtweb_backend/src/utils/file_disks_utils.rs | 30 +++++++++++++++++--
 virtweb_frontend/src/api/DiskImageApi.ts      |  4 ++-
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/virtweb_backend/src/utils/file_disks_utils.rs b/virtweb_backend/src/utils/file_disks_utils.rs
index 012f894..98a2ced 100644
--- a/virtweb_backend/src/utils/file_disks_utils.rs
+++ b/virtweb_backend/src/utils/file_disks_utils.rs
@@ -284,7 +284,7 @@ impl DiskFileInfo {
                 cmd
             }
 
-            // Decompress Raw to not sparse file
+            // Decompress Raw (Gz) to not sparse file
             (DiskFileFormat::GzCompressedRaw, DiskFileFormat::Raw { is_sparse: false }) => {
                 let mut cmd = Command::new(constants::PROGRAM_GZIP);
                 cmd.arg("--keep")
@@ -294,13 +294,23 @@ impl DiskFileInfo {
                     .stdout(File::create(&temp_file)?);
                 cmd
             }
+            // Decompress Raw (Xz) to not sparse file
+            (DiskFileFormat::XzCompressedRaw, DiskFileFormat::Raw { is_sparse: false }) => {
+                let mut cmd = Command::new(constants::PROGRAM_XZ);
+                cmd.arg("--keep")
+                    .arg("--decompress")
+                    .arg("--to-stdout")
+                    .arg(&self.file_path)
+                    .stdout(File::create(&temp_file)?);
+                cmd
+            }
 
-            // Decompress Raw to sparse file
+            // Decompress Raw (Gz) to sparse file
             // https://benou.fr/www/ben/decompressing-sparse-files.html
             (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={}",
+                    "{} --decompress --to-stdout {} | {} conv=sparse of={}",
                     constants::PROGRAM_GZIP,
                     self.file_path.display(),
                     constants::PROGRAM_DD,
@@ -309,6 +319,20 @@ impl DiskFileInfo {
                 cmd
             }
 
+            // Decompress Raw (XZ) to sparse file
+            // https://benou.fr/www/ben/decompressing-sparse-files.html
+            (DiskFileFormat::XzCompressedRaw, DiskFileFormat::Raw { is_sparse: true }) => {
+                let mut cmd = Command::new(constants::PROGRAM_BASH);
+                cmd.arg("-c").arg(format!(
+                    "{} --decompress --to-stdout {} | {} conv=sparse of={}",
+                    constants::PROGRAM_XZ,
+                    self.file_path.display(),
+                    constants::PROGRAM_DD,
+                    temp_file.display()
+                ));
+                cmd
+            }
+
             // Dumb copy of file
             (a, b) if a == b => {
                 let mut cmd = Command::new(constants::PROGRAM_COPY);
diff --git a/virtweb_frontend/src/api/DiskImageApi.ts b/virtweb_frontend/src/api/DiskImageApi.ts
index bcca6c4..d07c359 100644
--- a/virtweb_frontend/src/api/DiskImageApi.ts
+++ b/virtweb_frontend/src/api/DiskImageApi.ts
@@ -5,7 +5,9 @@ export type DiskImageFormat =
   | { format: "Raw"; is_sparse: boolean }
   | { format: "QCow2"; virtual_size?: number }
   | { format: "GzCompressedQCow2" }
-  | { format: "GzCompressedRaw" };
+  | { format: "GzCompressedRaw" }
+  | { format: "XzCompressedQCow2" }
+  | { format: "XzCompressedRaw" };
 
 export type DiskImage = {
   file_size: number;