[Libguestfs] [PATCH 3/4] New APIs: ntfsclone-in, ntfsclone-out.

Richard W.M. Jones rjones at redhat.com
Mon Feb 27 17:34:21 UTC 2012


From: "Richard W.M. Jones" <rjones at 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




More information about the Libguestfs mailing list