summaryrefslogtreecommitdiffstats
path: root/daemon/ntfsclone.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/ntfsclone.c')
-rw-r--r--daemon/ntfsclone.c212
1 files changed, 212 insertions, 0 deletions
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;
+}