cpio头结构是归档中每个文件的元数据块,含魔数、权限、大小等字段;不能直接用fread读文件名,因newc格式用ascii十六进制编码字段(如filesize为8位hex),须strtol解析,且数据后有4字节对齐填充,header中size不包含该填充,需按(size+3)&~3计算真实读取长度;逐个提取须三重校验:header 4字节对齐、后续字节数充足、下一header以"070701"或"trailer!!!"开头;std::ifstream须以binary模式打开、每次seekg前调用clear()、避免使用tellg判eof。

什么是CPIO头结构,为什么不能直接用fread读文件名?
CPIO存档没有全局索引表,所有文件信息都靠连续的header + data块拼接而成。header固定为110字节(ASCII格式)或26字节(binary格式),但最常见的是070701开头的newc格式——它用十六进制ASCII表示字段长度,比如file size字段是8位ASCII十六进制数,必须strtol(buf + 94, nullptr, 16)解析,不能当十进制读。
常见错误是把整个存档当二进制流盲读:跳过header后直接按file size字节数memcpy,结果发现内容错位。这是因为CPIO要求每个文件数据块末尾补零到4字节对齐,而header里的size不包含这些padding。你得先算出对齐后的真实读取长度:(size + 3) & ~3。
如何逐个提取文件而不崩溃?关键三步校验
newc格式header以"070701"开头,但不是所有匹配字符串都是合法header——可能只是某个文件内容里恰好出现这6个字节。必须做三重检查:
- 检查header起始位置是否为4字节对齐(CPIO规范强制要求)
- 解析
c_filesize后,确认后续至少有那么多字节可读(避免越界) - 读完data后,下一个header必须以
"070701"开头,或遇到"TRAILER!!!"(注意末尾两个!,不是三个)
漏掉任一检查,遇到损坏存档或恶意构造数据时,fseek会跑到负偏移,fread返回0却继续解析,程序就卡死在死循环里。
std::ifstream配合seekg读CPIO时要注意什么?
用std::ifstream比C FILE*更易出错:默认打开是std::ios::binary没错,但一旦调用过operator>>或getline,流状态可能被设为failbit,后续seekg直接失效。务必在每次seek前加ifs.clear()。
另一个坑是跨平台换行:CPIO header里全是ASCII字符,但Windows下如果用文本模式打开(哪怕指定了binary),某些编译器仍会把\r\n转成\n,导致header长度计算全错。必须显式用std::ios::binary标志打开:
std::ifstream ifs("archive.cpio", std::ios::binary);
还有,别用ifs.tellg()判断EOF——它在某些libc实现里对大文件返回-1,应该用ifs.peek() == EOF或ifs.read(&buf, 1).gcount() == 0。
遇到"070702"或"070707"怎么办?
"070702"是newc格式带CRC校验的变种(极少用),"070707"是old binary格式(字段全是小端16/32位整数)。它们header布局完全不同:old binary里c_namesize在offset 14,而newc在offset 46;old binary没有magic string校验,全靠字段范围判断合法性。
实际处理建议:先尝试按newc解析,若c_namesize > 1024 或 c_filesize为负值,立即回退并尝试old binary解析逻辑。不要硬写一个“通用解析器”——CPIO工具链(如cpio -i)本身也不支持自动识别多种格式混用。
真正麻烦的是嵌套CPIO(比如initramfs里套着另一个CPIO),这时"TRAILER!!!"之后还有数据,但标准解析器会停在那里。你需要额外记录原始文件总大小,并在TRAILER后检查是否还有剩余字节未处理。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











