diff --git a/3rdparty/pzip/include/pzip/file_task.h b/3rdparty/pzip/include/pzip/file_task.h index b78115e6..3487481e 100644 --- a/3rdparty/pzip/include/pzip/file_task.h +++ b/3rdparty/pzip/include/pzip/file_task.h @@ -106,6 +106,8 @@ class FileTask { fs::file_status status; // 文件状态 uintmax_t fileSize = 0; // 原始文件大小 ZipFileHeader header; // ZIP 头信息 + bool isSymlink = false; // 是否是符号链接 + std::string symlinkTarget; // 符号链接目标路径 // 压缩器(由 Archiver 管理) z_stream* compressor = nullptr; diff --git a/3rdparty/pzip/src/archiver.cpp b/3rdparty/pzip/src/archiver.cpp index ee48493c..226e4978 100644 --- a/3rdparty/pzip/src/archiver.cpp +++ b/3rdparty/pzip/src/archiver.cpp @@ -184,6 +184,13 @@ Error Archiver::compress(FileTask* task) { return Error(); } + if (task->isSymlink) { + const auto& target = task->symlinkTarget; + task->write(reinterpret_cast(target.data()), target.size()); + task->header.crc32 = ::crc32(0, reinterpret_cast(target.data()), target.size()); + return Error(); + } + std::ifstream file(task->path, std::ios::binary); if (!file.is_open()) { return Error(ErrorCode::FILE_OPEN_ERROR, "Cannot open file: " + task->path.string()); @@ -271,6 +278,14 @@ void Archiver::populateHeader(FileTask* task) { h.uncompressedSize = 0; h.compressedSize = 0; h.crc32 = 0; + } else if (task->isSymlink) { + // 符号链接:存储链接目标 + h.method = ZIP_METHOD_STORE; + h.flags &= ~ZIP_FLAG_DATA_DESCRIPTOR; + h.uncompressedSize = task->symlinkTarget.size(); + h.compressedSize = task->symlinkTarget.size(); + // 设置 Unix 符号链接属性 + h.externalAttr = static_cast(S_IFLNK | 0777) << 16; } else { h.method = ZIP_METHOD_DEFLATE; h.flags |= ZIP_FLAG_DATA_DESCRIPTOR; diff --git a/3rdparty/pzip/src/file_task.cpp b/3rdparty/pzip/src/file_task.cpp index 7c89b32f..49d04f72 100644 --- a/3rdparty/pzip/src/file_task.cpp +++ b/3rdparty/pzip/src/file_task.cpp @@ -82,17 +82,29 @@ Error FileTask::reset(const fs::path& filePath, const fs::path& relativeTo) { bufferUsed_ = 0; written_ = 0; + isSymlink = false; + symlinkTarget.clear(); // 设置新文件信息 path = filePath; std::error_code ec; - status = fs::status(path, ec); + // 使用 symlink_status 不跟随符号链接 + status = fs::symlink_status(path, ec); if (ec) { return Error(ErrorCode::FILE_NOT_FOUND, "Cannot stat file: " + path.string()); } - if (fs::is_regular_file(status)) { + // 检测符号链接 + isSymlink = fs::is_symlink(status); + if (isSymlink) { + // 读取符号链接目标 + symlinkTarget = fs::read_symlink(path, ec).string(); + if (ec) { + return Error(ErrorCode::FILE_READ_ERROR, "Cannot read symlink target: " + path.string()); + } + fileSize = symlinkTarget.size(); + } else if (fs::is_regular_file(status)) { fileSize = fs::file_size(path, ec); if (ec) { 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) { header = ZipFileHeader(); // 设置相对路径名 + // 注意:fs::relative() 会解析符号链接,所以使用 path 迭代器手动计算 if (!relativeTo.empty()) { - fs::path relPath = fs::relative(path, relativeTo, ec); - if (ec) { - // 如果无法计算相对路径,使用文件名 - header.name = path.filename().string(); - } else { + // 使用 path 迭代器跳过共同前缀 + auto pathIt = path.begin(); + auto relIt = relativeTo.begin(); + + while (pathIt != path.end() && relIt != relativeTo.end() && *pathIt == *relIt) { + ++pathIt; + ++relIt; + } + + // 构建相对路径 + fs::path relPath; + for (; pathIt != path.end(); ++pathIt) { + relPath /= *pathIt; + } + + if (!relPath.empty()) { header.name = utils::toZipPath(relPath); + } else { + header.name = utils::toZipPath(path.filename()); } } else { header.name = utils::toZipPath(path.filename());