| // 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/debug/zxdb/common/cache_dir.h" | |
| #include <fcntl.h> | |
| #include <sys/stat.h> | |
| #include <algorithm> | |
| #include <filesystem> | |
| #include <system_error> | |
| #include <tuple> | |
| #include <utility> | |
| #include <vector> | |
| #include "lib/syslog/cpp/macros.h" | |
| #include "src/developer/debug/zxdb/common/file_util.h" | |
| #if defined(__APPLE__) | |
| #define st_atim st_atimespec | |
| #endif | |
| namespace zxdb { | |
| namespace { | |
| uint64_t TsToNs(const timespec& ts) { return ts.tv_sec * 1000000000 + ts.tv_nsec; } | |
| } // namespace | |
| CacheDir::CacheDir(std::filesystem::path dir, uint64_t max_size_bytes) | |
| : cache_dir_(std::move(dir)), max_size_(max_size_bytes) { | |
| for (const auto& p : std::filesystem::recursive_directory_iterator(cache_dir_)) { | |
| std::error_code err; | |
| if (p.is_regular_file(err)) { | |
| struct stat file_stat; | |
| if (!lstat(p.path().c_str(), &file_stat)) { | |
| file_info_[p.path()].atime = TsToNs(file_stat.st_atim); | |
| file_info_[p.path()].size = file_stat.st_size; | |
| total_size_ += file_stat.st_size; | |
| } | |
| } | |
| } | |
| PruneDir(); | |
| } | |
| void CacheDir::NotifyFileAccess(const std::filesystem::path& file) { | |
| if (!PathStartsWith(file, cache_dir_)) | |
| return; | |
| if (auto exist = file_info_.find(file); exist != file_info_.end()) { | |
| timespec now; | |
| clock_gettime(CLOCK_REALTIME, &now); | |
| // Only touch the access time. | |
| timespec ts[2] = {now, {0, UTIME_OMIT}}; | |
| utimensat(AT_FDCWD, file.c_str(), ts, 0); | |
| exist->second.atime = TsToNs(now); | |
| } else { | |
| struct stat file_stat; | |
| if (!lstat(file.c_str(), &file_stat)) { | |
| file_info_[file].atime = TsToNs(file_stat.st_atim); | |
| file_info_[file].size = file_stat.st_size; | |
| total_size_ += file_stat.st_size; | |
| PruneDir(); | |
| } | |
| } | |
| } | |
| void CacheDir::PruneDir() { | |
| if (total_size_ <= max_size_) | |
| return; | |
| std::vector<std::pair<std::string, FileInfo>> file_info_v(file_info_.begin(), file_info_.end()); | |
| std::sort(file_info_v.begin(), file_info_v.end(), | |
| [](auto x, auto y) { return x.second.atime < y.second.atime; }); | |
| // Never remove the last file, regardless of how big it is. | |
| file_info_v.pop_back(); | |
| for (const auto& [file, info] : file_info_v) { | |
| std::error_code err; | |
| std::filesystem::remove(file, err); | |
| file_info_.erase(file); | |
| total_size_ -= info.size; | |
| if (total_size_ <= max_size_) | |
| break; | |
| } | |
| } | |
| } // namespace zxdb |