From: Kevin Wolf Date: Tue, 19 May 2009 15:57:21 +0000 (+0200) Subject: qcow2: Allow different cluster sizes X-Git-Tag: 0.10.0-0sb10~1179 X-Git-Url: http://git.maemo.org/git/?p=qemu;a=commitdiff_plain;h=73c632edc4e28d0a8cae3b34abf7ae46fa33bf1e qcow2: Allow different cluster sizes Add an option to specify the cluster size of a newly created qcow2 image. Default is 4k which is the same value that was hard-coded before. Signed-off-by: Kevin Wolf Signed-off-by: Anthony Liguori --- diff --git a/block/qcow2.c b/block/qcow2.c index 803cf7f..77f433e 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -62,6 +62,9 @@ #define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */ +#define MIN_CLUSTER_BITS 9 +#define MAX_CLUSTER_BITS 16 + typedef struct QCowHeader { uint32_t magic; uint32_t version; @@ -300,8 +303,8 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags) if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) goto fail; if (header.size <= 1 || - header.cluster_bits < 9 || - header.cluster_bits > 16) + header.cluster_bits < MIN_CLUSTER_BITS || + header.cluster_bits > MAX_CLUSTER_BITS) goto fail; if (header.crypt_method > QCOW_CRYPT_AES) goto fail; @@ -1583,9 +1586,30 @@ static void create_refcount_update(QCowCreateState *s, } } +static int get_bits_from_size(size_t size) +{ + int res = 0; + + if (size == 0) { + return -1; + } + + while (size != 1) { + /* Not a power of two */ + if (size & 1) { + return -1; + } + + size >>= 1; + res++; + } + + return res; +} + static int qcow_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, - int flags) + int flags, size_t cluster_size) { int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits; @@ -1619,8 +1643,20 @@ static int qcow_create2(const char *filename, int64_t total_size, header.backing_file_size = cpu_to_be32(backing_filename_len); header_size += backing_filename_len; } - s->cluster_bits = 12; /* 4 KB clusters */ + + /* Cluster size */ + s->cluster_bits = get_bits_from_size(cluster_size); + if (s->cluster_bits < MIN_CLUSTER_BITS || + s->cluster_bits > MAX_CLUSTER_BITS) + { + fprintf(stderr, "Cluster size must be a power of two between " + "%d and %dk\n", + 1 << MIN_CLUSTER_BITS, + 1 << (MAX_CLUSTER_BITS - 10)); + return -EINVAL; + } s->cluster_size = 1 << s->cluster_bits; + header.cluster_bits = cpu_to_be32(s->cluster_bits); header_size = (header_size + 7) & ~7; if (flags & BLOCK_FLAG_ENCRYPT) { @@ -1702,6 +1738,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) const char *backing_fmt = NULL; uint64_t sectors = 0; int flags = 0; + size_t cluster_size = 4096; /* Read out options */ while (options && options->name) { @@ -1713,11 +1750,16 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) backing_fmt = options->value.s; } else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) { flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0; + } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) { + if (options->value.n) { + cluster_size = options->value.n; + } } options++; } - return qcow_create2(filename, sectors, backing_file, backing_fmt, flags); + return qcow_create2(filename, sectors, backing_file, backing_fmt, flags, + cluster_size); } static int qcow_make_empty(BlockDriverState *bs) @@ -2920,6 +2962,7 @@ static QEMUOptionParameter qcow_create_options[] = { { BLOCK_OPT_BACKING_FILE, OPT_STRING }, { BLOCK_OPT_BACKING_FMT, OPT_STRING }, { BLOCK_OPT_ENCRYPT, OPT_FLAG }, + { BLOCK_OPT_CLUSTER_SIZE, OPT_SIZE }, { NULL } }; diff --git a/block_int.h b/block_int.h index 782de6c..0820ed1 100644 --- a/block_int.h +++ b/block_int.h @@ -36,6 +36,7 @@ #define BLOCK_OPT_COMPAT6 "compat6" #define BLOCK_OPT_BACKING_FILE "backing_file" #define BLOCK_OPT_BACKING_FMT "backing_fmt" +#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" typedef struct AIOPool { void (*cancel)(BlockDriverAIOCB *acb);