Changelog: - Fix packing of VMDK4Header. - Add write support for VMDK files (tested only on VMDK4 files).
Index: block-vmdk.c =================================================================== RCS file: /cvsroot/qemu/qemu/block-vmdk.c,v retrieving revision 1.4 diff -u -p -r1.4 block-vmdk.c --- block-vmdk.c 18 Sep 2004 19:32:11 -0000 1.4 +++ block-vmdk.c 24 Apr 2005 10:56:16 -0000 @@ -2,6 +2,7 @@ * Block driver for the VMDK format * * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2005 Filip Navara * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,9 +25,6 @@ #include "vl.h" #include "block_int.h" -/* XXX: this code is untested */ -/* XXX: add write support */ - #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') @@ -56,13 +54,14 @@ typedef struct { int64_t grain_offset; char filler[1]; char check_bytes[4]; -} VMDK4Header; +} __attribute__((packed)) VMDK4Header; #define L2_CACHE_SIZE 16 typedef struct BDRVVmdkState { int fd; int64_t l1_table_offset; + int64_t l1_backup_table_offset; uint32_t *l1_table; unsigned int l1_size; uint32_t l1_entry_sectors; @@ -96,9 +95,13 @@ static int vmdk_open(BlockDriverState *b uint32_t magic; int l1_size; - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return -1; + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + if (fd < 0) { + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) + return -1; + bs->read_only = 1; + } if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) goto fail; magic = be32_to_cpu(magic); @@ -111,7 +114,8 @@ static int vmdk_open(BlockDriverState *b s->l2_size = 1 << 9; s->l1_size = 1 << 6; bs->total_sectors = le32_to_cpu(header.disk_sectors); - s->l1_table_offset = le32_to_cpu(header.l1dir_offset) * 512; + s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9; + s->l1_backup_table_offset = 0; s->l1_entry_sectors = s->l2_size * s->cluster_sectors; } else if (magic == VMDK4_MAGIC) { VMDK4Header header; @@ -126,7 +130,8 @@ static int vmdk_open(BlockDriverState *b goto fail; s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) / s->l1_entry_sectors; - s->l1_table_offset = le64_to_cpu(header.rgd_offset) * 512; + s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; + s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; } else { goto fail; } @@ -147,8 +152,6 @@ static int vmdk_open(BlockDriverState *b if (!s->l2_cache) goto fail; s->fd = fd; - /* XXX: currently only read only */ - bs->read_only = 1; return 0; fail: qemu_free(s->l1_table); @@ -158,31 +161,46 @@ static int vmdk_open(BlockDriverState *b } static uint64_t get_cluster_offset(BlockDriverState *bs, - uint64_t offset) + uint64_t offset, int allocate) { BDRVVmdkState *s = bs->opaque; unsigned int l1_index, l2_offset, l2_index; int min_index, i, j; - uint32_t min_count, *l2_table; + uint32_t min_count, *l2_table, tmp; uint64_t cluster_offset; + int new_l2_table = 0; l1_index = (offset >> 9) / s->l1_entry_sectors; if (l1_index >= s->l1_size) return 0; l2_offset = s->l1_table[l1_index]; - if (!l2_offset) - return 0; - - for(i = 0; i < L2_CACHE_SIZE; i++) { - if (l2_offset == s->l2_cache_offsets[i]) { - /* increment the hit count */ - if (++s->l2_cache_counts[i] == 0xffffffff) { - for(j = 0; j < L2_CACHE_SIZE; j++) { - s->l2_cache_counts[j] >>= 1; + if (!l2_offset) { + if (!allocate) + return 0; + /* allocate a new l2 entry */ + l2_offset = lseek(s->fd, 0, SEEK_END); + /* round to cluster size */ + l2_offset = ((l2_offset + (s->cluster_sectors << 9) - 1) & + ~((s->cluster_sectors << 9) - 1)) >> 9; + /* update the L1 entry */ + s->l1_table[l1_index] = l2_offset; + tmp = cpu_to_le32(l2_offset); + lseek(s->fd, s->l1_table_offset + l1_index * sizeof(tmp), SEEK_SET); + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + new_l2_table = 1; + } else { + for(i = 0; i < L2_CACHE_SIZE; i++) { + if (l2_offset == s->l2_cache_offsets[i]) { + /* increment the hit count */ + if (++s->l2_cache_counts[i] == 0xffffffff) { + for(j = 0; j < L2_CACHE_SIZE; j++) { + s->l2_cache_counts[j] >>= 1; + } } + l2_table = s->l2_cache + (i * s->l2_size); + goto found; } - l2_table = s->l2_cache + (i * s->l2_size); - goto found; } } /* not found: load a new entry in the least used one */ @@ -196,14 +214,43 @@ static uint64_t get_cluster_offset(Block } l2_table = s->l2_cache + (min_index * s->l2_size); lseek(s->fd, (int64_t)l2_offset * 512, SEEK_SET); - if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != - s->l2_size * sizeof(uint32_t)) - return 0; + if (new_l2_table) { + memset(l2_table, 0, s->l2_size * sizeof(uint32_t)); + if (write(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != + s->l2_size * sizeof(uint32_t)) + return 0; + if (s->l1_backup_table_offset != 0) { + tmp = cpu_to_le32(l2_offset); + lseek(s->fd, s->l1_backup_table_offset + l1_index * sizeof(tmp), SEEK_SET); + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + } + } else { + if (read(s->fd, l2_table, s->l2_size * sizeof(uint32_t)) != + s->l2_size * sizeof(uint32_t)) + return 0; + } s->l2_cache_offsets[min_index] = l2_offset; s->l2_cache_counts[min_index] = 1; found: l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size; cluster_offset = le32_to_cpu(l2_table[l2_index]); + if (!cluster_offset) { + if (!allocate) + return 0; + cluster_offset = lseek(s->fd, 0, SEEK_END); + /* round to cluster size */ + cluster_offset = (cluster_offset + (s->cluster_sectors << 9) - 1) & + ~((s->cluster_sectors << 9) - 1); + ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9)); + cluster_offset >>= 9; + /* update L2 table */ + tmp = cpu_to_le32(cluster_offset); + l2_table[l2_index] = tmp; + lseek(s->fd, (int64_t)l2_offset * 512 + l2_index * sizeof(tmp), SEEK_SET); + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp)) + return 0; + } cluster_offset <<= 9; return cluster_offset; } @@ -215,7 +262,7 @@ static int vmdk_is_allocated(BlockDriver int index_in_cluster, n; uint64_t cluster_offset; - cluster_offset = get_cluster_offset(bs, sector_num << 9); + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0); index_in_cluster = sector_num % s->cluster_sectors; n = s->cluster_sectors - index_in_cluster; if (n > nb_sectors) @@ -232,7 +279,7 @@ static int vmdk_read(BlockDriverState *b uint64_t cluster_offset; while (nb_sectors > 0) { - cluster_offset = get_cluster_offset(bs, sector_num << 9); + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0); index_in_cluster = sector_num % s->cluster_sectors; n = s->cluster_sectors - index_in_cluster; if (n > nb_sectors) @@ -255,7 +302,27 @@ static int vmdk_read(BlockDriverState *b static int vmdk_write(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { - return -1; + BDRVVmdkState *s = bs->opaque; + int ret, index_in_cluster, n; + uint64_t cluster_offset; + + while (nb_sectors > 0) { + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n = s->cluster_sectors - index_in_cluster; + if (n > nb_sectors) + n = nb_sectors; + cluster_offset = get_cluster_offset(bs, sector_num << 9, 1); + if (!cluster_offset) + return -1; + lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET); + ret = write(s->fd, buf, n * 512); + if (ret != n * 512) + return -1; + nb_sectors -= n; + sector_num += n; + buf += n * 512; + } + return 0; } static void vmdk_close(BlockDriverState *bs)
_______________________________________________ Qemu-devel mailing list Qemu-devel@nongnu.org http://lists.nongnu.org/mailman/listinfo/qemu-devel