QED 镜像文件格式规范

文件格式如下:

+----------+----------+----------+-----+
| cluster0 | cluster1 | cluster2 | ... |
+----------+----------+----------+-----+

第一簇从 header 开始,记录普通簇的起始位置, 使头部可扩展并保存额外镜像信息。普通簇可为数据簇、L2 或 L1 表。 L1/L2 表由一个或多个连续簇组成。

通常文件大小是簇大小整数倍。若不是,最后簇之后的额外信息可能在写入时丢失。 合法附加信息应使用头部与首个普通簇之间的空间。

全部字段为小端。

表

表将块设备逻辑偏移转换为文件簇偏移。

#define TABLE_NOFFSETS (table_size * cluster_size / sizeof(uint64_t))

Table {
    uint64_t offsets[TABLE_NOFFSETS];
}

组织如下:

                   +----------+
                   | L1 表    |
                   +----------+
              ,------'  |  '------.
         +----------+   |    +----------+
         | L2 表    |  ...   | L2 表    |
         +----------+        +----------+
     ,------'  |  '------.
+----------+   |    +----------+
|   数据   |  ...   |   数据   |
+----------+        +----------+

每表由一个或多个连续簇组成,table_size 决定大小。 例如 cluster_size=64 KB、table_size=4 时,表为 256 KB。

逻辑镜像大小不能超过 L1 可覆盖的最大簇总大小:

header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size

L1、L2、数据簇偏移必须按 header.cluster_size 对齐。以下偏移有特殊含义。

L2 表偏移

  • 0:未分配 L2 表。

数据簇偏移

  • 0:未分配数据簇。

  • 1:全零簇,未实际分配簇。

未来扩展可能保存逐偏移信息,最低 12 位为此保留,必须为零。 cluster_size 大于 2^12 时有更多未用位,也应清零。

未分配的 L2 表与数据簇

读取未分配区域时访问后备文件,无后备则返回零。 后备可小于镜像,超出后备末尾的未分配区域读取为零。

写入未分配区域会分配新数据簇,如 L2 也未分配则同时分配。 新簇由后备数据(无后备则为零)和正在写入的数据填充。

零数据簇

零簇可节省存储全零区域的空间。

读取返回零。

备注

与未分配簇的区别是,零簇阻止读取后备文件内容。

写入零簇会分配新数据簇,用零及待写数据填充。

逻辑偏移转换

转换如下:

 table_bits table_bits    cluster_bits
 <--------> <--------> <--------------->
+----------+----------+-----------------+
| L1 index | L2 index |     byte offset |
+----------+----------+-----------------+

      Structure of a logical offset

offset_mask = ~(cluster_size - 1) # mask for the image file byte offset

def logical_to_cluster_offset(l1_index, l2_index, byte_offset):
  l2_offset = l1_table[l1_index]
  l2_table = load_table(l2_offset)
  cluster_offset = l2_table[l2_index] & offset_mask
  return cluster_offset + byte_offset

一致性检查

本节提供信息性背景,说明 QED_F_NEED_CHECK 功能位。

执行可能因崩溃或断电留下不一致状态的操作前,用此位标记镜像为脏。 脏镜像打开时必须检查,因为元数据可能不一致。

检查以下不变量:

  • 每簇必须且只能被引用一次。L1/L2 多次引用同簇属于不一致,无引用则为泄漏。

  • 偏移必须位于文件大小内,并按 cluster_size 对齐。

  • 表偏移距文件末尾至少为 table_size * cluster_size 字节,保证整表可容纳。

从 l1_table_offset 开始扫描全部 L2。 检查完成且除了泄漏外没有其他错误时,可清除 QED_F_NEED_CHECK 并访问镜像。