Skip to content
Draft
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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
/.idea/
/.vscode/
/.cache/
.omx/
.opencode/
.claude/
.gemini/
.cursor/
.agent/
.antigravity/
.codex/

/tools/node_modules
/tools/package-lock.json
Expand Down
24 changes: 13 additions & 11 deletions libs/linglong/src/linglong/package/uab_file.cpp
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -30,21 +30,28 @@

namespace linglong::package {

utils::error::Result<std::unique_ptr<UABFile>> UABFile::loadFromFile(int fd) noexcept
utils::error::Result<std::unique_ptr<UABFile>>
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
{
using UABFile::UABFile;
};

auto file = std::make_unique<EnableMaker>();
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) {
Expand Down Expand Up @@ -332,19 +339,14 @@ utils::error::Result<std::filesystem::path> UABFile::extractSignData() noexcept
seek(0);
});

auto selfFd = handle();
auto totalBytes = signSection->sh_size;
std::array<unsigned char, 4096> 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<char *>(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()));
}
Comment on lines +346 to 350
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

If this->read returns 0 (EOF) before totalBytes is reached, the loop will hang indefinitely because totalBytes is never decremented. You should check for an unexpected EOF and return an error.

        auto readBytes = this->read(reinterpret_cast<char *>(buf.data()), bytesRead);
        if (readBytes <= 0) {
            return LINGLONG_ERR(
              fmt::format("read from sign section error: {}", readBytes == 0 ? "unexpected EOF" : this->errorString().toStdString()));
        }


while (true) {
Expand Down
5 changes: 3 additions & 2 deletions libs/linglong/src/linglong/package/uab_file.h
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -24,7 +24,8 @@ class UABFile : public QFile
friend class MockUabFile;

public:
static utils::error::Result<std::unique_ptr<UABFile>> loadFromFile(int fd) noexcept;
static utils::error::Result<std::unique_ptr<UABFile>>
loadFromFile(const std::string &path) noexcept;
UABFile(UABFile &&) = delete;
UABFile &operator=(UABFile &&) = delete;
~UABFile() override;
Expand Down
145 changes: 130 additions & 15 deletions libs/linglong/src/linglong/package_manager/package_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,19 @@
#include <QDBusInterface>
#include <QDBusReply>
#include <QDBusUnixFileDescriptor>
#include <QEventLoop>

Check warning on line 48 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QEventLoop> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 48 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QEventLoop> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QMetaObject>

Check warning on line 49 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QMetaObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 49 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QMetaObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTimer>

Check warning on line 50 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTimer> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 50 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTimer> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QUuid>

Check warning on line 52 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <algorithm> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 52 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <algorithm> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <algorithm>

Check warning on line 53 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <cstdint> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 53 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <cstdint> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <array>

Check warning on line 54 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <utility> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 54 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <utility> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <cstdint>
#include <filesystem>

Check warning on line 56 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <fcntl.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 56 in libs/linglong/src/linglong/package_manager/package_manager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <fcntl.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <utility>

#include <fcntl.h>
#include <unistd.h>

namespace linglong::service {

Expand Down Expand Up @@ -84,6 +88,114 @@

} // 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<std::shared_ptr<CachedInstallFile>>
{
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<char, 64 * 1024> 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);
Comment on lines +179 to +180
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The write system call can theoretically return 0 if no bytes were written, which would lead to an infinite loop here. While rare for regular files, it is safer to check for bytesWritten <= 0 to prevent potential hangs.

            auto bytesWritten =
              ::write(outputFd, buffer.data() + bytesWrittenTotal, bytesRead - bytesWrittenTotal);
            if (bytesWritten <= 0) {

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<CachedInstallFile>(cachedPath.string());
}

PackageManager::PackageManager(
std::unique_ptr<linglong::repo::OSTreeRepo> repo,
std::unique_ptr<linglong::runtime::ContainerBuilder> containerBuilder,
Expand Down Expand Up @@ -477,10 +589,10 @@
}
}

QVariantMap PackageManager::installFromLayer(const QDBusUnixFileDescriptor &fd,
QVariantMap PackageManager::installFromLayer(const std::shared_ptr<CachedInstallFile> &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);
}
Expand Down Expand Up @@ -570,7 +682,7 @@

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,
Expand Down Expand Up @@ -701,7 +813,7 @@
});
}

QVariantMap PackageManager::installFromUAB(const QDBusUnixFileDescriptor &fd,
QVariantMap PackageManager::installFromUAB(const std::shared_ptr<CachedInstallFile> &stagedFile,
const api::types::v1::CommonOptions &options) noexcept
{
std::unique_ptr<utils::InstallHookManager> installHookManager =
Expand All @@ -712,13 +824,13 @@
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");
Expand All @@ -740,19 +852,22 @@
return toDBusReply(opts);
}

const static QHash<QString,
QVariantMap (PackageManager::*)(
const QDBusUnixFileDescriptor &,
const api::types::v1::CommonOptions &) noexcept>
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 &parameters) noexcept -> QVariantMap
Expand Down
26 changes: 24 additions & 2 deletions libs/linglong/src/linglong/package_manager/package_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,33 @@

#include <QDBusArgument>
#include <QDBusContext>
#include <QList>

Check warning on line 24 in libs/linglong/src/linglong/package_manager/package_manager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QList> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 24 in libs/linglong/src/linglong/package_manager/package_manager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QList> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QObject>

Check warning on line 25 in libs/linglong/src/linglong/package_manager/package_manager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 25 in libs/linglong/src/linglong/package_manager/package_manager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <filesystem>

Check warning on line 27 in libs/linglong/src/linglong/package_manager/package_manager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <memory> not found. Please note: Cppcheck does not need standard library headers to get proper results.

Check warning on line 27 in libs/linglong/src/linglong/package_manager/package_manager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <memory> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <memory>
#include <optional>

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
Expand Down Expand Up @@ -70,6 +87,11 @@
// Nothing to do here, Permissions() will be rejected in org.deepin.linglong.PackageManager.conf
void Permissions() { }

[[nodiscard]] static utils::error::Result<std::shared_ptr<CachedInstallFile>>
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
Expand Down Expand Up @@ -126,10 +148,10 @@
void ReplyReceived(const QVariantMap &replies);

private:
QVariantMap installFromLayer(const QDBusUnixFileDescriptor &fd,
QVariantMap installFromLayer(const std::shared_ptr<CachedInstallFile> &stagedFile,
const api::types::v1::CommonOptions &options) noexcept;

QVariantMap installFromUAB(const QDBusUnixFileDescriptor &fd,
QVariantMap installFromUAB(const std::shared_ptr<CachedInstallFile> &stagedFile,
const api::types::v1::CommonOptions &options) noexcept;

[[nodiscard]] utils::error::Result<void> lockRepo() noexcept;
Expand Down
Loading
Loading