Skip to content

fix: auto-detect Deflate64 in ZIP files#389

Merged
max-lvs merged 1 commit intolinuxdeepin:release/eaglefrom
LiHua000:release/eagle
Apr 16, 2026
Merged

fix: auto-detect Deflate64 in ZIP files#389
max-lvs merged 1 commit intolinuxdeepin:release/eaglefrom
LiHua000:release/eagle

Conversation

@LiHua000
Copy link
Copy Markdown
Contributor

@LiHua000 LiHua000 commented Apr 15, 2026

Use libzip API to detect Deflate64 compression and automatically switch to cli7z plugin for better compatibility.

log: fix bug

Bug:https://pms.uniontech.com//bug-view-357127.html

Summary by Sourcery

Detect Deflate64-compressed ZIP archives using libzip and automatically route them to the cli7z plugin for improved compatibility.

New Features:

  • Add ZIP compression method inspection using libzip to decide when to use an alternative archive plugin.

Enhancements:

  • Integrate libzip as a build dependency and link it into the main executable for ZIP inspection.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 15, 2026

Reviewer's Guide

Adds a libzip-based pre-scan for ZIP files to detect Deflate64 compression and automatically route such archives to the cli7z plugin, wiring in the new helper into UiTools and linking against libzip in the build system.

Sequence diagram for ZIP open with Deflate64 auto-routing to cli7z

sequenceDiagram
    actor User
    participant UiTools
    participant libzip
    participant PluginManager
    participant Plugin_cli7z as Plugin_cli7z
    participant Plugin_default as Plugin_default

    User->>UiTools: createInterface(fileName, bWrite=false, eType=APT_Auto)
    UiTools->>UiTools: determineMimeType(fileName)
    UiTools->>libzip: checkZipNeedsAlternativePlugin(fileName)
    libzip-->>UiTools: needsAlternative (true/false)
    alt needsAlternative == true
        UiTools->>UiTools: eType = APT_Cli7z
    end
    UiTools->>PluginManager: preferredReadPluginsFor(mimeType)
    PluginManager-->>UiTools: [Plugin_cli7z, Plugin_default, ...]
    alt eType == APT_Cli7z
        UiTools->>Plugin_cli7z: create ReadOnlyArchiveInterface
    else eType != APT_Cli7z
        UiTools->>Plugin_default: create ReadOnlyArchiveInterface
    end
    UiTools-->>User: ReadOnlyArchiveInterface
Loading

Class diagram for UiTools Deflate64 detection and plugin selection

classDiagram
    class UiTools {
        +static ReadOnlyArchiveInterface* createInterface(QString fileName, bool bWrite, AssignPluginType eType)
        +static ReadOnlyArchiveInterface* createInterface(QString fileName, CustomMimeType mimeType, Plugin* plugin)
        +static bool checkZipNeedsAlternativePlugin(QString strFileName)
    }

    class PluginManager {
        +static PluginManager& get_instance()
        +QVector~Plugin*~ preferredWritePluginsFor(CustomMimeType mimeType)
        +QVector~Plugin*~ preferredReadPluginsFor(CustomMimeType mimeType)
    }

    class ReadOnlyArchiveInterface
    class Plugin

    class AssignPluginType {
        <<enumeration>>
        APT_Auto
        APT_Cli7z
        APT_Other
    }

    class libzip_API {
        +zip_t* zip_open(const char* path, int flags, int* errorp)
        +zip_int64_t zip_get_num_entries(zip_t* archive, int flags)
        +int zip_stat_index(zip_t* archive, zip_uint64_t index, int flags, zip_stat* sb)
        +void zip_stat_init(zip_stat* sb)
        +int zip_close(zip_t* archive)
    }

    UiTools --> PluginManager : uses
    UiTools --> ReadOnlyArchiveInterface : creates
    UiTools --> Plugin : selects
    UiTools --> AssignPluginType : sets
    UiTools --> libzip_API : calls
Loading

File-Level Changes

Change Details Files
Add pre-scan logic in UiTools::createInterface to auto-switch ZIP handling to the cli7z plugin when Deflate64 is detected.
  • Include libzip header in uitools.cpp so ZIP archives can be inspected before plugin selection.
  • Before resolving read-only archive plugins, check if the operation is read-only, plugin type is auto, and MIME type is application/zip.
  • Invoke a new helper to decide whether an alternative plugin is needed and, if so, force AssignPluginType to APT_Cli7z, logging the decision.
src/source/common/uitools.cpp
Introduce UiTools::checkZipNeedsAlternativePlugin using libzip APIs to detect Deflate64-compressed entries in ZIP files.
  • Open the ZIP via zip_open in read-only mode and log a warning while falling back to default behavior if the archive cannot be opened.
  • Iterate up to the first 20 entries, skipping stored (uncompressed) entries, and inspect the first compressed entry's comp_method.
  • If comp_method equals Deflate64 (9), log an informational message with the file and entry name and mark the archive as needing an alternative plugin, otherwise stop scanning once a non-Deflate, non-stored method is found.
  • Ensure the ZIP archive is closed via zip_close and return whether an alternative plugin is required.
src/source/common/uitools.cpp
Expose the new ZIP check helper in UiTools header.
  • Declare static bool checkZipNeedsAlternativePlugin(const QString &strFileName) in UiTools with a brief Doxygen-style comment describing its purpose and parameters.
src/source/common/uitools.h
Link the application against libzip and add its include paths in the CMake configuration.
  • Use pkg_search_module to locate the libzip (ZIP) package and add its include directories to the global include path.
  • Add ${ZIP_LIBRARIES} to the main executable's target_link_libraries so the new libzip calls resolve at link time.
src/CMakeLists.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In checkZipNeedsAlternativePlugin, avoid magic numbers for compression methods (0, 9) and use libzip’s named constants (e.g., ZIP_CM_STORE, ZIP_CM_DEFLATE64) to improve readability and reduce the chance of mistakes if values change.
  • The detection logic stops after the first non-stored entry; if you need to handle archives with mixed compression methods, consider scanning all entries up to maxCheck instead of breaking after the first non-Deflate64 compressed file.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `checkZipNeedsAlternativePlugin`, avoid magic numbers for compression methods (`0`, `9`) and use libzip’s named constants (e.g., `ZIP_CM_STORE`, `ZIP_CM_DEFLATE64`) to improve readability and reduce the chance of mistakes if values change.
- The detection logic stops after the first non-stored entry; if you need to handle archives with mixed compression methods, consider scanning all entries up to `maxCheck` instead of breaking after the first non-Deflate64 compressed file.

## Individual Comments

### Comment 1
<location path="src/source/common/uitools.cpp" line_range="496-505" />
<code_context>
+        zip_stat_init(&stat_buffer);
+
+        if (zip_stat_index(archive, static_cast<zip_uint64_t>(i), 0, &stat_buffer) == 0) {
+            // 跳过 stored 文件(comp_method == 0)
+            if (stat_buffer.comp_method == 0) {
+                continue;
+            }
+
+            // 检查压缩方法
+            // ZIP_CM_DEFLATE (8) = 标准 Deflate
+            // ZIP_CM_DEFLATE64 (9) = Deflate64
+            if (stat_buffer.comp_method == 9) {  // ZIP_CM_DEFLATE64
+                qInfo() << "Detected Deflate64 compression method, switching to cli7z plugin"
+                        << "file:" << strFileName
</code_context>
<issue_to_address>
**suggestion:** Prefer libzip compression-method constants over magic numbers for clarity and robustness.

Use `ZIP_CM_STORE` instead of `0` and `ZIP_CM_DEFLATE64` (and optionally `ZIP_CM_DEFLATE`) instead of `9` when checking `stat_buffer.comp_method`. This keeps the code self-documenting and avoids relying on magic numbers that could be misread or change in future libzip versions.

```suggestion
        if (zip_stat_index(archive, static_cast<zip_uint64_t>(i), 0, &stat_buffer) == 0) {
            // 跳过 stored 文件(comp_method == ZIP_CM_STORE)
            if (stat_buffer.comp_method == ZIP_CM_STORE) {
                continue;
            }

            // 检查压缩方法
            // ZIP_CM_DEFLATE   = 标准 Deflate
            // ZIP_CM_DEFLATE64 = Deflate64
            if (stat_buffer.comp_method == ZIP_CM_DEFLATE64) {
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/source/common/uitools.cpp Outdated
max-lvs
max-lvs previously approved these changes Apr 15, 2026
Use libzip API to detect Deflate64 compression and automatically
switch to cli7z plugin for better compatibility.

log: fix bug

Bug:https://pms.uniontech.com//bug-view-357127.html
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这段代码的目的是在处理 ZIP 文件时,检测其是否使用了不常见的压缩算法(如 Deflate64),如果检测到,则自动切换到 cli7z 插件进行处理。这是一个很好的功能增强,可以提升软件的兼容性。

以下是对这段代码的详细审查和改进建议:

1. 语法与逻辑审查

  • CMakeLists.txt:

    • 语法正确。使用 pkg_search_module 查找 libzip 并添加到链接依赖是标准的做法。
    • 逻辑清晰:确保在编译和链接阶段都能正确找到库。
  • uitools.cpp:

    • 头文件包含: 正确包含了 <zip.h>
    • 函数逻辑 (checkZipNeedsAlternativePlugin):
      • 资源管理: 使用了 zip_openzip_close,但在 zip_open 后的 zip_stat_index 循环中,如果发生错误(尽管 zip_stat_index 返回 0 表示成功),或者中间发生异常(C++ 代码中较少见但在复杂逻辑中可能存在),资源可能会泄漏。建议使用 RAII(资源获取即初始化)模式或确保在所有退出路径上都调用 zip_close。目前的代码在 return 前都调用了 zip_close,这点做得很好。
      • 检测逻辑: 逻辑是检查前 20 个文件,跳过未压缩(Store)的文件,寻找第一个压缩文件。如果它是 Deflate64,则切换插件。如果它是标准 Deflate,则停止检查。这个逻辑是合理的,因为同一个 ZIP 文件通常不会混用多种压缩算法。
      • 类型转换: zip_stat_index 的第二个参数需要 zip_uint64_t,代码中使用了 static_cast<zip_uint64_t>(i),这是安全且规范的。

2. 代码质量改进建议

  • 资源管理 (RAII):

    • 虽然代码目前看起来在 return 前都调用了 zip_close,但为了代码的健壮性和防止未来修改引入 Bug,建议使用 C++ 的 RAII 机制来管理 zip_t* 指针。
    • 示例:
      struct ZipArchiveGuard {
          zip_t* archive;
          ZipArchiveGuard(zip_t* a) : archive(a) {}
          ~ZipArchiveGuard() { if (archive) zip_close(archive); }
          // 禁止拷贝
          ZipArchiveGuard(const ZipArchiveGuard&) = delete;
          ZipArchiveGuard& operator=(const ZipArchiveGuard&) = delete;
      };
      
      // 在函数中使用:
      int errcode = 0;
      zip_t *archive = zip_open(QFile::encodeName(strFileName).constData(), ZIP_RDONLY, &errcode);
      if (!archive) { /* ... */ return false; }
      
      ZipArchiveGuard guard(archive); // 自动管理释放
      
      // ... 后续逻辑 ...
      // 函数结束或返回时,guard 析构函数自动调用 zip_close
  • 错误处理:

    • zip_stat_index 返回非 0 时(虽然目前代码只检查 == 0),可以打印更详细的警告信息,例如 zip_error_strerror(zip_get_error(archive)),以便调试。
  • 魔数检测:

    • maxCheck = 20 是一个魔数。建议将其定义为类的静态常量或在函数顶部定义一个有意义的常量变量,例如 const int MAX_ENTRIES_TO_CHECK = 20;

3. 代码性能改进建议

  • 文件打开开销:
    • 问题: createInterface 函数中,如果文件类型是 ZIP 且只读,会调用 checkZipNeedsAlternativePlugin。该函数会打开 ZIP 文件(zip_open)并读取部分目录结构。随后,createInterface 会继续执行,最终创建插件接口(如 libarchivecli7z),这意味着该文件会被再次打开和解析。
    • 影响: 这会导致大 ZIP 文件或位于慢速设备(如网络存储)上的文件被打开两次,增加了 I/O 开销和启动延迟。
    • 建议:
      1. 优化: 考虑是否可以在插件加载器内部进行这种检测,或者将检测逻辑延迟到真正需要读取压缩方法时进行(但这可能改变插件选择的时机)。
      2. 缓存: 如果 UiTools 是长生命周期的,且文件路径可能被重复处理,可以考虑缓存检测结果(但要注意文件可能被修改)。
      3. 权衡: 鉴于目前只检查前 20 个条目,且 zip_open 主要是读取目录(通常在文件末尾),性能损耗可能尚可接受。但如果性能测试显示这是瓶颈,则需要重构。

4. 代码安全改进建议

  • 文件名编码:

    • 代码使用了 QFile::encodeName(strFileName).constData()QFile::encodeName 会将文件名转换为本地 8 位编码。这在大多数 Unix 系统上是 UTF-8,但在某些系统配置下可能不是。libzip 通常期望文件名是 UTF-8 编码(取决于编译选项和版本)。
    • 建议: 确认 libzip 的配置。如果 libzip 配置为支持 UTF-8,建议显式使用 strFileName.toUtf8().constData(),以避免本地编码转换带来的潜在问题(例如文件名包含非本地字符集字符时)。
  • 整数溢出:

    • zip_get_num_entries 返回 zip_int64_t。虽然与 maxCheck (int) 比较通常是安全的,但在极端情况下,保持类型一致或显式处理更好。目前的写法 i < num_entries && i < maxCheck 是安全的,因为 maxCheck 很小。
  • ZIP_CM_DEFLATE64 宏检查:

    • 代码中使用了 ZIP_CM_DEFLATE64。应确保编译时使用的 libzip 版本定义了这个宏。ZIP_CM_DEFLATE64 是在较新版本的 libzip 中添加的。如果项目需要支持旧版本的 libzip,可能需要添加预处理器检查或使用原始值(9),但使用宏是更好的做法。

5. 其他建议

  • 日志:
    • 日志输出使用了 qInfo()qWarning(),这很好。建议在检测到标准 Deflate 方法并提前退出循环时,也添加一个 qDebug() 级别的日志,这有助于在排查兼容性问题时确认检测流程。
    • 例如:
          // ...
          if (stat_buffer.comp_method == ZIP_CM_DEFLATE) {
              qDebug() << "Detected standard Deflate, no alternative plugin needed for file:" << strFileName;
              break;
          }
          // ...

总结

这段代码整体质量不错,逻辑清晰,能够解决特定的兼容性问题。主要改进点在于使用 RAII 增强资源管理的安全性,以及注意文件名编码和库版本兼容性。性能方面存在双重打开文件的微小开销,但在当前场景下通常可以接受。

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: LiHua000, max-lvs

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@LiHua000
Copy link
Copy Markdown
Contributor Author

/merge

1 similar comment
@LiHua000
Copy link
Copy Markdown
Contributor Author

/merge

@max-lvs max-lvs merged commit 44d5705 into linuxdeepin:release/eagle Apr 16, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants