Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Description: Deepin cooperation backend
Package: deepin-data-transfer
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, dde-cooperation-daemon
Recommends: deepin-security-loader
Description: deepin data transfer frontend
Deepin Desktop Environment (DDE) - data transfer frontend.

Expand Down
1 change: 1 addition & 0 deletions debian/deepin-data-transfer.install
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
usr/bin/deepin-data-transfer
usr/libexec/deepin/deepin-data-transfer
usr/lib/*/dde-cooperation/plugins/data-transfer/*.so*
usr/share/deepin-data-transfer/translations/*.qm
usr/share/icons/hicolor/scalable/apps/deepin-data-transfer.svg
Expand Down
12 changes: 10 additions & 2 deletions src/apps/data-transfer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ endif()
if (WIN32)
set(qt_required_components Core Gui Network Widgets Sql WinExtras)
else()
set(qt_required_components Core Gui Network Widgets)
set(qt_required_components Core Gui Network Widgets DBus)
endif()
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS ${qt_required_components})
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ${qt_required_components})
Expand Down Expand Up @@ -76,11 +76,19 @@ target_link_libraries(${PROJ_NAME}
if (CMAKE_SYSTEM MATCHES "Linux")
target_link_libraries(${PROJ_NAME}
PRIVATE
Qt${QT_VERSION_MAJOR}::DBus
${DtkWidget_LIBRARIES}
)
endif()

install(TARGETS ${PROJ_NAME} DESTINATION bin)
if (WIN32)
install(TARGETS ${PROJ_NAME} DESTINATION bin)
else()
# 真实二进制安装到 libexec,/usr/bin 由启动脚本占用(见下),
# 以便经 deepin-security-loader 附加 deepin-daemon 组启动
install(TARGETS ${PROJ_NAME} DESTINATION libexec/deepin)
install(PROGRAMS res/linux/deepin-data-transfer.sh DESTINATION bin RENAME deepin-data-transfer)
endif()

# translation files
TRANSLATION_GENERATE(QM_FILES ${CMAKE_SOURCE_DIR}/translations/${PROJ_NAME})
Expand Down
102 changes: 101 additions & 1 deletion src/apps/data-transfer/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2023-2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

Expand All @@ -10,7 +10,15 @@

#include <QDir>
#include <QIcon>
#include <QThread>
#include <QTranslator>
#include <QDBusConnection>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>

#include <fcntl.h>

static constexpr char kPluginInterface[] { "org.deepin.plugin.datatransfer" };
static constexpr char kPluginCore[] { "data-transfer-core" };
Expand All @@ -20,6 +28,91 @@ static constexpr char kPluginCore[] { "data-transfer-core" };
#define LIB_FILE_NAME(lib_name) QString("lib%1.so").arg(#lib_name)
#endif

#ifdef linux
// deepin-security-loader 启动协议(与 deepin-home-appstore-client 行为一致):
// 进程经 loader 附加 deepin-daemon 组启动时,loader 会附加 --fd1/--fd2 命令行参数,
// 分别传入一对管道:子进程把自身 system bus 唯一连接名和需要免鉴权的服务列表
// 以 JSON 写入 fd1,loader 收到后以组身份代为调用各服务的 SetAllowCaller 白名单
// 注册,之后 InstallPackage 等接口免 polkit 鉴权。
// 子进程若不回报,loader 会超时退出并结束子进程(表现为启动闪退),
// 因此经 loader 启动时必须尽早完成回报;直接启动(无 --fd1)则跳过。
static void initSecurityLoaderReport()
{
const QStringList args = QCoreApplication::arguments();
int fd1 = -1, fd2 = -1;
for (int i = 1; i + 1 < args.count(); ++i) {
if (args.at(i) != QLatin1String("--fd1") && args.at(i) != QLatin1String("--fd2"))
continue;
// toInt() 解析失败会返回 0(即 stdin),必须校验,避免误把标准输入当作管道 fd
bool ok = false;
const int fd = args.at(i + 1).toInt(&ok);
if (!ok)
continue;
Comment on lines +49 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nitpick (bug_risk): The parser accepts fd value 0 even though the comment says standard input must be rejected; an invocation containing --fd1 0 therefore treats stdin as the loader report pipe and writes the JSON handshake to it instead of rejecting the malformed loader arguments.

Triggers: When the process receives a malformed or user-supplied --fd1 0 argument.

Suggested fix: Require the parsed descriptor to be a valid non-standard descriptor, for example by rejecting values less than 3 before assigning fd1 or fd2.

Suggested change
if (!ok)
continue;
if (!ok || fd < 3)
continue;

if (args.at(i) == QLatin1String("--fd1"))
fd1 = fd;
else
fd2 = fd;
}
if (fd1 < 0)
return; // 未经 security-loader 启动,无需回报

// 管道 fd 保持打开(loader 侧不依赖 EOF 读取,与参照实现 appstore-client 一致),
// 但设置 FD_CLOEXEC,避免回报管道泄漏给后续 QProcess 派生的子进程
if (fcntl(fd1, F_SETFD, FD_CLOEXEC) < 0)
qWarning() << "failed to set FD_CLOEXEC on fd" << fd1;
if (fd2 >= 0 && fcntl(fd2, F_SETFD, FD_CLOEXEC) < 0)
qWarning() << "failed to set FD_CLOEXEC on fd" << fd2;

const QString uniqueName = QDBusConnection::systemBus().baseService();
if (uniqueName.isEmpty()) {
qWarning() << "system bus not connected, skip allow-caller report";
return;
}

QJsonObject dest;
dest.insert(QStringLiteral("DbusName"), QStringLiteral("com.deepin.lastore"));
dest.insert(QStringLiteral("DbusPath"), QStringLiteral("/com/deepin/lastore"));
dest.insert(QStringLiteral("DbusInterface"), QStringLiteral("com.deepin.lastore.Manager"));
QJsonObject msg;
msg.insert(QStringLiteral("UniqueName"), uniqueName);
msg.insert(QStringLiteral("DestList"), QJsonArray { dest });

QFile reportPipe;
if (!reportPipe.open(fd1, QIODevice::WriteOnly, QFileDevice::DontCloseHandle)) {
qWarning() << "failed to open security-loader report pipe:" << fd1;
return;
}
reportPipe.write(QJsonDocument(msg).toJson(QJsonDocument::Compact));
reportPipe.flush();
reportPipe.close();
qInfo() << "allow-caller report sent:" << uniqueName;

// fd2 可读回 {"Result":...,"ErrorMsg":...}。管道为非阻塞模式,loader 处理需要
// 一次 dbus 往返,轮询等待一小段时间用于日志,失败不影响主流程;
// 最多等 500ms——该调用位于单例检查之前,不应拖慢启动与窗口激活
if (fd2 >= 0) {
QFile resultPipe;
if (resultPipe.open(fd2, QIODevice::ReadOnly, QFileDevice::DontCloseHandle)) {
QByteArray resultData;
for (int i = 0; i < 10 && resultData.isEmpty(); ++i) {
resultData = resultPipe.readAll();
if (resultData.isEmpty())
QThread::msleep(50);
}
resultPipe.close();
if (resultData.isEmpty()) {
qWarning() << "no allow-caller result from security-loader within 500ms";
} else {
const QJsonObject result = QJsonDocument::fromJson(resultData).object();
qInfo() << "allow-caller register result:"
<< result.value(QLatin1String("Result")).toBool()
<< result.value(QLatin1String("ErrorMsg")).toString();
}
}
}
}
#endif

static bool loadPlugins()
{
QStringList pluginsDirs;
Expand Down Expand Up @@ -92,6 +185,13 @@ int main(int argc, char *argv[])
#endif


#ifdef linux
// 必须先于任何可能退出的分支(含单例失败):经 security-loader 启动时若不回报
// 连接名,loader 会超时并结束子进程,表现为启动闪退;该回报同时完成 lastore
// 免鉴权白名单注册,需早于插件里 SettingHelper 的第一次 InstallPackage
initSecurityLoaderReport();
#endif

bool canSetSingle = app.setSingleInstance(app.applicationName());
if (!canSetSingle) {
qInfo() << "single application is already running.";
Expand Down
27 changes: 27 additions & 0 deletions src/apps/data-transfer/res/linux/deepin-data-transfer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
#
# SPDX-License-Identifier: GPL-3.0-or-later
# Deepin Data Transfer launcher
# 参照 deepin-home-appstore-client:满足条件时经 deepin-security-loader
# 附加 deepin-daemon 组启动,使 lastore 免鉴权接口调用可行;
# 否则直接启动,鉴权回退为系统 polkit 弹窗(等同历史行为)。

REAL_BIN=/usr/libexec/deepin/deepin-data-transfer

# Check if /usr/bin/deepin-security-loader is executable and dbus version meets
# requirement(用 -x 而非 -f:exec 失败时 bash 会直接退出 127,无法走 || 回退,
# 须在 exec 前排除不可执行的 loader)
if [ -x "/usr/bin/deepin-security-loader" ]; then
# Check dbus version
if DBUS_VERSION=$(dpkg-query -W -f='${Version}' dbus 2>/dev/null); then
# Compare dbus version with 1.12.20.17-deepin1
if dpkg --compare-versions "$DBUS_VERSION" ge "1.12.20.17-deepin1"; then
# Use security loader with deepin-daemon group
exec /usr/bin/deepin-security-loader --group deepin-daemon -- "$REAL_BIN" "$@"
fi
fi
fi

# Fallback to direct launch
exec "$REAL_BIN" "$@"
Loading