qcow2: Support for feature table header extension

Instead of printing an ugly bitmask, qemu can now print a more helpful
string even for yet unknown features.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Kevin Wolf 2012-04-12 15:20:27 +02:00
parent 6377af48b0
commit cfcc4c62ff
2 changed files with 69 additions and 9 deletions

View File

@ -54,6 +54,7 @@ typedef struct {
} QCowExtension; } QCowExtension;
#define QCOW2_EXT_MAGIC_END 0 #define QCOW2_EXT_MAGIC_END 0
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA #define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
{ {
@ -76,7 +77,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
* return 0 upon success, non-0 otherwise * return 0 upon success, non-0 otherwise
*/ */
static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
uint64_t end_offset) uint64_t end_offset, void **p_feature_table)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
QCowExtension ext; QCowExtension ext;
@ -134,6 +135,18 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
#endif #endif
break; break;
case QCOW2_EXT_MAGIC_FEATURE_TABLE:
if (p_feature_table != NULL) {
void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature));
ret = bdrv_pread(bs->file, offset , feature_table, ext.len);
if (ret < 0) {
return ret;
}
*p_feature_table = feature_table;
}
break;
default: default:
/* unknown magic - save it in case we need to rewrite the header */ /* unknown magic - save it in case we need to rewrite the header */
{ {
@ -182,6 +195,24 @@ static void report_unsupported(BlockDriverState *bs, const char *fmt, ...)
bs->device_name, "qcow2", msg); bs->device_name, "qcow2", msg);
} }
static void report_unsupported_feature(BlockDriverState *bs,
Qcow2Feature *table, uint64_t mask)
{
while (table && table->name[0] != '\0') {
if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
if (mask & (1 << table->bit)) {
report_unsupported(bs, "%.46s",table->name);
mask &= ~(1 << table->bit);
}
}
table++;
}
if (mask) {
report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask);
}
}
static int qcow2_open(BlockDriverState *bs, int flags) static int qcow2_open(BlockDriverState *bs, int flags)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
@ -245,14 +276,23 @@ static int qcow2_open(BlockDriverState *bs, int flags)
} }
} }
if (header.backing_file_offset) {
ext_end = header.backing_file_offset;
} else {
ext_end = 1 << header.cluster_bits;
}
/* Handle feature bits */ /* Handle feature bits */
s->incompatible_features = header.incompatible_features; s->incompatible_features = header.incompatible_features;
s->compatible_features = header.compatible_features; s->compatible_features = header.compatible_features;
s->autoclear_features = header.autoclear_features; s->autoclear_features = header.autoclear_features;
if (s->incompatible_features != 0) { if (s->incompatible_features != 0) {
report_unsupported(bs, "incompatible features mask %" PRIx64, void *feature_table = NULL;
header.incompatible_features); qcow2_read_extensions(bs, header.header_length, ext_end,
&feature_table);
report_unsupported_feature(bs, feature_table,
s->incompatible_features);
ret = -ENOTSUP; ret = -ENOTSUP;
goto fail; goto fail;
} }
@ -343,12 +383,7 @@ static int qcow2_open(BlockDriverState *bs, int flags)
QLIST_INIT(&s->cluster_allocs); QLIST_INIT(&s->cluster_allocs);
/* read qcow2 extensions */ /* read qcow2 extensions */
if (header.backing_file_offset) { if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) {
ext_end = header.backing_file_offset;
} else {
ext_end = s->cluster_size;
}
if (qcow2_read_extensions(bs, header.header_length, ext_end)) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -912,6 +947,19 @@ int qcow2_update_header(BlockDriverState *bs)
buflen -= ret; buflen -= ret;
} }
/* Feature table */
Qcow2Feature features[] = {
/* no feature defined yet */
};
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
features, sizeof(features), buflen);
if (ret < 0) {
goto fail;
}
buf += ret;
buflen -= ret;
/* Keep unknown header extensions */ /* Keep unknown header extensions */
QLIST_FOREACH(uext, &s->unknown_header_ext, next) { QLIST_FOREACH(uext, &s->unknown_header_ext, next) {
ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen); ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen);

View File

@ -104,6 +104,18 @@ typedef struct Qcow2UnknownHeaderExtension {
uint8_t data[]; uint8_t data[];
} Qcow2UnknownHeaderExtension; } Qcow2UnknownHeaderExtension;
enum {
QCOW2_FEAT_TYPE_INCOMPATIBLE = 0,
QCOW2_FEAT_TYPE_COMPATIBLE = 1,
QCOW2_FEAT_TYPE_AUTOCLEAR = 2,
};
typedef struct Qcow2Feature {
uint8_t type;
uint8_t bit;
char name[46];
} QEMU_PACKED Qcow2Feature;
typedef struct BDRVQcowState { typedef struct BDRVQcowState {
int cluster_bits; int cluster_bits;
int cluster_size; int cluster_size;