qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH 3/5] block: Virtual Bridges VERDE GOW disk image for


From: Leonardo E. Reiter
Subject: [Qemu-devel] [PATCH 3/5] block: Virtual Bridges VERDE GOW disk image format, legacy GOW version 1 support implementation
Date: Thu, 8 Mar 2012 16:15:44 -0600

commit 368ff891c1d06581aacaab1063fa53838a6b8a99
Author: Leonardo E. Reiter <address@hidden>
Date:   Thu Mar 8 16:01:48 2012 -0600

    Virtual Bridges GOW version 1 disk image format implementation
    Signed-off-by: Leonardo E. Reiter <address@hidden>

diff --git a/block/gow1.c b/block/gow1.c
new file mode 100644
index 0000000..efead4b
--- /dev/null
+++ b/block/gow1.c
@@ -0,0 +1,386 @@
+/*
+ * Virtual Bridges Grow-on-Write v.1 block driver for QEMU
+ *
+ * Copyright (c) 2006-2008 Leonardo E. Reiter
+ * Copyright (c) 1984-2012 Virtual Bridges, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _WIN32
+#include <sys/mman.h>
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+#define BLOCK_GOW1
+#include "gow_int.h"
+
+typedef struct {
+    int fd;
+    gow_header_t *header;
+    size_t len;
+    off64_t base;
+} gow1_state_t;
+
+/* probe for GOW1 image */
+static int gow1_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    int fd;
+    uint8_t buffer[sizeof(uint32_t) * 3];
+    gow_header_t *header = (gow_header_t *) buffer;
+    size_t readsize = sizeof(buffer);
+    ssize_t bytes;
+
+    if (buf == NULL) {
+        return 0;
+    }
+
+    /* make sure header is sound - XXX: just check meta data, not map */
+    fd = open(filename, O_RDONLY);
+    if (fd >= 0) {
+        bytes = read(fd, header, readsize);
+        close(fd);
+        if (bytes != readsize || header->magic != le32_to_cpu(GOW_MAGIC) ||
+                le32_to_cpu(header->size) > GOW_MAXBLOCKS ||
+                le32_to_cpu(header->allocated) > le32_to_cpu(header->size)) {
+            return 0;
+        } else {
+            return 10000;
+        }
+    } else {
+        return 0;
+    }
+}
+
+/* open GOW1 image */
+static int gow1_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    gow1_state_t *s = bs->opaque;
+    int prot = PROT_READ;
+    int oflags = O_BINARY;
+
+    /* compute open flags */
+    if ((flags & O_ACCMODE) == O_RDWR) {
+        oflags |= O_RDWR;
+    } else {
+        oflags |= O_RDONLY;
+        bs->read_only = 1;
+    }
+
+    /* open the disk image */
+    s->fd = open(filename, oflags, 0644);
+    if (s->fd < 0) {
+        return errno == EROFS ? -EACCES : -errno;
+    }
+
+    /* map in the header and bitmap */
+    s->len = GOW_HEADER_SIZE;
+    if (!bs->read_only) {
+        prot |= PROT_WRITE;
+    }
+    s->header = mmap(NULL, s->len, prot, MAP_SHARED, s->fd, 0);
+    if (s->header == MAP_FAILED) {
+        close(s->fd);
+        return -EIO;
+    }
+    s->base = sizeof(gow_header_t);
+
+    /* calculate total sectors */
+    bs->total_sectors = ((int64_t) le32_to_cpu(s->header->size)
+            * GOW_BLOCKSIZE) >> 9;
+
+    return 0;
+}
+
+/* read all or part of a block, zero filling if block is not allocated */
+static inline int read_block(gow1_state_t *s, uint32_t block, uint32_t offset,
+        uint8_t *buf, size_t size)
+{
+    memset(buf, 0, size);
+    if (s->header->map[block] != GOW_FREE_BLOCK) {
+        if (pread64(s->fd, buf, size, IMG_OFFSET(block, offset)) < 0) {
+            return -errno;
+        }
+    }
+
+    return 0;
+}
+
+/* write all or part of a block, allocating if needed */
+static inline int write_block(gow1_state_t *s, uint32_t block, uint32_t offset,
+        const uint8_t *buf, size_t size)
+{
+    uint32_t allocated;
+    uint32_t *wb;
+    int index, wlen = (size / sizeof(uint32_t));
+
+    if (s->header->map[block] == GOW_FREE_BLOCK) {
+
+        /* if it's zero-filled buffer, don't actually allocate */
+        for (index = 0, wb = (uint32_t *) buf; index < wlen && *wb == 0;
+                ++index, ++wb)
+            ;
+        if (index >= wlen) {
+            return 0;
+        }
+
+        /* allocate the block */
+        if (le32_to_cpu(s->header->allocated) < le32_to_cpu(s->header->size)) {
+            s->header->map[block] = s->header->allocated;
+            allocated = le32_to_cpu(s->header->allocated);
+            s->header->allocated = cpu_to_le32(allocated + 1);
+        } else {
+
+            /* image full */
+            return -ENOSPC;
+        }
+    }
+
+    if (pwrite64(s->fd, buf, size, IMG_OFFSET(block, offset)) < 0) {
+        return -errno;
+    } else {
+        return 0;
+    }
+}
+
+/* synchronous positioned read from GOW1 image */
+static int gow1_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf,
+        int nb_sectors)
+{
+    gow1_state_t *s = bs->opaque;
+    uint32_t block, offset, sectors;
+    int64_t sector = sector_num;
+    int left = nb_sectors;
+    uint8_t *ptr = buf;
+    ssize_t bytes;
+    int ret;
+
+    /* loop until all sectors are read */
+    while (left > 0) {
+        block = BLOCK_NUM(sector);
+        offset = BLOCK_OFFSET(sector);
+        sectors = BLOCK_SECTORS(offset);
+        if (sectors > left) {
+            sectors = left;
+        }
+        bytes = sectors << 9;
+
+        ret = read_block(s, block, offset, ptr, bytes);
+        if (ret < 0) {
+            return ret;
+        }
+
+        /* adjust pointers and counts */
+        ptr += bytes;
+        left -= sectors;
+        sector += sectors;
+    }
+
+    return 0;
+}
+
+/* synchronous positioned write to GOW1 image */
+static int gow1_write(BlockDriverState *bs, int64_t sector_num,
+        const uint8_t *buf, int nb_sectors)
+{
+    gow1_state_t *s = bs->opaque;
+    uint32_t block, offset, sectors;
+    int64_t sector = sector_num;
+    int left = nb_sectors;
+    const uint8_t *ptr = buf;
+    ssize_t bytes;
+    int ret;
+
+    /* loop until all sectors are written */
+    while (left > 0) {
+        block = BLOCK_NUM(sector);
+        offset = BLOCK_OFFSET(sector);
+        sectors = BLOCK_SECTORS(offset);
+        if (sectors > left) {
+            sectors = left;
+        }
+        bytes = sectors << 9;
+
+        ret = write_block(s, block, offset, ptr, bytes);
+        if (ret < 0) {
+            return ret;
+        }
+
+        /* adjust pointers and counts */
+        ptr += bytes;
+        left -= sectors;
+        sector += sectors;
+    }
+
+    return 0;
+}
+
+/* close and unmap the GOW1 image */
+static void gow1_close(BlockDriverState *bs)
+{
+    gow1_state_t *s = bs->opaque;
+
+    munmap((void *) s->header, s->len);
+    close(s->fd);
+}
+
+/* create GOW1 image */
+static int gow1_create(const char *filename, QEMUOptionParameter *options)
+{
+    int fd;
+    gow_header_t *header = calloc(1, GOW_HEADER_SIZE);
+    int64_t total_size = 0;
+    const char *backing_file = NULL;
+    int flags = 0;
+    int index;
+    ssize_t written;
+
+    /* Read out options */
+    while (options && options->name) {
+        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+            total_size = options->value.n / 512;
+        } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+            backing_file = options->value.s;
+        } else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) {
+            flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0;
+        }
+        options++;
+    }
+    int64_t blocks = total_size / (GOW_BLOCKSIZE >> 9);
+
+    if (header == NULL) {
+        return -EIO;
+    }
+
+    if (flags || backing_file || blocks < 1 || blocks > GOW_MAXBLOCKS) {
+        free(header);
+        return -ENOTSUP;
+    }
+
+    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+    if (fd < 0) {
+        free(header);
+        return -EIO;
+    }
+
+    /* create and write the header */
+    header->magic = cpu_to_le32(GOW_MAGIC);
+    header->size = cpu_to_le32(((uint32_t) blocks));
+    header->allocated = 0;
+    for (index = 0; index < GOW_MAXBLOCKS; index++) {
+        header->map[index] = GOW_FREE_BLOCK;
+    }
+    written = write(fd, header, GOW_HEADER_SIZE);
+    free(header);
+    close(fd);
+    if (written != GOW_HEADER_SIZE) {
+        return -EIO;
+    } else {
+        return 0;
+    }
+}
+
+/* flush GOW1 image */
+static int gow1_flush(BlockDriverState *bs)
+{
+    gow1_state_t *s = bs->opaque;
+
+    return qemu_fdatasync(s->fd);
+}
+
+/* truncate (resize) the GOW1 image */
+static int gow1_truncate(BlockDriverState *bs, int64_t offset)
+{
+    gow1_state_t *s = bs->opaque;
+    int64_t size;
+    uint32_t index;
+
+    /* calculate size in blocks, rounding up to nearest block */
+    size = (offset + (GOW_BLOCKSIZE - 1)) / GOW_BLOCKSIZE;
+    if (size < 1 || size > GOW_MAXBLOCKS) {
+        return -ENOTSUP;
+    } else {
+
+        /* adjust header */
+        s->header->size = cpu_to_le32(((uint32_t) size));
+        if (le32_to_cpu(s->header->allocated) > le32_to_cpu(s->header->size)) {
+            s->header->allocated = s->header->size;
+        }
+
+        /* clear unallocated blocks, if any */
+        for (index = le32_to_cpu(s->header->allocated);
+                index < le32_to_cpu(s->header->size); ++index) {
+            s->header->map[index] = GOW_FREE_BLOCK;
+        }
+
+        /* XXX: actual image length doesn't change, even if making it smaller
+         * since the exact location of the blocks in the image file may be
+         * out of order */
+
+        gow1_flush(bs);
+
+        return 0;
+    }
+}
+
+/* get length in bytes of GOW1 image */
+static int64_t gow1_getlength(BlockDriverState *bs)
+{
+    return (int64_t) bs->total_sectors << 9;
+}
+
+static QEMUOptionParameter gow1_create_options[] = {
+    {
+        .name = BLOCK_OPT_SIZE,
+        .type = OPT_SIZE,
+        .help = "Virtual disk size"
+    },
+    {
+        .name = BLOCK_OPT_BACKING_FILE,
+        .type = OPT_STRING,
+        .help = "File name of a base image"
+    },
+    { NULL }
+};
+
+
+BlockDriver bdrv_gow1 = {
+    .format_name    = "gow1",
+    .instance_size  = sizeof(gow1_state_t),
+    .bdrv_probe     = gow1_probe,
+    .bdrv_file_open = gow1_open,
+    .bdrv_close     = gow1_close,
+    .bdrv_read      = gow1_read,
+    .bdrv_write     = gow1_write,
+
+    .bdrv_create    = gow1_create,
+    .bdrv_getlength = gow1_getlength,
+    .bdrv_truncate  = gow1_truncate,
+    .create_options = gow1_create_options,
+};
+
+static void bdrv_gow1_init(void)
+{
+    bdrv_register(&bdrv_gow1);
+}
+
+block_init(bdrv_gow1_init);
+
+#endif  /* ! _WIN32 */
+


reply via email to

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