Patchwork [5/6] VMDK: read/write compressed extent

login
register
mail settings
Submitter Feiran Zheng
Date July 27, 2011, 9:27 a.m.
Message ID <1311758866-30029-6-git-send-email-famcool@gmail.com>
Download mbox | patch
Permalink /patch/107006/
State New
Headers show

Comments

Feiran Zheng - July 27, 2011, 9:27 a.m.
Add support for reading/writing compressed extent.

Signed-off-by: Fam Zheng <famcool@gmail.com>
---
 block/vmdk.c |  115 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 103 insertions(+), 12 deletions(-)
Stefan Hajnoczi - Aug. 2, 2011, 11:11 a.m.
On Wed, Jul 27, 2011 at 10:27 AM, Fam Zheng <famcool@gmail.com> wrote:
> @@ -863,6 +884,9 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
>     }
>     ret = 0;
>  out:
> +    if (data) {
> +        qemu_free(data);
> +    }

qemu_free(NULL) is a nop, you don't need the if statement.  Just call
qemu_free(data).

>     return ret;
>  }
>
> @@ -871,15 +895,65 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset,
>                             int nb_sectors)
>  {
>     int ret;
> -
> +    int cluster_bytes, buf_bytes;
> +    uint8_t *cluster_buf, *compressed_data;
> +    uint8_t *uncomp_buf;
> +    uint32_t data_len;
> +    VmdkGrainMarker *marker;
> +    uLongf buf_len;
> +
> +
> +    if (!extent->compressed) {
> +        ret = bdrv_pread(extent->file,
> +                          cluster_offset + offset_in_cluster,
> +                          buf, nb_sectors * 512);
> +        if (ret == nb_sectors * 512) {
> +            return 0;
> +        } else {
> +            return -EIO;
> +        }
> +    }
> +    cluster_bytes = extent->cluster_sectors * 512;
> +    /* Read two clusters in case GrainMarker + compressed data > one cluster */
> +    buf_bytes = cluster_bytes * 2;
> +    cluster_buf = qemu_mallocz(buf_bytes);
> +    uncomp_buf = qemu_mallocz(cluster_bytes);

Do these really need to be zeroed?

>     ret = bdrv_pread(extent->file,
> -                      cluster_offset + offset_in_cluster,
> -                      buf, nb_sectors * 512);
> -    if (ret == nb_sectors * 512) {
> -        return 0;
> -    } else {
> -        return -EIO;
> +                cluster_offset,
> +                cluster_buf, buf_bytes);
> +    if (ret < 0) {
> +        goto out;
> +    }
> +    compressed_data = cluster_buf;
> +    buf_len = cluster_bytes;
> +    data_len = cluster_bytes;
> +    if (extent->has_marker) {
> +        marker = (VmdkGrainMarker *)cluster_buf;
> +        compressed_data = marker->data;
> +        data_len = marker->size;

Endianness?

Stefan

Patch

diff --git a/block/vmdk.c b/block/vmdk.c
index 5f1638e..4799aa5 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -771,10 +771,12 @@  static int get_cluster_offset(BlockDriverState *bs,
 
         /* Avoid the L2 tables update for the images that have snapshots. */
         *cluster_offset = bdrv_getlength(extent->file);
-        bdrv_truncate(
-            extent->file,
-            *cluster_offset + (extent->cluster_sectors << 9)
-        );
+        if (!extent->compressed) {
+            bdrv_truncate(
+                extent->file,
+                *cluster_offset + (extent->cluster_sectors << 9)
+            );
+        }
 
         *cluster_offset >>= 9;
         tmp = cpu_to_le32(*cluster_offset);
@@ -850,9 +852,28 @@  static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
                             int nb_sectors, int64_t sector_num)
 {
     int ret;
+    VmdkGrainMarker *data = NULL;
+    uLongf buf_len;
     const uint8_t *write_buf = buf;
     int write_len = nb_sectors * 512;
 
+    if (extent->compressed) {
+        if (!extent->has_marker) {
+            ret = -EINVAL;
+            goto out;
+        }
+        buf_len = (extent->cluster_sectors << 9) * 2;
+        data = qemu_mallocz(buf_len + sizeof(VmdkGrainMarker));
+        if (compress(data->data, &buf_len, buf, nb_sectors << 9) != Z_OK ||
+                buf_len == 0) {
+            ret = -EINVAL;
+            goto out;
+        }
+        data->lba = sector_num;
+        data->size = buf_len;
+        write_buf = (uint8_t *)data;
+        write_len = buf_len + sizeof(VmdkGrainMarker);
+    }
     ret = bdrv_pwrite(extent->file,
                         cluster_offset + offset_in_cluster,
                         write_buf,
@@ -863,6 +884,9 @@  static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
     }
     ret = 0;
  out:
+    if (data) {
+        qemu_free(data);
+    }
     return ret;
 }
 
@@ -871,15 +895,65 @@  static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset,
                             int nb_sectors)
 {
     int ret;
-
+    int cluster_bytes, buf_bytes;
+    uint8_t *cluster_buf, *compressed_data;
+    uint8_t *uncomp_buf;
+    uint32_t data_len;
+    VmdkGrainMarker *marker;
+    uLongf buf_len;
+ 
+
+    if (!extent->compressed) {
+        ret = bdrv_pread(extent->file,
+                          cluster_offset + offset_in_cluster,
+                          buf, nb_sectors * 512);
+        if (ret == nb_sectors * 512) {
+            return 0;
+        } else {
+            return -EIO;
+        }
+    }
+    cluster_bytes = extent->cluster_sectors * 512;
+    /* Read two clusters in case GrainMarker + compressed data > one cluster */
+    buf_bytes = cluster_bytes * 2;
+    cluster_buf = qemu_mallocz(buf_bytes);
+    uncomp_buf = qemu_mallocz(cluster_bytes);
     ret = bdrv_pread(extent->file,
-                      cluster_offset + offset_in_cluster,
-                      buf, nb_sectors * 512);
-    if (ret == nb_sectors * 512) {
-        return 0;
-    } else {
-        return -EIO;
+                cluster_offset,
+                cluster_buf, buf_bytes);
+    if (ret < 0) {
+        goto out;
+    }
+    compressed_data = cluster_buf;
+    buf_len = cluster_bytes;
+    data_len = cluster_bytes;
+    if (extent->has_marker) {
+        marker = (VmdkGrainMarker *)cluster_buf;
+        compressed_data = marker->data;
+        data_len = marker->size;
+    }
+    if (!data_len || data_len > buf_bytes) {
+        ret = -EINVAL;
+        goto out;
+    }
+    ret = uncompress(uncomp_buf, &buf_len, compressed_data, data_len);
+    if (ret != Z_OK) {
+        ret = -EINVAL;
+        goto out;
+
+    }
+    if (offset_in_cluster < 0 ||
+            offset_in_cluster + nb_sectors * 512 > buf_len) {
+        ret = -EINVAL;
+        goto out;
     }
+    memcpy(buf, uncomp_buf + offset_in_cluster, nb_sectors * 512);
+    ret = 0;
+
+ out:
+    qemu_free(uncomp_buf);
+    qemu_free(cluster_buf);
+    return ret;
 }
 
 static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
@@ -959,8 +1033,25 @@  static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
                                 bs,
                                 extent,
                                 &m_data,
-                                sector_num << 9, 1,
+                                sector_num << 9, !extent->compressed,
                                 &cluster_offset);
+        if (extent->compressed) {
+            if (ret == 0) {
+                /* Refuse write to allocated cluster for streamOptimized */
+                fprintf(stderr,
+                        "VMDK: can't write to allocated cluster"
+                        " for streamOptimized\n");
+                return -EIO;
+            } else {
+                /* allocate */
+                ret = get_cluster_offset(
+                                        bs,
+                                        extent,
+                                        &m_data,
+                                        sector_num << 9, 1,
+                                        &cluster_offset);
+            }
+        }
         if (ret) {
             return -EINVAL;
         }