From 290770f437321573b47b2d45111c8137659f5118 Mon Sep 17 00:00:00 2001 From: Mike Sul Date: Fri, 1 Sep 2023 12:07:48 +0200 Subject: [PATCH] rootfsmanager: Use new storage usage functionality - Use the new/unified functionality to obtain info about storage usage and available free space taking into account the reserved storage space. - Remove the old storage stat obtaining functionality from `rootfsmanager`. - Use the path to the sysroot's ostree repo consistently. - Adjust unit tests to the changes. Signed-off-by: Mike Sul --- src/rootfstreemanager.cc | 68 ++++++-------------------------- src/rootfstreemanager.h | 14 ------- tests/fixtures/liteclienttest.cc | 2 +- tests/nospace_test.cc | 5 +-- 4 files changed, 15 insertions(+), 74 deletions(-) diff --git a/src/rootfstreemanager.cc b/src/rootfstreemanager.cc index 8a349fea..50e91908 100644 --- a/src/rootfstreemanager.cc +++ b/src/rootfstreemanager.cc @@ -1,17 +1,13 @@ #include "rootfstreemanager.h" -#include -#include -#include - #include #include -#include #include "crypto/crypto.h" #include "http/httpclient.h" #include "ostree/repo.h" #include "storage/invstorage.h" +#include "storage/stat.h" #include "target.h" RootfsTreeManager::Config::Config(const PackageConfig& pconfig) { @@ -61,27 +57,26 @@ DownloadResult RootfsTreeManager::Download(const TufTarget& target) { if (getDeltaStatIfAvailable(target, remote, delta_stat)) { LOG_INFO << "Found and pulled delta stats, checking if update can fit on a disk..."; try { - UpdateStat update_stat{}; - bool ok{canDeltaFitOnDisk(delta_stat, update_stat)}; - const auto stat_msg{boost::format{"required %u, available %u out of %u(%u%% of the volume capacity %u)"} % - update_stat.deltaSize % update_stat.available % update_stat.maxAvailable % - update_stat.highWatermark % update_stat.storageCapacity}; - - if (!ok) { + storage::Volume::UsageInfo usage_info; + storage::Volume::getUsageInfo(sysroot_->repoPath(), sysroot_->reservedStorageSpacePercentageDelta(), + usage_info); + if (usage_info.available.first < delta_stat.uncompressedSize) { + std::stringstream err_msg; + err_msg << "required: " << usage_info.toRelativeType(delta_stat.uncompressedSize) << ", " << usage_info; return {DownloadResult::Status::DownloadFailed_NoSpace, - "Insufficient storage available; err: " + stat_msg.str(), sysroot_->path()}; + "Insufficient storage available; err: " + err_msg.str(), sysroot_->repoPath()}; } - LOG_INFO << "Fetching static delta; " + stat_msg.str(); + LOG_INFO << "Fetching static delta; required: " << usage_info.toRelativeType(delta_stat.uncompressedSize) + << usage_info; } catch (const std::exception& exc) { LOG_ERROR << "Failed to check if the static delta can fit on a disk, skipping the update size check...; err: " << exc.what(); - LOG_INFO << "Fetching ostree commit " + target.Sha256Hash() + " from " + remote.baseUrl; } } else { LOG_INFO << "No static delta or static delta stats are found, skipping the update size check..."; - LOG_INFO << "Fetching ostree commit " + target.Sha256Hash() + " from " + remote.baseUrl; } + LOG_INFO << "Fetching ostree commit " + target.Sha256Hash() + " from " + remote.baseUrl; pull_err = OstreeManager::pull(config.sysroot, remote.baseUrl, keys_, Target::fromTufTarget(target), nullptr, prog_cb, remote.isRemoteSet ? nullptr : remote.name.c_str(), remote.headers); if (pull_err.isSuccess()) { @@ -99,8 +94,7 @@ DownloadResult RootfsTreeManager::Download(const TufTarget& target) { (pull_err.description.find("Delta requires") != std::string::npos && pull_err.description.find("free space, but only") != std::string::npos)) { res = {DownloadResult::Status::DownloadFailed_NoSpace, - "Insufficient storage available; path: " + config.sysroot.string() + "; err: " + pull_err.description, - sysroot_->path()}; + "Insufficient storage available; err: " + pull_err.description, sysroot_->repoPath()}; break; } error_desc += pull_err.description + "\n"; @@ -311,27 +305,6 @@ bool RootfsTreeManager::getDeltaStatIfAvailable(const TufTarget& target, const R return false; } -bool RootfsTreeManager::canDeltaFitOnDisk(const DeltaStat& delta_stat, UpdateStat& update_stat) const { - StorageStat storage{}; - getStorageStat(sysroot_->repoPath(), storage); - const auto highWatermark{100 - sysroot_->reservedStorageSpacePercentageDelta()}; - - const uint64_t max_blocks_available = std::floor(storage.blockNumb * (static_cast(highWatermark) / 100)); - const uint64_t blocks_in_use = storage.blockNumb - storage.freeBlockNumb; - const uint64_t max_blocks_available_for_update = - (max_blocks_available > blocks_in_use) ? (max_blocks_available - blocks_in_use) : 0; - const uint64_t blocks_required_by_delta = (delta_stat.uncompressedSize / storage.blockSize) + - ((delta_stat.uncompressedSize % storage.blockSize != 0) ? 1 : 0); - - update_stat.storageCapacity = storage.blockSize * storage.blockNumb; - update_stat.highWatermark = highWatermark; - update_stat.maxAvailable = max_blocks_available * storage.blockSize; - update_stat.available = max_blocks_available_for_update * storage.blockSize; - update_stat.deltaSize = delta_stat.uncompressedSize; - - return blocks_required_by_delta <= max_blocks_available_for_update; -} - bool RootfsTreeManager::getDeltaStatsRef(const Json::Value& json, DeltaStatsRef& ref) { if (!json.isMember("delta-stats")) { return false; @@ -416,20 +389,3 @@ bool RootfsTreeManager::findDeltaStatForUpdate(const Json::Value& delta_stats, c found_delta_stat = {found_delta["size"].asUInt64(), found_delta["u_size"].asUInt64()}; return true; } - -void RootfsTreeManager::getStorageStat(const std::string& path, StorageStat& stat_out) { - int fd{-1}; - if ((fd = open(path.c_str(), O_DIRECTORY | O_RDONLY)) == -1) { - throw std::runtime_error(std::string("Failed to obtain a sysroot directory file descriptor; path: ") + path + - ", err: " + std::strerror(errno)); - } - struct statvfs fs_stat {}; - if (-1 == fstatvfs(fd, &fs_stat)) { - throw std::runtime_error(std::string("Failed to obtain statistic about the sysroot directory; path: ") + path + - ", err: " + std::strerror(errno)); - } - - stat_out.freeBlockNumb = (getuid() != 0 ? fs_stat.f_bavail : fs_stat.f_bfree); - stat_out.blockNumb = fs_stat.f_blocks; - stat_out.blockSize = fs_stat.f_bsize; // f_frsize == f_bsize on the linux-based systems -} diff --git a/src/rootfstreemanager.h b/src/rootfstreemanager.h index e36b9fcd..2fae5410 100644 --- a/src/rootfstreemanager.h +++ b/src/rootfstreemanager.h @@ -56,18 +56,6 @@ class RootfsTreeManager : public OstreeManager, public Downloader { uint64_t size; uint64_t uncompressedSize; }; - struct StorageStat { - uint64_t blockSize; - uint64_t freeBlockNumb; - uint64_t blockNumb; - }; - struct UpdateStat { - uint64_t storageCapacity; - unsigned int highWatermark; - uint64_t maxAvailable; - uint64_t available; - uint64_t deltaSize; - }; std::string getCurrentHash() const override { return sysroot_->getDeploymentHash(OSTree::Sysroot::Deployment::kCurrent); @@ -77,13 +65,11 @@ class RootfsTreeManager : public OstreeManager, public Downloader { void setRemote(const std::string& name, const std::string& url, const boost::optional& keys); data::InstallationResult verifyBootloaderUpdate(const Uptane::Target& target) const; bool getDeltaStatIfAvailable(const TufTarget& target, const Remote& remote, DeltaStat& delta_stat) const; - bool canDeltaFitOnDisk(const DeltaStat& delta_stat, UpdateStat& update_stat) const; static bool getDeltaStatsRef(const Json::Value& json, DeltaStatsRef& ref); static Json::Value downloadDeltaStats(const DeltaStatsRef& ref, const Remote& remote); static bool findDeltaStatForUpdate(const Json::Value& delta_stats, const std::string& from, const std::string& to, DeltaStat& found_delta_stat); - static void getStorageStat(const std::string& path, StorageStat& stat_out); const KeyManager& keys_; std::shared_ptr sysroot_; diff --git a/tests/fixtures/liteclienttest.cc b/tests/fixtures/liteclienttest.cc index 9a5855d8..9129d6fa 100644 --- a/tests/fixtures/liteclienttest.cc +++ b/tests/fixtures/liteclienttest.cc @@ -335,7 +335,7 @@ class ClientTest :virtual public ::testing::Test { ASSERT_EQ(download_result.status, expected_download_result.status); ASSERT_TRUE(download_result.description.find(expected_download_result.description) != std::string::npos) << "Actuall error message: " << download_result.description; if (expected_download_result.noSpace()) { - ASSERT_EQ(download_result.destination_path, sys_repo_.getPath()); + ASSERT_EQ(download_result.destination_path, sys_repo_.getRepo().getPath()); } if (download_result) { ASSERT_EQ(client.install(to), expected_install_code); diff --git a/tests/nospace_test.cc b/tests/nospace_test.cc index 1ed8e2a8..0882879f 100644 --- a/tests/nospace_test.cc +++ b/tests/nospace_test.cc @@ -303,9 +303,8 @@ TEST_F(NoSpaceTest, OstreeUpdateNoSpaceIfStaticDeltaStats) { {DownloadResult::Status::DownloadFailed_NoSpace, "Insufficient storage available"}); const auto events{device_gateway_.getEvents()}; const std::string event_err_msg{events[events.size() - 1]["event"]["details"].asString()}; - ASSERT_TRUE(std::string::npos != - event_err_msg.find("available 40960 out of 389120(95% of the volume capacity 409600)")) - << event_err_msg; + ASSERT_TRUE(std::string::npos != event_err_msg.find("available: 40960B 10%")) << event_err_msg; + ASSERT_TRUE(std::string::npos != event_err_msg.find("required: 42397B 11%")) << event_err_msg; ASSERT_TRUE(targetsMatch(client->getCurrent(), getInitialTarget())); } {