blob: d2f0d77ebad57890da9a74052a46ed6bfc9437b8 [file] [log] [blame]
Alexei Strots01395492023-03-20 13:59:56 -07001#include "aos/events/logging/log_backend.h"
2
3#include <dirent.h>
4
5#include <filesystem>
6
Austin Schuh99f7c6a2024-06-25 22:07:44 -07007#include "absl/flags/flag.h"
8#include "absl/log/check.h"
9#include "absl/log/log.h"
Alexei Strotsf08b8fb2023-04-21 19:46:08 -070010#include "absl/strings/match.h"
Alexei Strots01395492023-03-20 13:59:56 -070011#include "absl/strings/str_cat.h"
Alexei Strots01395492023-03-20 13:59:56 -070012
Alexei Strotsf08b8fb2023-04-21 19:46:08 -070013#include "aos/events/logging/file_operations.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070014#include "aos/util/file.h"
15
Austin Schuh99f7c6a2024-06-25 22:07:44 -070016ABSL_FLAG(
17 bool, sync, false,
Austin Schuh3ebaf782023-04-07 16:03:28 -070018 "If true, sync data to disk as we go so we don't get too far ahead. Also "
19 "fadvise that we are done with the memory once it hits disk.");
Alexei Strots01395492023-03-20 13:59:56 -070020
Austin Schuh99f7c6a2024-06-25 22:07:44 -070021ABSL_FLAG(uint32_t, queue_reserve, 32, "Pre-reserved size of write queue.");
Alexei Strotsa0b99d72023-04-11 15:12:42 -070022
Alexei Strots01395492023-03-20 13:59:56 -070023namespace aos::logger {
24namespace {
25constexpr const char *kTempExtension = ".tmp";
Alexei Strotsa0b99d72023-04-11 15:12:42 -070026
27// Assuming that kSector is power of 2, it aligns address to the left size.
28inline size_t AlignToLeft(size_t value) {
29 return value & (~(FileHandler::kSector - 1));
30}
31
32inline bool IsAligned(size_t value) {
33 return value % FileHandler::kSector == 0;
34}
35
36inline bool IsAlignedStart(const absl::Span<const uint8_t> span) {
37 return (reinterpret_cast<size_t>(span.data()) % FileHandler::kSector) == 0;
38}
39
40inline bool IsAlignedLength(const absl::Span<const uint8_t> span) {
41 return (span.size() % FileHandler::kSector) == 0;
42}
43
44} // namespace
45
46logger::QueueAligner::QueueAligner() {
Austin Schuh99f7c6a2024-06-25 22:07:44 -070047 aligned_queue_.reserve(absl::GetFlag(FLAGS_queue_reserve));
Alexei Strotsa0b99d72023-04-11 15:12:42 -070048}
49
50void logger::QueueAligner::FillAlignedQueue(
51 const absl::Span<const absl::Span<const uint8_t>> &queue) {
52 aligned_queue_.clear();
53
Austin Schuh790ec9c2023-05-03 13:47:15 -070054 size_t queue_index = 0;
Alexei Strotsa0b99d72023-04-11 15:12:42 -070055 for (const auto &span : queue) {
Austin Schuh790ec9c2023-05-03 13:47:15 -070056 ++queue_index;
Alexei Strotsa0b99d72023-04-11 15:12:42 -070057 // Generally, every span might have 3 optional parts (i.e. 2^3 cases):
58 // 1. unaligned prefix - from start till first aligned block.
59 // 2. aligned main - block with aligned start and size
60 // 3. unaligned suffix - block with aligned start, and size less than one
61 // sector. If size of the span is less than 1 sector, let's call it prefix.
62
63 auto *data = span.data();
64 size_t size = span.size();
65 const auto start = reinterpret_cast<size_t>(data);
66 VLOG(2) << "Consider span starting at " << std::hex << start
67 << " with size " << size;
68
Austin Schuh790ec9c2023-05-03 13:47:15 -070069 CHECK_GT(size, 0u)
70 << ": Nobody should be sending empty messages. Queue index "
71 << (queue_index - 1) << " out of " << queue.size();
Alexei Strotsa0b99d72023-04-11 15:12:42 -070072
73 const auto next_aligned =
74 IsAligned(start) ? start : AlignToLeft(start) + FileHandler::kSector;
75 const auto prefix_size = next_aligned - start;
76 VLOG(2) << "Calculated prefix size " << std::hex << prefix_size;
77
78 if (prefix_size >= size) {
79 // size of prefix >= size of span - alignment is not possible, accept the
80 // whole span
81 VLOG(2) << "Only prefix found";
82 CHECK_GT(size, 0u);
83 aligned_queue_.emplace_back(data, size, false);
84 continue;
85 }
86 CHECK_LT(prefix_size, FileHandler::kSector)
87 << ": Wrong calculation of 'next' aligned position";
88 if (prefix_size > 0) {
89 // Cut the prefix and move to the main part.
90 VLOG(2) << "Cutting prefix at " << std::hex << start << " of size "
91 << prefix_size;
92 aligned_queue_.emplace_back(data, prefix_size, false);
93 data += prefix_size;
94 size -= prefix_size;
95 CHECK(data <= span.data() + span.size()) << " :Boundaries after prefix";
96 }
97
98 if (IsAligned(size)) {
99 // the rest is aligned.
100 VLOG(2) << "Returning aligned main part";
101 CHECK_GT(size, 0u);
102 aligned_queue_.emplace_back(data, size, true);
103 continue;
104 }
105
106 const auto aligned_size = AlignToLeft(size);
107 CHECK(aligned_size < size) << ": Wrong calculation of 'main' size";
108 if (aligned_size > 0) {
109 VLOG(2) << "Cutting main part starting " << std::hex
110 << reinterpret_cast<size_t>(data) << " of size " << aligned_size;
111 aligned_queue_.emplace_back(data, aligned_size, true);
112
113 data += aligned_size;
114 size -= aligned_size;
115 CHECK(data <= span.data() + span.size()) << " :Boundaries after main";
116 }
117
118 VLOG(2) << "Cutting suffix part starting " << std::hex
119 << reinterpret_cast<size_t>(data) << " of size " << size;
120 CHECK_GT(size, 0u);
121 aligned_queue_.emplace_back(data, size, false);
122 }
Alexei Strots01395492023-03-20 13:59:56 -0700123}
124
colleen61276dc2023-06-01 09:23:29 -0700125FileHandler::FileHandler(std::string filename, bool supports_odirect)
126 : filename_(std::move(filename)), supports_odirect_(supports_odirect) {}
Alexei Strots01395492023-03-20 13:59:56 -0700127
128FileHandler::~FileHandler() { Close(); }
129
130WriteCode FileHandler::OpenForWrite() {
131 iovec_.reserve(10);
132 if (!aos::util::MkdirPIfSpace(filename_, 0777)) {
133 return WriteCode::kOutOfSpace;
134 } else {
135 fd_ = open(filename_.c_str(), O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, 0774);
136 if (fd_ == -1 && errno == ENOSPC) {
137 return WriteCode::kOutOfSpace;
138 } else {
139 PCHECK(fd_ != -1) << ": Failed to open " << filename_ << " for writing";
140 VLOG(1) << "Opened " << filename_ << " for writing";
141 }
142
143 flags_ = fcntl(fd_, F_GETFL, 0);
Alexei Strotsbc082d82023-05-03 08:43:42 -0700144 PCHECK(flags_ >= 0) << ": Failed to get flags for " << filename_;
Alexei Strots01395492023-03-20 13:59:56 -0700145
146 EnableDirect();
147
148 CHECK(std::filesystem::exists(filename_));
149
150 return WriteCode::kOk;
151 }
152}
153
154void FileHandler::EnableDirect() {
155 if (supports_odirect_ && !ODirectEnabled()) {
156 const int new_flags = flags_ | O_DIRECT;
157 // Track if we failed to set O_DIRECT. Note: Austin hasn't seen this call
158 // fail. The write call tends to fail instead.
159 if (fcntl(fd_, F_SETFL, new_flags) == -1) {
Alexei Strotsbc082d82023-05-03 08:43:42 -0700160 PLOG(WARNING) << "Failed to set O_DIRECT on " << filename_;
Alexei Strots01395492023-03-20 13:59:56 -0700161 supports_odirect_ = false;
162 } else {
Alexei Strotsbc082d82023-05-03 08:43:42 -0700163 VLOG(1) << "Enabled O_DIRECT on " << filename_;
Alexei Strots01395492023-03-20 13:59:56 -0700164 flags_ = new_flags;
165 }
166 }
167}
168
169void FileHandler::DisableDirect() {
170 if (supports_odirect_ && ODirectEnabled()) {
171 flags_ = flags_ & (~O_DIRECT);
172 PCHECK(fcntl(fd_, F_SETFL, flags_) != -1) << ": Failed to disable O_DIRECT";
Alexei Strotsbc082d82023-05-03 08:43:42 -0700173 VLOG(1) << "Disabled O_DIRECT on " << filename_;
Alexei Strots01395492023-03-20 13:59:56 -0700174 }
175}
176
177WriteResult FileHandler::Write(
178 const absl::Span<const absl::Span<const uint8_t>> &queue) {
179 iovec_.clear();
Austin Schuh3ebaf782023-04-07 16:03:28 -0700180 CHECK_LE(queue.size(), static_cast<size_t>(IOV_MAX));
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700181
182 queue_aligner_.FillAlignedQueue(queue);
183 CHECK_LE(queue_aligner_.aligned_queue().size(), static_cast<size_t>(IOV_MAX));
Alexei Strots01395492023-03-20 13:59:56 -0700184
185 // Ok, we now need to figure out if we were aligned, and if we were, how much
186 // of the data we are being asked to write is aligned.
187 //
Austin Schuh3ebaf782023-04-07 16:03:28 -0700188 // When writing with O_DIRECT, the kernel only will accept writes where the
189 // offset into the file is a multiple of kSector, the data is aligned to
190 // kSector in memory, and the length being written is a multiple of kSector.
191 // Some of the callers use an aligned ResizeableBuffer to generate 512 byte
192 // aligned buffers for this code to find and use.
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700193 bool was_aligned = IsAligned(total_write_bytes_);
194 VLOG(1) << "Started " << (was_aligned ? "aligned" : "unaligned")
195 << " at offset " << total_write_bytes_ << " on " << filename();
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700196
Alexei Strotscaf17d32023-04-03 22:31:11 -0700197 // Walk through aligned queue and batch writes based on aligned flag
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700198 for (const auto &item : queue_aligner_.aligned_queue()) {
199 if (was_aligned != item.aligned) {
200 // Switching aligned context. Let's flush current batch.
201 if (!iovec_.empty()) {
202 // Flush current queue if we need.
203 const auto code = WriteV(iovec_, was_aligned);
Austin Schuh3ebaf782023-04-07 16:03:28 -0700204 if (code == WriteCode::kOutOfSpace) {
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700205 // We cannot say anything about what number of messages was written
206 // for sure.
Austin Schuh3ebaf782023-04-07 16:03:28 -0700207 return {
208 .code = code,
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700209 .messages_written = queue.size(),
Austin Schuh3ebaf782023-04-07 16:03:28 -0700210 };
211 }
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700212 iovec_.clear();
Austin Schuh3ebaf782023-04-07 16:03:28 -0700213 }
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700214 // Write queue is flushed. WriteV updates the total_write_bytes_.
215 was_aligned = IsAligned(total_write_bytes_) && item.aligned;
Alexei Strots01395492023-03-20 13:59:56 -0700216 }
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700217 iovec_.push_back(
218 {.iov_base = const_cast<uint8_t *>(item.data), .iov_len = item.size});
Alexei Strots01395492023-03-20 13:59:56 -0700219 }
220
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700221 WriteCode result_code = WriteCode::kOk;
222 if (!iovec_.empty()) {
223 // Flush current queue if we need.
224 result_code = WriteV(iovec_, was_aligned);
225 }
Alexei Strots01395492023-03-20 13:59:56 -0700226 return {
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700227 .code = result_code,
Austin Schuh3ebaf782023-04-07 16:03:28 -0700228 .messages_written = queue.size(),
Alexei Strots01395492023-03-20 13:59:56 -0700229 };
230}
231
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700232WriteCode FileHandler::WriteV(const std::vector<struct iovec> &iovec,
233 bool aligned) {
Alexei Strots01395492023-03-20 13:59:56 -0700234 // Configure the file descriptor to match the mode we should be in. This is
235 // safe to over-call since it only does the syscall if needed.
236 if (aligned) {
237 EnableDirect();
238 } else {
239 DisableDirect();
240 }
241
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700242 VLOG(2) << "Flushing queue of " << iovec.size() << " elements, "
243 << (aligned ? "aligned" : "unaligned");
244
245 CHECK_GT(iovec.size(), 0u);
Alexei Strots01395492023-03-20 13:59:56 -0700246 const auto start = aos::monotonic_clock::now();
Alexei Strots01395492023-03-20 13:59:56 -0700247
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700248 // Validation of alignment assumptions.
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700249 if (aligned) {
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700250 CHECK(IsAligned(total_write_bytes_))
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700251 << ": Failed after writing " << total_write_bytes_
252 << " to the file, attempting aligned write with unaligned start.";
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700253
254 for (const auto &iovec_item : iovec) {
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700255 absl::Span<const uint8_t> data(
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700256 reinterpret_cast<const uint8_t *>(iovec_item.iov_base),
257 iovec_item.iov_len);
258 VLOG(2) << " iov_base " << static_cast<void *>(iovec_item.iov_base)
259 << ", iov_len " << iovec_item.iov_len;
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700260 CHECK(IsAlignedStart(data) && IsAlignedLength(data));
Austin Schuh9e8df9e2023-05-03 08:28:29 -0700261 }
262 }
263
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700264 // Calculation of expected written size.
265 size_t counted_size = 0;
266 for (const auto &iovec_item : iovec) {
267 CHECK_GT(iovec_item.iov_len, 0u);
268 counted_size += iovec_item.iov_len;
Austin Schuh3ebaf782023-04-07 16:03:28 -0700269 }
270
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700271 VLOG(2) << "Going to write " << counted_size;
272 CHECK_GT(counted_size, 0u);
273
274 const ssize_t written = writev(fd_, iovec.data(), iovec.size());
275 VLOG(2) << "Wrote " << written << ", for iovec size " << iovec.size();
276
277 const auto end = aos::monotonic_clock::now();
278 if (written == -1 && errno == ENOSPC) {
279 return WriteCode::kOutOfSpace;
280 }
281 PCHECK(written >= 0) << ": write failed, got " << written;
282 if (written < static_cast<ssize_t>(counted_size)) {
283 // Sometimes this happens instead of ENOSPC. On a real filesystem, this
284 // never seems to happen in any other case. If we ever want to log to a
285 // socket, this will happen more often. However, until we get there, we'll
286 // just assume it means we ran out of space.
287 return WriteCode::kOutOfSpace;
288 }
Austin Schuh3ebaf782023-04-07 16:03:28 -0700289
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700290 if (absl::GetFlag(FLAGS_sync)) {
Alexei Strots01395492023-03-20 13:59:56 -0700291 // Flush asynchronously and force the data out of the cache.
292 sync_file_range(fd_, total_write_bytes_, written, SYNC_FILE_RANGE_WRITE);
293 if (last_synced_bytes_ != 0) {
294 // Per Linus' recommendation online on how to do fast file IO, do a
295 // blocking flush of the previous write chunk, and then tell the kernel to
296 // drop the pages from the cache. This makes sure we can't get too far
297 // ahead.
298 sync_file_range(fd_, last_synced_bytes_,
299 total_write_bytes_ - last_synced_bytes_,
300 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE |
301 SYNC_FILE_RANGE_WAIT_AFTER);
302 posix_fadvise(fd_, last_synced_bytes_,
303 total_write_bytes_ - last_synced_bytes_,
304 POSIX_FADV_DONTNEED);
Alexei Strots01395492023-03-20 13:59:56 -0700305 }
Austin Schuh3c4029f2023-04-13 12:09:55 -0700306 last_synced_bytes_ = total_write_bytes_;
Alexei Strots01395492023-03-20 13:59:56 -0700307 }
308
Alexei Strots01395492023-03-20 13:59:56 -0700309 total_write_bytes_ += written;
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700310 if (aligned) {
311 written_aligned_ += written;
312 }
Alexei Strotsbc082d82023-05-03 08:43:42 -0700313 WriteStatistics()->UpdateStats(end - start, written, iovec.size());
Alexei Strots01395492023-03-20 13:59:56 -0700314 return WriteCode::kOk;
315}
316
317WriteCode FileHandler::Close() {
318 if (!is_open()) {
319 return WriteCode::kOk;
320 }
321 bool ran_out_of_space = false;
322 if (close(fd_) == -1) {
323 if (errno == ENOSPC) {
324 ran_out_of_space = true;
325 } else {
326 PLOG(ERROR) << "Closing log file failed";
327 }
328 }
329 fd_ = -1;
330 VLOG(1) << "Closed " << filename_;
331 return ran_out_of_space ? WriteCode::kOutOfSpace : WriteCode::kOk;
332}
333
colleen61276dc2023-06-01 09:23:29 -0700334FileBackend::FileBackend(std::string_view base_name, bool supports_odirect)
335 : supports_odirect_(supports_odirect),
336 base_name_(base_name),
337 separator_(base_name_.back() == '/' ? "" : "_") {}
Alexei Strots01395492023-03-20 13:59:56 -0700338
Austin Schuh95460cc2023-06-26 11:53:10 -0700339std::unique_ptr<LogSink> FileBackend::RequestFile(const std::string_view id) {
Alexei Strots01395492023-03-20 13:59:56 -0700340 const std::string filename = absl::StrCat(base_name_, separator_, id);
colleen61276dc2023-06-01 09:23:29 -0700341 return std::make_unique<FileHandler>(filename, supports_odirect_);
Alexei Strots01395492023-03-20 13:59:56 -0700342}
343
Austin Schuh95460cc2023-06-26 11:53:10 -0700344std::vector<FileBackend::File> FileBackend::ListFiles() const {
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700345 std::filesystem::path directory(base_name_);
346 if (!is_directory(directory)) {
347 directory = directory.parent_path();
348 }
349 internal::LocalFileOperations operations(directory.string());
Austin Schuh95460cc2023-06-26 11:53:10 -0700350 std::vector<internal::FileOperations::File> files;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700351 operations.FindLogs(&files);
352
Austin Schuh95460cc2023-06-26 11:53:10 -0700353 std::vector<File> names;
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700354 const std::string prefix = absl::StrCat(base_name_, separator_);
355 for (const auto &file : files) {
Austin Schuh95460cc2023-06-26 11:53:10 -0700356 CHECK(absl::StartsWith(file.name, prefix))
357 << ": File " << file.name << ", prefix " << prefix;
358 names.emplace_back(File{
359 .name = file.name.substr(prefix.size()),
360 .size = file.size,
361 });
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700362 }
363 return names;
364}
365
366std::unique_ptr<DataDecoder> FileBackend::GetDecoder(
Austin Schuh95460cc2023-06-26 11:53:10 -0700367 const std::string_view id) const {
Alexei Strotsf08b8fb2023-04-21 19:46:08 -0700368 const std::string filename = absl::StrCat(base_name_, separator_, id);
369 CHECK(std::filesystem::exists(filename));
370 return std::make_unique<DummyDecoder>(filename);
371}
372
colleen61276dc2023-06-01 09:23:29 -0700373RenamableFileBackend::RenamableFileBackend(std::string_view base_name,
374 bool supports_odirect)
375 : supports_odirect_(supports_odirect),
376 base_name_(base_name),
377 separator_(base_name_.back() == '/' ? "" : "_") {}
Alexei Strots01395492023-03-20 13:59:56 -0700378
Alexei Strotsbc082d82023-05-03 08:43:42 -0700379std::unique_ptr<LogSink> RenamableFileBackend::RequestFile(
Austin Schuh95460cc2023-06-26 11:53:10 -0700380 const std::string_view id) {
Alexei Strots01395492023-03-20 13:59:56 -0700381 const std::string filename =
382 absl::StrCat(base_name_, separator_, id, temp_suffix_);
colleen61276dc2023-06-01 09:23:29 -0700383 return std::make_unique<RenamableFileHandler>(this, filename,
384 supports_odirect_);
Alexei Strots01395492023-03-20 13:59:56 -0700385}
386
387void RenamableFileBackend::EnableTempFiles() {
388 use_temp_files_ = true;
389 temp_suffix_ = kTempExtension;
390}
391
392bool RenamableFileBackend::RenameLogBase(std::string_view new_base_name) {
393 if (new_base_name == base_name_) {
394 return true;
395 }
396 CHECK(old_base_name_.empty())
397 << "Only one change of base_name is supported. Was: " << old_base_name_;
398
399 std::string current_directory = base_name_;
400 std::string new_directory(new_base_name);
401
402 auto current_path_split = current_directory.rfind("/");
403 CHECK(current_path_split != std::string::npos)
404 << "Could not find / in the current directory path";
405 auto new_path_split = new_directory.rfind("/");
406 CHECK(new_path_split != std::string::npos)
407 << "Could not find / in the new directory path";
408
409 CHECK(new_base_name.substr(new_path_split) ==
410 current_directory.substr(current_path_split))
411 << "Rename of file base from " << current_directory << " to "
412 << new_directory << " is not supported.";
413
414 current_directory.resize(current_path_split);
415 new_directory.resize(new_path_split);
416 DIR *dir = opendir(current_directory.c_str());
417 if (dir) {
418 closedir(dir);
419 const int result = rename(current_directory.c_str(), new_directory.c_str());
420 if (result != 0) {
421 PLOG(ERROR) << "Unable to rename " << current_directory << " to "
422 << new_directory;
423 return false;
424 }
425 } else {
426 // Handle if directory was already renamed.
427 dir = opendir(new_directory.c_str());
428 if (!dir) {
429 LOG(ERROR) << "Old directory " << current_directory
430 << " missing and new directory " << new_directory
431 << " not present.";
432 return false;
433 }
434 closedir(dir);
435 }
436 old_base_name_ = base_name_;
437 base_name_ = std::string(new_base_name);
438 separator_ = base_name_.back() == '/' ? "" : "_";
439 return true;
440}
441
442WriteCode RenamableFileBackend::RenameFileAfterClose(
443 std::string_view filename) {
444 // Fast check that we can skip rename.
445 if (!use_temp_files_ && old_base_name_.empty()) {
446 return WriteCode::kOk;
447 }
448
449 std::string current_filename(filename);
450
451 // When changing the base name, we rename the log folder while there active
452 // buffer writers. Therefore, the name of that active buffer may still refer
453 // to the old file location rather than the new one.
454 if (!old_base_name_.empty()) {
455 auto offset = current_filename.find(old_base_name_);
456 if (offset != std::string::npos) {
457 current_filename.replace(offset, old_base_name_.length(), base_name_);
458 }
459 }
460
461 std::string final_filename = current_filename;
462 if (use_temp_files_) {
463 CHECK(current_filename.size() > temp_suffix_.size());
464 final_filename = current_filename.substr(
465 0, current_filename.size() - temp_suffix_.size());
466 }
467
468 int result = rename(current_filename.c_str(), final_filename.c_str());
469
470 bool ran_out_of_space = false;
471 if (result != 0) {
472 if (errno == ENOSPC) {
473 ran_out_of_space = true;
474 } else {
475 PLOG(FATAL) << "Renaming " << current_filename << " to " << final_filename
476 << " failed";
477 }
478 } else {
479 VLOG(1) << "Renamed " << current_filename << " -> " << final_filename;
480 }
481 return ran_out_of_space ? WriteCode::kOutOfSpace : WriteCode::kOk;
482}
483
484WriteCode RenamableFileBackend::RenamableFileHandler::Close() {
485 if (!is_open()) {
486 return WriteCode::kOk;
487 }
488 if (FileHandler::Close() == WriteCode::kOutOfSpace) {
489 return WriteCode::kOutOfSpace;
490 }
491 if (owner_->RenameFileAfterClose(filename()) == WriteCode::kOutOfSpace) {
492 return WriteCode::kOutOfSpace;
493 }
494 return WriteCode::kOk;
495}
Alexei Strotsa0b99d72023-04-11 15:12:42 -0700496
Austin Schuh3ebaf782023-04-07 16:03:28 -0700497} // namespace aos::logger