From 82c440d5ac88b78bf6897379b9908b065c9c4be8 Mon Sep 17 00:00:00 2001 From: dengbo Date: Thu, 23 Apr 2026 22:18:29 +0800 Subject: [PATCH] refactor: stage install files to cache before processing Replace file descriptor-based approach with path-based approach for install file handling. Now the PackageManager stages incoming D-Bus file descriptors into a cache directory and passes the cached file path to subsequent processing. This ensures reliable file access throughout the installation pipeline, as file descriptors from D-Bus can be unreliable. Added CachedInstallFile RAII wrapper for automatic cache cleanup on destruction. Log: Changed install file handling from file descriptor to cached file path approach for improved reliability Influence: 1. Test install from layer file with valid .layer file 2. Test install from UAB file with valid .uab file 3. Verify cached files are created in the cache directory 4. Verify cached files are automatically cleaned up after installation 5. Test with invalid file types and verify proper error handling 6. Verify file descriptor leaks are prevented during installation 7. Test that UAB installation hooks receive correct file path --- .gitignore | 8 + .../src/linglong/package/uab_file.cpp | 24 +-- libs/linglong/src/linglong/package/uab_file.h | 5 +- .../package_manager/package_manager.cpp | 145 ++++++++++++++++-- .../package_manager/package_manager.h | 26 +++- .../package_manager/uab_installation.cpp | 37 +++-- .../package_manager/uab_installation.h | 18 ++- libs/linglong/tests/ll-tests/CMakeLists.txt | 1 + .../src/linglong/package/uab_file_test.cpp | 8 +- .../package_manager/package_manager_test.cpp | 125 +++++++++++++++ libs/utils/src/linglong/utils/hooks.cpp | 21 +-- libs/utils/src/linglong/utils/hooks.h | 4 +- 12 files changed, 344 insertions(+), 78 deletions(-) create mode 100644 libs/linglong/tests/ll-tests/src/linglong/package_manager/package_manager_test.cpp diff --git a/.gitignore b/.gitignore index b52078ec9..c9031e681 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,14 @@ /.idea/ /.vscode/ /.cache/ +.omx/ +.opencode/ +.claude/ +.gemini/ +.cursor/ +.agent/ +.antigravity/ +.codex/ /tools/node_modules /tools/package-lock.json diff --git a/libs/linglong/src/linglong/package/uab_file.cpp b/libs/linglong/src/linglong/package/uab_file.cpp index f40b14880..7eef90146 100644 --- a/libs/linglong/src/linglong/package/uab_file.cpp +++ b/libs/linglong/src/linglong/package/uab_file.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -30,9 +30,10 @@ namespace linglong::package { -utils::error::Result> UABFile::loadFromFile(int fd) noexcept +utils::error::Result> +UABFile::loadFromFile(const std::string &path) noexcept { - LINGLONG_TRACE("load uab file from fd") + LINGLONG_TRACE("load uab file from path") struct EnableMaker : public UABFile { @@ -40,11 +41,17 @@ utils::error::Result> UABFile::loadFromFile(int fd) noe }; auto file = std::make_unique(); + file->setFileName(QString::fromStdString(path)); - if (!file->open(fd, QIODevice::ReadOnly, FileHandleFlag::AutoCloseHandle)) { + if (!file->open(QIODevice::ReadOnly)) { return LINGLONG_ERR(fmt::format("open uab failed: {}", file->errorString())); } + auto fd = file->handle(); + if (fd < 0) { + return LINGLONG_ERR(fmt::format("failed to get valid file descriptor from path: {}", path)); + } + elf_version(EV_CURRENT); auto *elf = elf_begin(fd, ELF_C_READ, nullptr); if (elf == nullptr) { @@ -332,19 +339,14 @@ utils::error::Result UABFile::extractSignData() noexcept seek(0); }); - auto selfFd = handle(); auto totalBytes = signSection->sh_size; std::array buf{}; while (totalBytes > 0) { auto bytesRead = totalBytes > buf.size() ? buf.size() : totalBytes; - auto readBytes = ::read(selfFd, buf.data(), bytesRead); + auto readBytes = this->read(reinterpret_cast(buf.data()), bytesRead); if (readBytes == -1) { - if (errno == EINTR) { - errno = 0; - continue; - } return LINGLONG_ERR( - fmt::format("read from sign section error: {}", common::error::errorString(errno))); + fmt::format("read from sign section error: {}", this->errorString().toStdString())); } while (true) { diff --git a/libs/linglong/src/linglong/package/uab_file.h b/libs/linglong/src/linglong/package/uab_file.h index db3d6ad5c..b2e32e448 100644 --- a/libs/linglong/src/linglong/package/uab_file.h +++ b/libs/linglong/src/linglong/package/uab_file.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -24,7 +24,8 @@ class UABFile : public QFile friend class MockUabFile; public: - static utils::error::Result> loadFromFile(int fd) noexcept; + static utils::error::Result> + loadFromFile(const std::string &path) noexcept; UABFile(UABFile &&) = delete; UABFile &operator=(UABFile &&) = delete; ~UABFile() override; diff --git a/libs/linglong/src/linglong/package_manager/package_manager.cpp b/libs/linglong/src/linglong/package_manager/package_manager.cpp index 3f20f68a5..3de244d1c 100644 --- a/libs/linglong/src/linglong/package_manager/package_manager.cpp +++ b/libs/linglong/src/linglong/package_manager/package_manager.cpp @@ -48,12 +48,16 @@ #include #include #include +#include #include +#include #include +#include #include #include +#include namespace linglong::service { @@ -84,6 +88,114 @@ QVariantMap toDBusReply(utils::error::ErrorCode code, } // namespace +CachedInstallFile::CachedInstallFile(std::string cachedFilePath) noexcept + : cachedFilePath(std::move(cachedFilePath)) +{ +} + +CachedInstallFile::~CachedInstallFile() +{ + if (this->cachedFilePath.empty()) { + return; + } + + std::error_code ec; + std::filesystem::remove(this->cachedFilePath, ec); + if (ec) { + LogW("failed to remove cached install file {}: {}", this->cachedFilePath, ec.message()); + } +} + +auto PackageManager::stageInstallFile(const QDBusUnixFileDescriptor &fd, + const QString &fileType, + const std::filesystem::path &cacheDir) noexcept + -> utils::error::Result> +{ + LINGLONG_TRACE("stage install file"); + + auto ensureRet = utils::ensureDirectory(cacheDir.string()); + if (!ensureRet) { + return LINGLONG_ERR("failed to prepare install cache directory", ensureRet); + } + + auto cachedPath = cacheDir + / fmt::format("install-from-file-{}.{}", + QUuid::createUuid().toString(QUuid::Id128).toStdString(), + fileType.toStdString()); + + auto inputFd = ::dup(fd.fileDescriptor()); + if (inputFd < 0) { + return LINGLONG_ERR(fmt::format("failed to duplicate install file descriptor: {}", + common::error::errorString(errno))); + } + auto closeInputFd = utils::finally::finally([&inputFd] { + if (inputFd >= 0) { + ::close(inputFd); + } + }); + + if (::lseek(inputFd, 0, SEEK_SET) == -1 && errno != ESPIPE) { + return LINGLONG_ERR(fmt::format("failed to rewind install file descriptor: {}", + common::error::errorString(errno))); + } + + auto outputFd = + ::open(cachedPath.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC, 0600); + if (outputFd < 0) { + return LINGLONG_ERR(fmt::format("failed to create cached install file {}: {}", + cachedPath.string(), + common::error::errorString(errno))); + } + + bool keepCachedFile = false; + auto closeOutputFd = utils::finally::finally([&outputFd, &keepCachedFile, &cachedPath] { + if (outputFd >= 0) { + ::close(outputFd); + } + if (!keepCachedFile) { + std::error_code ec; + std::filesystem::remove(cachedPath, ec); + } + }); + + std::array buffer; + while (true) { + auto bytesRead = ::read(inputFd, buffer.data(), buffer.size()); + if (bytesRead == 0) { + break; + } + + if (bytesRead < 0) { + if (errno == EINTR) { + continue; + } + + return LINGLONG_ERR(fmt::format("failed to read install file descriptor: {}", + common::error::errorString(errno))); + } + + ssize_t bytesWrittenTotal = 0; + while (bytesWrittenTotal < bytesRead) { + auto bytesWritten = + ::write(outputFd, buffer.data() + bytesWrittenTotal, bytesRead - bytesWrittenTotal); + if (bytesWritten < 0) { + if (errno == EINTR) { + continue; + } + + return LINGLONG_ERR(fmt::format("failed to write cached install file {}: {}", + cachedPath.string(), + common::error::errorString(errno))); + } + + bytesWrittenTotal += bytesWritten; + } + } + + keepCachedFile = true; + return std::make_shared(cachedPath.string()); +} + PackageManager::PackageManager( std::unique_ptr repo, std::unique_ptr containerBuilder, @@ -477,10 +589,10 @@ void PackageManager::setConfiguration(const QVariantMap ¶meters) noexcept } } -QVariantMap PackageManager::installFromLayer(const QDBusUnixFileDescriptor &fd, +QVariantMap PackageManager::installFromLayer(const std::shared_ptr &stagedFile, const api::types::v1::CommonOptions &options) noexcept { - auto layerFileRet = package::LayerFile::New(fd.fileDescriptor()); + auto layerFileRet = package::LayerFile::New(QString::fromStdString(stagedFile->path())); if (!layerFileRet) { return toDBusReply(layerFileRet); } @@ -570,7 +682,7 @@ QVariantMap PackageManager::installFromLayer(const QDBusUnixFileDescriptor &fd, auto installer = [this, - fdDup = fd, // keep file descriptor don't close by the destructor of QDBusUnixFileDescriptor + fileDup = stagedFile, // keep file don't close by the destructor packageRef, layerFile = *layerFileRet, module = packageInfo.packageInfoV2Module, @@ -701,7 +813,7 @@ QVariantMap PackageManager::installFromLayer(const QDBusUnixFileDescriptor &fd, }); } -QVariantMap PackageManager::installFromUAB(const QDBusUnixFileDescriptor &fd, +QVariantMap PackageManager::installFromUAB(const std::shared_ptr &stagedFile, const api::types::v1::CommonOptions &options) noexcept { std::unique_ptr installHookManager = @@ -712,13 +824,13 @@ QVariantMap PackageManager::installFromUAB(const QDBusUnixFileDescriptor &fd, return toDBusReply(ret); } - ret = installHookManager->executeInstallHooks(fd.fileDescriptor()); + ret = installHookManager->executeInstallHooks(stagedFile->path()); if (!ret) { return toDBusReply(utils::error::ErrorCode::Failed, "uab package signature verification failed."); } - auto action = UabInstallationAction::create(fd.fileDescriptor(), *this, *repo, options); + auto action = UabInstallationAction::create(stagedFile, *this, *repo, options); if (!action) { return toDBusReply(utils::error::ErrorCode::Failed, "failed to create uab installation action"); @@ -740,19 +852,22 @@ auto PackageManager::InstallFromFile(const QDBusUnixFileDescriptor &fd, return toDBusReply(opts); } - const static QHash - installers = { { "layer", &PackageManager::installFromLayer }, - { "uab", &PackageManager::installFromUAB } }; - - if (!installers.contains(fileType)) { + if (fileType != "layer" && fileType != "uab") { auto msg = fmt::format("{} is unsupported fileType", fileType.toStdString()); return toDBusReply(utils::error::ErrorCode::AppInstallUnsupportedFileFormat, msg); } - return std::invoke(installers[fileType], this, fd, *opts); + auto stagedFile = + stageInstallFile(fd, fileType, std::filesystem::path(LINGLONG_ROOT) / "cache"); + if (!stagedFile) { + return toDBusReply(stagedFile); + } + + if (fileType == "layer") { + return installFromLayer(*stagedFile, *opts); + } + + return installFromUAB(*stagedFile, *opts); } auto PackageManager::Install(const QVariantMap ¶meters) noexcept -> QVariantMap diff --git a/libs/linglong/src/linglong/package_manager/package_manager.h b/libs/linglong/src/linglong/package_manager/package_manager.h index d86934392..4067b3fd4 100644 --- a/libs/linglong/src/linglong/package_manager/package_manager.h +++ b/libs/linglong/src/linglong/package_manager/package_manager.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -31,6 +32,22 @@ namespace linglong::service { class Action; +class CachedInstallFile +{ +public: + explicit CachedInstallFile(std::string cachedFilePath) noexcept; + ~CachedInstallFile(); + CachedInstallFile(const CachedInstallFile &) = delete; + CachedInstallFile(CachedInstallFile &&) = delete; + auto operator=(const CachedInstallFile &) -> CachedInstallFile & = delete; + auto operator=(CachedInstallFile &&) -> CachedInstallFile & = delete; + + [[nodiscard]] const std::string &path() const noexcept { return cachedFilePath; } + +private: + std::string cachedFilePath; +}; + class PackageManager : public QObject, protected QDBusContext { Q_OBJECT @@ -70,6 +87,11 @@ public // Nothing to do here, Permissions() will be rejected in org.deepin.linglong.PackageManager.conf void Permissions() { } + [[nodiscard]] static utils::error::Result> + stageInstallFile(const QDBusUnixFileDescriptor &fd, + const QString &fileType, + const std::filesystem::path &cacheDir) noexcept; + bool waitConfirm(PackageTask &taskRef, api::types::v1::InteractionMessageType msgType, const api::types::v1::PackageManager1RequestInteractionAdditionalMessage @@ -126,10 +148,10 @@ public void ReplyReceived(const QVariantMap &replies); private: - QVariantMap installFromLayer(const QDBusUnixFileDescriptor &fd, + QVariantMap installFromLayer(const std::shared_ptr &stagedFile, const api::types::v1::CommonOptions &options) noexcept; - QVariantMap installFromUAB(const QDBusUnixFileDescriptor &fd, + QVariantMap installFromUAB(const std::shared_ptr &stagedFile, const api::types::v1::CommonOptions &options) noexcept; [[nodiscard]] utils::error::Result lockRepo() noexcept; diff --git a/libs/linglong/src/linglong/package_manager/uab_installation.cpp b/libs/linglong/src/linglong/package_manager/uab_installation.cpp index f5b6ee579..f8acbbf79 100644 --- a/libs/linglong/src/linglong/package_manager/uab_installation.cpp +++ b/libs/linglong/src/linglong/package_manager/uab_installation.cpp @@ -1,18 +1,26 @@ -// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later #include "uab_installation.h" +#include "linglong/common/error.h" +#include "linglong/utils/finally/finally.h" #include "linglong/utils/log/log.h" +#include +#include + namespace linglong::service { -std::shared_ptr UabInstallationAction::create( - int uabFD, PackageManager &pm, repo::OSTreeRepo &repo, api::types::v1::CommonOptions opts) +std::shared_ptr +UabInstallationAction::create(std::shared_ptr stagedFile, + PackageManager &pm, + repo::OSTreeRepo &repo, + api::types::v1::CommonOptions opts) { - auto p = new UabInstallationAction(uabFD, pm, repo, std::move(opts)); - return std::shared_ptr(p); + return std::shared_ptr( + new UabInstallationAction(std::move(stagedFile), pm, repo, std::move(opts))); } UabInstallationAction::CheckedLayers @@ -176,18 +184,13 @@ bool UabInstallationAction::extraModuleOnly(const std::vector stagedFile, PackageManager &pm, repo::OSTreeRepo &repo, api::types::v1::CommonOptions opts) : Action(pm, repo, opts) - , fd(dup(uabFD)) -{ -} - -UabInstallationAction::~UabInstallationAction() + , stagedFile(std::move(stagedFile)) { - close(fd); } utils::error::Result UabInstallationAction::prepare() @@ -198,9 +201,15 @@ utils::error::Result UabInstallationAction::prepare() return LINGLONG_OK; } - auto uabFileRet = package::UABFile::loadFromFile(fd); + if (!this->stagedFile) { + return LINGLONG_ERR("cached uab file missing"); + } + + auto uabFileRet = package::UABFile::loadFromFile(this->stagedFile->path()); if (!uabFileRet) { - return LINGLONG_ERR(fmt::format("failed to load uab file from fd {}", fd), uabFileRet); + return LINGLONG_ERR( + fmt::format("failed to load cached uab file from path {}", this->stagedFile->path()), + uabFileRet); } auto uabFile = std::move(uabFileRet).value(); diff --git a/libs/linglong/src/linglong/package_manager/uab_installation.h b/libs/linglong/src/linglong/package_manager/uab_installation.h index 2d83e041b..317738698 100644 --- a/libs/linglong/src/linglong/package_manager/uab_installation.h +++ b/libs/linglong/src/linglong/package_manager/uab_installation.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -14,6 +14,7 @@ #include "linglong/repo/ostree_repo.h" #include "linglong/utils/transaction.h" +#include #include #include @@ -22,10 +23,11 @@ namespace linglong::service { class UabInstallationAction : public Action { public: - static std::shared_ptr create(int uabFD, - PackageManager &pm, - repo::OSTreeRepo &repo, - api::types::v1::CommonOptions options); + static std::shared_ptr + create(std::shared_ptr stagedFile, + PackageManager &pm, + repo::OSTreeRepo &repo, + api::types::v1::CommonOptions options); using CheckedLayers = std::pair, std::vector>; @@ -37,7 +39,7 @@ class UabInstallationAction : public Action repo::OSTreeRepo &repo, const std::vector &layers); static bool extraModuleOnly(const std::vector &layers); - virtual ~UabInstallationAction(); + ~UabInstallationAction() override = default; virtual utils::error::Result prepare(); virtual utils::error::Result doAction(PackageTask &task); @@ -50,7 +52,7 @@ class UabInstallationAction : public Action virtual utils::error::Result postInstall(PackageTask &task); private: - UabInstallationAction(int uabFD, + UabInstallationAction(std::shared_ptr stagedFile, PackageManager &pm, repo::OSTreeRepo &repo, api::types::v1::CommonOptions options); @@ -60,7 +62,7 @@ class UabInstallationAction : public Action utils::error::Result installUabLayer(const std::vector &layers, std::optional subRef = std::nullopt); - int fd; + std::shared_ptr stagedFile; ActionOperation operation; std::string taskName; CheckedLayers checkedLayers; diff --git a/libs/linglong/tests/ll-tests/CMakeLists.txt b/libs/linglong/tests/ll-tests/CMakeLists.txt index fd0d621bb..f8a6f3d9a 100644 --- a/libs/linglong/tests/ll-tests/CMakeLists.txt +++ b/libs/linglong/tests/ll-tests/CMakeLists.txt @@ -31,6 +31,7 @@ pfl_add_executable( src/linglong/package/layer_dir_test.cpp src/linglong/package/layer_packager_test.cpp src/linglong/package_manager/action_test.cpp + src/linglong/package_manager/package_manager_test.cpp src/linglong/package_manager/task_test.cpp src/linglong/package_manager/task_queue_test.cpp src/linglong/package_manager/package_update_test.cpp diff --git a/libs/linglong/tests/ll-tests/src/linglong/package/uab_file_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/package/uab_file_test.cpp index d2ca48b54..f5f4addd2 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/package/uab_file_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/package/uab_file_test.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024-2026 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -120,12 +120,8 @@ std::unique_ptr UabFileTest::testDir; TEST_F(UabFileTest, UnpackFuseOffset) { - // 打开UAB文件 - int fd = open(uabFile.c_str(), O_RDONLY); - ASSERT_NE(fd, -1) << "Failed to open uab file" << strerror(errno); - // 初始化UABFile对象 - auto uab = linglong::package::UABFile::loadFromFile(fd); + auto uab = linglong::package::UABFile::loadFromFile(uabFile); ASSERT_TRUE(uab.has_value()) << "Failed to load uab file"; auto unpackRet = (*uab)->unpack(); ASSERT_TRUE(unpackRet.has_value()) diff --git a/libs/linglong/tests/ll-tests/src/linglong/package_manager/package_manager_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/package_manager/package_manager_test.cpp new file mode 100644 index 000000000..5ccf5f910 --- /dev/null +++ b/libs/linglong/tests/ll-tests/src/linglong/package_manager/package_manager_test.cpp @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. + * + * SPDX-License-Identifier: LGPL-3.0-or-later + */ + +#include + +#include "../../common/tempdir.h" +#include "linglong/package_manager/package_manager.h" +#include "linglong/package_manager/uab_installation.h" +#include "linglong/repo/ostree_repo.h" +#include "linglong/runtime/container_builder.h" +#include "ocppi/cli/crun/Crun.hpp" + +#include +#include +#include + +#include + +#include + +namespace { + +using namespace linglong; + +class TestRepo : public repo::OSTreeRepo +{ +public: + explicit TestRepo(const std::filesystem::path &path) + : repo::OSTreeRepo( + path, api::types::v1::RepoConfigV2{ .defaultRepo = "", .repos = {}, .version = 2 }) + { + } +}; + +class PackageManagerCacheTest : public ::testing::Test +{ +protected: + auto stageInstallFile(const QByteArray &payload, const QString &fileType) + -> std::shared_ptr + { + const auto sourcePath = sourceDir.path() / ("demo." + fileType.toStdString()); + QFile sourceFile(QString::fromStdString(sourcePath.string())); + EXPECT_TRUE(sourceFile.open(QIODevice::WriteOnly | QIODevice::Truncate)); + EXPECT_EQ(sourceFile.write(payload), payload.size()); + sourceFile.close(); + + const auto rawFd = ::open(sourcePath.c_str(), O_RDONLY | O_CLOEXEC); + EXPECT_GE(rawFd, 0); + + QDBusUnixFileDescriptor fd(rawFd); + auto stagedFile = service::PackageManager::stageInstallFile(fd, fileType, cacheDir.path()); + EXPECT_TRUE(stagedFile); + return *stagedFile; + } + + void SetUp() override + { + auto repoOwner = std::make_unique(cacheDir.path()); + repo = repoOwner.get(); + cli = ocppi::cli::crun::Crun::New(cacheDir.path()).value(); + auto containerBuilderOwner = std::make_unique(*cli); + pm = std::make_unique(std::move(repoOwner), + std::move(containerBuilderOwner), + nullptr); + } + + void TearDown() override + { + pm.reset(); + cli.reset(); + } + + TempDir sourceDir; + TempDir cacheDir; + repo::OSTreeRepo *repo{ nullptr }; + std::unique_ptr cli; + std::unique_ptr pm; +}; + +TEST_F(PackageManagerCacheTest, StageInstallFileCopiesPayloadIntoCacheDirectory) +{ + const QByteArray payload("install-from-file-cache-copy"); + auto stagedFile = stageInstallFile(payload, "uab"); + + EXPECT_TRUE(QFileInfo::exists(QString::fromStdString(stagedFile->path()))); + EXPECT_EQ(QFileInfo(QString::fromStdString(stagedFile->path())).absolutePath(), + QString::fromStdString(cacheDir.path().string())); + + QFile copiedFile(QString::fromStdString(stagedFile->path())); + ASSERT_TRUE(copiedFile.open(QIODevice::ReadOnly)); + EXPECT_EQ(copiedFile.readAll(), payload); +} + +TEST_F(PackageManagerCacheTest, CachedInstallFileRemovesStagedCopyOnDestruction) +{ + const QByteArray payload("install-from-file-cache-copy"); + auto stagedFile = stageInstallFile(payload, "uab"); + const auto stagedPath = stagedFile->path(); + + ASSERT_TRUE(QFileInfo::exists(QString::fromStdString(stagedPath))); + stagedFile.reset(); + + EXPECT_FALSE(QFileInfo::exists(QString::fromStdString(stagedPath))); +} + +TEST_F(PackageManagerCacheTest, UabInstallationActionKeepsStagedFileUntilActionDestruction) +{ + const QByteArray payload("install-from-file-cache-copy"); + auto stagedFile = stageInstallFile(payload, "uab"); + const auto stagedPath = stagedFile->path(); + + auto action = service::UabInstallationAction::create(stagedFile, *pm, *repo, {}); + + stagedFile.reset(); + ASSERT_TRUE(QFileInfo::exists(QString::fromStdString(stagedPath))); + + action.reset(); + + EXPECT_FALSE(QFileInfo::exists(QString::fromStdString(stagedPath))); +} + +} // namespace diff --git a/libs/utils/src/linglong/utils/hooks.cpp b/libs/utils/src/linglong/utils/hooks.cpp index e4eb8d06e..3392e0028 100644 --- a/libs/utils/src/linglong/utils/hooks.cpp +++ b/libs/utils/src/linglong/utils/hooks.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. + * SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. * * SPDX-License-Identifier: LGPL-3.0-or-later */ @@ -142,7 +142,8 @@ utils::error::Result InstallHookManager::parseInstallHooks() return LINGLONG_OK; } -utils::error::Result InstallHookManager::executeInstallHooks(int fd) noexcept +utils::error::Result +InstallHookManager::executeInstallHooks(const std::string &uabPath) noexcept { LINGLONG_TRACE("Executing pre-install hooks."); @@ -150,22 +151,6 @@ utils::error::Result InstallHookManager::executeInstallHooks(int fd) noexc return LINGLONG_OK; } - // Convert fd into a specific path using /proc/pid/fd/fd_num - std::ostringstream oss; - oss << "/proc/" << getpid() << "/fd/" << fd; - - std::array pathBuf{}; - auto size = readlink(oss.str().c_str(), pathBuf.data(), PATH_MAX); - - if (size == -1) { - return LINGLONG_ERR(fmt::format("Failed to read file link for fd {}: {}", - fd, - common::error::errorString(errno))); - } - - pathBuf[size] = '\0'; - std::string uabPath = pathBuf.data(); - std::vector> envVars = { { "LINGLONG_UAB_PATH", uabPath } }; return executeHookCommands(preInstallCommands, envVars); diff --git a/libs/utils/src/linglong/utils/hooks.h b/libs/utils/src/linglong/utils/hooks.h index 92a940b96..48fd00abd 100644 --- a/libs/utils/src/linglong/utils/hooks.h +++ b/libs/utils/src/linglong/utils/hooks.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. + * SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. * * SPDX-License-Identifier: LGPL-3.0-or-later */ @@ -24,7 +24,7 @@ class InstallHookManager final ~InstallHookManager() = default; utils::error::Result parseInstallHooks(); - utils::error::Result executeInstallHooks(int fd) noexcept; + utils::error::Result executeInstallHooks(const std::string &uabPath) noexcept; utils::error::Result executePostInstallHooks(const std::string &appID, const std::string &path) noexcept; utils::error::Result executePostUninstallHooks(const std::string &appID) noexcept;