blob: f87d815bea0a1d1df53495c035e91902abf8b57e [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "spaced/disk_usage.h"
#include <algorithm>
#include <memory>
#include <string>
#include <sys/statvfs.h>
#include <base/files/file_path.h>
#include <base/posix/eintr_wrapper.h>
#include <base/strings/string_util.h>
#include <rootdev/rootdev.h>
namespace spaced {
DiskUsageUtil::DiskUsageUtil()
: lvm_(std::make_unique<brillo::LogicalVolumeManager>()) {}
DiskUsageUtil::~DiskUsageUtil() {}
int DiskUsageUtil::StatVFS(const base::FilePath& path, struct statvfs* st) {
return HANDLE_EINTR(statvfs(path.value().c_str(), st));
}
base::Optional<brillo::Thinpool> DiskUsageUtil::GetThinpool() {
// Get the root device.
char root_device[PATH_MAX];
int ret = rootdev(root_device, sizeof(root_device),
true, // Do full resolution.
true); // Remove partition number.
if (ret != 0) {
LOG(WARNING) << "rootdev failed with error code " << ret;
return base::nullopt;
}
// For some storage devices (eg. eMMC), the path ends in a digit
// (eg. /dev/mmcblk0). Use 'p' as the partition separator while generating
// the partition's block device path. For other types of paths (/dev/sda), we
// directly append the partition number.
std::string stateful_dev(root_device);
if (base::IsAsciiDigit(stateful_dev[stateful_dev.size() - 1]))
stateful_dev += 'p';
stateful_dev += '1';
// Attempt to check if the stateful partition is setup with a valid physical
// volume.
base::FilePath physical_volume(stateful_dev);
base::Optional<brillo::PhysicalVolume> pv =
lvm_->GetPhysicalVolume(physical_volume);
if (!pv || !pv->IsValid())
return base::nullopt;
base::Optional<brillo::VolumeGroup> vg = lvm_->GetVolumeGroup(*pv);
if (!vg || !vg->IsValid())
return base::nullopt;
return lvm_->GetThinpool(*vg, "thinpool");
}
uint64_t DiskUsageUtil::GetFreeDiskSpace(const base::FilePath& path) {
// Use statvfs() to get the free space for the given path.
struct statvfs stat;
if (StatVFS(path, &stat) != 0) {
LOG(ERROR) << "Failed to run statvfs() on " << path;
return 0;
}
uint64_t free_disk_space =
static_cast<uint64_t>(stat.f_bavail) * stat.f_frsize;
base::Optional<brillo::Thinpool> thinpool = GetThinpool();
if (thinpool && thinpool->IsValid()) {
free_disk_space = std::min(free_disk_space, thinpool->GetFreeSpace());
}
return free_disk_space;
}
uint64_t DiskUsageUtil::GetTotalDiskSpace(const base::FilePath& path) {
// Use statvfs() to get the total space for the given path.
struct statvfs stat;
if (StatVFS(path, &stat) != 0) {
LOG(ERROR) << "Failed to run statvfs() on " << path;
return 0;
}
uint64_t total_disk_space =
static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize;
base::Optional<brillo::Thinpool> thinpool = GetThinpool();
if (thinpool && thinpool->IsValid()) {
total_disk_space = std::min(total_disk_space, thinpool->GetTotalSpace());
}
return total_disk_space;
}
} // namespace spaced