Commit ff1afc72f2dc508c176d0ed2bfee69545ea3b8ef

Authored by bellard
1 parent 6d82d04a

VMDK4 write support - fixed packing of VMDK4Header (Filip Navara)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1406 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 74 additions and 17 deletions
block-vmdk.c
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 * Block driver for the VMDK format 2 * Block driver for the VMDK format
3 * 3 *
4 * Copyright (c) 2004 Fabrice Bellard 4 * Copyright (c) 2004 Fabrice Bellard
  5 + * Copyright (c) 2005 Filip Navara
5 * 6 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal 8 * of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +25,6 @@ @@ -24,9 +25,6 @@
24 #include "vl.h" 25 #include "vl.h"
25 #include "block_int.h" 26 #include "block_int.h"
26 27
27 -/* XXX: this code is untested */  
28 -/* XXX: add write support */  
29 -  
30 #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') 28 #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
31 #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V') 29 #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
32 30
@@ -56,14 +54,16 @@ typedef struct { @@ -56,14 +54,16 @@ typedef struct {
56 int64_t grain_offset; 54 int64_t grain_offset;
57 char filler[1]; 55 char filler[1];
58 char check_bytes[4]; 56 char check_bytes[4];
59 -} VMDK4Header; 57 +} __attribute__((packed)) VMDK4Header;
60 58
61 #define L2_CACHE_SIZE 16 59 #define L2_CACHE_SIZE 16
62 60
63 typedef struct BDRVVmdkState { 61 typedef struct BDRVVmdkState {
64 int fd; 62 int fd;
65 int64_t l1_table_offset; 63 int64_t l1_table_offset;
  64 + int64_t l1_backup_table_offset;
66 uint32_t *l1_table; 65 uint32_t *l1_table;
  66 + uint32_t *l1_backup_table;
67 unsigned int l1_size; 67 unsigned int l1_size;
68 uint32_t l1_entry_sectors; 68 uint32_t l1_entry_sectors;
69 69
@@ -96,9 +96,13 @@ static int vmdk_open(BlockDriverState *bs, const char *filename) @@ -96,9 +96,13 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
96 uint32_t magic; 96 uint32_t magic;
97 int l1_size; 97 int l1_size;
98 98
99 - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);  
100 - if (fd < 0)  
101 - return -1; 99 + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
  100 + if (fd < 0) {
  101 + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
  102 + if (fd < 0)
  103 + return -1;
  104 + bs->read_only = 1;
  105 + }
102 if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) 106 if (read(fd, &magic, sizeof(magic)) != sizeof(magic))
103 goto fail; 107 goto fail;
104 magic = be32_to_cpu(magic); 108 magic = be32_to_cpu(magic);
@@ -111,7 +115,8 @@ static int vmdk_open(BlockDriverState *bs, const char *filename) @@ -111,7 +115,8 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
111 s->l2_size = 1 << 9; 115 s->l2_size = 1 << 9;
112 s->l1_size = 1 << 6; 116 s->l1_size = 1 << 6;
113 bs->total_sectors = le32_to_cpu(header.disk_sectors); 117 bs->total_sectors = le32_to_cpu(header.disk_sectors);
114 - s->l1_table_offset = le32_to_cpu(header.l1dir_offset) * 512; 118 + s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
  119 + s->l1_backup_table_offset = 0;
115 s->l1_entry_sectors = s->l2_size * s->cluster_sectors; 120 s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
116 } else if (magic == VMDK4_MAGIC) { 121 } else if (magic == VMDK4_MAGIC) {
117 VMDK4Header header; 122 VMDK4Header header;
@@ -126,7 +131,8 @@ static int vmdk_open(BlockDriverState *bs, const char *filename) @@ -126,7 +131,8 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
126 goto fail; 131 goto fail;
127 s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1) 132 s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
128 / s->l1_entry_sectors; 133 / s->l1_entry_sectors;
129 - s->l1_table_offset = le64_to_cpu(header.rgd_offset) * 512; 134 + s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
  135 + s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
130 } else { 136 } else {
131 goto fail; 137 goto fail;
132 } 138 }
@@ -143,14 +149,26 @@ static int vmdk_open(BlockDriverState *bs, const char *filename) @@ -143,14 +149,26 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
143 le32_to_cpus(&s->l1_table[i]); 149 le32_to_cpus(&s->l1_table[i]);
144 } 150 }
145 151
  152 + if (s->l1_backup_table_offset) {
  153 + s->l1_backup_table = qemu_malloc(l1_size);
  154 + if (!s->l1_backup_table)
  155 + goto fail;
  156 + if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1)
  157 + goto fail;
  158 + if (read(fd, s->l1_backup_table, l1_size) != l1_size)
  159 + goto fail;
  160 + for(i = 0; i < s->l1_size; i++) {
  161 + le32_to_cpus(&s->l1_backup_table[i]);
  162 + }
  163 + }
  164 +
146 s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t)); 165 s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
147 if (!s->l2_cache) 166 if (!s->l2_cache)
148 goto fail; 167 goto fail;
149 s->fd = fd; 168 s->fd = fd;
150 - /* XXX: currently only read only */  
151 - bs->read_only = 1;  
152 return 0; 169 return 0;
153 fail: 170 fail:
  171 + qemu_free(s->l1_backup_table);
154 qemu_free(s->l1_table); 172 qemu_free(s->l1_table);
155 qemu_free(s->l2_cache); 173 qemu_free(s->l2_cache);
156 close(fd); 174 close(fd);
@@ -158,12 +176,12 @@ static int vmdk_open(BlockDriverState *bs, const char *filename) @@ -158,12 +176,12 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
158 } 176 }
159 177
160 static uint64_t get_cluster_offset(BlockDriverState *bs, 178 static uint64_t get_cluster_offset(BlockDriverState *bs,
161 - uint64_t offset) 179 + uint64_t offset, int allocate)
162 { 180 {
163 BDRVVmdkState *s = bs->opaque; 181 BDRVVmdkState *s = bs->opaque;
164 unsigned int l1_index, l2_offset, l2_index; 182 unsigned int l1_index, l2_offset, l2_index;
165 int min_index, i, j; 183 int min_index, i, j;
166 - uint32_t min_count, *l2_table; 184 + uint32_t min_count, *l2_table, tmp;
167 uint64_t cluster_offset; 185 uint64_t cluster_offset;
168 186
169 l1_index = (offset >> 9) / s->l1_entry_sectors; 187 l1_index = (offset >> 9) / s->l1_entry_sectors;
@@ -172,7 +190,6 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, @@ -172,7 +190,6 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
172 l2_offset = s->l1_table[l1_index]; 190 l2_offset = s->l1_table[l1_index];
173 if (!l2_offset) 191 if (!l2_offset)
174 return 0; 192 return 0;
175 -  
176 for(i = 0; i < L2_CACHE_SIZE; i++) { 193 for(i = 0; i < L2_CACHE_SIZE; i++) {
177 if (l2_offset == s->l2_cache_offsets[i]) { 194 if (l2_offset == s->l2_cache_offsets[i]) {
178 /* increment the hit count */ 195 /* increment the hit count */
@@ -204,6 +221,26 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, @@ -204,6 +221,26 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
204 found: 221 found:
205 l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size; 222 l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
206 cluster_offset = le32_to_cpu(l2_table[l2_index]); 223 cluster_offset = le32_to_cpu(l2_table[l2_index]);
  224 + if (!cluster_offset) {
  225 + if (!allocate)
  226 + return 0;
  227 + cluster_offset = lseek(s->fd, 0, SEEK_END);
  228 + ftruncate(s->fd, cluster_offset + (s->cluster_sectors << 9));
  229 + cluster_offset >>= 9;
  230 + /* update L2 table */
  231 + tmp = cpu_to_le32(cluster_offset);
  232 + l2_table[l2_index] = tmp;
  233 + lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
  234 + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
  235 + return 0;
  236 + /* update backup L2 table */
  237 + if (s->l1_backup_table_offset != 0) {
  238 + l2_offset = s->l1_backup_table[l1_index];
  239 + lseek(s->fd, ((int64_t)l2_offset * 512) + (l2_index * sizeof(tmp)), SEEK_SET);
  240 + if (write(s->fd, &tmp, sizeof(tmp)) != sizeof(tmp))
  241 + return 0;
  242 + }
  243 + }
207 cluster_offset <<= 9; 244 cluster_offset <<= 9;
208 return cluster_offset; 245 return cluster_offset;
209 } 246 }
@@ -215,7 +252,7 @@ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, @@ -215,7 +252,7 @@ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
215 int index_in_cluster, n; 252 int index_in_cluster, n;
216 uint64_t cluster_offset; 253 uint64_t cluster_offset;
217 254
218 - cluster_offset = get_cluster_offset(bs, sector_num << 9); 255 + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
219 index_in_cluster = sector_num % s->cluster_sectors; 256 index_in_cluster = sector_num % s->cluster_sectors;
220 n = s->cluster_sectors - index_in_cluster; 257 n = s->cluster_sectors - index_in_cluster;
221 if (n > nb_sectors) 258 if (n > nb_sectors)
@@ -232,7 +269,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, @@ -232,7 +269,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
232 uint64_t cluster_offset; 269 uint64_t cluster_offset;
233 270
234 while (nb_sectors > 0) { 271 while (nb_sectors > 0) {
235 - cluster_offset = get_cluster_offset(bs, sector_num << 9); 272 + cluster_offset = get_cluster_offset(bs, sector_num << 9, 0);
236 index_in_cluster = sector_num % s->cluster_sectors; 273 index_in_cluster = sector_num % s->cluster_sectors;
237 n = s->cluster_sectors - index_in_cluster; 274 n = s->cluster_sectors - index_in_cluster;
238 if (n > nb_sectors) 275 if (n > nb_sectors)
@@ -255,7 +292,27 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, @@ -255,7 +292,27 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
255 static int vmdk_write(BlockDriverState *bs, int64_t sector_num, 292 static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
256 const uint8_t *buf, int nb_sectors) 293 const uint8_t *buf, int nb_sectors)
257 { 294 {
258 - return -1; 295 + BDRVVmdkState *s = bs->opaque;
  296 + int ret, index_in_cluster, n;
  297 + uint64_t cluster_offset;
  298 +
  299 + while (nb_sectors > 0) {
  300 + index_in_cluster = sector_num & (s->cluster_sectors - 1);
  301 + n = s->cluster_sectors - index_in_cluster;
  302 + if (n > nb_sectors)
  303 + n = nb_sectors;
  304 + cluster_offset = get_cluster_offset(bs, sector_num << 9, 1);
  305 + if (!cluster_offset)
  306 + return -1;
  307 + lseek(s->fd, cluster_offset + index_in_cluster * 512, SEEK_SET);
  308 + ret = write(s->fd, buf, n * 512);
  309 + if (ret != n * 512)
  310 + return -1;
  311 + nb_sectors -= n;
  312 + sector_num += n;
  313 + buf += n * 512;
  314 + }
  315 + return 0;
259 } 316 }
260 317
261 static void vmdk_close(BlockDriverState *bs) 318 static void vmdk_close(BlockDriverState *bs)