Skip to content

cherry-pick 2 release/eagle commits to develop/snipe#367

Merged
deepin-bot[bot] merged 2 commits intolinuxdeepin:develop/snipefrom
LiHua000:develop/snipe
Mar 30, 2026
Merged

cherry-pick 2 release/eagle commits to develop/snipe#367
deepin-bot[bot] merged 2 commits intolinuxdeepin:develop/snipefrom
LiHua000:develop/snipe

Conversation

@LiHua000
Copy link
Copy Markdown
Contributor

@LiHua000 LiHua000 commented Feb 26, 2026

1 fix: [unrar] can not extract rar files with longFilenames

2 fix: remove empty files left when split-volume encrypted extraction fails (e.g. wrong password)

Summary by Sourcery

Improve archive extraction robustness for long file names and failed encrypted extractions.

Bug Fixes:

  • Prevent long-filename extraction workflow from running when the configured move program is unavailable, avoiding unrar failures on long paths.
  • Remove leftover zero-size files and empty directories when full extraction fails (e.g. wrong password on split-volume encrypted archives).

Enhancements:

  • Add a capability check for the external move program before performing long-filename handling, with clear diagnostic logging when it is missing.
  • Ensure progress is reported as complete before aborting extraction when command output handling fails.

@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
Copy link
Copy Markdown

sourcery-ai bot commented Feb 26, 2026

Reviewer's Guide

Adds safety checks and cleanup around CLI-based archive extraction: long-filename handling is disabled when the configured move program is unavailable, extraction failure now triggers cleanup of zero-sized files and empty directories, and progress is forced to 100% on early termination so UIs complete correctly.

Updated class diagram for CliInterface extraction-related methods

classDiagram
    class ReadWriteArchiveInterface

    class CliInterface {
        +PluginFinishType extractFiles(files, options)
        +bool moveExtractTempFilesToDest(files, options)
        +void removeExtractedFilesOnFailure(strTargetPath, entries)
        +bool handleLongNameExtract(files)
        +bool checkMoveCapability()
        +void readStdout(handleAll)
        +void extractProcessFinished(exitCode, exitStatus)
    }

    CliInterface --|> ReadWriteArchiveInterface
Loading

File-Level Changes

Change Details Files
Guard long-filename extraction logic by ensuring the configured move program is available before attempting special handling.
  • After detecting long filenames, call a new capability check before enabling long-name handling.
  • If the move program is missing, log detailed warnings including the archive format and disable long-name handling to avoid using an unsupported path.
  • Apply this guard both in the regular extract path and in the password-handling branch so behavior is consistent.
3rdparty/interface/archiveinterface/cliinterface.cpp
Introduce utilities to check move capability and clean up residual extracted files on extraction failure, and wire them into the extract process lifecycle.
  • Add checkMoveCapability() to resolve the configured moveProgram with QStandardPaths::findExecutable and return whether it exists.
  • Add removeExtractedFilesOnFailure() to delete zero-length files and recursively remove now-empty directories under the extraction target, using either the current entries or all archive entries if none are passed.
  • On extract process failure for full extraction into a real target path, call removeExtractedFilesOnFailure() to clean up residue such as empty files from failed split-volume encrypted extraction.
  • On line-handling failure while reading stdout, emit a final 100% progress signal before killing the process so the UI can conclude the operation.
3rdparty/interface/archiveinterface/cliinterface.cpp
3rdparty/interface/archiveinterface/cliinterface.h

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 3 issues, and left some high level feedback:

  • The bHandleLongName && !checkMoveCapability() block (including repeated warning strings) appears twice in extractFiles; consider refactoring into a small helper to avoid duplication and keep the behavior consistent in one place.
  • Emitting signalprogress(100) immediately before killing the process on handleLine failure may be misleading for callers that interpret 100% as successful completion; consider distinguishing failure completion from success in the progress reporting.
  • In removeExtractedFilesOnFailure, looping repeatedly over the same paths list to find empty directories can be inefficient for large archives; you might consider tracking directories in a separate structure and walking them bottom-up to minimize repeated checks.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `bHandleLongName && !checkMoveCapability()` block (including repeated warning strings) appears twice in `extractFiles`; consider refactoring into a small helper to avoid duplication and keep the behavior consistent in one place.
- Emitting `signalprogress(100)` immediately before killing the process on `handleLine` failure may be misleading for callers that interpret 100% as successful completion; consider distinguishing failure completion from success in the progress reporting.
- In `removeExtractedFilesOnFailure`, looping repeatedly over the same `paths` list to find empty directories can be inefficient for large archives; you might consider tracking directories in a separate structure and walking them bottom-up to minimize repeated checks.

## Individual Comments

### Comment 1
<location path="3rdparty/interface/archiveinterface/cliinterface.cpp" line_range="207" />
<code_context>
+
+        if (bHandleLongName && !checkMoveCapability()) {
+            qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
+            qWarning() << "Archive format:" << m_mimetype.name() << "Skipping long name handling.";
+            qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
+            bHandleLongName = false;
</code_context>
<issue_to_address>
**suggestion:** Consider fixing the missing space and reducing duplication of this warning block.

The log currently joins `m_mimetype.name()` and `"Skipping long name handling."` without a space, yielding output like `"zipSkipping long name handling."`. Also, the `bHandleLongName && !checkMoveCapability()` block (including the warning and `bHandleLongName = false`) appears multiple times in `extractFiles`; consider a small helper or shared formatter so the behavior and message stay consistent across call sites.

Suggested implementation:

```cpp
        if (bHandleLongName && !checkMoveCapability()) {
            qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
            qWarning() << "Archive format:" << m_mimetype.name() << " Skipping long name handling.";
            qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
            bHandleLongName = false;
        }

```

To fully address the duplication concern across `extractFiles`, you can introduce a small helper (either a `static` function in an anonymous namespace in `cliinterface.cpp` or a private member on the relevant class), e.g.:

```cpp
static void logLongNameWithoutMoveProgram(const QMimeType &mimeType, const QVariant &moveProgramProperty)
{
    const QString moveProgram = moveProgramProperty.toString();
    qWarning() << "Long filename detected, but moveProgram (" << moveProgram << ") is not available.";
    qWarning() << "Archive format:" << mimeType.name() << " Skipping long name handling.";
    qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
}
```

Then replace each duplicated block of:

```cpp
if (bHandleLongName && !checkMoveCapability()) {
    qWarning() << ...;
    qWarning() << ...;
    qWarning() << ...;
    bHandleLongName = false;
}
```

with:

```cpp
if (bHandleLongName && !checkMoveCapability()) {
    logLongNameWithoutMoveProgram(m_mimetype, m_cliProps->property("moveProgram"));
    bHandleLongName = false;
}
```

so all call sites share the same, correctly formatted message.
</issue_to_address>

### Comment 2
<location path="3rdparty/interface/archiveinterface/cliinterface.cpp" line_range="1165" />
<code_context>
+        if (relPath.isEmpty()) {
+            continue;
+        }
+        paths.append(qMakePair(targetDir.absoluteFilePath(relPath), entry.isDirectory));
+    }
+
</code_context>
<issue_to_address>
**🚨 issue (security):** Using `absoluteFilePath(relPath)` directly may allow `..` segments to escape `strTargetPath`.

If `relPath` contains `..` components (e.g. from a crafted archive), `targetDir.absoluteFilePath(relPath)` may resolve outside `strTargetPath`, and `removeExtractedFilesOnFailure` could delete files or directories beyond the intended root. Consider normalizing and validating `relPath` (e.g. rejecting `..` or verifying the resolved path is under `targetDir.canonicalPath()`) before adding it to `paths`.
</issue_to_address>

### Comment 3
<location path="3rdparty/interface/archiveinterface/cliinterface.cpp" line_range="205" />
<code_context>
             }
         }
+
+        if (bHandleLongName && !checkMoveCapability()) {
+            qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
+            qWarning() << "Archive format:" << m_mimetype.name() << "Skipping long name handling.";
</code_context>
<issue_to_address>
**issue (complexity):** Consider extracting the duplicated long-filename/moveProgram check into a helper and simplifying the cleanup routine into separate file/dir phases to make the logic clearer and easier to maintain.

You can reduce duplication and simplify control flow in two spots without changing behavior.

### 1) Consolidate `bHandleLongName && !checkMoveCapability()` logic

You now have the same warning + flag-reset logic twice. Extracting a helper keeps behavior identical and makes future changes safer:

```cpp
// CliInterface.h
class CliInterface : public QObject {
    ...
private:
    void ensureMoveCapabilityForLongNames(bool &bHandleLongName);
    ...
};
```

```cpp
// CliInterface.cpp
void CliInterface::ensureMoveCapabilityForLongNames(bool &bHandleLongName)
{
    if (!bHandleLongName) {
        return;
    }
    if (checkMoveCapability()) {
        return;
    }

    qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
    qWarning() << "Archive format:" << m_mimetype.name() << "Skipping long name handling.";
    qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
    bHandleLongName = false;
}
```

Then replace both duplicated blocks with:

```cpp
// first location
if (!bLnfs) {
    ...
}
ensureMoveCapabilityForLongNames(bHandleLongName);

// second location
if (!bLnfs) {
    ...
}
ensureMoveCapabilityForLongNames(bHandleLongName);
```

This keeps the timing of the check the same while centralizing the messages and policy.

---

### 2) Simplify `removeExtractedFilesOnFailure`

You can avoid the `QList<QPair<QString,bool>>` and the do/while by separating file and directory paths and processing them in two clearly named phases:

```cpp
void CliInterface::removeExtractedFilesOnFailure(const QString &strTargetPath,
                                                 const QList<FileEntry> &entries)
{
    QList<FileEntry> listToRemove = entries;
    if (listToRemove.isEmpty()) {
        listToRemove = DataManager::get_instance().archiveData().mapFileEntry.values();
    }
    if (listToRemove.isEmpty()) {
        return;
    }

    QDir targetDir(strTargetPath);
    if (!targetDir.exists()) {
        return;
    }

    QStringList filePaths;
    QStringList dirPaths;
    for (const FileEntry &entry : listToRemove) {
        QString relPath = entry.strFullPath;
        if (relPath.endsWith(QLatin1Char('/'))) {
            relPath.chop(1);
        }
        if (relPath.isEmpty()) {
            continue;
        }

        const QString absPath = targetDir.absoluteFilePath(relPath);
        if (entry.isDirectory) {
            dirPaths << absPath;
        } else {
            filePaths << absPath;
        }
    }

    // remove zero-size files
    for (const QString &path : filePaths) {
        QFileInfo fi(path);
        if (fi.exists() && fi.isFile() && fi.size() == 0) {
            QFile::remove(path);
        }
    }

    // remove empty directories bottom-up
    // (one pass is usually enough; if you want to be extra safe, you can loop,
    // but in a helper with a clear name)
    std::sort(dirPaths.begin(), dirPaths.end(),
              [](const QString &a, const QString &b) { return a.length() > b.length(); });
    for (const QString &dirPath : dirPaths) {
        QDir d(dirPath);
        if (d.exists() && d.isEmpty()) {
            d.removeRecursively();
        }
    }
}
```

This keeps the functionality (cleaning up zero-size files and empty directories under the target) but reduces cognitive load by:

- Removing the `QPair<QString,bool>` indirection.
- Making the “file pass” and “directory pass” explicit.
- Using a length-based sort for directories to achieve a bottom‑up removal instead of a `do/while removed` loop.
</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.


if (bHandleLongName && !checkMoveCapability()) {
qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
qWarning() << "Archive format:" << m_mimetype.name() << "Skipping long name handling.";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Consider fixing the missing space and reducing duplication of this warning block.

The log currently joins m_mimetype.name() and "Skipping long name handling." without a space, yielding output like "zipSkipping long name handling.". Also, the bHandleLongName && !checkMoveCapability() block (including the warning and bHandleLongName = false) appears multiple times in extractFiles; consider a small helper or shared formatter so the behavior and message stay consistent across call sites.

Suggested implementation:

        if (bHandleLongName && !checkMoveCapability()) {
            qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
            qWarning() << "Archive format:" << m_mimetype.name() << " Skipping long name handling.";
            qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
            bHandleLongName = false;
        }

To fully address the duplication concern across extractFiles, you can introduce a small helper (either a static function in an anonymous namespace in cliinterface.cpp or a private member on the relevant class), e.g.:

static void logLongNameWithoutMoveProgram(const QMimeType &mimeType, const QVariant &moveProgramProperty)
{
    const QString moveProgram = moveProgramProperty.toString();
    qWarning() << "Long filename detected, but moveProgram (" << moveProgram << ") is not available.";
    qWarning() << "Archive format:" << mimeType.name() << " Skipping long name handling.";
    qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
}

Then replace each duplicated block of:

if (bHandleLongName && !checkMoveCapability()) {
    qWarning() << ...;
    qWarning() << ...;
    qWarning() << ...;
    bHandleLongName = false;
}

with:

if (bHandleLongName && !checkMoveCapability()) {
    logLongNameWithoutMoveProgram(m_mimetype, m_cliProps->property("moveProgram"));
    bHandleLongName = false;
}

so all call sites share the same, correctly formatted message.

if (relPath.isEmpty()) {
continue;
}
paths.append(qMakePair(targetDir.absoluteFilePath(relPath), entry.isDirectory));
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 (security): Using absoluteFilePath(relPath) directly may allow .. segments to escape strTargetPath.

If relPath contains .. components (e.g. from a crafted archive), targetDir.absoluteFilePath(relPath) may resolve outside strTargetPath, and removeExtractedFilesOnFailure could delete files or directories beyond the intended root. Consider normalizing and validating relPath (e.g. rejecting .. or verifying the resolved path is under targetDir.canonicalPath()) before adding it to paths.

}
}

if (bHandleLongName && !checkMoveCapability()) {
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 (complexity): Consider extracting the duplicated long-filename/moveProgram check into a helper and simplifying the cleanup routine into separate file/dir phases to make the logic clearer and easier to maintain.

You can reduce duplication and simplify control flow in two spots without changing behavior.

1) Consolidate bHandleLongName && !checkMoveCapability() logic

You now have the same warning + flag-reset logic twice. Extracting a helper keeps behavior identical and makes future changes safer:

// CliInterface.h
class CliInterface : public QObject {
    ...
private:
    void ensureMoveCapabilityForLongNames(bool &bHandleLongName);
    ...
};
// CliInterface.cpp
void CliInterface::ensureMoveCapabilityForLongNames(bool &bHandleLongName)
{
    if (!bHandleLongName) {
        return;
    }
    if (checkMoveCapability()) {
        return;
    }

    qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
    qWarning() << "Archive format:" << m_mimetype.name() << "Skipping long name handling.";
    qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
    bHandleLongName = false;
}

Then replace both duplicated blocks with:

// first location
if (!bLnfs) {
    ...
}
ensureMoveCapabilityForLongNames(bHandleLongName);

// second location
if (!bLnfs) {
    ...
}
ensureMoveCapabilityForLongNames(bHandleLongName);

This keeps the timing of the check the same while centralizing the messages and policy.


2) Simplify removeExtractedFilesOnFailure

You can avoid the QList<QPair<QString,bool>> and the do/while by separating file and directory paths and processing them in two clearly named phases:

void CliInterface::removeExtractedFilesOnFailure(const QString &strTargetPath,
                                                 const QList<FileEntry> &entries)
{
    QList<FileEntry> listToRemove = entries;
    if (listToRemove.isEmpty()) {
        listToRemove = DataManager::get_instance().archiveData().mapFileEntry.values();
    }
    if (listToRemove.isEmpty()) {
        return;
    }

    QDir targetDir(strTargetPath);
    if (!targetDir.exists()) {
        return;
    }

    QStringList filePaths;
    QStringList dirPaths;
    for (const FileEntry &entry : listToRemove) {
        QString relPath = entry.strFullPath;
        if (relPath.endsWith(QLatin1Char('/'))) {
            relPath.chop(1);
        }
        if (relPath.isEmpty()) {
            continue;
        }

        const QString absPath = targetDir.absoluteFilePath(relPath);
        if (entry.isDirectory) {
            dirPaths << absPath;
        } else {
            filePaths << absPath;
        }
    }

    // remove zero-size files
    for (const QString &path : filePaths) {
        QFileInfo fi(path);
        if (fi.exists() && fi.isFile() && fi.size() == 0) {
            QFile::remove(path);
        }
    }

    // remove empty directories bottom-up
    // (one pass is usually enough; if you want to be extra safe, you can loop,
    // but in a helper with a clear name)
    std::sort(dirPaths.begin(), dirPaths.end(),
              [](const QString &a, const QString &b) { return a.length() > b.length(); });
    for (const QString &dirPath : dirPaths) {
        QDir d(dirPath);
        if (d.exists() && d.isEmpty()) {
            d.removeRecursively();
        }
    }
}

This keeps the functionality (cleaning up zero-size files and empty directories under the target) but reduces cognitive load by:

  • Removing the QPair<QString,bool> indirection.
  • Making the “file pass” and “directory pass” explicit.
  • Using a length-based sort for directories to achieve a bottom‑up removal instead of a do/while removed loop.

@deepin-bot
Copy link
Copy Markdown
Contributor

deepin-bot bot commented Mar 5, 2026

TAG Bot

New tag: 6.5.23
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #369

@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这段代码主要增加了对解压失败时的清理功能、长文件名处理能力的检查,以及一些错误处理逻辑。以下是对代码的详细审查和改进建议:

1. 代码逻辑与结构

1.1 重复代码块

extractFiles 函数中,以下代码块出现了两次(第204-209行和第262-267行):

if (bHandleLongName && !checkMoveCapability()) {
    qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
    qWarning() << "Archive format:" << m_mimetype.name() << "Skipping long name handling.";
    qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
    bHandleLongName = false;
}

改进建议:将这段代码提取为一个单独的函数或内联函数,避免重复。例如:

void CliInterface::handleLongNameCapability(bool &bHandleLongName) {
    if (bHandleLongName && !checkMoveCapability()) {
        qWarning() << "Long filename detected, but moveProgram (" << m_cliProps->property("moveProgram").toString() << ") is not available.";
        qWarning() << "Archive format:" << m_mimetype.name() << "Skipping long name handling.";
        qWarning() << "The extraction tool will report errors for files with names exceeding system limit (255 bytes).";
        bHandleLongName = false;
    }
}

1.2 removeExtractedFilesOnFailure 函数逻辑

该函数在解压失败时清理已生成的文件,但有以下问题:

  • 文件清理条件:只删除大小为0的文件(fi.size() == 0),这可能不够全面。如果解压过程中生成了部分内容的文件,这些文件不会被清理。
  • 目录清理逻辑:使用 do-while 循环清理空目录,但效率较低,特别是对于深层目录结构。

改进建议

void CliInterface::removeExtractedFilesOnFailure(const QString &strTargetPath, const QList<FileEntry> &entries)
{
    QList<FileEntry> listToRemove = entries;
    if (listToRemove.isEmpty()) {
        listToRemove = DataManager::get_instance().archiveData().mapFileEntry.values();
    }
    if (listToRemove.isEmpty()) {
        return;
    }

    QDir targetDir(strTargetPath);
    if (!targetDir.exists()) {
        return;
    }

    // 收集所有路径
    QSet<QString> filePaths, dirPaths;
    for (const FileEntry &entry : listToRemove) {
        QString relPath = entry.strFullPath;
        if (relPath.endsWith(QLatin1Char('/'))) {
            relPath.chop(1);
        }
        if (relPath.isEmpty()) {
            continue;
        }
        QString fullPath = targetDir.absoluteFilePath(relPath);
        if (entry.isDirectory) {
            dirPaths.insert(fullPath);
        } else {
            filePaths.insert(fullPath);
        }
    }

    // 删除文件(包括部分解压的文件)
    for (const QString &path : filePaths) {
        QFileInfo fi(path);
        if (fi.exists() && fi.isFile()) {
            QFile::remove(path);
        }
    }

    // 删除空目录(按深度从深到浅)
    QStringList sortedDirs = dirPaths.toList();
    std::sort(sortedDirs.begin(), sortedDirs.end(), [](const QString &a, const QString &b) {
        return a.count('/') > b.count('/');
    });
    for (const QString &path : sortedDirs) {
        QDir d(path);
        if (d.exists() && d.isEmpty()) {
            d.removeRecursively();
        }
    }
}

2. 代码性能

2.1 checkMoveCapability 函数

该函数每次调用都会执行 QStandardPaths::findExecutable,这在频繁调用时可能影响性能。

改进建议:缓存 moveProgram 的路径,避免重复查找。例如:

bool CliInterface::checkMoveCapability()
{
    static QString cachedMoveProgramPath;
    static bool isCached = false;
    
    if (!isCached) {
        QString moveProgram = m_cliProps->property("moveProgram").toString();
        if (!moveProgram.isEmpty()) {
            cachedMoveProgramPath = QStandardPaths::findExecutable(moveProgram);
        }
        isCached = true;
    }
    
    return !cachedMoveProgramPath.isEmpty();
}

2.2 removeExtractedFilesOnFailure 函数

原代码中的 do-while 循环效率较低,改进后的版本通过排序和单次遍历提高了效率。

3. 代码安全

3.1 路径遍历风险

removeExtractedFilesOnFailure 函数中直接使用用户提供的路径(strTargetPath)和压缩包中的路径(entry.strFullPath)组合,可能存在路径遍历风险。

改进建议:添加路径验证,确保路径在目标目录内:

QString CliInterface::sanitizePath(const QString &basePath, const QString &relPath)
{
    QString fullPath = QDir(basePath).absoluteFilePath(relPath);
    QString normalizedBase = QDir(basePath).canonicalPath();
    QString normalizedFull = QDir(fullPath).canonicalPath();
    
    if (normalizedFull.startsWith(normalizedBase + QLatin1Char('/'))) {
        return normalizedFull;
    }
    return QString(); // 返回空字符串表示路径不安全
}

3.2 临时目录检查

代码中对临时目录的检查(destPath.startsWith("/tmp") && destPath.contains("/deepin-compressor-"))不够健壮,可能在不同系统上失效。

改进建议:使用 QTemporaryDir 或更通用的临时目录检查方法。

4. 其他建议

4.1 错误日志

checkMoveCapability 中,如果 moveProgram 不可用,建议添加日志输出:

bool CliInterface::checkMoveCapability()
{
    QString moveProgram = m_cliProps->property("moveProgram").toString();
    if (moveProgram.isEmpty()) {
        qWarning() << "No move program configured";
        return false;
    }
    
    QString moveProgramPath = QStandardPaths::findExecutable(moveProgram);
    if (moveProgramPath.isEmpty()) {
        qWarning() << "Move program not found:" << moveProgram;
        return false;
    }
    
    return true;
}

4.2 信号发射

readStdout 中添加了 emit signalprogress(100),但需要确保这个信号不会在错误情况下误导用户。建议检查上下文,确保这是合理的行为。

总结

这段代码在功能上是正确的,但在代码重复、性能优化和安全性方面有改进空间。建议:

  1. 提取重复代码块为独立函数
  2. 优化文件和目录清理逻辑
  3. 添加路径验证防止路径遍历
  4. 缓存 moveProgram 查找结果
  5. 改进临时目录检查方法

@LiHua000
Copy link
Copy Markdown
Contributor Author

/merge

@deepin-bot deepin-bot bot merged commit b5ab5a3 into linuxdeepin:develop/snipe Mar 30, 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.

2 participants