#define HEADER_SIZE 64
// always little-endian
-struct parallels_header {
+typedef struct ParallelsHeader {
char magic[16]; // "WithoutFreeSpace"
uint32_t version;
uint32_t heads;
uint32_t inuse;
uint32_t data_off;
char padding[12];
-} QEMU_PACKED;
+} QEMU_PACKED ParallelsHeader;
typedef struct BDRVParallelsState {
+ /** Locking is conservative, the lock protects
+ * - image file extending (truncate, fallocate)
+ * - any access to block allocation table
+ */
CoMutex lock;
uint32_t *catalog_bitmap;
static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
{
- const struct parallels_header *ph = (const void *)buf;
+ const ParallelsHeader *ph = (const void *)buf;
if (buf_size < HEADER_SIZE)
return 0;
{
BDRVParallelsState *s = bs->opaque;
int i;
- struct parallels_header ph;
+ ParallelsHeader ph;
int ret;
bs->read_only = 1; // no write support yet
return ret;
}
-static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
+static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num)
{
- BDRVParallelsState *s = bs->opaque;
uint32_t index, offset;
index = sector_num / s->tracks;
offset = sector_num % s->tracks;
/* not allocated */
- if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
+ if ((index >= s->catalog_size) || (s->catalog_bitmap[index] == 0))
return -1;
- return
- ((uint64_t)s->catalog_bitmap[index] * s->off_multiplier + offset) * 512;
+ return (uint64_t)s->catalog_bitmap[index] * s->off_multiplier + offset;
}
-static int parallels_read(BlockDriverState *bs, int64_t sector_num,
- uint8_t *buf, int nb_sectors)
+static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num,
+ int nb_sectors)
{
- while (nb_sectors > 0) {
- int64_t position = seek_to_sector(bs, sector_num);
- if (position >= 0) {
- if (bdrv_pread(bs->file, position, buf, 512) != 512)
- return -1;
- } else {
- memset(buf, 0, 512);
- }
- nb_sectors--;
- sector_num++;
- buf += 512;
- }
- return 0;
+ int ret = s->tracks - sector_num % s->tracks;
+ return MIN(nb_sectors, ret);
}
-static coroutine_fn int parallels_co_read(BlockDriverState *bs, int64_t sector_num,
- uint8_t *buf, int nb_sectors)
+static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, int *pnum)
{
- int ret;
BDRVParallelsState *s = bs->opaque;
+ int64_t offset;
+
qemu_co_mutex_lock(&s->lock);
- ret = parallels_read(bs, sector_num, buf, nb_sectors);
+ offset = seek_to_sector(s, sector_num);
qemu_co_mutex_unlock(&s->lock);
+
+ *pnum = cluster_remainder(s, sector_num, nb_sectors);
+
+ if (offset < 0) {
+ return 0;
+ }
+
+ return (offset << BDRV_SECTOR_BITS) |
+ BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
+}
+
+static coroutine_fn int 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 = seek_to_sector(s, sector_num);
+ qemu_co_mutex_unlock(&s->lock);
+
+ n = cluster_remainder(s, sector_num, nb_sectors);
+ nbytes = n << BDRV_SECTOR_BITS;
+
+ if (position < 0) {
+ qemu_iovec_memset(qiov, bytes_done, 0, nbytes);
+ } else {
+ qemu_iovec_reset(&hd_qiov);
+ qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
+
+ ret = bdrv_co_readv(bs->file, position, n, &hd_qiov);
+ if (ret < 0) {
+ break;
+ }
+ }
+
+ nb_sectors -= n;
+ sector_num += n;
+ bytes_done += nbytes;
+ }
+
+ qemu_iovec_destroy(&hd_qiov);
return ret;
}
.instance_size = sizeof(BDRVParallelsState),
.bdrv_probe = parallels_probe,
.bdrv_open = parallels_open,
- .bdrv_read = parallels_co_read,
.bdrv_close = parallels_close,
+ .bdrv_co_get_block_status = parallels_co_get_block_status,
+ .bdrv_co_readv = parallels_co_readv,
};
static void bdrv_parallels_init(void)