From: Olga Krishtal <okrishtal(a)virtuozzo.com>
Implementation is backend base as in case of storage pools.
This patch adds directory backend which is nothing more
than managing directories inside another one as starting
point.
Signed-off-by: Nikolay Shirokovskiy <nshirokovskiy(a)virtuozzo.com>
---
configure.ac | 33 +
daemon/Makefile.am | 4 +
po/POTFILES.in | 2 +
src/Makefile.am | 38 +
src/driver.h | 1 +
src/fs/fs_backend.h | 85 ++
src/fs/fs_backend_dir.c | 334 +++++++
src/fs/fs_backend_dir.h | 8 +
src/fs/fs_driver.c | 2164 ++++++++++++++++++++++++++++++++++++++++++++++
src/fs/fs_driver.h | 10 +
src/libvirt.c | 28 +
src/libvirt_private.syms | 1 +
12 files changed, 2708 insertions(+)
create mode 100644 src/fs/fs_backend.h
create mode 100644 src/fs/fs_backend_dir.c
create mode 100644 src/fs/fs_backend_dir.h
create mode 100644 src/fs/fs_driver.c
create mode 100644 src/fs/fs_driver.h
diff --git a/configure.ac b/configure.ac
index 2c81c95..36dc6b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1647,6 +1647,35 @@ fi
AM_CONDITIONAL([WITH_SECRETS], [test "$with_secrets" = "yes"])
+AC_ARG_WITH([fs-dir],
+ [AS_HELP_STRING([--with-fs-dir],
+ [with fs backend for fs driver @<:@default=yes@:>])],
+ [],[with_fs_dir=yes])
+
+if test "$with_libvirtd" = "no"; then
+ with_fs_dir=no
+fi
+
+if test "$with_fs_dir" = "yes" ; then
+ AC_DEFINE_UNQUOTED([WITH_FS_DIR], 1, [whether directory backend for fs driver is
enabled])
+fi
+AM_CONDITIONAL([WITH_FS_DIR], [test "$with_fs_dir" = "yes"])
+
+with_fs=no
+for backend in dir; do
+ if eval test \$with_fs_$backend = yes; then
+ with_fs=yes
+ break
+ fi
+done
+if test $with_fs = yes; then
+ AC_DEFINE([WITH_FS], [1],
+ [Define to 1 if at least one fs backend is in use])
+fi
+AM_CONDITIONAL([WITH_FS], [test "$with_fs" = "yes"])
+
+
+
AC_ARG_WITH([storage-dir],
[AS_HELP_STRING([--with-storage-dir],
[with directory backend for the storage driver @<:@default=yes@:>@])],
@@ -2760,6 +2789,10 @@ AC_MSG_NOTICE([Sheepdog: $with_storage_sheepdog])
AC_MSG_NOTICE([ Gluster: $with_storage_gluster])
AC_MSG_NOTICE([ ZFS: $with_storage_zfs])
AC_MSG_NOTICE([])
+AC_MSG_NOTICE([Fs Drivers])
+AC_MSG_NOTICE([])
+AC_MSG_NOTICE([ Dir: $with_fs_dir])
+AC_MSG_NOTICE([])
AC_MSG_NOTICE([Security Drivers])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ SELinux: $with_secdriver_selinux ($SELINUX_MOUNT)])
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 927d16f..63444cf 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -241,6 +241,10 @@ if WITH_STORAGE
libvirtd_LDADD += ../src/libvirt_driver_storage.la
endif WITH_STORAGE
+if WITH_FS
+ libvirtd_LDADD += ../src/libvirt_driver_fs.la
+endif WITH_FS
+
if WITH_NETWORK
libvirtd_LDADD += ../src/libvirt_driver_network.la
endif WITH_NETWORK
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 313edf2..8b3cfd5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -57,6 +57,8 @@ src/esx/esx_vi.c
src/esx/esx_vi_methods.c
src/esx/esx_vi_types.c
src/fdstream.c
+src/fs/fs_backend_dir.c
+src/fs/fs_driver.c
src/hyperv/hyperv_driver.c
src/hyperv/hyperv_util.c
src/hyperv/hyperv_wmi.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 77e64f4..768e4a7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -638,6 +638,7 @@ DRIVER_SOURCE_FILES = \
$(REMOTE_DRIVER_SOURCES) \
$(SECRET_DRIVER_SOURCES) \
$(STORAGE_DRIVER_SOURCES) \
+ $(FS_DRIVER_SOURCES) \
$(TEST_DRIVER_SOURCES) \
$(UML_DRIVER_SOURCES) \
$(VBOX_DRIVER_SOURCES) \
@@ -658,6 +659,7 @@ STATEFUL_DRIVER_SOURCE_FILES = \
$(QEMU_DRIVER_SOURCES) \
$(SECRET_DRIVER_SOURCES) \
$(STORAGE_DRIVER_SOURCES) \
+ $(FS_DRIVER_SOURCES) \
$(UML_DRIVER_SOURCES) \
$(XEN_DRIVER_SOURCES) \
$(NULL)
@@ -962,6 +964,14 @@ SECRET_UTIL_SOURCES = \
SECRET_DRIVER_SOURCES = \
secret/secret_driver.h secret/secret_driver.c
+# FS pool backend specific impls
+FS_DRIVER_SOURCES = \
+ fs/fs_driver.h fs/fs_driver.c \
+ fs/fs_backend.h
+
+FS_DRIVER_DIR_SOURCES = \
+ fs/fs_backend_dir.h fs/fs_backend_dir.c
+
# Storage backend specific impls
STORAGE_DRIVER_SOURCES = \
storage/storage_driver.h storage/storage_driver.c \
@@ -1625,6 +1635,32 @@ endif WITH_DRIVER_MODULES
libvirt_driver_secret_la_SOURCES = $(SECRET_DRIVER_SOURCES)
endif WITH_SECRETS
+libvirt_driver_fs_impl_la_SOURCES =
+libvirt_driver_fs_impl_la_CFLAGS = \
+ -I$(srcdir)/access \
+ -I$(srcdir)/conf \
+ $(AM_CFLAGS)
+libvirt_driver_fs_impl_la_LDFLAGS = $(AM_LDFLAGS)
+libvirt_driver_fs_impl_la_LIBADD =
+libvirt_driver_fs_impl_la_LIBADD += $(SECDRIVER_LIBS) $(LIBXML_LIBS)
+if WITH_FS
+noinst_LTLIBRARIES += libvirt_driver_fs_impl.la
+libvirt_driver_fs_la_SOURCES =
+libvirt_driver_fs_la_LIBADD = libvirt_driver_fs_impl.la
+if WITH_DRIVER_MODULES
+mod_LTLIBRARIES += libvirt_driver_fs.la
+libvirt_driver_fs_la_LIBADD += ../gnulib/lib/libgnu.la
+libvirt_driver_fs_la_LDFLAGS = -module -avoid-version $(AM_LDFLAGS)
+else ! WITH_DRIVER_MODULES
+noinst_LTLIBRARIES += libvirt_driver_fs.la
+# Stateful, so linked to daemon instead
+#libvirt_la_BUILT_LIBADD += libvirt_driver_fs.la
+endif ! WITH_DRIVER_MODULES
+libvirt_driver_fs_impl_la_SOURCES += $(FS_DRIVER_SOURCES)
+libvirt_driver_fs_impl_la_SOURCES += $(FS_DRIVER_DIR_SOURCES)
+endif WITH_FS
+
+
# Needed to keep automake quiet about conditionals
libvirt_driver_storage_impl_la_SOURCES =
libvirt_driver_storage_impl_la_CFLAGS = \
@@ -1896,6 +1932,8 @@ EXTRA_DIST += \
$(BHYVE_DRIVER_SOURCES) \
$(NETWORK_DRIVER_SOURCES) \
$(INTERFACE_DRIVER_SOURCES) \
+ $(FS_DRIVER_SOURCES) \
+ $(FS_DRIVER_DIR_SOURCES) \
$(STORAGE_DRIVER_SOURCES) \
$(STORAGE_DRIVER_FS_SOURCES) \
$(STORAGE_DRIVER_LVM_SOURCES) \
diff --git a/src/driver.h b/src/driver.h
index 57ad1f7..6d475f9 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -100,6 +100,7 @@ int virSetSharedNodeDeviceDriver(virNodeDeviceDriverPtr driver)
ATTRIBUTE_RETURN
int virSetSharedNWFilterDriver(virNWFilterDriverPtr driver) ATTRIBUTE_RETURN_CHECK;
int virSetSharedSecretDriver(virSecretDriverPtr driver) ATTRIBUTE_RETURN_CHECK;
int virSetSharedStorageDriver(virStorageDriverPtr driver) ATTRIBUTE_RETURN_CHECK;
+int virSetSharedFsDriver(virFsDriverPtr driver) ATTRIBUTE_RETURN_CHECK;
void *virDriverLoadModule(const char *name);
diff --git a/src/fs/fs_backend.h b/src/fs/fs_backend.h
new file mode 100644
index 0000000..f714b29
--- /dev/null
+++ b/src/fs/fs_backend.h
@@ -0,0 +1,85 @@
+#ifndef __VIR_FS_BACKEND_H__
+# define __VIR_FS_BACKEND_H__
+
+# include <sys/stat.h>
+
+# include "internal.h"
+# include "fs_conf.h"
+# include "fs_driver.h"
+
+typedef char * (*virFsBackendFindFspoolSources)(virConnectPtr conn,
+ const char *srcSpec,
+ unsigned int flags);
+typedef int (*virFsBackendCheckFspool)(virFsPoolObjPtr fspool,
+ bool *active);
+typedef int (*virFsBackendStartFspool)(virConnectPtr conn,
+ virFsPoolObjPtr fspool);
+typedef int (*virFsBackendBuildFspool)(virConnectPtr conn,
+ virFsPoolObjPtr fspool,
+ unsigned int flags);
+typedef int (*virFsBackendRefreshFspool)(virConnectPtr conn,
+ virFsPoolObjPtr fspool);
+typedef int (*virFsBackendStopFspool)(virConnectPtr conn,
+ virFsPoolObjPtr fspool);
+typedef int (*virFsBackendDeleteFspool)(virConnectPtr conn,
+ virFsPoolObjPtr fspool,
+ unsigned int flags);
+
+/* FIXME */
+
+/* A 'buildItem' backend must remove any volume created on error since
+ * the storage driver does not distinguish whether the failure is due
+ * to failure to create the volume, to reserve any space necessary for
+ * the volume, to get data about the volume, to change it's accessibility,
+ * etc. This avoids issues arising from a creation failure due to some
+ * external action which created a volume of the same name that libvirt
+ * was not aware of between checking the fspool and the create attempt. It
+ * also avoids extra round trips to just delete a file.
+ */
+typedef int (*virFsBackendBuildItem)(virConnectPtr conn,
+ virFsPoolObjPtr fspool,
+ virFsItemDefPtr item,
+ unsigned int flags);
+typedef int (*virFsBackendCreateItem)(virConnectPtr conn,
+ virFsPoolObjPtr fspool,
+ virFsItemDefPtr item);
+typedef int (*virFsBackendRefreshItem)(virConnectPtr conn,
+ virFsPoolObjPtr fspool,
+ virFsItemDefPtr item);
+typedef int (*virFsBackendDeleteItem)(virConnectPtr conn,
+ virFsPoolObjPtr fspool,
+ virFsItemDefPtr item,
+ unsigned int flags);
+typedef int (*virFsBackendBuildItemFrom)(virConnectPtr conn,
+ virFsPoolObjPtr fspool,
+ virFsItemDefPtr origitem,
+ virFsItemDefPtr newitem,
+ unsigned int flags);
+
+typedef struct _virFsBackend virFsBackend;
+typedef virFsBackend *virFsBackendPtr;
+
+/* Callbacks are optional unless documented otherwise; but adding more
+ * callbacks provides better fspool support. */
+struct _virFsBackend {
+ int type;
+
+ virFsBackendFindFspoolSources findFspoolSources;
+ virFsBackendCheckFspool checkFspool;
+ virFsBackendStartFspool startFspool;
+ virFsBackendBuildFspool buildFspool;
+ virFsBackendRefreshFspool refreshFspool; /* Must be non-NULL */
+ virFsBackendStopFspool stopFspool;
+ virFsBackendDeleteFspool deleteFspool;
+
+ virFsBackendBuildItem buildItem;
+ virFsBackendBuildItemFrom buildItemFrom;
+ virFsBackendCreateItem createItem;
+ virFsBackendRefreshItem refreshItem;
+ virFsBackendDeleteItem deleteItem;
+};
+
+# define VIR_FS_DEFAULT_POOL_PERM_MODE 0755
+# define VIR_FS_DEFAULT_ITEM_PERM_MODE 0600
+
+#endif /* __VIR_FS_BACKEND_H__ */
diff --git a/src/fs/fs_backend_dir.c b/src/fs/fs_backend_dir.c
new file mode 100644
index 0000000..fc08b85
--- /dev/null
+++ b/src/fs/fs_backend_dir.c
@@ -0,0 +1,334 @@
+#include <config.h>
+
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <dirent.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 "fs_backend_dir.h"
+#include "fs_conf.h"
+#include "vircommand.h"
+#include "viralloc.h"
+#include "virxml.h"
+#include "virfile.h"
+#include "virlog.h"
+#include "virstring.h"
+#include "fdstream.h"
+#include "stat-time.h"
+
+#define VIR_FROM_THIS VIR_FROM_FSPOOL
+
+VIR_LOG_INIT("fs.fs_backend_dir");
+
+static int
+virFsDirBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virFsPoolObjPtr fspool,
+ unsigned int flags)
+{
+ int ret = -1;
+ char *parent = NULL;
+ char *p = NULL;
+ mode_t mode;
+ unsigned int dir_create_flags;
+
+ virCheckFlags(0, -1);
+
+ if (VIR_STRDUP(parent, fspool->def->target.path) < 0)
+ goto error;
+ if (!(p = strrchr(parent, '/'))) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("path '%s' is not absolute"),
+ fspool->def->target.path);
+ goto error;
+ }
+
+ if (p != parent) {
+ /* assure all directories in the path prior to the final dir
+ * exist, with default uid/gid/mode. */
+ *p = '\0';
+ if (virFileMakePath(parent) < 0) {
+ virReportSystemError(errno, _("cannot create path '%s'"),
+ parent);
+ goto error;
+ }
+ }
+
+ dir_create_flags = VIR_DIR_CREATE_ALLOW_EXIST;
+ mode = fspool->def->target.perms.mode;
+
+ if (mode == (mode_t) -1 &&
+ (!virFileExists(fspool->def->target.path)))
+ mode = VIR_FS_DEFAULT_POOL_PERM_MODE;
+
+ /* Now create the final dir in the path with the uid/gid/mode
+ * requested in the config. If the dir already exists, just set
+ * the perms. */
+ if (virDirCreate(fspool->def->target.path,
+ mode,
+ fspool->def->target.perms.uid,
+ fspool->def->target.perms.gid,
+ dir_create_flags) < 0)
+ goto error;
+
+ ret = 0;
+
+ error:
+ VIR_FREE(parent);
+ return ret;
+}
+
+static int
+virFsDirRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virFsPoolObjPtr fspool)
+{
+ DIR *dir;
+ struct dirent *entry;
+ virFsItemDefPtr item = NULL;
+ struct statvfs sb;
+ struct stat statbuf;
+ int fd = 0;
+ int ret = -1;
+
+ if (virDirOpen(&dir, fspool->def->target.path) < 0)
+ goto cleanup;
+
+ while (virDirRead(dir, &entry, fspool->def->target.path) > 0) {
+ if (virStringHasControlChars(entry->d_name)) {
+ VIR_WARN("Ignoring control characters under '%s'",
+ fspool->def->target.path);
+ continue;
+ }
+
+ if (VIR_ALLOC(item) < 0)
+ goto cleanup;
+
+ if (VIR_STRDUP(item->name, entry->d_name) < 0)
+ goto cleanup;
+ item->type = VIR_FS_ITEM_DIR;
+ if (virAsprintf(&item->target.path, "%s/%s",
+ fspool->def->target.path,
+ item->name) == -1)
+ goto cleanup;
+
+ if (VIR_STRDUP(item->key, item->target.path) < 0)
+ goto cleanup;
+
+
+ if (VIR_APPEND_ELEMENT(fspool->items.objs, fspool->items.count, item) <
0)
+ goto cleanup;
+ }
+
+
+ if ((fd = open(fspool->def->target.path, O_RDONLY)) < 0) {
+ virReportSystemError(errno,
+ _("cannot open path '%s'"),
+ fspool->def->target.path);
+ goto cleanup;
+ }
+
+ if (fstat(fd, &statbuf) < 0) {
+ virReportSystemError(errno,
+ _("cannot stat path '%s'"),
+ fspool->def->target.path);
+ goto cleanup;
+ }
+
+ fspool->def->target.perms.mode = statbuf.st_mode & S_IRWXUGO;
+ fspool->def->target.perms.uid = statbuf.st_uid;
+ fspool->def->target.perms.gid = statbuf.st_gid;
+
+ if (statvfs(fspool->def->target.path, &sb) < 0) {
+ virReportSystemError(errno,
+ _("cannot statvfs path '%s'"),
+ fspool->def->target.path);
+ goto cleanup;
+ }
+
+ fspool->def->capacity = ((unsigned long long)sb.f_blocks *
+ (unsigned long long)sb.f_frsize);
+ fspool->def->available = ((unsigned long long)sb.f_bfree *
+ (unsigned long long)sb.f_frsize);
+ fspool->def->allocation = fspool->def->capacity -
fspool->def->available;
+
+ ret = 0;
+
+ cleanup:
+ VIR_DIR_CLOSE(dir);
+ VIR_FORCE_CLOSE(fd);
+ virFsItemDefFree(item);
+ if (ret < 0)
+ virFsPoolObjClearItems(fspool);
+ return ret;
+}
+
+static int
+virFsDirDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virFsPoolObjPtr fspool,
+ unsigned int flags)
+{
+ virCheckFlags(0, -1);
+
+ if (rmdir(fspool->def->target.path) < 0) {
+ virReportSystemError(errno, _("failed to remove fspool '%s'"),
+ fspool->def->target.path);
+ return -1;
+ }
+
+ return 0;
+
+}
+static int
+virFsDirItemBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virFsPoolObjPtr fspool ATTRIBUTE_UNUSED,
+ virFsItemDefPtr item,
+ unsigned int flags)
+{
+ virCheckFlags(0, -1);
+
+ if (item->type == VIR_FS_ITEM_DIR) {
+ if ((virDirCreate(item->target.path,
+ (item->target.perms->mode == (mode_t) -1 ?
+ VIR_FS_DEFAULT_ITEM_PERM_MODE:
+ item->target.perms->mode),
+ item->target.perms->uid,
+ item->target.perms->gid,
+ 0)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("error creating item"));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+virFsDirItemBuildFrom(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virFsPoolObjPtr fspool ATTRIBUTE_UNUSED,
+ virFsItemDefPtr item,
+ virFsItemDefPtr inputitem,
+ unsigned int flags)
+{
+ virCommandPtr cmd = NULL;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ item->target.capacity = inputitem->target.capacity;
+ cmd = virCommandNewArgList("cp", "-r",
inputitem->target.path,
+ item->target.path, NULL);
+ ret = virCommandRun(cmd, NULL);
+
+ virCommandFree(cmd);
+ return ret;
+}
+
+static int
+virFsDirItemCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virFsPoolObjPtr fspool,
+ virFsItemDefPtr item)
+{
+ if (strchr(item->name, '/')) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("volume name '%s' cannot contain
'/'"), item->name);
+ return -1;
+ }
+
+ VIR_FREE(item->target.path);
+ if (virAsprintf(&item->target.path, "%s/%s",
+ fspool->def->target.path,
+ item->name) == -1)
+ return -1;
+
+ if (virFileExists(item->target.path)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("item target path '%s' already exists"),
+ item->target.path);
+ return -1;
+ }
+
+ VIR_FREE(item->key);
+ return VIR_STRDUP(item->key, item->target.path);
+}
+
+
+static int
+virFsDirItemRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virFsPoolObjPtr fspool ATTRIBUTE_UNUSED,
+ virFsItemDefPtr item)
+{
+ int fd;
+ int ret = -1;
+ struct stat statbuf;
+ virCommandPtr cmd = NULL;
+ char *output = NULL, *end;
+
+ if ((fd = open(item->target.path, O_RDONLY)) < 0) {
+ virReportSystemError(errno, _("cannot open directory '%s'"),
+ item->target.path);
+ return -1;
+ }
+ if (fstat(fd, &statbuf) < 0) {
+ virReportSystemError(errno, _("cannot stat path '%s'"),
+ item->target.path);
+ goto cleanup;
+ }
+
+ cmd = virCommandNewArgList("du", "-sB1", item->target.path,
NULL);
+ virCommandSetOutputBuffer(cmd, &output);
+ if ((ret = virCommandRun(cmd, NULL)) < 0)
+ goto cleanup;
+
+ if (virStrToLong_ull(output, &end, 10, &item->target.allocation) < 0)
{
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Malformed du output: %s"), output);
+ goto cleanup;
+ }
+
+ if (&(item->target.perms) && VIR_ALLOC(*(&item->target.perms))
< 0)
+ goto cleanup;
+ item->target.perms->mode = statbuf.st_mode & S_IRWXUGO;
+ item->target.perms->uid = statbuf.st_uid;
+ item->target.perms->gid = statbuf.st_gid;
+
+ ret = 0;
+ cleanup:
+ VIR_FORCE_CLOSE(fd);
+ VIR_FREE(output);
+ virCommandFree(cmd);
+ return ret;
+}
+
+static int
+virFsDirItemDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virFsPoolObjPtr fspool ATTRIBUTE_UNUSED,
+ virFsItemDefPtr item,
+ unsigned int flags)
+{
+ virCheckFlags(0, -1);
+
+ return virFileDeleteTree(item->target.path);
+}
+
+virFsBackend virFsBackendDir = {
+ .type = VIR_FS_POOL_DIR,
+
+ .buildFspool = virFsDirBuild,
+ .refreshFspool = virFsDirRefresh,
+ .deleteFspool = virFsDirDelete,
+ .buildItem = virFsDirItemBuild,
+ .buildItemFrom = virFsDirItemBuildFrom,
+ .createItem = virFsDirItemCreate,
+ .deleteItem = virFsDirItemDelete,
+ .refreshItem = virFsDirItemRefresh,
+};
diff --git a/src/fs/fs_backend_dir.h b/src/fs/fs_backend_dir.h
new file mode 100644
index 0000000..d8ff68f
--- /dev/null
+++ b/src/fs/fs_backend_dir.h
@@ -0,0 +1,8 @@
+#ifndef __VIR_FS_BACKEND_DIR_H__
+# define __VIR_FS_BACKEND_DIR_H__
+
+# include "fs_backend.h"
+
+extern virFsBackend virFsBackendDir;
+
+#endif /* __VIR_FS_BACKEND_DIR_H__ */
diff --git a/src/fs/fs_driver.c b/src/fs/fs_driver.c
new file mode 100644
index 0000000..76aba7a
--- /dev/null
+++ b/src/fs/fs_driver.c
@@ -0,0 +1,2164 @@
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "virerror.h"
+#include "datatypes.h"
+#include "driver.h"
+#include "fs_driver.h"
+#include "fs_conf.h"
+#include "fs_backend.h"
+#include "viralloc.h"
+#include "virlog.h"
+#include "virfile.h"
+#include "fdstream.h"
+#include "configmake.h"
+#include "virstring.h"
+#include "viraccessapicheck.h"
+#include "dirname.h"
+
+#if WITH_FS_DIR
+# include "fs_backend_dir.h"
+#endif
+
+#define VIR_FROM_THIS VIR_FROM_FSPOOL
+
+VIR_LOG_INIT("fs.fs_driver");
+
+static virFsDriverStatePtr driver;
+
+typedef struct _virFsItemStreamInfo virFsItemStreamInfo;
+typedef virFsItemStreamInfo *virFsItemStreamInfoPtr;
+struct _virFsItemStreamInfo {
+ char *fspool_name;
+ char *item_path;
+};
+
+static int fsStateCleanup(void);
+
+static void fsDriverLock(void)
+{
+ virMutexLock(&driver->lock);
+}
+
+static void fsDriverUnlock(void)
+{
+ virMutexUnlock(&driver->lock);
+}
+
+static virFsBackendPtr backends[] = {
+#if WITH_FS_DIR
+ &virFsBackendDir,
+#endif
+};
+
+static virFsBackendPtr
+virFsBackendForType(int type)
+{
+ size_t i;
+ for (i = 0; backends[i]; i++)
+ if (backends[i]->type == type)
+ return backends[i];
+
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing backend for fspool type %d (%s)"),
+ type, NULLSTR(virFsPoolTypeToString(type)));
+ return NULL;
+}
+
+static void
+fsItemRemoveFromFsPool(virFsPoolObjPtr fspool,
+ virFsItemDefPtr item)
+{
+ size_t i;
+
+ for (i = 0; i < fspool->items.count; i++) {
+ if (fspool->items.objs[i] == item) {
+ VIR_INFO("Deleting item '%s' from fspool '%s'",
+ item->name, fspool->def->name);
+ virFsItemDefFree(item);
+
+ VIR_DELETE_ELEMENT(fspool->items.objs, i, fspool->items.count);
+ break;
+ }
+ }
+}
+
+static int
+fsItemDeleteInternal(virFsItemPtr obj,
+ virFsBackendPtr backend,
+ virFsPoolObjPtr fspool,
+ virFsItemDefPtr item,
+ unsigned int flags)
+{
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!backend->deleteItem) {
+ virReportError(VIR_ERR_NO_SUPPORT,
+ "%s", _("fspool does not support item deletion"));
+ goto cleanup;
+ }
+ if (backend->deleteItem(obj->conn, fspool, item, flags) < 0)
+ goto cleanup;
+
+ fsItemRemoveFromFsPool(fspool, item);
+
+ ret = 0;
+
+ cleanup:
+ return ret;
+}
+
+static virFsItemDefPtr
+virFsItemDefFromItem(virFsItemPtr obj,
+ virFsPoolObjPtr *fspool,
+ virFsBackendPtr *backend)
+{
+ virFsItemDefPtr item = NULL;
+
+ *fspool = NULL;
+
+ fsDriverLock();
+ *fspool = virFsPoolObjFindByName(&driver->fspools, obj->fspool);
+ fsDriverUnlock();
+
+ if (!*fspool) {
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching name '%s'"),
+ obj->fspool);
+ return NULL;
+ }
+
+ if (!virFsPoolObjIsActive(*fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
+ (*fspool)->def->name);
+ goto error;
+ }
+
+ if (!(item = virFsItemDefFindByName(*fspool, obj->name))) {
+ virReportError(VIR_ERR_NO_FS_ITEM,
+ _("no fs item with matching name '%s'"),
+ obj->name);
+ goto error;
+ }
+
+ if (backend) {
+ if (!(*backend = virFsBackendForType((*fspool)->def->type)))
+ goto error;
+ }
+
+ return item;
+
+ error:
+ virFsPoolObjUnlock(*fspool);
+ *fspool = NULL;
+
+ return NULL;
+}
+
+static void
+fsPoolUpdateState(virFsPoolObjPtr fspool)
+{
+ bool active;
+ virFsBackendPtr backend;
+ int ret = -1;
+ char *stateFile;
+
+ if (!(stateFile = virFileBuildPath(driver->stateDir,
+ fspool->def->name, ".xml")))
+ goto error;
+
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL) {
+ VIR_ERROR(_("Missing backend %d"), fspool->def->type);
+ goto error;
+ }
+
+ /* Backends which do not support 'checkFspool' are considered
+ * inactive by default.
+ */
+ active = false;
+ if (backend->checkFspool &&
+ backend->checkFspool(fspool, &active) < 0) {
+ virErrorPtr err = virGetLastError();
+ VIR_ERROR(_("Failed to initialize fspool '%s': %s"),
+ fspool->def->name, err ? err->message :
+ _("no error message found"));
+ goto error;
+ }
+
+ /* We can pass NULL as connection, most backends do not use
+ * it anyway, but if they do and fail, we want to log error and
+ * continue with other fspools.
+ */
+ if (active) {
+ virFsPoolObjClearItems(fspool);
+ if (backend->refreshFspool(NULL, fspool) < 0) {
+ virErrorPtr err = virGetLastError();
+ if (backend->stopFspool)
+ backend->stopFspool(NULL, fspool);
+ VIR_ERROR(_("Failed to restart fspool '%s': %s"),
+ fspool->def->name, err ? err->message :
+ _("no error message found"));
+ goto error;
+ }
+ }
+
+ fspool->active = active;
+ ret = 0;
+ error:
+ if (ret < 0) {
+ if (stateFile)
+ unlink(stateFile);
+ }
+ VIR_FREE(stateFile);
+
+ return;
+}
+
+static void
+fsPoolUpdateAllState(void)
+{
+ size_t i;
+
+ for (i = 0; i < driver->fspools.count; i++) {
+ virFsPoolObjPtr fspool = driver->fspools.objs[i];
+
+ virFsPoolObjLock(fspool);
+ fsPoolUpdateState(fspool);
+ virFsPoolObjUnlock(fspool);
+ }
+}
+
+static void
+fsDriverAutostart(void)
+{
+ size_t i;
+ virConnectPtr conn = NULL;
+
+ /* XXX Remove hardcoding of QEMU URI */
+ if (driver->privileged)
+ conn = virConnectOpen("qemu:///system");
+ else
+ conn = virConnectOpen("qemu:///session");
+ /* Ignoring NULL conn - let backends decide */
+
+ for (i = 0; i < driver->fspools.count; i++) {
+ virFsPoolObjPtr fspool = driver->fspools.objs[i];
+ virFsBackendPtr backend;
+ bool started = false;
+
+ virFsPoolObjLock(fspool);
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL) {
+ virFsPoolObjUnlock(fspool);
+ continue;
+ }
+
+ if (fspool->autostart &&
+ !virFsPoolObjIsActive(fspool)) {
+ if (backend->startFspool &&
+ backend->startFspool(conn, fspool) < 0) {
+ virErrorPtr err = virGetLastError();
+ VIR_ERROR(_("Failed to autostart fspool '%s': %s"),
+ fspool->def->name, err ? err->message :
+ _("no error message found"));
+ virFsPoolObjUnlock(fspool);
+ continue;
+ }
+ started = true;
+ }
+
+ if (started) {
+ char *stateFile;
+
+ virFsPoolObjClearItems(fspool);
+ stateFile = virFileBuildPath(driver->stateDir,
+ fspool->def->name, ".xml");
+ if (!stateFile ||
+ virFsPoolSaveState(stateFile, fspool->def) < 0 ||
+ backend->refreshFspool(conn, fspool) < 0) {
+ virErrorPtr err = virGetLastError();
+ if (stateFile)
+ unlink(stateFile);
+ if (backend->stopFspool)
+ backend->stopFspool(conn, fspool);
+ VIR_ERROR(_("Failed to autostart fspool '%s': %s"),
+ fspool->def->name, err ? err->message :
+ _("no error message found"));
+ } else {
+ fspool->active = true;
+ }
+ VIR_FREE(stateFile);
+ }
+ virFsPoolObjUnlock(fspool);
+ }
+
+ virObjectUnref(conn);
+}
+
+/**
+ * virFsStartup:
+ *
+ * Initialization function for the Fs Driver
+ */
+static int
+fsStateInitialize(bool privileged,
+ virStateInhibitCallback callback ATTRIBUTE_UNUSED,
+ void *opaque ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ char *configdir = NULL;
+ char *rundir = NULL;
+
+ if (VIR_ALLOC(driver) < 0)
+ return ret;
+
+ if (virMutexInit(&driver->lock) < 0) {
+ VIR_FREE(driver);
+ return ret;
+ }
+ fsDriverLock();
+
+ if (privileged) {
+ if (VIR_STRDUP(driver->configDir,
+ SYSCONFDIR "/libvirt/fs") < 0 ||
+ VIR_STRDUP(driver->autostartDir,
+ SYSCONFDIR "/libvirt/fs/autostart") < 0 ||
+ VIR_STRDUP(driver->stateDir,
+ LOCALSTATEDIR "/run/libvirt/fs") < 0)
+ goto error;
+ } else {
+ configdir = virGetUserConfigDirectory();
+ rundir = virGetUserRuntimeDirectory();
+ if (!(configdir && rundir))
+ goto error;
+
+ if ((virAsprintf(&driver->configDir,
+ "%s/fs", configdir) < 0) ||
+ (virAsprintf(&driver->autostartDir,
+ "%s/fs/autostart", configdir) < 0) ||
+ (virAsprintf(&driver->stateDir,
+ "%s/fs/run", rundir) < 0))
+ goto error;
+ }
+ driver->privileged = privileged;
+
+ if (virFileMakePath(driver->stateDir) < 0) {
+ virReportError(errno,
+ _("cannot create directory %s"),
+ driver->stateDir);
+ goto error;
+ }
+
+ if (virFsPoolLoadAllState(&driver->fspools,
+ driver->stateDir) < 0)
+ goto error;
+
+ if (virFsPoolLoadAllConfigs(&driver->fspools,
+ driver->configDir,
+ driver->autostartDir) < 0)
+ goto error;
+
+ fsPoolUpdateAllState();
+
+ fsDriverUnlock();
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(configdir);
+ VIR_FREE(rundir);
+ return ret;
+
+ error:
+ fsDriverUnlock();
+ fsStateCleanup();
+ goto cleanup;
+}
+
+/**
+ * fsStateAutoStart:
+ *
+ * Function to auto start the fs_driver
+ */
+static void
+fsStateAutoStart(void)
+{
+ if (!driver)
+ return;
+
+ fsDriverLock();
+ fsDriverAutostart();
+ fsDriverUnlock();
+}
+
+/**
+ * fsStateReload:
+ *
+ * Function to restart the fs_driver, it will recheck the configuration
+ * files and update its state
+ */
+static int
+fsStateReload(void)
+{
+ if (!driver)
+ return -1;
+
+ fsDriverLock();
+ virFsPoolLoadAllState(&driver->fspools,
+ driver->stateDir);
+ virFsPoolLoadAllConfigs(&driver->fspools,
+ driver->configDir,
+ driver->autostartDir);
+ fsDriverAutostart();
+ fsDriverUnlock();
+
+ return 0;
+}
+
+
+/**
+ * fsStateCleanup
+ *
+ * Shutdown the fs driver, it will stop all active fspools
+ */
+static int
+fsStateCleanup(void)
+{
+ if (!driver)
+ return -1;
+
+ fsDriverLock();
+
+ /* free inactive fspools */
+ virFsPoolObjListFree(&driver->fspools);
+
+ VIR_FREE(driver->configDir);
+ VIR_FREE(driver->autostartDir);
+ VIR_FREE(driver->stateDir);
+ fsDriverUnlock();
+ virMutexDestroy(&driver->lock);
+ VIR_FREE(driver);
+
+ return 0;
+}
+
+
+static virFsPoolObjPtr
+virFsPoolObjFromFsPool(virFsPoolPtr fspool)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virFsPoolObjPtr ret;
+
+ fsDriverLock();
+ if (!(ret = virFsPoolObjFindByUUID(&driver->fspools, fspool->uuid))) {
+ virUUIDFormat(fspool->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching uuid '%s' (%s)"),
+ uuidstr, fspool->name);
+ }
+ fsDriverUnlock();
+
+ return ret;
+}
+
+static int
+fsConnectNumOfFsPools(virConnectPtr conn)
+{
+ size_t i;
+ int nactive = 0;
+
+ if (virConnectNumOfFsPoolsEnsureACL(conn) < 0)
+ return -1;
+
+ fsDriverLock();
+ for (i = 0; i < driver->fspools.count; i++) {
+ virFsPoolObjPtr obj = driver->fspools.objs[i];
+ virFsPoolObjLock(obj);
+ if (virConnectNumOfFsPoolsCheckACL(conn, obj->def) &&
+ virFsPoolObjIsActive(obj))
+ nactive++;
+ virFsPoolObjUnlock(obj);
+ }
+ fsDriverUnlock();
+
+ return nactive;
+}
+
+static int
+fsConnectListAllFsPools(virConnectPtr conn,
+ virFsPoolPtr **fspools,
+ unsigned int flags)
+{
+ int ret = -1;
+
+ virCheckFlags(VIR_CONNECT_LIST_FS_POOLS_FILTERS_ALL, -1);
+
+ if (virConnectListAllFsPoolsEnsureACL(conn) < 0)
+ goto cleanup;
+
+ fsDriverLock();
+ ret = virFsPoolObjListExport(conn, driver->fspools, fspools,
+ virConnectListAllFsPoolsCheckACL,
+ flags);
+ fsDriverUnlock();
+
+ cleanup:
+ return ret;
+}
+
+static int
+fsConnectListFsPools(virConnectPtr conn,
+ char **const names,
+ int nnames)
+{
+ int got = 0;
+ size_t i;
+
+ if (virConnectListFsPoolsEnsureACL(conn) < 0)
+ return -1;
+
+ fsDriverLock();
+ for (i = 0; i < driver->fspools.count && got < nnames; i++) {
+ virFsPoolObjPtr obj = driver->fspools.objs[i];
+ virFsPoolObjLock(obj);
+ if (virConnectListFsPoolsCheckACL(conn, obj->def) &&
+ virFsPoolObjIsActive(obj)) {
+ if (VIR_STRDUP(names[got], obj->def->name) < 0) {
+ virFsPoolObjUnlock(obj);
+ goto cleanup;
+ }
+ got++;
+ }
+ virFsPoolObjUnlock(obj);
+ }
+ fsDriverUnlock();
+ return got;
+
+ cleanup:
+ fsDriverUnlock();
+ for (i = 0; i < got; i++)
+ VIR_FREE(names[i]);
+ memset(names, 0, nnames * sizeof(*names));
+ return -1;
+}
+
+static int
+fsConnectNumOfDefinedFsPools(virConnectPtr conn)
+{
+ size_t i;
+ int nactive = 0;
+
+ if (virConnectNumOfDefinedFsPoolsEnsureACL(conn) < 0)
+ return -1;
+
+ fsDriverLock();
+ for (i = 0; i < driver->fspools.count; i++) {
+ virFsPoolObjPtr obj = driver->fspools.objs[i];
+ virFsPoolObjLock(obj);
+ if (virConnectNumOfDefinedFsPoolsCheckACL(conn, obj->def) &&
+ !virFsPoolObjIsActive(obj))
+ nactive++;
+ virFsPoolObjUnlock(obj);
+ }
+ fsDriverUnlock();
+
+ return nactive;
+}
+
+static int
+fsConnectListDefinedFsPools(virConnectPtr conn,
+ char **const names,
+ int nnames)
+{
+ int got = 0;
+ size_t i;
+
+ if (virConnectListDefinedFsPoolsEnsureACL(conn) < 0)
+ return -1;
+
+ fsDriverLock();
+ for (i = 0; i < driver->fspools.count && got < nnames; i++) {
+ virFsPoolObjPtr obj = driver->fspools.objs[i];
+ virFsPoolObjLock(obj);
+ if (virConnectListDefinedFsPoolsCheckACL(conn, obj->def) &&
+ !virFsPoolObjIsActive(obj)) {
+ if (VIR_STRDUP(names[got], obj->def->name) < 0) {
+ virFsPoolObjUnlock(obj);
+ goto cleanup;
+ }
+ got++;
+ }
+ virFsPoolObjUnlock(obj);
+ }
+ fsDriverUnlock();
+ return got;
+
+ cleanup:
+ fsDriverUnlock();
+ for (i = 0; i < got; i++)
+ VIR_FREE(names[i]);
+ memset(names, 0, nnames * sizeof(*names));
+ return -1;
+}
+
+static virFsPoolPtr
+fsPoolLookupByUUID(virConnectPtr conn,
+ const unsigned char *uuid)
+{
+ virFsPoolObjPtr fspool;
+ virFsPoolPtr ret = NULL;
+
+ fsDriverLock();
+ fspool = virFsPoolObjFindByUUID(&driver->fspools, uuid);
+ fsDriverUnlock();
+
+ if (!fspool) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(uuid, uuidstr);
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching uuid '%s'"),
uuidstr);
+ return NULL;
+ }
+
+ if (virFsPoolLookupByUUIDEnsureACL(conn, fspool->def) < 0)
+ goto cleanup;
+
+ ret = virGetFsPool(conn, fspool->def->name, fspool->def->uuid,
+ NULL, NULL);
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static virFsPoolPtr
+fsPoolLookupByName(virConnectPtr conn,
+ const char *name)
+{
+ virFsPoolObjPtr fspool;
+ virFsPoolPtr ret = NULL;
+
+ fsDriverLock();
+ fspool = virFsPoolObjFindByName(&driver->fspools, name);
+ fsDriverUnlock();
+
+ if (!fspool) {
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching name '%s'"), name);
+ return NULL;
+ }
+
+ if (virFsPoolLookupByNameEnsureACL(conn, fspool->def) < 0)
+ goto cleanup;
+
+ ret = virGetFsPool(conn, fspool->def->name, fspool->def->uuid,
+ NULL, NULL);
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static virFsPoolPtr
+fsPoolLookupByItem(virFsItemPtr item)
+{
+ virFsPoolObjPtr fspool;
+ virFsPoolPtr ret = NULL;
+
+ fsDriverLock();
+ fspool = virFsPoolObjFindByName(&driver->fspools, item->fspool);
+ fsDriverUnlock();
+
+ if (!fspool) {
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching name '%s'"),
+ item->fspool);
+ return NULL;
+ }
+
+ if (virFsPoolLookupByItemEnsureACL(item->conn, fspool->def) < 0)
+ goto cleanup;
+
+ ret = virGetFsPool(item->conn, fspool->def->name, fspool->def->uuid,
+ NULL, NULL);
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+
+static virFsPoolPtr
+fsPoolCreateXML(virConnectPtr conn,
+ const char *xml,
+ unsigned int flags)
+{
+ virFsPoolDefPtr def;
+ virFsPoolObjPtr fspool = NULL;
+ virFsPoolPtr ret = NULL;
+ virFsBackendPtr backend;
+ char *stateFile = NULL;
+ unsigned int build_flags = 0;
+
+ virCheckFlags(VIR_FS_POOL_CREATE_WITH_BUILD |
+ VIR_FS_POOL_CREATE_WITH_BUILD_OVERWRITE |
+ VIR_FS_POOL_CREATE_WITH_BUILD_NO_OVERWRITE, NULL);
+
+ VIR_EXCLUSIVE_FLAGS_RET(VIR_FS_POOL_BUILD_OVERWRITE,
+ VIR_FS_POOL_BUILD_NO_OVERWRITE, NULL);
+
+ fsDriverLock();
+ if (!(def = virFsPoolDefParseString(xml)))
+ goto cleanup;
+
+ if (virFsPoolCreateXMLEnsureACL(conn, def) < 0)
+ goto cleanup;
+
+ if (virFsPoolObjIsDuplicate(&driver->fspools, def, 1) < 0)
+ goto cleanup;
+
+ if (virFsPoolSourceFindDuplicate(conn, &driver->fspools, def) < 0)
+ goto cleanup;
+
+ if ((backend = virFsBackendForType(def->type)) == NULL)
+ goto cleanup;
+
+ if (!(fspool = virFsPoolObjAssignDef(&driver->fspools, def)))
+ goto cleanup;
+ def = NULL;
+
+ if (backend->buildFspool) {
+ if (flags & VIR_FS_POOL_CREATE_WITH_BUILD_OVERWRITE)
+ build_flags |= VIR_FS_POOL_BUILD_OVERWRITE;
+ else if (flags & VIR_FS_POOL_CREATE_WITH_BUILD_NO_OVERWRITE)
+ build_flags |= VIR_FS_POOL_BUILD_NO_OVERWRITE;
+
+ if (build_flags ||
+ (flags & VIR_FS_POOL_CREATE_WITH_BUILD)) {
+ if (backend->buildFspool(conn, fspool, build_flags) < 0) {
+ virFsPoolObjRemove(&driver->fspools, fspool);
+ fspool = NULL;
+ goto cleanup;
+ }
+ }
+ }
+
+ if (backend->startFspool &&
+ backend->startFspool(conn, fspool) < 0) {
+ virFsPoolObjRemove(&driver->fspools, fspool);
+ fspool = NULL;
+ goto cleanup;
+ }
+
+ stateFile = virFileBuildPath(driver->stateDir,
+ fspool->def->name, ".xml");
+
+ if (!stateFile || virFsPoolSaveState(stateFile, fspool->def) < 0 ||
+ backend->refreshFspool(conn, fspool) < 0) {
+ if (stateFile)
+ unlink(stateFile);
+ if (backend->stopFspool)
+ backend->stopFspool(conn, fspool);
+ virFsPoolObjRemove(&driver->fspools, fspool);
+ fspool = NULL;
+ goto cleanup;
+ }
+ VIR_INFO("Creating fspool '%s'", fspool->def->name);
+ fspool->active = true;
+
+ ret = virGetFsPool(conn, fspool->def->name, fspool->def->uuid,
+ NULL, NULL);
+
+ cleanup:
+ VIR_FREE(stateFile);
+ virFsPoolDefFree(def);
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ fsDriverUnlock();
+ return ret;
+}
+
+static virFsPoolPtr
+fsPoolDefineXML(virConnectPtr conn,
+ const char *xml,
+ unsigned int flags)
+{
+ virFsPoolDefPtr def;
+ virFsPoolObjPtr fspool = NULL;
+ virFsPoolPtr ret = NULL;
+
+ virCheckFlags(0, NULL);
+
+ fsDriverLock();
+ if (!(def = virFsPoolDefParseString(xml)))
+ goto cleanup;
+
+ if (virFsPoolDefineXMLEnsureACL(conn, def) < 0)
+ goto cleanup;
+
+ if (virFsPoolObjIsDuplicate(&driver->fspools, def, 0) < 0)
+ goto cleanup;
+
+ if (virFsPoolSourceFindDuplicate(conn, &driver->fspools, def) < 0)
+ goto cleanup;
+
+ if (virFsBackendForType(def->type) == NULL)
+ goto cleanup;
+
+ if (!(fspool = virFsPoolObjAssignDef(&driver->fspools, def)))
+ goto cleanup;
+
+ if (virFsPoolObjSaveDef(driver, fspool, def) < 0) {
+ virFsPoolObjRemove(&driver->fspools, fspool);
+ def = NULL;
+ fspool = NULL;
+ goto cleanup;
+ }
+ def = NULL;
+
+ VIR_INFO("Defining fspool '%s'", fspool->def->name);
+ ret = virGetFsPool(conn, fspool->def->name, fspool->def->uuid,
+ NULL, NULL);
+
+ cleanup:
+ virFsPoolDefFree(def);
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ fsDriverUnlock();
+ return ret;
+}
+
+static int
+fsPoolCreate(virFsPoolPtr obj,
+ unsigned int flags)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ int ret = -1;
+ char *stateFile = NULL;
+ unsigned int build_flags = 0;
+
+ virCheckFlags(VIR_FS_POOL_CREATE_WITH_BUILD |
+ VIR_FS_POOL_CREATE_WITH_BUILD_OVERWRITE |
+ VIR_FS_POOL_CREATE_WITH_BUILD_NO_OVERWRITE, -1);
+
+ VIR_EXCLUSIVE_FLAGS_RET(VIR_FS_POOL_BUILD_OVERWRITE,
+ VIR_FS_POOL_BUILD_NO_OVERWRITE, -1);
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return -1;
+
+ if (virFsPoolCreateEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL)
+ goto cleanup;
+
+ if (virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is already active"),
+ fspool->def->name);
+ goto cleanup;
+ }
+
+ if (backend->buildFspool) {
+ if (flags & VIR_FS_POOL_CREATE_WITH_BUILD_OVERWRITE)
+ build_flags |= VIR_FS_POOL_BUILD_OVERWRITE;
+ else if (flags & VIR_FS_POOL_CREATE_WITH_BUILD_NO_OVERWRITE)
+ build_flags |= VIR_FS_POOL_BUILD_NO_OVERWRITE;
+
+ if (build_flags ||
+ (flags & VIR_FS_POOL_CREATE_WITH_BUILD)) {
+ if (backend->buildFspool(obj->conn, fspool, build_flags) < 0) {
+ virFsPoolObjRemove(&driver->fspools, fspool);
+ fspool = NULL;
+ goto cleanup;
+ }
+ }
+ }
+
+ VIR_INFO("Starting up fspool '%s'", fspool->def->name);
+ if (backend->startFspool &&
+ backend->startFspool(obj->conn, fspool) < 0)
+ goto cleanup;
+
+ stateFile = virFileBuildPath(driver->stateDir,
+ fspool->def->name, ".xml");
+
+ virFsPoolObjClearItems(fspool);
+ if (!stateFile || virFsPoolSaveState(stateFile, fspool->def) < 0 ||
+ backend->refreshFspool(obj->conn, fspool) < 0) {
+ if (stateFile)
+ unlink(stateFile);
+ goto cleanup;
+ }
+
+ fspool->active = true;
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(stateFile);
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static int
+fsPoolBuild(virFsPoolPtr obj,
+ unsigned int flags)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return -1;
+
+ if (virFsPoolBuildEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL)
+ goto cleanup;
+
+ if (virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is already active"),
+ fspool->def->name);
+ goto cleanup;
+ }
+
+ if (backend->buildFspool &&
+ backend->buildFspool(obj->conn, fspool, flags) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static int
+fsPoolUndefine(virFsPoolPtr obj)
+{
+ virFsPoolObjPtr fspool;
+ int ret = -1;
+
+ fsDriverLock();
+ if (!(fspool = virFsPoolObjFindByUUID(&driver->fspools, obj->uuid))) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(obj->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching uuid '%s' (%s)"),
+ uuidstr, obj->name);
+ goto cleanup;
+ }
+
+ if (virFsPoolUndefineEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if (virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is still active"),
+ fspool->def->name);
+ goto cleanup;
+ }
+
+ if (fspool->asyncjobs > 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("fspool '%s' has asynchronous jobs
running."),
+ fspool->def->name);
+ goto cleanup;
+ }
+
+ if (virFsPoolObjDeleteDef(fspool) < 0)
+ goto cleanup;
+
+ if (unlink(fspool->autostartLink) < 0 &&
+ errno != ENOENT &&
+ errno != ENOTDIR) {
+ char ebuf[1024];
+ VIR_ERROR(_("Failed to delete autostart link '%s': %s"),
+ fspool->autostartLink, virStrerror(errno, ebuf, sizeof(ebuf)));
+ }
+
+ VIR_FREE(fspool->configFile);
+ VIR_FREE(fspool->autostartLink);
+
+ VIR_INFO("Undefining fspool '%s'", fspool->def->name);
+ virFsPoolObjRemove(&driver->fspools, fspool);
+ fspool = NULL;
+ ret = 0;
+
+ cleanup:
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ fsDriverUnlock();
+ return ret;
+}
+
+static int
+fsPoolDestroy(virFsPoolPtr obj)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ char *stateFile = NULL;
+ int ret = -1;
+
+ fsDriverLock();
+ if (!(fspool = virFsPoolObjFindByUUID(&driver->fspools, obj->uuid))) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(obj->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching uuid '%s' (%s)"),
+ uuidstr, obj->name);
+ goto cleanup;
+ }
+
+ if (virFsPoolDestroyEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL)
+ goto cleanup;
+
+ VIR_INFO("Destroying fspool '%s'", fspool->def->name);
+
+ if (!virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
fspool->def->name);
+ goto cleanup;
+ }
+
+ if (fspool->asyncjobs > 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("fspool '%s' has asynchronous jobs
running."),
+ fspool->def->name);
+ goto cleanup;
+ }
+
+ if (!(stateFile = virFileBuildPath(driver->stateDir,
+ fspool->def->name,
+ ".xml")))
+ goto cleanup;
+
+ unlink(stateFile);
+ VIR_FREE(stateFile);
+
+ if (backend->stopFspool &&
+ backend->stopFspool(obj->conn, fspool) < 0)
+ goto cleanup;
+
+ virFsPoolObjClearItems(fspool);
+
+ fspool->active = false;
+
+ if (fspool->configFile == NULL) {
+ virFsPoolObjRemove(&driver->fspools, fspool);
+ fspool = NULL;
+ } else if (fspool->newDef) {
+ virFsPoolDefFree(fspool->def);
+ fspool->def = fspool->newDef;
+ fspool->newDef = NULL;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ fsDriverUnlock();
+ return ret;
+}
+
+static int
+fsPoolDelete(virFsPoolPtr obj,
+ unsigned int flags)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ char *stateFile = NULL;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return -1;
+
+ if (virFsPoolDeleteEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL)
+ goto cleanup;
+
+ VIR_INFO("Deleting fspool '%s'", fspool->def->name);
+
+ if (virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is still active"),
+ fspool->def->name);
+ goto cleanup;
+ }
+
+ if (fspool->asyncjobs > 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("fspool '%s' has asynchronous jobs
running."),
+ fspool->def->name);
+ goto cleanup;
+ }
+
+ if (!(stateFile = virFileBuildPath(driver->stateDir,
+ fspool->def->name,
+ ".xml")))
+ goto cleanup;
+
+ unlink(stateFile);
+ VIR_FREE(stateFile);
+
+ if (!backend->deleteFspool) {
+ virReportError(VIR_ERR_NO_SUPPORT,
+ "%s", _("fspool does not support fspool
deletion"));
+ goto cleanup;
+ }
+ if (backend->deleteFspool(obj->conn, fspool, flags) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static int
+fsPoolRefresh(virFsPoolPtr obj,
+ unsigned int flags)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ fsDriverLock();
+ if (!(fspool = virFsPoolObjFindByUUID(&driver->fspools, obj->uuid))) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(obj->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching uuid '%s' (%s)"),
+ uuidstr, obj->name);
+ goto cleanup;
+ }
+
+ if (virFsPoolRefreshEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL)
+ goto cleanup;
+
+ if (!virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
fspool->def->name);
+ goto cleanup;
+ }
+
+ if (fspool->asyncjobs > 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("fspool '%s' has asynchronous jobs
running."),
+ fspool->def->name);
+ goto cleanup;
+ }
+
+ virFsPoolObjClearItems(fspool);
+ if (backend->refreshFspool(obj->conn, fspool) < 0) {
+ if (backend->stopFspool)
+ backend->stopFspool(obj->conn, fspool);
+
+ fspool->active = false;
+
+ if (fspool->configFile == NULL) {
+ virFsPoolObjRemove(&driver->fspools, fspool);
+ fspool = NULL;
+ }
+ goto cleanup;
+ }
+ ret = 0;
+
+ cleanup:
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ fsDriverUnlock();
+ return ret;
+
+ return 0;
+}
+
+
+static int
+fsPoolGetInfo(virFsPoolPtr obj,
+ virFsPoolInfoPtr info)
+{
+ virFsPoolObjPtr fspool;
+ int ret = -1;
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return -1;
+
+ if (virFsPoolGetInfoEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if (virFsBackendForType(fspool->def->type) == NULL)
+ goto cleanup;
+
+ memset(info, 0, sizeof(virFsPoolInfo));
+ if (fspool->active)
+ info->state = VIR_FS_POOL_RUNNING;
+ else
+ info->state = VIR_FS_POOL_INACTIVE;
+ info->capacity = fspool->def->capacity;
+ info->allocation = fspool->def->allocation;
+ info->available = fspool->def->available;
+ ret = 0;
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static char *
+fsPoolGetXMLDesc(virFsPoolPtr obj,
+ unsigned int flags)
+{
+ virFsPoolObjPtr fspool;
+ virFsPoolDefPtr def;
+ char *ret = NULL;
+
+ virCheckFlags(VIR_FS_XML_INACTIVE, NULL);
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return NULL;
+
+ if (virFsPoolGetXMLDescEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if ((flags & VIR_FS_XML_INACTIVE) && fspool->newDef)
+ def = fspool->newDef;
+ else
+ def = fspool->def;
+
+ ret = virFsPoolDefFormat(def);
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static int
+fsPoolGetAutostart(virFsPoolPtr obj, int *autostart)
+{
+ virFsPoolObjPtr fspool;
+ int ret = -1;
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return -1;
+
+ if (virFsPoolGetAutostartEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if (!fspool->configFile) {
+ *autostart = 0;
+ } else {
+ *autostart = fspool->autostart;
+ }
+ ret = 0;
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static int
+fsPoolSetAutostart(virFsPoolPtr obj, int autostart)
+{
+ virFsPoolObjPtr fspool;
+ int ret = -1;
+
+ fsDriverLock();
+ fspool = virFsPoolObjFindByUUID(&driver->fspools, obj->uuid);
+
+ if (!fspool) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(obj->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fspool with matching uuid '%s' (%s)"),
+ uuidstr, obj->name);
+ goto cleanup;
+ }
+
+ if (virFsPoolSetAutostartEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if (!fspool->configFile) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("fspool has no config file"));
+ goto cleanup;
+ }
+
+ autostart = (autostart != 0);
+
+ if (fspool->autostart != autostart) {
+ if (autostart) {
+ if (virFileMakePath(driver->autostartDir) < 0) {
+ virReportSystemError(errno,
+ _("cannot create autostart directory
%s"),
+ driver->autostartDir);
+ goto cleanup;
+ }
+
+ if (symlink(fspool->configFile, fspool->autostartLink) < 0) {
+ virReportSystemError(errno,
+ _("Failed to create symlink '%s' to
'%s'"),
+ fspool->autostartLink, fspool->configFile);
+ goto cleanup;
+ }
+ } else {
+ if (unlink(fspool->autostartLink) < 0 &&
+ errno != ENOENT && errno != ENOTDIR) {
+ virReportSystemError(errno,
+ _("Failed to delete symlink
'%s'"),
+ fspool->autostartLink);
+ goto cleanup;
+ }
+ }
+ fspool->autostart = autostart;
+ }
+ ret = 0;
+
+ cleanup:
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ fsDriverUnlock();
+ return ret;
+}
+
+static int
+fsPoolNumOfItems(virFsPoolPtr obj)
+{
+ virFsPoolObjPtr fspool;
+ int ret = -1;
+ size_t i;
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return -1;
+
+ if (virFsPoolNumOfItemsEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if (!virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
fspool->def->name);
+ goto cleanup;
+ }
+ ret = 0;
+ for (i = 0; i < fspool->items.count; i++) {
+ if (virFsPoolNumOfItemsCheckACL(obj->conn, fspool->def,
+ fspool->items.objs[i]))
+ ret++;
+ }
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static int
+fsPoolListItems(virFsPoolPtr obj,
+ char **const names,
+ int maxnames)
+{
+ virFsPoolObjPtr fspool;
+ size_t i;
+ int n = 0;
+
+ memset(names, 0, maxnames * sizeof(*names));
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return -1;
+
+ if (virFsPoolListItemsEnsureACL(obj->conn, fspool->def) < 0)
+ goto cleanup;
+
+ if (!virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
fspool->def->name);
+ goto cleanup;
+ }
+
+ for (i = 0; i < fspool->items.count && n < maxnames; i++) {
+ if (!virFsPoolListItemsCheckACL(obj->conn, fspool->def,
+ fspool->items.objs[i]))
+ continue;
+ if (VIR_STRDUP(names[n++], fspool->items.objs[i]->name) < 0)
+ goto cleanup;
+ }
+
+ virFsPoolObjUnlock(fspool);
+ return n;
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ for (n = 0; n < maxnames; n++)
+ VIR_FREE(names[n]);
+
+ memset(names, 0, maxnames * sizeof(*names));
+ return -1;
+}
+
+static int
+fsPoolListAllItems(virFsPoolPtr fspool,
+ virFsItemPtr **items,
+ unsigned int flags)
+{
+ virFsPoolObjPtr obj;
+ size_t i;
+ virFsItemPtr *tmp_items = NULL;
+ virFsItemPtr item = NULL;
+ int nitems = 0;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!(obj = virFsPoolObjFromFsPool(fspool)))
+ return -1;
+
+ if (virFsPoolListAllItemsEnsureACL(fspool->conn, obj->def) < 0)
+ goto cleanup;
+
+ if (!virFsPoolObjIsActive(obj)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
obj->def->name);
+ goto cleanup;
+ }
+
+ /* Just returns the items count */
+ if (!items) {
+ ret = obj->items.count;
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC_N(tmp_items, obj->items.count + 1) < 0)
+ goto cleanup;
+
+ for (i = 0; i < obj->items.count; i++) {
+ if (!virFsPoolListAllItemsCheckACL(fspool->conn, obj->def,
+ obj->items.objs[i]))
+ continue;
+ if (!(item = virGetFsItem(fspool->conn, obj->def->name,
+ obj->items.objs[i]->name,
+ obj->items.objs[i]->key,
+ NULL, NULL)))
+ goto cleanup;
+ tmp_items[nitems++] = item;
+ }
+
+ *items = tmp_items;
+ tmp_items = NULL;
+ ret = nitems;
+
+ cleanup:
+ if (tmp_items) {
+ for (i = 0; i < nitems; i++)
+ virObjectUnref(tmp_items[i]);
+ VIR_FREE(tmp_items);
+ }
+
+ virFsPoolObjUnlock(obj);
+
+ return ret;
+}
+
+static virFsItemPtr
+fsItemLookupByName(virFsPoolPtr obj, const char *name)
+{
+ virFsPoolObjPtr fspool;
+ virFsItemDefPtr item;
+ virFsItemPtr ret = NULL;
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return NULL;
+
+ if (!virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
fspool->def->name);
+ goto cleanup;
+ }
+
+ item = virFsItemDefFindByName(fspool, name);
+
+ if (!item) {
+ virReportError(VIR_ERR_NO_FS_ITEM,
+ _("no fspool item with matching name '%s'"),
+ name);
+ goto cleanup;
+ }
+
+ if (virFsItemLookupByNameEnsureACL(obj->conn, fspool->def, item) < 0)
+ goto cleanup;
+
+ ret = virGetFsItem(obj->conn, fspool->def->name, item->name,
item->key,
+ NULL, NULL);
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+
+static virFsItemPtr
+fsItemLookupByKey(virConnectPtr conn, const char *key)
+{
+ size_t i;
+ virFsItemPtr ret = NULL;
+
+ fsDriverLock();
+ for (i = 0; i < driver->fspools.count && !ret; i++) {
+ virFsPoolObjLock(driver->fspools.objs[i]);
+ if (virFsPoolObjIsActive(driver->fspools.objs[i])) {
+ virFsItemDefPtr item =
+ virFsItemDefFindByKey(driver->fspools.objs[i], key);
+
+ if (item) {
+ virFsPoolDefPtr def = driver->fspools.objs[i]->def;
+ if (virFsItemLookupByKeyEnsureACL(conn, def, item) < 0) {
+ virFsPoolObjUnlock(driver->fspools.objs[i]);
+ goto cleanup;
+ }
+
+ ret = virGetFsItem(conn,
+ def->name,
+ item->name,
+ item->key,
+ NULL, NULL);
+ }
+ }
+ virFsPoolObjUnlock(driver->fspools.objs[i]);
+ }
+
+ if (!ret)
+ virReportError(VIR_ERR_NO_FS_ITEM,
+ _("no fspool item with matching key %s"), key);
+
+ cleanup:
+ fsDriverUnlock();
+ return ret;
+}
+
+static virFsItemPtr
+fsItemLookupByPath(virConnectPtr conn,
+ const char *path)
+{
+ size_t i;
+ virFsItemPtr ret = NULL;
+ char *cleanpath;
+
+ cleanpath = virFileSanitizePath(path);
+ if (!cleanpath)
+ return NULL;
+
+ fsDriverLock();
+ for (i = 0; i < driver->fspools.count && !ret; i++) {
+ virFsPoolObjPtr fspool = driver->fspools.objs[i];
+ virFsItemDefPtr item;
+
+ virFsPoolObjLock(fspool);
+
+ if (!virFsPoolObjIsActive(fspool)) {
+ virFsPoolObjUnlock(fspool);
+ continue;
+ }
+
+ item = virFsItemDefFindByPath(fspool, cleanpath);
+
+ if (item) {
+ if (virFsItemLookupByPathEnsureACL(conn, fspool->def, item) < 0) {
+ virFsPoolObjUnlock(fspool);
+ goto cleanup;
+ }
+
+ ret = virGetFsItem(conn, fspool->def->name,
+ item->name, item->key,
+ NULL, NULL);
+ }
+
+ virFsPoolObjUnlock(fspool);
+ }
+
+ if (!ret) {
+ if (STREQ(path, cleanpath)) {
+ virReportError(VIR_ERR_NO_FS_ITEM,
+ _("no fspool item with matching path '%s'"),
path);
+ } else {
+ virReportError(VIR_ERR_NO_FS_ITEM,
+ _("no fspool item with matching path '%s'
(%s)"),
+ path, cleanpath);
+ }
+ }
+
+ cleanup:
+ VIR_FREE(cleanpath);
+ fsDriverUnlock();
+ return ret;
+}
+
+static virFsItemPtr
+fsItemCreateXML(virFsPoolPtr obj,
+ const char *xmldesc,
+ unsigned int flags)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ virFsItemDefPtr itemdef = NULL;
+ virFsItemPtr ret = NULL, itemobj = NULL;
+
+ virCheckFlags(0, NULL);
+
+ if (!(fspool = virFsPoolObjFromFsPool(obj)))
+ return NULL;
+
+ if (!virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
fspool->def->name);
+ goto cleanup;
+ }
+
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL)
+ goto cleanup;
+
+ itemdef = virFsItemDefParseString(fspool->def, xmldesc,
+ VIR_ITEM_XML_PARSE_OPT_CAPACITY);
+ if (itemdef == NULL)
+ goto cleanup;
+
+ if (!itemdef->target.capacity && !backend->buildItem) {
+ virReportError(VIR_ERR_NO_SUPPORT,
+ "%s", _("item capacity required for this "
+ "fspool"));
+ goto cleanup;
+ }
+
+ if (virFsItemCreateXMLEnsureACL(obj->conn, fspool->def, itemdef) < 0)
+ goto cleanup;
+
+ if (virFsItemDefFindByName(fspool, itemdef->name)) {
+ virReportError(VIR_ERR_FS_ITEM_EXIST,
+ _("'%s'"), itemdef->name);
+ goto cleanup;
+ }
+
+ if (!backend->createItem) {
+ virReportError(VIR_ERR_NO_SUPPORT,
+ "%s", _("fspool does not support item "
+ "creation"));
+ goto cleanup;
+ }
+
+ if (VIR_REALLOC_N(fspool->items.objs,
+ fspool->items.count+1) < 0)
+ goto cleanup;
+
+ /* Wipe any key the user may have suggested, as item creation
+ * will generate the canonical key. */
+ VIR_FREE(itemdef->key);
+ if (backend->createItem(obj->conn, fspool, itemdef) < 0)
+ goto cleanup;
+
+ fspool->items.objs[fspool->items.count++] = itemdef;
+ itemobj = virGetFsItem(obj->conn, fspool->def->name, itemdef->name,
+ itemdef->key, NULL, NULL);
+ if (!itemobj) {
+ fspool->items.count--;
+ goto cleanup;
+ }
+
+
+ if (backend->buildItem) {
+ int buildret;
+ virFsItemDefPtr builditemdef = NULL;
+
+ if (VIR_ALLOC(builditemdef) < 0) {
+ itemdef = NULL;
+ goto cleanup;
+ }
+
+ /* Make a shallow copy of the 'defined' item definition, since the
+ * original allocation value will change as the user polls 'info',
+ * but we only need the initial requested values
+ */
+ memcpy(builditemdef, itemdef, sizeof(*itemdef));
+
+ /* Drop the fspool lock during item allocation */
+ fspool->asyncjobs++;
+ itemdef->building = true;
+ virFsPoolObjUnlock(fspool);
+
+ buildret = backend->buildItem(obj->conn, fspool, builditemdef, flags);
+
+ VIR_FREE(builditemdef);
+
+ fsDriverLock();
+ virFsPoolObjLock(fspool);
+ fsDriverUnlock();
+
+ itemdef->building = false;
+ fspool->asyncjobs--;
+
+ if (buildret < 0) {
+ /* buildItem handles deleting item on failure */
+ fsItemRemoveFromFsPool(fspool, itemdef);
+ itemdef = NULL;
+ goto cleanup;
+ }
+
+ }
+
+ if (backend->refreshItem &&
+ backend->refreshItem(obj->conn, fspool, itemdef) < 0) {
+ fsItemDeleteInternal(itemobj, backend, fspool, itemdef, 0);
+ itemdef = NULL;
+ goto cleanup;
+ }
+
+ /* Update fspool metadata ignoring the disk backend since
+ * it updates the fspool values.
+ */
+
+ VIR_INFO("Creating item '%s' in fs fspool '%s'",
+ itemobj->name, fspool->def->name);
+ ret = itemobj;
+ itemobj = NULL;
+ itemdef = NULL;
+
+ cleanup:
+ virObjectUnref(itemobj);
+ virFsItemDefFree(itemdef);
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static virFsItemPtr
+fsItemCreateXMLFrom(virFsPoolPtr obj,
+ const char *xmldesc,
+ virFsItemPtr vobj,
+ unsigned int flags)
+{
+ virFsPoolObjPtr fspool, origpool = NULL;
+ virFsBackendPtr backend;
+ virFsItemDefPtr origitem = NULL, newitem = NULL, shadowitem = NULL;
+ virFsItemPtr ret = NULL, itemobj = NULL;
+ int buildret;
+
+ virCheckFlags(0, NULL);
+
+ fsDriverLock();
+ fspool = virFsPoolObjFindByUUID(&driver->fspools, obj->uuid);
+ if (fspool && STRNEQ(obj->name, vobj->fspool)) {
+ virFsPoolObjUnlock(fspool);
+ origpool = virFsPoolObjFindByName(&driver->fspools, vobj->fspool);
+ virFsPoolObjLock(fspool);
+ }
+ fsDriverUnlock();
+ if (!fspool) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(obj->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fs fspool with matching uuid '%s' (%s)"),
+ uuidstr, obj->name);
+ goto cleanup;
+ }
+
+ if (STRNEQ(obj->name, vobj->fspool) && !origpool) {
+ virReportError(VIR_ERR_NO_FS_POOL,
+ _("no fs fspool with matching name '%s'"),
+ vobj->fspool);
+ goto cleanup;
+ }
+
+ if (!virFsPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fs fspool '%s' is not active"),
fspool->def->name);
+ goto cleanup;
+ }
+
+ if (origpool && !virFsPoolObjIsActive(origpool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fs fspool '%s' is not active"),
+ origpool->def->name);
+ goto cleanup;
+ }
+
+ if ((backend = virFsBackendForType(fspool->def->type)) == NULL)
+ goto cleanup;
+
+ origitem = virFsItemDefFindByName(origpool ?
+ origpool : fspool, vobj->name);
+ if (!origitem) {
+ virReportError(VIR_ERR_NO_FS_ITEM,
+ _("no fs item with matching name '%s'"),
+ vobj->name);
+ goto cleanup;
+ }
+
+ newitem = virFsItemDefParseString(fspool->def, xmldesc,
+ VIR_VOL_XML_PARSE_NO_CAPACITY);
+ if (newitem == NULL)
+ goto cleanup;
+
+ if (virFsItemCreateXMLFromEnsureACL(obj->conn, fspool->def, newitem) < 0)
+ goto cleanup;
+
+ if (virFsItemDefFindByName(fspool, newitem->name)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("fs item name '%s' already in use."),
+ newitem->name);
+ goto cleanup;
+ }
+
+ /* Use the original item's capacity in case the new capacity
+ * is less than that, or it was omitted */
+ if (newitem->target.capacity < origitem->target.capacity)
+ newitem->target.capacity = origitem->target.capacity;
+
+ if (!backend->buildItemFrom) {
+ virReportError(VIR_ERR_NO_SUPPORT,
+ "%s", _("fs fspool does not support"
+ " item creation from an existing item"));
+ goto cleanup;
+ }
+
+ if (origitem->building) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("item '%s' is still being allocated."),
+ origitem->name);
+ goto cleanup;
+ }
+
+ if (backend->refreshItem &&
+ backend->refreshItem(obj->conn, fspool, origitem) < 0)
+ goto cleanup;
+
+ if (VIR_REALLOC_N(fspool->items.objs,
+ fspool->items.count+1) < 0)
+ goto cleanup;
+
+ /* 'Define' the new item so we get async progress reporting.
+ * Wipe any key the user may have suggested, as item creation
+ * will generate the canonical key. */
+ VIR_FREE(newitem->key);
+ if (backend->createItem(obj->conn, fspool, newitem) < 0)
+ goto cleanup;
+
+ /* Make a shallow copy of the 'defined' item definition, since the
+ * original allocation value will change as the user polls 'info',
+ * but we only need the initial requested values
+ */
+ if (VIR_ALLOC(shadowitem) < 0)
+ goto cleanup;
+
+ memcpy(shadowitem, newitem, sizeof(*newitem));
+
+ fspool->items.objs[fspool->items.count++] = newitem;
+ itemobj = virGetFsItem(obj->conn, fspool->def->name, newitem->name,
+ newitem->key, NULL, NULL);
+ if (!itemobj) {
+ fspool->items.count--;
+ goto cleanup;
+ }
+
+ /* Drop the fspool lock during item allocation */
+ fspool->asyncjobs++;
+ newitem->building = true;
+ origitem->in_use++;
+ virFsPoolObjUnlock(fspool);
+
+ if (origpool) {
+ origpool->asyncjobs++;
+ virFsPoolObjUnlock(origpool);
+ }
+
+ buildret = backend->buildItemFrom(obj->conn, fspool, shadowitem, origitem,
flags);
+
+ fsDriverLock();
+ virFsPoolObjLock(fspool);
+ if (origpool)
+ virFsPoolObjLock(origpool);
+ fsDriverUnlock();
+
+ origitem->in_use--;
+ newitem->building = false;
+ fspool->asyncjobs--;
+
+ if (origpool) {
+ origpool->asyncjobs--;
+ virFsPoolObjUnlock(origpool);
+ origpool = NULL;
+ }
+
+ if (buildret < 0 ||
+ (backend->refreshItem &&
+ backend->refreshItem(obj->conn, fspool, newitem) < 0)) {
+ fsItemDeleteInternal(itemobj, backend, fspool, newitem, 0);
+ newitem = NULL;
+ goto cleanup;
+ }
+
+ fspool->def->allocation += newitem->target.allocation;
+ fspool->def->available -= newitem->target.allocation;
+
+ VIR_INFO("Creating item '%s' in fs fspool '%s'",
+ itemobj->name, fspool->def->name);
+ ret = itemobj;
+ itemobj = NULL;
+ newitem = NULL;
+
+ cleanup:
+ virObjectUnref(itemobj);
+ virFsItemDefFree(newitem);
+ VIR_FREE(shadowitem);
+ if (fspool)
+ virFsPoolObjUnlock(fspool);
+ if (origpool)
+ virFsPoolObjUnlock(origpool);
+ return ret;
+}
+
+
+static int
+fsItemGetInfo(virFsItemPtr obj,
+ virFsItemInfoPtr info)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ virFsItemDefPtr item;
+ int ret = -1;
+
+ if (!(item = virFsItemDefFromItem(obj, &fspool, &backend)))
+ return -1;
+
+ if (virFsItemGetInfoEnsureACL(obj->conn, fspool->def, item) < 0)
+ goto cleanup;
+
+ if (backend->refreshItem &&
+ backend->refreshItem(obj->conn, fspool, item) < 0)
+ goto cleanup;
+
+ memset(info, 0, sizeof(*info));
+ info->type = item->type;
+ info->capacity = item->target.capacity;
+ info->allocation = item->target.allocation;
+ ret = 0;
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static char *
+fsItemGetXMLDesc(virFsItemPtr obj, unsigned int flags)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ virFsItemDefPtr item;
+ char *ret = NULL;
+
+ virCheckFlags(0, NULL);
+
+ if (!(item = virFsItemDefFromItem(obj, &fspool, &backend)))
+ return NULL;
+
+ if (virFsItemGetXMLDescEnsureACL(obj->conn, fspool->def, item) < 0)
+ goto cleanup;
+
+ if (backend->refreshItem &&
+ backend->refreshItem(obj->conn, fspool, item) < 0)
+ goto cleanup;
+
+ ret = virFsItemDefFormat(fspool->def, item);
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+
+ return ret;
+}
+
+static char *
+fsItemGetPath(virFsItemPtr obj)
+{
+ virFsPoolObjPtr fspool;
+ virFsItemDefPtr item;
+ char *ret = NULL;
+
+ if (!(item = virFsItemDefFromItem(obj, &fspool, NULL)))
+ return NULL;
+
+ if (virFsItemGetPathEnsureACL(obj->conn, fspool->def, item) < 0)
+ goto cleanup;
+
+ ignore_value(VIR_STRDUP(ret, item->target.path));
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+
+static int fsPoolIsActive(virFsPoolPtr fspool)
+{
+ virFsPoolObjPtr obj;
+ int ret = -1;
+
+ if (!(obj = virFsPoolObjFromFsPool(fspool)))
+ return -1;
+
+ if (virFsPoolIsActiveEnsureACL(fspool->conn, obj->def) < 0)
+ goto cleanup;
+
+ ret = virFsPoolObjIsActive(obj);
+
+ cleanup:
+ virFsPoolObjUnlock(obj);
+ return ret;
+}
+
+static int fsPoolIsPersistent(virFsPoolPtr fspool)
+{
+ virFsPoolObjPtr obj;
+ int ret = -1;
+
+ if (!(obj = virFsPoolObjFromFsPool(fspool)))
+ return -1;
+
+ if (virFsPoolIsPersistentEnsureACL(fspool->conn, obj->def) < 0)
+ goto cleanup;
+
+ ret = obj->configFile ? 1 : 0;
+
+ cleanup:
+ virFsPoolObjUnlock(obj);
+ return ret;
+}
+
+
+static int
+fsItemDelete(virFsItemPtr obj,
+ unsigned int flags)
+{
+ virFsPoolObjPtr fspool;
+ virFsBackendPtr backend;
+ virFsItemDefPtr item = NULL;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!(item = virFsItemDefFromItem(obj, &fspool, &backend)))
+ return -1;
+
+ if (virFsItemDeleteEnsureACL(obj->conn, fspool->def, item) < 0)
+ goto cleanup;
+
+ if (item->in_use) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("item '%s' is still in use."),
+ item->name);
+ goto cleanup;
+ }
+
+ if (item->building) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("item '%s' is still being allocated."),
+ item->name);
+ goto cleanup;
+ }
+
+ if (fsItemDeleteInternal(obj, backend, fspool, item, flags) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virFsPoolObjUnlock(fspool);
+ return ret;
+}
+
+static virFsDriver fsDriver = {
+ .name = "fs",
+ .connectNumOfFsPools = fsConnectNumOfFsPools, /* 2.1.0 */
+ .connectListFsPools = fsConnectListFsPools, /* 2.1.0 */
+ .connectNumOfDefinedFsPools = fsConnectNumOfDefinedFsPools, /* 2.1.0 */
+ .connectListDefinedFsPools = fsConnectListDefinedFsPools, /* 2.1.0 */
+ .connectListAllFsPools = fsConnectListAllFsPools, /* 2.1.0 */
+ .fsPoolLookupByName = fsPoolLookupByName, /* 2.1.0 */
+ .fsPoolLookupByUUID = fsPoolLookupByUUID, /* 2.1.0 */
+ .fsPoolLookupByItem = fsPoolLookupByItem, /* 2.1.0 */
+ .fsPoolCreateXML = fsPoolCreateXML, /* 2.1.0 */
+ .fsPoolDefineXML = fsPoolDefineXML, /* 2.1.0 */
+ .fsPoolBuild = fsPoolBuild, /* 2.1.0 */
+ .fsPoolCreate = fsPoolCreate, /* 2.1.0 */
+ .fsPoolUndefine = fsPoolUndefine, /* 2.1.0 */
+ .fsPoolDestroy = fsPoolDestroy, /* 2.1.0 */
+ .fsPoolDelete = fsPoolDelete, /* 2.1.0 */
+ .fsPoolRefresh = fsPoolRefresh, /* 2.1.0 */
+ .fsPoolGetInfo = fsPoolGetInfo, /* 2.1.0 */
+ .fsPoolGetXMLDesc = fsPoolGetXMLDesc, /* 2.1.0 */
+ .fsPoolGetAutostart = fsPoolGetAutostart, /* 2.1.0 */
+ .fsPoolSetAutostart = fsPoolSetAutostart, /* 2.1.0 */
+ .fsPoolNumOfItems = fsPoolNumOfItems, /* 2.1.0 */
+ .fsPoolListItems = fsPoolListItems, /* 2.1.0 */
+ .fsPoolListAllItems = fsPoolListAllItems, /* 2.1.0 */
+ .fsItemLookupByName = fsItemLookupByName, /* 2.1.0 */
+ .fsItemLookupByKey = fsItemLookupByKey, /* 2.1.0 */
+ .fsItemLookupByPath = fsItemLookupByPath, /* 2.1.0 */
+ .fsItemCreateXML = fsItemCreateXML, /* 2.1.0 */
+ .fsItemCreateXMLFrom = fsItemCreateXMLFrom, /* 2.1.0 */
+ .fsItemDelete = fsItemDelete, /* 2.1.0 */
+ .fsItemGetInfo = fsItemGetInfo, /* 2.1.0 */
+ .fsItemGetXMLDesc = fsItemGetXMLDesc, /* 2.1.0 */
+ .fsItemGetPath = fsItemGetPath, /* 2.1.0 */
+ .fsPoolIsActive = fsPoolIsActive, /* 2.1.0 */
+ .fsPoolIsPersistent = fsPoolIsPersistent, /* 2.1.0 */
+};
+
+
+static virStateDriver stateDriver = {
+ .name = "fs",
+ .stateInitialize = fsStateInitialize,
+ .stateAutoStart = fsStateAutoStart,
+ .stateCleanup = fsStateCleanup,
+ .stateReload = fsStateReload,
+};
+
+int fsRegister(void)
+{
+ VIR_DEBUG("fsDriver = %p", &fsDriver);
+
+ if (virSetSharedFsDriver(&fsDriver) < 0)
+ return -1;
+
+ if (virRegisterStateDriver(&stateDriver) < 0)
+ return -1;
+
+ VIR_DEBUG("fsDriver = %p", &fsDriver);
+
+ return 0;
+}
diff --git a/src/fs/fs_driver.h b/src/fs/fs_driver.h
new file mode 100644
index 0000000..aaf0258
--- /dev/null
+++ b/src/fs/fs_driver.h
@@ -0,0 +1,10 @@
+#ifndef __VIR_FS_DRIVER_H__
+# define __VIR_FS_DRIVER_H__
+
+# include <sys/stat.h>
+
+# include "domain_conf.h"
+# include "fs_conf.h"
+
+int fsRegister(void);
+#endif /* __VIR_FS_DRIVER_H__ */
diff --git a/src/libvirt.c b/src/libvirt.c
index a5e0e41..19ab516 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -116,6 +116,7 @@ static int virStateDriverTabCount;
static virNetworkDriverPtr virSharedNetworkDriver;
static virInterfaceDriverPtr virSharedInterfaceDriver;
static virStorageDriverPtr virSharedStorageDriver;
+static virFsDriverPtr virSharedFsDriver;
static virNodeDeviceDriverPtr virSharedNodeDeviceDriver;
static virSecretDriverPtr virSharedSecretDriver;
static virNWFilterDriverPtr virSharedNWFilterDriver;
@@ -587,7 +588,30 @@ virSetSharedStorageDriver(virStorageDriverPtr driver)
return 0;
}
+/**
+ * virSetSharedFsDriver:
+ * @driver: pointer to a fs driver block
+ *
+ * Register a fs virtualization driver
+ *
+ * Returns the driver priority or -1 in case of error.
+ */
+int
+virSetSharedFsDriver(virFsDriverPtr driver)
+{
+ virCheckNonNullArgReturn(driver, -1);
+ if (virSharedFsDriver) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("A fs driver is already registered"));
+ return -1;
+ }
+
+ VIR_DEBUG("registering %s as fs driver", driver->name);
+
+ virSharedFsDriver = driver;
+ return 0;
+}
/**
* virSetSharedNodeDeviceDriver:
* @driver: pointer to a device monitor block
@@ -707,6 +731,8 @@ virRegisterConnectDriver(virConnectDriverPtr driver,
driver->secretDriver = virSharedSecretDriver;
if (driver->storageDriver == NULL)
driver->storageDriver = virSharedStorageDriver;
+ if (driver->fsDriver == NULL)
+ driver->fsDriver = virSharedFsDriver;
}
virConnectDriverTab[virConnectDriverTabCount] = driver;
@@ -1083,6 +1109,7 @@ virConnectOpenInternal(const char *name,
ret->nwfilterDriver = virConnectDriverTab[i]->nwfilterDriver;
ret->secretDriver = virConnectDriverTab[i]->secretDriver;
ret->storageDriver = virConnectDriverTab[i]->storageDriver;
+ ret->fsDriver = virConnectDriverTab[i]->fsDriver;
res = virConnectDriverTab[i]->hypervisorDriver->connectOpen(ret, auth,
conf, flags);
VIR_DEBUG("driver %zu %s returned %s",
@@ -1101,6 +1128,7 @@ virConnectOpenInternal(const char *name,
ret->nwfilterDriver = NULL;
ret->secretDriver = NULL;
ret->storageDriver = NULL;
+ ret->fsDriver = NULL;
if (res == VIR_DRV_OPEN_ERROR)
goto failed;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4b87c37..b6c0e87 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1068,6 +1068,7 @@ virDomainMigratePrepareTunnel3;
virDomainMigratePrepareTunnel3Params;
virRegisterConnectDriver;
virRegisterStateDriver;
+virSetSharedFsDriver;
virSetSharedInterfaceDriver;
virSetSharedNetworkDriver;
virSetSharedNodeDeviceDriver;
--
1.8.3.1