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> Signed-off-by: Maxim Nestratov <mnestratov@xxxxxxxxxxxxx> --- 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@xxxxxxxxxxxxx> + * + * 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@xxxxxxxxxxxxx> + * + * 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@xxxxxxxxxxxxx> + * + * 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list