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 2 * Block driver for the VMDK format
3 3 *
4 4 * Copyright (c) 2004 Fabrice Bellard
  5 + * Copyright (c) 2005 Filip Navara
5 6 *
6 7 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 8 * of this software and associated documentation files (the "Software"), to deal
... ... @@ -24,9 +25,6 @@
24 25 #include "vl.h"
25 26 #include "block_int.h"
26 27  
27   -/* XXX: this code is untested */
28   -/* XXX: add write support */
29   -
30 28 #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
31 29 #define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
32 30  
... ... @@ -56,14 +54,16 @@ typedef struct {
56 54 int64_t grain_offset;
57 55 char filler[1];
58 56 char check_bytes[4];
59   -} VMDK4Header;
  57 +} __attribute__((packed)) VMDK4Header;
60 58  
61 59 #define L2_CACHE_SIZE 16
62 60  
63 61 typedef struct BDRVVmdkState {
64 62 int fd;
65 63 int64_t l1_table_offset;
  64 + int64_t l1_backup_table_offset;
66 65 uint32_t *l1_table;
  66 + uint32_t *l1_backup_table;
67 67 unsigned int l1_size;
68 68 uint32_t l1_entry_sectors;
69 69  
... ... @@ -96,9 +96,13 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
96 96 uint32_t magic;
97 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 106 if (read(fd, &magic, sizeof(magic)) != sizeof(magic))
103 107 goto fail;
104 108 magic = be32_to_cpu(magic);
... ... @@ -111,7 +115,8 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
111 115 s->l2_size = 1 << 9;
112 116 s->l1_size = 1 << 6;
113 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 120 s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
116 121 } else if (magic == VMDK4_MAGIC) {
117 122 VMDK4Header header;
... ... @@ -126,7 +131,8 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
126 131 goto fail;
127 132 s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
128 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 136 } else {
131 137 goto fail;
132 138 }
... ... @@ -143,14 +149,26 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
143 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 165 s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
147 166 if (!s->l2_cache)
148 167 goto fail;
149 168 s->fd = fd;
150   - /* XXX: currently only read only */
151   - bs->read_only = 1;
152 169 return 0;
153 170 fail:
  171 + qemu_free(s->l1_backup_table);
154 172 qemu_free(s->l1_table);
155 173 qemu_free(s->l2_cache);
156 174 close(fd);
... ... @@ -158,12 +176,12 @@ static int vmdk_open(BlockDriverState *bs, const char *filename)
158 176 }
159 177  
160 178 static uint64_t get_cluster_offset(BlockDriverState *bs,
161   - uint64_t offset)
  179 + uint64_t offset, int allocate)
162 180 {
163 181 BDRVVmdkState *s = bs->opaque;
164 182 unsigned int l1_index, l2_offset, l2_index;
165 183 int min_index, i, j;
166   - uint32_t min_count, *l2_table;
  184 + uint32_t min_count, *l2_table, tmp;
167 185 uint64_t cluster_offset;
168 186  
169 187 l1_index = (offset >> 9) / s->l1_entry_sectors;
... ... @@ -172,7 +190,6 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
172 190 l2_offset = s->l1_table[l1_index];
173 191 if (!l2_offset)
174 192 return 0;
175   -
176 193 for(i = 0; i < L2_CACHE_SIZE; i++) {
177 194 if (l2_offset == s->l2_cache_offsets[i]) {
178 195 /* increment the hit count */
... ... @@ -204,6 +221,26 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
204 221 found:
205 222 l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
206 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 244 cluster_offset <<= 9;
208 245 return cluster_offset;
209 246 }
... ... @@ -215,7 +252,7 @@ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
215 252 int index_in_cluster, n;
216 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 256 index_in_cluster = sector_num % s->cluster_sectors;
220 257 n = s->cluster_sectors - index_in_cluster;
221 258 if (n > nb_sectors)
... ... @@ -232,7 +269,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
232 269 uint64_t cluster_offset;
233 270  
234 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 273 index_in_cluster = sector_num % s->cluster_sectors;
237 274 n = s->cluster_sectors - index_in_cluster;
238 275 if (n > nb_sectors)
... ... @@ -255,7 +292,27 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
255 292 static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
256 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 318 static void vmdk_close(BlockDriverState *bs)
... ...