根本原因是未正确初始化archive对象或忽略错误码:archive_read_new()仅创建对象,须显式调用archive_read_support_filter_all()和archive_read_support_format_all(),且顺序不可颠倒,否则遇.zip/.7z等格式会返回archive_fatal。

libarchive读取文件时为什么总是返回ARCHIVE_FATAL或ARCHIVE_WARN?
根本原因是没正确初始化archive对象或忽略错误码。libarchive的archive_read_new()只创建对象,不自动启用格式支持;必须显式调用archive_read_support_format_all()和archive_read_support_filter_all(),否则遇到.zip/.7z等格式会直接失败。
常见错误是只调用archive_read_open_filename()就进入循环读取,结果archive_read_next_header()返回ARCHIVE_FATAL且archive_error_string()提示“Unrecognized archive format”。
- 必须在
archive_read_open_*前完成格式与过滤器支持注册 - 每次
archive_read_next_header()后要检查返回值:只有ARCHIVE_OK才表示成功读到一个entry -
ARCHIVE_WARN可继续,但需用archive_error_string()查具体警告(比如CRC校验失败)
如何安全提取.tar.gz中带路径的文件避免目录穿越?
libarchive默认不做路径净化,archive_entry_pathname()返回的路径可能含../。直接fopen()或mkdir()会导致写入任意位置。
正确做法是用archive_entry_clear()配合白名单检查,或借助archive_entry_set_pathname()重写路径:
struct archive_entry *entry = archive_entry_new();
archive_read_next_header(a, &entry);
const char *path = archive_entry_pathname(entry);
// 检查是否含 "../" 或绝对路径
if (strstr(path, "..") || path[0] == '/') {
fprintf(stderr, "Unsafe path: %s\n", path);
archive_entry_free(entry);
continue;
}
- 仅靠字符串匹配
".."不够严谨,生产环境建议用realpath()做归一化后比对根目录前缀 - 提取时优先用
archive_read_data_block()分块读取,避免内存爆炸(尤其大文件) - 注意
archive_entry_size()可能为-1(未知大小),别直接malloc
libarchive读取.rar或.zst格式为何失败?
libarchive本身不内置RAR解码器,archive_read_support_format_rar()实际依赖系统级librar库;而.zstd支持从libarchive 3.4.0起才稳定,旧版本即使调用archive_read_support_filter_zstd()也会静默忽略。
验证方法:编译时加-DENABLE_ZSTD=ON并确认pkg-config --modversion libarchive输出≥3.4.0;对RAR,需确认ldd your_binary | grep rar能找到librar.so。
- macOS Homebrew安装的libarchive默认不含RAR支持,需手动编译
--with-rar - Windows下.libarchive预编译包通常不含zstd,得自己链接
libzstd.lib - 调用
archive_version_number()确认运行时版本,避免头文件版本和链接库版本不一致
为什么多线程读取同一个archive句柄会崩溃?
libarchive的archive对象不是线程安全的——archive_read_next_header()内部修改内部状态指针,多线程并发调用必然导致数据竞争。
正确方案是每个线程独占一个archive实例:
// 错误:共享a archive_read_open_filename(a, "a.tar", 10240); // 正确:每个线程重新open struct archive *a1 = archive_read_new(); archive_read_support_format_all(a1); archive_read_open_filename(a1, "a.tar", 10240);
- 不要试图用mutex保护单个archive对象,性能差且无法解决内部缓冲区冲突
- 若需并发处理多个压缩包,直接为每个文件创建独立archive更简单可靠
- 注意
archive_read_free()必须成对调用,漏掉会导致内存泄漏(尤其在循环中)
archive_read_support_filter_all()再archive_read_support_format_all(),否则gzip/bzip2等压缩层无法解包,错误码却显示“format not supported”而非“filter not found”。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











