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 <berrange@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2018-01-25 09:35:46 +00:00
parent 3be2d1684a
commit 064fec69be
15 changed files with 669 additions and 724 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -56,7 +56,6 @@
#include "locking/domain_lock.h"
#include "storage/storage_driver.h"
#include "storage/storage_source.h"
#ifdef MAJOR_IN_MKDEV
# include <sys/mkdev.h>

View File

@ -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"

View File

@ -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;

View File

@ -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"

View File

@ -24,12 +24,11 @@
#include <glusterfs/api/glfs.h>
#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"

View File

@ -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
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#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;
}

View File

@ -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
* <http://www.gnu.org/licenses/>.
*/
#ifndef __VIR_STORAGE_SOURCE_H__
# define __VIR_STORAGE_SOURCE_H__
# include <sys/stat.h>
# 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__ */

View File

@ -22,7 +22,7 @@
*/
#include <config.h>
#include "virstoragefile.h"
#include "virstoragefilebackend.h"
#include <unistd.h>
#include <fcntl.h>
@ -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;
}

View File

@ -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__ */

View File

@ -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"

View File

@ -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 @@
* <http://www.gnu.org/licenses/>.
*/
#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 <sys/stat.h>
@ -101,4 +101,4 @@ struct _virStorageFileBackend {
int virStorageFileBackendRegister(virStorageFileBackendPtr backend);
#endif /* __VIR_STORAGE_SOURCE_BACKEND_H__ */
#endif /* __VIR_STORAGE_FILE_BACKEND_H__ */

View File

@ -32,7 +32,6 @@
#include "dirname.h"
#include "storage/storage_driver.h"
#include "storage/storage_source.h"
#define VIR_FROM_THIS VIR_FROM_NONE