sparsify: Get free space on TMPDIR and give a warning if we estimate it is too little.

This commit is contained in:
Richard W.M. Jones
2013-08-06 13:18:54 +01:00
parent f6b2efcdff
commit 7c463ac477
6 changed files with 153 additions and 9 deletions

5
TODO
View File

@@ -465,11 +465,6 @@ opened with O_CLOEXEC. Therefore we need to examine every call to:
virt-sparsify enhancements virt-sparsify enhancements
-------------------------- --------------------------
TMPDIR should be checked to ensure that we won't run out of space
during the conversion, since current behaviour is very bad when this
happens (it usually causes virt-sparsify to hang). This requires
writing a small C binding to statvfs for OCaml.
'virt-sparsify --whitelist' option to generate skeletons (for 'virt-sparsify --whitelist' option to generate skeletons (for
debugging, bug forensics, diagnosis). The whilelist option would debugging, bug forensics, diagnosis). The whilelist option would
specify a list of files to be *preserved*. All other files in the specify a list of files to be *preserved*. All other files in the

View File

@@ -239,6 +239,7 @@ resize/progress-c.c
resize/tty-c.c resize/tty-c.c
resize/uri-c.c resize/uri-c.c
ruby/ext/guestfs/_guestfs.c ruby/ext/guestfs/_guestfs.c
sparsify/statvfs-c.c
src/actions-0.c src/actions-0.c
src/actions-1.c src/actions-1.c
src/actions-2.c src/actions-2.c

View File

@@ -26,7 +26,8 @@ CLEANFILES = *~ *.cmi *.cmo *.cmx *.cmxa *.o virt-sparsify
# Alphabetical order. # Alphabetical order.
SOURCES = \ SOURCES = \
sparsify.ml sparsify.ml \
statvfs-c.c
if HAVE_OCAML if HAVE_OCAML
@@ -39,6 +40,7 @@ OBJECTS = \
$(top_builddir)/resize/common_utils.cmx \ $(top_builddir)/resize/common_utils.cmx \
$(top_builddir)/resize/tTY.cmx \ $(top_builddir)/resize/tTY.cmx \
$(top_builddir)/resize/progress.cmx \ $(top_builddir)/resize/progress.cmx \
statvfs-c.o \
sparsify.cmx sparsify.cmx
bin_SCRIPTS = virt-sparsify bin_SCRIPTS = virt-sparsify

View File

@@ -26,12 +26,15 @@ module G = Guestfs
open Common_utils open Common_utils
external statvfs_free_space : string -> int64 =
"virt_sparsify_statvfs_free_space"
let () = Random.self_init () let () = Random.self_init ()
(* Command line argument parsing. *) (* Command line argument parsing. *)
let prog = Filename.basename Sys.executable_name let prog = Filename.basename Sys.executable_name
let indisk, outdisk, compress, convert, debug_gc, let indisk, outdisk, check_tmpdir, compress, convert, debug_gc,
format, ignores, machine_readable, format, ignores, machine_readable,
option, quiet, verbose, trace, zeroes = option, quiet, verbose, trace, zeroes =
let display_version () = let display_version () =
@@ -44,6 +47,17 @@ let indisk, outdisk, compress, convert, debug_gc,
let add xs s = xs := s :: !xs in let add xs s = xs := s :: !xs in
let check_tmpdir = ref `Warn in
let set_check_tmpdir = function
| "ignore" | "i" -> check_tmpdir := `Ignore
| "continue" | "cont" | "c" -> check_tmpdir := `Continue
| "warn" | "warning" | "w" -> check_tmpdir := `Warn
| "fail" | "f" | "error" -> check_tmpdir := `Fail
| str ->
eprintf (f_"--check-tmpdir: unknown argument `%s'\n") str;
exit 1
in
let compress = ref false in let compress = ref false in
let convert = ref "" in let convert = ref "" in
let debug_gc = ref false in let debug_gc = ref false in
@@ -57,6 +71,7 @@ let indisk, outdisk, compress, convert, debug_gc,
let zeroes = ref [] in let zeroes = ref [] in
let argspec = Arg.align [ let argspec = Arg.align [
"--check-tmpdir", Arg.String set_check_tmpdir, "ignore|..." ^ " " ^ s_"Check there is enough space in $TMPDIR";
"--compress", Arg.Set compress, " " ^ s_"Compressed output format"; "--compress", Arg.Set compress, " " ^ s_"Compressed output format";
"--convert", Arg.Set_string convert, s_"format" ^ " " ^ s_"Format of output disk (default: same as input)"; "--convert", Arg.Set_string convert, s_"format" ^ " " ^ s_"Format of output disk (default: same as input)";
"--debug-gc", Arg.Set debug_gc, " " ^ s_"Debug GC and memory allocations"; "--debug-gc", Arg.Set debug_gc, " " ^ s_"Debug GC and memory allocations";
@@ -90,6 +105,7 @@ read the man page virt-sparsify(1).
Arg.parse argspec anon_fun usage_msg; Arg.parse argspec anon_fun usage_msg;
(* Dereference the rest of the args. *) (* Dereference the rest of the args. *)
let check_tmpdir = !check_tmpdir in
let compress = !compress in let compress = !compress in
let convert = match !convert with "" -> None | str -> Some str in let convert = match !convert with "" -> None | str -> Some str in
let debug_gc = !debug_gc in let debug_gc = !debug_gc in
@@ -151,7 +167,7 @@ read the man page virt-sparsify(1).
if contains_colon outdisk then if contains_colon outdisk then
error (f_"output filename '%s' contains a colon (':'); qemu-img command line syntax prevents us from using such an image") outdisk; error (f_"output filename '%s' contains a colon (':'); qemu-img command line syntax prevents us from using such an image") outdisk;
indisk, outdisk, compress, convert, indisk, outdisk, check_tmpdir, compress, convert,
debug_gc, format, ignores, machine_readable, debug_gc, format, ignores, machine_readable,
option, quiet, verbose, trace, zeroes option, quiet, verbose, trace, zeroes
@@ -213,9 +229,58 @@ let () =
if output_format = "raw" && compress then if output_format = "raw" && compress then
error (f_"--compress cannot be used for raw output. Remove this option or use --convert qcow2.") error (f_"--compress cannot be used for raw output. Remove this option or use --convert qcow2.")
(* Get virtual size of the input disk. *)
let virtual_size = (new G.guestfs ())#disk_virtual_size indisk
let () = let () =
if not quiet then if not quiet then
printf (f_"Create overlay file to protect source disk ...\n%!") printf (f_"Input disk virtual size = %Ld bytes (%s)\n%!")
virtual_size (human_size virtual_size)
(* Check there is enough space in $TMPDIR. *)
let tmpdir = Filename.get_temp_dir_name ()
let () =
let print_warning () =
let free_space = statvfs_free_space tmpdir in
let extra_needed = virtual_size -^ free_space in
if extra_needed > 0L then (
eprintf (f_"\
WARNING: There may not be enough free space on %s.
You may need to set TMPDIR to point to a directory with more free space.
Max needed: %s. Free: %s. May need another %s.
Note this is an overestimate. If the guest disk is full of data
then not as much free space would be required.
You can ignore this warning or change it to a hard failure using the
--check-tmpdir=(ignore|continue|warn|fail) option. See virt-sysprep(1).
%!")
tmpdir (human_size virtual_size)
(human_size free_space) (human_size extra_needed);
true
) else false
in
match check_tmpdir with
| `Ignore -> ()
| `Continue -> ignore (print_warning ())
| `Warn ->
if print_warning () then (
eprintf "Press RETURN to continue or ^C to quit.\n%!";
ignore (read_line ())
);
| `Fail ->
if print_warning () then (
eprintf "Exiting because --check-tmpdir=fail was set.\n%!";
exit 2
)
let () =
if not quiet then
printf (f_"Create overlay file in %s to protect source disk ...\n%!") tmpdir
(* Create the temporary overlay file. *) (* Create the temporary overlay file. *)
let overlaydisk = let overlaydisk =

47
sparsify/statvfs-c.c Normal file
View File

@@ -0,0 +1,47 @@
/* virt-sparsify - interface to statvfs
* Copyright (C) 2013 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/statvfs.h>
#include <caml/alloc.h>
#include <caml/fail.h>
#include <caml/memory.h>
#include <caml/mlvalues.h>
value
virt_sparsify_statvfs_free_space (value pathv)
{
CAMLparam1 (pathv);
CAMLlocal1 (rv);
struct statvfs buf;
int64_t free_space;
if (statvfs (String_val (pathv), &buf) == -1) {
perror ("statvfs");
caml_failwith ("statvfs");
}
free_space = (int64_t) buf.f_bsize * buf.f_bavail;
rv = caml_copy_int64 (free_space);
CAMLreturn (rv);
}

View File

@@ -113,6 +113,40 @@ image.
Display help. Display help.
=item B<--check-tmpdir=ignore>
=item B<--check-tmpdir=continue>
=item B<--check-tmpdir=warn>
=item B<--check-tmpdir=fail>
Check if L</TMPDIR> has enough space to complete the operation. This
is just an estimate.
If the check indicates a problem, then you can either:
=over 4
=item *
B<ignore> it,
=item *
print a warning and B<continue>,
=item *
B<warn> and wait for the user to press the Return key
(this is the default), or:
=item *
B<fail> and exit.
=back
=item B<--compress> =item B<--compress>
Compress the output file. This I<only> works if the output format is Compress the output file. This I<only> works if the output format is