翻墙不方便。不方便的地方在于前天的梯子,今天就会没用。所以把手头用到的东西记录到墙内显得尤为重要,哪怕只不过是芝麻点儿的小事。
// 小端存储
struct sparse_header_t {
uint32_t magic; // 幻数, 值为 0xed26ff3a
uint16_t major_version; // 主版本号
uint16_t minor_version; // 副版本号
uint16_t file_hdr_sz; // 文件头大小, 为28字节,就是这个结构体的尺寸
uint16_t chunk_hdr_sz; // CHUNK的头大小, 1.0中 固定为12
uint32_t blk_sz; // 块大小, 必须为4的整数倍,默认值为:4096
uint32_t total_blks; // 总块数
uint32_t total_chunks; // 总CHUNK数
uint32_t image_checksum; // CRC, 802.3多项式
};
// 什么是CHUNK,什么是BLOCK?
// CHUNK是sparse文件的基本单元,一个sparse文件由28字节的文件头加上
// 连续的CHUNK组成。BLOCK为CHUNK的基本单元,一个CHUNK由一个
// CHUNK头(12字节)和若干个BLOCK组成,每个BLOCK的大小由文件头中
// 的blk_sz决定.
enum ChunkType {
CHUNK_TYPE_RAW = 0xcac1, // 该CHUNK中的数据为原始数据, 由若干(头中的chunk_sz)个块组成
CHUNK_TYPE_FILL = 0xcac2, // 该CHUNK是填充块, CHUNK中有4个字节
// 表示CHUNK定义的若干(头中的chunk_sz)个块全部填充为CHUNK中附带的4个字节数据
CHUNK_TYPE_DONT_CARE = 0xcac3, // 无需关心的块, 跳过即可
CHUNK_TYPE_CRC32 = 0xcac4 // CHUNK中数据为CRC32, 4字节
};
#define SPARSE_HEADER_MAGIC 0xed26ff3a
struct chunk_header_t {
uint16_t chunk_type; // CHUNK的类型
uint16_t reserved; // 保留, 用于结构体对齐
uint32_t chunk_sz; // CHUNK中块的个数
uint32_t total_sz; // CHUNK的大小, 以字节为单位,表示CHUNK头的大小加CHUNK中
// 数据大大小, 例如, 对于填充的CHUNK,total_sz为16,对于RAW,
// total_sz为blk_sz * chunk_sz + sizeof(chunk_header_t)
};
测试程序
// 将输入sparse格式的文件转存为原始格式
void dump(const char* file, const char* file_raw)
{
FILE* fp = fopen(file, "rb");
if (!fp) {
printf("can't open file:%s\n", file);
} else {
sparse_header_t hdr;
ssize_t hdr_sz = fread(&hdr, 1, sizeof(hdr), fp);
if (!check_file_format(&hdr, hdr_sz)) {
printf("\"%s\" is not a sparse file\n", file);
} else {
FILE* fp_raw = fopen(file_raw, "wb");
if (fp_raw) {
// allocate
uint32_t raw_sz = hdr.blk_sz * hdr.total_blks;
printf("Allocated %10u bytes\n", raw_sz);
char* blk = new char[hdr.blk_sz]; memset(blk, 0, hdr.blk_sz);
for (uint32_t k = 0; k < hdr.total_blks; ++k) {
fwrite(blk, 1, hdr.blk_sz, fp_raw);
}
rewind(fp_raw);
uint32_t chunk_cnt = 0;
while (!feof(fp)) {
chunk_header_t chunk;
ssize_t chunk_sz = fread(&chunk, 1, sizeof(chunk), fp);
if (chunk_sz != sizeof(chunk)) break;
switch (chunk.chunk_type) {
case CHUNK_TYPE_RAW:
printf("write raw chunk: %10u bytes\n", hdr.blk_sz*chunk.chunk_sz);
for (uint32_t k = 0; k < chunk.chunk_sz; ++k) {
fread(blk, 1, hdr.blk_sz, fp);
fwrite(blk, 1, hdr.blk_sz, fp_raw);
}
break;
case CHUNK_TYPE_FILL:
uint32_t pattern; fread(&pattern, 1, 4, fp);
for (uint32_t k = 0; k < hdr.blk_sz / 4; ++k) {
*(uint32_t*)(blk + k*4) = pattern;
}
printf("write fil chunk: %10u bytes, pattern:%08x\n",
4*chunk.chunk_sz,
*(uint32_t*)blk);
for (uint32_t k = 0; k < chunk.chunk_sz; ++k) {
fwrite(blk, 1, hdr.blk_sz, fp_raw);
}
break;
case CHUNK_TYPE_CRC32:
fread(blk, 1, 4, fp);
break;
}
}
delete[] blk;
fclose(fp_raw);
} else {
printf("can't create file:%s\n", file_raw);
}
}
fclose(fp);
}
}