From 74c9798cad05d5e4797fcbfb3110ed49b9719b32 Mon Sep 17 00:00:00 2001 From: liuwenhuan Date: Mon, 9 Nov 2026 13:11:14 +0800 Subject: [PATCH 1/2] fix: reset a reused plugin's result when a job starts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. ArchiveManager reuses the plugin interface for later operations on the same archive. The finish type and the error type of a failed operation stayed on the interface, so the next operation could end with the old error: after a wrong password, extracting again with the right password reported "The archive is damaged". 2. ReadOnlyArchiveInterface::resetResult() clears the error type; CliInterface also clears the finish type. SingleJob::start() and UpdateJob::start() call it, once per job, so an error found earlier in the same job (for example ET_LongNameError before the long-name extraction refreshes the list) is kept. Log: Forget the result of the previous operation when a new one starts. Influence: 1. Extract an encrypted archive with a wrong password, then with the right one: the second extraction succeeds. 2. Extracting a file with a long name still reports the long-name case. fix: 任务开始时重置复用插件的结果 1. ArchiveManager 对同一压缩包的后续操作复用插件对象。失败操作的结束类型 和错误类型留在插件对象上,下一次操作可能带着旧错误结束:输错密码后再用 正确密码解压,提示"压缩包已损坏"。 2. ReadOnlyArchiveInterface::resetResult() 清除错误类型,CliInterface 还 清除结束类型。SingleJob::start() 和 UpdateJob::start() 每个任务调用一 次,因此同一任务中较早发现的错误(如长文件名解压刷新列表前设置的 ET_LongNameError)得以保留。 Log: 新操作开始时清除上一次操作的结果。 Influence: 1. 加密压缩包先输错密码再输对密码解压:第二次解压成功。 2. 解压长文件名文件仍按长文件名情况处理。 --- 3rdparty/interface/archiveinterface/archiveinterface.cpp | 5 +++++ 3rdparty/interface/archiveinterface/archiveinterface.h | 6 ++++++ 3rdparty/interface/archiveinterface/cliinterface.cpp | 6 ++++++ 3rdparty/interface/archiveinterface/cliinterface.h | 6 ++++++ src/source/archivemanager/singlejob.cpp | 8 ++++++++ 5 files changed, 31 insertions(+) diff --git a/3rdparty/interface/archiveinterface/archiveinterface.cpp b/3rdparty/interface/archiveinterface/archiveinterface.cpp index e953a4bbe..0f856ed28 100644 --- a/3rdparty/interface/archiveinterface/archiveinterface.cpp +++ b/3rdparty/interface/archiveinterface/archiveinterface.cpp @@ -76,6 +76,11 @@ ErrorType ReadOnlyArchiveInterface::errorType() return m_eErrorType; } +void ReadOnlyArchiveInterface::resetResult() +{ + m_eErrorType = ET_NoError; +} + bool ReadOnlyArchiveInterface::doKill() { return false; // 修改默认为未取消 diff --git a/3rdparty/interface/archiveinterface/archiveinterface.h b/3rdparty/interface/archiveinterface/archiveinterface.h index 6721fc703..8445eab61 100644 --- a/3rdparty/interface/archiveinterface/archiveinterface.h +++ b/3rdparty/interface/archiveinterface/archiveinterface.h @@ -116,6 +116,12 @@ class ReadOnlyArchiveInterface : public QObject */ ErrorType errorType(); + /** + * @brief resetResult forget the result of the previous operation; the interface is reused for + * later operations on the same archive, so a job starts with this + */ + virtual void resetResult(); + /** * @brief pauseOperation 暂停操作 */ diff --git a/3rdparty/interface/archiveinterface/cliinterface.cpp b/3rdparty/interface/archiveinterface/cliinterface.cpp index 0db6cc57f..78ab5d7ae 100644 --- a/3rdparty/interface/archiveinterface/cliinterface.cpp +++ b/3rdparty/interface/archiveinterface/cliinterface.cpp @@ -66,6 +66,12 @@ CliInterface::~CliInterface() deleteProcess(); } +void CliInterface::resetResult() +{ + ReadOnlyArchiveInterface::resetResult(); + m_finishType = PFT_Nomral; +} + PluginFinishType CliInterface::list() { setPassword(QString()); diff --git a/3rdparty/interface/archiveinterface/cliinterface.h b/3rdparty/interface/archiveinterface/cliinterface.h index 64913354f..d6441cbfc 100644 --- a/3rdparty/interface/archiveinterface/cliinterface.h +++ b/3rdparty/interface/archiveinterface/cliinterface.h @@ -66,6 +66,12 @@ class CliInterface : public ReadWriteArchiveInterface explicit CliInterface(QObject *parent, const QVariantList &args); ~CliInterface() override; + /** + * @brief resetResult also forget the previous finish type: an operation that succeeds after a + * failed one on the same archive must not end as an error + */ + void resetResult() override; + // ReadOnlyArchiveInterface interface public: PluginFinishType list() override; diff --git a/src/source/archivemanager/singlejob.cpp b/src/source/archivemanager/singlejob.cpp index 9cb2a0e6b..0b92ffef8 100644 --- a/src/source/archivemanager/singlejob.cpp +++ b/src/source/archivemanager/singlejob.cpp @@ -56,6 +56,10 @@ void SingleJob::start() return; } + // The interface is reused for later operations on the same archive: start without the result of + // the previous one (errors found during this job, e.g. while listing first, are kept) + m_pInterface->resetResult(); + // 判断是否通过线程的方式调用 if (m_pInterface->waitForFinished()) { qCDebug(appLog) << "Executing work directly"; @@ -532,6 +536,10 @@ void UpdateJob::start() return; } + // The interface is reused for later operations on the same archive: start without the result of + // the previous one (errors found during this job, e.g. while listing first, are kept) + m_pInterface->resetResult(); + d->start(); // 开启线程,执行操作 } From 84fb665cf54b3af5571f68128b050a0b5dc605df Mon Sep 17 00:00:00 2001 From: liuwenhuan Date: Mon, 9 Nov 2026 13:11:23 +0800 Subject: [PATCH 2/2] fix: report extraction failures the output parsing does not recognise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. An extraction with unrar or 7z ended as a success whenever the tool's output had no message the plugin knows, whatever the exit status. A RAR with a checksum error (unrar exits 3) ended on "Extraction successful". 7z on a full disk (exits 2) prints "errno=28 : ", which the plugin only knew in English; "Extract here" ended as a success, closed the window and left the partial files. 2. CliInterface::extractProcessFinished(): a normal exit with code 2 or more and no recognised error is a failure. The plugin names the error with errorTypeForExitCode() (unrar: 5 and 9 cannot write, 11 wrong password, otherwise the archive is damaged). Exit code 1 is "warnings" for both tools and the files were extracted, so it stays a success. 3. Cli7zPlugin recognises "errno=28" (ENOSPC) as a full disk and records the error. Log: Report damaged archives and a full disk instead of a successful extraction. Influence: 1. Extract a RAR with a corrupted volume: "The archive is damaged". 2. Extract a 7z, damaged: "The archive is damaged"; onto a full disk: "Insufficient disk space". 3. Normal RAR/7z extraction still succeeds. fix: 报告输出解析未识别的解压失败 1. unrar、7z 解压时,只要输出里没有插件认识的提示,不论退出码如何都按 成功处理。校验和错误的 RAR(unrar 退出码 3)显示"解压成功"。7z 磁盘 写满(退出码 2)输出"errno=28 : <本地语言的错误描述>",插件只认英文 描述;"解压到当前文件夹"按成功处理,关闭窗口并留下不完整的文件。 2. CliInterface::extractProcessFinished():正常退出、退出码 >= 2 且没有 识别出错误时按失败处理,错误类型由插件的 errorTypeForExitCode() 给出 (unrar:5、9 为写入失败,11 为密码错误,其余为压缩包损坏)。两个工具 的退出码 1 都表示警告,文件已解出,仍按成功处理。 3. Cli7zPlugin 把 "errno=28"(ENOSPC)识别为磁盘已满并记录错误。 Log: 压缩包损坏、磁盘已满时报告失败,而不是显示解压成功。 Influence: 1. 解压分卷损坏的 RAR:提示压缩包已损坏。 2. 解压损坏的 7z:提示压缩包已损坏;解压到已满的磁盘:提示磁盘空间不足。 3. 正常的 RAR/7z 解压仍然成功。 --- 3rdparty/cli7zplugin/cli7zplugin.cpp | 6 ++++-- 3rdparty/clirarplugin/clirarplugin.cpp | 13 +++++++++++++ 3rdparty/clirarplugin/clirarplugin.h | 5 +++++ .../interface/archiveinterface/cliinterface.cpp | 16 ++++++++++++++++ .../interface/archiveinterface/cliinterface.h | 6 ++++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/3rdparty/cli7zplugin/cli7zplugin.cpp b/3rdparty/cli7zplugin/cli7zplugin.cpp index e9cc4a9e2..2c6312d97 100644 --- a/3rdparty/cli7zplugin/cli7zplugin.cpp +++ b/3rdparty/cli7zplugin/cli7zplugin.cpp @@ -121,7 +121,8 @@ bool Cli7zPlugin::isCorruptArchiveMsg(const QString &line) bool Cli7zPlugin::isDiskFullMsg(const QString &line) { - return line.contains(QLatin1String("No space left on device")); + // 7-Zip prints strerror() in the user's language ("errno=28 : 设备上没有空间"); 28 is ENOSPC + return line.contains(QLatin1String("No space left on device")) || line.contains(QLatin1String("errno=28 ")); } bool Cli7zPlugin::isFileExistsMsg(const QString &line) @@ -365,7 +366,8 @@ bool Cli7zPlugin::handleLine(const QString &line, WorkType workStatus) } if (isDiskFullMsg(line)) { -// m_eErrorType = ; + m_eErrorType = ET_InsufficientDiskSpace; + m_finishType = PFT_Error; return false; } diff --git a/3rdparty/clirarplugin/clirarplugin.cpp b/3rdparty/clirarplugin/clirarplugin.cpp index 61f4ccec7..ba175a1da 100644 --- a/3rdparty/clirarplugin/clirarplugin.cpp +++ b/3rdparty/clirarplugin/clirarplugin.cpp @@ -157,6 +157,19 @@ bool CliRarPlugin::isOpenFileFailed(const QString &line) return line.startsWith("Cannot create "); } +ErrorType CliRarPlugin::errorTypeForExitCode(int exitCode) const +{ + switch (exitCode) { + case 5: // write error + case 9: // cannot create a file + return ET_FileWriteError; + case 11: // wrong password + return ET_WrongPassword; + default: // 3 checksum error, 2 fatal error, ... + return ET_ArchiveDamaged; + } +} + void CliRarPlugin::killProcess(bool emitFinished) { Q_UNUSED(emitFinished); diff --git a/3rdparty/clirarplugin/clirarplugin.h b/3rdparty/clirarplugin/clirarplugin.h index 5f98b540f..2c83d5108 100644 --- a/3rdparty/clirarplugin/clirarplugin.h +++ b/3rdparty/clirarplugin/clirarplugin.h @@ -69,6 +69,11 @@ class CliRarPlugin : public CliInterface void killProcess(bool emitFinished) override; + /** + * @brief errorTypeForExitCode unrar: 5 write error, 9 cannot create a file, 11 wrong password + */ + ErrorType errorTypeForExitCode(int exitCode) const override; + private: ParseState m_parseState = ParseStateTitle; FileEntry m_fileEntry; diff --git a/3rdparty/interface/archiveinterface/cliinterface.cpp b/3rdparty/interface/archiveinterface/cliinterface.cpp index 78ab5d7ae..7ea39e4c1 100644 --- a/3rdparty/interface/archiveinterface/cliinterface.cpp +++ b/3rdparty/interface/archiveinterface/cliinterface.cpp @@ -1494,6 +1494,12 @@ void CliInterface::processFinished(int exitCode, QProcess::ExitStatus exitStatus emit signalFinished(m_finishType); } +ErrorType CliInterface::errorTypeForExitCode(int exitCode) const +{ + Q_UNUSED(exitCode); + return ET_ArchiveDamaged; +} + void CliInterface::extractProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { qCInfo(ifaceLog) << "Extraction process finished, exitcode:" << exitCode << " exitstatus:" << exitStatus; @@ -1505,6 +1511,16 @@ void CliInterface::extractProcessFinished(int exitCode, QProcess::ExitStatus exi m_finishType = PFT_Nomral; } + // A failure the output parsing did not recognise is still a failure: unrar exits 3 after a + // checksum error and 5 when it cannot write, 7z exits 2 after any fatal error. Exit code 1 is + // "warnings" for both (the files were extracted), so it stays a success. + if (QProcess::NormalExit == exitStatus && exitCode >= 2 && PFT_Nomral == m_finishType) { + m_finishType = PFT_Error; + if (ET_NoError == m_eErrorType) { + m_eErrorType = errorTypeForExitCode(exitCode); + } + } + m_indexOfListRootEntry = 0; m_isEmptyArchive = false; diff --git a/3rdparty/interface/archiveinterface/cliinterface.h b/3rdparty/interface/archiveinterface/cliinterface.h index d6441cbfc..b2d4546b3 100644 --- a/3rdparty/interface/archiveinterface/cliinterface.h +++ b/3rdparty/interface/archiveinterface/cliinterface.h @@ -149,6 +149,12 @@ class CliInterface : public ReadWriteArchiveInterface */ virtual void killProcess(bool emitFinished = true) = 0; + /** + * @brief errorTypeForExitCode the error of an extraction that exited with this code (2 or more) + * although its output showed no error the plugin recognises + */ + virtual ErrorType errorTypeForExitCode(int exitCode) const; + /** * @brief handleProgress 解析进度并发送进度信号 * @param line