镜像文件格式解析:raw、qcow2、vmdk、vhd 等格式的差异,以及它们对分区表的影响

镜像文件格式决定了数据在磁盘上的组织方式、元数据结构以及虚拟化平台的支持能力。不同的格式对分区表的存储、访问和修改行为有直接影响。本章将逐一解析主流镜像格式的核心差异,并重点分析它们对分区表排查工作的影响。

2.1 主流镜像格式概览

下表从元数据、稀疏特性、快照支持、平台兼容性四个维度对比了四种常见格式:

格式 元数据存储 稀疏文件支持 快照/克隆 主要平台
raw 无独立元数据 否(依赖文件系统) 不支持 所有平台
qcow2 内嵌头部 + L2 表 是(原生) 支持(内部/外部) QEMU/KVM
vmdk 描述符文件 + 扩展头 是(稀疏粒度可配) 支持(快照链) VMware
vhd/vhdx 页脚/头部 + BAT 表 是(动态扩展) 有限(差异盘) Hyper-V / Azure
排查要点: raw 格式没有元数据层,分区表直接暴露在文件偏移 0 处,fdisk -l 或 parted 可以直接读取。而 qcow2、vmdk、vhd 的元数据层会改变分区表的访问路径,必须通过 qemu-nbd 或 losetup -P 等工具映射后才能识别。

2.2 raw 格式:最直接的访问路径

raw 格式是磁盘数据的逐字节副本,没有头部、没有元数据、没有压缩。分区表位于 LBA0(偏移 0x1BE 处的主引导记录)。

  • 优势:性能最高,工具兼容性最好,dd、fdisk、parted 可直接操作。
  • 劣势:占用完整磁盘大小,不支持快照,传输效率低。
  • 分区表影响:无任何间接层,分区表损坏后恢复手段直接。
# 直接查看 raw 镜像的分区表
fdisk -l disk.raw

# 使用 dd 提取 MBR
dd if=disk.raw of=mbr.bin bs=512 count=1

2.3 qcow2 格式:元数据层带来的分区表偏移

qcow2 在镜像头部(通常 2 MB 对齐)存储了 L1/L2 地址映射表。虚拟机看到的 LBA 0 在物理文件中并不对应偏移 0,而是经过元数据翻译后的虚拟地址。

  • 元数据开销:头部 512 字节 + 扩展区域,后续 L1 表、L2 表、数据簇。
  • 稀疏特性:未写入的簇不占用物理空间,分区表所在簇通常被写入,因此物理上存在。
  • 分区表影响:直接 fdisk 读取 qcow2 文件会看到乱码或报错“无法读取分区表”。必须通过 qemu-nbd 挂载为网络块设备。
# 挂载 qcow2 镜像为 nbd 设备
modprobe nbd max_part=8
qemu-nbd --connect=/dev/nbd0 disk.qcow2

# 查看分区表
fdisk -l /dev/nbd0

# 完成后断开
qemu-nbd --disconnect /dev/nbd0
常见陷阱: 如果 qcow2 镜像使用了 preallocation=metadata 或 cluster_size=512 等非默认参数,分区表的物理偏移可能发生变化。排查时务必使用 qemu-img info 查看镜像参数。

2.4 vmdk 格式:描述符文件与扩展头

vmdk 分为两种类型:单文件(monolithic flat) 和 多文件(split / stream-optimized)。单文件 vmdk 的头部包含 512 字节的描述符区域,之后才是数据区。

  • 描述符区域:包含磁盘几何参数、CID、父镜像引用等元数据。
  • 数据区起始偏移:对于 monolithic flat 类型,数据从偏移 0 开始(头部在扩展描述符中);对于 monolithic sparse 类型,数据区从偏移 512 或 1 MB 开始。
  • 分区表影响:分区表可能不在文件偏移 0 处,需要根据描述符中的 ddb.geometry.sectors 和 ddb.geometry.heads 计算真实偏移。
# 查看 vmdk 描述符信息
head -20 disk.vmdk

# 使用 qemu-nbd 挂载(兼容 vmdk)
qemu-nbd --connect=/dev/nbd0 disk.vmdk
fdisk -l /dev/nbd0

2.5 vhd/vhdx 格式:页脚与 BAT 表

vhd 格式在文件末尾存储 512 字节的页脚(footer),包含磁盘大小、CHS 参数、UUID 等。vhdx 是 vhd 的改进版,使用 64 KB 对齐的头部和日志区域。

  • vhd 页脚:位于文件末尾 512 字节,数据区从偏移 0 开始,但动态扩展时 BAT(Block Allocation Table)位于文件头部。
  • vhdx 结构:头部 1 MB 区域包含元数据、日志、BAT 表,数据区从 1 MB 对齐偏移开始。
  • 分区表影响:vhd 的分区表在偏移 0 处,但动态扩展 vhd 的 BAT 表可能覆盖前 512 字节?不会——BAT 表位于头部,数据区从偏移 0 开始,分区表始终在 LBA0。
# 使用 losetup 挂载 vhd(需要内核支持)
losetup -P --show --read-only /dev/loop0 disk.vhd
fdisk -l /dev/loop0

# 使用 qemu-nbd 挂载 vhdx
qemu-nbd --connect=/dev/nbd0 disk.vhdx
fdisk -l /dev/nbd0

2.6 格式对分区表排查的影响对比

排查场景 raw qcow2 vmdk vhd/vhdx
直接 fdisk 读取 ✅ 支持 ❌ 需挂载 ❌ 需挂载 ❌ 需挂载
分区表物理偏移 固定 0 虚拟化,不固定 依赖描述符 固定 0
稀疏区域分区表丢失 不适用 可能(未写入) 可能(稀疏粒度大) 可能(动态扩展)
快照链中分区表 不适用 需遍历快照 需遍历快照 差异盘需合并
推荐排查工具 fdisk / dd qemu-nbd qemu-nbd / vmware-mount losetup / qemu-nbd
核心结论: 分区表排查的第一步是确认镜像格式。raw 格式可以直接操作;qcow2 和 vmdk 必须通过块设备映射工具访问;vhd/vhdx 虽然分区表在偏移 0,但动态扩展特性可能导致稀疏区域未分配,需要先转换为固定大小再排查。

2.7 格式转换对分区表的影响

使用 qemu-img convert 进行格式转换时,分区表内容会被逐字节复制,理论上不会改变。但以下情况需要警惕:

  • 稀疏转换:从 qcow2 转 raw 时,如果目标文件系统不支持稀疏,分区表所在簇会被正常写入,但其他未分配簇可能被填充为 0,不影响分区表。
  • 对齐参数:-o cluster_size=512 会改变 qcow2 的簇大小,但分区表数据不变。
  • 截断风险:转换时指定了错误的大小参数(如 -o size 小于原始磁盘),分区表可能被截断。
# 安全转换:保留完整数据
qemu-img convert -f qcow2 -O raw disk.qcow2 disk.raw

# 验证转换后分区表
fdisk -l disk.raw
镜像格式与分区表访问路径对比 raw 镜像 直接 fdisk 读取 分区表在偏移 0 qcow2 镜像 qemu-nbd 挂载 虚拟 LBA0 ≠ 文件偏移 0 vmdk 镜像 解析描述符 + 挂载 偏移依赖描述符 vhd/vhdx 镜像 losetup / qemu-nbd 分区表在偏移 0 ⚠ 排查关键: • raw / vhd:分区表在文件偏移 0,可直接读取。 • qcow2 / vmdk:分区表经过虚拟化映射,必须通过块设备工具访问。

2.8 实战排查流程

  1. 识别格式:使用 file 命令或 qemu-img info 确认镜像格式。
  2. 选择挂载方式:根据格式选择 qemu-nbd、losetup 或直接读取。
  3. 读取分区表:使用 fdisk -l、parted -l 或 gdisk -l 查看。
  4. 验证完整性:检查分区表签名(MBR 的 0x55AA,GPT 的 EFI PART)。
  5. 备份分区表:使用 dd 或 sgdisk --backup 备份。
# 通用排查脚本片段
IMG=$1
FORMAT=$(qemu-img info --output=json "$IMG" | jq -r '.format')
echo "镜像格式: $FORMAT"

case $FORMAT in
  raw)
    fdisk -l "$IMG"
    ;;
  qcow2|vmdk|vhdx)
    modprobe nbd max_part=8
    qemu-nbd --connect=/dev/nbd0 "$IMG"
    fdisk -l /dev/nbd0
    qemu-nbd --disconnect /dev/nbd0
    ;;
  vhd)
    losetup -P --show --read-only /dev/loop0 "$IMG"
    fdisk -l /dev/loop0
    losetup -d /dev/loop0
    ;;
  *)
    echo "不支持的格式"
    ;;
esac