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>
Signed-off-by: Maxim Nestratov <mnestratov(a)virtuozzo.com>
---
configure.ac | 38 +
daemon/Makefile.am | 4 +
m4/virt-driver-fspool.m4 | 52 ++
po/POTFILES.in | 2 +
src/Makefile.am | 38 +
src/driver.h | 1 +
src/fs/fs_backend.h | 107 +++
src/fs/fs_backend_dir.c | 355 ++++++++
src/fs/fs_backend_dir.h | 8 +
src/fs/fs_driver.c | 2058 ++++++++++++++++++++++++++++++++++++++++++++++
src/fs/fs_driver.h | 10 +
src/libvirt.c | 28 +
src/libvirt_private.syms | 1 +
13 files changed, 2702 insertions(+)
create mode 100644 m4/virt-driver-fspool.m4
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 f6076bd..5da4bf3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1074,6 +1074,11 @@ dnl
LIBVIRT_DRIVER_CHECK_BHYVE
+dnl
+dnl Checks for FS Driver
+dnl
+
+LIBVIRT_DRIVER_CHECK_FSPOOL
dnl
dnl check for kernel headers required by src/bridge.c
@@ -1647,6 +1652,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 +2794,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([])
+LIBVIRT_DRIVER_RESULT_FS
+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/m4/virt-driver-fspool.m4 b/m4/virt-driver-fspool.m4
new file mode 100644
index 0000000..0f1a569
--- /dev/null
+++ b/m4/virt-driver-fspool.m4
@@ -0,0 +1,52 @@
+dnl The File Systems Driver
+dnl
+dnl Copyright (C) 2016 Parallels IP Holdings GmbH
+dnl
+dnl This library is free software; you can redistribute it and/or
+dnl modify it under the terms of the GNU Lesser General Public
+dnl License as published by the Free Software Foundation; either
+dnl version 2.1 of the License, or (at your option) any later version.
+dnl
+dnl This library is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl Lesser General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU Lesser General Public
+dnl License along with this library. If not, see
+dnl <
http://www.gnu.org/licenses/>.
+dnl
+
+AC_DEFUN([LIBVIRT_DRIVER_CHECK_FSPOOL],[
+ AC_ARG_WITH([fs-dir],
+ [AS_HELP_STRING([--with-fs-dir],
+ [with direcktory backend for FS driver @<:@default=yes@:>@])],
+ [],[with_fs_dir=yes])
+ m4_divert_text([DEFAULTS], [with_fs=check])
+
+ 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_DEFUN([LIBVIRT_DRIVER_RESULT_FS],[
+ AC_MSG_NOTICE([ FS Driver: $with_fs_dir])
+])
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f4d2f25..0fc3b79 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -59,6 +59,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 d3c6e67..0388c7d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -644,6 +644,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) \
@@ -664,6 +665,7 @@ STATEFUL_DRIVER_SOURCE_FILES = \
$(QEMU_DRIVER_SOURCES) \
$(SECRET_DRIVER_SOURCES) \
$(STORAGE_DRIVER_SOURCES) \
+ $(FS_DRIVER_SOURCES) \
$(UML_DRIVER_SOURCES) \
$(XEN_DRIVER_SOURCES) \
$(VZ_DRIVER_SOURCES) \
@@ -971,6 +973,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 \
@@ -1637,6 +1647,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 = \
@@ -1908,6 +1944,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 64f7460..07cb3fb 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..f7e0bec
--- /dev/null
+++ b/src/fs/fs_backend.h
@@ -0,0 +1,107 @@
+/*
+ * fs_backend.h: file system backend implementation
+ * Author: Olga Krishtal <okrishtal(a)virtuozzo.com>
+ *
+ * Copyright (C) 2016 Parallels IP Holdings GmbH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __VIR_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..60f18c6
--- /dev/null
+++ b/src/fs/fs_backend_dir.c
@@ -0,0 +1,355 @@
+/*
+ * fs_backend_dir.c: file system backend implementation
+ * Author: Olga Krishtal <okrishtal(a)virtuozzo.com>
+ *
+ * Copyright (C) 2016 Parallels IP Holdings GmbH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <sys/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_FSITEM_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_FSITEM_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_FSPOOL_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..335e008
--- /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..21fa590
--- /dev/null
+++ b/src/fs/fs_driver.c
@@ -0,0 +1,2058 @@
+/*
+ * fs_driver.c: file system driver implementation
+ * Author: Olga Krishtal <okrishtal(a)virtuozzo.com>
+ *
+ * Copyright (C) 2016 Parallels IP Holdings GmbH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <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;
+
+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_FSPOOL,
+ _("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_FSITEM,
+ _("no fsitem 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_FSPOOL,
+ _("no fspool with matching uuid '%s' (%s)"),
+ uuidstr, fspool->name);
+ }
+ fsDriverUnlock();
+
+ return ret;
+}
+
+static int
+fsConnectListAllFSPools(virConnectPtr conn,
+ virFSPoolPtr **fspools,
+ unsigned int flags)
+{
+ int ret = -1;
+
+ virCheckFlags(VIR_CONNECT_LIST_FSPOOLS_FILTERS_ALL, -1);
+
+ if (virConnectListAllFSPoolsEnsureACL(conn) < 0)
+ goto cleanup;
+
+ fsDriverLock();
+ ret = virFSPoolObjListExport(conn, driver->fspools, fspools,
+ virConnectListAllFSPoolsCheckACL,
+ flags);
+ fsDriverUnlock();
+
+ cleanup:
+ return ret;
+}
+
+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_FSPOOL,
+ _("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_FSPOOL,
+ _("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_FSPOOL,
+ _("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_FSPOOL_CREATE_WITH_BUILD |
+ VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE |
+ VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE, NULL);
+
+ VIR_EXCLUSIVE_FLAGS_RET(VIR_FSPOOL_BUILD_OVERWRITE,
+ VIR_FSPOOL_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_FSPOOL_CREATE_WITH_BUILD_OVERWRITE)
+ build_flags |= VIR_FSPOOL_BUILD_OVERWRITE;
+ else if (flags & VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE)
+ build_flags |= VIR_FSPOOL_BUILD_NO_OVERWRITE;
+
+ if (build_flags ||
+ (flags & VIR_FSPOOL_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_FSPOOL_CREATE_WITH_BUILD |
+ VIR_FSPOOL_CREATE_WITH_BUILD_OVERWRITE |
+ VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE, -1);
+
+ VIR_EXCLUSIVE_FLAGS_RET(VIR_FSPOOL_BUILD_OVERWRITE,
+ VIR_FSPOOL_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_FSPOOL_CREATE_WITH_BUILD_OVERWRITE)
+ build_flags |= VIR_FSPOOL_BUILD_OVERWRITE;
+ else if (flags & VIR_FSPOOL_CREATE_WITH_BUILD_NO_OVERWRITE)
+ build_flags |= VIR_FSPOOL_BUILD_NO_OVERWRITE;
+
+ if (build_flags ||
+ (flags & VIR_FSPOOL_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_FSPOOL,
+ _("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_FSPOOL,
+ _("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_FSPOOL,
+ _("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_FSPOOL_RUNNING;
+ else
+ info->state = VIR_FSPOOL_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_FSPOOL,
+ _("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_FSITEM,
+ _("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_FSITEM,
+ _("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_FSITEM,
+ _("no fspool item with matching path '%s'"),
path);
+ } else {
+ virReportError(VIR_ERR_NO_FSITEM,
+ _("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_FSITEM_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 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_FSPOOL,
+ _("no fspool with matching uuid '%s' (%s)"),
+ uuidstr, obj->name);
+ goto cleanup;
+ }
+
+ if (STRNEQ(obj->name, vobj->fspool) && !origpool) {
+ virReportError(VIR_ERR_NO_FSPOOL,
+ _("no fspool with matching name '%s'"),
+ vobj->fspool);
+ goto cleanup;
+ }
+
+ if (!virFSPoolObjIsActive(fspool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fspool '%s' is not active"),
fspool->def->name);
+ goto cleanup;
+ }
+
+ if (origpool && !virFSPoolObjIsActive(origpool)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("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_FSITEM,
+ _("no fsitem 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,
+ _("fsitem 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", _("fspool does not support"
+ " item creation from an existing item"));
+ goto cleanup;
+ }
+
+ if (origitem->building) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("fsitem '%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 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",
+ .connectListAllFSPools = fsConnectListAllFSPools, /* 2.3.0 */
+ .fsPoolLookupByName = fsPoolLookupByName, /* 2.3.0 */
+ .fsPoolLookupByUUID = fsPoolLookupByUUID, /* 2.3.0 */
+ .fsPoolLookupByItem = fsPoolLookupByItem, /* 2.3.0 */
+ .fsPoolCreateXML = fsPoolCreateXML, /* 2.3.0 */
+ .fsPoolDefineXML = fsPoolDefineXML, /* 2.3.0 */
+ .fsPoolBuild = fsPoolBuild, /* 2.3.0 */
+ .fsPoolCreate = fsPoolCreate, /* 2.3.0 */
+ .fsPoolUndefine = fsPoolUndefine, /* 2.3.0 */
+ .fsPoolDestroy = fsPoolDestroy, /* 2.3.0 */
+ .fsPoolDelete = fsPoolDelete, /* 2.3.0 */
+ .fsPoolRefresh = fsPoolRefresh, /* 2.3.0 */
+ .fsPoolGetInfo = fsPoolGetInfo, /* 2.3.0 */
+ .fsPoolGetXMLDesc = fsPoolGetXMLDesc, /* 2.3.0 */
+ .fsPoolGetAutostart = fsPoolGetAutostart, /* 2.3.0 */
+ .fsPoolSetAutostart = fsPoolSetAutostart, /* 2.3.0 */
+ .fsPoolNumOfItems = fsPoolNumOfItems, /* 2.3.0 */
+ .fsPoolListItems = fsPoolListItems, /* 2.3.0 */
+ .fsPoolListAllItems = fsPoolListAllItems, /* 2.3.0 */
+ .fsItemLookupByName = fsItemLookupByName, /* 2.3.0 */
+ .fsItemLookupByKey = fsItemLookupByKey, /* 2.3.0 */
+ .fsItemLookupByPath = fsItemLookupByPath, /* 2.3.0 */
+ .fsItemCreateXML = fsItemCreateXML, /* 2.3.0 */
+ .fsItemCreateXMLFrom = fsItemCreateXMLFrom, /* 2.3.0 */
+ .fsItemDelete = fsItemDelete, /* 2.3.0 */
+ .fsItemGetInfo = fsItemGetInfo, /* 2.3.0 */
+ .fsItemGetXMLDesc = fsItemGetXMLDesc, /* 2.3.0 */
+ .fsItemGetPath = fsItemGetPath, /* 2.3.0 */
+ .fsPoolIsActive = fsPoolIsActive, /* 2.3.0 */
+ .fsPoolIsPersistent = fsPoolIsPersistent, /* 2.3.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 52462e3..775abdb 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;
@@ -1089,6 +1115,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",
@@ -1107,6 +1134,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 308dcc2..21ceeff 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1088,6 +1088,7 @@ virDomainMigratePrepareTunnel3;
virDomainMigratePrepareTunnel3Params;
virRegisterConnectDriver;
virRegisterStateDriver;
+virSetSharedFSDriver;
virSetSharedInterfaceDriver;
virSetSharedNetworkDriver;
virSetSharedNodeDeviceDriver;
--
1.8.3.1