CCF
Loading...
Searching...
No Matches
filenames.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
7
8#include <algorithm>
9#include <filesystem>
10#include <optional>
11#include <string>
12#include <vector>
13
14namespace snapshots
15{
16 namespace fs = std::filesystem;
17
18 static constexpr auto snapshot_file_prefix = "snapshot";
19 static constexpr auto snapshot_idx_delimiter = "_";
20 static constexpr auto snapshot_committed_suffix = ".committed";
21 static constexpr auto snapshot_ignored_file_suffix = "ignored";
22
23 static bool is_snapshot_file(const std::string& file_name)
24 {
25 return file_name.starts_with(snapshot_file_prefix);
26 }
27
28 static bool is_snapshot_file_committed(const std::string& file_name)
29 {
30 return file_name.ends_with(snapshot_committed_suffix);
31 }
32
33 static bool is_snapshot_file_ignored(const std::string& file_name)
34 {
35 return file_name.ends_with(snapshot_ignored_file_suffix);
36 }
37
38 static void ignore_snapshot_file(
39 const fs::path& dir, const std::string& file_name)
40 {
41 if (is_snapshot_file_ignored(file_name))
42 {
43 return;
44 }
45
46 auto ignored_file_name =
47 fmt::format("{}.{}", file_name, snapshot_ignored_file_suffix);
48 {
49 asynchost::TimeBoundLogger log_if_slow(fmt::format(
50 "Ignoring snapshot file - rename({} to {})",
51 file_name,
52 ignored_file_name));
53 files::rename(dir / file_name, dir / ignored_file_name);
54 }
55 }
56
57 static size_t read_idx(const std::string& str)
58 {
59 size_t idx = 0;
60 const auto* end_ptr = str.data() + str.size();
61
62 auto res = std::from_chars(str.data(), end_ptr, idx);
63 if (res.ec != std::errc())
64 {
65 throw std::logic_error(
66 fmt::format("Could not read idx from string \"{}\": {}", str, res.ec));
67 }
68
69 if (res.ptr != end_ptr)
70 {
71 throw std::logic_error(fmt::format(
72 R"(Trailing characters in "{}" cannot be converted to idx: "{}")",
73 str,
74 std::string(res.ptr, end_ptr)));
75 }
76 return idx;
77 }
78
79 static std::optional<size_t> get_evidence_commit_idx_from_file_name(
80 const std::string& file_name)
81 {
82 // Only returns an evidence commit index for 1.x committed snapshots.
83 // 1.x committed snapshots file names are of the form:
84 // "snapshot_X_Y.committed_Z" while 2.x+ ones are of the form:
85 // "snapshot_X_Y.committed"
86 auto pos = file_name.find(snapshot_committed_suffix);
87 if (pos == std::string::npos)
88 {
89 throw std::logic_error(
90 fmt::format("Snapshot file \"{}\" is not committed", file_name));
91 }
92
93 pos = file_name.find(snapshot_idx_delimiter, pos);
94 if (pos == std::string::npos)
95 {
96 // 2.x+ snapshot
97 return std::nullopt;
98 }
99
100 return read_idx(file_name.substr(pos + 1));
101 }
102
103 static size_t get_snapshot_idx_from_file_name(const std::string& file_name)
104 {
105 if (!is_snapshot_file(file_name))
106 {
107 throw std::logic_error(
108 fmt::format("File \"{}\" is not a valid snapshot file", file_name));
109 }
110
111 auto idx_pos = file_name.find_first_of(snapshot_idx_delimiter);
112 if (idx_pos == std::string::npos)
113 {
114 throw std::logic_error(fmt::format(
115 "Snapshot file name {} does not contain snapshot seqno", file_name));
116 }
117
118 auto evidence_idx_pos =
119 file_name.find_first_of(snapshot_idx_delimiter, idx_pos + 1);
120 if (evidence_idx_pos == std::string::npos)
121 {
122 throw std::logic_error(fmt::format(
123 "Snapshot file \"{}\" does not contain evidence index", file_name));
124 }
125
126 return read_idx(
127 file_name.substr(idx_pos + 1, evidence_idx_pos - idx_pos - 1));
128 }
129
130 static size_t get_snapshot_evidence_idx_from_file_name(
131 const std::string& file_name)
132 {
133 if (!is_snapshot_file(file_name))
134 {
135 throw std::logic_error(
136 fmt::format("File \"{}\" is not a valid snapshot file", file_name));
137 }
138
139 auto idx_pos = file_name.find_first_of(snapshot_idx_delimiter);
140 if (idx_pos == std::string::npos)
141 {
142 throw std::logic_error(
143 fmt::format("Snapshot file \"{}\" does not contain index", file_name));
144 }
145
146 auto evidence_idx_pos =
147 file_name.find_first_of(snapshot_idx_delimiter, idx_pos + 1);
148 if (evidence_idx_pos == std::string::npos)
149 {
150 throw std::logic_error(fmt::format(
151 "Snapshot file \"{}\" does not contain evidence index", file_name));
152 }
153
154 // Note: Snapshot file may not be committed
155 size_t end_str = std::string::npos;
156 auto commit_suffix_pos =
157 file_name.find_first_of(snapshot_committed_suffix, evidence_idx_pos + 1);
158 if (commit_suffix_pos != std::string::npos)
159 {
160 end_str = commit_suffix_pos - evidence_idx_pos - 1;
161 }
162
163 return read_idx(file_name.substr(evidence_idx_pos + 1, end_str));
164 }
165
166 inline std::vector<std::pair<size_t, fs::path>>
168 const std::vector<fs::path>& directories,
169 std::optional<size_t> minimum_idx = std::nullopt)
170 {
171 std::vector<std::pair<size_t, fs::path>> committed_snapshots_with_idx;
172
173 for (const auto& dir : directories)
174 {
175 for (const auto& f : fs::directory_iterator(dir))
176 {
177 auto file_name = f.path().filename();
178 if (!is_snapshot_file(file_name))
179 {
180 LOG_DEBUG_FMT("Ignoring non-snapshot file {}", file_name);
181 continue;
182 }
183
184 if (!is_snapshot_file_committed(file_name))
185 {
186 LOG_DEBUG_FMT("Ignoring non-committed snapshot file {}", file_name);
187 continue;
188 }
189
190 if (fs::exists(f.path()) && fs::is_empty(f.path()))
191 {
192 LOG_INFO_FMT("Ignoring empty snapshot file {}", file_name);
193 continue;
194 }
195
196 const auto idx = get_snapshot_idx_from_file_name(file_name.string());
197 if (minimum_idx.has_value() && idx < minimum_idx.value())
198 {
200 "Ignoring snapshot file {} below minimum idx {}",
201 file_name,
202 minimum_idx.value());
203 }
204 else
205 {
207 committed_snapshots_with_idx.emplace_back(idx, f.path());
208 }
209 }
210 }
211
212 std::sort(
213 committed_snapshots_with_idx.begin(),
214 committed_snapshots_with_idx.end(),
215 [](
216 const std::pair<size_t, fs::path>& lhs,
217 const std::pair<size_t, fs::path>& rhs) {
218 return lhs.first > rhs.first;
219 });
220
221 return committed_snapshots_with_idx;
222 }
223
224 inline std::optional<fs::path> find_latest_committed_snapshot_in_directories(
225 const std::vector<fs::path>& directories,
226 std::optional<size_t> minimum_idx = std::nullopt)
227 {
228 const auto paths =
229 find_committed_snapshots_in_directories(directories, minimum_idx);
230 if (paths.empty())
231 {
232 return std::nullopt;
233 }
234
235 return paths.front().second;
236 }
237
238 inline std::optional<fs::path> find_latest_committed_snapshot_in_directory(
239 const fs::path& directory, std::optional<size_t> minimum_idx = std::nullopt)
240 {
241 std::vector<fs::path> directories{directory};
243 directories, minimum_idx);
244 }
245}
#define LOG_INFO_FMT
Definition internal_logger.h:15
#define LOG_DEBUG_FMT
Definition internal_logger.h:14
Definition fetch.h:63
std::vector< std::pair< size_t, fs::path > > find_committed_snapshots_in_directories(const std::vector< fs::path > &directories, std::optional< size_t > minimum_idx=std::nullopt)
Definition filenames.h:167
std::optional< fs::path > find_latest_committed_snapshot_in_directory(const fs::path &directory, std::optional< size_t > minimum_idx=std::nullopt)
Definition filenames.h:238
std::optional< fs::path > find_latest_committed_snapshot_in_directories(const std::vector< fs::path > &directories, std::optional< size_t > minimum_idx=std::nullopt)
Definition filenames.h:224
Definition time_bound_logger.h:14