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
5#include <filesystem>
6#include <optional>
7#include <string>
8
9namespace snapshots
10{
11 namespace fs = std::filesystem;
12
13 static constexpr auto snapshot_file_prefix = "snapshot";
14 static constexpr auto snapshot_idx_delimiter = "_";
15 static constexpr auto snapshot_committed_suffix = ".committed";
16
17 static bool is_snapshot_file(const std::string& file_name)
18 {
19 return file_name.starts_with(snapshot_file_prefix);
20 }
21
22 static bool is_snapshot_file_committed(const std::string& file_name)
23 {
24 return file_name.find(snapshot_committed_suffix) != std::string::npos;
25 }
26
27 static size_t read_idx(const std::string& str)
28 {
29 size_t idx = 0;
30 const auto* end_ptr = str.data() + str.size();
31
32 auto res = std::from_chars(str.data(), end_ptr, idx);
33 if (res.ec != std::errc())
34 {
35 throw std::logic_error(
36 fmt::format("Could not read idx from string \"{}\": {}", str, res.ec));
37 }
38
39 if (res.ptr != end_ptr)
40 {
41 throw std::logic_error(fmt::format(
42 R"(Trailing characters in "{}" cannot be converted to idx: "{}")",
43 str,
44 std::string(res.ptr, end_ptr)));
45 }
46 return idx;
47 }
48
49 static std::optional<size_t> get_evidence_commit_idx_from_file_name(
50 const std::string& file_name)
51 {
52 // Only returns an evidence commit index for 1.x committed snapshots.
53 // 1.x committed snapshots file names are of the form:
54 // "snapshot_X_Y.committed_Z" while 2.x+ ones are of the form:
55 // "snapshot_X_Y.committed"
56 auto pos = file_name.find(snapshot_committed_suffix);
57 if (pos == std::string::npos)
58 {
59 throw std::logic_error(
60 fmt::format("Snapshot file \"{}\" is not committed", file_name));
61 }
62
63 pos = file_name.find(snapshot_idx_delimiter, pos);
64 if (pos == std::string::npos)
65 {
66 // 2.x+ snapshot
67 return std::nullopt;
68 }
69
70 return read_idx(file_name.substr(pos + 1));
71 }
72
73 static size_t get_snapshot_idx_from_file_name(const std::string& file_name)
74 {
75 if (!is_snapshot_file(file_name))
76 {
77 throw std::logic_error(
78 fmt::format("File \"{}\" is not a valid snapshot file", file_name));
79 }
80
81 auto idx_pos = file_name.find_first_of(snapshot_idx_delimiter);
82 if (idx_pos == std::string::npos)
83 {
84 throw std::logic_error(fmt::format(
85 "Snapshot file name {} does not contain snapshot seqno", file_name));
86 }
87
88 auto evidence_idx_pos =
89 file_name.find_first_of(snapshot_idx_delimiter, idx_pos + 1);
90 if (evidence_idx_pos == std::string::npos)
91 {
92 throw std::logic_error(fmt::format(
93 "Snapshot file \"{}\" does not contain evidence index", file_name));
94 }
95
96 return read_idx(
97 file_name.substr(idx_pos + 1, evidence_idx_pos - idx_pos - 1));
98 }
99
100 static size_t get_snapshot_evidence_idx_from_file_name(
101 const std::string& file_name)
102 {
103 if (!is_snapshot_file(file_name))
104 {
105 throw std::logic_error(
106 fmt::format("File \"{}\" is not a valid snapshot file", file_name));
107 }
108
109 auto idx_pos = file_name.find_first_of(snapshot_idx_delimiter);
110 if (idx_pos == std::string::npos)
111 {
112 throw std::logic_error(
113 fmt::format("Snapshot file \"{}\" does not contain index", file_name));
114 }
115
116 auto evidence_idx_pos =
117 file_name.find_first_of(snapshot_idx_delimiter, idx_pos + 1);
118 if (evidence_idx_pos == std::string::npos)
119 {
120 throw std::logic_error(fmt::format(
121 "Snapshot file \"{}\" does not contain evidence index", file_name));
122 }
123
124 // Note: Snapshot file may not be committed
125 size_t end_str = std::string::npos;
126 auto commit_suffix_pos =
127 file_name.find_first_of(snapshot_committed_suffix, evidence_idx_pos + 1);
128 if (commit_suffix_pos != std::string::npos)
129 {
130 end_str = commit_suffix_pos - evidence_idx_pos - 1;
131 }
132
133 return read_idx(file_name.substr(evidence_idx_pos + 1, end_str));
134 }
135
136 inline std::optional<fs::path> find_latest_committed_snapshot_in_directory(
137 const fs::path& directory, size_t& latest_committed_snapshot_idx)
138 {
139 std::optional<fs::path> latest_committed_snapshot_file_name = std::nullopt;
140
141 for (const auto& f : fs::directory_iterator(directory))
142 {
143 auto file_name = f.path().filename();
144 if (!is_snapshot_file(file_name))
145 {
146 LOG_INFO_FMT("Ignoring non-snapshot file {}", file_name);
147 continue;
148 }
149
150 if (!is_snapshot_file_committed(file_name))
151 {
152 LOG_INFO_FMT("Ignoring non-committed snapshot file {}", file_name);
153 continue;
154 }
155
156 if (fs::exists(f.path()) && fs::is_empty(f.path()))
157 {
158 LOG_INFO_FMT("Ignoring empty snapshot file {}", file_name);
159 continue;
160 }
161
162 auto snapshot_idx = get_snapshot_idx_from_file_name(file_name);
163 if (snapshot_idx > latest_committed_snapshot_idx)
164 {
165 latest_committed_snapshot_file_name = file_name;
166 latest_committed_snapshot_idx = snapshot_idx;
167 }
168 }
169
170 return latest_committed_snapshot_file_name;
171 }
172}
#define LOG_INFO_FMT
Definition internal_logger.h:15
Definition fetch.h:36
std::optional< fs::path > find_latest_committed_snapshot_in_directory(const fs::path &directory, size_t &latest_committed_snapshot_idx)
Definition filenames.h:136