[libvirt] Re: [Libvir] Reopening the old discussion about virDomainBlockPeek
Richard W.M. Jones
rjones at redhat.com
Wed Jun 4 15:10:32 UTC 2008
Attached is an updated patch. The changes are:
- updated to latest CVS
- run make check / syntax-check
- remove virsh subcommand (as per Dan's suggestion - see below)
- some more things that Dan pointed out - see below
I would like to add this to CVS because it is quite a pain tracking
CVS changes. I know there's no remote support at the moment, but I
can add that later.
On Thu, Apr 24, 2008 at 03:02:11PM +0100, Daniel P. Berrange wrote:
> > Index: configure.in
> > ===================================================================
> > RCS file: /data/cvs/libvirt/configure.in,v
> > retrieving revision 1.139
> > diff -u -r1.139 configure.in
> > --- configure.in 8 Apr 2008 16:45:57 -0000 1.139
> > +++ configure.in 16 Apr 2008 15:18:07 -0000
> > @@ -60,6 +60,10 @@
> >
> > LIBVIRT_COMPILE_WARNINGS(maximum)
> >
> > +dnl Support large files / 64 bit seek offsets.
> > +dnl Use --disable-largefile if you don't want this.
> > +AC_SYS_LARGEFILE
> > +
>
> IIRC, this is redundant now - we already added it elsewhere in the configure
> script when we did the storage patches.
In the patch I removed the second AC_SYS_LARGEFILE and left the useful
comment.
> > -
> > +int virDomainBlockPeek (virDomainPtr dom,
> > + const char *path,
> > + long long offset,
> > + size_t size,
> > + void *buffer);
>
> Should probably make offset be an 'unsigned long long' unless we have
> some semantics which want -ve numbers ? Is 'char *' better or worse
> than 'void *' for the buffer arg ?
I changed the offset to unsigned long long. Left the buffer as void *.
> > +/* "domblkpeek" command
> > + */
> > +static vshCmdInfo info_domblkpeek[] = {
> > + {"syntax", "domblkpeek <domain> <path> <offset> <size>"},
> > + {"help", gettext_noop("peek at a domain block device")},
> > + {"desc", gettext_noop("Peek into a domain block device.")},
> > + {NULL,NULL}
> > +};
> > +
> > +static vshCmdOptDef opts_domblkpeek[] = {
> > + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
> > + {"path", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("block device path")},
> > + {"offset", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("start offset")},
> > + {"size", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("size in bytes")},
> > + {NULL, 0, 0, NULL}
> > +};
>
>
> I'm wondering if this is perhaps one of the few APIs we should /not/
> include in virsh ? The data is pretty useless on its own - I figure
> any app needing to access this is almost certainly not going to be
> shell scripting, and thus using one of the language bindings directly.
> Having the virsh command will probably just encourage people to use
> this as a really dumb file copy command.
This virsh changed removed.
> > + /* The path is correct, now try to open it and get its size. */
> > + fd = open (path, O_RDONLY);
> > + if (fd == -1 || fstat (fd, &statbuf) == -1) {
> > + virXendError (domain->conn, VIR_ERR_SYSTEM_ERROR, strerror (errno));
> > + goto done;
> > + }
> > +
> > + /* XXX The following test fails for LVM devices for a couple
> > + * of reasons: (1) They are commonly symlinks to /dev/mapper/foo
> > + * and despite the man page for fstat, fstat stats the link not
> > + * the file. (2) Stat even on the block device returns st_size==0.
> > + *
> > + * Anyhow, it should be safe to ignore this test since we are
> > + * in O_RDONLY mode.
> > + */
> > +#if 0
> > + /* NB we know offset > 0, size >= 0 */
> > + if (offset + size > statbuf.st_size) {
> > + virXendError (domain->conn, VIR_ERR_INVALID_ARG, "offset");
> > + goto done;
> > + }
> > +#endif
>
> Actually the core problem is that fstat() does not return block device
> capacity. The best way to determine capacity of a block device is to
> lseek() to the end of it, and grab the return value of lseek.
>
> There's also a Linux speciifc ioctl() to get device capacity, but
> the lseek approach is portable.
I just removed this code. It's not needed.
> > +
> > + /* Seek and read. */
> > + /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should
> > + * be 64 bits on all platforms.
> > + */
> > + if (lseek (fd, offset, SEEK_SET) == (off_t) -1 ||
> > + saferead (fd, buffer, size) == (ssize_t) -1) {
> > + virXendError (domain->conn, VIR_ERR_SYSTEM_ERROR, strerror (errno));
> > + goto done;
> > + }
> > +
> > + ret = 0;
> > + done:
> > + if (fd >= 0) close (fd);
> > + return ret;
> > +}
> > +
> > #endif /* ! PROXY */
> > #endif /* WITH_XEN */
> > Index: src/xend_internal.h
> > ===================================================================
> > RCS file: /data/cvs/libvirt/src/xend_internal.h,v
> > retrieving revision 1.40
> > diff -u -r1.40 xend_internal.h
> > --- src/xend_internal.h 10 Apr 2008 16:54:54 -0000 1.40
> > +++ src/xend_internal.h 16 Apr 2008 15:18:26 -0000
> > @@ -228,6 +228,8 @@
> > int xenDaemonDomainMigratePrepare (virConnectPtr dconn, char **cookie, int *cookielen, const char *uri_in, char **uri_out, unsigned long flags, const char *dname, unsigned long resource);
> > int xenDaemonDomainMigratePerform (virDomainPtr domain, const char *cookie, int cookielen, const char *uri, unsigned long flags, const char *dname, unsigned long resource);
> >
> > +int xenDaemonDomainBlockPeek (virDomainPtr domain, const char *path, long long offset, size_t size, void *buffer);
> > +
> > #ifdef __cplusplus
> > }
> > #endif
> > Index: src/xm_internal.c
> > ===================================================================
> > RCS file: /data/cvs/libvirt/src/xm_internal.c,v
> > retrieving revision 1.70
> > diff -u -r1.70 xm_internal.c
> > --- src/xm_internal.c 10 Apr 2008 16:54:54 -0000 1.70
> > +++ src/xm_internal.c 16 Apr 2008 15:18:29 -0000
> > @@ -3160,4 +3160,15 @@
> > return (ret);
> > }
> >
> > +int
> > +xenXMDomainBlockPeek (virDomainPtr dom,
> > + const char *path ATTRIBUTE_UNUSED,
> > + long long offset ATTRIBUTE_UNUSED,
> > + size_t size ATTRIBUTE_UNUSED,
> > + void *buffer ATTRIBUTE_UNUSED)
> > +{
> > + xenXMError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
> > + return -1;
> > +}
>
> Hmm, is there no way to share the code here with the main Xen
> driver ? THe XenD driver impl parses the SEXPR to get the
> device info. Perhaps we could parse the XML format instead,
> then the only difference would be the API you call to get
> the XML doc in each driver. For that matter, if we worked off
> the XML format, this driver impl would be trivially sharable
> to QEMU and LXC, etc too.
I left this as it was because I'm not sure what you are suggesting.
Do you mean to do a dumpxml operation inside the driver? Note that
we're already dumping the XML outside the driver in order to get the
paths to pass in.
Rich.
--
Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones
virt-p2v converts physical machines to virtual machines. Boot with a
live CD or over the network (PXE) and turn machines into Xen guests.
http://et.redhat.com/~rjones/virt-p2v
-------------- next part --------------
Index: configure.in
===================================================================
RCS file: /data/cvs/libvirt/configure.in,v
retrieving revision 1.151
diff -b -u -p -r1.151 configure.in
--- configure.in 29 May 2008 19:27:04 -0000 1.151
+++ configure.in 4 Jun 2008 15:08:08 -0000
@@ -60,6 +60,10 @@ AM_PROG_CC_C_O
LIBVIRT_COMPILE_WARNINGS([maximum])
+dnl Support large files / 64 bit seek offsets.
+dnl Use --disable-largefile if you don't want this.
+AC_SYS_LARGEFILE
+
dnl Availability of various common functions (non-fatal if missing).
AC_CHECK_FUNCS([cfmakeraw regexec uname sched_getaffinity])
@@ -982,8 +986,6 @@ AC_SUBST([CYGWIN_EXTRA_LIBADD])
AC_SUBST([CYGWIN_EXTRA_PYTHON_LIBADD])
AC_SUBST([MINGW_EXTRA_LDFLAGS])
-AC_SYS_LARGEFILE
-
# Set LV_LIBTOOL_OBJDIR to "." or $lt_cv_objdir, depending on whether
# we're building shared libraries. This is the name of the directory
# in which .o files will be created.
Index: include/libvirt/libvirt.h
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h,v
retrieving revision 1.74
diff -b -u -p -r1.74 libvirt.h
--- include/libvirt/libvirt.h 23 May 2008 08:32:08 -0000 1.74
+++ include/libvirt/libvirt.h 4 Jun 2008 15:08:10 -0000
@@ -530,7 +530,11 @@ int virDomainInterfa
const char *path,
virDomainInterfaceStatsPtr stats,
size_t size);
-
+int virDomainBlockPeek (virDomainPtr dom,
+ const char *path,
+ unsigned long long offset,
+ size_t size,
+ void *buffer);
/*
* defined but not running domains
Index: include/libvirt/libvirt.h.in
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h.in,v
retrieving revision 1.49
diff -b -u -p -r1.49 libvirt.h.in
--- include/libvirt/libvirt.h.in 23 May 2008 08:32:08 -0000 1.49
+++ include/libvirt/libvirt.h.in 4 Jun 2008 15:08:10 -0000
@@ -530,7 +530,11 @@ int virDomainInterfa
const char *path,
virDomainInterfaceStatsPtr stats,
size_t size);
-
+int virDomainBlockPeek (virDomainPtr dom,
+ const char *path,
+ unsigned long long offset,
+ size_t size,
+ void *buffer);
/*
* defined but not running domains
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.47
diff -b -u -p -r1.47 driver.h
--- src/driver.h 13 May 2008 06:30:58 -0000 1.47
+++ src/driver.h 4 Jun 2008 15:08:11 -0000
@@ -226,6 +226,13 @@ typedef int
struct _virDomainInterfaceStats *stats);
typedef int
+ (*virDrvDomainBlockPeek)
+ (virDomainPtr domain,
+ const char *path,
+ unsigned long long offset, size_t size,
+ void *buffer);
+
+typedef int
(*virDrvDomainMigratePrepare)
(virConnectPtr dconn,
char **cookie,
@@ -337,6 +344,7 @@ struct _virDriver {
virDrvDomainMigrateFinish domainMigrateFinish;
virDrvDomainBlockStats domainBlockStats;
virDrvDomainInterfaceStats domainInterfaceStats;
+ virDrvDomainBlockPeek domainBlockPeek;
virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory;
virDrvNodeGetFreeMemory getFreeMemory;
};
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.143
diff -b -u -p -r1.143 libvirt.c
--- src/libvirt.c 29 May 2008 19:23:17 -0000 1.143
+++ src/libvirt.c 4 Jun 2008 15:08:14 -0000
@@ -2586,7 +2586,73 @@ virDomainInterfaceStats (virDomainPtr do
return -1;
}
+/**
+ * virDomainBlockPeek:
+ * @dom: pointer to the domain object
+ * @path: path to the block device
+ * @offset: offset within block device
+ * @size: size to read
+ * @buffer: return buffer (must be at least size bytes)
+ *
+ * This function allows you to read the contents of a domain's
+ * disk device.
+ *
+ * Typical uses for this are to determine if the domain has
+ * written a Master Boot Record (indicating that the domain
+ * has completed installation), or to try to work out the state
+ * of the domain's filesystems.
+ *
+ * (Note that in the local case you might try to open the
+ * block device or file directly, but that won't work in the
+ * remote case, nor if you don't have sufficient permission.
+ * Hence the need for this call).
+ *
+ * 'path' must be a device or file corresponding to the domain.
+ * In other words it must be the precise string returned in
+ * a <disk><source dev='...'/></disk> from
+ * virDomainGetXMLDesc.
+ *
+ * 'offset' and 'size' represent an area which must lie entirely
+ * within the device or file. 'size' may be 0 to test if the
+ * call would succeed.
+ *
+ * 'buffer' is the return buffer and must be at least 'size' bytes.
+ *
+ * Returns: 0 in case of success or -1 in case of failure.
+ */
+int
+virDomainBlockPeek (virDomainPtr dom,
+ const char *path,
+ unsigned long long offset /* really 64 bits */,
+ size_t size,
+ void *buffer)
+{
+ virConnectPtr conn;
+ DEBUG("domain=%p, path=%s, offset=%lld, size=%zi, buffer=%p",
+ dom, path, offset, size, buffer);
+ if (!VIR_IS_CONNECTED_DOMAIN (dom)) {
+ virLibDomainError (NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ return -1;
+ }
+ conn = dom->conn;
+
+ if (!path) {
+ virLibDomainError (dom, VIR_ERR_INVALID_ARG, "path == NULL");
+ return -1;
+ }
+ /* Allow size == 0 as an access test. */
+ if (size > 0 && !buffer) {
+ virLibDomainError (dom, VIR_ERR_INVALID_ARG, "buffer == NULL");
+ return -1;
+ }
+
+ if (conn->driver->domainBlockPeek)
+ return conn->driver->domainBlockPeek (dom, path, offset, size, buffer);
+
+ virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+ return -1;
+}
/************************************************************************
Index: src/libvirt_sym.version
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt_sym.version,v
retrieving revision 1.40
diff -b -u -p -r1.40 libvirt_sym.version
--- src/libvirt_sym.version 13 May 2008 06:30:58 -0000 1.40
+++ src/libvirt_sym.version 4 Jun 2008 15:08:14 -0000
@@ -73,6 +73,7 @@
virDomainSetSchedulerParameters;
virDomainBlockStats;
virDomainInterfaceStats;
+ virDomainBlockPeek;
virDomainAttachDevice;
virDomainDetachDevice;
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.82
diff -b -u -p -r1.82 qemu_driver.c
--- src/qemu_driver.c 29 May 2008 19:20:23 -0000 1.82
+++ src/qemu_driver.c 4 Jun 2008 15:08:17 -0000
@@ -3521,6 +3521,7 @@ static virDriver qemuDriver = {
NULL, /* domainMigrateFinish */
qemudDomainBlockStats, /* domainBlockStats */
qemudDomainInterfaceStats, /* domainInterfaceStats */
+ NULL, /* domainBlockPeek */
#if HAVE_NUMACTL
qemudNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */
qemudNodeGetFreeMemory, /* getFreeMemory */
Index: src/test.c
===================================================================
RCS file: /data/cvs/libvirt/src/test.c,v
retrieving revision 1.75
diff -b -u -p -r1.75 test.c
--- src/test.c 29 May 2008 19:20:23 -0000 1.75
+++ src/test.c 4 Jun 2008 15:08:18 -0000
@@ -2060,6 +2060,7 @@ static virDriver testDriver = {
NULL, /* domainMigrateFinish */
NULL, /* domainBlockStats */
NULL, /* domainInterfaceStats */
+ NULL, /* domainBlockPeek */
testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */
NULL, /* getFreeMemory */
};
Index: src/xen_unified.c
===================================================================
RCS file: /data/cvs/libvirt/src/xen_unified.c,v
retrieving revision 1.44
diff -b -u -p -r1.44 xen_unified.c
--- src/xen_unified.c 14 May 2008 19:51:24 -0000 1.44
+++ src/xen_unified.c 4 Jun 2008 15:08:19 -0000
@@ -1235,6 +1235,29 @@ xenUnifiedDomainInterfaceStats (virDomai
}
static int
+xenUnifiedDomainBlockPeek (virDomainPtr dom, const char *path,
+ unsigned long long offset, size_t size,
+ void *buffer)
+{
+ int r;
+ GET_PRIVATE (dom->conn);
+
+ if (priv->opened[XEN_UNIFIED_XEND_OFFSET]) {
+ r = xenDaemonDomainBlockPeek (dom, path, offset, size, buffer);
+ if (r != -2) return r;
+ /* r == -2 means declined, so fall through to XM driver ... */
+ }
+
+ if (priv->opened[XEN_UNIFIED_XM_OFFSET]) {
+ if (xenXMDomainBlockPeek (dom, path, offset, size, buffer) == 0)
+ return 0;
+ }
+
+ xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+ return -1;
+}
+
+static int
xenUnifiedNodeGetCellsFreeMemory (virConnectPtr conn, unsigned long long *freeMems,
int startCell, int maxCells)
{
@@ -1329,6 +1352,7 @@ static virDriver xenUnifiedDriver = {
.domainMigrateFinish = xenUnifiedDomainMigrateFinish,
.domainBlockStats = xenUnifiedDomainBlockStats,
.domainInterfaceStats = xenUnifiedDomainInterfaceStats,
+ .domainBlockPeek = xenUnifiedDomainBlockPeek,
.nodeGetCellsFreeMemory = xenUnifiedNodeGetCellsFreeMemory,
.getFreeMemory = xenUnifiedNodeGetFreeMemory,
};
Index: src/xend_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xend_internal.c,v
retrieving revision 1.194
diff -b -u -p -r1.194 xend_internal.c
--- src/xend_internal.c 29 May 2008 19:20:23 -0000 1.194
+++ src/xend_internal.c 4 Jun 2008 15:08:22 -0000
@@ -18,6 +18,8 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
@@ -1705,6 +1707,219 @@ error:
return ret;
}
+typedef int
+ (*sexp_blockdevs_cb)
+ (virConnectPtr conn, void *data,
+ int isBlock, int cdrom, int isNoSrcCdrom, int hvm,
+ const char *drvName, const char *drvType,
+ const char *src, const char *dst,
+ const char *mode);
+
+/**
+ * xend_parse_sexp_blockdevs:
+ * @conn: connection
+ * @root: root sexpr
+ * @xendConfigVersion: version of xend
+ * @fn: callback function
+ * @data: optional data for callback function
+ *
+ * This parses out block devices from the domain sexpr and calls
+ * fn (conn, data, ...) for each block device found.
+ *
+ * Returns 0 if successful or -1 if failed.
+ */
+static int
+xend_parse_sexp_blockdevs (virConnectPtr conn, struct sexpr *root,
+ int xendConfigVersion,
+ sexp_blockdevs_cb fn, void *data)
+{
+ struct sexpr *cur, *node;
+ int hvm;
+
+ hvm = sexpr_lookup(root, "domain/image/hvm") ? 1 : 0;
+
+ for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
+ node = cur->u.s.car;
+ /* Normally disks are in a (device (vbd ...)) block
+ but blktap disks ended up in a differently named
+ (device (tap ....)) block.... */
+ if (sexpr_lookup(node, "device/vbd") ||
+ sexpr_lookup(node, "device/tap")) {
+ char *offset;
+ int ret = -1;
+ int isBlock = 0;
+ int cdrom = 0;
+ int isNoSrcCdrom = 0;
+ char *drvName = NULL;
+ char *drvType = NULL;
+ const char *src = NULL;
+ const char *dst = NULL;
+ const char *mode = NULL;
+
+ /* Again dealing with (vbd...) vs (tap ...) differences */
+ if (sexpr_lookup(node, "device/vbd")) {
+ src = sexpr_node(node, "device/vbd/uname");
+ dst = sexpr_node(node, "device/vbd/dev");
+ mode = sexpr_node(node, "device/vbd/mode");
+ } else {
+ src = sexpr_node(node, "device/tap/uname");
+ dst = sexpr_node(node, "device/tap/dev");
+ mode = sexpr_node(node, "device/tap/mode");
+ }
+
+ if (dst == NULL) {
+ virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("domain information incomplete, vbd has no dev"));
+ goto bad_parse;
+ }
+
+ if (src == NULL) {
+ /* There is a case without the uname to the CD-ROM device */
+ offset = strchr(dst, ':');
+ if (offset) {
+ if (hvm && STREQ(offset, ":cdrom")) {
+ isNoSrcCdrom = 1;
+ }
+ offset[0] = '\0';
+ }
+ if (!isNoSrcCdrom) {
+ virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("domain information incomplete, vbd has no src"));
+ goto bad_parse;
+ }
+ }
+
+ if (!isNoSrcCdrom) {
+ offset = strchr(src, ':');
+ if (!offset) {
+ virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse vbd filename, missing driver name"));
+ goto bad_parse;
+ }
+
+ if (VIR_ALLOC_N(drvName, (offset-src)+1) < 0) {
+ virXendError(conn, VIR_ERR_NO_MEMORY,
+ _("allocate new buffer"));
+ goto bad_parse;
+ }
+ strncpy(drvName, src, (offset-src));
+ drvName[offset-src] = '\0';
+
+ src = offset + 1;
+
+ if (STREQ (drvName, "tap")) {
+ offset = strchr(src, ':');
+ if (!offset) {
+ virXendError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse vbd filename, missing driver type"));
+ goto bad_parse;
+ }
+
+ if (VIR_ALLOC_N(drvType, (offset-src)+1)< 0) {
+ virXendError(conn, VIR_ERR_NO_MEMORY,
+ _("allocate new buffer"));
+ goto bad_parse;
+ }
+ strncpy(drvType, src, (offset-src));
+ drvType[offset-src] = '\0';
+ src = offset + 1;
+ /* Its possible to use blktap driver for block devs
+ too, but kinda pointless because blkback is better,
+ so we assume common case here. If blktap becomes
+ omnipotent, we can revisit this, perhaps stat()'ing
+ the src file in question */
+ isBlock = 0;
+ } else if (STREQ(drvName, "phy")) {
+ isBlock = 1;
+ } else if (STREQ(drvName, "file")) {
+ isBlock = 0;
+ }
+ }
+
+ if (STREQLEN (dst, "ioemu:", 6))
+ dst += 6;
+
+ /* New style disk config from Xen >= 3.0.3 */
+ if (xendConfigVersion > 1) {
+ offset = strrchr(dst, ':');
+ if (offset) {
+ if (STREQ (offset, ":cdrom")) {
+ cdrom = 1;
+ } else if (STREQ (offset, ":disk")) {
+ /* The default anyway */
+ } else {
+ /* Unknown, lets pretend its a disk too */
+ }
+ offset[0] = '\0';
+ }
+ }
+
+ /* Call the callback function. */
+ ret = fn (conn, data, isBlock, cdrom, isNoSrcCdrom, hvm,
+ drvName, drvType, src, dst, mode);
+
+ bad_parse:
+ VIR_FREE(drvName);
+ VIR_FREE(drvType);
+
+ if (ret == -1) return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+xend_parse_sexp_desc_blockdev (virConnectPtr conn ATTRIBUTE_UNUSED,
+ void *data,
+ int isBlock, int cdrom, int isNoSrcCdrom,
+ int hvm,
+ const char *drvName, const char *drvType,
+ const char *src, const char *dst,
+ const char *mode)
+{
+ virBuffer *buf = (virBuffer *) data;
+ const char *bus = NULL;
+
+ if (!isNoSrcCdrom) {
+ virBufferVSprintf(buf, " <disk type='%s' device='%s'>\n",
+ isBlock ? "block" : "file",
+ cdrom ? "cdrom" : "disk");
+ if (drvType) {
+ virBufferVSprintf(buf, " <driver name='%s' type='%s'/>\n", drvName, drvType);
+ } else {
+ virBufferVSprintf(buf, " <driver name='%s'/>\n", drvName);
+ }
+ if (isBlock) {
+ virBufferVSprintf(buf, " <source dev='%s'/>\n", src);
+ } else {
+ virBufferVSprintf(buf, " <source file='%s'/>\n", src);
+ }
+ } else {
+ /* This case is the cdrom device only */
+ virBufferAddLit(buf, " <disk device='cdrom'>\n");
+ }
+ if (STRPREFIX(dst, "xvd") || !hvm) {
+ bus = "xen";
+ } else if (STRPREFIX(dst, "sd")) {
+ bus = "scsi";
+ } else {
+ bus = "ide";
+ }
+ virBufferVSprintf(buf, " <target dev='%s' bus='%s'/>\n",
+ dst, bus);
+
+ /* XXX should we force mode == r, if cdrom==1, or assume
+ xend has already done this ? */
+ if ((mode != NULL) && (STREQ (mode, "r")))
+ virBufferAddLit(buf, " <readonly/>\n");
+ else if ((mode != NULL) && (STREQ (mode, "w!")))
+ virBufferAddLit(buf, " <shareable/>\n");
+ virBufferAddLit(buf, " </disk>\n");
+
+ return 0;
+}
+
/**
* xend_parse_sexp_desc:
* @conn: the connection associated with the XML
@@ -1849,164 +2064,15 @@ xend_parse_sexp_desc(virConnectPtr conn,
if ((tmp != NULL) && (tmp[0] != 0))
virBufferVSprintf(&buf, " <emulator>%s</emulator>\n", tmp);
+ /* append block devices */
+ if (xend_parse_sexp_blockdevs (conn, root, xendConfigVersion,
+ xend_parse_sexp_desc_blockdev, &buf) == -1)
+ goto error;
+
+ /* append network devices and framebuffer */
for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
node = cur->u.s.car;
- /* Normally disks are in a (device (vbd ...)) block
- but blktap disks ended up in a differently named
- (device (tap ....)) block.... */
- if (sexpr_lookup(node, "device/vbd") ||
- sexpr_lookup(node, "device/tap")) {
- char *offset;
- int isBlock = 0;
- int cdrom = 0;
- int isNoSrcCdrom = 0;
- char *drvName = NULL;
- char *drvType = NULL;
- const char *src = NULL;
- const char *dst = NULL;
- const char *mode = NULL;
- const char *bus = NULL;
-
- /* Again dealing with (vbd...) vs (tap ...) differences */
- if (sexpr_lookup(node, "device/vbd")) {
- src = sexpr_node(node, "device/vbd/uname");
- dst = sexpr_node(node, "device/vbd/dev");
- mode = sexpr_node(node, "device/vbd/mode");
- } else {
- src = sexpr_node(node, "device/tap/uname");
- dst = sexpr_node(node, "device/tap/dev");
- mode = sexpr_node(node, "device/tap/mode");
- }
-
- if (dst == NULL) {
- virXendError(conn, VIR_ERR_INTERNAL_ERROR,
- _("domain information incomplete, vbd has no dev"));
- goto bad_parse;
- }
-
- if (src == NULL) {
- /* There is a case without the uname to the CD-ROM device */
- offset = strchr(dst, ':');
- if (offset) {
- if (hvm && STREQ( offset , ":cdrom")) {
- isNoSrcCdrom = 1;
- }
- offset[0] = '\0';
- }
- if (!isNoSrcCdrom) {
- virXendError(conn, VIR_ERR_INTERNAL_ERROR,
- _("domain information incomplete, vbd has no src"));
- goto bad_parse;
- }
- }
-
- if (!isNoSrcCdrom) {
- offset = strchr(src, ':');
- if (!offset) {
- virXendError(conn, VIR_ERR_INTERNAL_ERROR,
- _("cannot parse vbd filename, missing driver name"));
- goto bad_parse;
- }
-
- if (VIR_ALLOC_N(drvName, (offset-src)+1) < 0) {
- virXendError(conn, VIR_ERR_NO_MEMORY,
- _("allocate new buffer"));
- goto bad_parse;
- }
- strncpy(drvName, src, (offset-src));
- drvName[offset-src] = '\0';
-
- src = offset + 1;
-
- if (STREQ(drvName, "tap")) {
- offset = strchr(src, ':');
- if (!offset) {
- virXendError(conn, VIR_ERR_INTERNAL_ERROR,
- _("cannot parse vbd filename, missing driver type"));
- goto bad_parse;
- }
-
- if (VIR_ALLOC_N(drvType, (offset-src)+1)< 0) {
- virXendError(conn, VIR_ERR_NO_MEMORY,
- _("allocate new buffer"));
- goto bad_parse;
- }
- strncpy(drvType, src, (offset-src));
- drvType[offset-src] = '\0';
- src = offset + 1;
- /* Its possible to use blktap driver for block devs
- too, but kinda pointless because blkback is better,
- so we assume common case here. If blktap becomes
- omnipotent, we can revisit this, perhaps stat()'ing
- the src file in question */
- isBlock = 0;
- } else if (STREQ(drvName, "phy")) {
- isBlock = 1;
- } else if (STREQ(drvName, "file")) {
- isBlock = 0;
- }
- }
-
- if (STRPREFIX(dst, "ioemu:"))
- dst += 6;
-
- /* New style disk config from Xen >= 3.0.3 */
- if (xendConfigVersion > 1) {
- offset = strrchr(dst, ':');
- if (offset) {
- if (STREQ(offset, ":cdrom")) {
- cdrom = 1;
- } else if (STREQ(offset, ":disk")) {
- /* The default anyway */
- } else {
- /* Unknown, lets pretend its a disk too */
- }
- offset[0] = '\0';
- }
- }
-
- if (!isNoSrcCdrom) {
- virBufferVSprintf(&buf, " <disk type='%s' device='%s'>\n",
- isBlock ? "block" : "file",
- cdrom ? "cdrom" : "disk");
- if (drvType) {
- virBufferVSprintf(&buf, " <driver name='%s' type='%s'/>\n", drvName, drvType);
- } else {
- virBufferVSprintf(&buf, " <driver name='%s'/>\n", drvName);
- }
- if (isBlock) {
- virBufferVSprintf(&buf, " <source dev='%s'/>\n", src);
- } else {
- virBufferVSprintf(&buf, " <source file='%s'/>\n", src);
- }
- } else {
- /* This case is the cdrom device only */
- virBufferAddLit(&buf, " <disk device='cdrom'>\n");
- }
-
- if (STRPREFIX(dst, "xvd") || !hvm) {
- bus = "xen";
- } else if (STRPREFIX(dst, "sd")) {
- bus = "scsi";
- } else {
- bus = "ide";
- }
- virBufferVSprintf(&buf, " <target dev='%s' bus='%s'/>\n",
- dst, bus);
-
-
- /* XXX should we force mode == r, if cdrom==1, or assume
- xend has already done this ? */
- if ((mode != NULL) && (STREQ(mode, "r")))
- virBufferAddLit(&buf, " <readonly/>\n");
- else if ((mode != NULL) && (STREQ(mode, "w!")))
- virBufferAddLit(&buf, " <shareable/>\n");
- virBufferAddLit(&buf, " </disk>\n");
-
- bad_parse:
- VIR_FREE(drvName);
- VIR_FREE(drvType);
- } else if (sexpr_lookup(node, "device/vif")) {
+ if (sexpr_lookup(node, "device/vif")) {
const char *tmp2, *model;
tmp2 = sexpr_node(node, "device/vif/script");
tmp = sexpr_node(node, "device/vif/bridge");
@@ -4433,5 +4499,110 @@ error:
return (ret);
}
+struct check_path_data {
+ const char *path;
+ int ok;
+};
+
+static int
+check_path (virConnectPtr conn ATTRIBUTE_UNUSED, void *vp,
+ int isBlock ATTRIBUTE_UNUSED,
+ int cdrom, int isNoSrcCdrom,
+ int hvm ATTRIBUTE_UNUSED,
+ const char *drvName ATTRIBUTE_UNUSED,
+ const char *drvType ATTRIBUTE_UNUSED,
+ const char *src, const char *dst ATTRIBUTE_UNUSED,
+ const char *mode ATTRIBUTE_UNUSED)
+{
+ struct check_path_data *data = (struct check_path_data *) vp;
+
+ if (!isNoSrcCdrom && !cdrom && src && STREQ (src, data->path))
+ data->ok = 1;
+
+ return 0;
+}
+
+/**
+ * xenDaemonDomainBlockPeek:
+ * @dom: domain object
+ * @path: path to the file or device
+ * @offset: offset
+ * @size: size
+ * @buffer: return buffer
+ *
+ * Returns 0 if successful, -1 if error, -2 if declined.
+ */
+int
+xenDaemonDomainBlockPeek (virDomainPtr domain, const char *path,
+ unsigned long long offset, size_t size,
+ void *buffer)
+{
+ xenUnifiedPrivatePtr priv;
+ struct sexpr *root;
+ struct check_path_data data;
+ int fd, ret = -1;
+ struct stat statbuf;
+
+ priv = (xenUnifiedPrivatePtr) domain->conn->privateData;
+
+ if (domain->id < 0 && priv->xendConfigVersion < 3)
+ return -2; /* Decline, allow XM to handle it. */
+
+ /* Security check: The path must correspond to a block device. */
+ if (domain->id > 0)
+ root = sexpr_get (domain->conn, "/xend/domain/%d?detail=1",
+ domain->id);
+ else if (domain->id < 0)
+ root = sexpr_get (domain->conn, "/xend/domain/%s?detail=1",
+ domain->name);
+ else {
+ /* This call always fails for dom0. */
+ virXendError (domain->conn, VIR_ERR_NO_SUPPORT,
+ _("domainBlockPeek is not supported for dom0"));
+ return -1;
+ }
+
+ if (!root) {
+ virXendError (domain->conn, VIR_ERR_XEN_CALL, __FUNCTION__);
+ return -1;
+ }
+
+ data.path = path;
+ data.ok = 0;
+
+ if (xend_parse_sexp_blockdevs (domain->conn, root,
+ priv->xendConfigVersion,
+ check_path, &data) == -1)
+ return -1;
+
+ if (!data.ok) {
+ virXendError (domain->conn, VIR_ERR_INVALID_ARG,
+ _("invalid path"));
+ return -1;
+ }
+
+ /* The path is correct, now try to open it and get its size. */
+ fd = open (path, O_RDONLY);
+ if (fd == -1 || fstat (fd, &statbuf) == -1) {
+ virXendError (domain->conn, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+ goto done;
+ }
+
+ /* Seek and read. */
+ /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should
+ * be 64 bits on all platforms.
+ */
+ if (lseek (fd, offset, SEEK_SET) == (off_t) -1 ||
+ saferead (fd, buffer, size) == (ssize_t) -1) {
+ virXendError (domain->conn, VIR_ERR_SYSTEM_ERROR, strerror (errno));
+ goto done;
+ }
+
+ ret = 0;
+ done:
+ if (fd >= 0) close (fd);
+ return ret;
+}
+
#endif /* ! PROXY */
#endif /* WITH_XEN */
Index: src/xend_internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/xend_internal.h,v
retrieving revision 1.43
diff -b -u -p -r1.43 xend_internal.h
--- src/xend_internal.h 9 May 2008 08:17:19 -0000 1.43
+++ src/xend_internal.h 4 Jun 2008 15:08:22 -0000
@@ -244,6 +244,8 @@ virDomainPtr xenDaemonLookupByName(virCo
int xenDaemonDomainMigratePrepare (virConnectPtr dconn, char **cookie, int *cookielen, const char *uri_in, char **uri_out, unsigned long flags, const char *dname, unsigned long resource);
int xenDaemonDomainMigratePerform (virDomainPtr domain, const char *cookie, int cookielen, const char *uri, unsigned long flags, const char *dname, unsigned long resource);
+int xenDaemonDomainBlockPeek (virDomainPtr domain, const char *path, unsigned long long offset, size_t size, void *buffer);
+
#ifdef __cplusplus
}
#endif
Index: src/xm_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xm_internal.c,v
retrieving revision 1.80
diff -b -u -p -r1.80 xm_internal.c
--- src/xm_internal.c 3 Jun 2008 08:01:45 -0000 1.80
+++ src/xm_internal.c 4 Jun 2008 15:08:24 -0000
@@ -3241,4 +3241,15 @@ xenXMDomainDetachDevice(virDomainPtr dom
return (ret);
}
+int
+xenXMDomainBlockPeek (virDomainPtr dom,
+ const char *path ATTRIBUTE_UNUSED,
+ unsigned long long offset ATTRIBUTE_UNUSED,
+ size_t size ATTRIBUTE_UNUSED,
+ void *buffer ATTRIBUTE_UNUSED)
+{
+ xenXMError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+ return -1;
+}
+
#endif /* WITH_XEN */
Index: src/xm_internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/xm_internal.h,v
retrieving revision 1.11
diff -b -u -p -r1.11 xm_internal.h
--- src/xm_internal.h 10 Apr 2008 16:54:54 -0000 1.11
+++ src/xm_internal.h 4 Jun 2008 15:08:24 -0000
@@ -60,6 +60,7 @@ int xenXMDomainUndefine(virDomainPtr dom
virConfPtr xenXMParseXMLToConfig(virConnectPtr conn, const char *xml);
char *xenXMDomainFormatXML(virConnectPtr conn, virConfPtr conf);
+int xenXMDomainBlockPeek (virDomainPtr dom, const char *path, unsigned long long offset, size_t size, void *buffer);
#ifdef __cplusplus
}
Index: tests/sexpr2xmldata/sexpr2xml-curmem.xml
===================================================================
RCS file: /data/cvs/libvirt/tests/sexpr2xmldata/sexpr2xml-curmem.xml,v
retrieving revision 1.6
diff -b -u -p -r1.6 sexpr2xml-curmem.xml
--- tests/sexpr2xmldata/sexpr2xml-curmem.xml 8 May 2008 14:41:56 -0000 1.6
+++ tests/sexpr2xmldata/sexpr2xml-curmem.xml 4 Jun 2008 15:08:24 -0000
@@ -15,17 +15,17 @@
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>
+ <disk type='file' device='disk'>
+ <driver name='tap' type='aio'/>
+ <source file='/xen/rhel5.img'/>
+ <target dev='xvda' bus='xen'/>
+ </disk>
<interface type='bridge'>
<source bridge='xenbr0'/>
<target dev='vif5.0'/>
<mac address='00:16:3e:1d:06:15'/>
<script path='vif-bridge'/>
</interface>
- <disk type='file' device='disk'>
- <driver name='tap' type='aio'/>
- <source file='/xen/rhel5.img'/>
- <target dev='xvda' bus='xen'/>
- </disk>
<input type='mouse' bus='xen'/>
<graphics type='vnc' port='-1'/>
<console type='pty'>
Index: tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml
===================================================================
RCS file: /data/cvs/libvirt/tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml,v
retrieving revision 1.10
diff -b -u -p -r1.10 sexpr2xml-no-source-cdrom.xml
--- tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml 8 May 2008 14:41:56 -0000 1.10
+++ tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml 4 Jun 2008 15:08:25 -0000
@@ -20,11 +20,6 @@
<clock offset='utc'/>
<devices>
<emulator>/usr/lib/xen/bin/qemu-dm</emulator>
- <interface type='bridge'>
- <source bridge='xenbr0'/>
- <target dev='vif6.0'/>
- <mac address='00:16:3e:0a:7b:39'/>
- </interface>
<disk type='block' device='disk'>
<driver name='phy'/>
<source dev='/dev/sda8'/>
@@ -34,6 +29,11 @@
<target dev='hdc' bus='ide'/>
<readonly/>
</disk>
+ <interface type='bridge'>
+ <source bridge='xenbr0'/>
+ <target dev='vif6.0'/>
+ <mac address='00:16:3e:0a:7b:39'/>
+ </interface>
<input type='mouse' bus='ps2'/>
<graphics type='vnc' port='-1'/>
<serial type='pty'>
More information about the libvir-list
mailing list