From 7c463ac477168df5d4e4eb472ba01fa18b89c1a6 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 6 Aug 2013 13:18:54 +0100 Subject: [PATCH] sparsify: Get free space on TMPDIR and give a warning if we estimate it is too little. --- TODO | 5 --- po/POTFILES | 1 + sparsify/Makefile.am | 4 ++- sparsify/sparsify.ml | 71 ++++++++++++++++++++++++++++++++++++-- sparsify/statvfs-c.c | 47 +++++++++++++++++++++++++ sparsify/virt-sparsify.pod | 34 ++++++++++++++++++ 6 files changed, 153 insertions(+), 9 deletions(-) create mode 100644 sparsify/statvfs-c.c diff --git a/TODO b/TODO index 61cde4072..d7a951e18 100644 --- a/TODO +++ b/TODO @@ -465,11 +465,6 @@ opened with O_CLOEXEC. Therefore we need to examine every call to: 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 debugging, bug forensics, diagnosis). The whilelist option would specify a list of files to be *preserved*. All other files in the diff --git a/po/POTFILES b/po/POTFILES index 3c5e34ee0..2e9a00baf 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -239,6 +239,7 @@ resize/progress-c.c resize/tty-c.c resize/uri-c.c ruby/ext/guestfs/_guestfs.c +sparsify/statvfs-c.c src/actions-0.c src/actions-1.c src/actions-2.c diff --git a/sparsify/Makefile.am b/sparsify/Makefile.am index 723c9ec1b..7d05db4e9 100644 --- a/sparsify/Makefile.am +++ b/sparsify/Makefile.am @@ -26,7 +26,8 @@ CLEANFILES = *~ *.cmi *.cmo *.cmx *.cmxa *.o virt-sparsify # Alphabetical order. SOURCES = \ - sparsify.ml + sparsify.ml \ + statvfs-c.c if HAVE_OCAML @@ -39,6 +40,7 @@ OBJECTS = \ $(top_builddir)/resize/common_utils.cmx \ $(top_builddir)/resize/tTY.cmx \ $(top_builddir)/resize/progress.cmx \ + statvfs-c.o \ sparsify.cmx bin_SCRIPTS = virt-sparsify diff --git a/sparsify/sparsify.ml b/sparsify/sparsify.ml index 819a14576..05447881e 100644 --- a/sparsify/sparsify.ml +++ b/sparsify/sparsify.ml @@ -26,12 +26,15 @@ module G = Guestfs open Common_utils +external statvfs_free_space : string -> int64 = + "virt_sparsify_statvfs_free_space" + let () = Random.self_init () (* Command line argument parsing. *) 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, option, quiet, verbose, trace, zeroes = let display_version () = @@ -44,6 +47,17 @@ let indisk, outdisk, compress, convert, debug_gc, 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 convert = ref "" in let debug_gc = ref false in @@ -57,6 +71,7 @@ let indisk, outdisk, compress, convert, debug_gc, let zeroes = ref [] in 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"; "--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"; @@ -90,6 +105,7 @@ read the man page virt-sparsify(1). Arg.parse argspec anon_fun usage_msg; (* Dereference the rest of the args. *) + let check_tmpdir = !check_tmpdir in let compress = !compress in let convert = match !convert with "" -> None | str -> Some str in let debug_gc = !debug_gc in @@ -151,7 +167,7 @@ read the man page virt-sparsify(1). 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; - indisk, outdisk, compress, convert, + indisk, outdisk, check_tmpdir, compress, convert, debug_gc, format, ignores, machine_readable, option, quiet, verbose, trace, zeroes @@ -213,9 +229,58 @@ let () = if output_format = "raw" && compress then 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 () = 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. *) let overlaydisk = diff --git a/sparsify/statvfs-c.c b/sparsify/statvfs-c.c new file mode 100644 index 000000000..ce72d5b7d --- /dev/null +++ b/sparsify/statvfs-c.c @@ -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 + +#include +#include +#include + +#include +#include +#include +#include + +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); +} diff --git a/sparsify/virt-sparsify.pod b/sparsify/virt-sparsify.pod index b6c4cce9d..84e94c5d7 100644 --- a/sparsify/virt-sparsify.pod +++ b/sparsify/virt-sparsify.pod @@ -113,6 +113,40 @@ image. 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 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 it, + +=item * + +print a warning and B, + +=item * + +B and wait for the user to press the Return key +(this is the default), or: + +=item * + +B and exit. + +=back + =item B<--compress> Compress the output file. This I works if the output format is