qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [patch] VMDK write support / VMDK4 fix


From: Filip Navara
Subject: [Qemu-devel] [patch] VMDK write support / VMDK4 fix
Date: Sun, 24 Apr 2005 12:57:10 +0200
User-agent: Mozilla Thunderbird 0.9 (Windows/20041103)

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        24 Apr 2005 10:56:16 -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,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 *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 +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 *b
     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_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(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 +279,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 +302,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)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]