Blame SOURCES/0018-qemu-nbd-Implement-output-compression-for-qcow2-file.patch

c1a9fa
From 80831868395d161af8c47edf2f54234c63581d8d Mon Sep 17 00:00:00 2001
c1a9fa
From: "Richard W.M. Jones" <rjones@redhat.com>
c1a9fa
Date: Fri, 28 Jan 2022 09:30:29 +0000
c1a9fa
Subject: [PATCH] qemu-nbd: Implement output compression for qcow2 files
c1a9fa
c1a9fa
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
c1a9fa
(cherry picked from commit 71c4301909cb307def02ebcd0e89beee4138e7f2)
c1a9fa
---
c1a9fa
 lib/qemuNBD.ml    | 11 +++++++++--
c1a9fa
 lib/qemuNBD.mli   |  5 +++++
c1a9fa
 output/output.ml  | 39 ++++++++++++++++++++++++++++++++++++---
c1a9fa
 output/output.mli |  1 +
c1a9fa
 4 files changed, 51 insertions(+), 5 deletions(-)
c1a9fa
c1a9fa
diff --git a/lib/qemuNBD.ml b/lib/qemuNBD.ml
c1a9fa
index ae21b17c..bbb65f41 100644
c1a9fa
--- a/lib/qemuNBD.ml
c1a9fa
+++ b/lib/qemuNBD.ml
c1a9fa
@@ -55,14 +55,16 @@ type cmd = {
c1a9fa
   disk : string;
c1a9fa
   mutable snapshot : bool;
c1a9fa
   mutable format : string option;
c1a9fa
+  mutable imgopts : bool;
c1a9fa
 }
c1a9fa
 
c1a9fa
-let create disk = { disk; snapshot = false; format = None }
c1a9fa
+let create disk = { disk; snapshot = false; format = None; imgopts = false }
c1a9fa
 
c1a9fa
 let set_snapshot cmd snap = cmd.snapshot <- snap
c1a9fa
 let set_format cmd format = cmd.format <- format
c1a9fa
+let set_image_opts cmd imgopts = cmd.imgopts <- imgopts
c1a9fa
 
c1a9fa
-let run_unix socket { disk; snapshot; format } =
c1a9fa
+let run_unix socket { disk; snapshot; format; imgopts } =
c1a9fa
   assert (disk <> "");
c1a9fa
 
c1a9fa
   (* Create a temporary directory where we place the PID file. *)
c1a9fa
@@ -85,6 +87,11 @@ let run_unix socket { disk; snapshot; format } =
c1a9fa
   (* -s adds a protective overlay. *)
c1a9fa
   if snapshot then List.push_back args "-s";
c1a9fa
 
c1a9fa
+  (* --image-opts reinterprets the filename parameter as a set of
c1a9fa
+   * image options.
c1a9fa
+   *)
c1a9fa
+  if imgopts then List.push_back args "--image-opts";
c1a9fa
+
c1a9fa
   if have_selinux && qemu_nbd_has_selinux_label_option () then (
c1a9fa
     List.push_back args "--selinux-label";
c1a9fa
     List.push_back args "system_u:object_r:svirt_socket_t:s0"
c1a9fa
diff --git a/lib/qemuNBD.mli b/lib/qemuNBD.mli
c1a9fa
index e10d3106..afe9d944 100644
c1a9fa
--- a/lib/qemuNBD.mli
c1a9fa
+++ b/lib/qemuNBD.mli
c1a9fa
@@ -43,6 +43,11 @@ val set_snapshot : cmd -> bool -> unit
c1a9fa
 val set_format : cmd -> string option -> unit
c1a9fa
 (** Set the format [--format] parameter. *)
c1a9fa
 
c1a9fa
+val set_image_opts : cmd -> bool -> unit
c1a9fa
+(** Set whether the [--image-opts] parameter is used.  This changes
c1a9fa
+    the meaning of the [filename] parameter to a set of image options.
c1a9fa
+    Consult the qemu-nbd man page for more details. *)
c1a9fa
+
c1a9fa
 val run_unix : string -> cmd -> string * int
c1a9fa
 (** Start qemu-nbd command listening on a Unix domain socket,
c1a9fa
     waiting for the process to start up.
c1a9fa
diff --git a/output/output.ml b/output/output.ml
c1a9fa
index 5c6670b9..23c3932d 100644
c1a9fa
--- a/output/output.ml
c1a9fa
+++ b/output/output.ml
c1a9fa
@@ -69,7 +69,7 @@ let error_if_disk_count_gt dir n =
c1a9fa
   if Sys.file_exists socket then
c1a9fa
     error (f_"this output module doesn't support copying more than %d disks") n
c1a9fa
 
c1a9fa
-let output_to_local_file ?(changeuid = fun f -> f ())
c1a9fa
+let output_to_local_file ?(changeuid = fun f -> f ()) ?(compressed = false)
c1a9fa
       output_alloc output_format filename size socket =
c1a9fa
   (* Check nbdkit is installed and has the required plugin. *)
c1a9fa
   if not (Nbdkit.is_installed ()) then
c1a9fa
@@ -78,6 +78,24 @@ let output_to_local_file ?(changeuid = fun f -> f ())
c1a9fa
     error (f_"nbdkit-file-plugin is not installed or not working");
c1a9fa
   let nbdkit_config = Nbdkit.config () in
c1a9fa
 
c1a9fa
+  if compressed then (
c1a9fa
+    (* Only allow compressed with -of qcow2. *)
c1a9fa
+    if output_format <> "qcow2" then
c1a9fa
+      error (f_"‘-oo compressed’ is only allowed when the output format \
c1a9fa
+                is a local qcow2-format file, i.e. ‘-of qcow2’");
c1a9fa
+
c1a9fa
+    (* Check nbdcopy is new enough.  This assumes that the version of
c1a9fa
+     * libnbd is the same as the version of nbdcopy, but parsing this
c1a9fa
+     * is easier.  We can remove this check when we build-depend on
c1a9fa
+     * libnbd >= 1.14.
c1a9fa
+     *)
c1a9fa
+    let version =
c1a9fa
+      NBD.create () |> NBD.get_version |>
c1a9fa
+      String.nsplit "." |> List.map int_of_string in
c1a9fa
+    if version < [1; 13; 5] then
c1a9fa
+      error (f_"-oo compressed option requires nbdcopy >= 1.13.5")
c1a9fa
+  );
c1a9fa
+
c1a9fa
   let g = open_guestfs () in
c1a9fa
   let preallocation =
c1a9fa
     match output_alloc with
c1a9fa
@@ -103,9 +121,24 @@ let output_to_local_file ?(changeuid = fun f -> f ())
c1a9fa
      On_exit.kill pid
c1a9fa
 
c1a9fa
   | "qcow2" ->
c1a9fa
-     let cmd = QemuNBD.create filename in
c1a9fa
+     let cmd =
c1a9fa
+       if compressed then (
c1a9fa
+         let qemu_quote str = String.replace str "," ",," in
c1a9fa
+         let image_opts = [ "driver=compress";
c1a9fa
+                            "file.driver=qcow2";
c1a9fa
+                            "file.file.driver=file";
c1a9fa
+                            "file.file.filename=" ^ qemu_quote filename ] in
c1a9fa
+         let image_opts = String.concat "," image_opts in
c1a9fa
+         let cmd = QemuNBD.create image_opts in
c1a9fa
+         QemuNBD.set_image_opts cmd true;
c1a9fa
+         cmd
c1a9fa
+       )
c1a9fa
+       else (* not compressed *) (
c1a9fa
+         let cmd = QemuNBD.create filename in
c1a9fa
+         QemuNBD.set_format cmd (Some "qcow2");
c1a9fa
+         cmd
c1a9fa
+       ) in
c1a9fa
      QemuNBD.set_snapshot cmd false;
c1a9fa
-     QemuNBD.set_format cmd (Some "qcow2");
c1a9fa
      let _, pid = QemuNBD.run_unix socket cmd in
c1a9fa
      On_exit.kill pid
c1a9fa
 
c1a9fa
diff --git a/output/output.mli b/output/output.mli
c1a9fa
index 8d3d6865..c1f0f53d 100644
c1a9fa
--- a/output/output.mli
c1a9fa
+++ b/output/output.mli
c1a9fa
@@ -84,6 +84,7 @@ val error_if_disk_count_gt : string -> int -> unit
c1a9fa
     called. *)
c1a9fa
 
c1a9fa
 val output_to_local_file : ?changeuid:((unit -> unit) -> unit) ->
c1a9fa
+                           ?compressed:bool ->
c1a9fa
                            Types.output_allocation ->
c1a9fa
                            string -> string -> int64 -> string ->
c1a9fa
                            unit