Hello DLWorld 专注

qcow2镜像格式

QCOW镜像格式为big-endian。

标准头结构

第一个cluster包含qcow2镜像的头。

字节偏移 字段 描述
0 - 3 magic 字符串(“QFI\xfb”)
4 - 7 version 版本号,2或3
8 - 15 backing_file_offset 后端文件名在镜像中的偏移,0表示没有后端文件
16 - 19 backing_file_size 后端文件名的长度字节数,最大1023字节
20 - 23 cluster_bits 1 « cluster_bits是cluster大小,必须大于9-21之间
24 - 31 size 虚拟磁盘大小,单位字节
32 - 35 crypt_method 0,不加密;1,AES加密
36 - 39 l1_size 活跃L1表入口项(entries)数量
40 - 47 l1_table_offset 活跃L1表起始在镜像内的偏移,必须cluster对齐
48 - 55 refcount_table_offset refcount表起始在镜像内的偏移,必须cluster对齐
56 - 59 refcount_table_clusters refcount表占用的cluster数量
60 - 63 nb_snapshots 镜像中包含的快照数量
64 - 71 snapshots_offset 快照表起始在镜像内的偏移,必须cluster对齐
[root@test-11 ~]# hexdump -n 8 -s 24 test.qcow2 
0000018 0000 0600 0040 0000   

版本3之后增加的字段

字节偏移 字段 描述
72 - 79 incompatible_features 不兼容特性掩码,如果未知位被设置,镜像打开失败。
Bit 0: Dirty bit. 如果被设置,refcounts可能不一致,在使用镜像前需要扫描L1/L2表进行修复
Bit 1: Corrupt bit.如果被设置,数据结构体可能被破坏,镜像不能写入
Bits 2-63: 保留,设置为0
80 - 87 compatible_features 兼容特性掩码,未知位被设置,可以直接忽略。
Bit 0: Lazy refcounts bit. 如果被设置,可以使用lazy refcount更新,标记镜像dirty,延迟refcount元数据更新。
Bits 1-63: 保留,设置为0
88 - 95 autoclear_features Bitmask of auto-clear features. An implementation may only write to an image with unknown auto-clear features if it clears the respective bits from this field first.
Bit 0: Bitmaps extension bit,显示位图扩展数据的一致性,如果有位图扩展,但该位未被设置,则位图扩展数据不一致
Bits 1-63: 保留,设置为0
96 - 99 refcount_order 引用数块入口宽度(reference count block entry),不能超过6,V2镜像值为4
100 - 103 header_length 头结构体长度,V2镜像长度为72字节

紧接着镜像头之后,是一个可选的头扩展区,结构如下:

字节偏移 字段 描述
0 - 3 Header extension type 头扩展类型
0x00000000 - End of the header extension area
0xE2792ACA - Backing file format name
0x6803f857 - Feature name table
0x23852875 - Bitmaps extension
other - Unknown header extension, can be safely ignored
4 - 7 length 头扩展数据长度
8 - n data 头扩展数据
n - m pad 补齐,8字节对齐

除非特殊声明,每个镜像中最多只有一个头扩展类型。

如果镜像有后端文件,则后端文件名应该保存在头扩展区的尾部与第一个cluster尾部之间。而且这部分空间不允许存放其它数据,工具可以安全的修改和增加扩展,而不会影响兼容的特性。

特性名称表

特性名称表是一个可选的头部扩展,包含镜像使用特性的名称。特征名表项数量由头扩展数据的长度决定,每项格式如下:

偏移 字段 描述
0 feature type 特性类型
0: 不兼容特性
1: 兼容特性
2: 自动清除特性
1 bitmap Bit number within the selected feature bitmap (valid values: 0-63)
2 - 47 feature name 特征名,用0补齐。

位图扩展

位图(bitmaps)扩展是可选的头扩展,提供与虚拟磁盘相关的保存位图能力。目前只有一种位图类型:dirty tracking bitmap,追踪虚拟磁盘从某个时间点的变化。

位偏移 字段 描述
0 - 3 nb_bitmaps 镜像中包含的位图数量,Qemu目前最大支持65535
4 - 7 Reserved 保留位必须为0
8 - 15 bitmap_directory_size 位图目录大小,是所有位图头的总和
16 - 23 bitmap_directory_offset 位图目录起始在镜像内的偏移

主机cluster管理

qcow2通过维护每个主机cluster的引用计数管理主机clusters的分配。refcount为0表示空闲,1表示使用,>=2表示被使用且任何写访问必须执行COW操作。

refcounts用两级表管理,第一级是refcount table,大小可变(保存在头结构体),第二级可以包括多个clusters,但必须在镜像文件内部连续。包含指向第二级结构体的指针称作refcount blocks,大小为一个cluster。

    refcount_block_entries = (cluster_size * 8 / refcount_bits)
    refcount_block_index = (offset / cluster_size) % refcount_block_entries
    refcount_table_index = (offset / cluster_size) / refcount_block_entries
    refcount_block = load_cluster(refcount_table[refcount_table_index]);
    return refcount_block[refcount_block_index];

Refcount table项

位偏移 字段 描述
0 - 8 Reserved 保留,设置为0
9 - 63 refcount_offset refcount block起始在镜像内的偏移

Refcount block项(x = refcount_bits - 1): 0 - x: cluster的reference count

cluster映射

与refcounts一样,qcow2使用两级结构体映射guest cluster到host cluster。它们称作L1和L2表。

L1表大小可变(保存在头结构体),可以使用多个clusters,但在镜像文件内必须连续。L2表大小固定为一个cluster。

给定一个虚拟磁盘内的偏移(offset),计算镜像文件的偏移:

    l2_entries = (cluster_size / sizeof(uint64_t))
    l2_index = (offset / cluster_size) % l2_entries
    l1_index = (offset / cluster_size) / l2_entries
    l2_table = load_cluster(l1_table[l1_index]);
    cluster_offset = l2_table[l2_index];
    return cluster_offset + (offset % cluster_size)

L1 表项

位偏移 字段 描述
0-8 Reserved 保留,设置为0
9 - 55 l2_offset L2表起始在镜像内的偏移,如果为0,L2表和它描述的clusters都没有分配
56 - 62 Reserved 保留,设置为0
63 l2_refcount 0表示L2表没有使用或需要COW,
1表示refcount为1
[root@master qemu-2.6.0]# qemu-img info ~/test.qcow2 --output json
{
    "virtual-size": 26843545600, 
    "filename": "/root/test.qcow2", 
    "cluster-size": 65536, 
    "format": "qcow2", 
    "actual-size": 287100928, 
    "format-specific": {
        "type": "qcow2", 
        "data": {
            "compat": "1.1", 
            "lazy-refcounts": false
        }
    }, 
    "dirty-flag": false
}
[root@master qemu-2.6.0]# hexdump -n 4 -s 36 ~/test.qcow2 
0000024 0000 3200     

l1_size = 0x32 == 50 l1_size = virtual-size/cluster-size/l2-size virtual-size = 26843545600 cluster-size = 65536 l2-size = 65536/8 = 8192

[root@master qemu-2.6.0]# hexdump -n 8 -s 40 ~/t1.qcow2 
0000028 0000 0000 0300 0000                    

l1_offset 0x30000

[root@master qemu-2.6.0]# hexdump -n 8 -s 0x30000 ~/test.qcow2 
0030000 0080 0000 0400 0000         

l2_offset 0x40000

** l2_entry **

[root@master qemu-2.6.0]# hexdump -n 8 -s 0x40000 ~/test.qcow2 
0040000 c040 0000 0500 0000                    

cluster_offset 0x40c0000000050000

L2 表项

位偏移 字段 描述
0 - 61 cluster_desc cluster描述符
62 cluster_type 0表示标准clusters,1表示压缩clusters
63 cluster_refcount 0表示cluster没有使用或需要COW,1表示refcount为1
  1. 标准cluster描述符:
位偏移 字段 描述
0 cluster_zero 设置为1,cluster读全0。host cluster偏移用于描述预分配,但不用于从这个cluster读数据。V2总是0
1 - 8 Reserved 保留,设置0
9 - 55 cluster_offset host cluster偏移,0表示cluster未分配
56 - 61 Reserved 保留,设置0
  1. 压缩clusters描述符(x = 62 - (cluster_bits - 8)): 0 - x | host cluster偏移, x+1 - 61 | 镜像压缩大小,单位块(512字节)

如果cluster未分配,读请求从后端文件读数据。如果没有后端文件,或后端文件比镜像小,读到的就是全零。

快照

qcow2支持内部快照,操作的基本原则是切换活跃L1表,这样guest就看到不同的host clusters。 创建快照时,需要拷贝L1表和指向的L2表,从该L1表访问的clusters计数增加,这样写操作就会触发COW,而且对其它快照不可见。 加载快照时,新的活跃L1表所有项的bit63和它的L2表需要重建。 所有快照的目录存在快照表中,是镜像内一段连续的区域。快照表项大小可变,由ID长度、名称和额外数据决定。

偏移 字段 描述
0 - 7 l1_offset 快照的L1表起始在镜像内偏移,cluster对齐
8 - 11 l1_size 快照的L1表项数量
12 - 13 id_size 快照唯一ID字符串长度
14 - 15 name_size 快照名字长度
16 - 19 seconds 快照创建时间,据epoch的秒数
20 - 23 nanoseconds 快照创建时间的纳米部分
24 - 31   Time that the guest was running until the snapshot was taken in nanoseconds
32 - 35 vm_size 虚拟机状态大小
36 - 39 extra_size 额外数据在表项中的大小,用于格式未来扩展
variable extra_data Extra data for future extensions. Unknown fields must be ignored. Currently defined are (offset relative to napshot table entry):
Byte 40 - 47: Size of the VM state in bytes. 0 if no VM state is saved. If this field is present, the 32-bit value in bytes 32-35 is ignored.
Byte 48 - 55: Virtual disk size of the snapshot in bytes Version 3 images must include extra data at least up to byte 55.
variable ID 快照ID
variable name 快照名称
variable pad 8字节补齐

基本操作

注意事项

  1. 镜像格式不变,转换后大小也会变化

[root@31 ~]# qemu-img info --output json /opt/glance/images/19c4f335-f837-484e-b599-fc7cedce48e5 
{
    "virtual-size" |  21474836480,
    "filename" |  "/opt/glance/images/19c4f335-f837-484e-b599-fc7cedce48e5",
    "cluster-size" |  65536,
    "format" |  "qcow2",
    "actual-size" |  8378843136,
    "format-specific" |  {
        "type" |  "qcow2",
        "data" |  {
            "compat" |  "1.1",
            "lazy-refcounts" |  false,
            "refcount-bits" |  16,
            "corrupt" |  false
        }
    },
    "dirty-flag" |  false
}

[root@31 ~]# qemu-img convert -O qcow2 /opt/glance/images/19c4f335-f837-484e-b599-fc7cedce48e5 t1 

[root@31 ~]# qemu-img info --output json t1
{
    "virtual-size" |  21474836480,
    "filename" |  "t1",
    "cluster-size" |  65536,
    "format" |  "qcow2",
    "actual-size" |  8590069760,
    "format-specific" |  {
        "type" |  "qcow2",
        "data" |  {
            "compat" |  "1.1",
            "lazy-refcounts" |  false,
            "refcount-bits" |  16,
            "corrupt" |  false
        }
    },
    "dirty-flag" |  false
}