From 064fec69be4a4673e0df17b007bf781026c3b4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 25 Jan 2018 09:35:46 +0000 Subject: [PATCH] storage: move storage file backend framework into util directory The QEMU driver loadable module needs to be able to resolve all ELF symbols it references against libvirt.so. Some of its symbols can only be resolved against the storage_driver.so loadable module which creates a hard dependancy between them. By moving the storage file backend framework into the util directory, this gets included directly in the libvirt.so library. The actual backend implementations are still done as loadable modules, so this doesn't re-add deps on gluster libraries. Signed-off-by: Daniel P. Berrange --- po/POTFILES.in | 3 +- src/Makefile.am | 3 +- src/libvirt_private.syms | 19 + src/qemu/qemu_domain.c | 1 - src/qemu/qemu_driver.c | 1 - src/security/virt-aa-helper.c | 2 - src/storage/storage_backend_fs.c | 3 +- src/storage/storage_backend_gluster.c | 3 +- src/storage/storage_source.c | 645 ------------------ src/storage/storage_source.h | 59 -- src/util/virstoragefile.c | 609 ++++++++++++++++- src/util/virstoragefile.h | 32 + .../virstoragefilebackend.c} | 4 +- .../virstoragefilebackend.h} | 8 +- tests/virstoragetest.c | 1 - 15 files changed, 669 insertions(+), 724 deletions(-) delete mode 100644 src/storage/storage_source.c delete mode 100644 src/storage/storage_source.h rename src/{storage/storage_source_backend.c => util/virstoragefilebackend.c} (96%) rename src/{storage/storage_source_backend.h => util/virstoragefilebackend.h} (94%) diff --git a/po/POTFILES.in b/po/POTFILES.in index ea99f61677..2859554690 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -186,8 +186,6 @@ src/storage/storage_backend_sheepdog.c src/storage/storage_backend_vstorage.c src/storage/storage_backend_zfs.c src/storage/storage_driver.c -src/storage/storage_source.c -src/storage/storage_source_backend.c src/storage/storage_util.c src/test/test_driver.c src/uml/uml_conf.c @@ -263,6 +261,7 @@ src/util/virsexpr.c src/util/virsocketaddr.c src/util/virstorageencryption.c src/util/virstoragefile.c +src/util/virstoragefilebackend.c src/util/virstring.c src/util/virsysinfo.c src/util/virthreadjob.c diff --git a/src/Makefile.am b/src/Makefile.am index 99f9bfb6f2..54ef81d1ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -178,6 +178,7 @@ UTIL_SOURCES = \ util/virsocketaddr.h util/virsocketaddr.c \ util/virstorageencryption.c util/virstorageencryption.h \ util/virstoragefile.c util/virstoragefile.h \ + util/virstoragefilebackend.c util/virstoragefilebackend.h \ util/virstring.h util/virstring.c \ util/virsysinfo.c util/virsysinfo.h util/virsysinfopriv.h \ util/virsystemd.c util/virsystemd.h util/virsystemdpriv.h \ @@ -1064,8 +1065,6 @@ STORAGE_DRIVER_BACKEND_SOURCES = \ STORAGE_DRIVER_SOURCES = \ storage/storage_driver.h storage/storage_driver.c \ - storage/storage_source.h storage/storage_source.c \ - storage/storage_source_backend.h storage/storage_source_backend.c \ $(STORAGE_DRIVER_BACKEND_SOURCES) \ storage/storage_util.h storage/storage_util.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 0bce0bbfb2..3ec510bd32 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2720,24 +2720,39 @@ virStorageAuthDefCopy; virStorageAuthDefFormat; virStorageAuthDefFree; virStorageAuthDefParse; +virStorageFileAccess; virStorageFileCanonicalizePath; virStorageFileChainGetBroken; virStorageFileChainLookup; +virStorageFileChown; +virStorageFileCreate; +virStorageFileDeinit; virStorageFileFeatureTypeFromString; virStorageFileFeatureTypeToString; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; +virStorageFileGetBackingStoreStr; virStorageFileGetLVMKey; +virStorageFileGetMetadata; virStorageFileGetMetadataFromBuf; virStorageFileGetMetadataFromFD; virStorageFileGetMetadataInternal; virStorageFileGetRelativeBackingPath; virStorageFileGetSCSIKey; +virStorageFileGetUniqueIdentifier; +virStorageFileInit; +virStorageFileInitAs; virStorageFileIsClusterFS; virStorageFileParseBackingStoreStr; virStorageFileParseChainIndex; virStorageFileProbeFormat; +virStorageFileRead; +virStorageFileReportBrokenChain; virStorageFileResize; +virStorageFileStat; +virStorageFileSupportsAccess; +virStorageFileSupportsSecurityDriver; +virStorageFileUnlink; virStorageIsFile; virStorageIsRelative; virStorageNetHostDefClear; @@ -2776,6 +2791,10 @@ virStorageTypeFromString; virStorageTypeToString; +# util/virstoragefilebackend.h +virStorageFileBackendRegister; + + # util/virstring.h virArgvToString; virAsprintfInternal; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index aa652959ed..db8586360f 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -56,7 +56,6 @@ #include "locking/domain_lock.h" #include "storage/storage_driver.h" -#include "storage/storage_source.h" #ifdef MAJOR_IN_MKDEV # include diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8d77d8913a..2990c35484 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -100,7 +100,6 @@ #include "viraccessapicheck.h" #include "viraccessapicheckqemu.h" #include "storage/storage_driver.h" -#include "storage/storage_source.h" #include "virhostdev.h" #include "domain_capabilities.h" #include "vircgroup.h" diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 29a459da20..ff0068cecd 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -57,8 +57,6 @@ #include "virgettext.h" #include "virhostdev.h" -#include "storage/storage_source.h" - #define VIR_FROM_THIS VIR_FROM_SECURITY static char *progname; diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index c528c1dae5..9b0fcf92c5 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -37,10 +37,9 @@ #include "virerror.h" #include "storage_backend_fs.h" -#include "storage_source_backend.h" #include "storage_util.h" #include "storage_conf.h" -#include "virstoragefile.h" +#include "virstoragefilebackend.h" #include "vircommand.h" #include "viralloc.h" #include "virxml.h" diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c index 68d9c726e9..c6cc531e2f 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -24,12 +24,11 @@ #include #include "storage_backend_gluster.h" -#include "storage_source_backend.h" #include "storage_conf.h" #include "viralloc.h" #include "virerror.h" #include "virlog.h" -#include "virstoragefile.h" +#include "virstoragefilebackend.h" #include "virstring.h" #include "viruri.h" #include "storage_util.h" diff --git a/src/storage/storage_source.c b/src/storage/storage_source.c deleted file mode 100644 index a5eefe5032..0000000000 --- a/src/storage/storage_source.c +++ /dev/null @@ -1,645 +0,0 @@ -/* - * storage_source.c: Storage source object accessors to real storage - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - */ - -#include - -#include -#include - -#include -#include - -#include "virerror.h" -#include "storage_source.h" -#include "storage_source_backend.h" -#include "viralloc.h" -#include "virlog.h" -#include "virstring.h" -#include "virhash.h" - -#define VIR_FROM_THIS VIR_FROM_STORAGE - -VIR_LOG_INIT("storage.storage_source"); - - -static bool -virStorageFileIsInitialized(const virStorageSource *src) -{ - return src && src->drv; -} - - -static virStorageFileBackendPtr -virStorageFileGetBackendForSupportCheck(const virStorageSource *src) -{ - int actualType; - - if (!src) - return NULL; - - if (src->drv) - return src->drv->backend; - - actualType = virStorageSourceGetActualType(src); - - return virStorageFileBackendForTypeInternal(actualType, src->protocol, false); -} - - -static bool -virStorageFileSupportsBackingChainTraversal(virStorageSourcePtr src) -{ - virStorageFileBackendPtr backend; - - if (!(backend = virStorageFileGetBackendForSupportCheck(src))) - return false; - - return backend->storageFileGetUniqueIdentifier && - backend->storageFileRead && - backend->storageFileAccess; -} - - -/** - * virStorageFileSupportsSecurityDriver: - * - * @src: a storage file structure - * - * Check if a storage file supports operations needed by the security - * driver to perform labelling - */ -bool -virStorageFileSupportsSecurityDriver(const virStorageSource *src) -{ - virStorageFileBackendPtr backend; - - if (!(backend = virStorageFileGetBackendForSupportCheck(src))) - return false; - - return !!backend->storageFileChown; -} - - -/** - * virStorageFileSupportsAccess: - * - * @src: a storage file structure - * - * Check if a storage file supports checking if the storage source is accessible - * for the given vm. - */ -bool -virStorageFileSupportsAccess(const virStorageSource *src) -{ - virStorageFileBackendPtr backend; - - if (!(backend = virStorageFileGetBackendForSupportCheck(src))) - return false; - - return !!backend->storageFileAccess; -} - - -void -virStorageFileDeinit(virStorageSourcePtr src) -{ - if (!virStorageFileIsInitialized(src)) - return; - - if (src->drv->backend && - src->drv->backend->backendDeinit) - src->drv->backend->backendDeinit(src); - - VIR_FREE(src->drv); -} - - -/** - * virStorageFileInitAs: - * - * @src: storage source definition - * @uid: uid used to access the file, or -1 for current uid - * @gid: gid used to access the file, or -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 -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; - - if (src->drv->backend->backendInit && - src->drv->backend->backendInit(src) < 0) - goto error; - - return 0; - - error: - VIR_FREE(src->drv); - return -1; -} - - -/** - * virStorageFileInit: - * - * See virStorageFileInitAs. The file is initialized to be accessed by the - * current user. - */ -int -virStorageFileInit(virStorageSourcePtr src) -{ - return virStorageFileInitAs(src, -1, -1); -} - - -/** - * virStorageFileCreate: Creates an empty storage file via storage driver - * - * @src: file structure pointing to the file - * - * Returns 0 on success, -2 if the function isn't supported by the backend, - * -1 on other failure. Errno is set in case of failure. - */ -int -virStorageFileCreate(virStorageSourcePtr src) -{ - int ret; - - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileCreate) { - errno = ENOSYS; - return -2; - } - - ret = src->drv->backend->storageFileCreate(src); - - VIR_DEBUG("created storage file %p: ret=%d, errno=%d", - src, ret, errno); - - return ret; -} - - -/** - * virStorageFileUnlink: Unlink storage file via storage driver - * - * @src: file structure pointing to the file - * - * Unlinks the file described by the @file structure. - * - * Returns 0 on success, -2 if the function isn't supported by the backend, - * -1 on other failure. Errno is set in case of failure. - */ -int -virStorageFileUnlink(virStorageSourcePtr src) -{ - int ret; - - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileUnlink) { - errno = ENOSYS; - return -2; - } - - ret = src->drv->backend->storageFileUnlink(src); - - VIR_DEBUG("unlinked storage file %p: ret=%d, errno=%d", - src, ret, errno); - - return ret; -} - - -/** - * virStorageFileStat: returns stat struct of a file via storage driver - * - * @src: file structure pointing to the file - * @stat: stat structure to return data - * - * Returns 0 on success, -2 if the function isn't supported by the backend, - * -1 on other failure. Errno is set in case of failure. -*/ -int -virStorageFileStat(virStorageSourcePtr src, - struct stat *st) -{ - int ret; - - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileStat) { - errno = ENOSYS; - return -2; - } - - ret = src->drv->backend->storageFileStat(src, st); - - VIR_DEBUG("stat of storage file %p: ret=%d, errno=%d", - src, ret, errno); - - return ret; -} - - -/** - * virStorageFileRead: read bytes from a file into a buffer - * - * @src: file structure pointing to the file - * @offset: number of bytes to skip in the storage file - * @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 -virStorageFileRead(virStorageSourcePtr src, - size_t offset, - size_t 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->storageFileRead) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("storage file reading is not supported for " - "storage type %s (protocol: %s)"), - virStorageTypeToString(src->type), - virStorageNetProtocolTypeToString(src->protocol)); - return -2; - } - - ret = src->drv->backend->storageFileRead(src, offset, len, buf); - - VIR_DEBUG("read '%zd' bytes from storage '%p' starting at offset '%zu'", - ret, src, offset); - - 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 and is valid until the storage file is - * deinitialized. 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); -} - - -/** - * virStorageFileAccess: Check accessibility 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); -} - - -/** - * virStorageFileChown: Change owner of a storage file - * - * @src: storage file to change owner of - * @uid: new owner id - * @gid: new group id - * - * 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 -virStorageFileChown(const virStorageSource *src, - uid_t uid, - gid_t gid) -{ - if (!virStorageFileIsInitialized(src) || - !src->drv->backend->storageFileChown) { - errno = ENOSYS; - return -2; - } - - VIR_DEBUG("chown of storage file %p to %u:%u", - src, (unsigned int)uid, (unsigned int)gid); - - return src->drv->backend->storageFileChown(src, uid, gid); -} - - -/** - * virStorageFileReportBrokenChain: - * - * @errcode: errno when accessing @src - * @src: inaccessible file in the backing chain of @parent - * @parent: root virStorageSource being checked - * - * Reports the correct error message if @src is missing in the backing chain - * for @parent. - */ -void -virStorageFileReportBrokenChain(int errcode, - virStorageSourcePtr src, - virStorageSourcePtr parent) -{ - - if (src->drv) { - unsigned int access_user = src->drv->uid; - unsigned int access_group = src->drv->gid; - - if (src == parent) { - virReportSystemError(errcode, - _("Cannot access storage file '%s' " - "(as uid:%u, gid:%u)"), - src->path, access_user, access_group); - } else { - virReportSystemError(errcode, - _("Cannot access backing file '%s' " - "of storage file '%s' (as uid:%u, gid:%u)"), - src->path, parent->path, access_user, access_group); - } - } else { - if (src == parent) { - virReportSystemError(errcode, - _("Cannot access storage file '%s'"), - src->path); - } else { - virReportSystemError(errcode, - _("Cannot access backing file '%s' " - "of storage file '%s'"), - src->path, parent->path); - } - } -} - - -/* Recursive workhorse for virStorageFileGetMetadata. */ -static int -virStorageFileGetMetadataRecurse(virStorageSourcePtr src, - virStorageSourcePtr parent, - uid_t uid, gid_t gid, - bool allow_probe, - bool report_broken, - virHashTablePtr cycle, - unsigned int depth) -{ - int ret = -1; - const char *uniqueName; - char *buf = NULL; - ssize_t headerLen; - virStorageSourcePtr backingStore = NULL; - int backingFormat; - - VIR_DEBUG("path=%s format=%d uid=%u gid=%u probe=%d", - src->path, src->format, - (unsigned int)uid, (unsigned int)gid, allow_probe); - - /* 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 (virStorageFileAccess(src, F_OK) < 0) { - virStorageFileReportBrokenChain(errno, src, parent); - goto cleanup; - } - - 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, uniqueName, (void *)1) < 0) - goto cleanup; - - if ((headerLen = virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, - &buf)) < 0) - goto cleanup; - - if (virStorageFileGetMetadataInternal(src, buf, headerLen, - &backingFormat) < 0) - goto cleanup; - - if (src->backingStoreRaw) { - if (!(backingStore = virStorageSourceNewFromBacking(src))) - 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 ((ret = virStorageFileGetMetadataRecurse(backingStore, parent, - uid, gid, - allow_probe, report_broken, - cycle, depth + 1)) < 0) { - if (report_broken) - goto cleanup; - - /* if we fail somewhere midway, just accept and return a - * broken chain */ - ret = 0; - goto cleanup; - } - } else { - /* add terminator */ - if (VIR_ALLOC(backingStore) < 0) - goto cleanup; - } - - src->backingStore = backingStore; - backingStore = NULL; - ret = 0; - - cleanup: - if (virStorageSourceHasBacking(src)) - src->backingStore->id = depth; - VIR_FREE(buf); - virStorageFileDeinit(src); - 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. - * - * If @report_broken is true, the whole function fails with a possibly sane - * error instead of just returning a broken chain. - * - * Caller MUST free result after use via virStorageSourceFree. - */ -int -virStorageFileGetMetadata(virStorageSourcePtr src, - uid_t uid, gid_t gid, - bool allow_probe, - bool report_broken) -{ - VIR_DEBUG("path=%s format=%d uid=%u gid=%u probe=%d, report_broken=%d", - src->path, src->format, (unsigned int)uid, (unsigned int)gid, - allow_probe, report_broken); - - virHashTablePtr cycle = NULL; - virStorageType actualType = virStorageSourceGetActualType(src); - int ret = -1; - - if (!(cycle = virHashCreate(5, NULL))) - return -1; - - if (src->format <= VIR_STORAGE_FILE_NONE) { - if (actualType == VIR_STORAGE_TYPE_DIR) - src->format = VIR_STORAGE_FILE_DIR; - else if (allow_probe) - src->format = VIR_STORAGE_FILE_AUTO; - else - src->format = VIR_STORAGE_FILE_RAW; - } - - ret = virStorageFileGetMetadataRecurse(src, src, uid, gid, - allow_probe, report_broken, cycle, 1); - - virHashFree(cycle); - return ret; -} - - -/** - * virStorageFileGetBackingStoreStr: - * @src: storage object - * - * Extracts the backing store string as stored in the storage volume described - * by @src and returns it to the user. Caller is responsible for freeing it. - * In case when the string can't be retrieved or does not exist NULL is - * returned. - */ -char * -virStorageFileGetBackingStoreStr(virStorageSourcePtr src) -{ - virStorageSourcePtr tmp = NULL; - char *buf = NULL; - ssize_t headerLen; - char *ret = NULL; - - /* exit if we can't load information about the current image */ - if (!virStorageFileSupportsBackingChainTraversal(src)) - return NULL; - - if (virStorageFileAccess(src, F_OK) < 0) - return NULL; - - if ((headerLen = virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, - &buf)) < 0) - return NULL; - - if (!(tmp = virStorageSourceCopy(src, false))) - goto cleanup; - - if (virStorageFileGetMetadataInternal(tmp, buf, headerLen, NULL) < 0) - goto cleanup; - - VIR_STEAL_PTR(ret, tmp->backingStoreRaw); - - cleanup: - VIR_FREE(buf); - virStorageSourceFree(tmp); - - return ret; -} diff --git a/src/storage/storage_source.h b/src/storage/storage_source.h deleted file mode 100644 index 0640c138ed..0000000000 --- a/src/storage/storage_source.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * storage_source.h: Storage source accessors to real storaget - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * . - */ - -#ifndef __VIR_STORAGE_SOURCE_H__ -# define __VIR_STORAGE_SOURCE_H__ - -# include - -# include "virstoragefile.h" - -int virStorageFileInit(virStorageSourcePtr src); -int virStorageFileInitAs(virStorageSourcePtr src, - uid_t uid, gid_t gid); -void virStorageFileDeinit(virStorageSourcePtr src); - -int virStorageFileCreate(virStorageSourcePtr src); -int virStorageFileUnlink(virStorageSourcePtr src); -int virStorageFileStat(virStorageSourcePtr src, - struct stat *stat); -ssize_t virStorageFileRead(virStorageSourcePtr src, - size_t offset, - size_t len, - char **buf); -const char *virStorageFileGetUniqueIdentifier(virStorageSourcePtr src); -int virStorageFileAccess(virStorageSourcePtr src, int mode); -int virStorageFileChown(const virStorageSource *src, uid_t uid, gid_t gid); - -bool virStorageFileSupportsSecurityDriver(const virStorageSource *src); -bool virStorageFileSupportsAccess(const virStorageSource *src); - -int virStorageFileGetMetadata(virStorageSourcePtr src, - uid_t uid, gid_t gid, - bool allow_probe, - bool report_broken) - ATTRIBUTE_NONNULL(1); - -char *virStorageFileGetBackingStoreStr(virStorageSourcePtr src) - ATTRIBUTE_NONNULL(1); - -void virStorageFileReportBrokenChain(int errcode, - virStorageSourcePtr src, - virStorageSourcePtr parent); - -#endif /* __VIR_STORAGE_SOURCE_H__ */ diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 5f661c956c..7f878039ba 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -22,7 +22,7 @@ */ #include -#include "virstoragefile.h" +#include "virstoragefilebackend.h" #include #include @@ -4103,3 +4103,610 @@ virStorageSourcePrivateDataFormatRelPath(virStorageSourcePtr src, return 0; } + +static bool +virStorageFileIsInitialized(const virStorageSource *src) +{ + return src && src->drv; +} + + +static virStorageFileBackendPtr +virStorageFileGetBackendForSupportCheck(const virStorageSource *src) +{ + int actualType; + + if (!src) + return NULL; + + if (src->drv) + return src->drv->backend; + + actualType = virStorageSourceGetActualType(src); + + return virStorageFileBackendForTypeInternal(actualType, src->protocol, false); +} + + +static bool +virStorageFileSupportsBackingChainTraversal(virStorageSourcePtr src) +{ + virStorageFileBackendPtr backend; + + if (!(backend = virStorageFileGetBackendForSupportCheck(src))) + return false; + + return backend->storageFileGetUniqueIdentifier && + backend->storageFileRead && + backend->storageFileAccess; +} + + +/** + * virStorageFileSupportsSecurityDriver: + * + * @src: a storage file structure + * + * Check if a storage file supports operations needed by the security + * driver to perform labelling + */ +bool +virStorageFileSupportsSecurityDriver(const virStorageSource *src) +{ + virStorageFileBackendPtr backend; + + if (!(backend = virStorageFileGetBackendForSupportCheck(src))) + return false; + + return !!backend->storageFileChown; +} + + +/** + * virStorageFileSupportsAccess: + * + * @src: a storage file structure + * + * Check if a storage file supports checking if the storage source is accessible + * for the given vm. + */ +bool +virStorageFileSupportsAccess(const virStorageSource *src) +{ + virStorageFileBackendPtr backend; + + if (!(backend = virStorageFileGetBackendForSupportCheck(src))) + return false; + + return !!backend->storageFileAccess; +} + + +void +virStorageFileDeinit(virStorageSourcePtr src) +{ + if (!virStorageFileIsInitialized(src)) + return; + + if (src->drv->backend && + src->drv->backend->backendDeinit) + src->drv->backend->backendDeinit(src); + + VIR_FREE(src->drv); +} + + +/** + * virStorageFileInitAs: + * + * @src: storage source definition + * @uid: uid used to access the file, or -1 for current uid + * @gid: gid used to access the file, or -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 +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; + + if (src->drv->backend->backendInit && + src->drv->backend->backendInit(src) < 0) + goto error; + + return 0; + + error: + VIR_FREE(src->drv); + return -1; +} + + +/** + * virStorageFileInit: + * + * See virStorageFileInitAs. The file is initialized to be accessed by the + * current user. + */ +int +virStorageFileInit(virStorageSourcePtr src) +{ + return virStorageFileInitAs(src, -1, -1); +} + + +/** + * virStorageFileCreate: Creates an empty storage file via storage driver + * + * @src: file structure pointing to the file + * + * Returns 0 on success, -2 if the function isn't supported by the backend, + * -1 on other failure. Errno is set in case of failure. + */ +int +virStorageFileCreate(virStorageSourcePtr src) +{ + int ret; + + if (!virStorageFileIsInitialized(src) || + !src->drv->backend->storageFileCreate) { + errno = ENOSYS; + return -2; + } + + ret = src->drv->backend->storageFileCreate(src); + + VIR_DEBUG("created storage file %p: ret=%d, errno=%d", + src, ret, errno); + + return ret; +} + + +/** + * virStorageFileUnlink: Unlink storage file via storage driver + * + * @src: file structure pointing to the file + * + * Unlinks the file described by the @file structure. + * + * Returns 0 on success, -2 if the function isn't supported by the backend, + * -1 on other failure. Errno is set in case of failure. + */ +int +virStorageFileUnlink(virStorageSourcePtr src) +{ + int ret; + + if (!virStorageFileIsInitialized(src) || + !src->drv->backend->storageFileUnlink) { + errno = ENOSYS; + return -2; + } + + ret = src->drv->backend->storageFileUnlink(src); + + VIR_DEBUG("unlinked storage file %p: ret=%d, errno=%d", + src, ret, errno); + + return ret; +} + + +/** + * virStorageFileStat: returns stat struct of a file via storage driver + * + * @src: file structure pointing to the file + * @stat: stat structure to return data + * + * Returns 0 on success, -2 if the function isn't supported by the backend, + * -1 on other failure. Errno is set in case of failure. +*/ +int +virStorageFileStat(virStorageSourcePtr src, + struct stat *st) +{ + int ret; + + if (!virStorageFileIsInitialized(src) || + !src->drv->backend->storageFileStat) { + errno = ENOSYS; + return -2; + } + + ret = src->drv->backend->storageFileStat(src, st); + + VIR_DEBUG("stat of storage file %p: ret=%d, errno=%d", + src, ret, errno); + + return ret; +} + + +/** + * virStorageFileRead: read bytes from a file into a buffer + * + * @src: file structure pointing to the file + * @offset: number of bytes to skip in the storage file + * @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 +virStorageFileRead(virStorageSourcePtr src, + size_t offset, + size_t 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->storageFileRead) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("storage file reading is not supported for " + "storage type %s (protocol: %s)"), + virStorageTypeToString(src->type), + virStorageNetProtocolTypeToString(src->protocol)); + return -2; + } + + ret = src->drv->backend->storageFileRead(src, offset, len, buf); + + VIR_DEBUG("read '%zd' bytes from storage '%p' starting at offset '%zu'", + ret, src, offset); + + 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 and is valid until the storage file is + * deinitialized. 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); +} + + +/** + * virStorageFileAccess: Check accessibility 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); +} + + +/** + * virStorageFileChown: Change owner of a storage file + * + * @src: storage file to change owner of + * @uid: new owner id + * @gid: new group id + * + * 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 +virStorageFileChown(const virStorageSource *src, + uid_t uid, + gid_t gid) +{ + if (!virStorageFileIsInitialized(src) || + !src->drv->backend->storageFileChown) { + errno = ENOSYS; + return -2; + } + + VIR_DEBUG("chown of storage file %p to %u:%u", + src, (unsigned int)uid, (unsigned int)gid); + + return src->drv->backend->storageFileChown(src, uid, gid); +} + + +/** + * virStorageFileReportBrokenChain: + * + * @errcode: errno when accessing @src + * @src: inaccessible file in the backing chain of @parent + * @parent: root virStorageSource being checked + * + * Reports the correct error message if @src is missing in the backing chain + * for @parent. + */ +void +virStorageFileReportBrokenChain(int errcode, + virStorageSourcePtr src, + virStorageSourcePtr parent) +{ + + if (src->drv) { + unsigned int access_user = src->drv->uid; + unsigned int access_group = src->drv->gid; + + if (src == parent) { + virReportSystemError(errcode, + _("Cannot access storage file '%s' " + "(as uid:%u, gid:%u)"), + src->path, access_user, access_group); + } else { + virReportSystemError(errcode, + _("Cannot access backing file '%s' " + "of storage file '%s' (as uid:%u, gid:%u)"), + src->path, parent->path, access_user, access_group); + } + } else { + if (src == parent) { + virReportSystemError(errcode, + _("Cannot access storage file '%s'"), + src->path); + } else { + virReportSystemError(errcode, + _("Cannot access backing file '%s' " + "of storage file '%s'"), + src->path, parent->path); + } + } +} + + +/* Recursive workhorse for virStorageFileGetMetadata. */ +static int +virStorageFileGetMetadataRecurse(virStorageSourcePtr src, + virStorageSourcePtr parent, + uid_t uid, gid_t gid, + bool allow_probe, + bool report_broken, + virHashTablePtr cycle, + unsigned int depth) +{ + int ret = -1; + const char *uniqueName; + char *buf = NULL; + ssize_t headerLen; + virStorageSourcePtr backingStore = NULL; + int backingFormat; + + VIR_DEBUG("path=%s format=%d uid=%u gid=%u probe=%d", + src->path, src->format, + (unsigned int)uid, (unsigned int)gid, allow_probe); + + /* 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 (virStorageFileAccess(src, F_OK) < 0) { + virStorageFileReportBrokenChain(errno, src, parent); + goto cleanup; + } + + 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, uniqueName, (void *)1) < 0) + goto cleanup; + + if ((headerLen = virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, + &buf)) < 0) + goto cleanup; + + if (virStorageFileGetMetadataInternal(src, buf, headerLen, + &backingFormat) < 0) + goto cleanup; + + if (src->backingStoreRaw) { + if (!(backingStore = virStorageSourceNewFromBacking(src))) + 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 ((ret = virStorageFileGetMetadataRecurse(backingStore, parent, + uid, gid, + allow_probe, report_broken, + cycle, depth + 1)) < 0) { + if (report_broken) + goto cleanup; + + /* if we fail somewhere midway, just accept and return a + * broken chain */ + ret = 0; + goto cleanup; + } + } else { + /* add terminator */ + if (VIR_ALLOC(backingStore) < 0) + goto cleanup; + } + + src->backingStore = backingStore; + backingStore = NULL; + ret = 0; + + cleanup: + if (virStorageSourceHasBacking(src)) + src->backingStore->id = depth; + VIR_FREE(buf); + virStorageFileDeinit(src); + 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. + * + * If @report_broken is true, the whole function fails with a possibly sane + * error instead of just returning a broken chain. + * + * Caller MUST free result after use via virStorageSourceFree. + */ +int +virStorageFileGetMetadata(virStorageSourcePtr src, + uid_t uid, gid_t gid, + bool allow_probe, + bool report_broken) +{ + VIR_DEBUG("path=%s format=%d uid=%u gid=%u probe=%d, report_broken=%d", + src->path, src->format, (unsigned int)uid, (unsigned int)gid, + allow_probe, report_broken); + + virHashTablePtr cycle = NULL; + virStorageType actualType = virStorageSourceGetActualType(src); + int ret = -1; + + if (!(cycle = virHashCreate(5, NULL))) + return -1; + + if (src->format <= VIR_STORAGE_FILE_NONE) { + if (actualType == VIR_STORAGE_TYPE_DIR) + src->format = VIR_STORAGE_FILE_DIR; + else if (allow_probe) + src->format = VIR_STORAGE_FILE_AUTO; + else + src->format = VIR_STORAGE_FILE_RAW; + } + + ret = virStorageFileGetMetadataRecurse(src, src, uid, gid, + allow_probe, report_broken, cycle, 1); + + virHashFree(cycle); + return ret; +} + + +/** + * virStorageFileGetBackingStoreStr: + * @src: storage object + * + * Extracts the backing store string as stored in the storage volume described + * by @src and returns it to the user. Caller is responsible for freeing it. + * In case when the string can't be retrieved or does not exist NULL is + * returned. + */ +char * +virStorageFileGetBackingStoreStr(virStorageSourcePtr src) +{ + virStorageSourcePtr tmp = NULL; + char *buf = NULL; + ssize_t headerLen; + char *ret = NULL; + + /* exit if we can't load information about the current image */ + if (!virStorageFileSupportsBackingChainTraversal(src)) + return NULL; + + if (virStorageFileAccess(src, F_OK) < 0) + return NULL; + + if ((headerLen = virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, + &buf)) < 0) + return NULL; + + if (!(tmp = virStorageSourceCopy(src, false))) + goto cleanup; + + if (virStorageFileGetMetadataInternal(tmp, buf, headerLen, NULL) < 0) + goto cleanup; + + VIR_STEAL_PTR(ret, tmp->backingStoreRaw); + + cleanup: + VIR_FREE(buf); + virStorageSourceFree(tmp); + + return ret; +} diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index ecd806c93f..9e8afa4932 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -448,5 +448,37 @@ int virStorageSourcePrivateDataFormatRelPath(virStorageSourcePtr src, virBufferPtr buf); +int virStorageFileInit(virStorageSourcePtr src); +int virStorageFileInitAs(virStorageSourcePtr src, + uid_t uid, gid_t gid); +void virStorageFileDeinit(virStorageSourcePtr src); + +int virStorageFileCreate(virStorageSourcePtr src); +int virStorageFileUnlink(virStorageSourcePtr src); +int virStorageFileStat(virStorageSourcePtr src, + struct stat *stat); +ssize_t virStorageFileRead(virStorageSourcePtr src, + size_t offset, + size_t len, + char **buf); +const char *virStorageFileGetUniqueIdentifier(virStorageSourcePtr src); +int virStorageFileAccess(virStorageSourcePtr src, int mode); +int virStorageFileChown(const virStorageSource *src, uid_t uid, gid_t gid); + +bool virStorageFileSupportsSecurityDriver(const virStorageSource *src); +bool virStorageFileSupportsAccess(const virStorageSource *src); + +int virStorageFileGetMetadata(virStorageSourcePtr src, + uid_t uid, gid_t gid, + bool allow_probe, + bool report_broken) + ATTRIBUTE_NONNULL(1); + +char *virStorageFileGetBackingStoreStr(virStorageSourcePtr src) + ATTRIBUTE_NONNULL(1); + +void virStorageFileReportBrokenChain(int errcode, + virStorageSourcePtr src, + virStorageSourcePtr parent); #endif /* __VIR_STORAGE_FILE_H__ */ diff --git a/src/storage/storage_source_backend.c b/src/util/virstoragefilebackend.c similarity index 96% rename from src/storage/storage_source_backend.c rename to src/util/virstoragefilebackend.c index e093c04989..df86ee39c2 100644 --- a/src/storage/storage_source_backend.c +++ b/src/util/virstoragefilebackend.c @@ -1,5 +1,5 @@ /* - * storage_source_backend.c: internal storage source backend contract + * virstoragefilebackend.c: internal storage source backend contract * * Copyright (C) 2007-2018 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange @@ -30,7 +30,7 @@ #include "virerror.h" #include "viralloc.h" #include "internal.h" -#include "storage_source_backend.h" +#include "virstoragefilebackend.h" #include "virlog.h" #include "virfile.h" #include "configmake.h" diff --git a/src/storage/storage_source_backend.h b/src/util/virstoragefilebackend.h similarity index 94% rename from src/storage/storage_source_backend.h rename to src/util/virstoragefilebackend.h index 3af2a5f380..6cd51750ee 100644 --- a/src/storage/storage_source_backend.h +++ b/src/util/virstoragefilebackend.h @@ -1,5 +1,5 @@ /* - * storage_source_backend.h: internal storage source backend contract + * virstoragefilebackend.h: internal storage source backend contract * * Copyright (C) 2007-2018 Red Hat, Inc. * @@ -18,8 +18,8 @@ * . */ -#ifndef __VIR_STORAGE_SOURCE_BACKEND_H__ -# define __VIR_STORAGE_SOURCE_BACKEND_H__ +#ifndef __VIR_STORAGE_FILE_BACKEND_H__ +# define __VIR_STORAGE_FILE_BACKEND_H__ # include @@ -101,4 +101,4 @@ struct _virStorageFileBackend { int virStorageFileBackendRegister(virStorageFileBackendPtr backend); -#endif /* __VIR_STORAGE_SOURCE_BACKEND_H__ */ +#endif /* __VIR_STORAGE_FILE_BACKEND_H__ */ diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 1dc7608cee..6eed7134ed 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -32,7 +32,6 @@ #include "dirname.h" #include "storage/storage_driver.h" -#include "storage/storage_source.h" #define VIR_FROM_THIS VIR_FROM_NONE -- GitLab