sparsify: Let --in-place capture ^C and shut down gracefully (RHBZ#1277122).

When virt-sparsify receives a user quit signal (eg from ^C) it
currently kills virt-sparsify and qemu instantly, meaning any mount +
fstrim in progress is uncleanly stopped.  The (minor) side effect of
this is that the guest filesystem may require a journal replay or fsck
on boot.

Let virt-sparsify capture the user quit signal and shut down
gracefully.

It is not thought that the previous behaviour could cause guest
corruption; see
https://lists.nongnu.org/archive/html/qemu-devel/2015-11/threads.html#00402
for discussion.
This commit is contained in:
Richard W.M. Jones
2015-11-03 15:26:34 +00:00
parent 4e24338625
commit 8e3920a334

View File

@@ -35,8 +35,15 @@ let rec run disk format ignores machine_readable zeroes =
if trace () then g#set_trace true;
if verbose () then g#set_verbose true;
(* Capture ^C and clean up gracefully. *)
let quit = ref false in
let set_quit _ = quit := true in
Sys.set_signal Sys.sigint (Sys.Signal_handle set_quit);
Sys.set_signal Sys.sigquit (Sys.Signal_handle set_quit);
g#set_pgroup true;
try
perform g disk format ignores machine_readable zeroes
perform g disk format ignores machine_readable zeroes quit
with
G.Error msg as exn ->
if g#last_errno () = G.Errno.errno_ENOTSUP then (
@@ -45,7 +52,7 @@ let rec run disk format ignores machine_readable zeroes =
)
else raise exn
and perform g disk format ignores machine_readable zeroes =
and perform g disk format ignores machine_readable zeroes quit =
(* XXX Current limitation of the API. Can remove this hunk in future. *)
let format =
match format with
@@ -71,80 +78,98 @@ and perform g disk format ignores machine_readable zeroes =
let is_read_only_lv = is_read_only_lv g in
List.iter (
fun fs ->
if not (is_ignored fs) && not (is_read_only_lv fs) then (
if List.mem fs zeroes then (
message (f_"Zeroing %s") fs;
let tasks =
List.map (
fun fs () ->
if not (is_ignored fs) && not (is_read_only_lv fs) then (
if List.mem fs zeroes then (
message (f_"Zeroing %s") fs;
if not (g#blkdiscardzeroes fs) then
g#zero_device fs;
g#blkdiscard fs
) else (
let mounted =
try g#mount_options "discard" fs "/"; true
with _ -> false in
if mounted then (
message (f_"Trimming %s") fs;
g#fstrim "/"
if not (g#blkdiscardzeroes fs) then
g#zero_device fs;
g#blkdiscard fs
) else (
let is_linux_x86_swap =
(* Look for the signature for Linux swap on i386.
* Location depends on page size, so it definitely won't
* work on non-x86 architectures (eg. on PPC, page size is
* 64K). Also this avoids hibernated swap space: in those,
* the signature is moved to a different location.
*)
try g#pread_device fs 10 4086L = "SWAPSPACE2"
let mounted =
try g#mount_options "discard" fs "/"; true
with _ -> false in
if is_linux_x86_swap then (
message (f_"Clearing Linux swap on %s") fs;
if mounted then (
message (f_"Trimming %s") fs;
(* Don't use mkswap. Just preserve the header containing
* the label, UUID and swap format version (libguestfs
* mkswap may differ from guest's own).
*)
let header = g#pread_device fs 4096 0L in
g#blkdiscard fs;
if g#pwrite_device fs header 0L <> 4096 then
error (f_"pwrite: short write restoring swap partition header")
g#fstrim "/"
) else (
let is_linux_x86_swap =
(* Look for the signature for Linux swap on i386.
* Location depends on page size, so it definitely won't
* work on non-x86 architectures (eg. on PPC, page size is
* 64K). Also this avoids hibernated swap space: in those,
* the signature is moved to a different location.
*)
try g#pread_device fs 10 4086L = "SWAPSPACE2"
with _ -> false in
if is_linux_x86_swap then (
message (f_"Clearing Linux swap on %s") fs;
(* Don't use mkswap. Just preserve the header containing
* the label, UUID and swap format version (libguestfs
* mkswap may differ from guest's own).
*)
let header = g#pread_device fs 4096 0L in
g#blkdiscard fs;
if g#pwrite_device fs header 0L <> 4096 then
error (f_"pwrite: short write restoring swap partition header")
)
)
)
);
);
g#umount_all ()
)
) filesystems;
g#umount_all ()
)
) filesystems in
(* Discard unused space in volume groups. *)
let vgs = g#vgs () in
let vgs = Array.to_list vgs in
let vgs = List.sort compare vgs in
List.iter (
fun vg ->
if not (List.mem vg ignores) then (
let lvname = String.random8 () in
let lvdev = "/dev/" ^ vg ^ "/" ^ lvname in
let created =
try g#lvcreate_free lvname vg 100; true
with _ -> false in
let tasks = tasks @
List.map (
fun vg () ->
if not (List.mem vg ignores) then (
let lvname = String.random8 () in
let lvdev = "/dev/" ^ vg ^ "/" ^ lvname in
if created then (
message (f_"Discard space in volgroup %s") vg;
let created =
try g#lvcreate_free lvname vg 100; true
with _ -> false in
g#blkdiscard lvdev;
g#sync ();
g#lvremove lvdev
if created then (
message (f_"Discard space in volgroup %s") vg;
g#blkdiscard lvdev;
g#sync ();
g#lvremove lvdev
)
)
)
) vgs;
) vgs in
(* The above calls to List.map just created a list of tasks (thunks)
* to run. Now we actually run that code, keeping an eye on the
* state of the 'quit' flag.
*)
List.iter (
fun task ->
if not !quit then task ();
) tasks;
g#shutdown ();
g#close ();
(* Finished. *)
message (f_"Sparsify in-place operation completed with no errors")
if not !quit then (
(* Finished. *)
message (f_"Sparsify in-place operation completed with no errors")
)
else (
(* User quit. *)
error (f_"quit (^C) at user request")
)