cpio 文档格式研究
author: hjjdebug
date: 2026年 04月 22日 星期三 17:05:21 CST
descrip: cpio 文档格式研究
.cpio 是linux 内核文件系统格式, 是一种简单的归档格式. 下面我们来认真研究一下这种格式.
1. 创建一个cpio 文档
创建一个目录 ~/test/abc, 里面创建2个文件file1.txt, file2.txt, 用cpio 来打包
$find . -depth |cpio -o -H newc > /tmp/abc.cpio
-depth 选项是深度优先, 即先打包目录下的文件,再打包目录,这样作为内存文件系统才不会出错.
我们来研究一下abc.cpio 文件是什么格式?
$ file /tmp/abc.cpio
/tmp/abc.cpio: ASCII cpio archive (SVR4 with no CRC)
$ cat /tmp/abc.cpio
07070100C8057D000081B4000003E8000003E80000000169E844C300000012000000080000000300000000000000000000000A00000000file2.txtthis is file2.txt
07070100C80579000081B4000003E8000003E80000000169E844BA00000012000000080000000300000000000000000000000A00000000file1.txtthis is file1.txt
07070100C88070000041FD000003E8000003E80000000269E844A900000000000000080000000300000000000000000000000200000000.
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!
哈哈, 看到了吧, 前边是"头部",后面,file2.txt"文件名", this is file2.txt"文件内容", 依次循环,下一个file1.txt,下一个.下一个TRAILER!!!
2. cpio 文件格式介绍
CPIO(Copy In/Out) 是类 Unix 系统上的归档打包格式 / 工具,
把多个文件、目录、设备节点打包成一个字节流(.cpio),本身不压缩,常与 gzip 配合(.cpio.gz)
比tar 更轻量级, 适合内存文件系统.
格式: 极其简单.
头(Header)+ 路径名 + 数据 + 对齐填充,
循环;
末尾用 TRAILER!!! 标记结束
你看到的abc.cpio 就是很好的例子.
a.Header(元数据)
记录:魔数、权限、UID/GID、大小、时间戳、硬链接数、设备号等(类似 struct stat)。
b. 路径名字段
字符串(含结尾 \0),长度在 Header 中定义。
c. 文件数据
普通文件:原样写入;目录 / 链接 / 设备:无数据或少量数据。
d. 对齐填充
按 4/8 字节对齐,保证内核读取高效。
e. 变体. newc 格式, 元数据长度32位, 支持大文件.linux标准
3. 自己写代码分析cpio文件
调试代码,能够分辨清楚数据结构. 加深对文件格式的理解.
$ cat unpack_cpio.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
typedef unsigned char u8;
typedef unsigned int u32;
/* CPIO newc 固定 6 字节魔数 */
#define CPIO_NEWC_MAGIC "070701"
/* newc 格式 header(小端/ASCII 十六进制字符串)*/
struct cpio_newc_header {
char c_magic[6];
char c_ino[8];
char c_mode[8];
char c_uid[8];
char c_gid[8];
char c_nlink[8];
char c_mtime[8];
char c_filesize[8];
char c_devmajor[8];
char c_devminor[8];
char c_rdevmajor[8];
char c_rdevminor[8];
char c_namesize[8];
char c_check[8];
};
/* 从 ASCII 十六进制串转 u32 */
static u32 hex2u32(const char *s, int len)
{
u32 val = 0;
for (int i = 0; i < len; i++) {
val <<= 4;
char c = s[i];
if (c >= '0' && c <= '9') val += c - '0';
else if (c >= 'a' && c <= 'f') val += 10 + c - 'a';
else if (c >= 'A' && c <= 'F') val += 10 + c - 'A';
}
return val;
}
/* 按 4 字节对齐 */
#define ALIGN4(x) (((x) + 3) & ~3)
/*
* 从 fd 读取一个 cpio 条目并解压
* 返回 0 正常, 1 到 TRAILER!!! 结束, <0 错误
*/
static int unpack_cpio_entry(int fd)
{
struct cpio_newc_header hdr;
ssize_t rd;
rd = read(fd, &hdr, sizeof(hdr));
if (rd != sizeof(hdr)) return -1;
/* 魔数校验 */
if (memcmp(hdr.c_magic, CPIO_NEWC_MAGIC, 6) != 0)
return -2;
u32 namesize = hex2u32(hdr.c_namesize, 8);
u32 filesize = hex2u32(hdr.c_filesize, 8);
u32 mode = hex2u32(hdr.c_mode, 8);
/* 读取文件名 */
char name[256] = {0};
read(fd, name, namesize);
/* 结束标记 */
if (!strcmp(name, "TRAILER!!!"))
return 1;
printf("extract: %s (size=%u)\n", name, filesize);
/* 普通文件 */
if (S_ISREG(mode)) {
int out = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (out >= 0) {
char buf[4096];
u32 left = filesize;
while (left > 0) {
ssize_t n = read(fd, buf, (left > sizeof(buf)) ? sizeof(buf) : left);
if (n <= 0) break;
write(out, buf, n);
left -= n;
}
close(out);
}
}
/* 目录 */
else if (S_ISDIR(mode)) {
mkdir(name, 0755);
}
/* 软链 */
else if (S_ISLNK(mode)) {
char link[256] = {0};
read(fd, link, filesize);
symlink(link, name);
}
/* 跳过文件数据对齐 */
u32 skip_data = ALIGN4(filesize) - filesize;
if (skip_data) lseek(fd, skip_data, SEEK_CUR);
return 0;
}
/* 解包整个 cpio 文件 */
int unpack_cpio(const char *path)
{
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
while (1) {
int ret = unpack_cpio_entry(fd);
if (ret == 1) break; /* 正常结束 */
if (ret < 0) return ret;/* 出错 */
}
close(fd);
return 0;
}
/* 测试 */
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "usage: %s <cpio-file>\n", argv[0]);
return 1;
}
if (unpack_cpio(argv[1]) < 0) {
fprintf(stderr, "unpack cpio failed\n");
return 1;
}
return 0;
}
4. 验证
$ ./unpack_cpio /tmp/abc.cpio
extract: file2.txt (size=18)
extract: file1.txt (size=18)
extract: . (size=0)
测试cpio文件验证通过.
调试代码,对cpio文件的文件头, 文件名称,文件体, 循环.这种结构加深认识.
对文件模式S_ISREG(mode),S_ISDIR(mode) 判断也加深了理解.
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)