From: "Richard W.M. Jones" <rjones(a)redhat.com>
---
Makefile.am | 1 +
TODO | 2 -
configure.ac | 1 +
daemon/Makefile.am | 1 +
daemon/ntfsclone.c | 212 +++++++++++++++++++++++++++++++++++++
generator/generator_actions.ml | 27 +++++
po/POTFILES.in | 1 +
src/MAX_PROC_NR | 2 +-
tests/ntfsclone/Makefile.am | 30 +++++
tests/ntfsclone/test-ntfsclone.sh | 54 ++++++++++
10 files changed, 328 insertions(+), 3 deletions(-)
create mode 100644 daemon/ntfsclone.c
create mode 100644 tests/ntfsclone/Makefile.am
create mode 100755 tests/ntfsclone/test-ntfsclone.sh
diff --git a/Makefile.am b/Makefile.am
index e621145..dfee9c4 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
diff --git a/TODO b/TODO
index 9506673..9dafb4d 100644
--- a/TODO
+++ b/TODO
@@ -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 31a2cc5..c07609a 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 3a698cc..2dd8149 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 0000000..4287edb
--- /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 9df9ea5..2d24e6a 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 3a43d17..bed1f65 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 ae4cf41..7536e3d 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 0000000..637b247
--- /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 0000000..2dc4fe8
--- /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
--
1.7.9.1