* [Qemu-devel] multi-partition block driver
@ 2008-03-03 17:22 Aleksandar Kanchev
0 siblings, 0 replies; only message in thread
From: Aleksandar Kanchev @ 2008-03-03 17:22 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/html, Size: 2234 bytes --]
[-- Attachment #2: qemu-block-multipart.patch --]
[-- Type: text/x-patch, Size: 16753 bytes --]
INSTALL: 'tar -xzf qemu-0.9.1.tar.gz; patch -p0 < qemu-block-multipart.patch'
diff -Nurp qemu-0.9.1/block.c qemu-0.9.1-multipart/block.c
--- qemu-0.9.1/block.c 2008-01-06 20:38:42.000000000 +0100
+++ qemu-0.9.1-multipart/block.c 2008-03-03 09:56:42.000000000 +0100
@@ -273,8 +273,8 @@ static BlockDriver *find_image_format(co
#endif
drv = find_protocol(filename);
- /* no need to test disk image formats for vvfat */
- if (drv == &bdrv_vvfat)
+ /* no need to test disk image formats for vvfat or multipart */
+ if (drv == &bdrv_vvfat || drv == &bdrv_multipart)
return drv;
ret = bdrv_file_open(&bs, filename, BDRV_O_RDONLY);
@@ -1294,6 +1294,7 @@ void bdrv_init(void)
bdrv_register(&bdrv_vvfat);
bdrv_register(&bdrv_qcow2);
bdrv_register(&bdrv_parallels);
+ bdrv_register(&bdrv_multipart);
}
void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb,
diff -Nurp qemu-0.9.1/block.h qemu-0.9.1-multipart/block.h
--- qemu-0.9.1/block.h 2008-01-06 20:38:42.000000000 +0100
+++ qemu-0.9.1-multipart/block.h 2008-03-03 09:57:04.000000000 +0100
@@ -16,6 +16,7 @@ extern BlockDriver bdrv_vpc;
extern BlockDriver bdrv_vvfat;
extern BlockDriver bdrv_qcow2;
extern BlockDriver bdrv_parallels;
+extern BlockDriver bdrv_multipart;
typedef struct BlockDriverInfo {
/* in bytes, 0 if irrelevant */
diff -Nurp qemu-0.9.1/block-multipart.c qemu-0.9.1-multipart/block-multipart.c
--- qemu-0.9.1/block-multipart.c 1970-01-01 01:00:00.000000000 +0100
+++ qemu-0.9.1-multipart/block-multipart.c 2008-03-03 17:04:04.000000000 +0100
@@ -0,0 +1,596 @@
+/*
+ * QEMU Block driver for multi-partition images (virtually merges disk images)
+ *
+ * Copyright (c) 2008 Aleksandar Kanchev
+ *
+ * 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.
+ */
+/*
+ * The block driver expects a mini config file with the following format:
+ *
+ * The first line tells the driver if it should generate a
+ * partition table. If it is set to the word "dynamic", a partition table is
+ * generated, anything else (including an empty line) tells the driver not to
+ * modify the MBR.
+ *
+ * The second line specifies a disk_image with the drive's first track. It could
+ * be a simple raw 512b file containing the MBR. It could be even smaller,
+ * containing only the MBR's code. The first track is always kept in memory and
+ * changes are mirrored to the disk image if it's available. If the line is left
+ * empty, all changes will be lost when the VM is powered down.
+ *
+ * Lines from 3 to 6 contain the four partitions of the drive. The partition
+ * format is:
+ * <disk_image>[,<partition type>[,<partition status>]]
+ * example:
+ * /dev/hda1,83,80
+ * /dev/hda1,83,a
+ * /dev/hda1,83
+ * The first two lines are equivalent, where 'a' is 0x80 or "active" and 0x83
+ * is the linux partition type. The partition type and status are only
+ * relevant if the first line is set to "dynamic".
+ *
+ * The rest of the config file is ignored.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+
+#define FORMAT_NAME "multipart"
+#define PROTOCOL_NAME "multi"
+
+#define CONFIG_LINE_LEN 1024
+
+#define BLOCK_SHIFT 9
+#define BLOCK_SIZE (1 << BLOCK_SHIFT)
+#define TRACK_SECTORS 63
+#define HEADS 255
+
+
+typedef struct {
+ uint8_t head;
+ uint8_t sector;
+ uint8_t cylinder;
+} __attribute__((packed)) chs_t;
+
+typedef struct {
+ uint8_t status; // 0x80 = bootable, 0x00 = non-bootable, other = malformed
+ chs_t chs_start;
+ uint8_t type; // 0x07 = HPFS/NTFS, 0x82 = Linux swap, 0x83 = Linux ...
+ chs_t chs_end;
+ uint32_t lba_start;
+ uint32_t lba_length;
+} __attribute__((packed)) partition_t;
+
+typedef struct {
+ uint8_t code[440];
+ uint8_t disk_signature[4];
+ uint8_t zero[2];
+ partition_t partition[4];
+ uint8_t magic[2];
+} __attribute__((packed)) mbr_t;
+
+typedef union {
+ uint8_t buf[TRACK_SECTORS * BLOCK_SIZE];
+ mbr_t mbr;
+} first_track_t;
+
+typedef struct BdrvMultipartState {
+ first_track_t first_track;
+ BlockDriverState *bs_first_track;
+ BlockDriverState *bs_partition[4];
+} BdrvMultipartState;
+
+typedef struct {
+ int16_t status;
+ int16_t type;
+ char *filename;
+} config_partition_t;
+
+typedef struct {
+ uint8_t dynamic;
+ char *ft_filename;
+ config_partition_t partition[4];
+} config_t;
+
+
+static void info(const char *format, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "qemu %s: ", FORMAT_NAME);
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+}
+
+static int16_t get_hex(const char *str)
+{
+ int16_t ret = -1;
+
+ if (str[0] == '0' && tolower(str[1]) == 'x')
+ str += 2;
+
+ if (*str != '\0') {
+ int i, ch;
+
+ ret = 0;
+ for (i = 0; i < 2 && str[i] != '\0'; i++) {
+ ret <<= 4;
+
+ ch = tolower(str[i]);
+ if (ch >= 'a' && ch <= 'f')
+ ret += 10 + ch - 'a';
+ else if (ch >= '0' && ch <= '9')
+ ret += ch - '0';
+ }
+ }
+
+ return ret;
+}
+
+static char *pstrdup(const char *src)
+{
+ const char *p = src;
+ char *ret;
+ size_t len;
+
+ while (*p != '\0')
+ p++;
+
+ len = p - src;
+ ret = qemu_mallocz(len + 1);
+ if (ret != NULL)
+ memcpy(ret, src, len);
+
+ return ret;
+}
+
+static char *pgetline(int fd)
+{
+ static char buf[CONFIG_LINE_LEN + 1];
+ char *p;
+ int r;
+
+ for (p = buf; (r = read(fd, p, 1)) > 0; p++) {
+ if (*p == '\r' || *p == '\n')
+ break;
+
+ if ((p - buf + 1) >= (sizeof(buf) - 1)) {
+ p++;
+ break;
+ }
+ }
+
+ *p = '\0';
+
+ if (r <= 0 && p == buf)
+ return NULL;
+
+ return buf;
+}
+
+static void config_delete(config_t *cfg)
+{
+ int i;
+
+ if (cfg->ft_filename != NULL)
+ qemu_free(cfg->ft_filename);
+
+ for (i = 0; i < 4 && cfg->partition[i].filename != NULL; i++)
+ qemu_free(cfg->partition[i].filename);
+
+ qemu_free(cfg);
+}
+
+static int config_parse(const char *filename, config_t **pcfg)
+{
+ int nline;
+ int fd;
+ char *line;
+ char *p, *q;
+ config_t *cfg;
+
+ cfg = qemu_mallocz(sizeof(config_t));
+ if (!cfg)
+ return -ENOMEM;
+
+ strstart(filename, PROTOCOL_NAME ":", &filename);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ for (nline = 0; nline < 6; nline++) {
+ line = pgetline(fd);
+ if (!line) {
+ if (nline < 3) {
+ info("config is too short");
+ goto error;
+ }
+ break;
+ }
+
+ /* dynamic */
+ if (nline == 0) {
+ if (tolower(*line) == 'd')
+ cfg->dynamic = 1;
+
+ /* first track */
+ } else if (nline == 1) {
+ if (*line != '\0')
+ cfg->ft_filename = pstrdup(line);
+
+ /* partitions */
+ } else {
+ config_partition_t *partition = &(cfg->partition[nline - 2]);
+
+ p = strrchr(line, ',');
+ if (p != NULL) {
+ partition->type = get_hex(p + 1);
+
+ *p = '\0';
+ q = strrchr(line, ',');
+ if (q != NULL) {
+ partition->status = partition->type;
+ if (p[1] == 'a' && p[2] == '\0')
+ partition->status = 0x80;
+
+ partition->type = get_hex(q + 1);
+ *q = '\0';
+ }
+ }
+
+ if (*line == '\0') {
+ if (nline < 3) {
+ info("at least one partition is required");
+ goto error;
+ }
+ break;
+ }
+
+ partition->filename = pstrdup(line);
+ }
+ }
+
+ close(fd);
+ *pcfg = cfg;
+
+ return 0;
+
+error:
+ close(fd);
+ config_delete(cfg);
+
+ return -1;
+}
+
+static inline int first_track_init(BlockDriverState *bs, const char *filename, int flags)
+{
+ BdrvMultipartState *bs_mp = bs->opaque;
+
+ memset(bs_mp->first_track.buf, 0, sizeof(bs_mp->first_track));
+ bs->total_sectors = sizeof(bs_mp->first_track) >> BLOCK_SHIFT;
+
+ if (filename != NULL) {
+ int ret;
+ int64_t len;
+ BlockDriverState *bs_ft;
+
+ bs_ft = bdrv_new("");
+ if (!bs_ft)
+ return -ENOMEM;
+
+ ret = bdrv_open(bs_ft, filename, flags);
+ if (ret < 0) {
+ info("unable to open mbr image %s", filename);
+ return ret;
+ }
+
+ len = bdrv_getlength(bs_ft);
+ if (len > sizeof(bs_mp->first_track))
+ len = sizeof(bs_mp->first_track);
+
+ ret = bdrv_pread(bs_ft, 0, bs_mp->first_track.buf, len);
+ if (ret < 0) {
+ info("unable to read mbr image %s", filename);
+ bdrv_delete(bs_ft);
+ return ret;
+ }
+
+ if (bs_ft->read_only)
+ bdrv_delete(bs_ft);
+ else
+ bs_mp->bs_first_track = bs_ft;
+ }
+
+ return 0;
+}
+
+static void lbatochs(uint32_t lba, chs_t *pchs)
+{
+ uint32_t c, h, s;
+
+ c = lba / (HEADS * TRACK_SECTORS);
+ h = (lba % (HEADS * TRACK_SECTORS)) / TRACK_SECTORS;
+ s = (lba % (HEADS * TRACK_SECTORS)) % TRACK_SECTORS + 1;
+
+ pchs->head = h > 254 ? 254 : (uint8_t) h;
+ pchs->sector = s > 63 ? 63 : (uint8_t) s;
+
+ if (c > 1023)
+ c = 1023;
+
+ pchs->sector |= (uint8_t) ((c >> 2) & 0xc0);
+ pchs->cylinder = (uint8_t) (c & 0xff);
+}
+
+static inline int partitions_init(BlockDriverState *bs, int flags, config_t *cfg)
+{
+ int i, ret = 0;
+ mbr_t *mbr;
+ BdrvMultipartState *bs_mp = bs->opaque;
+ BlockDriverState *bs_part;
+
+ mbr = &(bs_mp->first_track.mbr);
+ if (cfg->dynamic) {
+ mbr->magic[0] = 0x55;
+ mbr->magic[1] = 0xAA;
+ }
+
+ for (i = 0; i < 4 && cfg->partition[i].filename != NULL; i++) {
+ config_partition_t *cpart = &(cfg->partition[i]);
+
+ bs_part = bdrv_new("");
+ if (!bs_part) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ ret = bdrv_open(bs_part, cpart->filename, flags);
+ if (ret < 0) {
+ info("unable to open partition image %s", cpart->filename);
+ qemu_free(bs_part);
+ break;
+ }
+
+ if (bs_part->total_sectors == 0) {
+ info("zero length partition %s", cpart->filename);
+ bdrv_delete(bs_part);
+ break;
+ }
+
+ bs_mp->bs_partition[i] = bs_part;
+
+ if (cfg->dynamic) {
+ partition_t *partition = &(mbr->partition[i]);
+
+ partition->lba_start = cpu_to_le32(bs->total_sectors);
+ lbatochs(partition->lba_start, &(partition->chs_start));
+
+ partition->lba_length = cpu_to_le32(bs_part->total_sectors);
+ lbatochs(bs->total_sectors + bs_part->total_sectors - 1,
+ &(partition->chs_end));
+
+ if (cpart->status >= 0)
+ partition->status = cpart->status;
+
+ if (cpart->type >= 0)
+ partition->type = cpart->type;
+ }
+
+ bs_mp->bs_partition[i] = bs_part;
+ bs->total_sectors += bs_part->total_sectors;
+ }
+
+ if (ret < 0) {
+ for (i = 0; i < 4 && bs_mp->bs_partition[i] != NULL; i++)
+ bdrv_delete(bs_mp->bs_partition[i]);
+ }
+
+ return ret;
+}
+
+static int ft_image_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ int ret = 0;
+ int64_t write_offset, write_length;
+ int64_t bdrv_length;
+
+ write_offset = sector_num << BLOCK_SHIFT;
+ bdrv_length = bdrv_getlength(bs);
+
+ if (write_offset < bdrv_length) {
+ write_length = nb_sectors << BLOCK_SHIFT;
+ if ((write_offset + write_length) > bdrv_length)
+ write_length = bdrv_length - write_offset;
+
+ ret = bdrv_pwrite(bs, write_offset, buf, write_length);
+ }
+
+ return ret;
+}
+
+
+static int multipart_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ int i, ret;
+ config_t *cfg;
+ BdrvMultipartState *bs_mp = bs->opaque;
+
+ ret = config_parse(filename, &cfg);
+ if (ret < 0)
+ return ret;
+
+ ret = first_track_init(bs, cfg->ft_filename, flags);
+ if (ret < 0) {
+ config_delete(cfg);
+ return ret;
+ }
+
+ ret = partitions_init(bs, flags, cfg);
+ if (ret >= 0 && cfg->dynamic && bs_mp->bs_first_track) {
+ ret = ft_image_write(bs_mp->bs_first_track, 0, bs_mp->first_track.buf, 1);
+ if (ret < 0) {
+ info("writing MBR to image %s failed", cfg->ft_filename);
+
+ /* clean up the partitions */
+ for (i = 0; i < 4 && bs_mp->bs_partition[i] != NULL; i++)
+ bdrv_delete(bs_mp->bs_partition[i]);
+ }
+ }
+
+ config_delete(cfg);
+
+ if (ret < 0 && bs_mp->bs_first_track != NULL)
+ bdrv_delete(bs_mp->bs_first_track);
+
+ return ret;
+}
+
+static int multipart_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ int i, ret = 0;
+ int read_sectors;
+ int64_t ft_nsectors;
+ BdrvMultipartState *bs_mp = bs->opaque;
+ BlockDriverState *bs_part;
+
+ ft_nsectors = sizeof(bs_mp->first_track) >> BLOCK_SHIFT;
+ if (sector_num < ft_nsectors) {
+ read_sectors = nb_sectors;
+ if ((sector_num + read_sectors) > ft_nsectors)
+ read_sectors = ft_nsectors - sector_num;
+
+ memcpy(buf, bs_mp->first_track.buf + (sector_num << BLOCK_SHIFT),
+ read_sectors << BLOCK_SHIFT);
+
+ sector_num += read_sectors;
+ nb_sectors -= read_sectors;
+ buf += read_sectors << BLOCK_SHIFT;
+ }
+
+ sector_num -= ft_nsectors;
+ for (i = 0; i < 4 && bs_mp->bs_partition[i] != NULL && nb_sectors > 0; i++) {
+ bs_part = bs_mp->bs_partition[i];
+
+ if (sector_num < bs_part->total_sectors) {
+ read_sectors = nb_sectors;
+ if ((sector_num + read_sectors) > bs_part->total_sectors)
+ read_sectors = bs_part->total_sectors - sector_num;
+
+ ret = bdrv_read(bs_part, sector_num, buf, read_sectors);
+ if (ret < 0)
+ break;
+
+ sector_num += read_sectors;
+ nb_sectors -= read_sectors;
+ buf += read_sectors << BLOCK_SHIFT;
+ }
+
+ sector_num -= bs_part->total_sectors;
+ }
+
+ return ret;
+}
+
+static int multipart_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ int i, ret = 0;
+ int write_sectors;
+ int64_t ft_nsectors;
+ BdrvMultipartState *bs_mp = bs->opaque;
+ BlockDriverState *bs_part;
+
+ ft_nsectors = sizeof(bs_mp->first_track) >> BLOCK_SHIFT;
+ if (sector_num < ft_nsectors) {
+ if (bs_mp->bs_first_track != NULL) {
+ ret = ft_image_write(bs_mp->bs_first_track, sector_num,
+ buf, nb_sectors);
+ if (ret < 0)
+ return ret;
+ }
+
+ write_sectors = nb_sectors;
+ if ((sector_num + write_sectors) > ft_nsectors)
+ write_sectors = ft_nsectors - sector_num;
+
+ memcpy(bs_mp->first_track.buf + (sector_num << BLOCK_SHIFT), buf,
+ write_sectors << BLOCK_SHIFT);
+
+ sector_num += write_sectors;
+ nb_sectors -= write_sectors;
+ buf += write_sectors << BLOCK_SHIFT;
+ }
+
+ sector_num -= ft_nsectors;
+ for (i = 0; i < 4 && bs_mp->bs_partition[i] != NULL && nb_sectors > 0; i++) {
+ bs_part = bs_mp->bs_partition[i];
+
+ if (sector_num < bs_part->total_sectors) {
+ write_sectors = nb_sectors;
+ if ((sector_num + write_sectors) > bs_part->total_sectors)
+ write_sectors = bs_part->total_sectors - sector_num;
+
+ ret = bdrv_write(bs_part, sector_num, buf, write_sectors);
+ if (ret < 0)
+ break;
+
+ sector_num += write_sectors;
+ nb_sectors -= write_sectors;
+ buf += write_sectors << BLOCK_SHIFT;
+ }
+
+ sector_num -= bs_part->total_sectors;
+ }
+
+ return ret;
+}
+
+static void multipart_close(BlockDriverState *bs)
+{
+ int i;
+ BdrvMultipartState *bs_mp = bs->opaque;
+
+ if (bs_mp->bs_first_track)
+ bdrv_delete(bs_mp->bs_first_track);
+
+ for (i = 0; i < 4 && bs_mp->bs_partition[i] != NULL; i++)
+ bdrv_delete(bs_mp->bs_partition[i]);
+}
+
+BlockDriver bdrv_multipart = {
+ .format_name = FORMAT_NAME,
+ .instance_size = sizeof(BdrvMultipartState),
+ .bdrv_open = multipart_open,
+ .bdrv_read = multipart_read,
+ .bdrv_write = multipart_write,
+ .bdrv_close = multipart_close,
+ .protocol_name = PROTOCOL_NAME
+};
+
diff -Nurp qemu-0.9.1/Makefile qemu-0.9.1-multipart/Makefile
--- qemu-0.9.1/Makefile 2008-01-06 20:38:41.000000000 +0100
+++ qemu-0.9.1-multipart/Makefile 2008-03-03 09:55:55.000000000 +0100
@@ -40,7 +40,7 @@ recurse-all: $(patsubst %,subdir-%, $(TA
BLOCK_OBJS=cutils.o
BLOCK_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o
BLOCK_OBJS+=block-dmg.o block-bochs.o block-vpc.o block-vvfat.o
-BLOCK_OBJS+=block-qcow2.o block-parallels.o
+BLOCK_OBJS+=block-qcow2.o block-parallels.o block-multipart.o
######################################################################
# libqemu_common.a: Target indepedent part of system emulation. The
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2008-03-03 17:24 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-03-03 17:22 [Qemu-devel] multi-partition block driver Aleksandar Kanchev
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).