Skip to content

Commit 0cea7c5

Browse files
LiHua000deepin-bot[bot]
authored andcommitted
fix(archive): tar.7z use QProcess pipeline instead of shell script
- Replace temp script with two QProcess (tar | 7z) to avoid command injection and special chars in password breaking the command - Add CliProperties::getTar7zTarArgs/getTar7z7zArgs for safe argv - Pass 7z password as single "-p"+password to avoid stdin prompt freeze - Add killTar7zPipelineIfActive() and writeToProcess guard in pipeline - cli7zplugin: call killTar7zPipelineIfActive() in killProcess() Log: 修复 tar.7z 使用脚本导致的命令注入与密码特殊字符问题,改为双 QProcess 管道并避免加密时卡死 Bug:https://pms.uniontech.com/bug-view-343119.html
1 parent 2aba266 commit 0cea7c5

5 files changed

Lines changed: 182 additions & 120 deletions

File tree

3rdparty/cli7zplugin/cli7zplugin.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ bool Cli7zPlugin::isOpenFileFailed(const QString &line)
151151
void Cli7zPlugin::killProcess(bool emitFinished)
152152
{
153153
Q_UNUSED(emitFinished);
154+
if (killTar7zPipelineIfActive()) {
155+
return;
156+
}
154157
if (!m_process) {
155158
return;
156159
}

3rdparty/interface/archiveinterface/cliinterface.cpp

Lines changed: 97 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -470,23 +470,47 @@ PluginFinishType CliInterface::addFiles(const QList<FileEntry> &files, const Com
470470
QFileInfo(m_strArchiveName).path(),
471471
sRenameList);
472472

473-
if (options.bTar_7z) { // 压缩tar.7z文件
473+
if (options.bTar_7z) { // 压缩tar.7z:用两个 QProcess 管道,避免 shell 拼接与密码特殊字符问题
474474
m_isTar7z = true;
475-
m_filesSize = options.qTotalSize; // 待压缩文件总大小
476-
m_scriptPath = QDir::tempPath() + "/tempScript_" + QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + ".sh";
477-
QFile scriptFile(m_scriptPath);
478-
if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
479-
QTextStream out(&scriptFile);
480-
out << "#!/bin/bash\n";
481-
for (const QString &arg : arguments) {
482-
out << arg << "\n";
483-
}
484-
scriptFile.close();
485-
QProcess::execute("chmod", { "+x", m_scriptPath });
486-
ret = runProcess(m_scriptPath, QStringList());
487-
} else {
488-
qWarning() << "Failed to create temporary script file.";
475+
m_filesSize = options.qTotalSize;
476+
const QString archivePath = temp_archiveName.isEmpty() ? m_strArchiveName : temp_archiveName;
477+
QStringList tarArgs = m_cliProps->getTar7zTarArgs(fileList, sRenameList);
478+
QStringList sevenZArgs = m_cliProps->getTar7z7zArgs(archivePath, password, options.bHeaderEncryption,
479+
options.iCompressionLevel, options.strCompressionMethod,
480+
options.strEncryptionMethod, options.iVolumeSize);
481+
QString tarPath = QStandardPaths::findExecutable(QStringLiteral("tar"));
482+
QString sevenZPath = QStandardPaths::findExecutable(m_cliProps->property("addProgram").toString());
483+
if (tarPath.isEmpty() || sevenZPath.isEmpty()) {
489484
ret = false;
485+
} else {
486+
m_tar7z_7z = new QProcess();
487+
m_tarProcess = new QProcess();
488+
m_tarProcess->setStandardOutputProcess(m_tar7z_7z);
489+
m_tar7z_7z->setProcessChannelMode(QProcess::MergedChannels);
490+
connect(m_tar7z_7z, &QProcess::readyReadStandardOutput, this, [this]() { readStdout(); });
491+
connect(m_tar7z_7z, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &CliInterface::processFinished);
492+
m_stdOutData.clear();
493+
m_isProcessKilled = false;
494+
m_tarProcess->start(tarPath, tarArgs);
495+
if (m_tarProcess->waitForStarted(5000)) {
496+
m_tar7z_7z->start(sevenZPath, sevenZArgs);
497+
// 不在此处 waitForStarted(7z),避免 7z 等 stdin 时阻塞主线程导致卡死
498+
ret = m_tar7z_7z->state() == QProcess::Starting || m_tar7z_7z->state() == QProcess::Running;
499+
if (ret) {
500+
m_processId = m_tar7z_7z->processId();
501+
m_childProcessId = QVector<qint64>() << m_tarProcess->processId();
502+
}
503+
connect(m_tar7z_7z, &QProcess::errorOccurred, this, [this](QProcess::ProcessError) {
504+
if (m_tar7z_7z && m_tar7z_7z->state() == QProcess::NotRunning) {
505+
processFinished(-1, QProcess::CrashExit);
506+
}
507+
});
508+
} else {
509+
ret = false;
510+
}
511+
if (!ret) {
512+
deleteProcess();
513+
}
490514
}
491515
} else {
492516
QString processName = m_cliProps->property("addProgram").toString();
@@ -507,7 +531,11 @@ PluginFinishType CliInterface::addFiles(const QList<FileEntry> &files, const Com
507531
qInfo() << "mtp 压缩完成,现在开始移动";
508532
QStringList args_list;
509533
args_list << temp_archiveName << m_strArchiveName;
510-
m_process->waitForFinished();
534+
if (m_tar7z_7z) {
535+
m_tar7z_7z->waitForFinished(-1);
536+
} else if (m_process) {
537+
m_process->waitForFinished();
538+
}
511539
QProcess mover;
512540
ret = 0 == mover.execute("mv", args_list);
513541
ret = mover.exitCode() == QProcess::NormalExit;
@@ -795,22 +823,53 @@ bool CliInterface::runProcess(const QString &programName, const QStringList &arg
795823
return true;
796824
}
797825

826+
bool CliInterface::killTar7zPipelineIfActive()
827+
{
828+
if (!m_tar7z_7z) {
829+
return false;
830+
}
831+
if (m_tarProcess && m_tarProcess->state() != QProcess::NotRunning) {
832+
m_tarProcess->kill();
833+
}
834+
if (m_tar7z_7z->state() != QProcess::NotRunning) {
835+
m_tar7z_7z->kill();
836+
}
837+
m_isProcessKilled = true;
838+
return true;
839+
}
840+
798841
void CliInterface::deleteProcess()
799842
{
843+
if (m_tar7z_7z) {
844+
m_tar7z_7z->blockSignals(true);
845+
if (m_tar7z_7z->state() != QProcess::NotRunning) {
846+
m_tar7z_7z->kill();
847+
m_tar7z_7z->waitForFinished(500);
848+
}
849+
delete m_tar7z_7z;
850+
m_tar7z_7z = nullptr;
851+
if (m_tarProcess) {
852+
m_tarProcess->blockSignals(true);
853+
if (m_tarProcess->state() != QProcess::NotRunning) {
854+
m_tarProcess->kill();
855+
m_tarProcess->waitForFinished(500);
856+
}
857+
delete m_tarProcess;
858+
m_tarProcess = nullptr;
859+
}
860+
return;
861+
}
800862
if (m_process) {
801863
readStdout(true);
802864
m_process->blockSignals(true); // delete m_process之前需要断开所有m_process信号,防止重复处理
803865
delete m_process;
804866
m_process = nullptr;
805-
if(!m_scriptPath.isEmpty()) {
806-
QFile::remove(m_scriptPath);
807-
}
808867
}
809868
}
810869

811870
void CliInterface::handleProgress(const QString &line)
812871
{
813-
if (m_process && m_process->program().at(0).contains("7z")) { // 解析7z相关进度、文件名
872+
if (m_tar7z_7z || (m_process && m_process->program().at(0).contains("7z"))) { // 解析7z相关进度、文件名(含 tar.7z 管道)
814873
int pos = line.indexOf(QLatin1Char('%'));
815874
if (pos > 1) {
816875
int percentage = line.midRef(pos - 3, 3).toInt();
@@ -904,24 +963,6 @@ void CliInterface::handleProgress(const QString &line)
904963

905964
emit signalCurFileName(fileName);
906965
}
907-
} else if (m_process && m_process->program().at(0).contains("tempScript")) {
908-
// 处理tar.7z进度
909-
// "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b 7M + [Content]"
910-
// "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b274M 1 + [Content]"
911-
int pos = line.lastIndexOf(" + [Content]");
912-
if (pos > 1) {
913-
int mPos = line.lastIndexOf("M ");
914-
int bPos = line.lastIndexOf("\b", mPos);
915-
QString tempLine = line.left(mPos);
916-
// 已经压缩的文件大小
917-
qint64 compressedSize = tempLine.right(tempLine.size() - bPos - 1).toLongLong();
918-
// 计算文件大小计算百分比
919-
qint64 percentage = compressedSize * 1024 * 1024 * 100 / m_filesSize;
920-
921-
emit signalprogress(percentage);
922-
// 无法获取正在压缩的某个文件名
923-
// emit signalCurFileName();
924-
}
925966
}
926967
}
927968

@@ -1031,9 +1072,14 @@ PluginFinishType CliInterface::handleCorrupt()
10311072

10321073
void CliInterface::writeToProcess(const QByteArray &data)
10331074
{
1034-
Q_ASSERT(m_process);
10351075
Q_ASSERT(!data.isNull());
1036-
1076+
// tar.7z 管道下 stdin 为 tar 输出,不能写入,密码已通过命令行传入
1077+
if (m_tar7z_7z) {
1078+
return;
1079+
}
1080+
if (!m_process) {
1081+
return;
1082+
}
10371083
// m_process->write(data);
10381084
m_process->pty()->write(data);
10391085
}
@@ -1290,14 +1336,17 @@ void CliInterface::readStdout(bool handleAll)
12901336
return;
12911337
}
12921338

1293-
Q_ASSERT(m_process);
1339+
QProcess *outProcess = m_tar7z_7z ? m_tar7z_7z : m_process;
1340+
if (!outProcess) {
1341+
return;
1342+
}
12941343

1295-
if (!m_process->bytesAvailable()) { // 无数据
1344+
if (!outProcess->bytesAvailable()) { // 无数据
12961345
return;
12971346
}
12981347

12991348
// 获取命令行输出
1300-
QByteArray dd = m_process->readAllStandardOutput();
1349+
QByteArray dd = outProcess->readAllStandardOutput();
13011350
m_stdOutData += dd;
13021351

13031352
// 换行分割
@@ -1309,12 +1358,10 @@ void CliInterface::readStdout(bool handleAll)
13091358
// }
13101359
bool isWrongPwd = isWrongPasswordMsg(lines.last());
13111360

1312-
if ((m_process->program().at(0).contains("7z") && m_process->program().at(1) != "l") && !isWrongPwd) {
1313-
handleAll = true; // 7z进度行结束无\n
1314-
}
1315-
1316-
if ((m_process->program().at(0).contains("tempScript")) && !isWrongPwd) {
1317-
handleAll = true; // compress .tar.7z progressline has no \n
1361+
// 7z 或 tar.7z 管道:进度行结束无 \n
1362+
bool is7zAdd = m_tar7z_7z || (m_process && m_process->program().at(0).contains("7z") && m_process->program().at(1) != "l");
1363+
if (is7zAdd && !isWrongPwd) {
1364+
handleAll = true;
13181365
}
13191366

13201367
bool foundErrorMessage = (isWrongPwd || isDiskFullMsg(QLatin1String(lines.last()))
@@ -1331,7 +1378,7 @@ void CliInterface::readStdout(bool handleAll)
13311378
// because the last line might be incomplete we leave it for now
13321379
// note, this last line may be an empty string if the stdoutdata ends
13331380
// with a newline
1334-
if (m_process->program().at(0).contains("unrar")) { // 针对unrar的命令行截取
1381+
if (m_process && m_process->program().at(0).contains("unrar")) { // 针对unrar的命令行截取
13351382
m_stdOutData.clear();
13361383
if (lines.count() > 0) {
13371384
if (!(lines[lines.count() - 1].endsWith("%") || lines[lines.count() - 1].endsWith("OK "))) {

3rdparty/interface/archiveinterface/cliinterface.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ class CliInterface : public ReadWriteArchiveInterface
143143
*/
144144
virtual void killProcess(bool emitFinished = true) = 0;
145145

146+
/**
147+
* @brief killTar7zPipelineIfActive 若当前为 tar.7z 管道则结束两个进程
148+
* @return 若已结束管道返回 true,否则 false
149+
*/
150+
bool killTar7zPipelineIfActive();
151+
146152
/**
147153
* @brief handleProgress 解析进度并发送进度信号
148154
* @param line
@@ -236,6 +242,8 @@ private slots:
236242
protected:
237243
CliProperties *m_cliProps = nullptr; // 命令属性
238244
/*KProcess*/KPtyProcess *m_process = nullptr; // 工作进程
245+
QProcess *m_tarProcess = nullptr; // 仅 tar.7z 管道:tar 进程
246+
QProcess *m_tar7z_7z = nullptr; // 仅 tar.7z 管道:7z 进程(输出来自此进程)
239247
PluginFinishType m_finishType = PFT_Nomral; // 插件结束类型
240248
QString m_strEncryptedFileName = QString(); // 当前被解压的加密文件名
241249
QVector<qint64> m_childProcessId; // 压缩tar.7z文件的子进程Id
@@ -262,7 +270,6 @@ private slots:
262270
QMap<QString, int> m_mapLongName; // 长文件名统计
263271
QMap<QString, int> m_mapLongDirName; // 长文件夹统计
264272
QMap<QString, int> m_mapRealDirValue; // 真实文件统计
265-
QString m_scriptPath; // 脚本路径
266273
};
267274

268275
#endif // CLIINTERFACE_H

0 commit comments

Comments
 (0)