blob: e595c9d08aa485461ef284ae72a89ede2c6b48b9 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "src/developer/forensics/crash_reports/store_metadata.h"
#include <lib/syslog/cpp/macros.h>
#include <algorithm>
#include <filesystem>
namespace forensics {
namespace crash_reports {
namespace fs = std::filesystem;
StoreMetadata::StoreMetadata(std::string store_root, const StorageSize max_size)
: store_root_(std::move(store_root)),
max_size_(max_size),
current_size_(StorageSize::Bytes(0u)),
is_directory_usable_(false) {
RecreateFromFilesystem();
}
bool StoreMetadata::RecreateFromFilesystem() {
current_size_ = StorageSize::Bytes(0u);
report_metadata_.clear();
program_metadata_.clear();
std::error_code error_code;
if (!fs::is_directory(store_root_) && !fs::create_directory(store_root_, error_code)) {
FX_LOGS(WARNING) << "Failed to create " << store_root_ << " " << error_code;
is_directory_usable_ = false;
return false;
}
for (const auto& program_dir : fs::directory_iterator(store_root_)) {
const std::string program = program_dir.path().filename();
for (const auto& report_dir : fs::directory_iterator(program_dir)) {
const ReportId report_id = std::stoull(report_dir.path().filename());
std::vector<std::string> attachments;
StorageSize report_size = StorageSize::Bytes(0);
for (const auto& attachment : fs::directory_iterator(report_dir)) {
attachments.push_back(attachment.path().filename());
std::error_code ec;
StorageSize attachment_size = StorageSize::Bytes(fs::file_size(attachment, ec));
if (!ec) {
report_size += attachment_size;
}
}
current_size_ += report_size;
report_metadata_[report_id].size = report_size;
report_metadata_[report_id].dir = report_dir.path();
report_metadata_[report_id].program = program;
report_metadata_[report_id].attachments = std::move(attachments);
program_metadata_[program].dir = program_dir.path();
program_metadata_[program].report_ids.push_back(report_id);
}
}
// Sort the reports such that the oldest report is at the front of the queue.
for (auto& [_, metadata] : program_metadata_) {
std::sort(metadata.report_ids.begin(), metadata.report_ids.end());
}
is_directory_usable_ = true;
return true;
}
bool StoreMetadata::IsDirectoryUsable() const { return is_directory_usable_; }
bool StoreMetadata::Contains(const ReportId report_id) const {
return report_metadata_.find(report_id) != report_metadata_.end();
}
bool StoreMetadata::Contains(const std::string& program) const {
return program_metadata_.find(program) != program_metadata_.end();
}
StorageSize StoreMetadata::CurrentSize() const { return current_size_; }
StorageSize StoreMetadata::RemainingSpace() const { return max_size_ - current_size_; }
const std::string& StoreMetadata::RootDir() const { return store_root_; }
void StoreMetadata::Add(const ReportId report_id, std::string program,
std::vector<std::string> attachments, const StorageSize size) {
FX_CHECK(IsDirectoryUsable());
current_size_ += size;
program_metadata_[program].dir = fs::path(store_root_) / program;
program_metadata_[program].report_ids.push_back(report_id);
report_metadata_[report_id].size = size;
report_metadata_[report_id].dir =
fs::path(program_metadata_[program].dir) / std::to_string(report_id);
report_metadata_[report_id].program = std::move(program);
report_metadata_[report_id].attachments = std::move(attachments);
}
void StoreMetadata::Delete(const ReportId report_id) {
FX_CHECK(IsDirectoryUsable());
FX_CHECK(Contains(report_id));
const auto& program = ReportProgram(report_id);
auto& report_ids = program_metadata_[program].report_ids;
report_ids.erase(std::find(report_ids.begin(), report_ids.end(), report_id));
current_size_ -= report_metadata_[report_id].size;
if (report_ids.empty()) {
program_metadata_.erase(program);
}
report_metadata_.erase(report_id);
}
std::vector<std::string> StoreMetadata::Programs() const {
std::vector<std::string> programs;
for (const auto& [program, _] : program_metadata_) {
programs.push_back(program);
}
return programs;
}
std::vector<ReportId> StoreMetadata::Reports() const {
std::vector<ReportId> report_ids;
for (const auto& [report_id, _] : report_metadata_) {
report_ids.push_back(report_id);
}
return report_ids;
}
const std::deque<ReportId>& StoreMetadata::ProgramReports(const std::string& program) const {
FX_CHECK(program_metadata_.find(program) != program_metadata_.end());
return program_metadata_.at(program).report_ids;
}
const std::string& StoreMetadata::ReportProgram(const ReportId report_id) const {
FX_CHECK(Contains(report_id));
return report_metadata_.at(report_id).program;
}
const std::string& StoreMetadata::ProgramDirectory(const std::string& program) const {
FX_CHECK(program_metadata_.find(program) != program_metadata_.end());
return program_metadata_.at(program).dir;
}
const std::string& StoreMetadata::ReportDirectory(const ReportId report_id) const {
FX_CHECK(Contains(report_id));
return report_metadata_.at(report_id).dir;
}
StorageSize StoreMetadata::ReportSize(const ReportId report_id) const {
FX_CHECK(Contains(report_id));
return report_metadata_.at(report_id).size;
}
std::vector<std::string> StoreMetadata::ReportAttachments(ReportId report_id,
const bool absolute_paths) const {
FX_CHECK(Contains(report_id));
auto& report_metadata = report_metadata_.at(report_id);
if (!absolute_paths) {
return report_metadata.attachments;
}
std::vector<std::string> attachments;
attachments.reserve(report_metadata.attachments.size());
for (const auto& attachment : report_metadata.attachments) {
attachments.push_back(fs::path(report_metadata.dir) / attachment);
}
return attachments;
}
} // namespace crash_reports
} // namespace forensics