Skip to content

fix: update libzip archive data in-memory like libarchive - #494

Open
LiHua000 wants to merge 1 commit into
linuxdeepin:masterfrom
LiHua000:agent/pms-bug-bot/196beecb666d
Open

LiHua000 wants to merge 1 commit into
linuxdeepin:masterfrom
LiHua000:agent/pms-bug-bot/196beecb666d

Conversation

@LiHua000

@LiHua000 LiHua000 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Root Cause Analysis

LibzipPlugin::updateArchiveData() ignored the options parameter (parameter name commented out as /*options*/), cleared all in-memory archive data via resetArchiveData(), then re-read from disk using zip_open(). The zip_open() failure handler was commented out, so when re-reading failed, the function silently returned PFT_Nomral (success) with empty/stale data, causing the UI to not refresh after rename operations. In contrast, LibarchivePlugin::updateArchiveData() uses the options parameter to update entries in-memory, which is why tar format refreshes correctly.

Key evidence: 3rdparty/libzipplugin/libzipplugin.cpp:556-589 (defective implementation) vs 3rdparty/libarchive/libarchive/libarchiveplugin.cpp:576-700 (correct reference implementation).

Fix Approach

Rewrote LibzipPlugin::updateArchiveData() to mirror LibarchivePlugin::updateArchiveData() — using the options parameter for in-memory updates across Delete, Rename, and Add operation types. This eliminates the disk re-read fragility entirely and naturally restores proper error handling (no more zip_open() path with commented-out failure handling). Only 3rdparty/libzipplugin/libzipplugin.cpp is modified, 1 file changed, 141 insertions(+), 24 deletions(-).

Change Safety Assessment

Code Safety

  • Risk Level: Medium
  • Target behavior (commented-out zip_open error handling) was not a historical bug fix product — it was unfinished early development code. This change does not revert any historical fix.
  • Single caller singlejob.cpp:545 invokes via virtual dispatch; function signature unchanged, return type unchanged. Related PMS bugs (365277, 356233, 353985) are all security/symlink fixes, unrelated to this change.

Business Impact Scope

Affects zip format archive file management: rename, delete, and add operations on files inside zip archives. After this fix, zip archives will refresh UI immediately after these operations, consistent with tar format behavior. tar/tar.gz and other formats are unaffected (only libzip plugin modified).

Verification Suggestion

Focus regression testing on zip format: rename/delete/add for both files and multi-level folders. Confirm tar format operations remain unaffected.


根因分析

LibzipPlugin::updateArchiveData() 忽略 options 参数(参数名被注释为 /*options*/),通过 resetArchiveData() 清空全部内存数据后使用 zip_open() 从磁盘重读。zip_open() 失败处理被注释掉,重读失败时函数静默返回 PFT_Nomral(成功),内存数据为空或过时,导致重命名后 UI 无法正确刷新。而 LibarchivePlugin::updateArchiveData() 利用 options 参数在内存中更新条目,因此 tar 格式能正确刷新。

关键证据:3rdparty/libzipplugin/libzipplugin.cpp:556-589(缺陷实现)对比 3rdparty/libarchive/libarchive/libarchiveplugin.cpp:576-700(正确参考实现)。

修复方案

参照 LibarchivePlugin::updateArchiveData() 重写 LibzipPlugin::updateArchiveData(),使用 options 参数对 Delete、Rename、Add 三种操作类型做内存级更新,消除磁盘重读脆弱性,自然恢复错误处理(不再有 zip_open() 路径及注释掉的失败处理)。仅修改 3rdparty/libzipplugin/libzipplugin.cpp,1 文件改动,141 行新增,24 行删除。

改动安全评估

代码安全评估

  • 风险等级: 中风险
  • 目标行为(注释掉的 zip_open 错误处理)不是历史 bug 修复产物,而是早期开发遗留的未完成代码,本次修复不撤销任何历史修复。
  • 唯一调用方 singlejob.cpp:545 通过虚函数分派调用,函数签名不变,返回类型不变。关联 PMS bug(365277、356233、353985)均为安全/符号链接修复,与本次改动无关。

业务影响范围

影响 zip 格式压缩包文件管理:zip 压缩包内文件的重命名、删除、追加操作。修复后 zip 压缩包在这些操作后 UI 即时刷新,与 tar 格式行为一致。tar/tar.gz 等格式不受影响(仅修改 libzip 插件)。

验证建议

重点回归测试 zip 格式下文件和多层文件夹的重命名/删除/追加操作,确认 tar 格式操作不受影响。

Summary by Sourcery

Keep ZIP archive metadata synchronized in memory after file management operations.

Bug Fixes:

  • Fix ZIP archive metadata refresh after file and folder delete, rename, and add operations so the UI reflects changes immediately.

Enhancements:

  • Update ZIP archive state in memory using operation details instead of clearing and rereading archive contents from disk.

1. Root cause: LibzipPlugin::updateArchiveData() ignored the options
   parameter, cleared all memory data, and re-read from disk via
   zip_open() with commented-out error handling on failure
2. Fix: rewrite updateArchiveData() to use the options parameter for
   in-memory updates (Delete/Rename/Add), mirroring
   LibarchivePlugin::updateArchiveData(), eliminating disk re-read
   fragility and restoring proper error handling
3. Impact: zip format archives now refresh UI immediately after
   rename/delete/add operations without reopening, consistent with
   tar format behavior; no signature change, single caller via
   virtual dispatch unaffected

Influence:
1. Test zip archive file rename, verify UI shows new name immediately
2. Test zip archive file/folder delete, verify UI updates instantly
3. Test zip archive file add, verify new entry appears without reopen
4. Test multi-level path folder rename/delete in zip archives
5. Verify tar format operations remain unaffected

fix: 修复zip格式压缩包重命名后需重新打开才显示的问题

1. 根因:LibzipPlugin::updateArchiveData() 忽略 options 参数,清空
   全部内存数据后通过 zip_open() 从磁盘重读,且 zip_open 失败时
   错误处理被注释掉,导致内存数据为空或过时,UI 无法正确刷新
2. 方案:参照 LibarchivePlugin::updateArchiveData() 实现,改用
   options 参数做内存级更新(Delete/Rename/Add),消除磁盘重读
   脆弱性,恢复错误处理
3. 影响:zip 格式压缩包重命名/删除/追加后 UI 即时刷新,与 tar
   格式行为一致;函数签名不变,单一调用方通过虚函数分派不受影响

Influence:
1. 测试zip压缩包内文件重命名,验证UI即时显示新名称
2. 测试zip压缩包内文件/文件夹删除,验证UI即时更新
3. 测试zip压缩包内文件追加,验证新条目无需重开即可显示
4. 测试多层路径文件夹的重命名/删除操作
5. 验证tar格式操作不受影响

PMS: BUG-232441
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: LiHua000

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

@sourcery-ai

sourcery-ai Bot commented Sep 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

Zip archive metadata is now updated in memory for delete, rename, and add operations, matching libarchive behavior and avoiding fragile disk re-reads that could leave the UI with stale or empty data.

Sequence diagram for in-memory zip archive updates

sequenceDiagram
    participant Operation as ArchiveOperation
    participant Plugin as LibzipPlugin
    participant Data as ArchiveData
    participant UI as ArchiveUI

    Operation->>Plugin: updateArchiveData(options)
    Plugin->>Data: Apply Delete, Rename, or Add
    Data-->>Plugin: Updated entries and sizes
    Plugin->>Data: QFileInfo(m_strArchiveName).size()
    Plugin-->>UI: PFT_Nomral
    UI->>Data: Refresh archive listing
Loading

Flow diagram for zip metadata update operations

flowchart TD
    A["updateArchiveData(options)"] --> B{"options.eType"}
    B -->|Delete| C["Remove entries and update qSize/listRootEntry"]
    B -->|Rename| D["Rename mapFileEntry and listRootEntry entries"]
    B -->|Add| E["Insert entry and update qSize/listRootEntry"]
    C --> F["Update qComressSize"]
    D --> F
    E --> F
    F --> G["Return PFT_Nomral"]
Loading

File-Level Changes

Change Details Files
Replaced disk-based archive-data reload with in-memory updates driven by the operation options.
  • Removed archive-data clearing and zip_open() re-read logic.
  • Implemented deletion of files and recursive directory contents, including size and root-entry maintenance.
  • Implemented file and directory renames, including path, filename, map, and root-entry updates.
  • Implemented additions with destination-path mapping, directory normalization, size accounting, and map/root-entry insertion.
  • Updated compressed-size metadata from the archive file after each operation.
3rdparty/libzipplugin/libzipplugin.cpp

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="3rdparty/libzipplugin/libzipplugin.cpp" line_range="647-657" />
<code_context>
+                    }
+                }
+            } else { // 重命名文件
+                stArchiveData.mapFileEntry.remove(entry.strFullPath); //在map中重命名该文件
+                QString strPath = QFileInfo(entry.strFullPath).path();
+                if(strPath == "." || strPath.isEmpty() || strPath.isNull()) {
+                    strAlias = entry.strAlias;
+                } else {
+                    strAlias = strPath + QDir::separator() + entry.strAlias;
+                }
+                FileEntry tmpEntry = entry;
+                tmpEntry.strFullPath = strAlias;
+                stArchiveData.mapFileEntry.insert(strAlias, tmpEntry);
+                // 更新文件夹第一层的数据
+                if (!entry.strFullPath.contains(QLatin1Char('/'))) {
+                    for (int i = 0; i < stArchiveData.listRootEntry.count(); i++) {
</code_context>
<issue_to_address>
**issue (bug_risk):** Renaming a file inside a folder inserts the renamed entry into `mapFileEntry` with its new `strFullPath` but leaves `strFileName` copied from the old `entry`. The nested archive view therefore displays the old filename after the rename until the archive is reopened and rebuilt from disk.

**Triggers:** When a file being renamed is not at the archive root.

**Suggested fix:** Set `tmpEntry.strFileName` to `QFileInfo(strAlias).fileName()` before inserting the renamed entry.
</issue_to_address>

### Comment 2
<location path="3rdparty/libzipplugin/libzipplugin.cpp" line_range="688" />
<code_context>
-//        m_bAllEntry = true;
-//        return minizip_list();
-    }
+            // 判断是否追加到第一层数据
+            if (destinationPath.isEmpty() && ((entry.strFullPath.count('/') == 1 && entry.strFullPath.endsWith('/')) || entry.strFullPath.count('/') == 0)) {
+                for (int i = 0; i < stArchiveData.listRootEntry.count(); i++) {
+                    if (stArchiveData.listRootEntry.at(i).strFullPath == entry.strFullPath) { // 在第一层数据中找到entry,不添加数据
+                        stArchiveData.listRootEntry.removeAt(i);
+                        break;
+                    }
+                }

</code_context>
<issue_to_address>
**issue (bug_risk):** The Add path uses the literal `'/'` when deciding whether an entry belongs in `listRootEntry`, while archive paths are normalized with `QDir::separator()`. On Windows, nested entries use backslashes and `count('/')` is zero, so every added nested file or directory is treated as a root entry and appears at the wrong archive level in the refreshed view.

**Triggers:** When adding a folder tree to a ZIP archive on Windows with an empty destination path.

**Suggested fix:** Use `QDir::separator()` consistently, or use a platform-independent archive path separator when counting and testing archive paths.

```suggestion
            if (destinationPath.isEmpty() && ((entry.strFullPath.count(QDir::separator()) == 1 && entry.strFullPath.endsWith(QDir::separator())) || entry.strFullPath.count(QDir::separator()) == 0)) {
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment on lines +647 to +657
stArchiveData.mapFileEntry.remove(entry.strFullPath); //在map中重命名该文件
QString strPath = QFileInfo(entry.strFullPath).path();
if(strPath == "." || strPath.isEmpty() || strPath.isNull()) {
strAlias = entry.strAlias;
} else {
strAlias = strPath + QDir::separator() + entry.strAlias;
}
FileEntry tmpEntry = entry;
tmpEntry.strFullPath = strAlias;
stArchiveData.mapFileEntry.insert(strAlias, tmpEntry);
// 更新文件夹第一层的数据

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Renaming a file inside a folder inserts the renamed entry into mapFileEntry with its new strFullPath but leaves strFileName copied from the old entry. The nested archive view therefore displays the old filename after the rename until the archive is reopened and rebuilt from disk.

Triggers: When a file being renamed is not at the archive root.

Suggested fix: Set tmpEntry.strFileName to QFileInfo(strAlias).fileName() before inserting the renamed entry.

// return minizip_list();
}
// 判断是否追加到第一层数据
if (destinationPath.isEmpty() && ((entry.strFullPath.count('/') == 1 && entry.strFullPath.endsWith('/')) || entry.strFullPath.count('/') == 0)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): The Add path uses the literal '/' when deciding whether an entry belongs in listRootEntry, while archive paths are normalized with QDir::separator(). On Windows, nested entries use backslashes and count('/') is zero, so every added nested file or directory is treated as a root entry and appears at the wrong archive level in the refreshed view.

Triggers: When adding a folder tree to a ZIP archive on Windows with an empty destination path.

Suggested fix: Use QDir::separator() consistently, or use a platform-independent archive path separator when counting and testing archive paths.

Suggested change
if (destinationPath.isEmpty() && ((entry.strFullPath.count('/') == 1 && entry.strFullPath.endsWith('/')) || entry.strFullPath.count('/') == 0)) {
if (destinationPath.isEmpty() && ((entry.strFullPath.count(QDir::separator()) == 1 && entry.strFullPath.endsWith(QDir::separator())) || entry.strFullPath.count(QDir::separator()) == 0)) {

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

🤖 AI 代码审查报告

总体评分: 92 分 (通过阈值: 70分)

Pass


📊 总体评价

项目 结果
审查结论 代码审查通过
评分详情 总体评分 92 分,大于 70 分通过阈值。本次变更将 LibzipPlugin::updateArchiveData() 从磁盘重读改为内存级更新(Delete/Rename/Add),消除了 zip_open() 失败时错误处理被注释掉的脆弱性,与 LibarchivePlugin 行为保持一致。代码逻辑正确,无安全漏洞,存在少量代码质量问题可进一步优化。

🔍 详细分析

1. 语法逻辑 ✅

评价: 良好 ✅ 通过

潜在问题:

  1. 3rdparty/libzipplugin/libzipplugin.cpp:115 - 文件重命名场景下,map中的FileEntry::strFileName未更新为entry.strAlias,与目录重命名场景(行78-81已正确更新strFileName)不一致,可能导致UI显示旧文件名

建议: 在文件重命名的map插入前补充 tmpEntry.strFileName = entry.strAlias; 保持与目录重命名场景一致


2. 代码质量 ✅

评价: 良好 ✅ 通过

潜在问题:

  1. 3rdparty/libzipplugin/libzipplugin.cpp:39 - 根条目(listRootEntry)更新逻辑重复出现4处以上(行39-46, 51-58, 95-106, 118-129, 155-168),应抽取为辅助函数如updateRootEntry()
  2. 3rdparty/libzipplugin/libzipplugin.cpp:553 - updateArchiveData函数体约120行,超过100行建议上限,建议将Delete/Rename/Add三种操作拆分为独立函数

建议: 抽取 updateRootEntry(ArchiveData&, const FileEntry&, const QString&) 辅助函数消除重复代码;将 Delete/Rename/Add 操作拆分为独立私有函数提升可读性


3. 代码性能 ✅

评价: 优秀 ✅ 通过

潜在问题:
✅ 未发现明显问题

建议: 从磁盘I/O(zip_open/zip_close)改为纯内存操作,性能显著提升,算法复杂度合理


4. 代码安全 🔒

评价: 优秀 ✅ 通过

🔐 发现 0 个安全漏洞

安全漏洞详情:
✅ 未发现安全漏洞

建议: 代码仅操作内存数据结构(QMap/QList),无外部I/O或用户输入处理,无安全风险


💡 改进建议代码示例

// 建议修复:文件重命名时补充 strFileName 更新
// 位置:updateArchiveData() 文件重命名分支,约行115
} else { // 重命名文件
    stArchiveData.mapFileEntry.remove(entry.strFullPath);
    QString strPath = QFileInfo(entry.strFullPath).path();
    if(strPath == "." || strPath.isEmpty() || strPath.isNull()) {
        strAlias = entry.strAlias;
    } else {
        strAlias = strPath + QDir::separator() + entry.strAlias;
    }
    FileEntry tmpEntry = entry;
    tmpEntry.strFullPath = strAlias;
    tmpEntry.strFileName = entry.strAlias;  // 补充:更新文件名
    stArchiveData.mapFileEntry.insert(strAlias, tmpEntry);
    // ... 后续根条目更新逻辑
}

// 建议优化:抽取根条目更新辅助函数
void LibzipPlugin::updateRootEntry(ArchiveData &data, const QString &oldPath,
                                    const FileEntry &newEntry)
{
    for (int i = 0; i < data.listRootEntry.count(); i++) {
        if (data.listRootEntry.at(i).strFullPath == oldPath) {
            data.listRootEntry.removeAt(i);
            data.listRootEntry.append(newEntry);
            break;
        }
    }
}

本报告由 AI 代码审查工具自动生成

@deepin-bot

deepin-bot Bot commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

TAG Bot

New tag: 6.5.35
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #499

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.

2 participants