Skip to content

Commit

Permalink
rootfsmanager: Use new storage usage functionality
Browse files Browse the repository at this point in the history
- 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 <[email protected]>
  • Loading branch information
mike-sul committed Sep 1, 2023
1 parent b2e2899 commit e468af4
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 62 deletions.
68 changes: 12 additions & 56 deletions src/rootfstreemanager.cc
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
#include "rootfstreemanager.h"

#include <fcntl.h>
#include <sys/statvfs.h>
#include <cstdio>

#include <boost/algorithm/hex.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>

#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) {
Expand Down Expand Up @@ -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()) {
Expand All @@ -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";
Expand Down Expand Up @@ -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<double>(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;
Expand Down Expand Up @@ -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
}
2 changes: 0 additions & 2 deletions src/rootfstreemanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,11 @@ class RootfsTreeManager : public OstreeManager, public Downloader {
void setRemote(const std::string& name, const std::string& url, const boost::optional<const KeyManager*>& 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<OSTree::Sysroot> sysroot_;
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/liteclienttest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 2 additions & 3 deletions tests/nospace_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
{
Expand Down

0 comments on commit e468af4

Please sign in to comment.