[libvirt] [PATCH RFC 00/22] Support for walking backing chains on remote volumes (especially gluster)

This series adds support for waliking (detecting) of backing chain on remote storage and implements the needed callbacks for the gluster storage system via native library. The last patch in the series mocks up path canonicalization on gluster as I'm working on that but this series is getting tedious to rebase and handle so I'd like to start reviews. Peter Krempa (22): qemu: Make qemuDomainPrepareDiskChainElement aware of remote storage storage: Store gluster volume name separately storage: Rework debugging of storage file access through storage driver conf: Fix domain disk path iterator to work with networked storage storage: Add NONE protocol type for network disks storage: Add support for access to files using provided uid/gid storage: Add storage file API to read file headers storage: backend: Add unique id retrieval API storage: Add API to check accessibility of storage volumes storage: Move virStorageFileGetMetadata to the storage driver storage: Determine the local storage type right away test: storage: Initialize storage source to correct type storage: backend: Add possibility to suppress errors from backend lookup storage: Switch metadata crawler to use storage driver to get unique path storage: Switch metadata crawler to use storage driver to read headers storage: Switch metadata crawler to use storage driver file access check storage: Add infrastructure to parse remote network backing names storage: Change to new backing store parser storage: Traverse backing chains of network disks qemu: Add support for networked disks for block pull/block rebase qemu: Add support for networked disks for block commit [DO NOT APPLY UPSTREAM] storage: gluster: mock up path canonicalization cfg.mk | 2 +- src/Makefile.am | 2 + src/conf/domain_conf.c | 70 +++- src/libvirt_private.syms | 3 +- src/qemu/qemu_command.c | 21 +- src/qemu/qemu_domain.c | 10 +- src/qemu/qemu_driver.c | 116 +++++-- src/security/virt-aa-helper.c | 2 + src/storage/storage_backend.c | 16 +- src/storage/storage_backend.h | 22 +- src/storage/storage_backend_fs.c | 127 +++++++- src/storage/storage_backend_gluster.c | 141 ++++++-- src/storage/storage_driver.c | 329 ++++++++++++++++++- src/storage/storage_driver.h | 15 +- src/util/virstoragefile.c | 596 +++++++++++++++++++++------------- src/util/virstoragefile.h | 16 +- tests/Makefile.am | 7 +- tests/virstoragetest.c | 26 +- 18 files changed, 1162 insertions(+), 359 deletions(-) -- 1.9.2

Refactor the function to accept a virStorageSourcePtr instead of just the path, add a check to run it only on local storage and fix callers (possibly by using a newly introduced wrapper that wraps a path in the virStorageSource struct for legacy code) --- src/qemu/qemu_driver.c | 77 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fca1a91..ac0de1e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12013,22 +12013,26 @@ static int qemuDomainPrepareDiskChainElement(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDiskDefPtr disk, - const char *file, + virStorageSourcePtr elem, qemuDomainDiskChainMode mode) { /* The easiest way to label a single file with the same * permissions it would have as if part of the disk chain is to * temporarily modify the disk in place. */ - char *origsrc = disk->src.path; - int origformat = disk->src.format; - virStorageSourcePtr origchain = disk->src.backingStore; + virStorageSource origdisk; bool origreadonly = disk->readonly; + virQEMUDriverConfigPtr cfg = NULL; int ret = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - disk->src.path = (char *) file; /* casting away const is safe here */ - disk->src.format = VIR_STORAGE_FILE_RAW; - disk->src.backingStore = NULL; + /* XXX Labelling of non-local files isn't currently supported */ + if (virStorageSourceGetActualType(&disk->src) == VIR_STORAGE_TYPE_NETWORK) + return 0; + + cfg = virQEMUDriverGetConfig(driver); + + /* XXX This will be easier when disk->src will be a pointer */ + memcpy(&origdisk, &disk->src, sizeof(origdisk)); + memcpy(&disk->src, elem, sizeof(*elem)); disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY; if (mode == VIR_DISK_CHAIN_NO_ACCESS) { @@ -12051,9 +12055,7 @@ qemuDomainPrepareDiskChainElement(virQEMUDriverPtr driver, ret = 0; cleanup: - disk->src.path = origsrc; - disk->src.format = origformat; - disk->src.backingStore = origchain; + memcpy(&disk->src, &origdisk, sizeof(origdisk)); disk->readonly = origreadonly; virObjectUnref(cfg); return ret; @@ -12061,6 +12063,25 @@ qemuDomainPrepareDiskChainElement(virQEMUDriverPtr driver, static int +qemuDomainPrepareDiskChainElementPath(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + const char *file, + qemuDomainDiskChainMode mode) +{ + virStorageSource src; + + memset(&src, 0, sizeof(src)); + + src.type = VIR_STORAGE_TYPE_FILE; + src.format = VIR_STORAGE_FILE_RAW; + src.path = (char *) file; /* casting away const is safe here */ + + return qemuDomainPrepareDiskChainElement(driver, vm, disk, &src, mode); +} + + +static int qemuDomainSnapshotFSFreeze(virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; @@ -12765,9 +12786,9 @@ qemuDomainSnapshotCreateSingleDiskActive(virQEMUDriverPtr driver, VIR_FORCE_CLOSE(fd); } - if (qemuDomainPrepareDiskChainElement(driver, vm, disk, source, + if (qemuDomainPrepareDiskChainElement(driver, vm, disk, &snap->src, VIR_DISK_CHAIN_READ_WRITE) < 0) { - qemuDomainPrepareDiskChainElement(driver, vm, disk, source, + qemuDomainPrepareDiskChainElement(driver, vm, disk, &snap->src, VIR_DISK_CHAIN_NO_ACCESS); goto cleanup; } @@ -12898,7 +12919,7 @@ qemuDomainSnapshotUndoSingleDiskActive(virQEMUDriverPtr driver, (persistDisk && VIR_STRDUP(persistSource, source) < 0)) goto cleanup; - qemuDomainPrepareDiskChainElement(driver, vm, disk, disk->src.path, + qemuDomainPrepareDiskChainElement(driver, vm, disk, &disk->src, VIR_DISK_CHAIN_NO_ACCESS); if (need_unlink && virStorageFileStat(&disk->src, &st) == 0 && S_ISREG(st.st_mode) && @@ -15208,10 +15229,10 @@ qemuDomainBlockCopy(virDomainObjPtr vm, if (VIR_STRDUP(mirror, dest) < 0) goto endjob; - if (qemuDomainPrepareDiskChainElement(driver, vm, disk, dest, - VIR_DISK_CHAIN_READ_WRITE) < 0) { - qemuDomainPrepareDiskChainElement(driver, vm, disk, dest, - VIR_DISK_CHAIN_NO_ACCESS); + if (qemuDomainPrepareDiskChainElementPath(driver, vm, disk, dest, + VIR_DISK_CHAIN_READ_WRITE) < 0) { + qemuDomainPrepareDiskChainElementPath(driver, vm, disk, dest, + VIR_DISK_CHAIN_NO_ACCESS); goto endjob; } @@ -15222,8 +15243,8 @@ qemuDomainBlockCopy(virDomainObjPtr vm, virDomainAuditDisk(vm, NULL, dest, "mirror", ret >= 0); qemuDomainObjExitMonitor(driver, vm); if (ret < 0) { - qemuDomainPrepareDiskChainElement(driver, vm, disk, dest, - VIR_DISK_CHAIN_NO_ACCESS); + qemuDomainPrepareDiskChainElementPath(driver, vm, disk, dest, + VIR_DISK_CHAIN_NO_ACCESS); goto endjob; } @@ -15400,12 +15421,12 @@ qemuDomainBlockCommit(virDomainPtr dom, * operation succeeds, but doing that requires tracking the * operation in XML across libvirtd restarts. */ clean_access = true; - if (qemuDomainPrepareDiskChainElement(driver, vm, disk, baseSource->path, + if (qemuDomainPrepareDiskChainElement(driver, vm, disk, baseSource, VIR_DISK_CHAIN_READ_WRITE) < 0 || (top_parent && top_parent != disk->src.path && - qemuDomainPrepareDiskChainElement(driver, vm, disk, - top_parent, - VIR_DISK_CHAIN_READ_WRITE) < 0)) + qemuDomainPrepareDiskChainElementPath(driver, vm, disk, + top_parent, + VIR_DISK_CHAIN_READ_WRITE) < 0)) goto endjob; /* Start the commit operation. Pass the user's original spelling, @@ -15423,12 +15444,12 @@ qemuDomainBlockCommit(virDomainPtr dom, endjob: if (ret < 0 && clean_access) { /* Revert access to read-only, if possible. */ - qemuDomainPrepareDiskChainElement(driver, vm, disk, baseSource->path, + qemuDomainPrepareDiskChainElement(driver, vm, disk, baseSource, VIR_DISK_CHAIN_READ_ONLY); if (top_parent && top_parent != disk->src.path) - qemuDomainPrepareDiskChainElement(driver, vm, disk, - top_parent, - VIR_DISK_CHAIN_READ_ONLY); + qemuDomainPrepareDiskChainElementPath(driver, vm, disk, + top_parent, + VIR_DISK_CHAIN_READ_ONLY); } if (!qemuDomainObjEndJob(driver, vm)) vm = NULL; -- 1.9.2

On 05/06/2014 07:36 AM, Peter Krempa wrote:
Refactor the function to accept a virStorageSourcePtr instead of just the path, add a check to run it only on local storage and fix callers (possibly by using a newly introduced wrapper that wraps a path in the virStorageSource struct for legacy code) --- src/qemu/qemu_driver.c | 77 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 28 deletions(-)
{ /* The easiest way to label a single file with the same * permissions it would have as if part of the disk chain is to * temporarily modify the disk in place. */
Actually, the easiest way is probably to modify the audit and security code to operate on a virStorageSourcePtr rather than a virDomainDiskDefPtr - but that's refactoring that can be done independently.
- char *origsrc = disk->src.path; - int origformat = disk->src.format; - virStorageSourcePtr origchain = disk->src.backingStore; + virStorageSource origdisk; bool origreadonly = disk->readonly; + virQEMUDriverConfigPtr cfg = NULL; int ret = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
- disk->src.path = (char *) file; /* casting away const is safe here */ - disk->src.format = VIR_STORAGE_FILE_RAW; - disk->src.backingStore = NULL; + /* XXX Labelling of non-local files isn't currently supported */ + if (virStorageSourceGetActualType(&disk->src) == VIR_STORAGE_TYPE_NETWORK) + return 0; + + cfg = virQEMUDriverGetConfig(driver); + + /* XXX This will be easier when disk->src will be a pointer */
"will be" as in later patches make the conversion? Might read better as "would be easier if disk->were a pointer"
+ memcpy(&origdisk, &disk->src, sizeof(origdisk)); + memcpy(&disk->src, elem, sizeof(*elem));
And indeed, doing a pass over the source tree to use a storageSource via pointer rather than inline struct would be a fairly mechanical (but probably large) patch worth doing.
@@ -12061,6 +12063,25 @@ qemuDomainPrepareDiskChainElement(virQEMUDriverPtr driver,
static int +qemuDomainPrepareDiskChainElementPath(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + const char *file, + qemuDomainDiskChainMode mode) +{ + virStorageSource src; + + memset(&src, 0, sizeof(src)); + + src.type = VIR_STORAGE_TYPE_FILE; + src.format = VIR_STORAGE_FILE_RAW; + src.path = (char *) file; /* casting away const is safe here */
Indeed; we can later add a VIR_STRDUP and VIR_FREE if the internals make it no longer safe. ACK with comment cleanup. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The gluster volume name was previously stored as part of the source path string. This isn't a good idea when we want to start to deal with resolving of relative paths on gluster as a storage volume can't actually be changed by ../-ing it. Parse and store the volume name separately for gluster storage volumes and use the newly stored variable appropriately. --- src/conf/domain_conf.c | 33 ++++++++++++++++++++++++++++++++- src/qemu/qemu_command.c | 19 ++++++++++++++----- src/storage/storage_backend_gluster.c | 27 ++++++++------------------- src/util/virstoragefile.c | 1 + src/util/virstoragefile.h | 1 + 5 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6c3bdad..3cb6693 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5002,6 +5002,27 @@ virDomainDiskSourceParse(xmlNodePtr node, goto cleanup; } + /* for historical reasons the volume name for gluster volume is stored + * as a part of the path. This is hard to work with when dealing with + * relative names. Split out the volume into a separate variable */ + if (src->path && src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) { + char *tmp; + if (!(tmp = strchr(src->path, '/')) || + tmp == src->path) { + virReportError(VIR_ERR_XML_ERROR, + _("missing volume name or file name in " + "gluster source path '%s'"), src->path); + goto cleanup; + } + + src->volume = src->path; + + if (VIR_STRDUP(src->path, tmp) < 0) + goto cleanup; + + tmp[0] = '\0'; + } + child = node->children; while (child != NULL) { if (child->type == XML_ELEMENT_NODE && @@ -14841,6 +14862,7 @@ virDomainDiskSourceFormat(virBufferPtr buf, unsigned int flags) { size_t n; + char *path = NULL; const char *startupPolicy = NULL; if (policy) @@ -14876,7 +14898,16 @@ virDomainDiskSourceFormat(virBufferPtr buf, case VIR_STORAGE_TYPE_NETWORK: virBufferAsprintf(buf, "<source protocol='%s'", virStorageNetProtocolTypeToString(src->protocol)); - virBufferEscapeString(buf, " name='%s'", src->path); + + + if (src->volume) { + if (virAsprintf(&path, "%s%s", src->volume, src->path) < 0) + return -1; + } + + virBufferEscapeString(buf, " name='%s'", path ? path : src->path); + + VIR_FREE(path); if (src->nhosts == 0) { virBufferAddLit(buf, "/>\n"); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6c1e17d..c0f2fb6 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3594,6 +3594,7 @@ qemuNetworkDriveGetPort(int protocol, static char * qemuBuildNetworkDriveURI(int protocol, const char *src, + const char *volume, size_t nhosts, virStorageNetHostDefPtr hosts, const char *username, @@ -3693,11 +3694,18 @@ qemuBuildNetworkDriveURI(int protocol, if ((uri->port = qemuNetworkDriveGetPort(protocol, hosts->port)) < 0) goto cleanup; - if (src && - virAsprintf(&uri->path, "%s%s", - src[0] == '/' ? "" : "/", - src) < 0) - goto cleanup; + if (src) { + if (volume) { + if (virAsprintf(&uri->path, "/%s%s", + volume, src) < 0) + goto cleanup; + } else { + if (virAsprintf(&uri->path, "%s%s", + src[0] == '/' ? "" : "/", + src) < 0) + goto cleanup; + } + } if (hosts->socket && virAsprintf(&uri->query, "socket=%s", hosts->socket) < 0) @@ -3859,6 +3867,7 @@ qemuGetDriveSourceString(virStorageSourcePtr src, case VIR_STORAGE_TYPE_NETWORK: if (!(*source = qemuBuildNetworkDriveURI(src->protocol, src->path, + src->volume, src->nhosts, src->hosts, username, diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c index 2f0592f..8679d96 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -533,8 +533,6 @@ typedef virStorageFileBackendGlusterPriv *virStorageFileBackendGlusterPrivPtr; struct _virStorageFileBackendGlusterPriv { glfs_t *vol; - char *volname; - char *path; }; @@ -547,7 +545,6 @@ virStorageFileBackendGlusterDeinit(virStorageSourcePtr src) if (priv->vol) glfs_fini(priv->vol); - VIR_FREE(priv->volname); VIR_FREE(priv); src->drv->priv = NULL; @@ -564,21 +561,14 @@ virStorageFileBackendGlusterInit(virStorageSourcePtr src) VIR_DEBUG("initializing gluster storage file %p(%s/%s)", src, hostname, src->path); - if (VIR_ALLOC(priv) < 0) - return -1; - - if (VIR_STRDUP(priv->volname, src->path) < 0) - goto error; - - if (!(priv->path = strchr(priv->volname, '/'))) { + if (!src->volume) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid path of gluster volume: '%s'"), - src->path); - goto error; + _("missing gluster volume name for path '%s'"), src->path); + return -1; } - *priv->path = '\0'; - priv->path++; + if (VIR_ALLOC(priv) < 0) + return -1; if (host->port && virStrToLong_i(host->port, NULL, 10, &port) < 0) { @@ -591,7 +581,7 @@ virStorageFileBackendGlusterInit(virStorageSourcePtr src) if (host->transport == VIR_STORAGE_NET_HOST_TRANS_UNIX) hostname = host->socket; - if (!(priv->vol = glfs_new(priv->volname))) { + if (!(priv->vol = glfs_new(src->volume))) { virReportOOMError(); goto error; } @@ -617,7 +607,6 @@ virStorageFileBackendGlusterInit(virStorageSourcePtr src) return 0; error: - VIR_FREE(priv->volname); if (priv->vol) glfs_fini(priv->vol); VIR_FREE(priv); @@ -632,7 +621,7 @@ virStorageFileBackendGlusterUnlink(virStorageSourcePtr src) virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; int ret; - ret = glfs_unlink(priv->vol, priv->path); + ret = glfs_unlink(priv->vol, src->path); /* preserve errno */ VIR_DEBUG("removing storage file %p(%s/%s): ret=%d, errno=%d", @@ -648,7 +637,7 @@ virStorageFileBackendGlusterStat(virStorageSourcePtr src, virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; int ret; - ret = glfs_stat(priv->vol, priv->path, st); + ret = glfs_stat(priv->vol, src->path, st); /* preserve errno */ VIR_DEBUG("stat of storage file %p(%s/%s): ret=%d, errno=%d", diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 96af27b..cf270f6 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1755,6 +1755,7 @@ virStorageSourceClear(virStorageSourcePtr def) return; VIR_FREE(def->path); + VIR_FREE(def->volume); virStorageSourcePoolDefFree(def->srcpool); VIR_FREE(def->driverName); virBitmapFree(def->features); diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 6072409..3777d66 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -210,6 +210,7 @@ typedef virStorageSource *virStorageSourcePtr; struct _virStorageSource { int type; /* enum virStorageType */ char *path; + char *volume; /* volume name for remote storage */ int protocol; /* enum virStorageNetProtocol */ size_t nhosts; virStorageNetHostDefPtr hosts; -- 1.9.2

On 05/06/2014 07:36 AM, Peter Krempa wrote:
The gluster volume name was previously stored as part of the source path string. This isn't a good idea when we want to start to deal with resolving of relative paths on gluster as a storage volume can't actually be changed by ../-ing it.
Parse and store the volume name separately for gluster storage volumes and use the newly stored variable appropriately. --- src/conf/domain_conf.c | 33 ++++++++++++++++++++++++++++++++- src/qemu/qemu_command.c | 19 ++++++++++++++----- src/storage/storage_backend_gluster.c | 27 ++++++++------------------- src/util/virstoragefile.c | 1 + src/util/virstoragefile.h | 1 + 5 files changed, 56 insertions(+), 25 deletions(-)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Print the debug statements of individual file access functions from the main API functions instead of the individual backend functions. Also enhance initialization debug messages on a per-backend basis. --- src/storage/storage_backend_fs.c | 43 ++++++++++++++++++++++------------- src/storage/storage_backend_gluster.c | 30 +++++++++--------------- src/storage/storage_driver.c | 27 +++++++++++++++++++--- 3 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index de27890..3d088ba 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -1339,18 +1339,31 @@ virStorageBackend virStorageBackendNetFileSystem = { }; +static void +virStorageFileBackendFileDeinit(virStorageSourcePtr src) +{ + VIR_DEBUG("deinitializing FS storage file %p (%s:%s)", src, + virStorageTypeToString(virStorageSourceGetActualType(src)), + src->path); + +} + + static int -virStorageFileBackendFileUnlink(virStorageSourcePtr src) +virStorageFileBackendFileInit(virStorageSourcePtr src) { - int ret; + VIR_DEBUG("initializing FS storage file %p (%s:%s)", src, + virStorageTypeToString(virStorageSourceGetActualType(src)), + src->path); - ret = unlink(src->path); - /* preserve errno */ + return 0; +} - VIR_DEBUG("removing storage file %p(%s): ret=%d, errno=%d", - src, src->path, ret, errno); - return ret; +static int +virStorageFileBackendFileUnlink(virStorageSourcePtr src) +{ + return unlink(src->path); } @@ -1358,21 +1371,16 @@ static int virStorageFileBackendFileStat(virStorageSourcePtr src, struct stat *st) { - int ret; - - ret = stat(src->path, st); - /* preserve errno */ - - VIR_DEBUG("stat of storage file %p(%s): ret=%d, errno=%d", - src, src->path, ret, errno); - - return ret; + return stat(src->path, st); } virStorageFileBackend virStorageFileBackendFile = { .type = VIR_STORAGE_TYPE_FILE, + .backendInit = virStorageFileBackendFileInit, + .backendDeinit = virStorageFileBackendFileDeinit, + .storageFileUnlink = virStorageFileBackendFileUnlink, .storageFileStat = virStorageFileBackendFileStat, }; @@ -1381,6 +1389,9 @@ virStorageFileBackend virStorageFileBackendFile = { virStorageFileBackend virStorageFileBackendBlock = { .type = VIR_STORAGE_TYPE_BLOCK, + .backendInit = virStorageFileBackendFileInit, + .backendDeinit = virStorageFileBackendFileDeinit, + .storageFileStat = virStorageFileBackendFileStat, }; diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c index 8679d96..e578e73 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -539,10 +539,12 @@ struct _virStorageFileBackendGlusterPriv { static void virStorageFileBackendGlusterDeinit(virStorageSourcePtr src) { - VIR_DEBUG("deinitializing gluster storage file %p(%s/%s)", - src, src->hosts[0].name, src->path); virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + VIR_DEBUG("deinitializing gluster storage file %p (gluster://%s:%s/%s%s)", + src, src->hosts->name, src->hosts->port ? src->hosts->port : "0", + src->volume, src->path); + if (priv->vol) glfs_fini(priv->vol); @@ -558,12 +560,14 @@ virStorageFileBackendGlusterInit(virStorageSourcePtr src) const char *hostname = host->name; int port = 0; - VIR_DEBUG("initializing gluster storage file %p(%s/%s)", - src, hostname, src->path); + VIR_DEBUG("initializing gluster storage file %p (gluster://%s:%s/%s%s)", + src, hostname, host->port ? host->port : "0", + NULLSTR(src->volume), src->path); if (!src->volume) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing gluster volume name for path '%s'"), src->path); + _("missing gluster volume name for path '%s'"), + src->path); return -1; } @@ -619,14 +623,8 @@ static int virStorageFileBackendGlusterUnlink(virStorageSourcePtr src) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; - int ret; - - ret = glfs_unlink(priv->vol, src->path); - /* preserve errno */ - VIR_DEBUG("removing storage file %p(%s/%s): ret=%d, errno=%d", - src, src->hosts[0].name, src->path, ret, errno); - return ret; + return glfs_unlink(priv->vol, src->path); } @@ -635,14 +633,8 @@ virStorageFileBackendGlusterStat(virStorageSourcePtr src, struct stat *st) { virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; - int ret; - ret = glfs_stat(priv->vol, src->path, st); - /* preserve errno */ - - VIR_DEBUG("stat of storage file %p(%s/%s): ret=%d, errno=%d", - src, src->hosts[0].name, src->path, ret, errno); - return ret; + return glfs_stat(priv->vol, src->path, st); } diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 2cb8347..db327f4 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -2808,13 +2808,20 @@ virStorageFileInit(virStorageSourcePtr src) int virStorageFileCreate(virStorageSourcePtr src) { + int ret; + if (!virStorageFileIsInitialized(src) || !src->drv->backend->storageFileCreate) { errno = ENOSYS; return -2; } - return src->drv->backend->storageFileCreate(src); + ret = src->drv->backend->storageFileCreate(src); + + VIR_DEBUG("created storage file %p: ret=%d, errno=%d", + src, ret, errno); + + return ret; } @@ -2831,13 +2838,20 @@ virStorageFileCreate(virStorageSourcePtr src) int virStorageFileUnlink(virStorageSourcePtr src) { + int ret; + if (!virStorageFileIsInitialized(src) || !src->drv->backend->storageFileUnlink) { errno = ENOSYS; return -2; } - return src->drv->backend->storageFileUnlink(src); + ret = src->drv->backend->storageFileUnlink(src); + + VIR_DEBUG("unlinked storage file %p: ret=%d, errno=%d", + src, ret, errno); + + return ret; } @@ -2854,11 +2868,18 @@ int virStorageFileStat(virStorageSourcePtr src, struct stat *st) { + int ret; + if (!virStorageFileIsInitialized(src) || !src->drv->backend->storageFileStat) { errno = ENOSYS; return -2; } - return src->drv->backend->storageFileStat(src, st); + ret = src->drv->backend->storageFileStat(src, st); + + VIR_DEBUG("stat of storage file %p: ret=%d, errno=%d", + src, ret, errno); + + return ret; } -- 1.9.2

On 05/06/2014 07:36 AM, Peter Krempa wrote:
Print the debug statements of individual file access functions from the main API functions instead of the individual backend functions.
Also enhance initialization debug messages on a per-backend basis. --- src/storage/storage_backend_fs.c | 43 ++++++++++++++++++++++------------- src/storage/storage_backend_gluster.c | 30 +++++++++--------------- src/storage/storage_driver.c | 27 +++++++++++++++++++--- 3 files changed, 62 insertions(+), 38 deletions(-)
ACK -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Skip networked storage but continue iteration through backing chain to iterate through all the local paths in the backing chain. --- src/conf/domain_conf.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 3cb6693..25758d9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -18686,34 +18686,37 @@ virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, int ret = -1; size_t depth = 0; virStorageSourcePtr tmp; - const char *path = virDomainDiskGetSource(disk); - int type = virDomainDiskGetType(disk); + char *brokenRaw = NULL; - if (!path || type == VIR_STORAGE_TYPE_NETWORK || - (type == VIR_STORAGE_TYPE_VOLUME && - disk->src.srcpool && - disk->src.srcpool->mode == VIR_STORAGE_SOURCE_POOL_MODE_DIRECT)) - return 0; - - if (iter(disk, path, 0, opaque) < 0) - goto cleanup; + if (!ignoreOpenFailure) { + if (virStorageFileChainGetBroken(&disk->src, &brokenRaw) < 0) + goto cleanup; - tmp = disk->src.backingStore; - while (tmp && virStorageIsFile(tmp->path)) { - if (!ignoreOpenFailure && tmp->backingStoreRaw && !tmp->backingStore) { + if (brokenRaw) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to visit backing chain file %s"), - tmp->backingStoreRaw); + brokenRaw); goto cleanup; } - if (iter(disk, tmp->path, ++depth, opaque) < 0) - goto cleanup; - tmp = tmp->backingStore; + } + + for (tmp = &disk->src; tmp; tmp = tmp->backingStore) { + int actualType = virStorageSourceGetActualType(tmp); + /* execute the callback only for local storage */ + if (actualType != VIR_STORAGE_TYPE_NETWORK && + actualType != VIR_STORAGE_TYPE_VOLUME && + tmp->path) { + if (iter(disk, tmp->path, depth, opaque) < 0) + goto cleanup; + } + + depth++; } ret = 0; cleanup: + VIR_FREE(brokenRaw); return ret; } -- 1.9.2

On 05/06/2014 07:36 AM, Peter Krempa wrote:
Skip networked storage but continue iteration through backing chain to iterate through all the local paths in the backing chain. --- src/conf/domain_conf.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-)
ACK. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Currently the protocol type with index 0 was NBD which made it hard to distinguish wether the protocol type was actually assigned. Add a new protocol type with index 0 to distinguish it explicitly. --- src/qemu/qemu_command.c | 2 ++ src/qemu/qemu_driver.c | 3 +++ src/util/virstoragefile.c | 1 + src/util/virstoragefile.h | 1 + 4 files changed, 7 insertions(+) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c0f2fb6..48eeae0 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3582,6 +3582,7 @@ qemuNetworkDriveGetPort(int protocol, case VIR_STORAGE_NET_PROTOCOL_RBD: case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: /* not aplicable */ return -1; } @@ -3798,6 +3799,7 @@ qemuBuildNetworkDriveURI(int protocol, case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: goto cleanup; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ac0de1e..b0a79e4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12338,6 +12338,7 @@ qemuDomainSnapshotPrepareDiskExternalBackingInactive(virDomainDiskDefPtr disk) case VIR_STORAGE_TYPE_NETWORK: switch ((virStorageNetProtocol) disk->src.protocol) { + case VIR_STORAGE_NET_PROTOCOL_NONE: case VIR_STORAGE_NET_PROTOCOL_NBD: case VIR_STORAGE_NET_PROTOCOL_RBD: case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: @@ -12403,6 +12404,7 @@ qemuDomainSnapshotPrepareDiskExternalOverlayActive(virDomainSnapshotDiskDefPtr d case VIR_STORAGE_NET_PROTOCOL_GLUSTER: return 0; + case VIR_STORAGE_NET_PROTOCOL_NONE: case VIR_STORAGE_NET_PROTOCOL_NBD: case VIR_STORAGE_NET_PROTOCOL_RBD: case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: @@ -12545,6 +12547,7 @@ qemuDomainSnapshotPrepareDiskInternal(virConnectPtr conn, case VIR_STORAGE_TYPE_NETWORK: switch ((virStorageNetProtocol) disk->src.protocol) { + case VIR_STORAGE_NET_PROTOCOL_NONE: case VIR_STORAGE_NET_PROTOCOL_NBD: case VIR_STORAGE_NET_PROTOCOL_RBD: case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index cf270f6..73cef3c 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -72,6 +72,7 @@ VIR_ENUM_IMPL(virStorageFileFeature, ) VIR_ENUM_IMPL(virStorageNetProtocol, VIR_STORAGE_NET_PROTOCOL_LAST, + "none", "nbd", "rbd", "sheepdog", diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 3777d66..95f2e45 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -118,6 +118,7 @@ struct _virStorageTimestamps { /* Information related to network storage */ typedef enum { + VIR_STORAGE_NET_PROTOCOL_NONE, VIR_STORAGE_NET_PROTOCOL_NBD, VIR_STORAGE_NET_PROTOCOL_RBD, VIR_STORAGE_NET_PROTOCOL_SHEEPDOG, -- 1.9.2

On 05/06/2014 07:36 AM, Peter Krempa wrote:
Currently the protocol type with index 0 was NBD which made it hard to distinguish wether the protocol type was actually assigned. Add a new
s/wether/whether/
protocol type with index 0 to distinguish it explicitly. --- src/qemu/qemu_command.c | 2 ++ src/qemu/qemu_driver.c | 3 +++ src/util/virstoragefile.c | 1 + src/util/virstoragefile.h | 1 + 4 files changed, 7 insertions(+)
Missing a change to src/conf/domain_conf.c: virDomainDiskSourceParse must change: if ((src->protocol = virStorageNetProtocolTypeFromString(protocol)) < 0){ to use <= 0, so that XML doesn't start accepting "none" as a valid protocol type.
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c0f2fb6..48eeae0 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3582,6 +3582,7 @@ qemuNetworkDriveGetPort(int protocol,
case VIR_STORAGE_NET_PROTOCOL_RBD: case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: /* not aplicable */
s/aplicable/applicable/ while at it -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

To allow using the storage driver APIs to access files on various storage sources in an universal fashion possibly on storage such as nfs with root squash we'll need to store the desired uid/gid in the metadata. Add new initialisation API that will store the desired uid/gid and a wrapper for the current use. Additionally add docs for the two APIs. --- src/storage/storage_backend.h | 3 +++ src/storage/storage_driver.c | 39 ++++++++++++++++++++++++++++++++++++++- src/storage/storage_driver.h | 5 +++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index 456b9d7..fcbb6da 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -169,6 +169,9 @@ typedef virStorageFileBackend *virStorageFileBackendPtr; struct _virStorageDriverData { virStorageFileBackendPtr backend; void *priv; + + uid_t uid; + gid_t gid; }; typedef int diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index db327f4..4c283fa 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -2774,13 +2774,37 @@ virStorageFileDeinit(virStorageSourcePtr src) } +/** + * virStorageFileInitAs: + * + * @src: storage source definition + * @uid: uid to access the file as, -1 for current uid + * @gid: gid to access the file as, -1 for current gid + * + * Initialize a storage source to be used with storage driver. Use the provided + * uid and gid if possible for the operations. + * + * Returns 0 if the storage file was successfully initialized, -1 if the + * initialization failed. Libvirt error is reported. + */ int -virStorageFileInit(virStorageSourcePtr src) +virStorageFileInitAs(virStorageSourcePtr src, + uid_t uid, gid_t gid) { int actualType = virStorageSourceGetActualType(src); if (VIR_ALLOC(src->drv) < 0) return -1; + if (uid == (uid_t) -1) + src->drv->uid = geteuid(); + else + src->drv->uid = uid; + + if (gid == (gid_t) -1) + src->drv->gid = getegid(); + else + src->drv->gid = gid; + if (!(src->drv->backend = virStorageFileBackendForType(actualType, src->protocol))) goto error; @@ -2798,6 +2822,19 @@ virStorageFileInit(virStorageSourcePtr src) /** + * virStorageFileInit: + * + * See virStorageFileInitAs. The file is initialized to be accessed by the + * current user. + */ +int +virStorageFileInit(virStorageSourcePtr src) +{ + return virStorageFileInitAs(src, (uid_t) -1, (gid_t) -1); +} + + +/** * virStorageFileCreate: Creates an empty storage file via storage driver * * @src: file structure pointing to the file diff --git a/src/storage/storage_driver.h b/src/storage/storage_driver.h index fb03870..49be999 100644 --- a/src/storage/storage_driver.h +++ b/src/storage/storage_driver.h @@ -29,8 +29,9 @@ # include "storage_conf.h" # include "virstoragefile.h" -int -virStorageFileInit(virStorageSourcePtr src); +int virStorageFileInit(virStorageSourcePtr src); +int virStorageFileInitAs(virStorageSourcePtr src, + uid_t uid, gid_t gid); void virStorageFileDeinit(virStorageSourcePtr src); int virStorageFileCreate(virStorageSourcePtr src); -- 1.9.2

Add storage driver based functions to access headers of storage files for metadata extraction. Along with this patch a local filesystem and gluster via libgfapi implementation is provided. The gluster implementation is based on code of the saferead_lim function. --- src/storage/storage_backend.h | 7 ++++ src/storage/storage_backend_fs.c | 30 ++++++++++++++++ src/storage/storage_backend_gluster.c | 66 +++++++++++++++++++++++++++++++++++ src/storage/storage_driver.c | 40 +++++++++++++++++++++ src/storage/storage_driver.h | 3 ++ 5 files changed, 146 insertions(+) diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index fcbb6da..6be5ca7 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -190,6 +190,12 @@ typedef int (*virStorageFileBackendStat)(virStorageSourcePtr src, struct stat *st); +typedef ssize_t +(*virStorageFileBackendReadHeader)(virStorageSourcePtr src, + ssize_t max_len, + char **buf); + + virStorageFileBackendPtr virStorageFileBackendForType(int type, int protocol); @@ -204,6 +210,7 @@ struct _virStorageFileBackend { * error on failure. */ virStorageFileBackendInit backendInit; virStorageFileBackendDeinit backendDeinit; + virStorageFileBackendReadHeader storageFileReadHeader; /* The following group of callbacks is expected to set errno * and return -1 on error. No libvirt error shall be reported */ diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 3d088ba..33551e7 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -1375,6 +1375,34 @@ virStorageFileBackendFileStat(virStorageSourcePtr src, } +static ssize_t +virStorageFileBackendFileReadHeader(virStorageSourcePtr src, + ssize_t max_len, + char **buf) +{ + int fd = -1; + ssize_t ret = -1; + + if ((fd = virFileOpenAs(src->path, O_RDONLY, 0, + src->drv->uid, src->drv->gid, 0)) < 0) { + virReportSystemError(-fd, _("Failed to open file '%s'"), + src->path); + return -1; + } + + if ((ret = virFileReadHeaderFD(fd, max_len, buf)) < 0) { + virReportSystemError(errno, + _("cannot read header '%s'"), src->path); + goto cleanup; + } + + cleanup: + VIR_FORCE_CLOSE(fd); + + return ret; +} + + virStorageFileBackend virStorageFileBackendFile = { .type = VIR_STORAGE_TYPE_FILE, @@ -1383,6 +1411,7 @@ virStorageFileBackend virStorageFileBackendFile = { .storageFileUnlink = virStorageFileBackendFileUnlink, .storageFileStat = virStorageFileBackendFileStat, + .storageFileReadHeader = virStorageFileBackendFileReadHeader, }; @@ -1393,6 +1422,7 @@ virStorageFileBackend virStorageFileBackendBlock = { .backendDeinit = virStorageFileBackendFileDeinit, .storageFileStat = virStorageFileBackendFileStat, + .storageFileReadHeader = virStorageFileBackendFileReadHeader, }; diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c index e578e73..badffae 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -638,6 +638,71 @@ virStorageFileBackendGlusterStat(virStorageSourcePtr src, } +static ssize_t +virStorageFileBackendGlusterReadHeader(virStorageSourcePtr src, + ssize_t max_len, + char **buf) +{ + virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + glfs_fd_t *fd = NULL; + size_t alloc = 0; + size_t size = 0; + int save_errno; + ssize_t ret = -1; + + *buf = NULL; + + if (!(fd = glfs_open(priv->vol, src->path, O_RDONLY))) { + virReportSystemError(errno, _("Failed to open file '%s'"), + src->path); + goto cleanup; + } + + /* code below is shamelesly stolen from saferead_lim */ + for (;;) { + int count; + int requested; + + if (size + BUFSIZ + 1 > alloc) { + alloc += alloc / 2; + if (alloc < size + BUFSIZ + 1) + alloc = size + BUFSIZ + 1; + + if (VIR_REALLOC_N(*buf, alloc) < 0) { + save_errno = errno; + break; + } + } + + /* Ensure that (size + requested <= max_len); */ + requested = MIN(size < max_len ? max_len - size : 0, + alloc - size - 1); + count = glfs_read(fd, *buf + size, requested, 0); + size += count; + + if (count != requested || requested == 0) { + save_errno = errno; + if (count < 0) { + virReportSystemError(errno, + _("cannot read header '%s'"), src->path); + break; + } + ret = size; + goto cleanup; + } + } + + VIR_FREE(*buf); + errno = save_errno; + + cleanup: + if (fd) + glfs_close(fd); + + return ret; +} + + virStorageFileBackend virStorageFileBackendGluster = { .type = VIR_STORAGE_TYPE_NETWORK, .protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER, @@ -647,4 +712,5 @@ virStorageFileBackend virStorageFileBackendGluster = { .storageFileUnlink = virStorageFileBackendGlusterUnlink, .storageFileStat = virStorageFileBackendGlusterStat, + .storageFileReadHeader = virStorageFileBackendGlusterReadHeader, }; diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 4c283fa..8da8386 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -2920,3 +2920,43 @@ virStorageFileStat(virStorageSourcePtr src, return ret; } + + +/** + * virStorageFileReadHeader: read the beginning bytes of a file into a buffer + * + * @src: file structure pointing to the file + * @max_len: maximum number of bytes read from the storage file + * @buf: buffer to read the data into. buffer shall be freed by caller) + * + * Returns the count of bytes read on success and -1 on failure, -2 if the + * function isn't supported by the backend. Libvirt error is reported on failure. + */ +ssize_t +virStorageFileReadHeader(virStorageSourcePtr src, + ssize_t max_len, + char **buf) +{ + ssize_t ret; + + if (!virStorageFileIsInitialized(src)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("storage file backend not initialized")); + return -1; + } + + if (!src->drv->backend->storageFileReadHeader) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("storage file header reading is not supported for " + "storage type %s (protocol: %s)'"), + virStorageTypeToString(src->type), + virStorageNetProtocolTypeToString(src->protocol)); + return -1; + } + + ret = src->drv->backend->storageFileReadHeader(src, max_len, buf); + + VIR_DEBUG("read of storage header %p: ret=%zd", src, ret); + + return ret; +} diff --git a/src/storage/storage_driver.h b/src/storage/storage_driver.h index 49be999..d67d74b 100644 --- a/src/storage/storage_driver.h +++ b/src/storage/storage_driver.h @@ -38,6 +38,9 @@ int virStorageFileCreate(virStorageSourcePtr src); int virStorageFileUnlink(virStorageSourcePtr src); int virStorageFileStat(virStorageSourcePtr src, struct stat *stat); +ssize_t virStorageFileReadHeader(virStorageSourcePtr src, + ssize_t max_len, + char **buf); int storageRegister(void); -- 1.9.2

Different protocols have different means to uniquely identify a storage file. This patch implements a storage driver API to retrieve a unique string describing a volume. The current implementation works for local storage only and returns the canonical path of the volume. To add caching support the local filesystem driver now has a private structure holding the cached string, which is created only when it's initially accessed. This patch provides the implementation for local files only for start. --- src/storage/storage_backend.h | 3 +++ src/storage/storage_backend_fs.c | 49 ++++++++++++++++++++++++++++++++++++++++ src/storage/storage_driver.c | 30 ++++++++++++++++++++++++ src/storage/storage_driver.h | 1 + 4 files changed, 83 insertions(+) diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index 6be5ca7..5d71cde 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -195,6 +195,8 @@ typedef ssize_t ssize_t max_len, char **buf); +typedef const char * +(*virStorageFileBackendGetUniqueIdentifier)(virStorageSourcePtr src); virStorageFileBackendPtr virStorageFileBackendForType(int type, int protocol); @@ -211,6 +213,7 @@ struct _virStorageFileBackend { virStorageFileBackendInit backendInit; virStorageFileBackendDeinit backendDeinit; virStorageFileBackendReadHeader storageFileReadHeader; + virStorageFileBackendGetUniqueIdentifier storageFileGetUniqueIdentifier; /* The following group of callbacks is expected to set errno * and return -1 on error. No libvirt error shall be reported */ diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 33551e7..1f436fa 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -1339,6 +1339,14 @@ virStorageBackend virStorageBackendNetFileSystem = { }; +typedef struct _virStorageFileBackendFsPriv virStorageFileBackendFsPriv; +typedef virStorageFileBackendFsPriv *virStorageFileBackendFsPrivPtr; + +struct _virStorageFileBackendFsPriv { + char *uid; /* unique file identifier (canonical path) */ +}; + + static void virStorageFileBackendFileDeinit(virStorageSourcePtr src) { @@ -1346,16 +1354,27 @@ virStorageFileBackendFileDeinit(virStorageSourcePtr src) virStorageTypeToString(virStorageSourceGetActualType(src)), src->path); + virStorageFileBackendFsPrivPtr priv = src->drv->priv; + + VIR_FREE(priv->uid); + VIR_FREE(priv); } static int virStorageFileBackendFileInit(virStorageSourcePtr src) { + virStorageFileBackendFsPrivPtr priv = NULL; + VIR_DEBUG("initializing FS storage file %p (%s:%s)", src, virStorageTypeToString(virStorageSourceGetActualType(src)), src->path); + if (VIR_ALLOC(priv) < 0) + return -1; + + src->drv->priv = priv; + return 0; } @@ -1403,6 +1422,23 @@ virStorageFileBackendFileReadHeader(virStorageSourcePtr src, } +static const char * +virStorageFileBackendFileGetUniqueIdentifier(virStorageSourcePtr src) +{ + virStorageFileBackendFsPrivPtr priv = src->drv->priv; + + if (!priv->uid) { + if (!(priv->uid = canonicalize_file_name(src->path))) { + virReportSystemError(errno, _("can't canonicalize path '%s'"), + src->path); + return NULL; + } + } + + return priv->uid; +} + + virStorageFileBackend virStorageFileBackendFile = { .type = VIR_STORAGE_TYPE_FILE, @@ -1412,6 +1448,8 @@ virStorageFileBackend virStorageFileBackendFile = { .storageFileUnlink = virStorageFileBackendFileUnlink, .storageFileStat = virStorageFileBackendFileStat, .storageFileReadHeader = virStorageFileBackendFileReadHeader, + + .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier, }; @@ -1423,7 +1461,18 @@ virStorageFileBackend virStorageFileBackendBlock = { .storageFileStat = virStorageFileBackendFileStat, .storageFileReadHeader = virStorageFileBackendFileReadHeader, + + .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier, }; +virStorageFileBackend virStorageFileBackendDir = { + .type = VIR_STORAGE_TYPE_DIR, + + .backendInit = virStorageFileBackendFileInit, + .backendDeinit = virStorageFileBackendFileDeinit, + + .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier, +}; + #endif /* WITH_STORAGE_FS */ diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 8da8386..2fb70f2 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -2960,3 +2960,33 @@ virStorageFileReadHeader(virStorageSourcePtr src, return ret; } + + +/* + * virStorageFileGetUniqueIdentifier: Get a unique string describing the volume + * + * @src: file structure pointing to the file + * + * Returns a string uniquely describing a single volume (canonical path). + * The string shall not be freed. Returns NULL on error and sets a libvirt + * error code */ +const char * +virStorageFileGetUniqueIdentifier(virStorageSourcePtr src) +{ + if (!virStorageFileIsInitialized(src)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("storage file backend not initialized")); + return NULL; + } + + if (!src->drv->backend->storageFileGetUniqueIdentifier) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unique storage file identifier not implemented for " + "storage type %s (protocol: %s)'"), + virStorageTypeToString(src->type), + virStorageNetProtocolTypeToString(src->protocol)); + return NULL; + } + + return src->drv->backend->storageFileGetUniqueIdentifier(src); +} diff --git a/src/storage/storage_driver.h b/src/storage/storage_driver.h index d67d74b..9280ef0 100644 --- a/src/storage/storage_driver.h +++ b/src/storage/storage_driver.h @@ -41,6 +41,7 @@ int virStorageFileStat(virStorageSourcePtr src, ssize_t virStorageFileReadHeader(virStorageSourcePtr src, ssize_t max_len, char **buf); +const char *virStorageFileGetUniqueIdentifier(virStorageSourcePtr src); int storageRegister(void); -- 1.9.2

Add a storage driver API equivalent ot the access() function. Implementations for the filesystem and gluster backends are provided. --- src/storage/storage_backend.h | 5 +++++ src/storage/storage_backend_fs.c | 13 +++++++++++++ src/storage/storage_backend_gluster.c | 11 +++++++++++ src/storage/storage_driver.c | 24 ++++++++++++++++++++++++ src/storage/storage_driver.h | 1 + 5 files changed, 54 insertions(+) diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index 5d71cde..56643fb 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -198,6 +198,10 @@ typedef ssize_t typedef const char * (*virStorageFileBackendGetUniqueIdentifier)(virStorageSourcePtr src); +typedef int +(*virStorageFileBackendAccess)(virStorageSourcePtr src, + int mode); + virStorageFileBackendPtr virStorageFileBackendForType(int type, int protocol); @@ -220,6 +224,7 @@ struct _virStorageFileBackend { virStorageFileBackendCreate storageFileCreate; virStorageFileBackendUnlink storageFileUnlink; virStorageFileBackendStat storageFileStat; + virStorageFileBackendAccess storageFileAccess; }; #endif /* __VIR_STORAGE_BACKEND_H__ */ diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 1f436fa..85264a7 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -1439,6 +1439,15 @@ virStorageFileBackendFileGetUniqueIdentifier(virStorageSourcePtr src) } +static int +virStorageFileBackendFileAccess(virStorageSourcePtr src, + int mode) +{ + return virFileAccessibleAs(src->path, mode, + src->drv->uid, src->drv->gid); +} + + virStorageFileBackend virStorageFileBackendFile = { .type = VIR_STORAGE_TYPE_FILE, @@ -1448,6 +1457,7 @@ virStorageFileBackend virStorageFileBackendFile = { .storageFileUnlink = virStorageFileBackendFileUnlink, .storageFileStat = virStorageFileBackendFileStat, .storageFileReadHeader = virStorageFileBackendFileReadHeader, + .storageFileAccess = virStorageFileBackendFileAccess, .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier, }; @@ -1461,6 +1471,7 @@ virStorageFileBackend virStorageFileBackendBlock = { .storageFileStat = virStorageFileBackendFileStat, .storageFileReadHeader = virStorageFileBackendFileReadHeader, + .storageFileAccess = virStorageFileBackendFileAccess, .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier, }; @@ -1472,6 +1483,8 @@ virStorageFileBackend virStorageFileBackendDir = { .backendInit = virStorageFileBackendFileInit, .backendDeinit = virStorageFileBackendFileDeinit, + .storageFileAccess = virStorageFileBackendFileAccess, + .storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier, }; diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c index badffae..1a844c9 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -703,6 +703,16 @@ virStorageFileBackendGlusterReadHeader(virStorageSourcePtr src, } +static int +virStorageFileBackendGlusterAccess(virStorageSourcePtr src, + int mode) +{ + virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + + return glfs_access(priv->vol, src->path, mode); +} + + virStorageFileBackend virStorageFileBackendGluster = { .type = VIR_STORAGE_TYPE_NETWORK, .protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER, @@ -713,4 +723,5 @@ virStorageFileBackend virStorageFileBackendGluster = { .storageFileUnlink = virStorageFileBackendGlusterUnlink, .storageFileStat = virStorageFileBackendGlusterStat, .storageFileReadHeader = virStorageFileBackendGlusterReadHeader, + .storageFileAccess = virStorageFileBackendGlusterAccess, }; diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 2fb70f2..dd58400 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -2990,3 +2990,27 @@ virStorageFileGetUniqueIdentifier(virStorageSourcePtr src) return src->drv->backend->storageFileGetUniqueIdentifier(src); } + + +/** + * virStorageFileAccess: Check accessability of a storage file + * + * @src: storage file to check access permissions + * @mode: accessibility check options (see man 2 access) + * + * Returns 0 on success, -1 on error and sets errno. No libvirt + * error is reported. Returns -2 if the operation isn't supported + * by libvirt storage backend. + */ +int +virStorageFileAccess(virStorageSourcePtr src, + int mode) +{ + if (!virStorageFileIsInitialized(src) || + !src->drv->backend->storageFileAccess) { + errno = ENOSYS; + return -2; + } + + return src->drv->backend->storageFileAccess(src, mode); +} diff --git a/src/storage/storage_driver.h b/src/storage/storage_driver.h index 9280ef0..5452df2 100644 --- a/src/storage/storage_driver.h +++ b/src/storage/storage_driver.h @@ -42,6 +42,7 @@ ssize_t virStorageFileReadHeader(virStorageSourcePtr src, ssize_t max_len, char **buf); const char *virStorageFileGetUniqueIdentifier(virStorageSourcePtr src); +int virStorageFileAccess(virStorageSourcePtr src, int mode); int storageRegister(void); -- 1.9.2

My future work will modify the metadata crawler function to use the storage driver file APIs to access the files instead of accessing them directly so that we will be able to request the metadata for remote files too. To avoid linking the storage driver to every helper file using the utils code, the backing chain traversal function needs to be moved to the storage driver source. Additionally the virt-aa-helper and virstoragetest programs need to be linked with the storage driver as a result of this change. --- cfg.mk | 2 +- src/Makefile.am | 2 + src/libvirt_private.syms | 2 +- src/qemu/qemu_domain.c | 2 + src/security/virt-aa-helper.c | 2 + src/storage/storage_driver.c | 233 ++++++++++++++++++++++++++++++++++++++++++ src/storage/storage_driver.h | 5 + src/util/virstoragefile.c | 233 +----------------------------------------- src/util/virstoragefile.h | 7 +- tests/Makefile.am | 7 +- tests/virstoragetest.c | 2 + 11 files changed, 258 insertions(+), 239 deletions(-) diff --git a/cfg.mk b/cfg.mk index 3f4bba0..cfe5fcc 100644 --- a/cfg.mk +++ b/cfg.mk @@ -774,7 +774,7 @@ sc_prohibit_cross_inclusion: access/ | conf/) safe="($$dir|conf|util)";; \ locking/) safe="($$dir|util|conf|rpc)";; \ cpu/| network/| node_device/| rpc/| security/| storage/) \ - safe="($$dir|util|conf)";; \ + safe="($$dir|util|conf|storage)";; \ xenapi/ | xenxs/ ) safe="($$dir|util|conf|xen)";; \ *) safe="($$dir|$(mid_dirs)|util)";; \ esac; \ diff --git a/src/Makefile.am b/src/Makefile.am index e9dc9e0..072e761 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2564,8 +2564,10 @@ virt_aa_helper_LDFLAGS = \ $(PIE_LDFLAGS) \ $(NULL) virt_aa_helper_LDADD = \ + libvirt.la \ libvirt_conf.la \ libvirt_util.la \ + libvirt_driver_storage_impl.la \ ../gnulib/lib/libgnu.la if WITH_DTRACE_PROBES virt_aa_helper_LDADD += libvirt_probes.lo diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index afda71f..78221aa 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1847,9 +1847,9 @@ virStorageFileFeatureTypeToString; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; virStorageFileGetLVMKey; -virStorageFileGetMetadata; virStorageFileGetMetadataFromBuf; virStorageFileGetMetadataFromFD; +virStorageFileGetMetadataFromFDInternal; virStorageFileGetSCSIKey; virStorageFileIsClusterFS; virStorageFileParseChainIndex; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 1301f64..7ea9032 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -39,6 +39,8 @@ #include "virstoragefile.h" #include "virstring.h" +#include "storage/storage_driver.h" + #include <sys/time.h> #include <fcntl.h> diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 32fc04a..bf540b4 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -55,6 +55,8 @@ #include "virrandom.h" #include "virstring.h" +#include "storage/storage_driver.h" + #define VIR_FROM_THIS VIR_FROM_SECURITY static char *progname; diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index dd58400..25772e8 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -49,6 +49,7 @@ #include "configmake.h" #include "virstring.h" #include "viraccessapicheck.h" +#include "dirname.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -3014,3 +3015,235 @@ virStorageFileAccess(virStorageSourcePtr src, return src->drv->backend->storageFileAccess(src, mode); } + + +/** + * Given a starting point START (a directory containing the original + * file, if the original file was opened via a relative path; ignored + * if NAME is absolute), determine the location of the backing file + * NAME (possibly relative), and compute the relative DIRECTORY + * (optional) and CANONICAL (mandatory) location of the backing file. + * Return 0 on success, negative on error. + */ +static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) +virFindBackingFile(const char *start, const char *path, + char **directory, char **canonical) +{ + /* FIXME - when we eventually allow non-raw network devices, we + * must ensure that we handle backing files the same way as qemu. + * For a qcow2 top file of gluster://server/vol/img, qemu treats + * the relative backing file 'rel' as meaning + * 'gluster://server/vol/rel', while the backing file '/abs' is + * used as a local file. But we cannot canonicalize network + * devices via canonicalize_file_name(), because they are not part + * of the local file system. */ + char *combined = NULL; + int ret = -1; + + if (*path == '/') { + /* Safe to cast away const */ + combined = (char *)path; + } else if (virAsprintf(&combined, "%s/%s", start, path) < 0) { + goto cleanup; + } + + if (directory && !(*directory = mdir_name(combined))) { + virReportOOMError(); + goto cleanup; + } + + if (virFileAccessibleAs(combined, F_OK, geteuid(), getegid()) < 0) { + virReportSystemError(errno, + _("Cannot access backing file '%s'"), + combined); + goto cleanup; + } + + if (!(*canonical = canonicalize_file_name(combined))) { + virReportSystemError(errno, + _("Can't canonicalize path '%s'"), path); + goto cleanup; + } + + ret = 0; + + cleanup: + if (combined != path) + VIR_FREE(combined); + return ret; +} + + +/* Recursive workhorse for virStorageFileGetMetadata. */ +static int +virStorageFileGetMetadataRecurse(virStorageSourcePtr src, + const char *canonPath, + uid_t uid, gid_t gid, + bool allow_probe, + virHashTablePtr cycle) +{ + int fd; + int ret = -1; + virStorageSourcePtr backingStore = NULL; + int backingFormat; + + VIR_DEBUG("path=%s canonPath=%s dir=%s format=%d uid=%d gid=%d probe=%d", + src->path, canonPath, NULLSTR(src->relDir), src->format, + (int)uid, (int)gid, allow_probe); + + if (virHashLookup(cycle, canonPath)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store for %s is self-referential"), + src->path); + return -1; + } + + if (virHashAddEntry(cycle, canonPath, (void *)1) < 0) + return -1; + + if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) { + if ((fd = virFileOpenAs(src->path, O_RDONLY, 0, uid, gid, 0)) < 0) { + virReportSystemError(-fd, _("Failed to open file '%s'"), + src->path); + return -1; + } + + if (virStorageFileGetMetadataFromFDInternal(src, fd, + &backingFormat) < 0) { + VIR_FORCE_CLOSE(fd); + return -1; + } + + if (VIR_CLOSE(fd) < 0) + VIR_WARN("could not close file %s", src->path); + } else { + /* TODO: currently we call this only for local storage */ + return 0; + } + + /* check whether we need to go deeper */ + if (!src->backingStoreRaw) + return 0; + + if (VIR_ALLOC(backingStore) < 0) + return -1; + + if (VIR_STRDUP(backingStore->relPath, src->backingStoreRaw) < 0) + goto cleanup; + + if (virStorageIsFile(src->backingStoreRaw)) { + backingStore->type = VIR_STORAGE_TYPE_FILE; + + if (virFindBackingFile(src->relDir, + src->backingStoreRaw, + &backingStore->relDir, + &backingStore->path) < 0) { + /* the backing file is (currently) unavailable, treat this + * file as standalone: + * backingStoreRaw is kept to mark broken image chains */ + VIR_WARN("Backing file '%s' of image '%s' is missing.", + src->backingStoreRaw, src->path); + ret = 0; + goto cleanup; + } + } else { + /* TODO: To satisfy the test case, copy the network URI as path. This + * will be removed later. */ + backingStore->type = VIR_STORAGE_TYPE_NETWORK; + + if (VIR_STRDUP(backingStore->path, src->backingStoreRaw) < 0) + goto cleanup; + } + + if (backingFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) + backingStore->format = VIR_STORAGE_FILE_RAW; + else if (backingFormat == VIR_STORAGE_FILE_AUTO_SAFE) + backingStore->format = VIR_STORAGE_FILE_AUTO; + else + backingStore->format = backingFormat; + + if (virStorageFileGetMetadataRecurse(backingStore, + backingStore->path, + uid, gid, allow_probe, + cycle) < 0) { + /* if we fail somewhere midway, just accept and return a + * broken chain */ + ret = 0; + goto cleanup; + } + + src->backingStore = backingStore; + backingStore = NULL; + ret = 0; + + cleanup: + virStorageSourceFree(backingStore); + return ret; +} + + +/** + * virStorageFileGetMetadata: + * + * Extract metadata about the storage volume with the specified + * image format. If image format is VIR_STORAGE_FILE_AUTO, it + * will probe to automatically identify the format. Recurses through + * the entire chain. + * + * Open files using UID and GID (or pass -1 for the current user/group). + * Treat any backing files without explicit type as raw, unless ALLOW_PROBE. + * + * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a + * format, since a malicious guest can turn a raw file into any + * other non-raw format at will. + * + * Caller MUST free result after use via virStorageSourceFree. + */ +int +virStorageFileGetMetadata(virStorageSourcePtr src, + uid_t uid, gid_t gid, + bool allow_probe) +{ + VIR_DEBUG("path=%s format=%d uid=%d gid=%d probe=%d", + src->path, src->format, (int)uid, (int)gid, allow_probe); + + virHashTablePtr cycle = NULL; + char *canonPath = NULL; + int ret = -1; + + if (!(cycle = virHashCreate(5, NULL))) + return -1; + + if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) { + if (!(canonPath = canonicalize_file_name(src->path))) { + virReportSystemError(errno, _("unable to resolve '%s'"), + src->path); + goto cleanup; + } + + if (!src->relPath && + VIR_STRDUP(src->relPath, src->path) < 0) + goto cleanup; + + if (!src->relDir && + !(src->relDir = mdir_name(src->path))) { + virReportOOMError(); + goto cleanup; + } + } else { + /* TODO: currently unimplemented for non-local storage */ + ret = 0; + goto cleanup; + } + + if (src->format <= VIR_STORAGE_FILE_NONE) + src->format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW; + + ret = virStorageFileGetMetadataRecurse(src, canonPath, uid, gid, + allow_probe, cycle); + + cleanup: + VIR_FREE(canonPath); + virHashFree(cycle); + return ret; +} diff --git a/src/storage/storage_driver.h b/src/storage/storage_driver.h index 5452df2..bd9e9ed 100644 --- a/src/storage/storage_driver.h +++ b/src/storage/storage_driver.h @@ -44,6 +44,11 @@ ssize_t virStorageFileReadHeader(virStorageSourcePtr src, const char *virStorageFileGetUniqueIdentifier(virStorageSourcePtr src); int virStorageFileAccess(virStorageSourcePtr src, int mode); +int virStorageFileGetMetadata(virStorageSourcePtr src, + uid_t uid, gid_t gid, + bool allow_probe) + ATTRIBUTE_NONNULL(1); + int storageRegister(void); #endif /* __VIR_STORAGE_DRIVER_H__ */ diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 73cef3c..347c2c3 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -28,7 +28,6 @@ #include <unistd.h> #include <fcntl.h> #include <stdlib.h> -#include "dirname.h" #include "viralloc.h" #include "virerror.h" #include "virlog.h" @@ -563,62 +562,6 @@ qedGetBackingStore(char **res, return BACKING_STORE_OK; } -/** - * Given a starting point START (a directory containing the original - * file, if the original file was opened via a relative path; ignored - * if NAME is absolute), determine the location of the backing file - * NAME (possibly relative), and compute the relative DIRECTORY - * (optional) and CANONICAL (mandatory) location of the backing file. - * Return 0 on success, negative on error. - */ -static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) -virFindBackingFile(const char *start, const char *path, - char **directory, char **canonical) -{ - /* FIXME - when we eventually allow non-raw network devices, we - * must ensure that we handle backing files the same way as qemu. - * For a qcow2 top file of gluster://server/vol/img, qemu treats - * the relative backing file 'rel' as meaning - * 'gluster://server/vol/rel', while the backing file '/abs' is - * used as a local file. But we cannot canonicalize network - * devices via canonicalize_file_name(), because they are not part - * of the local file system. */ - char *combined = NULL; - int ret = -1; - - if (*path == '/') { - /* Safe to cast away const */ - combined = (char *)path; - } else if (virAsprintf(&combined, "%s/%s", start, path) < 0) { - goto cleanup; - } - - if (directory && !(*directory = mdir_name(combined))) { - virReportOOMError(); - goto cleanup; - } - - if (virFileAccessibleAs(combined, F_OK, geteuid(), getegid()) < 0) { - virReportSystemError(errno, - _("Cannot access backing file '%s'"), - combined); - goto cleanup; - } - - if (!(*canonical = canonicalize_file_name(combined))) { - virReportSystemError(errno, - _("Can't canonicalize path '%s'"), path); - goto cleanup; - } - - ret = 0; - - cleanup: - if (combined != path) - VIR_FREE(combined); - return ret; -} - static bool virStorageFileMatchesMagic(int format, @@ -1017,7 +960,7 @@ virStorageFileGetMetadataFromBuf(const char *path, /* Internal version that also supports a containing directory name. */ -static int +int virStorageFileGetMetadataFromFDInternal(virStorageSourcePtr meta, int fd, int *backingFormat) @@ -1106,180 +1049,6 @@ virStorageFileGetMetadataFromFD(const char *path, } -/* Recursive workhorse for virStorageFileGetMetadata. */ -static int -virStorageFileGetMetadataRecurse(virStorageSourcePtr src, - const char *canonPath, - uid_t uid, gid_t gid, - bool allow_probe, - virHashTablePtr cycle) -{ - int fd; - int ret = -1; - virStorageSourcePtr backingStore = NULL; - int backingFormat; - - VIR_DEBUG("path=%s canonPath=%s dir=%s format=%d uid=%d gid=%d probe=%d", - src->path, canonPath, NULLSTR(src->relDir), src->format, - (int)uid, (int)gid, allow_probe); - - if (virHashLookup(cycle, canonPath)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store for %s is self-referential"), - src->path); - return -1; - } - - if (virHashAddEntry(cycle, canonPath, (void *)1) < 0) - return -1; - - if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) { - if ((fd = virFileOpenAs(src->path, O_RDONLY, 0, uid, gid, 0)) < 0) { - virReportSystemError(-fd, _("Failed to open file '%s'"), - src->path); - return -1; - } - - if (virStorageFileGetMetadataFromFDInternal(src, fd, - &backingFormat) < 0) { - VIR_FORCE_CLOSE(fd); - return -1; - } - - if (VIR_CLOSE(fd) < 0) - VIR_WARN("could not close file %s", src->path); - } else { - /* TODO: currently we call this only for local storage */ - return 0; - } - - /* check whether we need to go deeper */ - if (!src->backingStoreRaw) - return 0; - - if (VIR_ALLOC(backingStore) < 0) - return -1; - - if (VIR_STRDUP(backingStore->relPath, src->backingStoreRaw) < 0) - goto cleanup; - - if (virStorageIsFile(src->backingStoreRaw)) { - backingStore->type = VIR_STORAGE_TYPE_FILE; - - if (virFindBackingFile(src->relDir, - src->backingStoreRaw, - &backingStore->relDir, - &backingStore->path) < 0) { - /* the backing file is (currently) unavailable, treat this - * file as standalone: - * backingStoreRaw is kept to mark broken image chains */ - VIR_WARN("Backing file '%s' of image '%s' is missing.", - src->backingStoreRaw, src->path); - ret = 0; - goto cleanup; - } - } else { - /* TODO: To satisfy the test case, copy the network URI as path. This - * will be removed later. */ - backingStore->type = VIR_STORAGE_TYPE_NETWORK; - - if (VIR_STRDUP(backingStore->path, src->backingStoreRaw) < 0) - goto cleanup; - } - - if (backingFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) - backingStore->format = VIR_STORAGE_FILE_RAW; - else if (backingFormat == VIR_STORAGE_FILE_AUTO_SAFE) - backingStore->format = VIR_STORAGE_FILE_AUTO; - else - backingStore->format = backingFormat; - - if (virStorageFileGetMetadataRecurse(backingStore, - backingStore->path, - uid, gid, allow_probe, - cycle) < 0) { - /* if we fail somewhere midway, just accept and return a - * broken chain */ - ret = 0; - goto cleanup; - } - - src->backingStore = backingStore; - backingStore = NULL; - ret = 0; - - cleanup: - virStorageSourceFree(backingStore); - return ret; -} - - -/** - * virStorageFileGetMetadata: - * - * Extract metadata about the storage volume with the specified - * image format. If image format is VIR_STORAGE_FILE_AUTO, it - * will probe to automatically identify the format. Recurses through - * the entire chain. - * - * Open files using UID and GID (or pass -1 for the current user/group). - * Treat any backing files without explicit type as raw, unless ALLOW_PROBE. - * - * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a - * format, since a malicious guest can turn a raw file into any - * other non-raw format at will. - * - * Caller MUST free result after use via virStorageSourceFree. - */ -int -virStorageFileGetMetadata(virStorageSourcePtr src, - uid_t uid, gid_t gid, - bool allow_probe) -{ - VIR_DEBUG("path=%s format=%d uid=%d gid=%d probe=%d", - src->path, src->format, (int)uid, (int)gid, allow_probe); - - virHashTablePtr cycle = NULL; - char *canonPath = NULL; - int ret = -1; - - if (!(cycle = virHashCreate(5, NULL))) - return -1; - - if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) { - if (!(canonPath = canonicalize_file_name(src->path))) { - virReportSystemError(errno, _("unable to resolve '%s'"), - src->path); - goto cleanup; - } - - if (!src->relPath && - VIR_STRDUP(src->relPath, src->path) < 0) - goto cleanup; - - if (!src->relDir && - !(src->relDir = mdir_name(src->path))) { - virReportOOMError(); - goto cleanup; - } - } else { - /* TODO: currently unimplemented for non-local storage */ - ret = 0; - goto cleanup; - } - - if (src->format <= VIR_STORAGE_FILE_NONE) - src->format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW; - - ret = virStorageFileGetMetadataRecurse(src, canonPath, uid, gid, - allow_probe, cycle); - - cleanup: - VIR_FREE(canonPath); - virHashFree(cycle); - return ret; -} - /** * virStorageFileChainCheckBroken * diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 95f2e45..ae27936 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -263,10 +263,9 @@ struct _virStorageSource { int virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid); -int virStorageFileGetMetadata(virStorageSourcePtr src, - uid_t uid, gid_t gid, - bool allow_probe) - ATTRIBUTE_NONNULL(1); +int virStorageFileGetMetadataFromFDInternal(virStorageSourcePtr meta, + int fd, + int *backingFormat); virStorageSourcePtr virStorageFileGetMetadataFromFD(const char *path, int fd, int format); diff --git a/tests/Makefile.am b/tests/Makefile.am index 5ef8940..4d24822 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -869,7 +869,12 @@ virstringtest_LDADD = $(LDADDS) virstoragetest_SOURCES = \ virstoragetest.c testutils.h testutils.c -virstoragetest_LDADD = $(LDADDS) +virstoragetest_LDADD = $(LDADDS) \ + ../src/libvirt.la \ + ../src/libvirt_conf.la \ + ../src/libvirt_util.la \ + ../src/libvirt_driver_storage_impl.la \ + ../gnulib/lib/libgnu.la viridentitytest_SOURCES = \ viridentitytest.c testutils.h testutils.c diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index d49098e..fb96c71 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -31,6 +31,8 @@ #include "virstring.h" #include "dirname.h" +#include "storage/storage_driver.h" + #define VIR_FROM_THIS VIR_FROM_NONE VIR_LOG_INIT("tests.storagetest"); -- 1.9.2

When walking the backing chain we previously set the storage type to _FILE and let the virStorageFileGetMetadataFromFDInternal update it to the correct type later on. This patch moves the actual storage type determination to the place where we parse the backing store name so that the code can later be switched to use virStorageFileReadHeader() directly. --- src/storage/storage_driver.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 25772e8..163b402 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -3084,6 +3084,7 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, { int fd; int ret = -1; + struct stat st; virStorageSourcePtr backingStore = NULL; int backingFormat; @@ -3146,6 +3147,16 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, ret = 0; goto cleanup; } + + /* update the type for local storage */ + if (stat(backingStore->path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + backingStore->type = VIR_STORAGE_TYPE_DIR; + backingStore->format = VIR_STORAGE_FILE_DIR; + } else if (S_ISBLK(st.st_mode)) { + backingStore->type = VIR_STORAGE_TYPE_BLOCK; + } + } } else { /* TODO: To satisfy the test case, copy the network URI as path. This * will be removed later. */ -- 1.9.2

Stat the path of the storage file being tested to set the correct type into the virStorageSource. This will avoid breaking the test suite when inquiring metadata of directory paths in the next patches. --- tests/virstoragetest.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index fb96c71..746bf63 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -98,6 +98,7 @@ testStorageFileGetMetadata(const char *path, uid_t uid, gid_t gid, bool allow_probe) { + struct stat st; virStorageSourcePtr ret = NULL; if (VIR_ALLOC(ret) < 0) @@ -106,6 +107,15 @@ testStorageFileGetMetadata(const char *path, ret->type = VIR_STORAGE_TYPE_FILE; ret->format = format; + if (stat(path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + ret->type = VIR_STORAGE_TYPE_DIR; + ret->format = VIR_STORAGE_FILE_DIR; + } else if (S_ISBLK(st.st_mode)) { + ret->type = VIR_STORAGE_TYPE_BLOCK; + } + } + if (VIR_STRDUP(ret->relPath, path) < 0) goto error; -- 1.9.2

Add a new function wrapper and tweak the storage file backend lookup function so that it can be used without reporting error. This will be useful in the metadata crawler code where we need silently break if metadata retrieval is not supported for the current storage type. --- src/storage/storage_backend.c | 16 ++++++++++++++-- src/storage/storage_backend.h | 4 +++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index e016cc8..380ca58 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -1173,8 +1173,9 @@ virStorageBackendForType(int type) virStorageFileBackendPtr -virStorageFileBackendForType(int type, - int protocol) +virStorageFileBackendForTypeInternal(int type, + int protocol, + bool report) { size_t i; @@ -1188,6 +1189,9 @@ virStorageFileBackendForType(int type, } } + if (!report) + return NULL; + if (type == VIR_STORAGE_TYPE_NETWORK) { virReportError(VIR_ERR_INTERNAL_ERROR, _("missing storage backend for network files " @@ -1203,6 +1207,14 @@ virStorageFileBackendForType(int type, } +virStorageFileBackendPtr +virStorageFileBackendForType(int type, + int protocol) +{ + return virStorageFileBackendForTypeInternal(type, protocol, true); +} + + struct diskType { int part_table_type; unsigned short offset; diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index 56643fb..76c1afa 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -203,7 +203,9 @@ typedef int int mode); virStorageFileBackendPtr virStorageFileBackendForType(int type, int protocol); - +virStorageFileBackendPtr virStorageFileBackendForTypeInternal(int type, + int protocol, + bool report); struct _virStorageFileBackend { -- 1.9.2

Use the virStorageFileGetUniqueIdentifier() function to get a unique identifier regardless of the target storage type instead of relying on canonicalize_path(). A new function that checks wether we support a given image is introduced to avoid errors for unimplemented backends. --- src/storage/storage_driver.c | 77 +++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 163b402..cdf01e7 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -2761,6 +2761,30 @@ virStorageFileIsInitialized(virStorageSourcePtr src) return !!src->drv; } + +static bool +virStorageFileSupportsBackingChainTraversal(virStorageSourcePtr src) +{ + int actualType = virStorageSourceGetActualType(src); + virStorageFileBackendPtr backend; + + if (!src) + return false; + + if (src->drv) { + backend = src->drv->backend; + } else { + if (!(backend = virStorageFileBackendForTypeInternal(actualType, + src->protocol, + false))) + return false; + } + + return backend->storageFileGetUniqueIdentifier && + backend->storageFileReadHeader && + backend->storageFileAccess; +} + void virStorageFileDeinit(virStorageSourcePtr src) { @@ -3077,7 +3101,6 @@ virFindBackingFile(const char *start, const char *path, /* Recursive workhorse for virStorageFileGetMetadata. */ static int virStorageFileGetMetadataRecurse(virStorageSourcePtr src, - const char *canonPath, uid_t uid, gid_t gid, bool allow_probe, virHashTablePtr cycle) @@ -3085,49 +3108,63 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, int fd; int ret = -1; struct stat st; + const char *uniqueName; virStorageSourcePtr backingStore = NULL; int backingFormat; - VIR_DEBUG("path=%s canonPath=%s dir=%s format=%d uid=%d gid=%d probe=%d", - src->path, canonPath, NULLSTR(src->relDir), src->format, + VIR_DEBUG("path=%s dir=%s format=%d uid=%d gid=%d probe=%d", + src->path, NULLSTR(src->relDir), src->format, (int)uid, (int)gid, allow_probe); - if (virHashLookup(cycle, canonPath)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store for %s is self-referential"), - src->path); + /* exit if we can't load information about the current image */ + if (!virStorageFileSupportsBackingChainTraversal(src)) + return 0; + + if (virStorageFileInitAs(src, uid, gid) < 0) return -1; + + if (!(uniqueName = virStorageFileGetUniqueIdentifier(src))) + goto cleanup; + + if (virHashLookup(cycle, uniqueName)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store for %s (%s) is self-referential"), + src->path, uniqueName); + goto cleanup; } - if (virHashAddEntry(cycle, canonPath, (void *)1) < 0) - return -1; + if (virHashAddEntry(cycle, uniqueName, (void *)1) < 0) + goto cleanup; if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) { if ((fd = virFileOpenAs(src->path, O_RDONLY, 0, uid, gid, 0)) < 0) { virReportSystemError(-fd, _("Failed to open file '%s'"), src->path); - return -1; + goto cleanup; } if (virStorageFileGetMetadataFromFDInternal(src, fd, &backingFormat) < 0) { VIR_FORCE_CLOSE(fd); - return -1; + goto cleanup; } if (VIR_CLOSE(fd) < 0) VIR_WARN("could not close file %s", src->path); } else { /* TODO: currently we call this only for local storage */ - return 0; + ret = 0; + goto cleanup; } /* check whether we need to go deeper */ - if (!src->backingStoreRaw) - return 0; + if (!src->backingStoreRaw) { + ret = 0; + goto cleanup; + } if (VIR_ALLOC(backingStore) < 0) - return -1; + goto cleanup; if (VIR_STRDUP(backingStore->relPath, src->backingStoreRaw) < 0) goto cleanup; @@ -3174,7 +3211,6 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, backingStore->format = backingFormat; if (virStorageFileGetMetadataRecurse(backingStore, - backingStore->path, uid, gid, allow_probe, cycle) < 0) { /* if we fail somewhere midway, just accept and return a @@ -3188,6 +3224,7 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, ret = 0; cleanup: + virStorageFileDeinit(src); virStorageSourceFree(backingStore); return ret; } @@ -3226,12 +3263,6 @@ virStorageFileGetMetadata(virStorageSourcePtr src, return -1; if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) { - if (!(canonPath = canonicalize_file_name(src->path))) { - virReportSystemError(errno, _("unable to resolve '%s'"), - src->path); - goto cleanup; - } - if (!src->relPath && VIR_STRDUP(src->relPath, src->path) < 0) goto cleanup; @@ -3250,7 +3281,7 @@ virStorageFileGetMetadata(virStorageSourcePtr src, if (src->format <= VIR_STORAGE_FILE_NONE) src->format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW; - ret = virStorageFileGetMetadataRecurse(src, canonPath, uid, gid, + ret = virStorageFileGetMetadataRecurse(src, uid, gid, allow_probe, cycle); cleanup: -- 1.9.2

Use virStorageFileReadHeader() to read headers of storage files possibly on remote storate to retrieve the image metadata. The backend information is now parsed by virStorageFileGetMetadataInternal which is now exported from the util source and virStorageFileGetMetadataFromFDInternal now doesn't need to be exported. --- src/libvirt_private.syms | 2 +- src/storage/storage_driver.c | 26 +++++++------------------- src/util/virstoragefile.c | 5 ++--- src/util/virstoragefile.h | 9 ++++++--- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 78221aa..90bd48c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1849,7 +1849,7 @@ virStorageFileFormatTypeToString; virStorageFileGetLVMKey; virStorageFileGetMetadataFromBuf; virStorageFileGetMetadataFromFD; -virStorageFileGetMetadataFromFDInternal; +virStorageFileGetMetadataInternal; virStorageFileGetSCSIKey; virStorageFileIsClusterFS; virStorageFileParseChainIndex; diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index cdf01e7..f2c548d 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -3105,10 +3105,11 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, bool allow_probe, virHashTablePtr cycle) { - int fd; int ret = -1; struct stat st; const char *uniqueName; + char *buf = NULL; + ssize_t headerLen; virStorageSourcePtr backingStore = NULL; int backingFormat; @@ -3136,26 +3137,13 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, if (virHashAddEntry(cycle, uniqueName, (void *)1) < 0) goto cleanup; - if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) { - if ((fd = virFileOpenAs(src->path, O_RDONLY, 0, uid, gid, 0)) < 0) { - virReportSystemError(-fd, _("Failed to open file '%s'"), - src->path); - goto cleanup; - } - - if (virStorageFileGetMetadataFromFDInternal(src, fd, - &backingFormat) < 0) { - VIR_FORCE_CLOSE(fd); - goto cleanup; - } + if ((headerLen = virStorageFileReadHeader(src, VIR_STORAGE_MAX_HEADER, + &buf)) < 0) + goto cleanup; - if (VIR_CLOSE(fd) < 0) - VIR_WARN("could not close file %s", src->path); - } else { - /* TODO: currently we call this only for local storage */ - ret = 0; + if (virStorageFileGetMetadataInternal(src, buf, headerLen, + &backingFormat) < 0) goto cleanup; - } /* check whether we need to go deeper */ if (!src->backingStoreRaw) { diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 347c2c3..365320b 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -738,8 +738,7 @@ qcow2GetFeatures(virBitmapPtr *features, * with information about the file and its backing store. Return format * of the backing store as BACKING_FORMAT. PATH and FORMAT have to be * pre-populated in META */ -static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) -ATTRIBUTE_NONNULL(4) +int virStorageFileGetMetadataInternal(virStorageSourcePtr meta, char *buf, size_t len, @@ -960,7 +959,7 @@ virStorageFileGetMetadataFromBuf(const char *path, /* Internal version that also supports a containing directory name. */ -int +static int virStorageFileGetMetadataFromFDInternal(virStorageSourcePtr meta, int fd, int *backingFormat) diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index ae27936..936b0ac 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -263,9 +263,12 @@ struct _virStorageSource { int virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid); -int virStorageFileGetMetadataFromFDInternal(virStorageSourcePtr meta, - int fd, - int *backingFormat); +int virStorageFileGetMetadataInternal(virStorageSourcePtr meta, + char *buf, + size_t len, + int *backingFormat) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4); + virStorageSourcePtr virStorageFileGetMetadataFromFD(const char *path, int fd, int format); -- 1.9.2

Use virStorageFileAccess() to to check wether the file is accessible in the main part of the metadata crawler. --- src/storage/storage_driver.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index f2c548d..cf969c8 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -3076,13 +3076,6 @@ virFindBackingFile(const char *start, const char *path, goto cleanup; } - if (virFileAccessibleAs(combined, F_OK, geteuid(), getegid()) < 0) { - virReportSystemError(errno, - _("Cannot access backing file '%s'"), - combined); - goto cleanup; - } - if (!(*canonical = canonicalize_file_name(combined))) { virReportSystemError(errno, _("Can't canonicalize path '%s'"), path); @@ -3124,6 +3117,13 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, if (virStorageFileInitAs(src, uid, gid) < 0) return -1; + if (virStorageFileAccess(src, F_OK) < 0) { + virReportSystemError(errno, + _("Cannot access backing file %s"), + src->path); + goto cleanup; + } + if (!(uniqueName = virStorageFileGetUniqueIdentifier(src))) goto cleanup; -- 1.9.2

Add parsers for relative and absolute backing names for local and remote storage files. This parser parses relative paths as relative to their parents and absolute paths according to the protocol or local access. For remote storage volumes, all URI based backing file names are supported and for the qemu colon syntax the NBD protocol is supported. --- src/libvirt_private.syms | 1 + src/util/virstoragefile.c | 360 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virstoragefile.h | 4 + 3 files changed, 365 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 90bd48c..8342ec9 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1867,6 +1867,7 @@ virStorageSourceClear; virStorageSourceClearBackingStore; virStorageSourceFree; virStorageSourceGetActualType; +virStorageSourceNewFromBacking; virStorageSourcePoolDefFree; virStorageSourcePoolModeTypeFromString; virStorageSourcePoolModeTypeToString; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 365320b..0d155ca 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -38,6 +38,8 @@ #include "virendian.h" #include "virstring.h" #include "virutil.h" +#include "viruri.h" +#include "dirname.h" #if HAVE_SYS_SYSCALL_H # include <sys/syscall.h> #endif @@ -658,6 +660,19 @@ virStorageIsFile(const char *backing) } +static bool +virStorageIsRelative(const char *backing) +{ + if (backing[0] == '/') + return false; + + if (!virStorageIsFile(backing)) + return false; + + return true; +} + + static int virStorageFileProbeFormatFromBuf(const char *path, char *buf, @@ -1558,3 +1573,348 @@ virStorageSourceFree(virStorageSourcePtr def) virStorageSourceClear(def); VIR_FREE(def); } + + +static virStorageSourcePtr +virStorageSourceNewFromBackingRelative(virStorageSourcePtr parent, + const char *rel) +{ + char *dirname = NULL; + virStorageSourcePtr ret; + + if (VIR_ALLOC(ret) < 0) + return NULL; + + ret->backingRelative = true; + + /* XXX Once we get rid of the need to use canonical names in path, we will be + * able to use mdir_name on parent->path instead of using parent->relDir */ + if (virAsprintf(&ret->path, "%s/%s", parent->relDir, rel) < 0) + goto error; + + if (virStorageSourceGetActualType(parent) != VIR_STORAGE_TYPE_NETWORK) { + ret->type = VIR_STORAGE_TYPE_FILE; + + /* XXX store the relative directory name for test's sake */ + if (!(ret->relDir = mdir_name(ret->path))) { + virReportOOMError(); + goto error; + } + + /* XXX we don't currently need to store the canonical path but the + * change would break the test suite. Get rid of this later */ + char *tmp = ret->path; + if (!(ret->path = canonicalize_file_name(tmp))) { + ret->path = tmp; + tmp = NULL; + } + VIR_FREE(tmp); + } else { + ret->type = VIR_STORAGE_TYPE_NETWORK; + + /* copy the host network part */ + ret->protocol = parent->protocol; + if (!(ret->hosts = virStorageNetHostDefCopy(parent->nhosts, parent->hosts))) + goto error; + ret->nhosts = parent->nhosts; + + if (VIR_STRDUP(ret->volume, parent->volume) < 0) + goto error; + + /* XXX store the relative directory name for test's sake */ + if (!(ret->relDir = mdir_name(ret->path))) { + virReportOOMError(); + goto error; + } + } + + cleanup: + VIR_FREE(dirname); + return ret; + + error: + virStorageSourceFree(ret); + ret = NULL; + goto cleanup; +} + + +static int +virStorageSourceParseBackingURI(virStorageSourcePtr src, + const char *path) +{ + virURIPtr uri = NULL; + char **scheme = NULL; + int ret = -1; + + if (!(uri = virURIParse(path))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse backing file location '%s'"), + path); + goto cleanup; + } + + if (!(scheme = virStringSplit(uri->scheme, "+", 2))) + goto cleanup; + + if (!scheme[0] || + (src->protocol = virStorageNetProtocolTypeFromString(scheme[0])) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid backing protocol '%s'"), + NULLSTR(scheme[0])); + goto cleanup; + } + + if (scheme[1] && + (src->hosts->transport = virStorageNetHostTransportTypeFromString(scheme[1])) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid protocol transport type '%s'"), + scheme[1]); + goto cleanup; + } + + /* handle socket stored as a query */ + if (uri->query) { + if (VIR_STRDUP(src->hosts->socket, STRSKIP(uri->query, "socket=")) < 0) + goto cleanup; + } + + /* XXX We currently don't support auth, so don't bother parsing it */ + + /* possibly skip the leading slash */ + if (VIR_STRDUP(src->path, + *uri->path == '/' ? uri->path + 1 : uri->path) < 0) + goto cleanup; + + if (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) { + char *tmp; + if (!(tmp = strchr(src->path, '/')) || + tmp == src->path) { + virReportError(VIR_ERR_XML_ERROR, + _("missing volume name or file name in " + "gluster source path '%s'"), src->path); + goto cleanup; + } + + src->volume = src->path; + + if (VIR_STRDUP(src->path, tmp) < 0) + goto cleanup; + + tmp[0] = '\0'; + } + + if (VIR_ALLOC(src->hosts) < 0) + goto cleanup; + + src->nhosts = 1; + + if (uri->port > 0) { + if (virAsprintf(&src->hosts->port, "%d", uri->port) < 0) + goto cleanup; + } + + if (VIR_STRDUP(src->hosts->name, uri->server) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virURIFree(uri); + virStringFreeList(scheme); + return ret; +} + + +static int +virStorageSourceParseBackingColon(virStorageSourcePtr src, + const char *path) +{ + char **backing = NULL; + int ret = -1; + + if (!(backing = virStringSplit(path, ":", 0))) + goto cleanup; + + if (!backing[0] || + (src->protocol = virStorageNetProtocolTypeFromString(backing[0])) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid backing protocol '%s'"), + NULLSTR(backing[0])); + goto cleanup; + } + + switch ((virStorageNetProtocol) src->protocol) { + case VIR_STORAGE_NET_PROTOCOL_NBD: + if (VIR_ALLOC_N(src->hosts, 1) < 0) + goto cleanup; + src->nhosts = 1; + src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; + + /* format: [] denotes optional sections, uppercase are variable strings + * nbd:unix:/PATH/TO/SOCKET[:exportname=EXPORTNAME] + * nbd:HOSTNAME:PORT[:exportname=EXPORTNAME] + */ + if (!backing[1]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing remote information in '%s' for protocol nbd"), + path); + goto cleanup; + } else if (STREQ(backing[1], "unix")) { + if (!backing[2]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing unix socket path in nbd backing string %s"), + path); + goto cleanup; + } + + if (VIR_STRDUP(src->hosts->socket, backing[2]) < 0) + goto cleanup; + + } else { + if (!backing[1]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing host name in nbd string '%s'"), + path); + goto cleanup; + } + + if (VIR_STRDUP(src->hosts->name, backing[1]) < 0) + goto cleanup; + + if (!backing[2]) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing port in nbd string '%s'"), + path); + goto cleanup; + } + + if (VIR_STRDUP(src->hosts->port, backing[2]) < 0) + goto cleanup; + } + + if (backing[3] && STRPREFIX(backing[3], "exportname=")) { + if (VIR_STRDUP(src->path, backing[3] + strlen("exportname=")) < 0) + goto cleanup; + } + break; + + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + case VIR_STORAGE_NET_PROTOCOL_RBD: + case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store parser is not implemented for protocol %s"), + backing[0]); + goto cleanup; + + case VIR_STORAGE_NET_PROTOCOL_HTTP: + case VIR_STORAGE_NET_PROTOCOL_HTTPS: + case VIR_STORAGE_NET_PROTOCOL_FTP: + case VIR_STORAGE_NET_PROTOCOL_FTPS: + case VIR_STORAGE_NET_PROTOCOL_TFTP: + case VIR_STORAGE_NET_PROTOCOL_ISCSI: + case VIR_STORAGE_NET_PROTOCOL_GLUSTER: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("malformed backing store path for protocol %s"), + backing[0]); + goto cleanup; + } + + ret = 0; + + cleanup: + virStringFreeList(backing); + return ret; + +} + + +static virStorageSourcePtr +virStorageSourceNewFromBackingAbsolute(const char *path) +{ + virStorageSourcePtr ret; + + if (VIR_ALLOC(ret) < 0) + return NULL; + + if (virStorageIsFile(path)) { + ret->type = VIR_STORAGE_TYPE_FILE; + + /* XXX store the relative directory name for test's sake */ + if (!(ret->relDir = mdir_name(path))) { + virReportOOMError(); + goto error; + } + + /* XXX we don't currently need to store the canonical path but the + * change would break the test suite. Get rid of this later */ + if (!(ret->path = canonicalize_file_name(path))) { + if (VIR_STRDUP(ret->path, path) < 0) + goto error; + } + } else { + ret->type = VIR_STORAGE_TYPE_NETWORK; + + /* handle URI formatted backing stores */ + if (strstr(path, "://")) { + if (virStorageSourceParseBackingURI(ret, path) < 0) + goto error; + } else { + if (virStorageSourceParseBackingColon(ret, path) < 0) + goto error; + } + + /* XXX fill relative path so that relative names work with network storage too */ + if (ret->path) { + if (!(ret->relDir = mdir_name(ret->path))) { + virReportOOMError(); + goto error; + } + } else { + if (VIR_STRDUP(ret->relDir, "") < 0) + goto error; + } + } + + return ret; + + error: + virStorageSourceFree(ret); + return NULL; +} + + +virStorageSourcePtr +virStorageSourceNewFromBacking(virStorageSourcePtr parent) +{ + struct stat st; + virStorageSourcePtr ret; + + if (virStorageIsRelative(parent->backingStoreRaw)) + ret = virStorageSourceNewFromBackingRelative(parent, + parent->backingStoreRaw); + else + ret = virStorageSourceNewFromBackingAbsolute(parent->backingStoreRaw); + + if (ret) { + if (VIR_STRDUP(ret->relPath, parent->backingStoreRaw) < 0) { + virStorageSourceFree(ret); + return NULL; + } + + /* possibly update local type */ + if (ret->type == VIR_STORAGE_TYPE_FILE) { + if (stat(ret->path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + ret->type = VIR_STORAGE_TYPE_DIR; + ret->format = VIR_STORAGE_FILE_DIR; + } else if (S_ISBLK(st.st_mode)) { + ret->type = VIR_STORAGE_TYPE_BLOCK; + } + } + } + } + + return ret; +} diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 936b0ac..3ccfacc 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -254,6 +254,8 @@ struct _virStorageSource { /* Name of the child backing store recorded in metadata of the * current file. */ char *backingStoreRaw; + /* is backing store identified as relative */ + bool backingRelative; }; @@ -318,5 +320,7 @@ void virStorageSourceClear(virStorageSourcePtr def); int virStorageSourceGetActualType(virStorageSourcePtr def); void virStorageSourceFree(virStorageSourcePtr def); void virStorageSourceClearBackingStore(virStorageSourcePtr def); +virStorageSourcePtr virStorageSourceNewFromBacking(virStorageSourcePtr parent); + #endif /* __VIR_STORAGE_FILE_H__ */ -- 1.9.2

Use the new backing store parser in the backing chain crawler. This change needs one test change where information about the NBD image are now parsed differently. --- src/storage/storage_driver.c | 90 +------------------------------------------- tests/virstoragetest.c | 14 ++++--- 2 files changed, 9 insertions(+), 95 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index cf969c8..ac49553 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -3041,56 +3041,6 @@ virStorageFileAccess(virStorageSourcePtr src, } -/** - * Given a starting point START (a directory containing the original - * file, if the original file was opened via a relative path; ignored - * if NAME is absolute), determine the location of the backing file - * NAME (possibly relative), and compute the relative DIRECTORY - * (optional) and CANONICAL (mandatory) location of the backing file. - * Return 0 on success, negative on error. - */ -static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) -virFindBackingFile(const char *start, const char *path, - char **directory, char **canonical) -{ - /* FIXME - when we eventually allow non-raw network devices, we - * must ensure that we handle backing files the same way as qemu. - * For a qcow2 top file of gluster://server/vol/img, qemu treats - * the relative backing file 'rel' as meaning - * 'gluster://server/vol/rel', while the backing file '/abs' is - * used as a local file. But we cannot canonicalize network - * devices via canonicalize_file_name(), because they are not part - * of the local file system. */ - char *combined = NULL; - int ret = -1; - - if (*path == '/') { - /* Safe to cast away const */ - combined = (char *)path; - } else if (virAsprintf(&combined, "%s/%s", start, path) < 0) { - goto cleanup; - } - - if (directory && !(*directory = mdir_name(combined))) { - virReportOOMError(); - goto cleanup; - } - - if (!(*canonical = canonicalize_file_name(combined))) { - virReportSystemError(errno, - _("Can't canonicalize path '%s'"), path); - goto cleanup; - } - - ret = 0; - - cleanup: - if (combined != path) - VIR_FREE(combined); - return ret; -} - - /* Recursive workhorse for virStorageFileGetMetadata. */ static int virStorageFileGetMetadataRecurse(virStorageSourcePtr src, @@ -3099,7 +3049,6 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, virHashTablePtr cycle) { int ret = -1; - struct stat st; const char *uniqueName; char *buf = NULL; ssize_t headerLen; @@ -3151,46 +3100,9 @@ virStorageFileGetMetadataRecurse(virStorageSourcePtr src, goto cleanup; } - if (VIR_ALLOC(backingStore) < 0) + if (!(backingStore = virStorageSourceNewFromBacking(src))) goto cleanup; - if (VIR_STRDUP(backingStore->relPath, src->backingStoreRaw) < 0) - goto cleanup; - - if (virStorageIsFile(src->backingStoreRaw)) { - backingStore->type = VIR_STORAGE_TYPE_FILE; - - if (virFindBackingFile(src->relDir, - src->backingStoreRaw, - &backingStore->relDir, - &backingStore->path) < 0) { - /* the backing file is (currently) unavailable, treat this - * file as standalone: - * backingStoreRaw is kept to mark broken image chains */ - VIR_WARN("Backing file '%s' of image '%s' is missing.", - src->backingStoreRaw, src->path); - ret = 0; - goto cleanup; - } - - /* update the type for local storage */ - if (stat(backingStore->path, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - backingStore->type = VIR_STORAGE_TYPE_DIR; - backingStore->format = VIR_STORAGE_FILE_DIR; - } else if (S_ISBLK(st.st_mode)) { - backingStore->type = VIR_STORAGE_TYPE_BLOCK; - } - } - } else { - /* TODO: To satisfy the test case, copy the network URI as path. This - * will be removed later. */ - backingStore->type = VIR_STORAGE_TYPE_NETWORK; - - if (VIR_STRDUP(backingStore->path, src->backingStoreRaw) < 0) - goto cleanup; - } - if (backingFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) backingStore->format = VIR_STORAGE_FILE_RAW; else if (backingFormat == VIR_STORAGE_FILE_AUTO_SAFE) diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 746bf63..29297ef 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -730,20 +730,22 @@ mymain(void) /* Rewrite qcow2 to use an nbd: protocol as backend */ virCommandFree(cmd); cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2", - "-F", "raw", "-b", "nbd:example.org:6000", + "-F", "raw", "-b", "nbd:example.org:6000:exportname=blah", "qcow2", NULL); if (virCommandRun(cmd, NULL) < 0) ret = -1; - qcow2.expBackingStore = "nbd:example.org:6000"; - qcow2.expBackingStoreRaw = "nbd:example.org:6000"; + qcow2.expBackingStore = "blah"; + qcow2.expBackingStoreRaw = "nbd:example.org:6000:exportname=blah"; /* Qcow2 file with backing protocol instead of file */ testFileData nbd = { - .pathRel = "nbd:example.org:6000", - .pathAbs = "nbd:example.org:6000", - .path = "nbd:example.org:6000", + .pathRel = "nbd:example.org:6000:exportname=blah", + .pathAbs = "nbd:example.org:6000:exportname=blah", + .path = "blah", .type = VIR_STORAGE_TYPE_NETWORK, .format = VIR_STORAGE_FILE_RAW, + .relDirRel = ".", + .relDirAbs = ".", }; TEST_CHAIN(11, "qcow2", absqcow2, VIR_STORAGE_FILE_QCOW2, (&qcow2, &nbd), EXP_PASS, -- 1.9.2

Now we don't need to skip backing chain detection for remote disks. --- src/qemu/qemu_domain.c | 8 +++----- src/storage/storage_driver.c | 18 ++++++------------ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 7ea9032..cd37ac4 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2414,12 +2414,10 @@ qemuDomainDetermineDiskChain(virQEMUDriverPtr driver, int ret = 0; uid_t uid; gid_t gid; - const char *src = virDomainDiskGetSource(disk); - int type = virDomainDiskGetType(disk); + int type = virStorageSourceGetActualType(&disk->src); - if (!src || - type == VIR_STORAGE_TYPE_NETWORK || - type == VIR_STORAGE_TYPE_VOLUME) + if (type != VIR_STORAGE_TYPE_NETWORK && + !disk->src.path) goto cleanup; if (disk->src.backingStore) { diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index ac49553..1486b72 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -3162,19 +3162,13 @@ virStorageFileGetMetadata(virStorageSourcePtr src, if (!(cycle = virHashCreate(5, NULL))) return -1; - if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK) { - if (!src->relPath && - VIR_STRDUP(src->relPath, src->path) < 0) - goto cleanup; + if (!src->relPath && + VIR_STRDUP(src->relPath, src->path) < 0) + goto cleanup; - if (!src->relDir && - !(src->relDir = mdir_name(src->path))) { - virReportOOMError(); - goto cleanup; - } - } else { - /* TODO: currently unimplemented for non-local storage */ - ret = 0; + if (!src->relDir && + !(src->relDir = mdir_name(src->path))) { + virReportOOMError(); goto cleanup; } -- 1.9.2

Now that we are able to select images from the backing chain via indexed access we should also convert possible network sources to qemu-compatible strings before passing them to qemu. --- src/qemu/qemu_driver.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b0a79e4..e9cf5d5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14901,6 +14901,7 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm, virDomainDiskDefPtr disk; virStorageSourcePtr baseSource = NULL; unsigned int baseIndex = 0; + char *basePath = NULL; if (!virDomainObjIsActive(vm)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", @@ -14969,9 +14970,16 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm, base, baseIndex, NULL)))) goto endjob; + if (baseIndex) { + if (qemuGetDriveSourceString(baseSource, NULL, &basePath) < 0) + goto endjob; + } else { + if (VIR_STRDUP(basePath, base) < 0) + goto endjob; + } + qemuDomainObjEnterMonitor(driver, vm); - ret = qemuMonitorBlockJob(priv->mon, device, - baseIndex ? baseSource->path : base, + ret = qemuMonitorBlockJob(priv->mon, device, basePath, bandwidth, info, mode, async); qemuDomainObjExitMonitor(driver, vm); if (ret < 0) @@ -15042,6 +15050,7 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm, } cleanup: + VIR_FREE(basePath); VIR_FREE(device); if (vm) virObjectUnlock(vm); -- 1.9.2

Now that we are able to select images from the backing chain via indexed access we should also convert possible network sources to qemu-compatible strings before passing them to qemu. --- src/qemu/qemu_driver.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e9cf5d5..1188300 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -15355,6 +15355,8 @@ qemuDomainBlockCommit(virDomainPtr dom, unsigned int baseIndex = 0; const char *top_parent = NULL; bool clean_access = false; + char *topPath = NULL; + char *basePath = NULL; virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW, -1); @@ -15441,6 +15443,22 @@ qemuDomainBlockCommit(virDomainPtr dom, VIR_DISK_CHAIN_READ_WRITE) < 0)) goto endjob; + if (top && !topIndex) { + if (VIR_STRDUP(topPath, top) < 0) + goto endjob; + } else { + if (qemuGetDriveSourceString(topSource, NULL, &topPath) < 0) + goto endjob; + } + + if (base && !baseIndex) { + if (VIR_STRDUP(basePath, base) < 0) + goto endjob; + } else { + if (qemuGetDriveSourceString(baseSource, NULL, &basePath) < 0) + goto endjob; + } + /* Start the commit operation. Pass the user's original spelling, * if any, through to qemu, since qemu may behave differently * depending on whether the input was specified as relative or @@ -15448,8 +15466,7 @@ qemuDomainBlockCommit(virDomainPtr dom, * thing if the user specified a relative name). */ qemuDomainObjEnterMonitor(driver, vm); ret = qemuMonitorBlockCommit(priv->mon, device, - top && !topIndex ? top : topSource->path, - base && !baseIndex ? base : baseSource->path, + topPath, basePath, bandwidth); qemuDomainObjExitMonitor(driver, vm); @@ -15467,6 +15484,8 @@ qemuDomainBlockCommit(virDomainPtr dom, vm = NULL; cleanup: + VIR_FREE(topPath); + VIR_FREE(basePath); VIR_FREE(device); if (vm) virObjectUnlock(vm); -- 1.9.2

Add code to fake gluster storage path canonicalization while I'm working on the code. --- src/storage/storage_backend_gluster.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c index 1a844c9..411851b 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -533,6 +533,7 @@ typedef virStorageFileBackendGlusterPriv *virStorageFileBackendGlusterPrivPtr; struct _virStorageFileBackendGlusterPriv { glfs_t *vol; + char *uid; }; @@ -547,6 +548,7 @@ virStorageFileBackendGlusterDeinit(virStorageSourcePtr src) if (priv->vol) glfs_fini(priv->vol); + VIR_FREE(priv->uid); VIR_FREE(priv); src->drv->priv = NULL; @@ -606,6 +608,10 @@ virStorageFileBackendGlusterInit(virStorageSourcePtr src) goto error; } + if (virAsprintf(&priv->uid, "gluster://%s:%d/%s/%s", hostname, port, + src->volume, src->path) < 0) + goto error; + src->drv->priv = priv; return 0; @@ -713,6 +719,15 @@ virStorageFileBackendGlusterAccess(virStorageSourcePtr src, } +static const char * +virStorageFileBackendGlusterGetUniqueIdentifier(virStorageSourcePtr src) +{ + virStorageFileBackendGlusterPrivPtr priv = src->drv->priv; + + return priv->uid; +} + + virStorageFileBackend virStorageFileBackendGluster = { .type = VIR_STORAGE_TYPE_NETWORK, .protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER, @@ -724,4 +739,8 @@ virStorageFileBackend virStorageFileBackendGluster = { .storageFileStat = virStorageFileBackendGlusterStat, .storageFileReadHeader = virStorageFileBackendGlusterReadHeader, .storageFileAccess = virStorageFileBackendGlusterAccess, + + .storageFileGetUniqueIdentifier = virStorageFileBackendGlusterGetUniqueIdentifier, + + }; -- 1.9.2
participants (2)
-
Eric Blake
-
Peter Krempa