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
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'>
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list