//#define DEBUG_ALLOC
//#define DEBUG_ALLOC2
+//#define DEBUG_EXT
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
#define QCOW_VERSION 2
uint64_t snapshots_offset;
} QCowHeader;
+
+typedef struct {
+ uint32_t magic;
+ uint32_t len;
+} QCowExtension;
+#define QCOW_EXT_MAGIC_END 0
+#define QCOW_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
+
+
typedef struct __attribute__((packed)) QCowSnapshotHeader {
/* header is 8 byte aligned */
uint64_t l1_table_offset;
uint32_t crypt_method_header;
AES_KEY aes_encrypt_key;
AES_KEY aes_decrypt_key;
-
- int64_t highest_alloc; /* highest cluester allocated (in clusters) */
- int64_t nc_free; /* num of free clusters below highest_alloc */
-
uint64_t snapshots_offset;
int snapshots_size;
int nb_snapshots;
#ifdef DEBUG_ALLOC
static void check_refcounts(BlockDriverState *bs);
#endif
-static void scan_refcount(BlockDriverState *bs, int64_t *high, int64_t *free);
-
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
{
return 0;
}
+
+/*
+ * read qcow2 extension and fill bs
+ * start reading from start_offset
+ * finish reading upon magic of value 0 or when end_offset reached
+ * unknown magic is skipped (future extension this version knows nothing about)
+ * return 0 upon success, non-0 otherwise
+ */
+static int qcow_read_extensions(BlockDriverState *bs, uint64_t start_offset,
+ uint64_t end_offset)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowExtension ext;
+ uint64_t offset;
+
+#ifdef DEBUG_EXT
+ printf("qcow_read_extensions: start=%ld end=%ld\n", start_offset, end_offset);
+#endif
+ offset = start_offset;
+ while (offset < end_offset) {
+
+#ifdef DEBUG_EXT
+ /* Sanity check */
+ if (offset > s->cluster_size)
+ printf("qcow_handle_extension: suspicious offset %lu\n", offset);
+
+ printf("attemting to read extended header in offset %lu\n", offset);
+#endif
+
+ if (bdrv_pread(s->hd, offset, &ext, sizeof(ext)) != sizeof(ext)) {
+ fprintf(stderr, "qcow_handle_extension: ERROR: pread fail from offset %lu\n",
+ offset);
+ return 1;
+ }
+ be32_to_cpus(&ext.magic);
+ be32_to_cpus(&ext.len);
+ offset += sizeof(ext);
+#ifdef DEBUG_EXT
+ printf("ext.magic = 0x%x\n", ext.magic);
+#endif
+ switch (ext.magic) {
+ case QCOW_EXT_MAGIC_END:
+ return 0;
+
+ case QCOW_EXT_MAGIC_BACKING_FORMAT:
+ if (ext.len >= sizeof(bs->backing_format)) {
+ fprintf(stderr, "ERROR: ext_backing_format: len=%u too large"
+ " (>=%lu)\n",
+ ext.len, sizeof(bs->backing_format));
+ return 2;
+ }
+ if (bdrv_pread(s->hd, offset , bs->backing_format,
+ ext.len) != ext.len)
+ return 3;
+ bs->backing_format[ext.len] = '\0';
+#ifdef DEBUG_EXT
+ printf("Qcow2: Got format extension %s\n", bs->backing_format);
+#endif
+ offset += ((ext.len + 7) & ~7);
+ break;
+
+ default:
+ /* unknown magic -- just skip it */
+ offset += ((ext.len + 7) & ~7);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVQcowState *s = bs->opaque;
int len, i, shift, ret;
QCowHeader header;
+ uint64_t ext_end;
/* Performance is terrible right now with cache=writethrough due mainly
* to reference count updates. If the user does not explicitly specify
if (refcount_init(bs) < 0)
goto fail;
- scan_refcount(bs, &s->highest_alloc, &s->nc_free);
+ /* read qcow2 extensions */
+ if (header.backing_file_offset)
+ ext_end = header.backing_file_offset;
+ else
+ ext_end = s->cluster_size;
+ if (qcow_read_extensions(bs, sizeof(header), ext_end))
+ goto fail;
/* read the backing file name */
if (header.backing_file_offset != 0) {
}
}
-static int qcow_create(const char *filename, int64_t total_size,
- const char *backing_file, int flags)
+static int qcow_create2(const char *filename, int64_t total_size,
+ const char *backing_file, const char *backing_format,
+ int flags)
{
+
int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits;
+ int backing_format_len = 0;
QCowHeader header;
uint64_t tmp, offset;
QCowCreateState s1, *s = &s1;
+ QCowExtension ext_bf = {0, 0};
+
memset(s, 0, sizeof(*s));
header_size = sizeof(header);
backing_filename_len = 0;
if (backing_file) {
+ if (backing_format) {
+ ext_bf.magic = QCOW_EXT_MAGIC_BACKING_FORMAT;
+ backing_format_len = strlen(backing_format);
+ ext_bf.len = (backing_format_len + 7) & ~7;
+ header_size += ((sizeof(ext_bf) + ext_bf.len + 7) & ~7);
+ }
header.backing_file_offset = cpu_to_be64(header_size);
backing_filename_len = strlen(backing_file);
header.backing_file_size = cpu_to_be32(backing_filename_len);
/* write all the data */
write(fd, &header, sizeof(header));
if (backing_file) {
+ if (backing_format_len) {
+ char zero[16];
+ int d = ext_bf.len - backing_format_len;
+
+ memset(zero, 0, sizeof(zero));
+ cpu_to_be32s(&ext_bf.magic);
+ cpu_to_be32s(&ext_bf.len);
+ write(fd, &ext_bf, sizeof(ext_bf));
+ write(fd, backing_format, backing_format_len);
+ if (d>0) {
+ write(fd, zero, d);
+ }
+ }
write(fd, backing_file, backing_filename_len);
}
lseek(fd, s->l1_table_offset, SEEK_SET);
return 0;
}
+static int qcow_create(const char *filename, int64_t total_size,
+ const char *backing_file, int flags)
+{
+ return qcow_create2(filename, total_size, backing_file, NULL, flags);
+}
+
static int qcow_make_empty(BlockDriverState *bs)
{
#if 0
bdi->cluster_size = s->cluster_size;
bdi->vm_state_offset = (int64_t)s->l1_vm_state_index <<
(s->cluster_bits + s->l2_bits);
- bdi->highest_alloc = s->highest_alloc << s->cluster_bits;
- bdi->num_free_bytes = s->nc_free << s->cluster_bits;
return 0;
}
return 0;
}
-static void scan_refcount(BlockDriverState *bs, int64_t *high, int64_t *free)
-{
- BDRVQcowState *s = bs->opaque;
- int64_t refcnt_index, cluster_index, cluster_end, h = 0, f = 0;
- int64_t tail = 0; /* do not count last consecutive free entries */
-
- for (refcnt_index=0; refcnt_index < s->refcount_table_size; refcnt_index++){
- if (s->refcount_table[refcnt_index] == 0) {
- f += 1 << (s->cluster_bits - REFCOUNT_SHIFT);
- tail += 1 << (s->cluster_bits - REFCOUNT_SHIFT);
- continue;
- }
- cluster_index = refcnt_index << (s->cluster_bits - REFCOUNT_SHIFT);
- cluster_end = (refcnt_index + 1) << (s->cluster_bits - REFCOUNT_SHIFT);
- for ( ; cluster_index < cluster_end; cluster_index++) {
- if (get_refcount(bs, cluster_index) == 0) {
- f++;
- tail++;
- }
- else {
- h = cluster_index;
- tail = 0;
- }
- }
- }
-
- f -= tail;
- if (free)
- *free = f;
- if (high)
- *high = (h+1);
-}
-
static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
{
BDRVQcowState *s = bs->opaque;
size,
(s->free_cluster_index - nb_clusters) << s->cluster_bits);
#endif
-
- if (s->highest_alloc < s->free_cluster_index) {
- s->nc_free += (s->free_cluster_index - s->highest_alloc);
- s->highest_alloc = s->free_cluster_index;
- }
-
return (s->free_cluster_index - nb_clusters) << s->cluster_bits;
}
block_index = cluster_index &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
refcount = be16_to_cpu(s->refcount_block_cache[block_index]);
-
- if (refcount == 1 && addend == -1)
- s->nc_free += 1;
- else if (refcount == 0 && addend == 1)
- s->nc_free -= 1;
-
refcount += addend;
if (refcount < 0 || refcount > 0xffff)
return -EINVAL;
.bdrv_snapshot_delete = qcow_snapshot_delete,
.bdrv_snapshot_list = qcow_snapshot_list,
.bdrv_get_info = qcow_get_info,
+
+ .bdrv_create2 = qcow_create2,
};