New APIs: ntfsclone-in, ntfsclone-out.

This commit is contained in:
Richard W.M. Jones
2012-02-27 12:00:57 +00:00
parent 51aa51884e
commit e567f064e2
10 changed files with 328 additions and 3 deletions

View File

@@ -41,6 +41,7 @@ SUBDIRS += tests/protocol
SUBDIRS += tests/lvm
SUBDIRS += tests/luks
SUBDIRS += tests/md
SUBDIRS += tests/ntfsclone
SUBDIRS += tests/regressions
endif

2
TODO
View File

@@ -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

View File

@@ -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

View File

@@ -137,6 +137,7 @@ guestfsd_SOURCES = \
mount.c \
names.c \
ntfs.c \
ntfsclone.c \
optgroups.c \
optgroups.h \
parted.c \

212
daemon/ntfsclone.c Normal file
View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -1 +1 @@
307
309

View File

@@ -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)

View File

@@ -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