Steve: See patch below.
Rich. On Wed, Sep 26, 2012 at 04:05:47PM +0800, Wanlong Gao wrote: > add new virt-diff tool > > Signed-off-by: Wanlong Gao <[email protected]> > --- > cat/Makefile.am | 35 +++- > cat/virt-diff.c | 589 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > cat/virt-diff.pod | 214 ++++++++++++++++++++ > po/POTFILES | 2 + > 4 files changed, 837 insertions(+), 3 deletions(-) > create mode 100644 cat/virt-diff.c > create mode 100755 cat/virt-diff.pod > > diff --git a/cat/Makefile.am b/cat/Makefile.am > index 3549248..dd4b80f 100644 > --- a/cat/Makefile.am > +++ b/cat/Makefile.am > @@ -27,7 +27,7 @@ EXTRA_DIST = \ > > CLEANFILES = stamp-virt-cat.pod stamp-virt-ls.pod stamp-virt-filesystems.pod > > -bin_PROGRAMS = virt-cat virt-filesystems virt-ls > +bin_PROGRAMS = virt-cat virt-filesystems virt-ls virt-diff > > SHARED_SOURCE_FILES = \ > ../fish/config.c \ > @@ -91,13 +91,32 @@ virt_ls_LDADD = \ > $(top_builddir)/src/libguestfs.la \ > ../gnulib/lib/libgnu.la > > +virt_diff_SOURCES = \ > + ../fish/keys.c \ > + virt-diff.c > + > +virt_diff_CFLAGS = \ > + -DGUESTFS_WARN_DEPRECATED=1 \ > + -I$(top_srcdir)/src -I$(top_builddir)/src \ > + -I$(top_srcdir)/fish \ > + -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ > + -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ > + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ > + $(LIBCONFIG_CFLAGS) > + > +virt_diff_LDADD = \ > + $(LIBCONFIG_LIBS) \ > + $(top_builddir)/src/libguestfs.la \ > + ../gnulib/lib/libgnu.la > + > # Manual pages and HTML files for the website. > -man_MANS = virt-cat.1 virt-filesystems.1 virt-ls.1 > +man_MANS = virt-cat.1 virt-filesystems.1 virt-ls.1 virt-diff.1 > > noinst_DATA = \ > $(top_builddir)/html/virt-cat.1.html \ > $(top_builddir)/html/virt-filesystems.1.html \ > - $(top_builddir)/html/virt-ls.1.html > + $(top_builddir)/html/virt-ls.1.html \ > + $(top_builddir)/html/virt-diff.1.html > > virt-cat.1 $(top_builddir)/html/virt-cat.1.html: stamp-virt-cat.pod > > @@ -129,6 +148,16 @@ stamp-virt-filesystems.pod: virt-filesystems.pod > $< > touch $@ > > +virt-diff.1 $(top_builddir)/html/virt-diff.1.html: stamp-virt-diff.pod > + > +stamp-virt-diff.pod: virt-diff.pod > + $(PODWRAPPER) \ > + --man virt-diff.1 \ > + --html $(top_builddir)/html/virt-diff.1.html \ > + --license GPLv2+ \ > + $< > + touch $@ > + > # Tests. > > TESTS_ENVIRONMENT = $(top_builddir)/run --test > diff --git a/cat/virt-diff.c b/cat/virt-diff.c > new file mode 100644 > index 0000000..1ebcd0b > --- /dev/null > +++ b/cat/virt-diff.c > @@ -0,0 +1,589 @@ > +/* virt-diff > + * Copyright (C) 2012 Fujitsu Limited. > + * Copyright (C) 2012 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 <termios.h> > +#include <string.h> > +#include <inttypes.h> > +#include <unistd.h> > +#include <getopt.h> > +#include <fcntl.h> > +#include <locale.h> > +#include <assert.h> > +#include <time.h> > +#include <libintl.h> > +#include <sys/wait.h> > + > +#include "c-ctype.h" > + > +#include "human.h" > +#include "progname.h" > + > +#include "guestfs.h" > +#include "options.h" > + > +#ifndef O_CLOEXEC > +#define O_CLOEXEC 0 > +#endif > + > +int verbose = 0; > +int keys_from_stdin = 0; > +int echo_keys = 0; > +/* libguestfs handle for seed. */ > +static guestfs_h *sg; > + > +/* libguestfs handle for temp. */ > +static guestfs_h *tg; > + > +const char *libvirt_uri = NULL; > + > +static inline char * > +diff_bad_case (char const *s) > +{ > + return (char *) s; > +} > + > +/* Make a LUKS map name from the partition name, > + * eg "/dev/vda2" => "luksvda2" > + */ > +static void > +diff_make_mapname (const char *device, char *mapname, size_t len) > +{ > + size_t i = 0; > + > + if (len < 5) > + abort (); > + strcpy (mapname, "luks"); > + mapname += 4; > + len -= 4; > + > + if (STRPREFIX (device, "/dev/")) > + i = 5; > + > + for (; device[i] != '\0' && len >= 1; ++i) { > + if (c_isalnum (device[i])) { > + *mapname++ = device[i]; > + len--; > + } > + } > + > + *mapname = '\0'; > +} > + > +static void > +free_strings (char **strings) > +{ > + size_t i; > + > + for (i = 0; strings[i] != NULL; ++i) > + free (strings[i]); > + free (strings); > +} > + > +static size_t > +count_strings (char **strings) > +{ > + size_t i; > + > + for (i = 0; strings[i] != NULL; ++i) > + ; > + return i; > +} > + > +/* Simple implementation of decryption: look for any crypto_LUKS > + * partitions and decrypt them, then rescan for VGs. This only works > + * for Fedora whole-disk encryption. WIP to make this work for other > + * encryption schemes. > + */ > +static void > +diff_inspect_do_decrypt (guestfs_h *g) > +{ > + char **partitions = guestfs_list_partitions (g); > + if (partitions == NULL) > + exit (EXIT_FAILURE); > + > + int need_rescan = 0; > + size_t i; > + for (i = 0; partitions[i] != NULL; ++i) { > + char *type = guestfs_vfs_type (g, partitions[i]); > + if (type && STREQ (type, "crypto_LUKS")) { > + char mapname[32]; > + diff_make_mapname (partitions[i], mapname, sizeof mapname); > + > + char *key = read_key (partitions[i]); > + /* XXX Should we call guestfs_luks_open_ro if readonly flag > + * is set? This might break 'mount_ro'. > + */ > + if (guestfs_luks_open (g, partitions[i], key, mapname) == -1) > + exit (EXIT_FAILURE); > + > + free (key); > + > + need_rescan = 1; > + } > + free (type); > + } > + > + free_strings (partitions); > + > + if (need_rescan) { > + if (guestfs_vgscan (g) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_vg_activate_all (g, 1) == -1) > + exit (EXIT_FAILURE); > + } > +} > + > +static int > +compare_keys (const void *p1, const void *p2) > +{ > + const char *key1 = * (char * const *) p1; > + const char *key2 = * (char * const *) p2; > + > + return strcmp (key1, key2); > +} > + > +static int > +compare_keys_len (const void *p1, const void *p2) > +{ > + const char *key1 = * (char * const *) p1; > + const char *key2 = * (char * const *) p2; > + int c; > + > + c = strlen (key1) - strlen (key2); > + if (c != 0) > + return c; > + > + return compare_keys (p1, p2); > +} > + > +static void > +diff_inspect_mount_root (guestfs_h *g, const char *root) > +{ > + char **mountpoints = guestfs_inspect_get_mountpoints (g, root); > + if (mountpoints == NULL) > + exit (EXIT_FAILURE); > + > + /* Sort by key length, shortest key first, so that we end up > + * mounting the filesystems in the correct order. > + */ > + qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *), > + compare_keys_len); > + > + size_t i; > + size_t mount_errors = 0; > + for (i = 0; mountpoints[i] != NULL; i += 2) { > + int r; > + r = guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]); > + if (r == -1) { > + /* If the "/" filesystem could not be mounted, give up, else > + * just count the errors and print a warning. > + */ > + if (STREQ (mountpoints[i], "/")) > + exit (EXIT_FAILURE); > + mount_errors++; > + } > + } > + > + free_strings (mountpoints); > + > + if (mount_errors) > + fprintf (stderr, _("%s: some filesystems could not be mounted > (ignored)\n"), > + program_name); > +} > + > +/* This function implements the -i option. */ > +static void > +diff_inspect_mount (guestfs_h *g) > +{ > + const char *root = NULL; > + diff_inspect_do_decrypt (g); > + > + char **roots = guestfs_inspect_os (g); > + if (roots == NULL) > + exit (EXIT_FAILURE); > + > + if (roots[0] == NULL) { > + fprintf (stderr, > + _("%s: no operating system was found on this disk\n"), program_name); > + free_strings (roots); > + exit (EXIT_FAILURE); > + } > + > + if (roots[1] != NULL) { > + fprintf (stderr, > + _("%s: multi-boot operating systems are not supported\n"), > program_name); > + free_strings (roots); > + exit (EXIT_FAILURE); > + } > + > + root = roots[0]; > + free (roots); > + > + diff_inspect_mount_root (g, root); > +} > + > +static void > +free_drive (struct drv *drv) > +{ > + if (!drv) return; > + > + free (drv->device); > + free (drv); > +} > + > +static int > +diff_command (const char *stmpdir, const char *ttmpdir, const char *dir) > +{ > + char *spath = NULL, *tpath = NULL; > + pid_t pid; > + > + if (asprintf (&spath, "%s%s", stmpdir, dir) == -1) > + goto error; > + > + if (asprintf (&tpath, "%s%s", ttmpdir, dir) == -1) > + goto error; > + > + pid = fork(); > + if (pid == -1) > + goto error; > + > + if (pid == 0) { > + execlp ("diff", "diff", "-urN", spath, tpath, NULL); > + perror ("running command: diff"); > + _exit (EXIT_FAILURE); > + } > + > + if (waitpid (pid, NULL, 0) == -1) > + goto error; > + > + return 0; > + > +error: > + perror ("running command: diff"); > + if (spath) free (spath); > + if (tpath) free (tpath); > + return -1; > +} > + > +static void __attribute__((noreturn)) > +diff_usage (int status) > +{ > + if (status != EXIT_SUCCESS) { > + fprintf (stderr, _("Try `%s --help' for more information.\n"), > + program_name); > + } else { > + fprintf (stdout, > + _("%s: Show the differences between seed and target Guest\n" > + "Copyright (C) 2012 Fujitsu Limited.\n" > + "Copyright (C) 2012 Red Hat Inc.\n" > + "Usage:\n" > + " %s [--options] -s domname -t domname path\n" > + " %s [--options] -a disk.img -d disk.img path\n" > + "Options:\n" > + " -c|--connect uri Specify libvirt URI for -s and -m > options\n" > + " -s|--sd guest Add seed disk from libvirt guest\n" > + " -t|--td guest Add target disk from libvirt guest\n" > + " -a|--sa image Add seed disk from disk image\n" > + " -d|--ta image Add target disk from libvirt guest\n" > + " --format[=raw|..] Force disk format for -a and -d > options\n" > + " --keys-from-stdin Read passphrases from stdin\n" > + " --echo-keys Don't turn off echo for passphrases\n" > + " --help Display brief help\n" > + " -v|--verbose Verbose messages\n" > + " -V|--version Display version and exit\n" > + " -x Trace libguestfs API calls\n" > + "For more information, see the manpage %s(1).\n"), > + program_name, program_name, program_name, program_name); > + } > + exit (status); > +} > + > +int > +main (int argc, char *argv[]) > +{ > + /* set global program name that is not polluted with libtool artifacts. */ > + set_program_name (argv[0]); > + bindtextdomain (PACKAGE, LOCALEBASEDIR); > + textdomain (PACKAGE); > + > + enum { HELP_OPTION = CHAR_MAX + 1 }; > + > + static const char *options = "c:a:s:d:t:vVx"; > + static const struct option long_options[] = { > + { "connect", 1, 0, 'c' }, > + { "sa", 1, 0, 'a' }, > + { "sd", 1, 0, 's' }, > + { "ta", 1, 0, 'd' }, > + { "td", 1, 0, 't' }, > + { "format", 2, 0, 0 }, > + { "keys-from-stdin", 0, 0, 0 }, > + { "echo-keys", 0, 0, 0 }, > + { "help", 0, 0, HELP_OPTION }, > + { "verbose", 0, 0, 'v' }, > + { "version", 0, 0, 'V' }, > + {0, 0, 0, 0} > + }; > + > + struct drv *sdrv = NULL; > + struct drv *tdrv = NULL; > + const char *format = NULL; > + int c, nr; > + int option_index; > + int spid, tpid; > + > + sg = guestfs_create (); > + if (sg == NULL) { > + fprintf (stderr, _("guestfs_create: failed to create seed handle\n")); > + exit (EXIT_FAILURE); > + } > + > + tg = guestfs_create (); > + if (tg == NULL) { > + fprintf (stderr, _("guestfs_create: failed to create comparison > handle\n")); > + exit (EXIT_FAILURE); > + } > + > + argv[0] = diff_bad_case (program_name); > + > + for (;;) { > + c = getopt_long (argc, argv, options, long_options, &option_index); > + if (c == -1) break; > + > + switch (c) { > + case 0: > + if (STREQ (long_options[option_index].name, "keys-from-stdin")) { > + keys_from_stdin = 1; > + } else if (STREQ (long_options[option_index].name, "echo-keys")) { > + echo_keys = 1; > + } else if (STREQ (long_options[option_index].name, "format")) { > + if (!optarg || STREQ (optarg, "")) > + format = NULL; > + else > + format = optarg; > + } else { > + fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), > + program_name, long_options[option_index].name, > option_index); > + exit (EXIT_FAILURE); > + } > + break; > + > + case 'c': > + libvirt_uri = optarg; > + break; > + > + case 's': > + if (sdrv) { > + fprintf(stderr, _("Only one seed device")); > + exit (EXIT_FAILURE); > + } > + sdrv = calloc (1, sizeof (struct drv)); > + if (!sdrv) { > + perror ("malloc"); > + exit (EXIT_FAILURE); > + } > + sdrv->type = drv_d; > + sdrv->nr_drives = -1; > + sdrv->d.guest = optarg; > + sdrv->next = NULL; > + break; > + > + case 't': > + if (tdrv) { > + fprintf (stderr, _("Only one diff device")); > + exit (EXIT_FAILURE); > + } > + tdrv = calloc (1, sizeof (struct drv)); > + if (!tdrv) { > + perror ("malloc"); > + exit (EXIT_FAILURE); > + } > + tdrv->type = drv_d; > + tdrv->nr_drives = -1; > + tdrv->d.guest = optarg; > + tdrv->next = NULL; > + break; > + > + case 'a': > + if (sdrv) { > + fprintf (stderr, _("Only one seed device")); > + exit (EXIT_FAILURE); > + } > + sdrv = calloc (1, sizeof (struct drv)); > + if (!sdrv) { > + perror ("malloc"); > + exit (EXIT_FAILURE); > + } > + sdrv->type = drv_a; > + sdrv->nr_drives = -1; > + sdrv->a.filename = optarg; > + sdrv->a.format = format; > + sdrv->next = NULL; > + break; > + > + case 'd': > + if (tdrv) { > + fprintf (stderr, _("Only one diff device")); > + exit (EXIT_FAILURE); > + } > + tdrv = calloc (1, sizeof (struct drv)); > + if (!tdrv) { > + perror ("malloc"); > + exit (EXIT_FAILURE); > + } > + tdrv->type = drv_a; > + tdrv->nr_drives = -1; > + tdrv->a.filename = optarg; > + tdrv->a.format = format; > + tdrv->next = NULL; > + break; > + > + case HELP_OPTION: > + diff_usage (EXIT_SUCCESS); > + > + case 'v': > + verbose++; > + guestfs_set_verbose (sg, verbose); > + guestfs_set_verbose (tg, verbose); > + break; > + > + case 'V': > + { > + struct guestfs_version *v = guestfs_version (sg); > + printf ("%s %"PRIi64".%"PRIi64".%"PRIi64"%s\n", > + program_name, > + v->major, v->minor, v->release, v->extra); > + exit (EXIT_SUCCESS); > + } > + break; > + > + case 'x': > + guestfs_set_trace (sg, 1); > + guestfs_set_trace (tg, 1); > + break; > + > + default: > + diff_usage (EXIT_FAILURE); > + } > + } > + > + if (sdrv == NULL) > + diff_usage (EXIT_FAILURE); > + if (tdrv == NULL) > + diff_usage (EXIT_FAILURE); > + > + struct guestfs_add_domain_argv optargs = { > + .bitmask = 0, > + .libvirturi = libvirt_uri, > + .readonly = 1, > + .allowuuid = 1, > + .readonlydisk = "read", > + }; > + > + nr = guestfs_add_domain_argv (sg, sdrv->d.guest, &optargs); > + if (nr == -1) > + exit (EXIT_FAILURE); > + sdrv->nr_drives = nr; > + nr = guestfs_add_domain_argv (tg, tdrv->d.guest, &optargs); > + if (nr == -1) > + exit (EXIT_FAILURE); > + tdrv->nr_drives = nr; > + > + if (guestfs_launch (sg) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_launch (tg) == -1) > + exit (EXIT_FAILURE); > + > + diff_inspect_mount (sg); > + diff_inspect_mount (tg); > + > + char stempdir[] = "/tmp/sGuestXXXXXX"; > + char ttempdir[] = "/tmp/tGuestXXXXXX"; > + > + if (mkdtemp (stempdir) == NULL) { > + perror ("mkdtemp"); > + exit (EXIT_FAILURE); > + } > + if (mkdtemp (ttempdir) == NULL) { > + perror ("mkdtemp"); > + exit (EXIT_FAILURE); > + } > + > + if (guestfs_mount_local (sg, stempdir, -1) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_mount_local (tg, ttempdir, -1) == -1) > + exit (EXIT_FAILURE); > + > + spid = fork (); > + if (spid == -1) { > + perror ("fork"); > + exit (EXIT_FAILURE); > + } > + if (spid == 0) { > + if (guestfs_mount_local_run (sg) == -1) > + exit (EXIT_FAILURE); > + _exit (EXIT_SUCCESS); > + } > + > + tpid = fork(); > + if (tpid == -1) { > + perror ("fork"); > + exit (EXIT_FAILURE); > + } > + if (tpid == 0) { > + if (guestfs_mount_local_run (tg) == -1) > + exit (EXIT_FAILURE); > + _exit (EXIT_SUCCESS); > + } > + > + const char *dir = argv[optind]; > + > + if (diff_command (stempdir, ttempdir, dir) == -1) > + exit (EXIT_FAILURE); > + > + if (guestfs_umount_local (sg, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1) == -1) > + exit (EXIT_FAILURE); > + if (waitpid (spid, NULL, 0) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_umount_local (tg, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1) == -1) > + exit (EXIT_FAILURE); > + if (waitpid (tpid, NULL, 0) == -1) > + exit (EXIT_FAILURE); > + > + if (rmdir (stempdir) == -1) > + exit (EXIT_FAILURE); > + if (rmdir (ttempdir) == -1) > + exit (EXIT_FAILURE); > + > + if (guestfs_shutdown (sg) == -1) > + exit (EXIT_FAILURE); > + if (guestfs_shutdown (tg) == -1) > + exit (EXIT_FAILURE); > + > + free_drive (sdrv); > + free_drive (tdrv); > + > + guestfs_close (sg); > + guestfs_close (tg); > + > + exit (EXIT_SUCCESS); > +} > diff --git a/cat/virt-diff.pod b/cat/virt-diff.pod > new file mode 100755 > index 0000000..45e9b74 > --- /dev/null > +++ b/cat/virt-diff.pod > @@ -0,0 +1,214 @@ > +=encoding utf8 > + > +=head1 NAME > + > +virt-diff - Compare files between two virtual machines > + > +=head1 SYNOPSIS > + > + virt-diff [--options] -s domname -d domname file > + > + virt-diff [--options] -a disk.img -d disk.img file > + > + > +=head1 DESCRIPTION > + > +C<virt-diff> is a command line tool to compare files or directories > +between two virtual machines. > + > +=head1 EXAMPLES > + > +Compare C</etc/fstab> file from inside the libvirt VM called > +C<sdomain> to inside the libvirt VM called C<ddomain>: > + > + virt-diff -s sdomain -m ddomain /etc/fstab > + > +Compare C</etc/hostname> file from a VM disk image C<a.img> > +to another VM disk image C<b.img>: > + > + virt-diff -a a.img -d b.img /etc/hostname > + > +Compare C</etc/> directory recursely from one libvirt VM called > +C<sdomain> to another libvirt VM called C<ddomain>: > + > + virt-diff -s sdomain -m ddomain /etc > + > +=head1 OPTIONS > + > +=over 4 > + > +=item B<--help> > + > +Display brief help. > + > +=item B<-c> URI > + > +=item B<--connect> URI > + > +If using libvirt, connect to the given I<URI>. If omitted, then we > +connect to the default libvirt hypervisor. > + > +If you specify guest block devices directly (I<-a>), then libvirt is > +not used at all. > + > +=item B<-s> guest > + > +=item B<--sd> guest > + > +Add the seed disks from the named libvirt guest. Domain UUIDs can be > +used instead of names. > + > +=item B<-t> guest > + > +=item B<--td> guest > + > +Add the taget disks from the named libvirt guest. Domain UUIDs can be > +used instead of names. > + > +=item B<-a> file > + > +=item B<--sa> file > + > +Add the seed disk image which should be a disk image from a virtual machine. > + > +The format of the disk image is auto-detected. To override this and > +force a particular format use the I<--format=..> option. > + > +=item B<-d> file > + > +=item B<--ta> file > + > +Add the targe disk image which should be a disk image from a virtual machine. > + > +The format of the disk image is auto-detected. To override this and > +force a particular format use the I<--format=..> option. > + > +=item B<--echo-keys> > + > +When prompting for keys and passphrases, virt-diff normally turns > +echoing off so you cannot see what you are typing. If you are not > +worried about Tempest attacks and there is no one else in the room you > +can specify this flag to see what you are typing. > + > +=item B<--format=raw|qcow2|..> > + > +=item B<--format> > + > +The default for the I<-a> and I<-d> option is to auto-detect the format > +of the disk image. Using this forces the disk format for I<-a> and I<-d> > +options which follow on the command line. Using I<--format> with no argument > +switches back to auto-detection for subsequent I<-a> and I<-d> options. > + > +For example: > + > + virt-diff --format=qcow2 -a disk1.img -d disk2.img file > + > +forces qcow2 format (no auto-detection) for C<disk1.img> and C<disk2.img>. > + > + virt-diff --format=raw -a disk.img --format -d another.img file > + > +forces raw format (no auto-detection) for C<disk.img> and reverts to > +auto-detection for C<another.img>. > + > +If you have untrusted raw-format guest disk images, you should use > +this option to specify the disk format. This avoids a possible > +security problem with malicious guests (CVE-2010-3851). > + > +=item B<--keys-from-stdin> > + > +Read key or passphrase parameters from stdin. The default is > +to try to read passphrases from the user by opening C</dev/tty>. > + > +=item B<-v> > + > +=item B<--verbose> > + > +Enable verbose messages for debugging. > + > +=item B<-V> > + > +=item B<--version> > + > +Display version number and exit. > + > +=item B<-x> > + > +Enable tracing of libguestfs API calls. > + > +=back > + > +=head1 WINDOWS PATHS > + > +C<virt-diff> has a limited ability to understand Windows drive letters > +and paths (eg. C<E:\foo\bar.txt>). > + > +If and only if the guest is running Windows then: > + > +=over 4 > + > +=item * > + > +Drive letter prefixes like C<C:> are resolved against the > +Windows Registry to the correct filesystem. > + > +=item * > + > +Any backslash (C<\>) characters in the path are replaced > +with forward slashes so that libguestfs can process it. > + > +=item * > + > +The path is resolved case insensitively to locate the file > +that should be displayed. > + > +=back > + > +There are some known shortcomings: > + > +=over 4 > + > +=item * > + > +Some NTFS symbolic links may not be followed correctly. > + > +=item * > + > +NTFS junction points that cross filesystems are not followed. > + > +=back > + > + > +=head1 SHELL QUOTING > + > +Libvirt guest names can contain arbitrary characters, some of which > +have meaning to the shell such as C<#> and space. You may need to > +quote or escape these characters on the command line. See the shell > +manual page L<sh(1)> for details. > + > +=head1 EXIT STATUS > + > +This program returns 0 if successful, or non-zero if there was an > +error. > + > +=head1 SEE ALSO > + > +L<guestfs(3)>, > +L<guestfish(1)>, > +L<virt-copy-out(1)>, > +L<virt-cat(1)>, > +L<virt-ls(1)>, > +L<virt-edit(1)>, > +L<virt-tar-out(1)>, > +L<http://libguestfs.org/>. > + > +=head1 AUTHOR > + > +Wanlong Gao, Fujitsu Ltd. > + > +Richard W.M. Jones L<http://people.redhat.com/~rjones/> > + > +=head1 COPYRIGHT > + > +Copyright (C) 2012 Fujitsu Ltd. > + > +Copyright (C) 2012 Red Hat Inc. > diff --git a/po/POTFILES b/po/POTFILES > index 548156c..3625ce6 100644 > --- a/po/POTFILES > +++ b/po/POTFILES > @@ -1,6 +1,7 @@ > align/domains.c > align/scan.c > cat/virt-cat.c > +cat/virt-diff.c > cat/virt-filesystems.c > cat/virt-ls.c > daemon/9p.c > @@ -136,6 +137,7 @@ fish/tilde.c > fish/time.c > format/format.c > fuse/guestmount.c > +gobject/docs/guestfs-scan.c > gobject/src/optargs-add_domain.c > gobject/src/optargs-add_drive.c > gobject/src/optargs-btrfs_filesystem_resize.c > -- > 1.7.12.1.401.gb5d156c -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/ _______________________________________________ Libguestfs mailing list [email protected] https://www.redhat.com/mailman/listinfo/libguestfs
