diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2012-02-27 12:00:57 +0000 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2012-02-27 13:43:35 +0000 |
commit | e567f064e235f23ef709d4ed52834fb2135c6956 (patch) | |
tree | 158318adc792cda391de877e8f9184111ce7d750 | |
parent | 51aa51884e8dfa22b489e958743bf8f3ac3785cc (diff) | |
download | libguestfs-e567f064e235f23ef709d4ed52834fb2135c6956.tar.gz libguestfs-e567f064e235f23ef709d4ed52834fb2135c6956.tar.xz libguestfs-e567f064e235f23ef709d4ed52834fb2135c6956.zip |
New APIs: ntfsclone-in, ntfsclone-out.
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | daemon/Makefile.am | 1 | ||||
-rw-r--r-- | daemon/ntfsclone.c | 212 | ||||
-rw-r--r-- | generator/generator_actions.ml | 27 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | src/MAX_PROC_NR | 2 | ||||
-rw-r--r-- | tests/ntfsclone/Makefile.am | 30 | ||||
-rwxr-xr-x | tests/ntfsclone/test-ntfsclone.sh | 54 |
10 files changed, 328 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am index e6211456..dfee9c49 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,7 @@ SUBDIRS += tests/protocol SUBDIRS += tests/lvm SUBDIRS += tests/luks SUBDIRS += tests/md +SUBDIRS += tests/ntfsclone SUBDIRS += tests/regressions endif @@ -385,8 +385,6 @@ ntfslabel: display or change filesystem label (we should unify all set*label APIs into a single set_vfs_label which can deal with any filesystem) -ntfsclone: clone, image, restore, rescue NTFS - ntfscluster: display file(s) that occupy a cluster or sector ntfsinfo: print various information about NTFS volume and files diff --git a/configure.ac b/configure.ac index 31a2cc55..c07609a6 100644 --- a/configure.ac +++ b/configure.ac @@ -1146,6 +1146,7 @@ AC_CONFIG_FILES([Makefile tests/luks/Makefile tests/lvm/Makefile tests/md/Makefile + tests/ntfsclone/Makefile tests/protocol/Makefile tests/qemu/Makefile tests/regressions/Makefile diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 3a698cc7..2dd8149f 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -137,6 +137,7 @@ guestfsd_SOURCES = \ mount.c \ names.c \ ntfs.c \ + ntfsclone.c \ optgroups.c \ optgroups.h \ parted.c \ diff --git a/daemon/ntfsclone.c b/daemon/ntfsclone.c new file mode 100644 index 00000000..4287edbe --- /dev/null +++ b/daemon/ntfsclone.c @@ -0,0 +1,212 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2009-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 <inttypes.h> +#include <string.h> +#include <unistd.h> + +#include "read-file.h" + +#include "daemon.h" +#include "actions.h" +#include "optgroups.h" + +/* Read the error file. Returns a string that the caller must free. */ +static char * +read_error_file (char *error_file) +{ + size_t len; + char *str; + + str = read_file (error_file, &len); + if (str == NULL) { + str = strdup ("(no error)"); + if (str == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + len = strlen (str); + } + + /* Remove trailing \n character if any. */ + if (len > 0 && str[len-1] == '\n') + str[--len] = '\0'; + + return str; /* caller frees */ +} + +static int +write_cb (void *fd_ptr, const void *buf, size_t len) +{ + int fd = *(int *)fd_ptr; + return xwrite (fd, buf, len); +} + +/* Has one FileIn parameter. */ +int +do_ntfsclone_in (const char *device) +{ + int err, r; + FILE *fp; + char *cmd; + char error_file[] = "/tmp/ntfscloneXXXXXX"; + int fd; + + fd = mkstemp (error_file); + if (fd == -1) { + reply_with_perror ("mkstemp"); + return -1; + } + + close (fd); + + /* Construct the command. */ + if (asprintf_nowarn (&cmd, "ntfsclone -O %s --restore-image - 2> %s", + device, error_file) == -1) { + err = errno; + r = cancel_receive (); + errno = err; + reply_with_perror ("asprintf"); + unlink (error_file); + return -1; + } + + if (verbose) + fprintf (stderr, "%s\n", cmd); + + fp = popen (cmd, "w"); + if (fp == NULL) { + err = errno; + r = cancel_receive (); + errno = err; + reply_with_perror ("%s", cmd); + unlink (error_file); + free (cmd); + return -1; + } + free (cmd); + + /* The semantics of fwrite are too undefined, so write to the + * file descriptor directly instead. + */ + fd = fileno (fp); + + r = receive_file (write_cb, &fd); + if (r == -1) { /* write error */ + cancel_receive (); + char *errstr = read_error_file (error_file); + reply_with_error ("write error on device: %s: %s", device, errstr); + free (errstr); + unlink (error_file); + pclose (fp); + return -1; + } + if (r == -2) { /* cancellation from library */ + /* This error is ignored by the library since it initiated the + * cancel. Nevertheless we must send an error reply here. + */ + reply_with_error ("ntfsclone cancelled"); + pclose (fp); + unlink (error_file); + return -1; + } + + if (pclose (fp) != 0) { + char *errstr = read_error_file (error_file); + reply_with_error ("ntfsclone subcommand failed on device: %s: %s", + device, errstr); + free (errstr); + unlink (error_file); + return -1; + } + + unlink (error_file); + + return 0; +} + +/* Has one FileOut parameter. */ +/* Takes optional arguments, consult optargs_bitmask. */ +int +do_ntfsclone_out (const char *device, + int metadataonly, int rescue, int ignorefscheck, + int preservetimestamps, int force) +{ + int r; + FILE *fp; + char *cmd; + char buf[GUESTFS_MAX_CHUNK_SIZE]; + + /* Construct the ntfsclone command. */ + if (asprintf (&cmd, "ntfsclone -o - --save-image%s%s%s%s%s %s", + (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_METADATAONLY_BITMASK) && metadataonly ? " --metadata" : "", + (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_RESCUE_BITMASK) && rescue ? " --rescue" : "", + (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_IGNOREFSCHECK_BITMASK) && ignorefscheck ? " --ignore-fs-check" : "", + (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_PRESERVETIMESTAMPS_BITMASK) && preservetimestamps ? " --preserve-timestamps" : "", + (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_FORCE_BITMASK) && force ? " --force" : "", + device) == -1) { + reply_with_perror ("asprintf"); + return -1; + } + + if (verbose) + fprintf (stderr, "%s\n", cmd); + + fp = popen (cmd, "r"); + if (fp == NULL) { + reply_with_perror ("%s", cmd); + free (cmd); + return -1; + } + free (cmd); + + /* Now we must send the reply message, before the file contents. After + * this there is no opportunity in the protocol to send any error + * message back. Instead we can only cancel the transfer. + */ + reply (NULL, NULL); + + while ((r = fread (buf, 1, sizeof buf, fp)) > 0) { + if (send_file_write (buf, r) < 0) { + pclose (fp); + return -1; + } + } + + if (ferror (fp)) { + perror (device); + send_file_end (1); /* Cancel. */ + pclose (fp); + return -1; + } + + if (pclose (fp) != 0) { + perror (device); + send_file_end (1); /* Cancel. */ + return -1; + } + + if (send_file_end (0)) /* Normal end of file. */ + return -1; + + return 0; +} diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index 9df9ea5a..2d24e6ae 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -6682,6 +6682,33 @@ scan the filesystem for inconsistencies. The optional C<clearbadsectors> flag clears the list of bad sectors. This is useful after cloning a disk with bad sectors to a new disk."); + ("ntfsclone_out", (RErr, [Device "device"; FileOut "backupfile"], [OBool "metadataonly"; OBool "rescue"; OBool "ignorefscheck"; OBool "preservetimestamps"; OBool "force"]), 308, [Optional "ntfs3g"; Cancellable], + [], (* tested in tests/ntfsclone *) + "save NTFS to backup file", + "\ +Stream the NTFS filesystem C<device> to the local file +C<backupfile>. The format used for the backup file is a +special format used by the L<ntfsclone(8)> tool. + +If the optional C<metadataonly> flag is true, then I<only> the +metadata is saved, losing all the user data (this is useful +for diagnosing some filesystem problems). + +The optional C<rescue>, C<ignorefscheck>, C<preservetimestamps> +and C<force> flags have precise meanings detailed in the +L<ntfsclone(8)> man page. + +Use C<guestfs_ntfsclone_in> to restore the file back to a +libguestfs device."); + + ("ntfsclone_in", (RErr, [FileIn "backupfile"; Device "device"], []), 309, [Optional "ntfs3g"; Cancellable], + [], (* tested in tests/ntfsclone *) + "restore NTFS from backup file", + "\ +Restore the C<backupfile> (from a previous call to +C<guestfs_ntfsclone_out>) to C<device>, overwriting +any existing contents of this device."); + ] let all_functions = non_daemon_functions @ daemon_functions diff --git a/po/POTFILES.in b/po/POTFILES.in index 3a43d176..bed1f657 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -56,6 +56,7 @@ daemon/modprobe.c daemon/mount.c daemon/names.c daemon/ntfs.c +daemon/ntfsclone.c daemon/optgroups.c daemon/parted.c daemon/pingdaemon.c diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index ae4cf41b..7536e3d3 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -307 +309 diff --git a/tests/ntfsclone/Makefile.am b/tests/ntfsclone/Makefile.am new file mode 100644 index 00000000..637b247a --- /dev/null +++ b/tests/ntfsclone/Makefile.am @@ -0,0 +1,30 @@ +# libguestfs +# 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 $(top_srcdir)/subdir-rules.mk + +TESTS = \ + test-ntfsclone.sh + +random_val := $(shell awk 'BEGIN{srand(); print 1+int(255*rand())}' < /dev/null) + +TESTS_ENVIRONMENT = \ + MALLOC_PERTURB_=$(random_val) \ + $(top_builddir)/run + +EXTRA_DIST = \ + $(TESTS) diff --git a/tests/ntfsclone/test-ntfsclone.sh b/tests/ntfsclone/test-ntfsclone.sh new file mode 100755 index 00000000..2dc4fe8b --- /dev/null +++ b/tests/ntfsclone/test-ntfsclone.sh @@ -0,0 +1,54 @@ +#!/bin/bash - +# libguestfs +# 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. + +# Test the ntfsclone-in/-out commands. + +set -e + +rm -f test1.img backup1 backup2 + +guestfish=../../fish/guestfish + +# Skip if ntfs-3g is not supported by the appliance. +if ! $guestfish add /dev/null : run : available "ntfs3g"; then + echo "$0: skipped because ntfs-3g is not supported by the appliance" + exit 0 +fi + +# Export the filesystems to the backup file. +$guestfish --ro -a ../guests/windows.img <<EOF +run +ntfsclone-out /dev/sda1 backup1 preservetimestamps:true force:true +ntfsclone-out /dev/sda2 backup2 metadataonly:true ignorefscheck:true +EOF + +# Restore to another disk image. +output=$($guestfish -N part:300M <<EOF +ntfsclone-in backup1 /dev/sda1 +vfs-type /dev/sda1 +EOF +) + +if [ "$output" != "ntfs" ]; then + echo "$0: unexpected filesystem type after restore: $output" + exit 1 +fi + +#ls -lh backup[12] + +rm -f test1.img backup1 backup2 |