Virt-df[1] has now gained the ability to fully parse LVM2 partitions, thus: # virt-df -c qemu:///system -h Filesystem Size Used Available Type rhel51x32kvm:hda1 96.8 MiB 14.6 MiB 82.2 MiB Linux ext2/3 rhel51x32kvm:VolGroup00/LogVol00 6.4 GiB 3.6 GiB 2.8 GiB Linux ext2/3 rhel51x32kvm:VolGroup00/LogVol01 992.0 MiB Linux swap However it still has to do it by opening the local partitions / files, which means it isn't using a "proper" part of libvirt and more importantly it cannot run remotely. I'd like to find out whether the time has come for us to look again at a virDomainBlockPeek call for libvirt. Here is the original thread plus patch from 7 months ago: http://www.redhat.com/archives/libvir-list/2007-October/thread.html#00089 (I've attached an updated patch against current CVS). I appreciate that some cases might not be simple (iSCSI?), but the same argument applies to block device statistics too, and we make those available where possible. I think a best-effort call allowing callers to peek into the block devices of guests would be very useful. Rich. [1] http://hg.et.redhat.com/applications/virt/applications/virt-df--devel -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top
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 + dnl Availability of various common functions (non-fatal if missing). AC_CHECK_FUNCS([cfmakeraw regexec uname]) Index: include/libvirt/libvirt.h =================================================================== RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h,v retrieving revision 1.72 diff -u -r1.72 libvirt.h --- include/libvirt/libvirt.h 10 Apr 2008 16:54:54 -0000 1.72 +++ include/libvirt/libvirt.h 16 Apr 2008 15:18:08 -0000 @@ -522,7 +522,11 @@ const char *path, virDomainInterfaceStatsPtr stats, size_t size); - +int virDomainBlockPeek (virDomainPtr dom, + const char *path, + 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.47 diff -u -r1.47 libvirt.h.in --- include/libvirt/libvirt.h.in 10 Apr 2008 16:54:54 -0000 1.47 +++ include/libvirt/libvirt.h.in 16 Apr 2008 15:18:09 -0000 @@ -522,7 +522,11 @@ const char *path, virDomainInterfaceStatsPtr stats, size_t size); - +int virDomainBlockPeek (virDomainPtr dom, + const char *path, + 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.46 diff -u -r1.46 driver.h --- src/driver.h 10 Apr 2008 16:54:54 -0000 1.46 +++ src/driver.h 16 Apr 2008 15:18:10 -0000 @@ -224,6 +224,13 @@ struct _virDomainInterfaceStats *stats); typedef int + (*virDrvDomainBlockPeek) + (virDomainPtr domain, + const char *path, + long long offset, size_t size, + void *buffer); + +typedef int (*virDrvDomainMigratePrepare) (virConnectPtr dconn, char **cookie, @@ -335,6 +342,7 @@ 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.137 diff -u -r1.137 libvirt.c --- src/libvirt.c 10 Apr 2008 16:54:54 -0000 1.137 +++ src/libvirt.c 16 Apr 2008 15:18:14 -0000 @@ -2573,7 +2573,78 @@ 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, + 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 (offset < 0) { + virLibDomainError (dom, VIR_ERR_INVALID_ARG, + "offset is negative"); + return -1; + } + 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.38 diff -u -r1.38 libvirt_sym.version --- src/libvirt_sym.version 28 Feb 2008 17:06:32 -0000 1.38 +++ src/libvirt_sym.version 16 Apr 2008 15:18: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.67 diff -u -r1.67 qemu_driver.c --- src/qemu_driver.c 10 Apr 2008 16:54:54 -0000 1.67 +++ src/qemu_driver.c 16 Apr 2008 15:18:16 -0000 @@ -3064,6 +3064,7 @@ NULL, /* domainMigrateFinish */ qemudDomainBlockStats, /* domainBlockStats */ qemudDomainInterfaceStats, /* domainInterfaceStats */ + NULL, /* domainBlockPeek */ NULL, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ }; Index: src/test.c =================================================================== RCS file: /data/cvs/libvirt/src/test.c,v retrieving revision 1.72 diff -u -r1.72 test.c --- src/test.c 10 Apr 2008 16:53:29 -0000 1.72 +++ src/test.c 16 Apr 2008 15:18:18 -0000 @@ -2066,6 +2066,7 @@ NULL, /* domainMigrateFinish */ NULL, /* domainBlockStats */ NULL, /* domainInterfaceStats */ + NULL, /* domainBlockPeek */ testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ }; Index: src/virsh.c =================================================================== RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.145 diff -u -r1.145 virsh.c --- src/virsh.c 10 Apr 2008 17:37:23 -0000 1.145 +++ src/virsh.c 16 Apr 2008 15:18:22 -0000 @@ -237,6 +237,7 @@ static vshCmdOpt *vshCommandOpt(vshCmd * cmd, const char *name); static int vshCommandOptInt(vshCmd * cmd, const char *name, int *found); +static long long vshCommandOptInt64(vshCmd * cmd, const char *name, int *found); static char *vshCommandOptString(vshCmd * cmd, const char *name, int *found); #if 0 @@ -820,6 +821,77 @@ return TRUE; } +/* "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} +}; + +static int +cmdDomblkpeek (vshControl *ctl, vshCmd *cmd) +{ + virDomainPtr dom; + char *name, *path; + long long offset; + size_t size; + char *buffer = 0; + int found, ret = FALSE; + + if (!vshConnectionUsability (ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain (ctl, cmd, "domain", &name))) + goto done; + + if (!(path = vshCommandOptString (cmd, "path", NULL))) + goto done; + + offset = vshCommandOptInt64 (cmd, "offset", &found); + if (!found) goto done; + + size = vshCommandOptInt (cmd, "size", &found); + if (!found) goto done; + + buffer = malloc (size); + if (!buffer) { + vshError (ctl, FALSE, _("Memory allocation failed for buffer")); + goto done; + } + + if (virDomainBlockPeek (dom, path, offset, size, buffer) == -1) { + vshError (ctl, FALSE, _("Failed to peek block device %s %s"), + name, path); + goto done; + } + + /* Dump out the memory - user can pipe into od/hexdump if they + * desire. + */ + if (size > 0) { + if (fwrite (buffer, size, 1, stdout) != 1) { + vshError (ctl, FALSE, "write: %s", strerror (errno)); + goto done; + } + } + + ret = TRUE; + done: + if (buffer) free (buffer); + if (dom) virDomainFree(dom); + return ret; +} + /* * "suspend" command */ @@ -5105,6 +5177,7 @@ {"domstate", cmdDomstate, opts_domstate, info_domstate}, {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat}, {"domifstat", cmdDomIfstat, opts_domifstat, info_domifstat}, + {"domblkpeek", cmdDomblkpeek, opts_domblkpeek, info_domblkpeek}, {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml}, {"freecell", cmdFreecell, opts_freecell, info_freecell}, {"hostname", cmdHostname, NULL, info_hostname}, @@ -5384,6 +5457,37 @@ return res; } +static int +xstrtoint64 (char const *s, int base, long long *result) +{ + char *p; + + errno = 0; + *result = strtoll (s, &p, base); + if (errno || !(*p == 0 || *p == '\n') || p == s) + return -1; + return 0; +} + +/* + * Returns option as 64 bit integer. + */ +static long long +vshCommandOptInt64(vshCmd * cmd, const char *name, int *found) +{ + vshCmdOpt *arg = vshCommandOpt(cmd, name); + long long res; + + if (arg && arg->data) { + if (xstrtoint64 (arg->data, 10, &res) == 0) { + *found = TRUE; + return res; + } + } + *found = FALSE; + return -1; +} + /* * Returns option as STRING */ Index: src/xen_unified.c =================================================================== RCS file: /data/cvs/libvirt/src/xen_unified.c,v retrieving revision 1.42 diff -u -r1.42 xen_unified.c --- src/xen_unified.c 10 Apr 2008 16:54:54 -0000 1.42 +++ src/xen_unified.c 16 Apr 2008 15:18:23 -0000 @@ -1207,6 +1207,29 @@ } static int +xenUnifiedDomainBlockPeek (virDomainPtr dom, const char *path, + 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) { @@ -1299,6 +1322,7 @@ .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.180 diff -u -r1.180 xend_internal.c --- src/xend_internal.c 10 Apr 2008 16:54:54 -0000 1.180 +++ src/xend_internal.c 16 Apr 2008 15:18:26 -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> @@ -1376,6 +1378,213 @@ return(0); } +typedef int + (*sexp_blockdevs_cb) + (virConnectPtr conn, void *data, + int isBlock, int cdrom, int isNoSrcCdrom, + 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; + const char *dst; + const char *mode; + + /* 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; + } + + drvName = malloc((offset-src)+1); + if (!drvName) { + 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; + } + + drvType = malloc((offset-src)+1); + if (!drvType) { + 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, + drvName, drvType, src, dst, mode); + + bad_parse: + if (drvName) + free(drvName); + if (drvType) + 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, + const char *drvName, const char *drvType, + const char *src, const char *dst, + const char *mode) +{ + virBuffer *buf = (virBuffer *) data; + + 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"); + } + virBufferVSprintf(buf, " <target dev='%s'/>\n", dst); + + /* 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 @@ -1525,156 +1734,16 @@ 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; - /* 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 && !strcmp( 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; - } - - drvName = malloc((offset-src)+1); - if (!drvName) { - 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 (!strcmp(drvName, "tap")) { - offset = strchr(src, ':'); - if (!offset) { - virXendError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot parse vbd filename, missing driver type")); - goto bad_parse; - } - - drvType = malloc((offset-src)+1); - if (!drvType) { - 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 (!strcmp(drvName, "phy")) { - isBlock = 1; - } else if (!strcmp(drvName, "file")) { - isBlock = 0; - } - } - - if (!strncmp(dst, "ioemu:", 6)) - dst += 6; - - /* New style disk config from Xen >= 3.0.3 */ - if (xendConfigVersion > 1) { - offset = strrchr(dst, ':'); - if (offset) { - if (!strcmp(offset, ":cdrom")) { - cdrom = 1; - } else if (!strcmp(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"); - } - virBufferVSprintf(&buf, " <target dev='%s'/>\n", dst); - - - /* XXX should we force mode == r, if cdrom==1, or assume - xend has already done this ? */ - if ((mode != NULL) && (!strcmp(mode, "r"))) - virBufferAddLit(&buf, " <readonly/>\n"); - else if ((mode != NULL) && (!strcmp(mode, "w!"))) - virBufferAddLit(&buf, " <shareable/>\n"); - virBufferAddLit(&buf, " </disk>\n"); - - bad_parse: - free(drvName); - free(drvType); - } else if (sexpr_lookup(node, "device/vif")) { + if (sexpr_lookup(node, "device/vif")) { const char *tmp2; tmp2 = sexpr_node(node, "device/vif/script"); tmp = sexpr_node(node, "device/vif/bridge"); @@ -3944,5 +4013,124 @@ 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, + 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, + 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, "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; + } + + /* 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 + + /* 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; +} + #endif /* WITH_XEN */ Index: src/xm_internal.h =================================================================== RCS file: /data/cvs/libvirt/src/xm_internal.h,v retrieving revision 1.11 diff -u -r1.11 xm_internal.h --- src/xm_internal.h 10 Apr 2008 16:54:54 -0000 1.11 +++ src/xm_internal.h 16 Apr 2008 15:18:29 -0000 @@ -60,6 +60,7 @@ virConfPtr xenXMParseXMLToConfig(virConnectPtr conn, const char *xml); char *xenXMDomainFormatXML(virConnectPtr conn, virConfPtr conf); +int xenXMDomainBlockPeek (virDomainPtr dom, const char *path, 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.4 diff -u -r1.4 sexpr2xml-curmem.xml --- tests/sexpr2xmldata/sexpr2xml-curmem.xml 21 Aug 2007 08:54:07 -0000 1.4 +++ tests/sexpr2xmldata/sexpr2xml-curmem.xml 16 Apr 2008 15:18:29 -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:disk'/> + </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:disk'/> - </disk> <input type='mouse' bus='xen'/> <graphics type='vnc' port='5905'/> </devices> Index: tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml =================================================================== RCS file: /data/cvs/libvirt/tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml,v retrieving revision 1.8 diff -u -r1.8 sexpr2xml-no-source-cdrom.xml --- tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml 30 Sep 2007 15:36:47 -0000 1.8 +++ tests/sexpr2xmldata/sexpr2xml-no-source-cdrom.xml 16 Apr 2008 15:18:29 -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'/> <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='5906'/> </devices>
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list