<commit_before>/*
 * qemu_backup.c: Implementation and handling of the backup jobs
 *
 * 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 "qemu_block.h"
#include "qemu_conf.h"
#include "qemu_capabilities.h"
#include "qemu_monitor.h"
#include "qemu_process.h"
#include "qemu_backup.h"
#include "qemu_monitor_json.h"
#include "qemu_checkpoint.h"
#include "qemu_command.h"

#include "virerror.h"
#include "virlog.h"
#include "virbuffer.h"
#include "viralloc.h"
#include "virxml.h"
#include "virstoragefile.h"
#include "virstring.h"
#include "backup_conf.h"
#include "virdomaincheckpointobjlist.h"

#define VIR_FROM_THIS VIR_FROM_QEMU

VIR_LOG_INIT("qemu.qemu_backup");


static virDomainBackupDefPtr
qemuDomainGetBackup(virDomainObjPtr vm)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;

    if (!priv->backup) {
        virReportError(VIR_ERR_NO_DOMAIN_BACKUP, "%s",
                       _("no domain backup job present"));
        return NULL;
    }

    return priv->backup;
}


static int
qemuBackupPrepare(virDomainBackupDefPtr def)
{

    if (def->type == VIR_DOMAIN_BACKUP_TYPE_PULL) {
        if (!def->server) {
            def->server = g_new(virStorageNetHostDef, 1);

            def->server->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
            def->server->name = g_strdup("localhost");
        }

        switch ((virStorageNetHostTransport) def->server->transport) {
        case VIR_STORAGE_NET_HOST_TRANS_TCP:
            /* TODO: Update qemu.conf to provide a port range,
             * probably starting at 10809, for obtaining automatic
             * port via virPortAllocatorAcquire, as well as store
             * somewhere if we need to call virPortAllocatorRelease
             * during BackupEnd. Until then, user must provide port */
            if (!def->server->port) {
                virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
                               _("<domainbackup> must specify TCP port for now"));
                return -1;
            }
            break;

        case VIR_STORAGE_NET_HOST_TRANS_UNIX:
            /* TODO: Do we need to mess with selinux? */
            break;

        case VIR_STORAGE_NET_HOST_TRANS_RDMA:
        case VIR_STORAGE_NET_HOST_TRANS_LAST:
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("unexpected transport in <domainbackup>"));
            return -1;
        }
    }

    return 0;
}


struct qemuBackupDiskData {
    virDomainBackupDiskDefPtr backupdisk;
    virDomainDiskDefPtr domdisk;
    qemuBlockJobDataPtr blockjob;
    virStorageSourcePtr store;
    char *incrementalBitmap;
    qemuBlockStorageSourceChainDataPtr crdata;
    bool labelled;
    bool initialized;
    bool created;
    bool added;
    bool started;
    bool done;
};


static void
qemuBackupDiskDataCleanupOne(virDomainObjPtr vm,
                             struct qemuBackupDiskData *dd)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;

    if (dd->started)
        return;

    if (dd->added) {
        qemuDomainObjEnterMonitor(priv->driver, vm);
        qemuBlockStorageSourceAttachRollback(priv->mon, dd->crdata->srcdata[0]);
        ignore_value(qemuDomainObjExitMonitor(priv->driver, vm));
    }

    if (dd->created) {
        if (virStorageFileUnlink(dd->store) < 0)
            VIR_WARN("Unable to remove just-created %s", NULLSTR(dd->store->path));
    }

    if (dd->initialized)
        virStorageFileDeinit(dd->store);

    if (dd->labelled)
        qemuDomainStorageSourceAccessRevoke(priv->driver, vm, dd->store);

    if (dd->blockjob)
        qemuBlockJobStartupFinalize(vm, dd->blockjob);

    qemuBlockStorageSourceChainDataFree(dd->crdata);
}


static void
qemuBackupDiskDataCleanup(virDomainObjPtr vm,
                          struct qemuBackupDiskData *dd,
                          size_t ndd)
{
    virErrorPtr orig_err;
    size_t i;

    if (!dd)
        return;

    virErrorPreserveLast(&orig_err);

    for (i = 0; i < ndd; i++)
        qemuBackupDiskDataCleanupOne(vm, dd + i);

    g_free(dd);
    virErrorRestore(&orig_err);
}



static int
qemuBackupDiskPrepareOneBitmaps(struct qemuBackupDiskData *dd,
                                virJSONValuePtr actions,
                                virDomainMomentDefPtr *incremental)
{
    g_autoptr(virJSONValue) mergebitmaps = NULL;
    g_autoptr(virJSONValue) mergebitmapsstore = NULL;

    if (!(mergebitmaps = virJSONValueNewArray()))
        return -1;

    /* TODO: this code works only if the bitmaps are present on a single node.
     * The algorithm needs to be changed so that it looks into the backing chain
     * so that we can combine all relevant bitmaps for a given backing chain */
    while (*incremental) {
        if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(mergebitmaps,
                                                             dd->domdisk->src->nodeformat,
                                                             (*incremental)->name) < 0)
            return -1;

        incremental++;
    }

    if (!(mergebitmapsstore = virJSONValueCopy(mergebitmaps)))
        return -1;

    if (qemuMonitorTransactionBitmapAdd(actions,
                                        dd->domdisk->src->nodeformat,
                                        dd->incrementalBitmap,
                                        false,
                                        true, 0) < 0)
        return -1;

    if (qemuMonitorTransactionBitmapMerge(actions,
                                          dd->domdisk->src->nodeformat,
                                          dd->incrementalBitmap,
                                          &mergebitmaps) < 0)
        return -1;

    if (qemuMonitorTransactionBitmapAdd(actions,<commit_msg>qemu: backup: Extract calculations of bitmaps to merge for incremental backup<commit_after>/*
 * qemu_backup.c: Implementation and handling of the backup jobs
 *
 * 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 "qemu_block.h"
#include "qemu_conf.h"
#include "qemu_capabilities.h"
#include "qemu_monitor.h"
#include "qemu_process.h"
#include "qemu_backup.h"
#include "qemu_monitor_json.h"
#include "qemu_checkpoint.h"
#include "qemu_command.h"

#include "virerror.h"
#include "virlog.h"
#include "virbuffer.h"
#include "viralloc.h"
#include "virxml.h"
#include "virstoragefile.h"
#include "virstring.h"
#include "backup_conf.h"
#include "virdomaincheckpointobjlist.h"

#define VIR_FROM_THIS VIR_FROM_QEMU

VIR_LOG_INIT("qemu.qemu_backup");


static virDomainBackupDefPtr
qemuDomainGetBackup(virDomainObjPtr vm)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;

    if (!priv->backup) {
        virReportError(VIR_ERR_NO_DOMAIN_BACKUP, "%s",
                       _("no domain backup job present"));
        return NULL;
    }

    return priv->backup;
}


static int
qemuBackupPrepare(virDomainBackupDefPtr def)
{

    if (def->type == VIR_DOMAIN_BACKUP_TYPE_PULL) {
        if (!def->server) {
            def->server = g_new(virStorageNetHostDef, 1);

            def->server->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
            def->server->name = g_strdup("localhost");
        }

        switch ((virStorageNetHostTransport) def->server->transport) {
        case VIR_STORAGE_NET_HOST_TRANS_TCP:
            /* TODO: Update qemu.conf to provide a port range,
             * probably starting at 10809, for obtaining automatic
             * port via virPortAllocatorAcquire, as well as store
             * somewhere if we need to call virPortAllocatorRelease
             * during BackupEnd. Until then, user must provide port */
            if (!def->server->port) {
                virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
                               _("<domainbackup> must specify TCP port for now"));
                return -1;
            }
            break;

        case VIR_STORAGE_NET_HOST_TRANS_UNIX:
            /* TODO: Do we need to mess with selinux? */
            break;

        case VIR_STORAGE_NET_HOST_TRANS_RDMA:
        case VIR_STORAGE_NET_HOST_TRANS_LAST:
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("unexpected transport in <domainbackup>"));
            return -1;
        }
    }

    return 0;
}


struct qemuBackupDiskData {
    virDomainBackupDiskDefPtr backupdisk;
    virDomainDiskDefPtr domdisk;
    qemuBlockJobDataPtr blockjob;
    virStorageSourcePtr store;
    char *incrementalBitmap;
    qemuBlockStorageSourceChainDataPtr crdata;
    bool labelled;
    bool initialized;
    bool created;
    bool added;
    bool started;
    bool done;
};


static void
qemuBackupDiskDataCleanupOne(virDomainObjPtr vm,
                             struct qemuBackupDiskData *dd)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;

    if (dd->started)
        return;

    if (dd->added) {
        qemuDomainObjEnterMonitor(priv->driver, vm);
        qemuBlockStorageSourceAttachRollback(priv->mon, dd->crdata->srcdata[0]);
        ignore_value(qemuDomainObjExitMonitor(priv->driver, vm));
    }

    if (dd->created) {
        if (virStorageFileUnlink(dd->store) < 0)
            VIR_WARN("Unable to remove just-created %s", NULLSTR(dd->store->path));
    }

    if (dd->initialized)
        virStorageFileDeinit(dd->store);

    if (dd->labelled)
        qemuDomainStorageSourceAccessRevoke(priv->driver, vm, dd->store);

    if (dd->blockjob)
        qemuBlockJobStartupFinalize(vm, dd->blockjob);

    qemuBlockStorageSourceChainDataFree(dd->crdata);
}


static void
qemuBackupDiskDataCleanup(virDomainObjPtr vm,
                          struct qemuBackupDiskData *dd,
                          size_t ndd)
{
    virErrorPtr orig_err;
    size_t i;

    if (!dd)
        return;

    virErrorPreserveLast(&orig_err);

    for (i = 0; i < ndd; i++)
        qemuBackupDiskDataCleanupOne(vm, dd + i);

    g_free(dd);
    virErrorRestore(&orig_err);
}


static virJSONValuePtr
qemuBackupDiskPrepareOneBitmapsChain(virDomainMomentDefPtr *incremental,
                                     virStorageSourcePtr backingChain)
{
    g_autoptr(virJSONValue) ret = NULL;

    if (!(ret = virJSONValueNewArray()))
        return NULL;

    /* TODO: this code works only if the bitmaps are present on a single node.
     * The algorithm needs to be changed so that it looks into the backing chain
     * so that we can combine all relevant bitmaps for a given backing chain */
    while (*incremental) {
        if (qemuMonitorTransactionBitmapMergeSourceAddBitmap(ret,
                                                             backingChain->nodeformat,
                                                             (*incremental)->name) < 0)
            return NULL;

        incremental++;
    }

    return g_steal_pointer(&ret);
}


static int
qemuBackupDiskPrepareOneBitmaps(struct qemuBackupDiskData *dd,
                                virJSONValuePtr actions,
                                virDomainMomentDefPtr *incremental)
{
    g_autoptr(virJSONValue) mergebitmaps = NULL;
    g_autoptr(virJSONValue) mergebitmapsstore = NULL;

    if (!(mergebitmaps = qemuBackupDiskPrepareOneBitmapsChain(incremental,
                                                              dd->domdisk->src)))
        return -1;

    if (!(mergebitmapsstore = virJSONValueCopy(mergebitmaps)))
        return -1;

    if (qemuMonitorTransactionBitmapAdd(actions,
                                        dd->domdisk->src->nodeformat,
                                        dd->incrementalBitmap,
                                        false,
                                        true, 0) < 0)
        return -1;

    if (qemuMonitorTransactionBitmapMerge(actions,
                                          dd->domdisk->src->nodeformat,
                                          dd->incrementalBitmap,
                                          &mergebitmaps) < 0)
        return -1;

    if (qemuMonitorTransactionBitmapAdd(actions,
{'fraction_non_alphanumeric': 0.08621987951807229, 'fraction_numerical': 0.00338855421686747, 'mean_word_length': 2.240058550866065, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 103, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 31, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5772999', 'n_tokens_mistral': 3857, 'n_tokens_neox': 3747, 'n_words': 956}