qemu/block/parallels-ext.c
<<
>>
Prefs
   1/*
   2 * Support of Parallels Format Extension. It's a part of Parallels format
   3 * driver.
   4 *
   5 * Copyright (c) 2021 Virtuozzo International GmbH
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27#include "qapi/error.h"
  28#include "block/block_int.h"
  29#include "parallels.h"
  30#include "crypto/hash.h"
  31#include "qemu/uuid.h"
  32
  33#define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL
  34
  35#define PARALLELS_END_OF_FEATURES_MAGIC 0x0ULL
  36#define PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC 0x20385FAE252CB34AULL
  37
  38typedef struct ParallelsFormatExtensionHeader {
  39    uint64_t magic; /* PARALLELS_FORMAT_EXTENSION_MAGIC */
  40    uint8_t check_sum[16];
  41} QEMU_PACKED ParallelsFormatExtensionHeader;
  42
  43typedef struct ParallelsFeatureHeader {
  44    uint64_t magic;
  45    uint64_t flags;
  46    uint32_t data_size;
  47    uint32_t _unused;
  48} QEMU_PACKED ParallelsFeatureHeader;
  49
  50typedef struct ParallelsDirtyBitmapFeature {
  51    uint64_t size;
  52    uint8_t id[16];
  53    uint32_t granularity;
  54    uint32_t l1_size;
  55    /* L1 table follows */
  56} QEMU_PACKED ParallelsDirtyBitmapFeature;
  57
  58/* Given L1 table read bitmap data from the image and populate @bitmap */
  59static int parallels_load_bitmap_data(BlockDriverState *bs,
  60                                      const uint64_t *l1_table,
  61                                      uint32_t l1_size,
  62                                      BdrvDirtyBitmap *bitmap,
  63                                      Error **errp)
  64{
  65    BDRVParallelsState *s = bs->opaque;
  66    int ret = 0;
  67    uint64_t offset, limit;
  68    uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
  69    uint8_t *buf = NULL;
  70    uint64_t i, tab_size =
  71        DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size),
  72                     s->cluster_size);
  73
  74    if (tab_size != l1_size) {
  75        error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond "
  76                   "to bitmap size and cluster size. Expected %" PRIu64,
  77                   l1_size, tab_size);
  78        return -EINVAL;
  79    }
  80
  81    buf = qemu_blockalign(bs, s->cluster_size);
  82    limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
  83    for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
  84        uint64_t count = MIN(bm_size - offset, limit);
  85        uint64_t entry = l1_table[i];
  86
  87        if (entry == 0) {
  88            /* No need to deserialize zeros because @bitmap is cleared. */
  89            continue;
  90        }
  91
  92        if (entry == 1) {
  93            bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false);
  94        } else {
  95            ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf,
  96                             s->cluster_size);
  97            if (ret < 0) {
  98                error_setg_errno(errp, -ret,
  99                                 "Failed to read bitmap data cluster");
 100                goto finish;
 101            }
 102            bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count,
 103                                               false);
 104        }
 105    }
 106    ret = 0;
 107
 108    bdrv_dirty_bitmap_deserialize_finish(bitmap);
 109
 110finish:
 111    qemu_vfree(buf);
 112
 113    return ret;
 114}
 115
 116/*
 117 * @data buffer (of @data_size size) is the Dirty bitmaps feature which
 118 * consists of ParallelsDirtyBitmapFeature followed by L1 table.
 119 */
 120static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs,
 121                                              uint8_t *data,
 122                                              size_t data_size,
 123                                              Error **errp)
 124{
 125    int ret;
 126    ParallelsDirtyBitmapFeature bf;
 127    g_autofree uint64_t *l1_table = NULL;
 128    BdrvDirtyBitmap *bitmap;
 129    QemuUUID uuid;
 130    char uuidstr[UUID_FMT_LEN + 1];
 131    int i;
 132
 133    if (data_size < sizeof(bf)) {
 134        error_setg(errp, "Too small Bitmap Feature area in Parallels Format "
 135                   "Extension: %zu bytes, expected at least %zu bytes",
 136                   data_size, sizeof(bf));
 137        return NULL;
 138    }
 139    memcpy(&bf, data, sizeof(bf));
 140    bf.size = le64_to_cpu(bf.size);
 141    bf.granularity = le32_to_cpu(bf.granularity) << BDRV_SECTOR_BITS;
 142    bf.l1_size = le32_to_cpu(bf.l1_size);
 143    data += sizeof(bf);
 144    data_size -= sizeof(bf);
 145
 146    if (bf.size != bs->total_sectors) {
 147        error_setg(errp, "Bitmap size (in sectors) %" PRId64 " differs from "
 148                   "disk size in sectors %" PRId64, bf.size, bs->total_sectors);
 149        return NULL;
 150    }
 151
 152    if (bf.l1_size * sizeof(uint64_t) > data_size) {
 153        error_setg(errp, "Bitmaps feature corrupted: l1 table exceeds "
 154                   "extension data_size");
 155        return NULL;
 156    }
 157
 158    memcpy(&uuid, bf.id, sizeof(uuid));
 159    qemu_uuid_unparse(&uuid, uuidstr);
 160    bitmap = bdrv_create_dirty_bitmap(bs, bf.granularity, uuidstr, errp);
 161    if (!bitmap) {
 162        return NULL;
 163    }
 164
 165    l1_table = g_new(uint64_t, bf.l1_size);
 166    for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) {
 167        l1_table[i] = ldq_le_p(data);
 168    }
 169
 170    ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp);
 171    if (ret < 0) {
 172        bdrv_release_dirty_bitmap(bitmap);
 173        return NULL;
 174    }
 175
 176    /* We support format extension only for RO parallels images. */
 177    assert(!(bs->open_flags & BDRV_O_RDWR));
 178    bdrv_dirty_bitmap_set_readonly(bitmap, true);
 179
 180    return bitmap;
 181}
 182
 183static int parallels_parse_format_extension(BlockDriverState *bs,
 184                                            uint8_t *ext_cluster, Error **errp)
 185{
 186    BDRVParallelsState *s = bs->opaque;
 187    int ret;
 188    int remaining = s->cluster_size;
 189    uint8_t *pos = ext_cluster;
 190    ParallelsFormatExtensionHeader eh;
 191    g_autofree uint8_t *hash = NULL;
 192    size_t hash_len = 0;
 193    GSList *bitmaps = NULL, *el;
 194
 195    memcpy(&eh, pos, sizeof(eh));
 196    eh.magic = le64_to_cpu(eh.magic);
 197    pos += sizeof(eh);
 198    remaining -= sizeof(eh);
 199
 200    if (eh.magic != PARALLELS_FORMAT_EXTENSION_MAGIC) {
 201        error_setg(errp, "Wrong parallels Format Extension magic: 0x%" PRIx64
 202                   ", expected: 0x%llx", eh.magic,
 203                   PARALLELS_FORMAT_EXTENSION_MAGIC);
 204        goto fail;
 205    }
 206
 207    ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining,
 208                             &hash, &hash_len, errp);
 209    if (ret < 0) {
 210        goto fail;
 211    }
 212
 213    if (hash_len != sizeof(eh.check_sum) ||
 214        memcmp(hash, eh.check_sum, sizeof(eh.check_sum)) != 0) {
 215        error_setg(errp, "Wrong checksum in Format Extension header. Format "
 216                   "extension is corrupted.");
 217        goto fail;
 218    }
 219
 220    while (true) {
 221        ParallelsFeatureHeader fh;
 222        BdrvDirtyBitmap *bitmap;
 223
 224        if (remaining < sizeof(fh)) {
 225            error_setg(errp, "Can not read feature header, as remaining bytes "
 226                       "(%d) in Format Extension is less than Feature header "
 227                       "size (%zu)", remaining, sizeof(fh));
 228            goto fail;
 229        }
 230
 231        memcpy(&fh, pos, sizeof(fh));
 232        pos += sizeof(fh);
 233        remaining -= sizeof(fh);
 234
 235        fh.magic = le64_to_cpu(fh.magic);
 236        fh.flags = le64_to_cpu(fh.flags);
 237        fh.data_size = le32_to_cpu(fh.data_size);
 238
 239        if (fh.flags) {
 240            error_setg(errp, "Flags for extension feature are unsupported");
 241            goto fail;
 242        }
 243
 244        if (fh.data_size > remaining) {
 245            error_setg(errp, "Feature data_size exceedes Format Extension "
 246                       "cluster");
 247            goto fail;
 248        }
 249
 250        switch (fh.magic) {
 251        case PARALLELS_END_OF_FEATURES_MAGIC:
 252            return 0;
 253
 254        case PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC:
 255            bitmap = parallels_load_bitmap(bs, pos, fh.data_size, errp);
 256            if (!bitmap) {
 257                goto fail;
 258            }
 259            bitmaps = g_slist_append(bitmaps, bitmap);
 260            break;
 261
 262        default:
 263            error_setg(errp, "Unknown feature: 0x%" PRIu64, fh.magic);
 264            goto fail;
 265        }
 266
 267        pos = ext_cluster + QEMU_ALIGN_UP(pos + fh.data_size - ext_cluster, 8);
 268    }
 269
 270fail:
 271    for (el = bitmaps; el; el = el->next) {
 272        bdrv_release_dirty_bitmap(el->data);
 273    }
 274    g_slist_free(bitmaps);
 275
 276    return -EINVAL;
 277}
 278
 279int parallels_read_format_extension(BlockDriverState *bs,
 280                                    int64_t ext_off, Error **errp)
 281{
 282    BDRVParallelsState *s = bs->opaque;
 283    int ret;
 284    uint8_t *ext_cluster = qemu_blockalign(bs, s->cluster_size);
 285
 286    assert(ext_off > 0);
 287
 288    ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size);
 289    if (ret < 0) {
 290        error_setg_errno(errp, -ret, "Failed to read Format Extension cluster");
 291        goto out;
 292    }
 293
 294    ret = parallels_parse_format_extension(bs, ext_cluster, errp);
 295
 296out:
 297    qemu_vfree(ext_cluster);
 298
 299    return ret;
 300}
 301