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 2 * Block driver for Conectix/Microsoft Virtual PC images
3 3 *
4 4 * Copyright (c) 2005 Alex Beregszaszi
  5 + * Copyright (c) 2009 Kevin Wolf <kwolf@suse.de>
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
... ... @@ -107,10 +108,16 @@ struct vhd_dyndisk_header {
107 108 typedef struct BDRVVPCState {
108 109 BlockDriverState *hd;
109 110  
  111 + uint8_t footer_buf[HEADER_SIZE];
  112 + uint64_t free_data_block_offset;
110 113 int max_table_entries;
111 114 uint32_t *pagetable;
  115 + uint64_t bat_offset;
  116 + uint64_t last_bitmap_offset;
112 117  
113 118 uint32_t block_size;
  119 + uint32_t bitmap_size;
  120 +
114 121 #ifdef CACHE
115 122 uint8_t *pageentry_u8;
116 123 uint32_t *pageentry_u32;
... ... @@ -135,16 +142,14 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
135 142 struct vhd_dyndisk_header* dyndisk_header;
136 143 uint8_t buf[HEADER_SIZE];
137 144  
138   - bs->read_only = 1; // no write support yet
139   -
140 145 ret = bdrv_file_open(&s->hd, filename, flags);
141 146 if (ret < 0)
142 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 150 goto fail;
146 151  
147   - footer = (struct vhd_footer*) buf;
  152 + footer = (struct vhd_footer*) s->footer_buf;
148 153 if (strncmp(footer->creator, "conectix", 8))
149 154 goto fail;
150 155  
... ... @@ -158,26 +163,41 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
158 163 != HEADER_SIZE)
159 164 goto fail;
160 165  
161   - footer = NULL;
162 166 dyndisk_header = (struct vhd_dyndisk_header*) buf;
163 167  
164 168 if (strncmp(dyndisk_header->magic, "cxsparse", 8))
165 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 175 s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
169 176 s->pagetable = qemu_malloc(s->max_table_entries * 4);
170 177 if (!s->pagetable)
171 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 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 201 #ifdef CACHE
182 202 s->pageentry_u8 = qemu_malloc(512);
183 203 if (!s->pageentry_u8)
... ... @@ -196,8 +216,12 @@ static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
196 216 /*
197 217 * Returns the absolute byte offset of the given sector in the image file.
198 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 226 BDRVVPCState *s = bs->opaque;
203 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 231 pagetable_index = offset / s->block_size;
208 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 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 253 // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
217 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 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 357 static int vpc_read(BlockDriverState *bs, int64_t sector_num,
252 358 uint8_t *buf, int nb_sectors)
253 359 {
... ... @@ -256,7 +362,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
256 362 int64_t offset;
257 363  
258 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 367 if (offset == -1) {
262 368 memset(buf, 0, 512);
... ... @@ -273,6 +379,34 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
273 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 410 static void vpc_close(BlockDriverState *bs)
277 411 {
278 412 BDRVVPCState *s = bs->opaque;
... ... @@ -289,6 +423,6 @@ BlockDriver bdrv_vpc = {
289 423 vpc_probe,
290 424 vpc_open,
291 425 vpc_read,
292   - NULL,
  426 + vpc_write,
293 427 vpc_close,
294 428 };
... ...