OVF 与 OVA 区别与转换

在 ESXi 上导出虚拟机的时候,vSphere Web 端只能导出 ovf 格式的虚拟机,无法导出 OVA 格式的,使用 vSphere client 能导出这两种格式。看来一哈官方文档和标准手册,才明白。其实 OVA 是 ovf 的打包文件,导入 ova 格式的时候会自动解包出虚拟机的元数据信息。

OVF

The OVF descriptor contains the metadata about the OVF package. This is an extensible XML document for encoding information, such as product details, virtual hardware requirements, and licensing.

根据 Open Virtualization Format 的标准手册 Open Virtualization Format Specification 的描述,OVF 包含以下文件。确切地来讲 OVF 不是单个文件,而是一个未打包成一个文件的包? 虽然这样讲不太严格 😂。打包成一个文件就时 OVA 而已。

  • one OVF descriptor with extension .ovf

描述符用于指定服务对虚拟硬件的要求,并且还包括其他信息,例如虚拟磁盘的说明、服务本身、来宾操作系统、许可协议 (EULA)、在设备中启动和停止 VM 的说明以及服务安装说明。描述符文件的扩展名为 .ovf 。

  • zero or one OVF manifest with extension .mf

清单文件是软件包中每个文件的 SHA-1 摘要,可以用来检测任何损坏,以验证软件包的内容。清单文件的扩展名为 .mf 。

  • zero or one OVF certificate with extension .cert

签名是用软件包所含 X.509 证书中的公钥进行签名的清单文件摘要,用于对软件包作者进行验证。签名文件的扩展名为 .cert 。

  • zero or more disk image files

OVF 不指定磁盘映像格式。OVF 包中包含组成虚拟磁盘的文件(格式由导出虚拟磁盘所用的虚拟化产品定义)。XenServer 生成的 OVF 包具有动态 VHD 格式的磁盘映像;VMware 产品和 Virtual Box 生成的 OVF 包具有流技术优化 VMDK 格式的虚拟磁盘。

  • zero or more additional resource files, such as ISO images

.mf

An OVF package may have a manifest file containing the SHA digests of individual files in the package.OVF packages authored according to this version of the specification shall use SHA256 digests. The manifest file shall have an extension .mf and the same base name as the .ovf file and be a sibling of the .ovf file. If the manifest file is present, a consumer of the OVF package should verify the digests in the manifest file in the OVF package by computing the actual SHA digests and comparing them with the digests listed in the manifest file. The manifest file shall contain SHA digests for all distinct files referenced in the References element of the OVF descriptor and for no other files.

翻译一哈

一个 OVF 包可能会有一个

file usage need
one OVF descriptor with extension .ovf 虚拟机配置信息 必须
zero or one OVF manifest with extension .mf SHA256 非必须
zero or one OVF certificate with extension .cert 验证证书 非必须
zero or more disk image files 虚拟机磁盘 非必须
zero or more additional resource files, such as ISO images 其他资源 非必须

下面看一个 ovf 文件的示例 ,可以看出 ovf 是 xml 格式的,描述了虚拟机的配置信息、元数据信息

<?xml version='1.0' encoding='UTF-8'?>
<Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData">
  <References>
    <File ovf:id="file1" ovf:href="Alpine-240-1.vmdk"/>
  </References>
  <DiskSection>
    <Info>List of the virtual disks</Info>
    <Disk ovf:capacityAllocationUnits="byte" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:diskId="vmdisk1" ovf:capacity="2147483648" ovf:fileRef="file1"/>
  </DiskSection>
  <NetworkSection>
    <Info>The list of logical networks</Info>
    <Network ovf:name="VM Network">
      <Description>The VM Network network</Description>
    </Network>
  </NetworkSection>
  <VirtualSystem ovf:id="Alpine-240">
    <Info>A Virtual system</Info>
    <Name>Alpine-240</Name>
    <OperatingSystemSection ovf:id="101" vmw:osType="otherLinux64Guest">
      <Info>The operating system installed</Info>
      <Description>其他 Linux (64 位)</Description>
    </OperatingSystemSection>
    <VirtualHardwareSection>
      <Info>Virtual hardware requirements</Info>
      <System>
        <vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
        <vssd:InstanceID>0</vssd:InstanceID>
        <vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
      </System>
      <Item>
        <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
        <rasd:Description>Number of Virtual CPUs</rasd:Description>
        <rasd:ElementName>2 virtual CPU(s)</rasd:ElementName>
        <rasd:InstanceID>1</rasd:InstanceID>
        <rasd:ResourceType>3</rasd:ResourceType>
        <rasd:VirtualQuantity>2</rasd:VirtualQuantity>
        <vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
      </Item>
      <Item>
        <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
        <rasd:Description>Memory Size</rasd:Description>
        <rasd:ElementName>2048MB of memory</rasd:ElementName>
        <rasd:InstanceID>2</rasd:InstanceID>
        <rasd:ResourceType>4</rasd:ResourceType>
        <rasd:VirtualQuantity>2048</rasd:VirtualQuantity>
      </Item>
      <Item>
        <rasd:Address>0</rasd:Address>
        <rasd:Description>SCSI Controller</rasd:Description>
        <rasd:ElementName>SCSI Controller 1</rasd:ElementName>
        <rasd:InstanceID>3</rasd:InstanceID>
        <rasd:ResourceSubType>lsilogic</rasd:ResourceSubType>
        <rasd:ResourceType>6</rasd:ResourceType>
        <vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="16"/>
      </Item>
      <Item>
        <rasd:AddressOnParent>0</rasd:AddressOnParent>
        <rasd:ElementName>Hard Disk 1</rasd:ElementName>
        <rasd:HostResource>ovf:/disk/vmdisk1</rasd:HostResource>
        <rasd:InstanceID>4</rasd:InstanceID>
        <rasd:Parent>3</rasd:Parent>
        <rasd:ResourceType>17</rasd:ResourceType>
      </Item>
      <Item>
        <rasd:AddressOnParent>0</rasd:AddressOnParent>
        <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
        <rasd:Connection>VM Network</rasd:Connection>
        <rasd:ElementName>Network adapter 1</rasd:ElementName>
        <rasd:InstanceID>5</rasd:InstanceID>
        <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
        <rasd:ResourceType>10</rasd:ResourceType>
        <vmw:Config ovf:required="false" vmw:key="connectable.allowGuestControl" vmw:value="true"/>
        <vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="32"/>
        <vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true"/>
      </Item>
      <Item ovf:required="false">
        <rasd:ElementName>Video card</rasd:ElementName>
        <rasd:InstanceID>6</rasd:InstanceID>
        <rasd:ResourceType>24</rasd:ResourceType>
        <vmw:Config ovf:required="false" vmw:key="videoRamSizeInKB" vmw:value="4096"/>
        <vmw:Config ovf:required="false" vmw:key="useAutoDetect" vmw:value="false"/>
        <vmw:Config ovf:required="false" vmw:key="graphicsMemorySizeInKB" vmw:value="262144"/>
        <vmw:Config ovf:required="false" vmw:key="numDisplays" vmw:value="1"/>
        <vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="false"/>
        <vmw:Config ovf:required="false" vmw:key="use3dRenderer" vmw:value="automatic"/>
      </Item>
      <vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="false"/>
      <vmw:Config ovf:required="false" vmw:key="nestedHVEnabled" vmw:value="false"/>
      <vmw:Config ovf:required="false" vmw:key="virtualSMCPresent" vmw:value="false"/>
      <vmw:Config ovf:required="false" vmw:key="cpuHotRemoveEnabled" vmw:value="false"/>
      <vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="false"/>
      <vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="bios"/>
      <vmw:Config ovf:required="false" vmw:key="virtualICH7MPresent" vmw:value="false"/>
      <vmw:ExtraConfig ovf:required="false" vmw:key="nvram" vmw:value="Alpine-240.nvram"/>
      <vmw:ExtraConfig ovf:required="false" vmw:key="virtualHW.productCompatibility" vmw:value="hosted"/>
    </VirtualHardwareSection>
  </VirtualSystem>
</Envelope>

下面是 mf 文件信息

SHA256(Alpine-240.ovf)= 3d5b06e6741da7919e33775fb2c1b3e77968b4d7b020f4055a8a401f1127be29
SHA256(Alpine-240-1.vmdk)= b41596be4a846877cf82c4fd221763ba1cb4384f178454dc064f60f2ccdfd50e

在导入 ovf 格式的虚拟机文件时,会解析这个 ovf 文件,通过这个文件里描述的设备信息自动创建新的虚拟机。

OVA

根据官方的描述

An OVF package can be stored as either a single compressed file (.ova) or a set of files

An OVF package may be stored as a compressed OVF package or as a set of files in a directory structure. A compressed OVF package is stored as single file. The file extension is .ova (open virtual appliance or application).

In addition, the entries shall be in one of the following orders inside the OVF package:

    1. OVF descriptor
    1. The remaining files shall be in the same order as listed in the References section

or

    1. OVF descriptor
    1. OVF manifest
    1. OVF certificate

or

    1. OVF descriptor
    1. The intermediate files shall be in the same order as listed in the References section
    1. OVF manifest
    1. OVF certificate

需要注意的是,OVA 单个文件里打包了 OVF 所有的文件,文件是有顺序的,第一个一定要是 OVF 描述文件,即导出虚拟机时的那个 .ovf 后缀的文件

使用场景

OVF 包包含一系列未压缩的文件,对于需要访问文件中各个磁盘映像的用户而言较为方便,而 OVA 包只是一个大型文件。尽管您可以压缩此文件,但它不像一系列文件(如 OVF)那样灵活。

OVA 更适用于适合只使用一个文件的特定应用场合(例如创建用于 Web 下载的软件包),这种情况下软件包更易于处理。与 OVF 相比,导出和导入 OVA 包所需的时间更长。

转换

OVA 转 OVF 很简单,使用 tar 解包就行

比如任意解包一个 OVA 文件后会出来 ovf vmdk mf 这三个文件,而且解包出来的顺序也是和 OVA 标准定义的那样,第一个必须未 .ovf 文件。这里我讲的是解包而不是解压,是因为 OVA 和 OVF 里包含的文件,最大的就是磁盘文件,而磁盘文件在导出的时候虚拟机已经进行了压缩,你可以使用 df 命令看看磁盘占用的空间,以及导出的磁盘占用的空间,你就会发现导出的磁盘文件大小远小于系统占用的空间。

tar -xvf Ubuntu1804.ova
Ubuntu1804.ovf
Ubuntu1804-disk1.vmdk
Ubuntu1804.mf

OVF 转 OVA

按照 OVA 标准的格式,按顺序打包 OVF 包里的文件就行,注意 .ovf 文件一定要在第一个

tar -cf OP.ova Ubuntu1804.ovf Ubuntu1804-disk1.vmdk Ubuntu1804.mf

OVA = tar OVF ?

我们把 OVA 解包出来,再打包回去,两者文件是否一样呢?

看一下两者的 sha256sum 信息就可以了,结果证明是不一样的

e6b0f08dc80ef6509cd547b87d8fe9373069d6758b86f3cbb43e804a3c9b7e7d  Ubuntu1804.ova
d56d3fa5f3a57f7210726303ef32fd96d98cc7522af4f4fdbac2be7f23a5cadb  OP.ova

两者导入虚拟机后没有任何差别,都可以导进去,都能开机和使用,但新生成的 OVA 文件元数据信息是不一样的。

使用 less 命令看一下两者的文件头

原来的 OVA 文件头

Ubuntu1804.ovf^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@0000644^@0000100^@0000100^@00000017732^@13531417642^@0012505^@0^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ustar^@ ^@VMware^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@VMware^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@<?xml version="1.0" encoding="UTF-8"?>

新生成的 OVA 文件头

Ubuntu1804.ovf^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@0000777^@0001750^@0001750^@00000017732^@13531417642^@012152^@ 0^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ustar  ^@muzi^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@muzi^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@<?xml version="1.0" encoding="UTF-8"?>

第一眼看到 644 755 777 就发现,这一定是文件的权限,其中还有文件所属用户,原来的是 VMware 用户,而新生成的是我本地的用户。

刨根到底,去看一哈 tar 包的定义 Basic Tar Format tar 包就是由一个个文件顺序排列而成,每个文件由两部分组成:文件头和文件内容。就像下面这样

|文件头|文件内容|--|文件头|文件内容|--|文件头|文件内容|--|文件头|文件内容|

我们从 OVA 解包出来后,文件头的内容就会更改为我们本机的内容,而不再是原有的文件所主。

文件头的定义在


/* tar Header Block, from POSIX 1003.1-1990.  */

/* POSIX header.  */

struct posix_header
{                       /* byte offset */
  char name[100];       /*   0 */   文件名
  char mode[8];         /* 100 */   用户权限
  char uid[8];          /* 108 */   user id
  char gid[8];          /* 116 */   group id
  char size[12];        /* 124 */   文件大小
  char mtime[12];       /* 136 */   修改时间
  char chksum[8];       /* 148 */   校验值
  char typeflag;        /* 156 */   文件类型标志
  char linkname[100];   /* 157 */   符号链接指向
  char magic[6];        /* 257 */
  char version[2];      /* 263 */
  char uname[32];       /* 265 */   user name
  char gname[32];       /* 297 */   group name
  char devmajor[8];     /* 329 */   设备文件 major
  char devminor[8];     /* 337 */   设备文件 minor
  char prefix[155];     /* 345 */
                        /* 500 */
};

#define TMAGIC   "ustar"        /* ustar and a null */
#define TMAGLEN  6
#define TVERSION "00"           /* 00 and no null */
#define TVERSLEN 2

/* Values used in typeflag field.  */
#define REGTYPE  '0'            /* regular file */
#define AREGTYPE '\0'           /* regular file */
#define LNKTYPE  '1'            /* link */
#define SYMTYPE  '2'            /* reserved */
#define CHRTYPE  '3'            /* character special */
#define BLKTYPE  '4'            /* block special */
#define DIRTYPE  '5'            /* directory */
#define FIFOTYPE '6'            /* FIFO special */
#define CONTTYPE '7'            /* reserved */

#define XHDTYPE  'x'            /* Extended header referring to the
                                   next file in the archive */
#define XGLTYPE  'g'            /* Global extended header */

/* Bits used in the mode field, values in octal.  */
#define TSUID    04000          /* set UID on execution */
#define TSGID    02000          /* set GID on execution */
#define TSVTX    01000          /* reserved */
                                /* file permissions */
#define TUREAD   00400          /* read by owner */
#define TUWRITE  00200          /* write by owner */
#define TUEXEC   00100          /* execute/search by owner */
#define TGREAD   00040          /* read by group */
#define TGWRITE  00020          /* write by group */
#define TGEXEC   00010          /* execute/search by group */
#define TOREAD   00004          /* read by other */
#define TOWRITE  00002          /* write by other */
#define TOEXEC   00001          /* execute/search by other */

/* tar Header Block, GNU extensions.  */

/* In GNU tar, SYMTYPE is for to symbolic links, and CONTTYPE is for
   contiguous files, so maybe disobeying the "reserved" comment in POSIX
   header description.  I suspect these were meant to be used this way, and
   should not have really been "reserved" in the published standards.  */

/* *BEWARE* *BEWARE* *BEWARE* that the following information is still
   boiling, and may change.  Even if the OLDGNU format description should be
   accurate, the so-called GNU format is not yet fully decided.  It is
   surely meant to use only extensions allowed by POSIX, but the sketch
   below repeats some ugliness from the OLDGNU format, which should rather
   go away.  Sparse files should be saved in such a way that they do *not*
   require two passes at archive creation time.  Huge files get some POSIX
   fields to overflow, alternate solutions have to be sought for this.  */

/* Descriptor for a single file hole.  */

struct sparse
{                              /* byte offset */
  char offset[12];              /*   0 */
  char numbytes[12];            /*  12 */
                                /*  24 */
};

/* Sparse files are not supported in POSIX ustar format.  For sparse files
   with a POSIX header, a GNU extra header is provided which holds overall
   sparse information and a few sparse descriptors.  When an old GNU header
   replaces both the POSIX header and the GNU extra header, it holds some
   sparse descriptors too.  Whether POSIX or not, if more sparse descriptors
   are still needed, they are put into as many successive sparse headers as
   necessary.  The following constants tell how many sparse descriptors fit
   in each kind of header able to hold them.  */

#define SPARSES_IN_EXTRA_HEADER  16
#define SPARSES_IN_OLDGNU_HEADER 4
#define SPARSES_IN_SPARSE_HEADER 21

/* Extension header for sparse files, used immediately after the GNU extra
   header, and used only if all sparse information cannot fit into that
   extra header.  There might even be many such extension headers, one after
   the other, until all sparse information has been recorded.  */

struct sparse_header
{                              /* byte offset */
  struct sparse sp[SPARSES_IN_SPARSE_HEADER];
                                /*   0 */
  char isextended;              /* 504 */
                                /* 505 */
};

/* The old GNU format header conflicts with POSIX format in such a way that
   POSIX archives may fool old GNU tar's, and POSIX tar's might well be
   fooled by old GNU tar archives.  An old GNU format header uses the space
   used by the prefix field in a POSIX header, and cumulates information
   normally found in a GNU extra header.  With an old GNU tar header, we
   never see any POSIX header nor GNU extra header.  Supplementary sparse
   headers are allowed, however.  */

struct oldgnu_header
{                              /* byte offset */
  char unused_pad1[345];        /*   0 */
  char atime[12];               /* 345 Incr. archive: atime of the file */
  char ctime[12];               /* 357 Incr. archive: ctime of the file */
  char offset[12];              /* 369 Multivolume archive: the offset of
                                   the start of this volume */
  char longnames[4];            /* 381 Not used */
  char unused_pad2;             /* 385 */
  struct sparse sp[SPARSES_IN_OLDGNU_HEADER];
                                /* 386 */
  char isextended;              /* 482 Sparse file: Extension sparse header
                                   follows */
  char realsize[12];            /* 483 Sparse file: Real size*/
                                /* 495 */
};

/* OLDGNU_MAGIC uses both magic and version fields, which are contiguous.
   Found in an archive, it indicates an old GNU header format, which will be
   hopefully become obsolescent.  With OLDGNU_MAGIC, uname and gname are
   valid, though the header is not truly POSIX conforming.  */
#define OLDGNU_MAGIC "ustar  "  /* 7 chars and a null */

/* The standards committee allows only capital A through capital Z for
   user-defined expansion.  Other letters in use include:

   'A' Solaris Access Control List
   'E' Solaris Extended Attribute File
   'I' Inode only, as in 'star'
   'N' Obsolete GNU tar, for file names that do not fit into the main header.
   'X' POSIX 1003.1-2001 eXtended (VU version)  */

/* This is a dir entry that contains the names of files that were in the
   dir at the time the dump was made.  */
#define GNUTYPE_DUMPDIR 'D'

/* Identifies the *next* file on the tape as having a long linkname.  */
#define GNUTYPE_LONGLINK 'K'

/* Identifies the *next* file on the tape as having a long name.  */
#define GNUTYPE_LONGNAME 'L'

/* This is the continuation of a file that began on another volume.  */
#define GNUTYPE_MULTIVOL 'M'

/* This is for sparse files.  */
#define GNUTYPE_SPARSE 'S'

/* This file is a tape/volume header.  Ignore it on extraction.  */
#define GNUTYPE_VOLHDR 'V'

/* Solaris extended header */
#define SOLARIS_XHDTYPE 'X'

/* Jörg Schilling star header */

struct star_header
{                              /* byte offset */
  char name[100];               /*   0 */
  char mode[8];                 /* 100 */
  char uid[8];                  /* 108 */
  char gid[8];                  /* 116 */
  char size[12];                /* 124 */
  char mtime[12];               /* 136 */
  char chksum[8];               /* 148 */
  char typeflag;                /* 156 */
  char linkname[100];           /* 157 */
  char magic[6];                /* 257 */
  char version[2];              /* 263 */
  char uname[32];               /* 265 */
  char gname[32];               /* 297 */
  char devmajor[8];             /* 329 */
  char devminor[8];             /* 337 */
  char prefix[131];             /* 345 */
  char atime[12];               /* 476 */
  char ctime[12];               /* 488 */
                                /* 500 */
};

#define SPARSES_IN_STAR_HEADER      4
#define SPARSES_IN_STAR_EXT_HEADER  21

struct star_in_header
{
  char fill[345];       /*   0  Everything that is before t_prefix */
  char prefix[1];       /* 345  t_name prefix */
  char fill2;           /* 346  */
  char fill3[8];        /* 347  */
  char isextended;      /* 355  */
  struct sparse sp[SPARSES_IN_STAR_HEADER]; /* 356  */
  char realsize[12];    /* 452  Actual size of the file */
  char offset[12];      /* 464  Offset of multivolume contents */
  char atime[12];       /* 476  */
  char ctime[12];       /* 488  */
  char mfill[8];        /* 500  */
  char xmagic[4];       /* 508  "tar" */
};

struct star_ext_header
{
  struct sparse sp[SPARSES_IN_STAR_EXT_HEADER];
  char isextended;
};

收获

通过阅读官方标准手册对 OVF 和 OVA 有两个更深刻的了解,对 tar 包的格式和 Unix 文件头初次了解。这么好的资料准备有空翻译几个关键的章节来水一篇博客 😂