diff --git a/libs/linglong/tests/ll-tests/src/linglong/utils/file_test.cpp b/libs/linglong/tests/ll-tests/src/linglong/utils/file_test.cpp index 09ab6ff4a..6b3035612 100644 --- a/libs/linglong/tests/ll-tests/src/linglong/utils/file_test.cpp +++ b/libs/linglong/tests/ll-tests/src/linglong/utils/file_test.cpp @@ -406,6 +406,38 @@ TEST_F(FileTest, EnsureDirectory) EXPECT_TRUE(fs::is_directory(multiple_dir)); } +TEST_F(FileTest, EnsureDirectoryErrorIncludesPathWhenRemovingFileFails) +{ + if (::geteuid() == 0) { + GTEST_SKIP() << "root can remove files regardless of the parent permission bits"; + } + + const auto parent = dest_dir / "read-only-parent"; + fs::create_directory(parent); + const auto permissions = fs::status(parent).permissions(); + const auto file = parent / "existing-file"; + std::ofstream(file) << "content"; + fs::permissions(parent, fs::perms::owner_read | fs::perms::owner_exec); + + auto result = linglong::utils::ensureDirectory(file); + + fs::permissions(parent, permissions); + ASSERT_FALSE(result); + EXPECT_NE(result.error().message().find(file.string()), std::string::npos); +} + +TEST_F(FileTest, EnsureDirectoryErrorIncludesPathWhenCreatingDirectoryFails) +{ + const auto parent = dest_dir / "regular-file"; + std::ofstream(parent) << "content"; + const auto directory = parent / "nested-directory"; + + auto result = linglong::utils::ensureDirectory(directory); + + ASSERT_FALSE(result); + EXPECT_NE(result.error().message().find(directory.string()), std::string::npos); +} + TEST_F(FileTest, EnsureDirectoryWithPermissionsIgnoresUmask) { constexpr auto permissions = fs::perms::owner_all | fs::perms::group_read @@ -532,6 +564,20 @@ TEST_F(FileTest, RelinkFile) } } +TEST_F(FileTest, RelinkFileErrorIncludesTemporaryPath) +{ + const auto parent = dest_dir / "regular-file"; + std::ofstream(parent) << "content"; + const auto link = parent / "link"; + const auto target = dest_dir / "target"; + const auto temporary = fs::path{ link.string() + ".linyaps.tmp" }; + + auto result = linglong::utils::relinkFileTo(link, target); + + ASSERT_FALSE(result); + EXPECT_NE(result.error().message().find(temporary.string()), std::string::npos); +} + TEST_F(FileTest, WriteFile) { // Test writing to a new file @@ -575,6 +621,19 @@ TEST_F(FileTest, WriteFile) EXPECT_FALSE(result.has_value()); // Should fail because parent directory doesn't exist } +TEST_F(FileTest, WriteFileErrorIncludesPath) +{ + const fs::path fullDevice{ "/dev/full" }; + if (!fs::exists(fullDevice)) { + GTEST_SKIP() << "/dev/full is required to exercise a deferred write failure"; + } + + auto result = linglong::utils::writeFile(fullDevice, "content"); + + ASSERT_FALSE(result); + EXPECT_NE(result.error().message().find(fullDevice.string()), std::string::npos); +} + TEST_F(FileTest, ReadFile) { // Test reading an existing file diff --git a/libs/utils/src/linglong/utils/file.cpp b/libs/utils/src/linglong/utils/file.cpp index 8175b197a..d50c1b704 100644 --- a/libs/utils/src/linglong/utils/file.cpp +++ b/libs/utils/src/linglong/utils/file.cpp @@ -81,8 +81,9 @@ linglong::utils::error::Result writeFile(const std::filesystem::path &file out << content; out.close(); if (!out) { - return LINGLONG_ERR( - fmt::format("failed to write file {}", common::error::errorString(errno))); + return LINGLONG_ERR(fmt::format("failed to write file {}: {}", + filepath, + common::error::errorString(errno))); } return LINGLONG_OK; } @@ -356,12 +357,12 @@ linglong::utils::error::Result ensureDirectory(const std::filesystem::path std::filesystem::remove(dir, ec); if (ec) { - return LINGLONG_ERR("failed to remove directory", ec); + return LINGLONG_ERR(fmt::format("failed to remove directory {}", dir), ec); } } if (!std::filesystem::create_directories(dir, ec) && ec) { - return LINGLONG_ERR("failed to create directory", ec); + return LINGLONG_ERR(fmt::format("failed to create directory {}", dir), ec); } return LINGLONG_OK; @@ -466,7 +467,8 @@ linglong::utils::error::Result relinkFileTo(const std::filesystem::path &l std::filesystem::create_symlink(target, tmpPath, ec); if (ec) { - return LINGLONG_ERR("failed to create symlink", ec); + return LINGLONG_ERR( + fmt::format("failed to create symlink {} -> {}", tmpPath, target), ec); } std::filesystem::rename(tmpPath, link, ec);