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
diff -u -p -r1.4 block-vmdk.c
--- block-vmdk.c	18 Sep 2004 19:32:11 -0000	1.4
+++ block-vmdk.c	26 Apr 2005 05:35:34 -0000
@@ -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,14 +54,16 @@ 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;
+    uint32_t *l1_backup_table;
     unsigned int l1_size;
     uint32_t l1_entry_sectors;
 
@@ -96,9 +96,13 @@ static int vmdk_open(BlockDriverState *b
     uint32_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 +115,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 +131,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;
     }
@@ -143,14 +149,26 @@ static int vmdk_open(BlockDriverState *b
         le32_to_cpus(&s->l1_table[i]);
     }
 
+    if (s->l1_backup_table_offset) {
+        s->l1_backup_table = qemu_malloc(l1_size);
+        if (!s->l1_backup_table)
+            goto fail;
+        if (lseek(fd, s->l1_backup_table_offset, SEEK_SET) == -1)
+            goto fail;
+        if (read(fd, s->l1_backup_table, l1_size) != l1_size)
+            goto fail;
+        for(i = 0; i < s->l1_size; i++) {
+            le32_to_cpus(&s->l1_backup_table[i]);
+        }
+    }
+
     s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
     if (!s->l2_cache)
         goto fail;
     s->fd = fd;
-    /* XXX: currently only read only */
-    bs->read_only = 1;
     return 0;
  fail:
+    qemu_free(s->l1_backup_table);
     qemu_free(s->l1_table);
     qemu_free(s->l2_cache);
     close(fd);
@@ -158,12 +176,12 @@ 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;
     
     l1_index = (offset >> 9) / s->l1_entry_sectors;
@@ -172,7 +190,6 @@ static uint64_t get_cluster_offset(Block
     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 */
@@ -204,6 +221,26 @@ static uint64_t get_cluster_offset(Block
  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);
+        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;
+        /* update backup L2 table */
+        if (s->l1_backup_table_offset != 0) {
+            l2_offset = s->l1_backup_table[l1_index];
+            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 +252,7 @@ static int vmdk_is_allocated(BlockDriver
     int 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 +269,7 @@ static int vmdk_read(BlockDriverState *b
     uint64_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 +292,27 @@ static int vmdk_read(BlockDriverState *b
 static 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)
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to