| // 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 |