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) 判断也加深了理解.

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐