[PATCH 0/2] Implement upload-offset, download-offset APIs, and hexedit guestfish command
by Richard W.M. Jones
This two-part patch implements the 'hexedit' command described here:
http://rwmj.wordpress.com/2010/09/18/new-feature-guestfish-hexedit/#content
The first part implements two new APIs for partial writing and reading
of files. These are modelled after guestfs_upload and
guestfs_download but allow you to specify an offset and (for download
only) a size:
int guestfs_download_offset (guestfs_h *g,
const char *remotefilename, const char *filename,
int64_t offset, int64_t size);
int guestfs_upload_offset (guestfs_h *g,
const char *filename, const char *remotefilename,
int64_t offset);
Since these calls use the streaming FileIn/FileOut part of the
protocol, they are both efficient and unlimited in the amount and type
of data that can be streamed.
http://libguestfs.org/guestfs.3.html#functions_that_have_filein_parameters
I also wrote the hexedit command using the putative hread/hwrite API
and the code was much longer and more involved. Although there is an
advantage to hread/hwrite which is you can read and write to and from
memory.
The second part of the patch implements the hexedit command itself in
guestfish.
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
New in Fedora 11: Fedora Windows cross-compiler. Compile Windows
programs, test, and build Windows installers. Over 70 libraries supprt'd
http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
14 years, 3 months
virt-resize: ntfsresize: location outside device
by sean darcy
I have a 15G qcow2 xp vm with only 1 partition:
-rw-------. 1 root root 10514137088 Sep 12 11:10 XP.img
I want to resize it to 20G. So I :
virsh vol-create-as --format raw windows XP-new-20G.img 20G
Vol XP-new-20G.img created
-rw-------. 1 root root 21474836480 Sep 12 13:17 XP-new-20G.img
But:
virt-resize --expand /dev/sda1 XP.img XP-new-20G.img
Summary of changes:
/dev/sda1: partition will be resized from 14.6G to 20.0G
/dev/sda1: content will be expanded using the 'ntfsresize' method
part_add: do_part_add: parted: /dev/vdb: Error: The location 41938815s
is outside of the device /dev/vdb. at /usr/bin/virt-resize line 1092.
Do I use guestfs to do this?
BTW, I originally formatted the new vm as qcow2, but virt-resize
doesn't recognize it can expand a qcow2 vm:
virt-resize --expand /dev/sda1 XP.img XP-new-20G.img
virt-resize: XP-new-20G.img: file is too small to be a disk image (262144 bytes)
Thanks,
sean
14 years, 3 months
[PATCH] New APIs: hopen-device hopen-file hread hwrite hseek hclose hclose-all
by Matthew Booth
These APIs provide handle-based read/write streaming and random access to files
and block devices.
---
daemon/.gitignore | 1 +
daemon/Makefile.am | 1 +
daemon/hfile.c | 196 +++++++++++++++++++++++++++++++++++++++++++++
daemon/m4/gnulib-cache.m4 | 3 +-
po/POTFILES.in | 2 +
regressions/Makefile.am | 1 +
regressions/test-hfile.pl | 102 +++++++++++++++++++++++
src/MAX_PROC_NR | 2 +-
src/generator.ml | 114 ++++++++++++++++++++++++++
9 files changed, 420 insertions(+), 2 deletions(-)
create mode 100644 daemon/hfile.c
create mode 100755 regressions/test-hfile.pl
diff --git a/daemon/.gitignore b/daemon/.gitignore
index e06c684..c9837be 100644
--- a/daemon/.gitignore
+++ b/daemon/.gitignore
@@ -50,6 +50,7 @@ m4/gettime.m4
m4/gettimeofday.m4
m4/getpagesize.m4
m4/getugroups.m4
+m4/gl_list.m4
m4/glibc21.m4
m4/glob.m4
m4/gnulib-common.m4
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 0c8be08..c243bb6 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -94,6 +94,7 @@ guestfsd_SOURCES = \
guestfsd.c \
headtail.c \
hexdump.c \
+ hfile.c \
htonl.c \
initrd.c \
inotify.c \
diff --git a/daemon/hfile.c b/daemon/hfile.c
new file mode 100644
index 0000000..fedb502
--- /dev/null
+++ b/daemon/hfile.c
@@ -0,0 +1,196 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2010 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "gl_array_list.h"
+
+#include "daemon.h"
+#include "actions.h"
+
+gl_list_t handles;
+
+static void init_handle_list (void) __attribute__((constructor));
+void
+init_handle_list (void)
+{
+ handles = gl_list_nx_create_empty(GL_ARRAY_LIST, NULL, NULL, NULL, false);
+}
+
+static int
+hopen_check(int fd, const char *path)
+{
+ if (fd < 0) {
+ reply_with_perror_errno(errno, "open %s failed", path);
+ return -1;
+ }
+
+ gl_list_node_t node = gl_list_nx_add_last(handles, (void *)(long)fd);
+ if (NULL == node) {
+ close(fd);
+
+ reply_with_error("gl_list_nx_add_last");
+ return -1;
+ }
+
+ return fd;
+}
+
+int /* RInt */
+do_hopen_file (const char *path, int flags)
+{
+ int fd;
+
+ CHROOT_IN;
+ if (flags && O_CREAT) {
+ fd = open(path, flags, 0777);
+ } else {
+ fd = open(path, flags);
+ }
+ CHROOT_OUT;
+
+ return hopen_check(fd, path);
+}
+
+int /* RInt */
+do_hopen_device (const char *device, int flags)
+{
+ int fd;
+
+ fd = open(device, flags);
+
+ return hopen_check(fd, device);
+}
+
+int /* RErr */
+do_hclose (int handle)
+{
+ gl_list_node_t node = gl_list_search(handles, (void *)(long)handle);
+ if (NULL == node) {
+ reply_with_error("%i does not refer to an open handle", handle);
+ return -1;
+ }
+ gl_list_remove_node(handles, node);
+
+ if (close(handle) < 0) {
+ reply_with_perror_errno(errno, "close handle %i failed", handle);
+ return -1;
+ }
+
+ return 0;
+}
+
+int /* RErr */
+do_hclose_all (void)
+{
+ const void *eltp;
+ gl_list_node_t node;
+ gl_list_iterator_t i = gl_list_iterator(handles);
+
+ char *msg = NULL;
+ size_t msglen = 0;
+
+ bool failed = false;
+ while (gl_list_iterator_next(&i, &eltp, &node)) {
+ gl_list_remove_node(handles, node);
+
+ int fd = (long)eltp;
+ if (close(fd) < 0) {
+ failed = true;
+
+ char *error = NULL;
+ int len = asprintf(&error, "close handle %i failed: %m ", fd);
+ if (len < 0) {
+ reply_with_error("asprintf");
+ }
+
+ msg = realloc(msg, msglen + len);
+ if (NULL == msg) {
+ reply_with_error("realloc");
+ return -1;
+ }
+
+ memcpy(msg + msglen, error, len);
+ msglen += len;
+ free(error);
+ }
+ }
+
+ if (failed) {
+ msg[msglen] = '\0';
+ reply_with_error("%s", msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+char * /* RBufferOut */
+do_hread (int handle, int64_t size, size_t *size_r)
+{
+ char *buf = malloc(size);
+ if (NULL == buf) {
+ reply_with_error("malloc");
+ return NULL;
+ }
+
+ ssize_t in = read(handle, buf, size);
+ if (in < 0) {
+ reply_with_perror_errno(errno, "error reading from handle %i", handle);
+ goto error;
+ }
+
+ *size_r = in;
+ return buf;
+error:
+ free(buf);
+ return NULL;
+}
+
+int /* RErr */
+do_hwrite (int handle, const char *content, size_t content_size)
+{
+ size_t pos = 0;
+ while (pos < content_size) {
+ ssize_t out = write(handle, content + pos, content_size - pos);
+
+ if (out < 0) {
+ reply_with_perror_errno(errno, "error writing to handle %i", handle);
+ return -1;
+ }
+ pos += out;
+ }
+
+ return 0;
+}
+
+int64_t /* RInt64 */
+do_hseek (int handle, int64_t offset, int whence)
+{
+ int64_t ret = lseek(handle, offset, whence);
+ if (ret < 0) {
+ reply_with_perror_errno(errno, "error seeking in handle %i", handle);
+ return -1;
+ }
+
+ return ret;
+}
diff --git a/daemon/m4/gnulib-cache.m4 b/daemon/m4/gnulib-cache.m4
index f026cb3..25ffd27 100644
--- a/daemon/m4/gnulib-cache.m4
+++ b/daemon/m4/gnulib-cache.m4
@@ -15,11 +15,12 @@
# Specification in the form of a command-line invocation:
-# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --with-tests --no-libtool --macro-prefix=gl byteswap c-ctype connect error fsusage futimens getaddrinfo getline glob hash ignore-value manywarnings mkdtemp netdb openat perror pread read-file readlink select sleep socket strchrnul strndup symlinkat sys_select sys_wait vasprintf warnings
+# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --with-tests --no-libtool --macro-prefix=gl array-list byteswap c-ctype connect error fsusage futimens getaddrinfo getline glob hash ignore-value manywarnings mkdtemp netdb openat perror pread read-file readlink select sleep socket strchrnul strndup symlinkat sys_select sys_wait vasprintf warnings
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([])
gl_MODULES([
+ array-list
byteswap
c-ctype
connect
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e5fb857..c846e05 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -29,6 +29,7 @@ daemon/grub.c
daemon/guestfsd.c
daemon/headtail.c
daemon/hexdump.c
+daemon/hfile.c
daemon/htonl.c
daemon/initrd.c
daemon/inotify.c
@@ -99,6 +100,7 @@ perl/lib/Sys/Guestfs.pm
perl/lib/Sys/Guestfs/Lib.pm
python/guestfs-py.c
regressions/rhbz501893.c
+regressions/test-hfile.pl
regressions/test-lvm-mapping.pl
regressions/test-noexec-stack.pl
ruby/ext/guestfs/_guestfs.c
diff --git a/regressions/Makefile.am b/regressions/Makefile.am
index ca4292a..f7d9992 100644
--- a/regressions/Makefile.am
+++ b/regressions/Makefile.am
@@ -35,6 +35,7 @@ TESTS = \
test-cancellation-download-librarycancels.sh \
test-cancellation-upload-daemoncancels.sh \
test-find0.sh \
+ test-hfile.pl \
test-luks.sh \
test-lvm-filtering.sh \
test-lvm-mapping.pl \
diff --git a/regressions/test-hfile.pl b/regressions/test-hfile.pl
new file mode 100755
index 0000000..4c23722
--- /dev/null
+++ b/regressions/test-hfile.pl
@@ -0,0 +1,102 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use File::Temp qw(tempfile);
+use Sys::Guestfs;
+
+# Create a new guestfs handle
+my $g = Sys::Guestfs->new();
+
+# Create a 10M image file and add it to the handle
+my ($fh, $path) = tempfile();
+truncate($fh, 1024*1024*10);
+close($fh) or die("close $path failed: $!");
+$g->add_drive($path);
+
+$g->launch();
+
+# As qemu is now running we can unlink the image
+unlink($path) or die("unlink $path failed: $!");
+
+my $string = "abcd";
+
+# Test read and writing directly to a block device
+my $h = $g->hopen_device("/dev/vda", 2);
+$g->hwrite($h, $string);
+$g->hseek($h, 0, 0);
+my $in = $g->hread($h, length($string));
+
+die("device: hread returned $in") unless($in eq $string);
+$g->hclose($h);
+
+# Check $h is no longer valid
+eval {
+ $g->hseek($h, 0, 0);
+};
+die("device: handle wasn't closed") unless($@);
+
+# Make an ext2 filesystem and mount it
+$g->mkfs("ext2", "/dev/vda");
+$g->mount("/dev/vda", "/");
+
+# Open should fail read-only
+eval {
+ $h = $g->hopen_file("/test", 0);
+};
+die("file: read-only open non-existant file") unless($@);
+
+# Open should fail write-only without O_CREAT
+eval {
+ $h = $g->hopen_file("/test", 1);
+};
+die("file: write-only open non-existant file") unless($@);
+
+# Open should fail read-write without O_CREAT
+eval {
+ $h = $g->hopen_file("/test", 2);
+};
+die("file: read-write open non-existant file") unless($@);
+
+# Open the file write only, and create it
+$h = $g->hopen_file("/test", 1 | 64);
+
+# Write data to it
+$g->hwrite($h, $string);
+
+# Verify we can't read from it
+$g->hseek($h, 0, 0);
+eval {
+ $in = $g->hread($h, length($string));
+};
+die("file: read of write-only file") unless($@);
+
+# close and check the contents with cat
+$g->hclose($h);
+$in = $g->cat("/test");
+die("file: hwrite data test") unless($in eq $string);
+
+# Open the file read-only
+$h = $g->hopen_file("/test", 0);
+
+# Read the file in 1 byte chunks until the end
+$in = "";
+my $chunk;
+do {
+ $chunk = $g->hread($h, 1);
+ $in .= $chunk;
+} until(length($chunk) == 0);
+die("file: hread in chunks") unless($in eq $string);
+
+# Read past EOF returns a short read
+$g->hseek($h, 0, 0);
+$in = $g->hread($h, length($string) + 1);
+die("file: read past EOF") unless($in eq $string);
+
+# Test close-all
+$g->hclose_all();
+eval {
+ $g->hseek($h, 0, 0);
+};
+die("file: close-all") unless($@);
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index c1d1ffb..305aa98 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-266
+273
diff --git a/src/generator.ml b/src/generator.ml
index 00caa6a..48010dc 100755
--- a/src/generator.ml
+++ b/src/generator.ml
@@ -5355,6 +5355,120 @@ filesystem can be found.
To find the label of a filesystem, use C<guestfs_vfs_label>.");
+ ("hopen_file", (RInt "handle", [Pathname "path"; Int "flags"]), 267, [],
+ [],
+ "open a file",
+ "\
+This command opens C<path> in the guest and returns a handle
+to it. The returned handle is an opaque int which can be passed
+to C<guestfs_h*> calls.
+
+C<flags> determines how the file is opened. It is a bitwise or of:
+
+=over
+
+=item 0
+
+(O_RDONLY) Open the file read only.
+
+=item 1
+
+(O_WRONLY) Open the file write only.
+
+=item 2
+
+(O_RDWR) Open the file read-write.
+
+=item 64
+
+(O_CREAT) Create the file if it does not already exist. The file
+will be created with a mode of 0777, modified by the daemon's
+umask. See L<UMASK>.
+
+=item 512
+
+(O_TRUNC) Truncate a file opened for write to zero length when
+opening.
+
+=item 1024
+
+(O_APPEND) Always write to the end of the file.
+
+=item 262144
+
+(O_NOATIME) Don't update the access time when reading from a file.
+
+=back");
+
+ ("hopen_device", (RInt "handle", [Device "device"; Int "flags"]), 268, [],
+ [],
+ "open a block device",
+ "\
+This command opens a block device and returns a handle to it.
+The returned handle is an opaque int which can be passed to
+C<guestfs_h*> calls.
+
+See L<guestfs_hopen_file> for a description of C<flags>.");
+
+ ("hclose", (RErr, [Int "handle"]), 269, [],
+ [],
+ "close a file handle",
+ "\
+This command closes C<handle> and releases all resources associated
+with it.");
+
+ ("hclose_all", (RErr, []), 270, [],
+ [],
+ "close all open file handles",
+ "\
+This command closes all file handles which have been opened with
+C<guestfs_hopen_*>.");
+
+ ("hread", (RBufferOut "content", [Int "handle"; Int64 "length"]), 271,
+ [ProtocolLimitWarning], [],
+ "read data from an open handle",
+ "\
+This command reads up to C<length> bytes from C<handle> and returns
+the data in C<content>. It will attempt to read exactly C<length>
+bytes, but may return less. Returned C<content> with size 0
+indicates EOF.");
+
+ ("hwrite", (RErr, [Int "handle"; BufferIn "content"]), 272,
+ [ProtocolLimitWarning], [],
+ "write data to an open handle",
+ "\
+This command writes all the data in C<content> to C<handle>. It
+returns an error if this fails.");
+
+ ("hseek", (RInt64 "newoffset", [Int "handle"; Int64 "offset";
+ Int "whence"]), 273, [],
+ [],
+ "seek on an open handle",
+ "\
+This command updates the current position in C<handle>. This
+affects the C<guestfs_hread> and C<guestfs_hwrite> calls.
+
+C<whence> determines the point of reference for C<offset>. It
+must be one of:
+
+=over
+
+=item 0
+
+C<offset> is relative to the beginning of the file.
+
+=item 1
+
+C<offset> is relative to the current position.
+
+=item 2
+
+C<offset> is relative to the end of the file.
+
+=back
+
+hseek returns the new offset from the beginning of the file.");
+
]
let all_functions = non_daemon_functions @ daemon_functions
--
1.7.2.2
14 years, 3 months
[PATCH 1/4] Check that we're not overwriting an existing Libvirt domain
by Matthew Booth
Exit with an error if we would overwrite an existing libvirt domain.
Fixes RHBZ#617110
---
lib/Sys/VirtV2V/Connection/LibVirt.pm | 4 ++++
lib/Sys/VirtV2V/Target/LibVirt.pm | 31 ++++++++++++++++++++++++++++++-
lib/Sys/VirtV2V/Target/RHEV.pm | 11 +++++++++++
3 files changed, 45 insertions(+), 1 deletions(-)
diff --git a/lib/Sys/VirtV2V/Connection/LibVirt.pm b/lib/Sys/VirtV2V/Connection/LibVirt.pm
index b83a10d..51331da 100644
--- a/lib/Sys/VirtV2V/Connection/LibVirt.pm
+++ b/lib/Sys/VirtV2V/Connection/LibVirt.pm
@@ -79,6 +79,10 @@ sub new
$self->{uri} = URI->new($uri);
$self->{name} = $name;
+ # Check that the guest doesn't already exist on the target
+ die(user_message(__x("Domain {name} already exists on the target.",
+ name => $name))) if ($target->guest_exists($name));
+
# Parse uri authority for hostname and username
$self->{uri}->authority() =~ /^(?:([^:]*)(?::([^@]*))?@)?(.*)$/
or die(user_message(__x("Unable to parse URI authority: {auth}",
diff --git a/lib/Sys/VirtV2V/Target/LibVirt.pm b/lib/Sys/VirtV2V/Target/LibVirt.pm
index 574590c..279e93d 100644
--- a/lib/Sys/VirtV2V/Target/LibVirt.pm
+++ b/lib/Sys/VirtV2V/Target/LibVirt.pm
@@ -145,8 +145,10 @@ sub close
package Sys::VirtV2V::Target::LibVirt;
-use Sys::VirtV2V::Util qw(user_message);
+use Sys::Virt;
+use Sys::Virt::Error;
+use Sys::VirtV2V::Util qw(user_message);
use Locale::TextDomain 'virt-v2v';
=head1 NAME
@@ -288,6 +290,33 @@ sub get_volume
return Sys::VirtV2V::Target::LibVirt::Vol->_get($self->{pool}, $name);
}
+=item guest_exists(name)
+
+Return 1 if a guest with I<name> already exists, 0 otherwise.
+
+=cut
+
+sub guest_exists
+{
+ my $self = shift;
+ my ($name) = @_;
+
+ eval {
+ $self->{vmm}->get_domain_by_name($name);
+ };
+
+ if ($@) {
+ if ($@->code == Sys::Virt::Error::ERR_NO_DOMAIN) {
+ return 0;
+ }
+
+ die(user_message(__x("Error checking for domain: {error}",
+ error => $@->stringify())));
+ }
+
+ return 1;
+}
+
=item create_guest(dom)
Create the guest in the target
diff --git a/lib/Sys/VirtV2V/Target/RHEV.pm b/lib/Sys/VirtV2V/Target/RHEV.pm
index 574b022..218cf63 100644
--- a/lib/Sys/VirtV2V/Target/RHEV.pm
+++ b/lib/Sys/VirtV2V/Target/RHEV.pm
@@ -618,6 +618,17 @@ sub get_volume
die("Cannot retrieve an existing RHEV storage volume by name");
}
+=item guest_exists(name)
+
+This always returns 0 for a RHEV target.
+
+=cut
+
+sub guest_exists
+{
+ return 0;
+}
+
=item create_guest(dom)
Create the guest in the target
--
1.7.2.3
14 years, 3 months
[PATCH v2] Add progress bars
by Richard Jones
This is an updated and extended version of the original patch:
https://www.redhat.com/archives/libguestfs/2010-August/msg00163.html
This adds OCaml and Perl bindings (both tested), support for
progress bars in virt-resize, and adds progress notifications
to a number of the simpler commands.
Still to do is to add progress messages to more commands. There
are still a few commands which would be fairly easy (eg. download and
some t*-out commands). There are many others where implementing
progress messages will be quite a lot more complex.
Note: I have changed the default frequency of messages. They are
now sent after two seconds, and every 1/3rd of a second thereafter.
This makes the progress bars smoother and avoids aliasing effects
when displaying time left in seconds.
Note (2): All known comments about the previous patches have been
incorporated.
Rich.
14 years, 4 months