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@xxxxxxxxxxxxx> --- 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 8d7d63e..435b1ec 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 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 db69fbb..6275f9e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -642,6 +642,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) \ @@ -662,6 +663,7 @@ STATEFUL_DRIVER_SOURCE_FILES = \ $(QEMU_DRIVER_SOURCES) \ $(SECRET_DRIVER_SOURCES) \ $(STORAGE_DRIVER_SOURCES) \ + $(FS_DRIVER_SOURCES) \ $(UML_DRIVER_SOURCES) \ $(XEN_DRIVER_SOURCES) \ $(NULL) @@ -968,6 +970,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 \ @@ -1631,6 +1641,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 = \ @@ -1902,6 +1938,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 52462e3..cccebdd 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 68150d6..f504143 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1085,6 +1085,7 @@ virDomainMigratePrepareTunnel3; virDomainMigratePrepareTunnel3Params; virRegisterConnectDriver; virRegisterStateDriver; +virSetSharedFsDriver; virSetSharedInterfaceDriver; virSetSharedNetworkDriver; virSetSharedNodeDeviceDriver; -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list