[Top][All Lists]
[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
Date: Thu Mar 8 16:01:48 2012 -0600
Virtual Bridges GOW version 1 disk image format implementation
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 */
+
- [Qemu-devel] [PATCH 3/5] block: Virtual Bridges VERDE GOW disk image format, legacy GOW version 1 support implementation,
Leonardo E. Reiter <=