Commit 15d35bc55724d424439967fe8d1550ff9e3221bd

Authored by aliguori
1 parent b71d1c2e

block-vpc: Write support (Kevin Wolf)

Add write support for VHD images.

Signed-off-by: Kevin Wolf <kwolf@suse.de>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6458 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 150 additions and 16 deletions
block-vpc.c
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 * Block driver for Conectix/Microsoft Virtual PC images 2 * Block driver for Conectix/Microsoft Virtual PC images
3 * 3 *
4 * Copyright (c) 2005 Alex Beregszaszi 4 * Copyright (c) 2005 Alex Beregszaszi
  5 + * Copyright (c) 2009 Kevin Wolf <kwolf@suse.de>
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
@@ -107,10 +108,16 @@ struct vhd_dyndisk_header { @@ -107,10 +108,16 @@ struct vhd_dyndisk_header {
107 typedef struct BDRVVPCState { 108 typedef struct BDRVVPCState {
108 BlockDriverState *hd; 109 BlockDriverState *hd;
109 110
  111 + uint8_t footer_buf[HEADER_SIZE];
  112 + uint64_t free_data_block_offset;
110 int max_table_entries; 113 int max_table_entries;
111 uint32_t *pagetable; 114 uint32_t *pagetable;
  115 + uint64_t bat_offset;
  116 + uint64_t last_bitmap_offset;
112 117
113 uint32_t block_size; 118 uint32_t block_size;
  119 + uint32_t bitmap_size;
  120 +
114 #ifdef CACHE 121 #ifdef CACHE
115 uint8_t *pageentry_u8; 122 uint8_t *pageentry_u8;
116 uint32_t *pageentry_u32; 123 uint32_t *pageentry_u32;
@@ -135,16 +142,14 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags) @@ -135,16 +142,14 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
135 struct vhd_dyndisk_header* dyndisk_header; 142 struct vhd_dyndisk_header* dyndisk_header;
136 uint8_t buf[HEADER_SIZE]; 143 uint8_t buf[HEADER_SIZE];
137 144
138 - bs->read_only = 1; // no write support yet  
139 -  
140 ret = bdrv_file_open(&s->hd, filename, flags); 145 ret = bdrv_file_open(&s->hd, filename, flags);
141 if (ret < 0) 146 if (ret < 0)
142 return ret; 147 return ret;
143 148
144 - if (bdrv_pread(s->hd, 0, buf, HEADER_SIZE) != HEADER_SIZE) 149 + if (bdrv_pread(s->hd, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
145 goto fail; 150 goto fail;
146 151
147 - footer = (struct vhd_footer*) buf; 152 + footer = (struct vhd_footer*) s->footer_buf;
148 if (strncmp(footer->creator, "conectix", 8)) 153 if (strncmp(footer->creator, "conectix", 8))
149 goto fail; 154 goto fail;
150 155
@@ -158,26 +163,41 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags) @@ -158,26 +163,41 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
158 != HEADER_SIZE) 163 != HEADER_SIZE)
159 goto fail; 164 goto fail;
160 165
161 - footer = NULL;  
162 dyndisk_header = (struct vhd_dyndisk_header*) buf; 166 dyndisk_header = (struct vhd_dyndisk_header*) buf;
163 167
164 if (strncmp(dyndisk_header->magic, "cxsparse", 8)) 168 if (strncmp(dyndisk_header->magic, "cxsparse", 8))
165 goto fail; 169 goto fail;
166 170
167 171
  172 + s->block_size = be32_to_cpu(dyndisk_header->block_size);
  173 + s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
  174 +
168 s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); 175 s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
169 s->pagetable = qemu_malloc(s->max_table_entries * 4); 176 s->pagetable = qemu_malloc(s->max_table_entries * 4);
170 if (!s->pagetable) 177 if (!s->pagetable)
171 goto fail; 178 goto fail;
172 179
173 - if (bdrv_pread(s->hd, be64_to_cpu(dyndisk_header->table_offset),  
174 - s->pagetable, s->max_table_entries * 4) != s->max_table_entries * 4) 180 + s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
  181 + if (bdrv_pread(s->hd, s->bat_offset, s->pagetable,
  182 + s->max_table_entries * 4) != s->max_table_entries * 4)
175 goto fail; 183 goto fail;
176 184
177 - for (i = 0; i < s->max_table_entries; i++)  
178 - be32_to_cpus(&s->pagetable[i]); 185 + s->free_data_block_offset =
  186 + (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
  187 +
  188 + for (i = 0; i < s->max_table_entries; i++) {
  189 + be32_to_cpus(&s->pagetable[i]);
  190 + if (s->pagetable[i] != 0xFFFFFFFF) {
  191 + int64_t next = (512 * (int64_t) s->pagetable[i]) +
  192 + s->bitmap_size + s->block_size;
  193 +
  194 + if (next> s->free_data_block_offset)
  195 + s->free_data_block_offset = next;
  196 + }
  197 + }
  198 +
  199 + s->last_bitmap_offset = (int64_t) -1;
179 200
180 - s->block_size = be32_to_cpu(dyndisk_header->block_size);  
181 #ifdef CACHE 201 #ifdef CACHE
182 s->pageentry_u8 = qemu_malloc(512); 202 s->pageentry_u8 = qemu_malloc(512);
183 if (!s->pageentry_u8) 203 if (!s->pageentry_u8)
@@ -196,8 +216,12 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags) @@ -196,8 +216,12 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
196 /* 216 /*
197 * Returns the absolute byte offset of the given sector in the image file. 217 * Returns the absolute byte offset of the given sector in the image file.
198 * If the sector is not allocated, -1 is returned instead. 218 * If the sector is not allocated, -1 is returned instead.
  219 + *
  220 + * The parameter write must be 1 if the offset will be used for a write
  221 + * operation (the block bitmaps is updated then), 0 otherwise.
199 */ 222 */
200 -static inline int64_t get_sector_offset(BlockDriverState *bs, int64_t sector_num) 223 +static inline int64_t get_sector_offset(BlockDriverState *bs,
  224 + int64_t sector_num, int write)
201 { 225 {
202 BDRVVPCState *s = bs->opaque; 226 BDRVVPCState *s = bs->opaque;
203 uint64_t offset = sector_num * 512; 227 uint64_t offset = sector_num * 512;
@@ -207,11 +231,24 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, int64_t sector_num @@ -207,11 +231,24 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, int64_t sector_num
207 pagetable_index = offset / s->block_size; 231 pagetable_index = offset / s->block_size;
208 pageentry_index = (offset % s->block_size) / 512; 232 pageentry_index = (offset % s->block_size) / 512;
209 233
210 - if (pagetable_index > s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)  
211 - return -1; // not allocated 234 + if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)
  235 + return -1; // not allocated
212 236
213 bitmap_offset = 512 * s->pagetable[pagetable_index]; 237 bitmap_offset = 512 * s->pagetable[pagetable_index];
214 - block_offset = bitmap_offset + 512 + (512 * pageentry_index); 238 + block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index);
  239 +
  240 + // We must ensure that we don't write to any sectors which are marked as
  241 + // unused in the bitmap. We get away with setting all bits in the block
  242 + // bitmap each time we write to a new block. This might cause Virtual PC to
  243 + // miss sparse read optimization, but it's not a problem in terms of
  244 + // correctness.
  245 + if (write && (s->last_bitmap_offset != bitmap_offset)) {
  246 + uint8_t bitmap[s->bitmap_size];
  247 +
  248 + s->last_bitmap_offset = bitmap_offset;
  249 + memset(bitmap, 0xff, s->bitmap_size);
  250 + bdrv_pwrite(s->hd, bitmap_offset, bitmap, s->bitmap_size);
  251 + }
215 252
216 // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", 253 // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
217 // sector_num, pagetable_index, pageentry_index, 254 // sector_num, pagetable_index, pageentry_index,
@@ -248,6 +285,75 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, int64_t sector_num @@ -248,6 +285,75 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, int64_t sector_num
248 return block_offset; 285 return block_offset;
249 } 286 }
250 287
  288 +/*
  289 + * Writes the footer to the end of the image file. This is needed when the
  290 + * file grows as it overwrites the old footer
  291 + *
  292 + * Returns 0 on success and < 0 on error
  293 + */
  294 +static int rewrite_footer(BlockDriverState* bs)
  295 +{
  296 + int ret;
  297 + BDRVVPCState *s = bs->opaque;
  298 + int64_t offset = s->free_data_block_offset;
  299 +
  300 + ret = bdrv_pwrite(s->hd, offset, s->footer_buf, HEADER_SIZE);
  301 + if (ret < 0)
  302 + return ret;
  303 +
  304 + return 0;
  305 +}
  306 +
  307 +/*
  308 + * Allocates a new block. This involves writing a new footer and updating
  309 + * the Block Allocation Table to use the space at the old end of the image
  310 + * file (overwriting the old footer)
  311 + *
  312 + * Returns the sectors' offset in the image file on success and < 0 on error
  313 + */
  314 +static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
  315 +{
  316 + BDRVVPCState *s = bs->opaque;
  317 + int64_t bat_offset;
  318 + uint32_t index, bat_value;
  319 + int ret;
  320 + uint8_t bitmap[s->bitmap_size];
  321 +
  322 + // Check if sector_num is valid
  323 + if ((sector_num < 0) || (sector_num > bs->total_sectors))
  324 + return -1;
  325 +
  326 + // Write entry into in-memory BAT
  327 + index = (sector_num * 512) / s->block_size;
  328 + if (s->pagetable[index] != 0xFFFFFFFF)
  329 + return -1;
  330 +
  331 + s->pagetable[index] = s->free_data_block_offset / 512;
  332 +
  333 + // Initialize the block's bitmap
  334 + memset(bitmap, 0xff, s->bitmap_size);
  335 + bdrv_pwrite(s->hd, s->free_data_block_offset, bitmap, s->bitmap_size);
  336 +
  337 + // Write new footer (the old one will be overwritten)
  338 + s->free_data_block_offset += s->block_size + s->bitmap_size;
  339 + ret = rewrite_footer(bs);
  340 + if (ret < 0)
  341 + goto fail;
  342 +
  343 + // Write BAT entry to disk
  344 + bat_offset = s->bat_offset + (4 * index);
  345 + bat_value = be32_to_cpu(s->pagetable[index]);
  346 + ret = bdrv_pwrite(s->hd, bat_offset, &bat_value, 4);
  347 + if (ret < 0)
  348 + goto fail;
  349 +
  350 + return get_sector_offset(bs, sector_num, 0);
  351 +
  352 +fail:
  353 + s->free_data_block_offset -= (s->block_size + s->bitmap_size);
  354 + return -1;
  355 +}
  356 +
251 static int vpc_read(BlockDriverState *bs, int64_t sector_num, 357 static int vpc_read(BlockDriverState *bs, int64_t sector_num,
252 uint8_t *buf, int nb_sectors) 358 uint8_t *buf, int nb_sectors)
253 { 359 {
@@ -256,7 +362,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, @@ -256,7 +362,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
256 int64_t offset; 362 int64_t offset;
257 363
258 while (nb_sectors > 0) { 364 while (nb_sectors > 0) {
259 - offset = get_sector_offset(bs, sector_num); 365 + offset = get_sector_offset(bs, sector_num, 0);
260 366
261 if (offset == -1) { 367 if (offset == -1) {
262 memset(buf, 0, 512); 368 memset(buf, 0, 512);
@@ -273,6 +379,34 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, @@ -273,6 +379,34 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
273 return 0; 379 return 0;
274 } 380 }
275 381
  382 +static int vpc_write(BlockDriverState *bs, int64_t sector_num,
  383 + const uint8_t *buf, int nb_sectors)
  384 +{
  385 + BDRVVPCState *s = bs->opaque;
  386 + int64_t offset;
  387 + int ret;
  388 +
  389 + while (nb_sectors > 0) {
  390 + offset = get_sector_offset(bs, sector_num, 1);
  391 +
  392 + if (offset == -1) {
  393 + offset = alloc_block(bs, sector_num);
  394 + if (offset < 0)
  395 + return -1;
  396 + }
  397 +
  398 + ret = bdrv_pwrite(s->hd, offset, buf, 512);
  399 + if (ret != 512)
  400 + return -1;
  401 +
  402 + nb_sectors--;
  403 + sector_num++;
  404 + buf += 512;
  405 + }
  406 +
  407 + return 0;
  408 +}
  409 +
276 static void vpc_close(BlockDriverState *bs) 410 static void vpc_close(BlockDriverState *bs)
277 { 411 {
278 BDRVVPCState *s = bs->opaque; 412 BDRVVPCState *s = bs->opaque;
@@ -289,6 +423,6 @@ BlockDriver bdrv_vpc = { @@ -289,6 +423,6 @@ BlockDriver bdrv_vpc = {
289 vpc_probe, 423 vpc_probe,
290 vpc_open, 424 vpc_open,
291 vpc_read, 425 vpc_read,
292 - NULL, 426 + vpc_write,
293 vpc_close, 427 vpc_close,
294 }; 428 };