Skip to content

Commit 275f4c1

Browse files
fix: Add support for symbolic links in file compression
- Introduced isSymlink and symlinkTarget attributes in FileTask to handle symbolic links. - Updated Archiver to compress symlink targets and adjust ZIP header properties accordingly. - Modified FileTask reset method to detect and read symlink targets, ensuring proper file size handling. Log: Enhance file compression capabilities by supporting symbolic links
1 parent 29d44af commit 275f4c1

3 files changed

Lines changed: 50 additions & 7 deletions

File tree

3rdparty/pzip/include/pzip/file_task.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ class FileTask {
106106
fs::file_status status; // 文件状态
107107
uintmax_t fileSize = 0; // 原始文件大小
108108
ZipFileHeader header; // ZIP 头信息
109+
bool isSymlink = false; // 是否是符号链接
110+
std::string symlinkTarget; // 符号链接目标路径
109111

110112
// 压缩器(由 Archiver 管理)
111113
z_stream* compressor = nullptr;

3rdparty/pzip/src/archiver.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ Error Archiver::compress(FileTask* task) {
184184
return Error();
185185
}
186186

187+
if (task->isSymlink) {
188+
const auto& target = task->symlinkTarget;
189+
task->write(reinterpret_cast<const uint8_t*>(target.data()), target.size());
190+
task->header.crc32 = ::crc32(0, reinterpret_cast<const Bytef*>(target.data()), target.size());
191+
return Error();
192+
}
193+
187194
std::ifstream file(task->path, std::ios::binary);
188195
if (!file.is_open()) {
189196
return Error(ErrorCode::FILE_OPEN_ERROR, "Cannot open file: " + task->path.string());
@@ -271,6 +278,14 @@ void Archiver::populateHeader(FileTask* task) {
271278
h.uncompressedSize = 0;
272279
h.compressedSize = 0;
273280
h.crc32 = 0;
281+
} else if (task->isSymlink) {
282+
// 符号链接:存储链接目标
283+
h.method = ZIP_METHOD_STORE;
284+
h.flags &= ~ZIP_FLAG_DATA_DESCRIPTOR;
285+
h.uncompressedSize = task->symlinkTarget.size();
286+
h.compressedSize = task->symlinkTarget.size();
287+
// 设置 Unix 符号链接属性
288+
h.externalAttr = static_cast<uint32_t>(S_IFLNK | 0777) << 16;
274289
} else {
275290
h.method = ZIP_METHOD_DEFLATE;
276291
h.flags |= ZIP_FLAG_DATA_DESCRIPTOR;

3rdparty/pzip/src/file_task.cpp

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,29 @@ Error FileTask::reset(const fs::path& filePath, const fs::path& relativeTo) {
8282

8383
bufferUsed_ = 0;
8484
written_ = 0;
85+
isSymlink = false;
86+
symlinkTarget.clear();
8587

8688
// 设置新文件信息
8789
path = filePath;
8890

8991
std::error_code ec;
90-
status = fs::status(path, ec);
92+
// 使用 symlink_status 不跟随符号链接
93+
status = fs::symlink_status(path, ec);
9194
if (ec) {
9295
return Error(ErrorCode::FILE_NOT_FOUND, "Cannot stat file: " + path.string());
9396
}
9497

95-
if (fs::is_regular_file(status)) {
98+
// 检测符号链接
99+
isSymlink = fs::is_symlink(status);
100+
if (isSymlink) {
101+
// 读取符号链接目标
102+
symlinkTarget = fs::read_symlink(path, ec).string();
103+
if (ec) {
104+
return Error(ErrorCode::FILE_READ_ERROR, "Cannot read symlink target: " + path.string());
105+
}
106+
fileSize = symlinkTarget.size();
107+
} else if (fs::is_regular_file(status)) {
96108
fileSize = fs::file_size(path, ec);
97109
if (ec) {
98110
return Error(ErrorCode::FILE_READ_ERROR, "Cannot get file size: " + path.string());
@@ -105,13 +117,27 @@ Error FileTask::reset(const fs::path& filePath, const fs::path& relativeTo) {
105117
header = ZipFileHeader();
106118

107119
// 设置相对路径名
120+
// 注意:fs::relative() 会解析符号链接,所以使用 path 迭代器手动计算
108121
if (!relativeTo.empty()) {
109-
fs::path relPath = fs::relative(path, relativeTo, ec);
110-
if (ec) {
111-
// 如果无法计算相对路径,使用文件名
112-
header.name = path.filename().string();
113-
} else {
122+
// 使用 path 迭代器跳过共同前缀
123+
auto pathIt = path.begin();
124+
auto relIt = relativeTo.begin();
125+
126+
while (pathIt != path.end() && relIt != relativeTo.end() && *pathIt == *relIt) {
127+
++pathIt;
128+
++relIt;
129+
}
130+
131+
// 构建相对路径
132+
fs::path relPath;
133+
for (; pathIt != path.end(); ++pathIt) {
134+
relPath /= *pathIt;
135+
}
136+
137+
if (!relPath.empty()) {
114138
header.name = utils::toZipPath(relPath);
139+
} else {
140+
header.name = utils::toZipPath(path.filename());
115141
}
116142
} else {
117143
header.name = utils::toZipPath(path.filename());

0 commit comments

Comments
 (0)