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