<commit_before>/*
 * storage_driver.c: core driver for storage APIs
 *
 * Copyright (C) 2006-2015 Red Hat, Inc.
 * Copyright (C) 2006-2008 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Author: <NAME> <<EMAIL>>
 */

#include <config.h>

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>

#if HAVE_PWD_H
# include <pwd.h>
#endif
#include <errno.h>
#include <string.h>

#include "virerror.h"
#include "datatypes.h"
#include "driver.h"
#include "storage_driver.h"
#include "storage_conf.h"
#include "storage_event.h"
#include "viralloc.h"
#include "storage_backend.h"
#include "virlog.h"
#include "virfile.h"
#include "virfdstream.h"
#include "configmake.h"
#include "virsecret.h"
#include "virstring.h"
#include "viraccessapicheck.h"
//#include "dirname.h"
#include "storage_util.h"

#define VIR_FROM_THIS VIR_FROM_STORAGE

VIR_LOG_INIT("storage.storage_driver");

static virStorageDriverStatePtr driver;

static int storageStateCleanup(void);

typedef struct _virStorageVolStreamInfo virStorageVolStreamInfo;
typedef virStorageVolStreamInfo *virStorageVolStreamInfoPtr;
struct _virStorageVolStreamInfo {
    char *pool_name;
    char *vol_path;
};

static void storageDriverLock(void)
{
    virMutexLock(&driver->lock);
}
static void storageDriverUnlock(void)
{
    virMutexUnlock(&driver->lock);
}


/**
 * virStoragePoolUpdateInactive:
 * @poolptr: pointer to a variable holding the pool object pointer
 *
 * This function is supposed to be called after a pool becomes inactive. The
 * function switches to the new config object for persistent pools. Inactive
 * pools are removed.
 */
static void
virStoragePoolUpdateInactive(virStoragePoolObjPtr *objptr)
{
    virStoragePoolObjPtr obj = *objptr;

    if (!virStoragePoolObjGetConfigFile(obj)) {
        virStoragePoolObjRemove(&driver->pools, obj);
        *objptr = NULL;
    } else if (virStoragePoolObjGetNewDef(obj)) {
        virStoragePoolObjDefUseNewDef(obj);
    }
}


static void
storagePoolUpdateState(virStoragePoolObjPtr obj)
{
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(obj);
    bool active = false;
    virStorageBackendPtr backend;
    char *stateFile;

    if (!(stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml")))
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Missing backend %d"), def->type);
        goto cleanup;
    }

    /* Backends which do not support 'checkPool' are considered
     * inactive by default. */
    if (backend->checkPool &&
        backend->checkPool(obj, &active) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to initialize storage pool '%s': %s"),
                       def->name, virGetLastErrorMessage());
        active = false;
    }

    /* 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 pools.
     */
    if (active) {
        virStoragePoolObjClearVols(obj);
        if (backend->refreshPool(NULL, obj) < 0) {
            if (backend->stopPool)
                backend->stopPool(NULL, obj);
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Failed to restart storage pool '%s': %s"),
                           def->name, virGetLastErrorMessage());
            active = false;
        }
    }

    virStoragePoolObjSetActive(obj, active);

    if (!virStoragePoolObjIsActive(obj))
        virStoragePoolUpdateInactive(&obj);

 cleanup:
    if (!active && stateFile)
        ignore_value(unlink(stateFile));
    VIR_FREE(stateFile);

    return;
}

static void
storagePoolUpdateAllState(void)
{
    size_t i;

    for (i = 0; i < driver->pools.count; i++) {
        virStoragePoolObjPtr obj = driver->pools.objs[i];

        virStoragePoolObjLock(obj);
        storagePoolUpdateState(obj);
        virStoragePoolObjUnlock(obj);
    }
}

static void
storageDriverAutostart(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->pools.count; i++) {
        virStoragePoolObjPtr obj = driver->pools.objs[i];
        virStoragePoolDefPtr def = virStoragePoolObjGetDef(obj);
        virStorageBackendPtr backend;
        bool started = false;

        virStoragePoolObjLock(obj);
        if ((backend = virStorageBackendForType(def->type)) == NULL) {
            virStoragePoolObjUnlock(obj);
            continue;
        }

        if (virStoragePoolObjIsAutostart(obj) &&
            !virStoragePoolObjIsActive(obj)) {
            if (backend->startPool &&
                backend->startPool(conn, obj) < 0) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("Failed to autostart storage pool '%s': %s"),
                               def->name, virGetLastErrorMessage());
                virStoragePoolObjUnlock(obj);
                continue;
            }
            started = true;
        }

        if (started) {
            char *stateFile;

            virStoragePoolObjClearVols(obj);
            stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml");
            if (!stateFile ||
                virStoragePoolSaveState(stateFile, def) < 0 ||
                backend->refreshPool(conn, obj) < 0) {
                if (stateFile)
                    unlink(stateFile);
                if (backend->stopPool)
                    backend->stopPool(conn, obj);
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("Failed to autostart storage pool '%s': %s"),
                               def->name, virGetLastErrorMessage());
            } else {
                virStoragePoolObjSetActive(obj, true);
            }
            VIR_FREE(stateFile);
        }
        virStoragePoolObjUnlock(obj);
    }

    virObjectUnref(conn);
}

/**
 * virStorageStartup:
 *
 * Initialization function for the Storage Driver
 */
static int
storageStateInitialize(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;
    }
    storageDriverLock();

    if (privileged) {
        if (VIR_STRDUP(driver->configDir,
                       SYSCONFDIR "/libvirt/storage") < 0 ||
            VIR_STRDUP(driver->autostartDir,
                       SYSCONFDIR "/libvirt/storage/autostart") < 0 ||
            VIR_STRDUP(driver->stateDir,
                       LOCALSTATEDIR "/run/libvirt/storage") < 0)
            goto error;
    } else {
        configdir = virGetUserConfigDirectory();
        rundir = virGetUserRuntimeDirectory();
        if (!(configdir && rundir))
            goto error;

        if ((virAsprintf(&driver->configDir,
                        "%s/storage", configdir) < 0) ||
            (virAsprintf(&driver->autostartDir,
                        "%s/storage/autostart", configdir) < 0) ||
            (virAsprintf(&driver->stateDir,
                         "%s/storage/run", rundir) < 0))
            goto error;
    }
    driver->privileged = privileged;

    if (virFileMakePath(driver->stateDir) < 0) {
        virReportError(errno,
                       _("cannot create directory %s"),
                       driver->stateDir);
        goto error;
    }

    if (virStoragePoolObjLoadAllState(&driver->pools,
                                      driver->stateDir) < 0)
        goto error;

    if (virStoragePoolObjLoadAllConfigs(&driver->pools,
                                        driver->configDir,
                                        driver->autostartDir) < 0)
        goto error;

    storagePoolUpdateAllState();

    driver->storageEventState = virObjectEventStateNew();

    storageDriverUnlock();

    ret = 0;
 cleanup:
    VIR_FREE(configdir);
    VIR_FREE(rundir);
    return ret;

 error:
    storageDriverUnlock();
    storageStateCleanup();
    goto cleanup;
}

/**
 * storageStateAutoStart:
 *
 * Function to auto start the storage driver
 */
static void
storageStateAutoStart(void)
{
    if (!driver)
        return;

    storageDriverLock();
    storageDriverAutostart();
    storageDriverUnlock();
}

/**
 * storageStateReload:
 *
 * Function to restart the storage driver, it will recheck the configuration
 * files and update its state
 */
static int
storageStateReload(void)
{
    if (!driver)
        return -1;

    storageDriverLock();
    virStoragePoolObjLoadAllState(&driver->pools,
                                  driver->stateDir);
    virStoragePoolObjLoadAllConfigs(&driver->pools,
                                    driver->configDir,
                                    driver->autostartDir);
    storageDriverAutostart();
    storageDriverUnlock();

    return 0;
}


/**
 * storageStateCleanup
 *
 * Shutdown the storage driver, it will stop all active storage pools
 */
static int
storageStateCleanup(void)
{
    if (!driver)
        return -1;

    storageDriverLock();

    virObjectUnref(driver->storageEventState);

    /* free inactive pools */
    virStoragePoolObjListFree(&driver->pools);

    VIR_FREE(driver->configDir);
    VIR_FREE(driver->autostartDir);
    VIR_FREE(driver->stateDir);
    storageDriverUnlock();
    virMutexDestroy(&driver->lock);
    VIR_FREE(driver);

    return 0;
}


static virStoragePoolObjPtr
storagePoolObjFindByUUID(const unsigned char *uuid,
                         const char *name)
{
    virStoragePoolObjPtr obj;
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    if (!(obj = virStoragePoolObjFindByUUID(&driver->pools, uuid))) {
        virUUIDFormat(uuid, uuidstr);
        if (name)
            virReportError(VIR_ERR_NO_STORAGE_POOL,
                           _("no storage pool with matching uuid '%s' (%s)"),
                           uuidstr, name);
        else
            virReportError(VIR_ERR_NO_STORAGE_POOL,
                           _("no storage pool with matching uuid '%s'"),
                           uuidstr);
    }

    return obj;
}


static virStoragePoolObjPtr
virStoragePoolObjFromStoragePool(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr ret;

    storageDriverLock();
    ret = storagePoolObjFindByUUID(pool->uuid, pool->name);
    storageDriverUnlock();

    return ret;
}


static virStoragePoolObjPtr
storagePoolObjFindByName(const char *name)
{
    virStoragePoolObjPtr obj;

    storageDriverLock();
    if (!(obj = virStoragePoolObjFindByName(&driver->pools, name)))
        virReportError(VIR_ERR_NO_STORAGE_POOL,
                       _("no storage pool with matching name '%s'"), name);
    storageDriverUnlock();

    return obj;
}


static virStoragePoolPtr
storagePoolLookupByUUID(virConnectPtr conn,
                        const unsigned char *uuid)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;

    storageDriverLock();
    obj = storagePoolObjFindByUUID(uuid, NULL);
    storageDriverUnlock();
    if (!obj)
        return NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolLookupByUUIDEnsureACL(conn, def) < 0)
        goto cleanup;

    pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    virStoragePoolObjUnlock(obj);
    return pool;
}

static virStoragePoolPtr
storagePoolLookupByName(virConnectPtr conn,
                        const char *name)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;

    if (!(obj = storagePoolObjFindByName(name)))
        return NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolLookupByNameEnsureACL(conn, def) < 0)
        goto cleanup;

    pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    virStoragePoolObjUnlock(obj);
    return pool;
}

static virStoragePoolPtr
storagePoolLookupByVolume(virStorageVolPtr vol)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;

    if (!(obj = storagePoolObjFindByName(vol->pool)))
        return NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolLookupByVolumeEnsureACL(vol->conn, def) < 0)
        goto cleanup;

    pool = virGetStoragePool(vol->conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    virStoragePoolObjUnlock(obj);
    return pool;
}

static int
storageConnectNumOfStoragePools(virConnectPtr conn)
{
    int nactive = 0;

    if (virConnectNumOfStoragePoolsEnsureACL(conn) < 0)
        return -1;

    storageDriverLock();
    nactive = virStoragePoolObjNumOfStoragePools(&driver->pools, conn, true,
                                                 virConnectNumOfStoragePoolsCheckACL);
    storageDriverUnlock();

    return nactive;
}


static int
storageConnectListStoragePools(virConnectPtr conn,
                               char **const names,
                               int maxnames)
{
    int got = 0;

    if (virConnectListStoragePoolsEnsureACL(conn) < 0)
        return -1;

    storageDriverLock();
    got = virStoragePoolObjGetNames(&driver->pools, conn, true,
                                    virConnectListStoragePoolsCheckACL,
                                    names, maxnames);
    storageDriverUnlock();
    return got;
}

static int
storageConnectNumOfDefinedStoragePools(virConnectPtr conn)
{
    int nactive = 0;

    if (virConnectNumOfDefinedStoragePoolsEnsureACL(conn) < 0)
        return -1;

    storageDriverLock();
    nactive = virStoragePoolObjNumOfStoragePools(&driver->pools, conn, false,
                                                 virConnectNumOfDefinedStoragePoolsCheckACL);
    storageDriverUnlock();

    return nactive;
}


static int
storageConnectListDefinedStoragePools(virConnectPtr conn,
                                      char **const names,
                                      int maxnames)
{
    int got = 0;

    if (virConnectListDefinedStoragePoolsEnsureACL(conn) < 0)
        return -1;

    storageDriverLock();
    got = virStoragePoolObjGetNames(&driver->pools, conn, false,
                                    virConnectListDefinedStoragePoolsCheckACL,
                                    names, maxnames);
    storageDriverUnlock();
    return got;
}

/* This method is required to be re-entrant / thread safe, so
   uses no driver lock */
static char *
storageConnectFindStoragePoolSources(virConnectPtr conn,
                                     const char *type,
                                     const char *srcSpec,
                                     unsigned int flags)
{
    int backend_type;
    virStorageBackendPtr backend;
    char *ret = NULL;

    if (virConnectFindStoragePoolSourcesEnsureACL(conn) < 0)
        return NULL;

    backend_type = virStoragePoolTypeFromString(type);
    if (backend_type < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("unknown storage pool type %s"), type);
        goto cleanup;
    }

    backend = virStorageBackendForType(backend_type);
    if (backend == NULL)
        goto cleanup;

    if (!backend->findPoolSources) {
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("pool type '%s' does not support source "
                         "discovery"), type);
        goto cleanup;
    }

    ret = backend->findPoolSources(conn, srcSpec, flags);

 cleanup:
    return ret;
}


static int
storagePoolIsActive(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolIsActiveEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    ret = virStoragePoolObjIsActive(obj);

 cleanup:
    virStoragePoolObjUnlock(obj);
    return ret;
}


static int
storagePoolIsPersistent(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolIsPersistentEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    ret = virStoragePoolObjGetConfigFile(obj) ? 1 : 0;

 cleanup:
    virStoragePoolObjUnlock(obj);
    return ret;
}


static virStoragePoolPtr
storagePoolCreateXML(virConnectPtr conn,
                     const char *xml,
                     unsigned int flags)
{
    virStoragePoolDefPtr newDef;
    virStoragePoolObjPtr obj = NULL;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    char *stateFile = NULL;
    unsigned int build_flags = 0;

    virCheckFlags(VIR_STORAGE_POOL_CREATE_WITH_BUILD |
                  VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE |
                  VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE, NULL);

    VIR_EXCLUSIVE_FLAGS_RET(VIR_STORAGE_POOL_BUILD_OVERWRITE,
                            VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, NULL);

    storageDriverLock();
    if (!(newDef = virStoragePoolDefParseString(xml)))
        goto cleanup;

    if (virStoragePoolCreateXMLEnsureACL(conn, newDef) < 0)
        goto cleanup;

    if (virStoragePoolObjIsDuplicate(&driver->pools, newDef, 1) < 0)
        goto cleanup;

    if (virStoragePoolObjSourceFindDuplicate(conn, &driver->pools, newDef) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(newDef->type)) == NULL)
        goto cleanup;

    if (!(obj = virStoragePoolObjAssignDef(&driver->pools, newDef)))
        goto cleanup;
    newDef = NULL;
    def = virStoragePoolObjGetDef(obj);

    if (backend->buildPool) {
        if (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE)
            build_flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
        else if (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE)
            build_flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;

        if (build_flags ||
            (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD)) {
            if (backend->buildPool(conn, obj, build_flags) < 0) {
                virStoragePoolObjRemove(&driver->pools, obj);
                obj = NULL;
                goto cleanup;
            }
        }
    }

    if (backend->startPool &&
        backend->startPool(conn, obj) < 0) {
        virStoragePoolObjRemove(&driver->pools, obj);
        obj = NULL;
        goto cleanup;
    }

    stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml");

    virStoragePoolObjClearVols(obj);
    if (!stateFile || virStoragePoolSaveState(stateFile, def) < 0 ||
        backend->refreshPool(conn, obj) < 0) {
        if (stateFile)
            unlink(stateFile);
        if (backend->stopPool)
            backend->stopPool(conn, obj);
        virStoragePoolObjRemove(&driver->pools, obj);
        obj = NULL;
        goto cleanup;
    }

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_STARTED,
                                            0);

    VIR_INFO("Creating storage pool '%s'", def->name);
    virStoragePoolObjSetActive(obj, true);

    pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    VIR_FREE(stateFile);
    virStoragePoolDefFree(newDef);
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    if (obj)
        virStoragePoolObjUnlock(obj);
    storageDriverUnlock();
    return pool;
}

static virStoragePoolPtr
storagePoolDefineXML(virConnectPtr conn,
                     const char *xml,
                     unsigned int flags)
{
    virStoragePoolDefPtr newDef;
    virStoragePoolObjPtr obj = NULL;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;
    virObjectEventPtr event = NULL;

    virCheckFlags(0, NULL);

    storageDriverLock();
    if (!(newDef = virStoragePoolDefParseString(xml)))
        goto cleanup;

    if (virXMLCheckIllegalChars("name", newDef->name, "\n") < 0)
        goto cleanup;

    if (virStoragePoolDefineXMLEnsureACL(conn, newDef) < 0)
        goto cleanup;

    if (virStoragePoolObjIsDuplicate(&driver->pools, newDef, 0) < 0)
        goto cleanup;

    if (virStoragePoolObjSourceFindDuplicate(conn, &driver->pools, newDef) < 0)
        goto cleanup;

    if (virStorageBackendForType(newDef->type) == NULL)
        goto cleanup;

    if (!(obj = virStoragePoolObjAssignDef(&driver->pools, newDef)))
        goto cleanup;
    newDef = NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolObjSaveDef(driver, obj, def) < 0) {
        virStoragePoolObjRemove(&driver->pools, obj);
        obj = NULL;
        goto cleanup;
    }

    event = virStoragePoolEventLifecycleNew(def->name, def->uuid,
                                            VIR_STORAGE_POOL_EVENT_DEFINED,
                                            0);

    VIR_INFO("Defining storage pool '%s'", def->name);
    pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    virStoragePoolDefFree(newDef);
    if (obj)
        virStoragePoolObjUnlock(obj);
    storageDriverUnlock();
    return pool;
}

static int
storagePoolUndefine(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    const char *autostartLink;
    virObjectEventPtr event = NULL;
    int ret = -1;

    storageDriverLock();
    if (!(obj = storagePoolObjFindByUUID(pool->uuid, pool->name)))
        goto cleanup;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolUndefineEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if (virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is still active"),
                       def->name);
        goto cleanup;
    }

    if (virStoragePoolObjGetAsyncjobs(obj) > 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("pool '%s' has asynchronous jobs running."),
                       def->name);
        goto cleanup;
    }

    autostartLink = virStoragePoolObjGetAutostartLink(obj);
    if (virStoragePoolObjDeleteDef(obj) < 0)
        goto cleanup;

    if (autostartLink && unlink(autostartLink) < 0 &&
        errno != ENOENT && errno != ENOTDIR) {
        char ebuf[1024];
        VIR_ERROR(_("Failed to delete autostart link '%s': %s"),
                  autostartLink, virStrerror(errno, ebuf, sizeof(ebuf)));
    }

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_UNDEFINED,
                                            0);

    VIR_INFO("Undefining storage pool '%s'", def->name);
    virStoragePoolObjRemove(&driver->pools, obj);
    obj = NULL;
    ret = 0;

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    if (obj)
        virStoragePoolObjUnlock(obj);
    storageDriverUnlock();
    return ret;
}

static int
storagePoolCreate(virStoragePoolPtr pool,
                  unsigned int flags)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    int ret = -1;
    char *stateFile = NULL;
    unsigned int build_flags = 0;

    virCheckFlags(VIR_STORAGE_POOL_CREATE_WITH_BUILD |
                  VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE |
                  VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE, -1);

    VIR_EXCLUSIVE_FLAGS_RET(VIR_STORAGE_POOL_BUILD_OVERWRITE,
                            VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, -1);

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolCreateEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL)
        goto cleanup;

    if (virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is already active"),
                       def->name);
        goto cleanup;
    }

    if (backend->buildPool) {
        if (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE)
            build_flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
        else if (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE)
            build_flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;

        if (build_flags ||
            (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD)) {
            if (backend->buildPool(pool->conn, obj, build_flags) < 0)
                goto cleanup;
        }
    }

    VIR_INFO("Starting up storage pool '%s'", def->name);
    if (backend->startPool &&
        backend->startPool(pool->conn, obj) < 0)
        goto cleanup;

    stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml");

    virStoragePoolObjClearVols(obj);
    if (!stateFile || virStoragePoolSaveState(stateFile, def) < 0 ||
        backend->refreshPool(pool->conn, obj) < 0) {
        if (stateFile)
            unlink(stateFile);
        if (backend->stopPool)
            backend->stopPool(pool->conn, obj);
        goto cleanup;
    }

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_STARTED,
                                            0);

    virStoragePoolObjSetActive(obj, true);
    ret = 0;

 cleanup:
    VIR_FREE(stateFile);
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    if (obj)
        virStoragePoolObjUnlock(obj);
    return ret;
}

static int
storagePoolBuild(virStoragePoolPtr pool,
                 unsigned int flags)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolBuildEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL)
        goto cleanup;

    if (virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is already active"),
                       def->name);
        goto cleanup;
    }

    if (backend->buildPool &&
        backend->buildPool(pool->conn, obj, flags) < 0)
        goto cleanup;

    event = virStoragePoolEventLifecycleNew(obj->def->name,
                                            obj->def->uuid,
                                            VIR_STORAGE_POOL_EVENT_CREATED,
                                            0);

    ret = 0;

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    virStoragePoolObjUnlock(obj);
    return ret;
}


static int
storagePoolDestroy(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    char *stateFile = NULL;
    int ret = -1;

    storageDriverLock();
    if (!(obj = storagePoolObjFindByUUID(pool->uuid, pool->name)))
        goto cleanup;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolDestroyEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL)
        goto cleanup;

    VIR_INFO("Destroying storage pool '%s'", def->name);

    if (!virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is not active"), def->name);
        goto cleanup;
    }

    if (virStoragePoolObjGetAsyncjobs(obj) > 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("pool '%s' has asynchronous jobs running."),
                       def->name);
        goto cleanup;
    }

    if (!(stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml")))
        goto cleanup;

    unlink(stateFile);
    VIR_FREE(stateFile);

    if (backend->stopPool &&
        backend->stopPool(pool->conn, obj) < 0)
        goto cleanup;

    virStoragePoolObjClearVols(obj);

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_STOPPED,
                                            0);

    virStoragePoolObjSetActive(obj, false);

    virStoragePoolUpdateInactive(&obj);

    ret = 0;

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    if (obj)
        virStoragePoolObjUnlock(obj);
    storageDriverUnlock();
    return ret;
}

static int
storagePoolDelete(virStoragePoolPtr pool,
                  unsigned int flags)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    char *stateFile = NULL;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolDeleteEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL)
        goto cleanup;

    VIR_INFO("Deleting storage pool '%s'", def->name);

    if (virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is still active"),
                       def->name);
        goto cleanup;
    }

    if (virStoragePoolObjGetAsyncjobs(obj) > 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("pool '%s' has asynchronous jobs running."),
                       def->name);
        goto cleanup;
    }

    if (!(stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml")))
        goto cleanup;

    unlink(stateFile);
    VIR_FREE(stateFile);

    if (!backend->deletePool) {
        virReportError(VIR_ERR_NO_SUPPORT,
                       "%s", _("pool does not support pool deletion"));
        goto cleanup;
    }
    if (backend->deletePool(pool->conn, obj, flags) < 0)
        goto cleanup;

    event = virStoragePoolEventLifecycleNew(obj->def->name,
                                            obj->def->uuid,
                                            VIR_STORAGE_POOL_EVENT_DELETED,
                                            0);

    ret = 0;

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    virStoragePoolObjUnlock(obj);
    return ret;
}


static int
storagePoolRefresh(virStoragePoolPtr pool,
                   unsigned int flags)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;<commit_msg>storage: Use virStoragePoolObjGetDef accessor for new driver events<commit_after>/*
 * storage_driver.c: core driver for storage APIs
 *
 * Copyright (C) 2006-2015 Red Hat, Inc.
 * Copyright (C) 2006-2008 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * Author: <NAME> <<EMAIL>>
 */

#include <config.h>

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>

#if HAVE_PWD_H
# include <pwd.h>
#endif
#include <errno.h>
#include <string.h>

#include "virerror.h"
#include "datatypes.h"
#include "driver.h"
#include "storage_driver.h"
#include "storage_conf.h"
#include "storage_event.h"
#include "viralloc.h"
#include "storage_backend.h"
#include "virlog.h"
#include "virfile.h"
#include "virfdstream.h"
#include "configmake.h"
#include "virsecret.h"
#include "virstring.h"
#include "viraccessapicheck.h"
//#include "dirname.h"
#include "storage_util.h"

#define VIR_FROM_THIS VIR_FROM_STORAGE

VIR_LOG_INIT("storage.storage_driver");

static virStorageDriverStatePtr driver;

static int storageStateCleanup(void);

typedef struct _virStorageVolStreamInfo virStorageVolStreamInfo;
typedef virStorageVolStreamInfo *virStorageVolStreamInfoPtr;
struct _virStorageVolStreamInfo {
    char *pool_name;
    char *vol_path;
};

static void storageDriverLock(void)
{
    virMutexLock(&driver->lock);
}
static void storageDriverUnlock(void)
{
    virMutexUnlock(&driver->lock);
}


/**
 * virStoragePoolUpdateInactive:
 * @poolptr: pointer to a variable holding the pool object pointer
 *
 * This function is supposed to be called after a pool becomes inactive. The
 * function switches to the new config object for persistent pools. Inactive
 * pools are removed.
 */
static void
virStoragePoolUpdateInactive(virStoragePoolObjPtr *objptr)
{
    virStoragePoolObjPtr obj = *objptr;

    if (!virStoragePoolObjGetConfigFile(obj)) {
        virStoragePoolObjRemove(&driver->pools, obj);
        *objptr = NULL;
    } else if (virStoragePoolObjGetNewDef(obj)) {
        virStoragePoolObjDefUseNewDef(obj);
    }
}


static void
storagePoolUpdateState(virStoragePoolObjPtr obj)
{
    virStoragePoolDefPtr def = virStoragePoolObjGetDef(obj);
    bool active = false;
    virStorageBackendPtr backend;
    char *stateFile;

    if (!(stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml")))
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Missing backend %d"), def->type);
        goto cleanup;
    }

    /* Backends which do not support 'checkPool' are considered
     * inactive by default. */
    if (backend->checkPool &&
        backend->checkPool(obj, &active) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to initialize storage pool '%s': %s"),
                       def->name, virGetLastErrorMessage());
        active = false;
    }

    /* 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 pools.
     */
    if (active) {
        virStoragePoolObjClearVols(obj);
        if (backend->refreshPool(NULL, obj) < 0) {
            if (backend->stopPool)
                backend->stopPool(NULL, obj);
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Failed to restart storage pool '%s': %s"),
                           def->name, virGetLastErrorMessage());
            active = false;
        }
    }

    virStoragePoolObjSetActive(obj, active);

    if (!virStoragePoolObjIsActive(obj))
        virStoragePoolUpdateInactive(&obj);

 cleanup:
    if (!active && stateFile)
        ignore_value(unlink(stateFile));
    VIR_FREE(stateFile);

    return;
}

static void
storagePoolUpdateAllState(void)
{
    size_t i;

    for (i = 0; i < driver->pools.count; i++) {
        virStoragePoolObjPtr obj = driver->pools.objs[i];

        virStoragePoolObjLock(obj);
        storagePoolUpdateState(obj);
        virStoragePoolObjUnlock(obj);
    }
}

static void
storageDriverAutostart(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->pools.count; i++) {
        virStoragePoolObjPtr obj = driver->pools.objs[i];
        virStoragePoolDefPtr def = virStoragePoolObjGetDef(obj);
        virStorageBackendPtr backend;
        bool started = false;

        virStoragePoolObjLock(obj);
        if ((backend = virStorageBackendForType(def->type)) == NULL) {
            virStoragePoolObjUnlock(obj);
            continue;
        }

        if (virStoragePoolObjIsAutostart(obj) &&
            !virStoragePoolObjIsActive(obj)) {
            if (backend->startPool &&
                backend->startPool(conn, obj) < 0) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("Failed to autostart storage pool '%s': %s"),
                               def->name, virGetLastErrorMessage());
                virStoragePoolObjUnlock(obj);
                continue;
            }
            started = true;
        }

        if (started) {
            char *stateFile;

            virStoragePoolObjClearVols(obj);
            stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml");
            if (!stateFile ||
                virStoragePoolSaveState(stateFile, def) < 0 ||
                backend->refreshPool(conn, obj) < 0) {
                if (stateFile)
                    unlink(stateFile);
                if (backend->stopPool)
                    backend->stopPool(conn, obj);
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("Failed to autostart storage pool '%s': %s"),
                               def->name, virGetLastErrorMessage());
            } else {
                virStoragePoolObjSetActive(obj, true);
            }
            VIR_FREE(stateFile);
        }
        virStoragePoolObjUnlock(obj);
    }

    virObjectUnref(conn);
}

/**
 * virStorageStartup:
 *
 * Initialization function for the Storage Driver
 */
static int
storageStateInitialize(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;
    }
    storageDriverLock();

    if (privileged) {
        if (VIR_STRDUP(driver->configDir,
                       SYSCONFDIR "/libvirt/storage") < 0 ||
            VIR_STRDUP(driver->autostartDir,
                       SYSCONFDIR "/libvirt/storage/autostart") < 0 ||
            VIR_STRDUP(driver->stateDir,
                       LOCALSTATEDIR "/run/libvirt/storage") < 0)
            goto error;
    } else {
        configdir = virGetUserConfigDirectory();
        rundir = virGetUserRuntimeDirectory();
        if (!(configdir && rundir))
            goto error;

        if ((virAsprintf(&driver->configDir,
                        "%s/storage", configdir) < 0) ||
            (virAsprintf(&driver->autostartDir,
                        "%s/storage/autostart", configdir) < 0) ||
            (virAsprintf(&driver->stateDir,
                         "%s/storage/run", rundir) < 0))
            goto error;
    }
    driver->privileged = privileged;

    if (virFileMakePath(driver->stateDir) < 0) {
        virReportError(errno,
                       _("cannot create directory %s"),
                       driver->stateDir);
        goto error;
    }

    if (virStoragePoolObjLoadAllState(&driver->pools,
                                      driver->stateDir) < 0)
        goto error;

    if (virStoragePoolObjLoadAllConfigs(&driver->pools,
                                        driver->configDir,
                                        driver->autostartDir) < 0)
        goto error;

    storagePoolUpdateAllState();

    driver->storageEventState = virObjectEventStateNew();

    storageDriverUnlock();

    ret = 0;
 cleanup:
    VIR_FREE(configdir);
    VIR_FREE(rundir);
    return ret;

 error:
    storageDriverUnlock();
    storageStateCleanup();
    goto cleanup;
}

/**
 * storageStateAutoStart:
 *
 * Function to auto start the storage driver
 */
static void
storageStateAutoStart(void)
{
    if (!driver)
        return;

    storageDriverLock();
    storageDriverAutostart();
    storageDriverUnlock();
}

/**
 * storageStateReload:
 *
 * Function to restart the storage driver, it will recheck the configuration
 * files and update its state
 */
static int
storageStateReload(void)
{
    if (!driver)
        return -1;

    storageDriverLock();
    virStoragePoolObjLoadAllState(&driver->pools,
                                  driver->stateDir);
    virStoragePoolObjLoadAllConfigs(&driver->pools,
                                    driver->configDir,
                                    driver->autostartDir);
    storageDriverAutostart();
    storageDriverUnlock();

    return 0;
}


/**
 * storageStateCleanup
 *
 * Shutdown the storage driver, it will stop all active storage pools
 */
static int
storageStateCleanup(void)
{
    if (!driver)
        return -1;

    storageDriverLock();

    virObjectUnref(driver->storageEventState);

    /* free inactive pools */
    virStoragePoolObjListFree(&driver->pools);

    VIR_FREE(driver->configDir);
    VIR_FREE(driver->autostartDir);
    VIR_FREE(driver->stateDir);
    storageDriverUnlock();
    virMutexDestroy(&driver->lock);
    VIR_FREE(driver);

    return 0;
}


static virStoragePoolObjPtr
storagePoolObjFindByUUID(const unsigned char *uuid,
                         const char *name)
{
    virStoragePoolObjPtr obj;
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    if (!(obj = virStoragePoolObjFindByUUID(&driver->pools, uuid))) {
        virUUIDFormat(uuid, uuidstr);
        if (name)
            virReportError(VIR_ERR_NO_STORAGE_POOL,
                           _("no storage pool with matching uuid '%s' (%s)"),
                           uuidstr, name);
        else
            virReportError(VIR_ERR_NO_STORAGE_POOL,
                           _("no storage pool with matching uuid '%s'"),
                           uuidstr);
    }

    return obj;
}


static virStoragePoolObjPtr
virStoragePoolObjFromStoragePool(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr ret;

    storageDriverLock();
    ret = storagePoolObjFindByUUID(pool->uuid, pool->name);
    storageDriverUnlock();

    return ret;
}


static virStoragePoolObjPtr
storagePoolObjFindByName(const char *name)
{
    virStoragePoolObjPtr obj;

    storageDriverLock();
    if (!(obj = virStoragePoolObjFindByName(&driver->pools, name)))
        virReportError(VIR_ERR_NO_STORAGE_POOL,
                       _("no storage pool with matching name '%s'"), name);
    storageDriverUnlock();

    return obj;
}


static virStoragePoolPtr
storagePoolLookupByUUID(virConnectPtr conn,
                        const unsigned char *uuid)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;

    storageDriverLock();
    obj = storagePoolObjFindByUUID(uuid, NULL);
    storageDriverUnlock();
    if (!obj)
        return NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolLookupByUUIDEnsureACL(conn, def) < 0)
        goto cleanup;

    pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    virStoragePoolObjUnlock(obj);
    return pool;
}

static virStoragePoolPtr
storagePoolLookupByName(virConnectPtr conn,
                        const char *name)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;

    if (!(obj = storagePoolObjFindByName(name)))
        return NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolLookupByNameEnsureACL(conn, def) < 0)
        goto cleanup;

    pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    virStoragePoolObjUnlock(obj);
    return pool;
}

static virStoragePoolPtr
storagePoolLookupByVolume(virStorageVolPtr vol)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;

    if (!(obj = storagePoolObjFindByName(vol->pool)))
        return NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolLookupByVolumeEnsureACL(vol->conn, def) < 0)
        goto cleanup;

    pool = virGetStoragePool(vol->conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    virStoragePoolObjUnlock(obj);
    return pool;
}

static int
storageConnectNumOfStoragePools(virConnectPtr conn)
{
    int nactive = 0;

    if (virConnectNumOfStoragePoolsEnsureACL(conn) < 0)
        return -1;

    storageDriverLock();
    nactive = virStoragePoolObjNumOfStoragePools(&driver->pools, conn, true,
                                                 virConnectNumOfStoragePoolsCheckACL);
    storageDriverUnlock();

    return nactive;
}


static int
storageConnectListStoragePools(virConnectPtr conn,
                               char **const names,
                               int maxnames)
{
    int got = 0;

    if (virConnectListStoragePoolsEnsureACL(conn) < 0)
        return -1;

    storageDriverLock();
    got = virStoragePoolObjGetNames(&driver->pools, conn, true,
                                    virConnectListStoragePoolsCheckACL,
                                    names, maxnames);
    storageDriverUnlock();
    return got;
}

static int
storageConnectNumOfDefinedStoragePools(virConnectPtr conn)
{
    int nactive = 0;

    if (virConnectNumOfDefinedStoragePoolsEnsureACL(conn) < 0)
        return -1;

    storageDriverLock();
    nactive = virStoragePoolObjNumOfStoragePools(&driver->pools, conn, false,
                                                 virConnectNumOfDefinedStoragePoolsCheckACL);
    storageDriverUnlock();

    return nactive;
}


static int
storageConnectListDefinedStoragePools(virConnectPtr conn,
                                      char **const names,
                                      int maxnames)
{
    int got = 0;

    if (virConnectListDefinedStoragePoolsEnsureACL(conn) < 0)
        return -1;

    storageDriverLock();
    got = virStoragePoolObjGetNames(&driver->pools, conn, false,
                                    virConnectListDefinedStoragePoolsCheckACL,
                                    names, maxnames);
    storageDriverUnlock();
    return got;
}

/* This method is required to be re-entrant / thread safe, so
   uses no driver lock */
static char *
storageConnectFindStoragePoolSources(virConnectPtr conn,
                                     const char *type,
                                     const char *srcSpec,
                                     unsigned int flags)
{
    int backend_type;
    virStorageBackendPtr backend;
    char *ret = NULL;

    if (virConnectFindStoragePoolSourcesEnsureACL(conn) < 0)
        return NULL;

    backend_type = virStoragePoolTypeFromString(type);
    if (backend_type < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("unknown storage pool type %s"), type);
        goto cleanup;
    }

    backend = virStorageBackendForType(backend_type);
    if (backend == NULL)
        goto cleanup;

    if (!backend->findPoolSources) {
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("pool type '%s' does not support source "
                         "discovery"), type);
        goto cleanup;
    }

    ret = backend->findPoolSources(conn, srcSpec, flags);

 cleanup:
    return ret;
}


static int
storagePoolIsActive(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolIsActiveEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    ret = virStoragePoolObjIsActive(obj);

 cleanup:
    virStoragePoolObjUnlock(obj);
    return ret;
}


static int
storagePoolIsPersistent(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolIsPersistentEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    ret = virStoragePoolObjGetConfigFile(obj) ? 1 : 0;

 cleanup:
    virStoragePoolObjUnlock(obj);
    return ret;
}


static virStoragePoolPtr
storagePoolCreateXML(virConnectPtr conn,
                     const char *xml,
                     unsigned int flags)
{
    virStoragePoolDefPtr newDef;
    virStoragePoolObjPtr obj = NULL;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    char *stateFile = NULL;
    unsigned int build_flags = 0;

    virCheckFlags(VIR_STORAGE_POOL_CREATE_WITH_BUILD |
                  VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE |
                  VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE, NULL);

    VIR_EXCLUSIVE_FLAGS_RET(VIR_STORAGE_POOL_BUILD_OVERWRITE,
                            VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, NULL);

    storageDriverLock();
    if (!(newDef = virStoragePoolDefParseString(xml)))
        goto cleanup;

    if (virStoragePoolCreateXMLEnsureACL(conn, newDef) < 0)
        goto cleanup;

    if (virStoragePoolObjIsDuplicate(&driver->pools, newDef, 1) < 0)
        goto cleanup;

    if (virStoragePoolObjSourceFindDuplicate(conn, &driver->pools, newDef) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(newDef->type)) == NULL)
        goto cleanup;

    if (!(obj = virStoragePoolObjAssignDef(&driver->pools, newDef)))
        goto cleanup;
    newDef = NULL;
    def = virStoragePoolObjGetDef(obj);

    if (backend->buildPool) {
        if (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE)
            build_flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
        else if (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE)
            build_flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;

        if (build_flags ||
            (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD)) {
            if (backend->buildPool(conn, obj, build_flags) < 0) {
                virStoragePoolObjRemove(&driver->pools, obj);
                obj = NULL;
                goto cleanup;
            }
        }
    }

    if (backend->startPool &&
        backend->startPool(conn, obj) < 0) {
        virStoragePoolObjRemove(&driver->pools, obj);
        obj = NULL;
        goto cleanup;
    }

    stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml");

    virStoragePoolObjClearVols(obj);
    if (!stateFile || virStoragePoolSaveState(stateFile, def) < 0 ||
        backend->refreshPool(conn, obj) < 0) {
        if (stateFile)
            unlink(stateFile);
        if (backend->stopPool)
            backend->stopPool(conn, obj);
        virStoragePoolObjRemove(&driver->pools, obj);
        obj = NULL;
        goto cleanup;
    }

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_STARTED,
                                            0);

    VIR_INFO("Creating storage pool '%s'", def->name);
    virStoragePoolObjSetActive(obj, true);

    pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    VIR_FREE(stateFile);
    virStoragePoolDefFree(newDef);
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    if (obj)
        virStoragePoolObjUnlock(obj);
    storageDriverUnlock();
    return pool;
}

static virStoragePoolPtr
storagePoolDefineXML(virConnectPtr conn,
                     const char *xml,
                     unsigned int flags)
{
    virStoragePoolDefPtr newDef;
    virStoragePoolObjPtr obj = NULL;
    virStoragePoolDefPtr def;
    virStoragePoolPtr pool = NULL;
    virObjectEventPtr event = NULL;

    virCheckFlags(0, NULL);

    storageDriverLock();
    if (!(newDef = virStoragePoolDefParseString(xml)))
        goto cleanup;

    if (virXMLCheckIllegalChars("name", newDef->name, "\n") < 0)
        goto cleanup;

    if (virStoragePoolDefineXMLEnsureACL(conn, newDef) < 0)
        goto cleanup;

    if (virStoragePoolObjIsDuplicate(&driver->pools, newDef, 0) < 0)
        goto cleanup;

    if (virStoragePoolObjSourceFindDuplicate(conn, &driver->pools, newDef) < 0)
        goto cleanup;

    if (virStorageBackendForType(newDef->type) == NULL)
        goto cleanup;

    if (!(obj = virStoragePoolObjAssignDef(&driver->pools, newDef)))
        goto cleanup;
    newDef = NULL;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolObjSaveDef(driver, obj, def) < 0) {
        virStoragePoolObjRemove(&driver->pools, obj);
        obj = NULL;
        goto cleanup;
    }

    event = virStoragePoolEventLifecycleNew(def->name, def->uuid,
                                            VIR_STORAGE_POOL_EVENT_DEFINED,
                                            0);

    VIR_INFO("Defining storage pool '%s'", def->name);
    pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL);

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    virStoragePoolDefFree(newDef);
    if (obj)
        virStoragePoolObjUnlock(obj);
    storageDriverUnlock();
    return pool;
}

static int
storagePoolUndefine(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    const char *autostartLink;
    virObjectEventPtr event = NULL;
    int ret = -1;

    storageDriverLock();
    if (!(obj = storagePoolObjFindByUUID(pool->uuid, pool->name)))
        goto cleanup;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolUndefineEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if (virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is still active"),
                       def->name);
        goto cleanup;
    }

    if (virStoragePoolObjGetAsyncjobs(obj) > 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("pool '%s' has asynchronous jobs running."),
                       def->name);
        goto cleanup;
    }

    autostartLink = virStoragePoolObjGetAutostartLink(obj);
    if (virStoragePoolObjDeleteDef(obj) < 0)
        goto cleanup;

    if (autostartLink && unlink(autostartLink) < 0 &&
        errno != ENOENT && errno != ENOTDIR) {
        char ebuf[1024];
        VIR_ERROR(_("Failed to delete autostart link '%s': %s"),
                  autostartLink, virStrerror(errno, ebuf, sizeof(ebuf)));
    }

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_UNDEFINED,
                                            0);

    VIR_INFO("Undefining storage pool '%s'", def->name);
    virStoragePoolObjRemove(&driver->pools, obj);
    obj = NULL;
    ret = 0;

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    if (obj)
        virStoragePoolObjUnlock(obj);
    storageDriverUnlock();
    return ret;
}

static int
storagePoolCreate(virStoragePoolPtr pool,
                  unsigned int flags)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    int ret = -1;
    char *stateFile = NULL;
    unsigned int build_flags = 0;

    virCheckFlags(VIR_STORAGE_POOL_CREATE_WITH_BUILD |
                  VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE |
                  VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE, -1);

    VIR_EXCLUSIVE_FLAGS_RET(VIR_STORAGE_POOL_BUILD_OVERWRITE,
                            VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, -1);

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolCreateEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL)
        goto cleanup;

    if (virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is already active"),
                       def->name);
        goto cleanup;
    }

    if (backend->buildPool) {
        if (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD_OVERWRITE)
            build_flags |= VIR_STORAGE_POOL_BUILD_OVERWRITE;
        else if (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD_NO_OVERWRITE)
            build_flags |= VIR_STORAGE_POOL_BUILD_NO_OVERWRITE;

        if (build_flags ||
            (flags & VIR_STORAGE_POOL_CREATE_WITH_BUILD)) {
            if (backend->buildPool(pool->conn, obj, build_flags) < 0)
                goto cleanup;
        }
    }

    VIR_INFO("Starting up storage pool '%s'", def->name);
    if (backend->startPool &&
        backend->startPool(pool->conn, obj) < 0)
        goto cleanup;

    stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml");

    virStoragePoolObjClearVols(obj);
    if (!stateFile || virStoragePoolSaveState(stateFile, def) < 0 ||
        backend->refreshPool(pool->conn, obj) < 0) {
        if (stateFile)
            unlink(stateFile);
        if (backend->stopPool)
            backend->stopPool(pool->conn, obj);
        goto cleanup;
    }

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_STARTED,
                                            0);

    virStoragePoolObjSetActive(obj, true);
    ret = 0;

 cleanup:
    VIR_FREE(stateFile);
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    if (obj)
        virStoragePoolObjUnlock(obj);
    return ret;
}

static int
storagePoolBuild(virStoragePoolPtr pool,
                 unsigned int flags)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolBuildEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL)
        goto cleanup;

    if (virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is already active"),
                       def->name);
        goto cleanup;
    }

    if (backend->buildPool &&
        backend->buildPool(pool->conn, obj, flags) < 0)
        goto cleanup;

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_CREATED,
                                            0);

    ret = 0;

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    virStoragePoolObjUnlock(obj);
    return ret;
}


static int
storagePoolDestroy(virStoragePoolPtr pool)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    char *stateFile = NULL;
    int ret = -1;

    storageDriverLock();
    if (!(obj = storagePoolObjFindByUUID(pool->uuid, pool->name)))
        goto cleanup;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolDestroyEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL)
        goto cleanup;

    VIR_INFO("Destroying storage pool '%s'", def->name);

    if (!virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is not active"), def->name);
        goto cleanup;
    }

    if (virStoragePoolObjGetAsyncjobs(obj) > 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("pool '%s' has asynchronous jobs running."),
                       def->name);
        goto cleanup;
    }

    if (!(stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml")))
        goto cleanup;

    unlink(stateFile);
    VIR_FREE(stateFile);

    if (backend->stopPool &&
        backend->stopPool(pool->conn, obj) < 0)
        goto cleanup;

    virStoragePoolObjClearVols(obj);

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_STOPPED,
                                            0);

    virStoragePoolObjSetActive(obj, false);

    virStoragePoolUpdateInactive(&obj);

    ret = 0;

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    if (obj)
        virStoragePoolObjUnlock(obj);
    storageDriverUnlock();
    return ret;
}

static int
storagePoolDelete(virStoragePoolPtr pool,
                  unsigned int flags)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
    virObjectEventPtr event = NULL;
    char *stateFile = NULL;
    int ret = -1;

    if (!(obj = virStoragePoolObjFromStoragePool(pool)))
        return -1;
    def = virStoragePoolObjGetDef(obj);

    if (virStoragePoolDeleteEnsureACL(pool->conn, def) < 0)
        goto cleanup;

    if ((backend = virStorageBackendForType(def->type)) == NULL)
        goto cleanup;

    VIR_INFO("Deleting storage pool '%s'", def->name);

    if (virStoragePoolObjIsActive(obj)) {
        virReportError(VIR_ERR_OPERATION_INVALID,
                       _("storage pool '%s' is still active"),
                       def->name);
        goto cleanup;
    }

    if (virStoragePoolObjGetAsyncjobs(obj) > 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("pool '%s' has asynchronous jobs running."),
                       def->name);
        goto cleanup;
    }

    if (!(stateFile = virFileBuildPath(driver->stateDir, def->name, ".xml")))
        goto cleanup;

    unlink(stateFile);
    VIR_FREE(stateFile);

    if (!backend->deletePool) {
        virReportError(VIR_ERR_NO_SUPPORT,
                       "%s", _("pool does not support pool deletion"));
        goto cleanup;
    }
    if (backend->deletePool(pool->conn, obj, flags) < 0)
        goto cleanup;

    event = virStoragePoolEventLifecycleNew(def->name,
                                            def->uuid,
                                            VIR_STORAGE_POOL_EVENT_DELETED,
                                            0);

    ret = 0;

 cleanup:
    if (event)
        virObjectEventStateQueue(driver->storageEventState, event);
    virStoragePoolObjUnlock(obj);
    return ret;
}


static int
storagePoolRefresh(virStoragePoolPtr pool,
                   unsigned int flags)
{
    virStoragePoolObjPtr obj;
    virStoragePoolDefPtr def;
    virStorageBackendPtr backend;
{'fraction_non_alphanumeric': 0.09621667029650602, 'fraction_numerical': 0.003951339410721509, 'mean_word_length': 2.3149236798679866, 'pattern_counts': {'":': 0, '<': 143, '<?xml version=': 0, '>': 399, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 20}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 124, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5498182', 'n_tokens_mistral': 19055, 'n_tokens_neox': 18365, 'n_words': 4032}