VMDK write support / VMDK4 fix
PATCH 1/1 VMDK write support / VMDK4 fix
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
block-vmdk.c +97 -30
@@ -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 *buint32_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 *bif (!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(BlockDriverint 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 *buint64_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 *bstatic 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)
Quoted 3 lines · Filip Navara
Changelog:
- Fix packing of VMDK4Header.
- Add write support for VMDK files (tested only on VMDK4 files).
Please discard this patch for now...it causes some image corruption.
- Filip