mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-28 16:45:26 +00:00
064fec69be
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>
925 lines
26 KiB
C
925 lines
26 KiB
C
/*
|
|
* storage_backend_fs.c: storage backend for FS and directory handling
|
|
*
|
|
* Copyright (C) 2007-2015 Red Hat, Inc.
|
|
* Copyright (C) 2007-2008 Daniel P. Berrange
|
|
*
|
|
* 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/>.
|
|
*
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#include <libxml/parser.h>
|
|
#include <libxml/tree.h>
|
|
#include <libxml/xpath.h>
|
|
|
|
#include "virerror.h"
|
|
#include "storage_backend_fs.h"
|
|
#include "storage_util.h"
|
|
#include "storage_conf.h"
|
|
#include "virstoragefilebackend.h"
|
|
#include "vircommand.h"
|
|
#include "viralloc.h"
|
|
#include "virxml.h"
|
|
#include "virfile.h"
|
|
#include "virlog.h"
|
|
#include "virstring.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
|
|
|
VIR_LOG_INIT("storage.storage_backend_fs");
|
|
|
|
#if WITH_STORAGE_FS
|
|
|
|
# include <mntent.h>
|
|
|
|
struct _virNetfsDiscoverState {
|
|
const char *host;
|
|
virStoragePoolSourceList list;
|
|
};
|
|
|
|
typedef struct _virNetfsDiscoverState virNetfsDiscoverState;
|
|
|
|
static int
|
|
virStorageBackendFileSystemNetFindPoolSourcesFunc(char **const groups,
|
|
void *data)
|
|
{
|
|
virNetfsDiscoverState *state = data;
|
|
const char *name, *path;
|
|
virStoragePoolSource *src = NULL;
|
|
int ret = -1;
|
|
|
|
path = groups[0];
|
|
|
|
if (!(name = strrchr(path, '/'))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("invalid netfs path (no /): %s"), path);
|
|
goto cleanup;
|
|
}
|
|
name += 1;
|
|
if (*name == '\0') {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("invalid netfs path (ends in /): %s"), path);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(src = virStoragePoolSourceListNewSource(&state->list)))
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC_N(src->hosts, 1) < 0)
|
|
goto cleanup;
|
|
src->nhost = 1;
|
|
|
|
if (VIR_STRDUP(src->hosts[0].name, state->host) < 0 ||
|
|
VIR_STRDUP(src->dir, path) < 0)
|
|
goto cleanup;
|
|
src->format = VIR_STORAGE_POOL_NETFS_NFS;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virStorageBackendFileSystemNetFindNFSPoolSources(virNetfsDiscoverState *state)
|
|
{
|
|
int ret = -1;
|
|
|
|
/*
|
|
* # showmount --no-headers -e HOSTNAME
|
|
* /tmp *
|
|
* /A dir demo1.foo.bar,demo2.foo.bar
|
|
*
|
|
* Extract directory name (including possible interior spaces ...).
|
|
*/
|
|
|
|
const char *regexes[] = {
|
|
"^(/.*\\S) +\\S+$"
|
|
};
|
|
int vars[] = {
|
|
1
|
|
};
|
|
|
|
virCommandPtr cmd = NULL;
|
|
|
|
cmd = virCommandNewArgList(SHOWMOUNT,
|
|
"--no-headers",
|
|
"--exports",
|
|
state->host,
|
|
NULL);
|
|
|
|
if (virCommandRunRegex(cmd, 1, regexes, vars,
|
|
virStorageBackendFileSystemNetFindPoolSourcesFunc,
|
|
state, NULL, NULL) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
virCommandFree(cmd);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static char *
|
|
virStorageBackendFileSystemNetFindPoolSources(const char *srcSpec,
|
|
unsigned int flags)
|
|
{
|
|
virNetfsDiscoverState state = {
|
|
.host = NULL,
|
|
.list = {
|
|
.type = VIR_STORAGE_POOL_NETFS,
|
|
.nsources = 0,
|
|
.sources = NULL
|
|
}
|
|
};
|
|
virStoragePoolSourcePtr source = NULL;
|
|
char *ret = NULL;
|
|
size_t i;
|
|
int retNFS = -1;
|
|
int retGluster = 0;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!srcSpec) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("hostname must be specified for netfs sources"));
|
|
return NULL;
|
|
}
|
|
|
|
if (!(source = virStoragePoolDefParseSourceString(srcSpec,
|
|
VIR_STORAGE_POOL_NETFS)))
|
|
return NULL;
|
|
|
|
if (source->nhost != 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Expected exactly 1 host for the storage pool"));
|
|
goto cleanup;
|
|
}
|
|
|
|
state.host = source->hosts[0].name;
|
|
|
|
retNFS = virStorageBackendFileSystemNetFindNFSPoolSources(&state);
|
|
|
|
retGluster = virStorageBackendFindGlusterPoolSources(state.host,
|
|
VIR_STORAGE_POOL_NETFS,
|
|
&state.list, false);
|
|
|
|
if (retGluster < 0)
|
|
goto cleanup;
|
|
|
|
/* If both fail, then we won't return an empty list - return an error */
|
|
if (retNFS < 0 && retGluster == 0) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("no storage pools were found on host '%s'"),
|
|
state.host);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(ret = virStoragePoolSourceListFormat(&state.list)))
|
|
goto cleanup;
|
|
|
|
cleanup:
|
|
for (i = 0; i < state.list.nsources; i++)
|
|
virStoragePoolSourceClear(&state.list.sources[i]);
|
|
VIR_FREE(state.list.sources);
|
|
|
|
virStoragePoolSourceFree(source);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @pool storage pool to check FS types
|
|
*
|
|
* Determine if storage pool FS types are properly set up
|
|
*
|
|
* Return 0 if everything's OK, -1 on error
|
|
*/
|
|
static int
|
|
virStorageBackendFileSystemIsValid(virStoragePoolObjPtr pool)
|
|
{
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
|
|
|
|
if (def->type == VIR_STORAGE_POOL_NETFS) {
|
|
if (def->source.nhost != 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("expected exactly 1 host for the storage pool"));
|
|
return -1;
|
|
}
|
|
if (def->source.hosts[0].name == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("missing source host"));
|
|
return -1;
|
|
}
|
|
if (def->source.dir == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("missing source path"));
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (def->source.ndevice != 1) {
|
|
if (def->source.ndevice == 0)
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing source device"));
|
|
else
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("expected exactly 1 device for the "
|
|
"storage pool"));
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* virStorageBackendFileSystemGetPoolSource
|
|
* @pool: storage pool object pointer
|
|
*
|
|
* Allocate/return a string representing the FS storage pool source.
|
|
* It is up to the caller to VIR_FREE the allocated string
|
|
*/
|
|
static char *
|
|
virStorageBackendFileSystemGetPoolSource(virStoragePoolObjPtr pool)
|
|
{
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
|
|
char *src = NULL;
|
|
|
|
if (def->type == VIR_STORAGE_POOL_NETFS) {
|
|
if (def->source.format == VIR_STORAGE_POOL_NETFS_CIFS) {
|
|
if (virAsprintf(&src, "//%s/%s",
|
|
def->source.hosts[0].name,
|
|
def->source.dir) < 0)
|
|
return NULL;
|
|
} else {
|
|
if (virAsprintf(&src, "%s:%s",
|
|
def->source.hosts[0].name,
|
|
def->source.dir) < 0)
|
|
return NULL;
|
|
}
|
|
} else {
|
|
if (VIR_STRDUP(src, def->source.devices[0].path) < 0)
|
|
return NULL;
|
|
}
|
|
return src;
|
|
}
|
|
|
|
|
|
/**
|
|
* @pool storage pool to check for status
|
|
*
|
|
* Determine if a storage pool is already mounted
|
|
*
|
|
* Return 0 if not mounted, 1 if mounted, -1 on error
|
|
*/
|
|
static int
|
|
virStorageBackendFileSystemIsMounted(virStoragePoolObjPtr pool)
|
|
{
|
|
int ret = -1;
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
|
|
char *src = NULL;
|
|
FILE *mtab;
|
|
struct mntent ent;
|
|
char buf[1024];
|
|
int rc1, rc2;
|
|
|
|
if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
|
|
virReportSystemError(errno,
|
|
_("cannot read mount list '%s'"),
|
|
_PATH_MOUNTED);
|
|
goto cleanup;
|
|
}
|
|
|
|
while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
|
|
if (!(src = virStorageBackendFileSystemGetPoolSource(pool)))
|
|
goto cleanup;
|
|
|
|
/* compare both mount destinations and sources to be sure the mounted
|
|
* FS pool is really the one we're looking for
|
|
*/
|
|
if ((rc1 = virFileComparePaths(ent.mnt_dir, def->target.path)) < 0 ||
|
|
(rc2 = virFileComparePaths(ent.mnt_fsname, src)) < 0)
|
|
goto cleanup;
|
|
|
|
if (rc1 && rc2) {
|
|
ret = 1;
|
|
goto cleanup;
|
|
}
|
|
|
|
VIR_FREE(src);
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
VIR_FORCE_FCLOSE(mtab);
|
|
VIR_FREE(src);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @pool storage pool to mount
|
|
*
|
|
* Ensure that a FS storage pool is mounted on its target location.
|
|
* If already mounted, this is a no-op
|
|
*
|
|
* Returns 0 if successfully mounted, -1 on error
|
|
*/
|
|
static int
|
|
virStorageBackendFileSystemMount(virStoragePoolObjPtr pool)
|
|
{
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
|
|
char *src = NULL;
|
|
/* 'mount -t auto' doesn't seem to auto determine nfs (or cifs),
|
|
* while plain 'mount' does. We have to craft separate argvs to
|
|
* accommodate this */
|
|
bool netauto = (def->type == VIR_STORAGE_POOL_NETFS &&
|
|
def->source.format == VIR_STORAGE_POOL_NETFS_AUTO);
|
|
bool glusterfs = (def->type == VIR_STORAGE_POOL_NETFS &&
|
|
def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS);
|
|
bool cifsfs = (def->type == VIR_STORAGE_POOL_NETFS &&
|
|
def->source.format == VIR_STORAGE_POOL_NETFS_CIFS);
|
|
virCommandPtr cmd = NULL;
|
|
int ret = -1;
|
|
int rc;
|
|
|
|
if (virStorageBackendFileSystemIsValid(pool) < 0)
|
|
return -1;
|
|
|
|
if ((rc = virStorageBackendFileSystemIsMounted(pool)) < 0)
|
|
return -1;
|
|
|
|
/* Short-circuit if already mounted */
|
|
if (rc == 1) {
|
|
VIR_INFO("Target '%s' is already mounted", def->target.path);
|
|
return 0;
|
|
}
|
|
|
|
if (!(src = virStorageBackendFileSystemGetPoolSource(pool)))
|
|
return -1;
|
|
|
|
if (netauto)
|
|
cmd = virCommandNewArgList(MOUNT,
|
|
src,
|
|
def->target.path,
|
|
NULL);
|
|
else if (glusterfs)
|
|
cmd = virCommandNewArgList(MOUNT,
|
|
"-t",
|
|
virStoragePoolFormatFileSystemNetTypeToString(def->source.format),
|
|
src,
|
|
"-o",
|
|
"direct-io-mode=1",
|
|
def->target.path,
|
|
NULL);
|
|
else if (cifsfs)
|
|
cmd = virCommandNewArgList(MOUNT,
|
|
"-t",
|
|
virStoragePoolFormatFileSystemNetTypeToString(def->source.format),
|
|
src,
|
|
def->target.path,
|
|
"-o",
|
|
"guest",
|
|
NULL);
|
|
else
|
|
cmd = virCommandNewArgList(MOUNT,
|
|
"-t",
|
|
(def->type == VIR_STORAGE_POOL_FS ?
|
|
virStoragePoolFormatFileSystemTypeToString(def->source.format) :
|
|
virStoragePoolFormatFileSystemNetTypeToString(def->source.format)),
|
|
src,
|
|
def->target.path,
|
|
NULL);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virCommandFree(cmd);
|
|
VIR_FREE(src);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @pool storage pool to start
|
|
*
|
|
* Starts a directory or FS based storage pool. The underlying source
|
|
* device will be mounted for FS based pools.
|
|
*
|
|
* Returns 0 on success, -1 on error
|
|
*/
|
|
static int
|
|
virStorageBackendFileSystemStart(virStoragePoolObjPtr pool)
|
|
{
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
|
|
|
|
if (def->type != VIR_STORAGE_POOL_DIR &&
|
|
virStorageBackendFileSystemMount(pool) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @pool storage pool to unmount
|
|
*
|
|
* Stops a file storage pool. The underlying source device is unmounted
|
|
* for FS based pools. Any cached data about volumes is released.
|
|
*
|
|
* Ensure that a FS storage pool is not mounted on its target location.
|
|
* If already unmounted, this is a no-op.
|
|
*
|
|
* Returns 0 if successfully unmounted, -1 on error
|
|
*/
|
|
static int
|
|
virStorageBackendFileSystemStop(virStoragePoolObjPtr pool)
|
|
{
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
|
|
virCommandPtr cmd = NULL;
|
|
int ret = -1;
|
|
int rc;
|
|
|
|
if (virStorageBackendFileSystemIsValid(pool) < 0)
|
|
return -1;
|
|
|
|
/* Short-circuit if already unmounted */
|
|
if ((rc = virStorageBackendFileSystemIsMounted(pool)) != 1)
|
|
return rc;
|
|
|
|
cmd = virCommandNewArgList(UMOUNT, def->target.path, NULL);
|
|
if (virCommandRun(cmd, NULL) < 0)
|
|
goto cleanup;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
virCommandFree(cmd);
|
|
return ret;
|
|
}
|
|
#endif /* WITH_STORAGE_FS */
|
|
|
|
|
|
static int
|
|
virStorageBackendFileSystemCheck(virStoragePoolObjPtr pool,
|
|
bool *isActive)
|
|
{
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
|
|
|
|
if (def->type == VIR_STORAGE_POOL_DIR) {
|
|
*isActive = virFileExists(def->target.path);
|
|
#if WITH_STORAGE_FS
|
|
} else {
|
|
int ret;
|
|
*isActive = false;
|
|
|
|
if (virStorageBackendFileSystemIsValid(pool) < 0)
|
|
return -1;
|
|
|
|
if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) {
|
|
if (ret < 0)
|
|
return -1;
|
|
*isActive = true;
|
|
}
|
|
#endif /* WITH_STORAGE_FS */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* some platforms don't support mkfs */
|
|
#ifdef MKFS
|
|
static int
|
|
virStorageBackendExecuteMKFS(const char *device,
|
|
const char *format)
|
|
{
|
|
int ret = 0;
|
|
virCommandPtr cmd = NULL;
|
|
|
|
cmd = virCommandNewArgList(MKFS, "-t", format, NULL);
|
|
|
|
/* use the force, otherwise mkfs.xfs won't overwrite existing fs.
|
|
* Similarly mkfs.ext2, mkfs.ext3, and mkfs.ext4 require supplying -F
|
|
* and mkfs.vfat uses -I */
|
|
if (STREQ(format, "xfs"))
|
|
virCommandAddArg(cmd, "-f");
|
|
else if (STREQ(format, "ext2") ||
|
|
STREQ(format, "ext3") ||
|
|
STREQ(format, "ext4"))
|
|
virCommandAddArg(cmd, "-F");
|
|
else if (STREQ(format, "vfat"))
|
|
virCommandAddArg(cmd, "-I");
|
|
|
|
virCommandAddArg(cmd, device);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
|
virReportSystemError(errno,
|
|
_("Failed to make filesystem of "
|
|
"type '%s' on device '%s'"),
|
|
format, device);
|
|
ret = -1;
|
|
}
|
|
|
|
virCommandFree(cmd);
|
|
return ret;
|
|
}
|
|
#else /* #ifdef MKFS */
|
|
static int
|
|
virStorageBackendExecuteMKFS(const char *device ATTRIBUTE_UNUSED,
|
|
const char *format ATTRIBUTE_UNUSED)
|
|
{
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("mkfs is not supported on this platform: "
|
|
"Failed to make filesystem of "
|
|
"type '%s' on device '%s'"),
|
|
format, device);
|
|
return -1;
|
|
}
|
|
#endif /* #ifdef MKFS */
|
|
|
|
static int
|
|
virStorageBackendMakeFileSystem(virStoragePoolObjPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
|
|
const char *device = NULL, *format = NULL;
|
|
bool ok_to_mkfs = false;
|
|
int ret = -1;
|
|
|
|
if (def->source.devices == NULL) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("No source device specified when formatting pool '%s'"),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
|
|
device = def->source.devices[0].path;
|
|
format = virStoragePoolFormatFileSystemTypeToString(def->source.format);
|
|
VIR_DEBUG("source device: '%s' format: '%s'", device, format);
|
|
|
|
if (!virFileExists(device)) {
|
|
virReportError(VIR_ERR_OPERATION_INVALID,
|
|
_("Source device does not exist when formatting pool '%s'"),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
|
|
if (flags & VIR_STORAGE_POOL_BUILD_OVERWRITE) {
|
|
ok_to_mkfs = true;
|
|
} else if (flags & VIR_STORAGE_POOL_BUILD_NO_OVERWRITE &&
|
|
virStorageBackendDeviceIsEmpty(device, format, true)) {
|
|
ok_to_mkfs = true;
|
|
}
|
|
|
|
if (ok_to_mkfs)
|
|
ret = virStorageBackendExecuteMKFS(device, format);
|
|
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @pool storage pool to build
|
|
* @flags controls the pool formatting behaviour
|
|
*
|
|
* Build a directory or FS based storage pool.
|
|
*
|
|
* If no flag is set, it only makes the directory.
|
|
*
|
|
* If VIR_STORAGE_POOL_BUILD_NO_OVERWRITE set, it probes to determine if
|
|
* any filesystem already exists on the target device, returning an error
|
|
* if one exists. If no filesystem already exists, use mkfs to format the
|
|
* target device.
|
|
*
|
|
* If VIR_STORAGE_POOL_BUILD_OVERWRITE is set, mkfs is always executed and
|
|
* any existing data on the target device is overwritten unconditionally.
|
|
*
|
|
* The underlying source device is mounted for FS based pools.
|
|
*
|
|
* Returns 0 on success, -1 on error
|
|
*/
|
|
static int
|
|
virStorageBackendFileSystemBuild(virStoragePoolObjPtr pool,
|
|
unsigned int flags)
|
|
{
|
|
virCheckFlags(VIR_STORAGE_POOL_BUILD_OVERWRITE |
|
|
VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, -1);
|
|
|
|
VIR_EXCLUSIVE_FLAGS_RET(VIR_STORAGE_POOL_BUILD_OVERWRITE,
|
|
VIR_STORAGE_POOL_BUILD_NO_OVERWRITE,
|
|
-1);
|
|
|
|
if (virStorageBackendBuildLocal(pool) < 0)
|
|
return -1;
|
|
|
|
if (flags != 0)
|
|
return virStorageBackendMakeFileSystem(pool, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
virStorageBackend virStorageBackendDirectory = {
|
|
.type = VIR_STORAGE_POOL_DIR,
|
|
|
|
.buildPool = virStorageBackendFileSystemBuild,
|
|
.checkPool = virStorageBackendFileSystemCheck,
|
|
.refreshPool = virStorageBackendRefreshLocal,
|
|
.deletePool = virStorageBackendDeleteLocal,
|
|
.buildVol = virStorageBackendVolBuildLocal,
|
|
.buildVolFrom = virStorageBackendVolBuildFromLocal,
|
|
.createVol = virStorageBackendVolCreateLocal,
|
|
.refreshVol = virStorageBackendVolRefreshLocal,
|
|
.deleteVol = virStorageBackendVolDeleteLocal,
|
|
.resizeVol = virStorageBackendVolResizeLocal,
|
|
.uploadVol = virStorageBackendVolUploadLocal,
|
|
.downloadVol = virStorageBackendVolDownloadLocal,
|
|
.wipeVol = virStorageBackendVolWipeLocal,
|
|
};
|
|
|
|
#if WITH_STORAGE_FS
|
|
virStorageBackend virStorageBackendFileSystem = {
|
|
.type = VIR_STORAGE_POOL_FS,
|
|
|
|
.buildPool = virStorageBackendFileSystemBuild,
|
|
.checkPool = virStorageBackendFileSystemCheck,
|
|
.startPool = virStorageBackendFileSystemStart,
|
|
.refreshPool = virStorageBackendRefreshLocal,
|
|
.stopPool = virStorageBackendFileSystemStop,
|
|
.deletePool = virStorageBackendDeleteLocal,
|
|
.buildVol = virStorageBackendVolBuildLocal,
|
|
.buildVolFrom = virStorageBackendVolBuildFromLocal,
|
|
.createVol = virStorageBackendVolCreateLocal,
|
|
.refreshVol = virStorageBackendVolRefreshLocal,
|
|
.deleteVol = virStorageBackendVolDeleteLocal,
|
|
.resizeVol = virStorageBackendVolResizeLocal,
|
|
.uploadVol = virStorageBackendVolUploadLocal,
|
|
.downloadVol = virStorageBackendVolDownloadLocal,
|
|
.wipeVol = virStorageBackendVolWipeLocal,
|
|
};
|
|
virStorageBackend virStorageBackendNetFileSystem = {
|
|
.type = VIR_STORAGE_POOL_NETFS,
|
|
|
|
.buildPool = virStorageBackendFileSystemBuild,
|
|
.checkPool = virStorageBackendFileSystemCheck,
|
|
.startPool = virStorageBackendFileSystemStart,
|
|
.findPoolSources = virStorageBackendFileSystemNetFindPoolSources,
|
|
.refreshPool = virStorageBackendRefreshLocal,
|
|
.stopPool = virStorageBackendFileSystemStop,
|
|
.deletePool = virStorageBackendDeleteLocal,
|
|
.buildVol = virStorageBackendVolBuildLocal,
|
|
.buildVolFrom = virStorageBackendVolBuildFromLocal,
|
|
.createVol = virStorageBackendVolCreateLocal,
|
|
.refreshVol = virStorageBackendVolRefreshLocal,
|
|
.deleteVol = virStorageBackendVolDeleteLocal,
|
|
.resizeVol = virStorageBackendVolResizeLocal,
|
|
.uploadVol = virStorageBackendVolUploadLocal,
|
|
.downloadVol = virStorageBackendVolDownloadLocal,
|
|
.wipeVol = virStorageBackendVolWipeLocal,
|
|
};
|
|
#endif /* WITH_STORAGE_FS */
|
|
|
|
|
|
typedef struct _virStorageFileBackendFsPriv virStorageFileBackendFsPriv;
|
|
typedef virStorageFileBackendFsPriv *virStorageFileBackendFsPrivPtr;
|
|
|
|
struct _virStorageFileBackendFsPriv {
|
|
char *canonpath; /* unique file identifier (canonical path) */
|
|
};
|
|
|
|
|
|
static void
|
|
virStorageFileBackendFileDeinit(virStorageSourcePtr src)
|
|
{
|
|
VIR_DEBUG("deinitializing FS storage file %p (%s:%s)", src,
|
|
virStorageTypeToString(virStorageSourceGetActualType(src)),
|
|
src->path);
|
|
|
|
virStorageFileBackendFsPrivPtr priv = src->drv->priv;
|
|
|
|
VIR_FREE(priv->canonpath);
|
|
VIR_FREE(priv);
|
|
}
|
|
|
|
|
|
static int
|
|
virStorageFileBackendFileInit(virStorageSourcePtr src)
|
|
{
|
|
virStorageFileBackendFsPrivPtr priv = NULL;
|
|
|
|
VIR_DEBUG("initializing FS storage file %p (%s:%s)[%u:%u]", src,
|
|
virStorageTypeToString(virStorageSourceGetActualType(src)),
|
|
src->path,
|
|
(unsigned int)src->drv->uid, (unsigned int)src->drv->gid);
|
|
|
|
if (VIR_ALLOC(priv) < 0)
|
|
return -1;
|
|
|
|
src->drv->priv = priv;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virStorageFileBackendFileCreate(virStorageSourcePtr src)
|
|
{
|
|
int fd = -1;
|
|
mode_t mode = S_IRUSR;
|
|
|
|
if (!src->readonly)
|
|
mode |= S_IWUSR;
|
|
|
|
if ((fd = virFileOpenAs(src->path, O_WRONLY | O_TRUNC | O_CREAT, mode,
|
|
src->drv->uid, src->drv->gid, 0)) < 0) {
|
|
errno = -fd;
|
|
return -1;
|
|
}
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
virStorageFileBackendFileUnlink(virStorageSourcePtr src)
|
|
{
|
|
return unlink(src->path);
|
|
}
|
|
|
|
|
|
static int
|
|
virStorageFileBackendFileStat(virStorageSourcePtr src,
|
|
struct stat *st)
|
|
{
|
|
return stat(src->path, st);
|
|
}
|
|
|
|
|
|
static ssize_t
|
|
virStorageFileBackendFileRead(virStorageSourcePtr src,
|
|
size_t offset,
|
|
size_t 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 (offset > 0) {
|
|
if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
|
|
virReportSystemError(errno, _("cannot seek into '%s'"), src->path);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if ((ret = virFileReadHeaderFD(fd, len, buf)) < 0) {
|
|
virReportSystemError(errno,
|
|
_("cannot read header '%s'"), src->path);
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static const char *
|
|
virStorageFileBackendFileGetUniqueIdentifier(virStorageSourcePtr src)
|
|
{
|
|
virStorageFileBackendFsPrivPtr priv = src->drv->priv;
|
|
|
|
if (!priv->canonpath) {
|
|
if (!(priv->canonpath = canonicalize_file_name(src->path))) {
|
|
virReportSystemError(errno, _("can't canonicalize path '%s'"),
|
|
src->path);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return priv->canonpath;
|
|
}
|
|
|
|
|
|
static int
|
|
virStorageFileBackendFileAccess(virStorageSourcePtr src,
|
|
int mode)
|
|
{
|
|
return virFileAccessibleAs(src->path, mode,
|
|
src->drv->uid, src->drv->gid);
|
|
}
|
|
|
|
|
|
static int
|
|
virStorageFileBackendFileChown(const virStorageSource *src,
|
|
uid_t uid,
|
|
gid_t gid)
|
|
{
|
|
return chown(src->path, uid, gid);
|
|
}
|
|
|
|
|
|
virStorageFileBackend virStorageFileBackendFile = {
|
|
.type = VIR_STORAGE_TYPE_FILE,
|
|
|
|
.backendInit = virStorageFileBackendFileInit,
|
|
.backendDeinit = virStorageFileBackendFileDeinit,
|
|
|
|
.storageFileCreate = virStorageFileBackendFileCreate,
|
|
.storageFileUnlink = virStorageFileBackendFileUnlink,
|
|
.storageFileStat = virStorageFileBackendFileStat,
|
|
.storageFileRead = virStorageFileBackendFileRead,
|
|
.storageFileAccess = virStorageFileBackendFileAccess,
|
|
.storageFileChown = virStorageFileBackendFileChown,
|
|
|
|
.storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier,
|
|
};
|
|
|
|
|
|
virStorageFileBackend virStorageFileBackendBlock = {
|
|
.type = VIR_STORAGE_TYPE_BLOCK,
|
|
|
|
.backendInit = virStorageFileBackendFileInit,
|
|
.backendDeinit = virStorageFileBackendFileDeinit,
|
|
|
|
.storageFileStat = virStorageFileBackendFileStat,
|
|
.storageFileRead = virStorageFileBackendFileRead,
|
|
.storageFileAccess = virStorageFileBackendFileAccess,
|
|
.storageFileChown = virStorageFileBackendFileChown,
|
|
|
|
.storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier,
|
|
};
|
|
|
|
|
|
virStorageFileBackend virStorageFileBackendDir = {
|
|
.type = VIR_STORAGE_TYPE_DIR,
|
|
|
|
.backendInit = virStorageFileBackendFileInit,
|
|
.backendDeinit = virStorageFileBackendFileDeinit,
|
|
|
|
.storageFileAccess = virStorageFileBackendFileAccess,
|
|
.storageFileChown = virStorageFileBackendFileChown,
|
|
|
|
.storageFileGetUniqueIdentifier = virStorageFileBackendFileGetUniqueIdentifier,
|
|
};
|
|
|
|
|
|
int
|
|
virStorageBackendFsRegister(void)
|
|
{
|
|
if (virStorageBackendRegister(&virStorageBackendDirectory) < 0)
|
|
return -1;
|
|
|
|
#if WITH_STORAGE_FS
|
|
if (virStorageBackendRegister(&virStorageBackendFileSystem) < 0)
|
|
return -1;
|
|
|
|
if (virStorageBackendRegister(&virStorageBackendNetFileSystem) < 0)
|
|
return -1;
|
|
#endif /* WITH_STORAGE_FS */
|
|
|
|
if (virStorageFileBackendRegister(&virStorageFileBackendFile) < 0)
|
|
return -1;
|
|
|
|
if (virStorageFileBackendRegister(&virStorageFileBackendBlock) < 0)
|
|
return -1;
|
|
|
|
if (virStorageFileBackendRegister(&virStorageFileBackendDir) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|