eeb1e6dc49
We should extend the bitmap if the file is extended and set the bit in the image used bitmap once the cluster is allocated. Sanity check at that moment also looks like a good idea. Signed-off-by: Denis V. Lunev <den@openvz.org> Reviewed-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com>
1404 lines
41 KiB
C
1404 lines
41 KiB
C
/*
|
|
* Block driver for Parallels disk image format
|
|
*
|
|
* Copyright (c) 2007 Alex Beregszaszi
|
|
* Copyright (c) 2015 Denis V. Lunev <den@openvz.org>
|
|
*
|
|
* This code was originally based on comparing different disk images created
|
|
* by Parallels. Currently it is based on opened OpenVZ sources
|
|
* available at
|
|
* http://git.openvz.org/?p=ploop;a=summary
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qapi/error.h"
|
|
#include "block/block_int.h"
|
|
#include "block/qdict.h"
|
|
#include "sysemu/block-backend.h"
|
|
#include "qemu/module.h"
|
|
#include "qemu/option.h"
|
|
#include "qapi/qmp/qdict.h"
|
|
#include "qapi/qobject-input-visitor.h"
|
|
#include "qapi/qapi-visit-block-core.h"
|
|
#include "qemu/bswap.h"
|
|
#include "qemu/bitmap.h"
|
|
#include "qemu/memalign.h"
|
|
#include "migration/blocker.h"
|
|
#include "parallels.h"
|
|
|
|
/**************************************************************/
|
|
|
|
#define HEADER_MAGIC "WithoutFreeSpace"
|
|
#define HEADER_MAGIC2 "WithouFreSpacExt"
|
|
#define HEADER_VERSION 2
|
|
#define HEADER_INUSE_MAGIC (0x746F6E59)
|
|
#define MAX_PARALLELS_IMAGE_FACTOR (1ull << 32)
|
|
|
|
static QEnumLookup prealloc_mode_lookup = {
|
|
.array = (const char *const[]) {
|
|
"falloc",
|
|
"truncate",
|
|
},
|
|
.size = PRL_PREALLOC_MODE__MAX
|
|
};
|
|
|
|
#define PARALLELS_OPT_PREALLOC_MODE "prealloc-mode"
|
|
#define PARALLELS_OPT_PREALLOC_SIZE "prealloc-size"
|
|
|
|
static QemuOptsList parallels_runtime_opts = {
|
|
.name = "parallels",
|
|
.head = QTAILQ_HEAD_INITIALIZER(parallels_runtime_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = PARALLELS_OPT_PREALLOC_SIZE,
|
|
.type = QEMU_OPT_SIZE,
|
|
.help = "Preallocation size on image expansion",
|
|
.def_value_str = "128M",
|
|
},
|
|
{
|
|
.name = PARALLELS_OPT_PREALLOC_MODE,
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "Preallocation mode on image expansion "
|
|
"(allowed values: falloc, truncate)",
|
|
.def_value_str = "falloc",
|
|
},
|
|
{ /* end of list */ },
|
|
},
|
|
};
|
|
|
|
static QemuOptsList parallels_create_opts = {
|
|
.name = "parallels-create-opts",
|
|
.head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = BLOCK_OPT_SIZE,
|
|
.type = QEMU_OPT_SIZE,
|
|
.help = "Virtual disk size",
|
|
},
|
|
{
|
|
.name = BLOCK_OPT_CLUSTER_SIZE,
|
|
.type = QEMU_OPT_SIZE,
|
|
.help = "Parallels image cluster size",
|
|
.def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
|
|
},
|
|
{ /* end of list */ }
|
|
}
|
|
};
|
|
|
|
|
|
static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
|
|
{
|
|
return (uint64_t)le32_to_cpu(s->bat_bitmap[idx]) * s->off_multiplier;
|
|
}
|
|
|
|
static uint32_t bat_entry_off(uint32_t idx)
|
|
{
|
|
return sizeof(ParallelsHeader) + sizeof(uint32_t) * idx;
|
|
}
|
|
|
|
static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num)
|
|
{
|
|
uint32_t index, offset;
|
|
|
|
index = sector_num / s->tracks;
|
|
offset = sector_num % s->tracks;
|
|
|
|
/* not allocated */
|
|
if ((index >= s->bat_size) || (s->bat_bitmap[index] == 0)) {
|
|
return -1;
|
|
}
|
|
return bat2sect(s, index) + offset;
|
|
}
|
|
|
|
static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num,
|
|
int nb_sectors)
|
|
{
|
|
int ret = s->tracks - sector_num % s->tracks;
|
|
return MIN(nb_sectors, ret);
|
|
}
|
|
|
|
static uint32_t host_cluster_index(BDRVParallelsState *s, int64_t off)
|
|
{
|
|
off -= s->data_start << BDRV_SECTOR_BITS;
|
|
return off / s->cluster_size;
|
|
}
|
|
|
|
static int64_t block_status(BDRVParallelsState *s, int64_t sector_num,
|
|
int nb_sectors, int *pnum)
|
|
{
|
|
int64_t start_off = -2, prev_end_off = -2;
|
|
|
|
*pnum = 0;
|
|
while (nb_sectors > 0 || start_off == -2) {
|
|
int64_t offset = seek_to_sector(s, sector_num);
|
|
int to_end;
|
|
|
|
if (start_off == -2) {
|
|
start_off = offset;
|
|
prev_end_off = offset;
|
|
} else if (offset != prev_end_off) {
|
|
break;
|
|
}
|
|
|
|
to_end = cluster_remainder(s, sector_num, nb_sectors);
|
|
nb_sectors -= to_end;
|
|
sector_num += to_end;
|
|
*pnum += to_end;
|
|
|
|
if (offset > 0) {
|
|
prev_end_off += to_end;
|
|
}
|
|
}
|
|
return start_off;
|
|
}
|
|
|
|
static void parallels_set_bat_entry(BDRVParallelsState *s,
|
|
uint32_t index, uint32_t offset)
|
|
{
|
|
s->bat_bitmap[index] = cpu_to_le32(offset);
|
|
bitmap_set(s->bat_dirty_bmap, bat_entry_off(index) / s->bat_dirty_block, 1);
|
|
}
|
|
|
|
static int mark_used(BlockDriverState *bs, unsigned long *bitmap,
|
|
uint32_t bitmap_size, int64_t off, uint32_t count)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
uint32_t cluster_index = host_cluster_index(s, off);
|
|
unsigned long next_used;
|
|
if (cluster_index + count > bitmap_size) {
|
|
return -E2BIG;
|
|
}
|
|
next_used = find_next_bit(bitmap, bitmap_size, cluster_index);
|
|
if (next_used < cluster_index + count) {
|
|
return -EBUSY;
|
|
}
|
|
bitmap_set(bitmap, cluster_index, count);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Collect used bitmap. The image can contain errors, we should fill the
|
|
* bitmap anyway, as much as we can. This information will be used for
|
|
* error resolution.
|
|
*/
|
|
static int parallels_fill_used_bitmap(BlockDriverState *bs)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
int64_t payload_bytes;
|
|
uint32_t i;
|
|
int err = 0;
|
|
|
|
payload_bytes = bdrv_getlength(bs->file->bs);
|
|
if (payload_bytes < 0) {
|
|
return payload_bytes;
|
|
}
|
|
payload_bytes -= s->data_start * BDRV_SECTOR_SIZE;
|
|
if (payload_bytes < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
s->used_bmap_size = DIV_ROUND_UP(payload_bytes, s->cluster_size);
|
|
if (s->used_bmap_size == 0) {
|
|
return 0;
|
|
}
|
|
s->used_bmap = bitmap_try_new(s->used_bmap_size);
|
|
if (s->used_bmap == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < s->bat_size; i++) {
|
|
int err2;
|
|
int64_t host_off = bat2sect(s, i) << BDRV_SECTOR_BITS;
|
|
if (host_off == 0) {
|
|
continue;
|
|
}
|
|
|
|
err2 = mark_used(bs, s->used_bmap, s->used_bmap_size, host_off, 1);
|
|
if (err2 < 0 && err == 0) {
|
|
err = err2;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void parallels_free_used_bitmap(BlockDriverState *bs)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
s->used_bmap_size = 0;
|
|
g_free(s->used_bmap);
|
|
}
|
|
|
|
static int64_t coroutine_fn GRAPH_RDLOCK
|
|
allocate_clusters(BlockDriverState *bs, int64_t sector_num,
|
|
int nb_sectors, int *pnum)
|
|
{
|
|
int ret = 0;
|
|
BDRVParallelsState *s = bs->opaque;
|
|
int64_t pos, space, idx, to_allocate, i, len;
|
|
|
|
pos = block_status(s, sector_num, nb_sectors, pnum);
|
|
if (pos > 0) {
|
|
return pos;
|
|
}
|
|
|
|
idx = sector_num / s->tracks;
|
|
to_allocate = DIV_ROUND_UP(sector_num + *pnum, s->tracks) - idx;
|
|
|
|
/*
|
|
* This function is called only by parallels_co_writev(), which will never
|
|
* pass a sector_num at or beyond the end of the image (because the block
|
|
* layer never passes such a sector_num to that function). Therefore, idx
|
|
* is always below s->bat_size.
|
|
* block_status() will limit *pnum so that sector_num + *pnum will not
|
|
* exceed the image end. Therefore, idx + to_allocate cannot exceed
|
|
* s->bat_size.
|
|
* Note that s->bat_size is an unsigned int, therefore idx + to_allocate
|
|
* will always fit into a uint32_t.
|
|
*/
|
|
assert(idx < s->bat_size && idx + to_allocate <= s->bat_size);
|
|
|
|
space = to_allocate * s->tracks;
|
|
len = bdrv_co_getlength(bs->file->bs);
|
|
if (len < 0) {
|
|
return len;
|
|
}
|
|
if (s->data_end + space > (len >> BDRV_SECTOR_BITS)) {
|
|
uint32_t new_usedsize;
|
|
|
|
space += s->prealloc_size;
|
|
/*
|
|
* We require the expanded size to read back as zero. If the
|
|
* user permitted truncation, we try that; but if it fails, we
|
|
* force the safer-but-slower fallocate.
|
|
*/
|
|
if (s->prealloc_mode == PRL_PREALLOC_MODE_TRUNCATE) {
|
|
ret = bdrv_co_truncate(bs->file,
|
|
(s->data_end + space) << BDRV_SECTOR_BITS,
|
|
false, PREALLOC_MODE_OFF,
|
|
BDRV_REQ_ZERO_WRITE, NULL);
|
|
if (ret == -ENOTSUP) {
|
|
s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE;
|
|
}
|
|
}
|
|
if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) {
|
|
ret = bdrv_co_pwrite_zeroes(bs->file,
|
|
s->data_end << BDRV_SECTOR_BITS,
|
|
space << BDRV_SECTOR_BITS, 0);
|
|
}
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
new_usedsize = s->used_bmap_size +
|
|
(space << BDRV_SECTOR_BITS) / s->cluster_size;
|
|
s->used_bmap = bitmap_zero_extend(s->used_bmap, s->used_bmap_size,
|
|
new_usedsize);
|
|
s->used_bmap_size = new_usedsize;
|
|
}
|
|
|
|
/*
|
|
* Try to read from backing to fill empty clusters
|
|
* FIXME: 1. previous write_zeroes may be redundant
|
|
* 2. most of data we read from backing will be rewritten by
|
|
* parallels_co_writev. On aligned-to-cluster write we do not need
|
|
* this read at all.
|
|
* 3. it would be good to combine write of data from backing and new
|
|
* data into one write call.
|
|
*/
|
|
if (bs->backing) {
|
|
int64_t nb_cow_sectors = to_allocate * s->tracks;
|
|
int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS;
|
|
void *buf = qemu_blockalign(bs, nb_cow_bytes);
|
|
|
|
ret = bdrv_co_pread(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
|
|
nb_cow_bytes, buf, 0);
|
|
if (ret < 0) {
|
|
qemu_vfree(buf);
|
|
return ret;
|
|
}
|
|
|
|
ret = bdrv_co_pwrite(bs->file, s->data_end * BDRV_SECTOR_SIZE,
|
|
nb_cow_bytes, buf, 0);
|
|
qemu_vfree(buf);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = mark_used(bs, s->used_bmap, s->used_bmap_size,
|
|
s->data_end << BDRV_SECTOR_BITS, to_allocate);
|
|
if (ret < 0) {
|
|
/* Image consistency is broken. Alarm! */
|
|
return ret;
|
|
}
|
|
for (i = 0; i < to_allocate; i++) {
|
|
parallels_set_bat_entry(s, idx + i, s->data_end / s->off_multiplier);
|
|
s->data_end += s->tracks;
|
|
}
|
|
|
|
return bat2sect(s, idx) + sector_num % s->tracks;
|
|
}
|
|
|
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
parallels_co_flush_to_os(BlockDriverState *bs)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
unsigned long size = DIV_ROUND_UP(s->header_size, s->bat_dirty_block);
|
|
unsigned long bit;
|
|
|
|
qemu_co_mutex_lock(&s->lock);
|
|
|
|
bit = find_first_bit(s->bat_dirty_bmap, size);
|
|
while (bit < size) {
|
|
uint32_t off = bit * s->bat_dirty_block;
|
|
uint32_t to_write = s->bat_dirty_block;
|
|
int ret;
|
|
|
|
if (off + to_write > s->header_size) {
|
|
to_write = s->header_size - off;
|
|
}
|
|
ret = bdrv_co_pwrite(bs->file, off, to_write,
|
|
(uint8_t *)s->header + off, 0);
|
|
if (ret < 0) {
|
|
qemu_co_mutex_unlock(&s->lock);
|
|
return ret;
|
|
}
|
|
bit = find_next_bit(s->bat_dirty_bmap, size, bit + 1);
|
|
}
|
|
bitmap_zero(s->bat_dirty_bmap, size);
|
|
|
|
qemu_co_mutex_unlock(&s->lock);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int coroutine_fn parallels_co_block_status(BlockDriverState *bs,
|
|
bool want_zero,
|
|
int64_t offset,
|
|
int64_t bytes,
|
|
int64_t *pnum,
|
|
int64_t *map,
|
|
BlockDriverState **file)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
int count;
|
|
|
|
assert(QEMU_IS_ALIGNED(offset | bytes, BDRV_SECTOR_SIZE));
|
|
qemu_co_mutex_lock(&s->lock);
|
|
offset = block_status(s, offset >> BDRV_SECTOR_BITS,
|
|
bytes >> BDRV_SECTOR_BITS, &count);
|
|
qemu_co_mutex_unlock(&s->lock);
|
|
|
|
*pnum = count * BDRV_SECTOR_SIZE;
|
|
if (offset < 0) {
|
|
return 0;
|
|
}
|
|
|
|
*map = offset * BDRV_SECTOR_SIZE;
|
|
*file = bs->file->bs;
|
|
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
|
|
}
|
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
parallels_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
|
QEMUIOVector *qiov, int flags)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
uint64_t bytes_done = 0;
|
|
QEMUIOVector hd_qiov;
|
|
int ret = 0;
|
|
|
|
qemu_iovec_init(&hd_qiov, qiov->niov);
|
|
|
|
while (nb_sectors > 0) {
|
|
int64_t position;
|
|
int n, nbytes;
|
|
|
|
qemu_co_mutex_lock(&s->lock);
|
|
position = allocate_clusters(bs, sector_num, nb_sectors, &n);
|
|
qemu_co_mutex_unlock(&s->lock);
|
|
if (position < 0) {
|
|
ret = (int)position;
|
|
break;
|
|
}
|
|
|
|
nbytes = n << BDRV_SECTOR_BITS;
|
|
|
|
qemu_iovec_reset(&hd_qiov);
|
|
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
|
|
|
|
ret = bdrv_co_pwritev(bs->file, position * BDRV_SECTOR_SIZE, nbytes,
|
|
&hd_qiov, 0);
|
|
if (ret < 0) {
|
|
break;
|
|
}
|
|
|
|
nb_sectors -= n;
|
|
sector_num += n;
|
|
bytes_done += nbytes;
|
|
}
|
|
|
|
qemu_iovec_destroy(&hd_qiov);
|
|
return ret;
|
|
}
|
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
parallels_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
|
QEMUIOVector *qiov)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
uint64_t bytes_done = 0;
|
|
QEMUIOVector hd_qiov;
|
|
int ret = 0;
|
|
|
|
qemu_iovec_init(&hd_qiov, qiov->niov);
|
|
|
|
while (nb_sectors > 0) {
|
|
int64_t position;
|
|
int n, nbytes;
|
|
|
|
qemu_co_mutex_lock(&s->lock);
|
|
position = block_status(s, sector_num, nb_sectors, &n);
|
|
qemu_co_mutex_unlock(&s->lock);
|
|
|
|
nbytes = n << BDRV_SECTOR_BITS;
|
|
|
|
qemu_iovec_reset(&hd_qiov);
|
|
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
|
|
|
|
if (position < 0) {
|
|
if (bs->backing) {
|
|
ret = bdrv_co_preadv(bs->backing, sector_num * BDRV_SECTOR_SIZE,
|
|
nbytes, &hd_qiov, 0);
|
|
if (ret < 0) {
|
|
break;
|
|
}
|
|
} else {
|
|
qemu_iovec_memset(&hd_qiov, 0, 0, nbytes);
|
|
}
|
|
} else {
|
|
ret = bdrv_co_preadv(bs->file, position * BDRV_SECTOR_SIZE, nbytes,
|
|
&hd_qiov, 0);
|
|
if (ret < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
nb_sectors -= n;
|
|
sector_num += n;
|
|
bytes_done += nbytes;
|
|
}
|
|
|
|
qemu_iovec_destroy(&hd_qiov);
|
|
return ret;
|
|
}
|
|
|
|
static void parallels_check_unclean(BlockDriverState *bs,
|
|
BdrvCheckResult *res,
|
|
BdrvCheckMode fix)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
|
|
if (!s->header_unclean) {
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr, "%s image was not closed correctly\n",
|
|
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR");
|
|
res->corruptions++;
|
|
if (fix & BDRV_FIX_ERRORS) {
|
|
/* parallels_close will do the job right */
|
|
res->corruptions_fixed++;
|
|
s->header_unclean = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns true if data_off is correct, otherwise false. In both cases
|
|
* correct_offset is set to the proper value.
|
|
*/
|
|
static bool parallels_test_data_off(BDRVParallelsState *s,
|
|
int64_t file_nb_sectors,
|
|
uint32_t *correct_offset)
|
|
{
|
|
uint32_t data_off, min_off;
|
|
bool old_magic;
|
|
|
|
/*
|
|
* There are two slightly different image formats: with "WithoutFreeSpace"
|
|
* or "WithouFreSpacExt" magic words. Call the first one as "old magic".
|
|
* In such images data_off field can be zero. In this case the offset is
|
|
* calculated as the end of BAT table plus some padding to ensure sector
|
|
* size alignment.
|
|
*/
|
|
old_magic = !memcmp(s->header->magic, HEADER_MAGIC, 16);
|
|
|
|
min_off = DIV_ROUND_UP(bat_entry_off(s->bat_size), BDRV_SECTOR_SIZE);
|
|
if (!old_magic) {
|
|
min_off = ROUND_UP(min_off, s->cluster_size / BDRV_SECTOR_SIZE);
|
|
}
|
|
|
|
if (correct_offset) {
|
|
*correct_offset = min_off;
|
|
}
|
|
|
|
data_off = le32_to_cpu(s->header->data_off);
|
|
if (data_off == 0 && old_magic) {
|
|
return true;
|
|
}
|
|
|
|
if (data_off < min_off || data_off > file_nb_sectors) {
|
|
return false;
|
|
}
|
|
|
|
if (correct_offset) {
|
|
*correct_offset = data_off;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
parallels_check_data_off(BlockDriverState *bs, BdrvCheckResult *res,
|
|
BdrvCheckMode fix)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
int64_t file_size;
|
|
uint32_t data_off;
|
|
|
|
file_size = bdrv_co_nb_sectors(bs->file->bs);
|
|
if (file_size < 0) {
|
|
res->check_errors++;
|
|
return file_size;
|
|
}
|
|
|
|
if (parallels_test_data_off(s, file_size, &data_off)) {
|
|
return 0;
|
|
}
|
|
|
|
res->corruptions++;
|
|
if (fix & BDRV_FIX_ERRORS) {
|
|
int err;
|
|
s->header->data_off = cpu_to_le32(data_off);
|
|
s->data_start = data_off;
|
|
|
|
parallels_free_used_bitmap(bs);
|
|
err = parallels_fill_used_bitmap(bs);
|
|
if (err == -ENOMEM) {
|
|
res->check_errors++;
|
|
return err;
|
|
}
|
|
|
|
res->corruptions_fixed++;
|
|
}
|
|
|
|
fprintf(stderr, "%s data_off field has incorrect value\n",
|
|
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
parallels_check_outside_image(BlockDriverState *bs, BdrvCheckResult *res,
|
|
BdrvCheckMode fix)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
uint32_t i;
|
|
int64_t off, high_off, size;
|
|
|
|
size = bdrv_co_getlength(bs->file->bs);
|
|
if (size < 0) {
|
|
res->check_errors++;
|
|
return size;
|
|
}
|
|
|
|
high_off = 0;
|
|
for (i = 0; i < s->bat_size; i++) {
|
|
off = bat2sect(s, i) << BDRV_SECTOR_BITS;
|
|
if (off + s->cluster_size > size) {
|
|
fprintf(stderr, "%s cluster %u is outside image\n",
|
|
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i);
|
|
res->corruptions++;
|
|
if (fix & BDRV_FIX_ERRORS) {
|
|
parallels_set_bat_entry(s, i, 0);
|
|
res->corruptions_fixed++;
|
|
}
|
|
continue;
|
|
}
|
|
if (high_off < off) {
|
|
high_off = off;
|
|
}
|
|
}
|
|
|
|
if (high_off == 0) {
|
|
res->image_end_offset = s->data_end << BDRV_SECTOR_BITS;
|
|
} else {
|
|
res->image_end_offset = high_off + s->cluster_size;
|
|
s->data_end = res->image_end_offset >> BDRV_SECTOR_BITS;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
parallels_check_leak(BlockDriverState *bs, BdrvCheckResult *res,
|
|
BdrvCheckMode fix, bool explicit)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
int64_t size;
|
|
int ret;
|
|
|
|
size = bdrv_co_getlength(bs->file->bs);
|
|
if (size < 0) {
|
|
res->check_errors++;
|
|
return size;
|
|
}
|
|
|
|
if (size > res->image_end_offset) {
|
|
int64_t count;
|
|
count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size);
|
|
if (explicit) {
|
|
fprintf(stderr,
|
|
"%s space leaked at the end of the image %" PRId64 "\n",
|
|
fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR",
|
|
size - res->image_end_offset);
|
|
res->leaks += count;
|
|
}
|
|
if (fix & BDRV_FIX_LEAKS) {
|
|
Error *local_err = NULL;
|
|
|
|
/*
|
|
* In order to really repair the image, we must shrink it.
|
|
* That means we have to pass exact=true.
|
|
*/
|
|
ret = bdrv_co_truncate(bs->file, res->image_end_offset, true,
|
|
PREALLOC_MODE_OFF, 0, &local_err);
|
|
if (ret < 0) {
|
|
error_report_err(local_err);
|
|
res->check_errors++;
|
|
return ret;
|
|
}
|
|
if (explicit) {
|
|
res->leaks_fixed += count;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
parallels_check_duplicate(BlockDriverState *bs, BdrvCheckResult *res,
|
|
BdrvCheckMode fix)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
int64_t host_off, host_sector, guest_sector;
|
|
unsigned long *bitmap;
|
|
uint32_t i, bitmap_size, bat_entry;
|
|
int n, ret = 0;
|
|
uint64_t *buf = NULL;
|
|
bool fixed = false;
|
|
|
|
/*
|
|
* Create a bitmap of used clusters.
|
|
* If a bit is set, there is a BAT entry pointing to this cluster.
|
|
* Loop through the BAT entries, check bits relevant to an entry offset.
|
|
* If bit is set, this entry is duplicated. Otherwise set the bit.
|
|
*
|
|
* We shouldn't worry about newly allocated clusters outside the image
|
|
* because they are created higher then any existing cluster pointed by
|
|
* a BAT entry.
|
|
*/
|
|
bitmap_size = host_cluster_index(s, res->image_end_offset);
|
|
if (bitmap_size == 0) {
|
|
return 0;
|
|
}
|
|
if (res->image_end_offset % s->cluster_size) {
|
|
/* A not aligned image end leads to a bitmap shorter by 1 */
|
|
bitmap_size++;
|
|
}
|
|
|
|
bitmap = bitmap_new(bitmap_size);
|
|
|
|
buf = qemu_blockalign(bs, s->cluster_size);
|
|
|
|
for (i = 0; i < s->bat_size; i++) {
|
|
host_off = bat2sect(s, i) << BDRV_SECTOR_BITS;
|
|
if (host_off == 0) {
|
|
continue;
|
|
}
|
|
|
|
ret = mark_used(bs, bitmap, bitmap_size, host_off, 1);
|
|
assert(ret != -E2BIG);
|
|
if (ret == 0) {
|
|
continue;
|
|
}
|
|
|
|
/* this cluster duplicates another one */
|
|
fprintf(stderr, "%s duplicate offset in BAT entry %u\n",
|
|
fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i);
|
|
|
|
res->corruptions++;
|
|
|
|
if (!(fix & BDRV_FIX_ERRORS)) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Reset the entry and allocate a new cluster
|
|
* for the relevant guest offset. In this way we let
|
|
* the lower layer to place the new cluster properly.
|
|
* Copy the original cluster to the allocated one.
|
|
* But before save the old offset value for repairing
|
|
* if we have an error.
|
|
*/
|
|
bat_entry = s->bat_bitmap[i];
|
|
parallels_set_bat_entry(s, i, 0);
|
|
|
|
ret = bdrv_co_pread(bs->file, host_off, s->cluster_size, buf, 0);
|
|
if (ret < 0) {
|
|
res->check_errors++;
|
|
goto out_repair_bat;
|
|
}
|
|
|
|
guest_sector = (i * (int64_t)s->cluster_size) >> BDRV_SECTOR_BITS;
|
|
host_sector = allocate_clusters(bs, guest_sector, s->tracks, &n);
|
|
if (host_sector < 0) {
|
|
res->check_errors++;
|
|
goto out_repair_bat;
|
|
}
|
|
host_off = host_sector << BDRV_SECTOR_BITS;
|
|
|
|
ret = bdrv_co_pwrite(bs->file, host_off, s->cluster_size, buf, 0);
|
|
if (ret < 0) {
|
|
res->check_errors++;
|
|
goto out_repair_bat;
|
|
}
|
|
|
|
if (host_off + s->cluster_size > res->image_end_offset) {
|
|
res->image_end_offset = host_off + s->cluster_size;
|
|
}
|
|
|
|
/*
|
|
* In the future allocate_cluster() will reuse holed offsets
|
|
* inside the image. Keep the used clusters bitmap content
|
|
* consistent for the new allocated clusters too.
|
|
*
|
|
* Note, clusters allocated outside the current image are not
|
|
* considered, and the bitmap size doesn't change. This specifically
|
|
* means that -E2BIG is OK.
|
|
*/
|
|
ret = mark_used(bs, bitmap, bitmap_size, host_off, 1);
|
|
if (ret == -EBUSY) {
|
|
res->check_errors++;
|
|
goto out_repair_bat;
|
|
}
|
|
|
|
fixed = true;
|
|
res->corruptions_fixed++;
|
|
|
|
}
|
|
|
|
if (fixed) {
|
|
/*
|
|
* When new clusters are allocated, the file size increases by
|
|
* 128 Mb. We need to truncate the file to the right size. Let
|
|
* the leak fix code make its job without res changing.
|
|
*/
|
|
ret = parallels_check_leak(bs, res, fix, false);
|
|
}
|
|
|
|
out_free:
|
|
g_free(buf);
|
|
g_free(bitmap);
|
|
return ret;
|
|
/*
|
|
* We can get here only from places where index and old_offset have
|
|
* meaningful values.
|
|
*/
|
|
out_repair_bat:
|
|
s->bat_bitmap[i] = bat_entry;
|
|
goto out_free;
|
|
}
|
|
|
|
static void parallels_collect_statistics(BlockDriverState *bs,
|
|
BdrvCheckResult *res,
|
|
BdrvCheckMode fix)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
int64_t off, prev_off;
|
|
uint32_t i;
|
|
|
|
res->bfi.total_clusters = s->bat_size;
|
|
res->bfi.compressed_clusters = 0; /* compression is not supported */
|
|
|
|
prev_off = 0;
|
|
for (i = 0; i < s->bat_size; i++) {
|
|
off = bat2sect(s, i) << BDRV_SECTOR_BITS;
|
|
/*
|
|
* If BDRV_FIX_ERRORS is not set, out-of-image BAT entries were not
|
|
* fixed. Skip not allocated and out-of-image BAT entries.
|
|
*/
|
|
if (off == 0 || off + s->cluster_size > res->image_end_offset) {
|
|
prev_off = 0;
|
|
continue;
|
|
}
|
|
|
|
if (prev_off != 0 && (prev_off + s->cluster_size) != off) {
|
|
res->bfi.fragmented_clusters++;
|
|
}
|
|
prev_off = off;
|
|
res->bfi.allocated_clusters++;
|
|
}
|
|
}
|
|
|
|
static int coroutine_fn GRAPH_RDLOCK
|
|
parallels_co_check(BlockDriverState *bs, BdrvCheckResult *res,
|
|
BdrvCheckMode fix)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
int ret;
|
|
|
|
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
|
parallels_check_unclean(bs, res, fix);
|
|
|
|
ret = parallels_check_data_off(bs, res, fix);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = parallels_check_outside_image(bs, res, fix);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = parallels_check_leak(bs, res, fix, true);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = parallels_check_duplicate(bs, res, fix);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
parallels_collect_statistics(bs, res, fix);
|
|
}
|
|
|
|
ret = bdrv_co_flush(bs);
|
|
if (ret < 0) {
|
|
res->check_errors++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int coroutine_fn GRAPH_UNLOCKED
|
|
parallels_co_create(BlockdevCreateOptions* opts, Error **errp)
|
|
{
|
|
BlockdevCreateOptionsParallels *parallels_opts;
|
|
BlockDriverState *bs;
|
|
BlockBackend *blk;
|
|
int64_t total_size, cl_size;
|
|
uint32_t bat_entries, bat_sectors;
|
|
ParallelsHeader header;
|
|
uint8_t tmp[BDRV_SECTOR_SIZE];
|
|
int ret;
|
|
|
|
assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS);
|
|
parallels_opts = &opts->u.parallels;
|
|
|
|
/* Sanity checks */
|
|
total_size = parallels_opts->size;
|
|
|
|
if (parallels_opts->has_cluster_size) {
|
|
cl_size = parallels_opts->cluster_size;
|
|
} else {
|
|
cl_size = DEFAULT_CLUSTER_SIZE;
|
|
}
|
|
|
|
/* XXX What is the real limit here? This is an insanely large maximum. */
|
|
if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) {
|
|
error_setg(errp, "Cluster size is too large");
|
|
return -EINVAL;
|
|
}
|
|
if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) {
|
|
error_setg(errp, "Image size is too large for this cluster size");
|
|
return -E2BIG;
|
|
}
|
|
|
|
if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) {
|
|
error_setg(errp, "Image size must be a multiple of 512 bytes");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) {
|
|
error_setg(errp, "Cluster size must be a multiple of 512 bytes");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Create BlockBackend to write to the image */
|
|
bs = bdrv_co_open_blockdev_ref(parallels_opts->file, errp);
|
|
if (bs == NULL) {
|
|
return -EIO;
|
|
}
|
|
|
|
blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
|
|
errp);
|
|
if (!blk) {
|
|
ret = -EPERM;
|
|
goto out;
|
|
}
|
|
blk_set_allow_write_beyond_eof(blk, true);
|
|
|
|
/* Create image format */
|
|
bat_entries = DIV_ROUND_UP(total_size, cl_size);
|
|
bat_sectors = DIV_ROUND_UP(bat_entry_off(bat_entries), cl_size);
|
|
bat_sectors = (bat_sectors * cl_size) >> BDRV_SECTOR_BITS;
|
|
|
|
memset(&header, 0, sizeof(header));
|
|
memcpy(header.magic, HEADER_MAGIC2, sizeof(header.magic));
|
|
header.version = cpu_to_le32(HEADER_VERSION);
|
|
/* don't care much about geometry, it is not used on image level */
|
|
header.heads = cpu_to_le32(HEADS_NUMBER);
|
|
header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE
|
|
/ HEADS_NUMBER / SEC_IN_CYL);
|
|
header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS);
|
|
header.bat_entries = cpu_to_le32(bat_entries);
|
|
header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE));
|
|
header.data_off = cpu_to_le32(bat_sectors);
|
|
|
|
/* write all the data */
|
|
memset(tmp, 0, sizeof(tmp));
|
|
memcpy(tmp, &header, sizeof(header));
|
|
|
|
ret = blk_co_pwrite(blk, 0, BDRV_SECTOR_SIZE, tmp, 0);
|
|
if (ret < 0) {
|
|
goto exit;
|
|
}
|
|
ret = blk_co_pwrite_zeroes(blk, BDRV_SECTOR_SIZE,
|
|
(bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
|
|
if (ret < 0) {
|
|
goto exit;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
blk_co_unref(blk);
|
|
bdrv_co_unref(bs);
|
|
return ret;
|
|
|
|
exit:
|
|
error_setg_errno(errp, -ret, "Failed to create Parallels image");
|
|
goto out;
|
|
}
|
|
|
|
static int coroutine_fn GRAPH_UNLOCKED
|
|
parallels_co_create_opts(BlockDriver *drv, const char *filename,
|
|
QemuOpts *opts, Error **errp)
|
|
{
|
|
BlockdevCreateOptions *create_options = NULL;
|
|
BlockDriverState *bs = NULL;
|
|
QDict *qdict;
|
|
Visitor *v;
|
|
int ret;
|
|
|
|
static const QDictRenames opt_renames[] = {
|
|
{ BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
/* Parse options and convert legacy syntax */
|
|
qdict = qemu_opts_to_qdict_filtered(opts, NULL, ¶llels_create_opts,
|
|
true);
|
|
|
|
if (!qdict_rename_keys(qdict, opt_renames, errp)) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* Create and open the file (protocol layer) */
|
|
ret = bdrv_co_create_file(filename, opts, errp);
|
|
if (ret < 0) {
|
|
goto done;
|
|
}
|
|
|
|
bs = bdrv_co_open(filename, NULL, NULL,
|
|
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
|
|
if (bs == NULL) {
|
|
ret = -EIO;
|
|
goto done;
|
|
}
|
|
|
|
/* Now get the QAPI type BlockdevCreateOptions */
|
|
qdict_put_str(qdict, "driver", "parallels");
|
|
qdict_put_str(qdict, "file", bs->node_name);
|
|
|
|
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
|
if (!v) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
visit_type_BlockdevCreateOptions(v, NULL, &create_options, errp);
|
|
visit_free(v);
|
|
if (!create_options) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* Silently round up sizes */
|
|
create_options->u.parallels.size =
|
|
ROUND_UP(create_options->u.parallels.size, BDRV_SECTOR_SIZE);
|
|
create_options->u.parallels.cluster_size =
|
|
ROUND_UP(create_options->u.parallels.cluster_size, BDRV_SECTOR_SIZE);
|
|
|
|
/* Create the Parallels image (format layer) */
|
|
ret = parallels_co_create(create_options, errp);
|
|
if (ret < 0) {
|
|
goto done;
|
|
}
|
|
ret = 0;
|
|
|
|
done:
|
|
qobject_unref(qdict);
|
|
bdrv_co_unref(bs);
|
|
qapi_free_BlockdevCreateOptions(create_options);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int parallels_probe(const uint8_t *buf, int buf_size,
|
|
const char *filename)
|
|
{
|
|
const ParallelsHeader *ph = (const void *)buf;
|
|
|
|
if (buf_size < sizeof(ParallelsHeader)) {
|
|
return 0;
|
|
}
|
|
|
|
if ((!memcmp(ph->magic, HEADER_MAGIC, 16) ||
|
|
!memcmp(ph->magic, HEADER_MAGIC2, 16)) &&
|
|
(le32_to_cpu(ph->version) == HEADER_VERSION)) {
|
|
return 100;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parallels_update_header(BlockDriverState *bs)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
unsigned size = MAX(bdrv_opt_mem_align(bs->file->bs),
|
|
sizeof(ParallelsHeader));
|
|
|
|
if (size > s->header_size) {
|
|
size = s->header_size;
|
|
}
|
|
return bdrv_pwrite_sync(bs->file, 0, size, s->header, 0);
|
|
}
|
|
|
|
|
|
static int parallels_opts_prealloc(BlockDriverState *bs, QDict *options,
|
|
Error **errp)
|
|
{
|
|
int err;
|
|
char *buf;
|
|
int64_t bytes;
|
|
BDRVParallelsState *s = bs->opaque;
|
|
Error *local_err = NULL;
|
|
QemuOpts *opts = qemu_opts_create(¶llels_runtime_opts, NULL, 0, errp);
|
|
if (!opts) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
err = -EINVAL;
|
|
if (!qemu_opts_absorb_qdict(opts, options, errp)) {
|
|
goto done;
|
|
}
|
|
|
|
bytes = qemu_opt_get_size_del(opts, PARALLELS_OPT_PREALLOC_SIZE, 0);
|
|
s->prealloc_size = bytes >> BDRV_SECTOR_BITS;
|
|
buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE);
|
|
/* prealloc_mode can be downgraded later during allocate_clusters */
|
|
s->prealloc_mode = qapi_enum_parse(&prealloc_mode_lookup, buf,
|
|
PRL_PREALLOC_MODE_FALLOCATE,
|
|
&local_err);
|
|
g_free(buf);
|
|
if (local_err != NULL) {
|
|
error_propagate(errp, local_err);
|
|
goto done;
|
|
}
|
|
err = 0;
|
|
|
|
done:
|
|
qemu_opts_del(opts);
|
|
return err;
|
|
}
|
|
|
|
static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
|
Error **errp)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
ParallelsHeader ph;
|
|
int ret, size, i;
|
|
int64_t file_nb_sectors, sector;
|
|
uint32_t data_start;
|
|
bool need_check = false;
|
|
|
|
ret = parallels_opts_prealloc(bs, options, errp);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
file_nb_sectors = bdrv_nb_sectors(bs->file->bs);
|
|
if (file_nb_sectors < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = bdrv_pread(bs->file, 0, sizeof(ph), &ph, 0);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
bs->total_sectors = le64_to_cpu(ph.nb_sectors);
|
|
|
|
if (le32_to_cpu(ph.version) != HEADER_VERSION) {
|
|
goto fail_format;
|
|
}
|
|
if (!memcmp(ph.magic, HEADER_MAGIC, 16)) {
|
|
s->off_multiplier = 1;
|
|
bs->total_sectors = 0xffffffff & bs->total_sectors;
|
|
} else if (!memcmp(ph.magic, HEADER_MAGIC2, 16)) {
|
|
s->off_multiplier = le32_to_cpu(ph.tracks);
|
|
} else {
|
|
goto fail_format;
|
|
}
|
|
|
|
s->tracks = le32_to_cpu(ph.tracks);
|
|
if (s->tracks == 0) {
|
|
error_setg(errp, "Invalid image: Zero sectors per track");
|
|
return -EINVAL;
|
|
}
|
|
if (s->tracks > INT32_MAX/513) {
|
|
error_setg(errp, "Invalid image: Too big cluster");
|
|
return -EFBIG;
|
|
}
|
|
s->prealloc_size = MAX(s->tracks, s->prealloc_size);
|
|
s->cluster_size = s->tracks << BDRV_SECTOR_BITS;
|
|
|
|
s->bat_size = le32_to_cpu(ph.bat_entries);
|
|
if (s->bat_size > INT_MAX / sizeof(uint32_t)) {
|
|
error_setg(errp, "Catalog too large");
|
|
return -EFBIG;
|
|
}
|
|
|
|
size = bat_entry_off(s->bat_size);
|
|
s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs));
|
|
s->header = qemu_try_blockalign(bs->file->bs, s->header_size);
|
|
if (s->header == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = bdrv_pread(bs->file, 0, s->header_size, s->header, 0);
|
|
if (ret < 0) {
|
|
goto fail;
|
|
}
|
|
s->bat_bitmap = (uint32_t *)(s->header + 1);
|
|
|
|
if (le32_to_cpu(ph.inuse) == HEADER_INUSE_MAGIC) {
|
|
need_check = s->header_unclean = true;
|
|
}
|
|
|
|
{
|
|
bool ok = parallels_test_data_off(s, file_nb_sectors, &data_start);
|
|
need_check = need_check || !ok;
|
|
}
|
|
|
|
s->data_start = data_start;
|
|
s->data_end = s->data_start;
|
|
if (s->data_end < (s->header_size >> BDRV_SECTOR_BITS)) {
|
|
/*
|
|
* There is not enough unused space to fit to block align between BAT
|
|
* and actual data. We can't avoid read-modify-write...
|
|
*/
|
|
s->header_size = size;
|
|
}
|
|
|
|
if (ph.ext_off) {
|
|
if (flags & BDRV_O_RDWR) {
|
|
/*
|
|
* It's unsafe to open image RW if there is an extension (as we
|
|
* don't support it). But parallels driver in QEMU historically
|
|
* ignores the extension, so print warning and don't care.
|
|
*/
|
|
warn_report("Format Extension ignored in RW mode");
|
|
} else {
|
|
ret = parallels_read_format_extension(
|
|
bs, le64_to_cpu(ph.ext_off) << BDRV_SECTOR_BITS, errp);
|
|
if (ret < 0) {
|
|
goto fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) {
|
|
s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC);
|
|
ret = parallels_update_header(bs);
|
|
if (ret < 0) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
s->bat_dirty_block = 4 * qemu_real_host_page_size();
|
|
s->bat_dirty_bmap =
|
|
bitmap_new(DIV_ROUND_UP(s->header_size, s->bat_dirty_block));
|
|
|
|
/* Disable migration until bdrv_activate method is added */
|
|
error_setg(&s->migration_blocker, "The Parallels format used by node '%s' "
|
|
"does not support live migration",
|
|
bdrv_get_device_or_node_name(bs));
|
|
ret = migrate_add_blocker(s->migration_blocker, errp);
|
|
if (ret < 0) {
|
|
error_setg(errp, "Migration blocker error");
|
|
goto fail;
|
|
}
|
|
qemu_co_mutex_init(&s->lock);
|
|
|
|
for (i = 0; i < s->bat_size; i++) {
|
|
sector = bat2sect(s, i);
|
|
if (sector + s->tracks > s->data_end) {
|
|
s->data_end = sector + s->tracks;
|
|
}
|
|
}
|
|
need_check = need_check || s->data_end > file_nb_sectors;
|
|
|
|
if (!need_check) {
|
|
ret = parallels_fill_used_bitmap(bs);
|
|
if (ret == -ENOMEM) {
|
|
goto fail;
|
|
}
|
|
need_check = need_check || ret < 0; /* These are correctable errors */
|
|
}
|
|
|
|
/*
|
|
* We don't repair the image here if it's opened for checks. Also we don't
|
|
* want to change inactive images and can't change readonly images.
|
|
*/
|
|
if ((flags & (BDRV_O_CHECK | BDRV_O_INACTIVE)) || !(flags & BDRV_O_RDWR)) {
|
|
return 0;
|
|
}
|
|
|
|
/* Repair the image if corruption was detected. */
|
|
if (need_check) {
|
|
BdrvCheckResult res;
|
|
ret = bdrv_check(bs, &res, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
|
|
if (ret < 0) {
|
|
error_setg_errno(errp, -ret, "Could not repair corrupted image");
|
|
migrate_del_blocker(s->migration_blocker);
|
|
goto fail;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
fail_format:
|
|
error_setg(errp, "Image not in Parallels format");
|
|
return -EINVAL;
|
|
|
|
fail:
|
|
/*
|
|
* "s" object was allocated by g_malloc0 so we can safely
|
|
* try to free its fields even they were not allocated.
|
|
*/
|
|
parallels_free_used_bitmap(bs);
|
|
|
|
error_free(s->migration_blocker);
|
|
g_free(s->bat_dirty_bmap);
|
|
qemu_vfree(s->header);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void parallels_close(BlockDriverState *bs)
|
|
{
|
|
BDRVParallelsState *s = bs->opaque;
|
|
|
|
if ((bs->open_flags & BDRV_O_RDWR) && !(bs->open_flags & BDRV_O_INACTIVE)) {
|
|
s->header->inuse = 0;
|
|
parallels_update_header(bs);
|
|
|
|
/* errors are ignored, so we might as well pass exact=true */
|
|
bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS, true,
|
|
PREALLOC_MODE_OFF, 0, NULL);
|
|
}
|
|
|
|
parallels_free_used_bitmap(bs);
|
|
|
|
g_free(s->bat_dirty_bmap);
|
|
qemu_vfree(s->header);
|
|
|
|
migrate_del_blocker(s->migration_blocker);
|
|
error_free(s->migration_blocker);
|
|
}
|
|
|
|
static bool parallels_is_support_dirty_bitmaps(BlockDriverState *bs)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static BlockDriver bdrv_parallels = {
|
|
.format_name = "parallels",
|
|
.instance_size = sizeof(BDRVParallelsState),
|
|
.create_opts = ¶llels_create_opts,
|
|
.is_format = true,
|
|
.supports_backing = true,
|
|
|
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
|
.bdrv_supports_persistent_dirty_bitmap = parallels_is_support_dirty_bitmaps,
|
|
|
|
.bdrv_probe = parallels_probe,
|
|
.bdrv_open = parallels_open,
|
|
.bdrv_close = parallels_close,
|
|
.bdrv_child_perm = bdrv_default_perms,
|
|
.bdrv_co_block_status = parallels_co_block_status,
|
|
.bdrv_co_flush_to_os = parallels_co_flush_to_os,
|
|
.bdrv_co_readv = parallels_co_readv,
|
|
.bdrv_co_writev = parallels_co_writev,
|
|
.bdrv_co_create = parallels_co_create,
|
|
.bdrv_co_create_opts = parallels_co_create_opts,
|
|
.bdrv_co_check = parallels_co_check,
|
|
};
|
|
|
|
static void bdrv_parallels_init(void)
|
|
{
|
|
bdrv_register(&bdrv_parallels);
|
|
}
|
|
|
|
block_init(bdrv_parallels_init);
|