| // Copyright 2022 The Chromium 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 "components/reporting/util/file.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/strings/strcat.h" |
| |
| namespace reporting { |
| |
| bool DeleteFileWarnIfFailed(const base::FilePath& path) { |
| const auto delete_result = base::DeleteFile(path); |
| if (!delete_result) { |
| LOG(WARNING) << "Failed to delete " << path.MaybeAsASCII(); |
| } |
| return delete_result; |
| } |
| |
| bool DeleteFilesWarnIfFailed( |
| base::FileEnumerator& dir_enum, |
| base::RepeatingCallback<bool(const base::FilePath&)> pred) { |
| std::vector<base::FilePath> files_to_delete; |
| for (auto full_name = dir_enum.Next(); !full_name.empty(); |
| full_name = dir_enum.Next()) { |
| if (pred.Run(full_name)) { |
| files_to_delete.push_back(std::move(full_name)); |
| } |
| } |
| bool success = true; |
| for (const auto& file_to_delete : files_to_delete) { |
| if (!DeleteFileWarnIfFailed(file_to_delete)) { |
| success = false; |
| } |
| } |
| return success; |
| } |
| |
| StatusOr<std::string> MaybeReadFile(const base::FilePath& file_path, |
| int64_t offset) { |
| base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| if (!file.IsValid()) { |
| return Status(error::NOT_FOUND, |
| base::StrCat({"Could not open health data file ", |
| file_path.MaybeAsASCII()})); |
| } |
| |
| base::File::Info file_info; |
| if (!file.GetInfo(&file_info) || file_info.size - offset < 0) { |
| return Status(error::DATA_LOSS, |
| base::StrCat({"Failed to read data file info ", |
| file_path.MaybeAsASCII()})); |
| } |
| |
| std::string result; |
| result.resize(file_info.size - offset); |
| const int read_result = |
| file.Read(offset, result.data(), file_info.size - offset); |
| if (read_result != file_info.size - offset) { |
| return Status(error::DATA_LOSS, base::StrCat({"Failed to read data file ", |
| file_path.MaybeAsASCII()})); |
| } |
| |
| return result; |
| } |
| |
| Status AppendLine(const base::FilePath& file_path, |
| const base::StringPiece& data) { |
| base::File file(file_path, |
| base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND); |
| if (!file.IsValid()) { |
| return Status(error::NOT_FOUND, |
| base::StrCat({"Could not open health data file ", |
| file_path.MaybeAsASCII()})); |
| } |
| |
| const std::string line = base::StrCat({data, "\n"}); |
| const int write_count = file.Write(0, line.data(), line.size()); |
| if (write_count < 0 || static_cast<size_t>(write_count) < line.size()) { |
| return Status(error::DATA_LOSS, |
| base::StrCat({"Failed to write health data file ", |
| file_path.MaybeAsASCII(), |
| " write count=", std::to_string(write_count)})); |
| } |
| return Status::StatusOK(); |
| } |
| |
| StatusOr<uint32_t> RemoveAndTruncateLine(const base::FilePath& file_path, |
| uint32_t pos) { |
| StatusOr<std::string> status_or = MaybeReadFile(file_path, pos); |
| if (!status_or.ok()) { |
| return status_or.status(); |
| } |
| std::string content = status_or.ValueOrDie(); |
| uint32_t offset = 0; |
| // Search for next new line after pos. |
| while (offset < content.length()) { |
| if (content.at(offset++) == '\n') { |
| break; |
| } |
| } |
| |
| // Check if the last line was removed. |
| if (offset >= content.length()) { |
| content = ""; |
| } else { |
| content = content.substr(offset); |
| } |
| |
| Status status = MaybeWriteFile(file_path, content); |
| if (!status.ok()) { |
| return status; |
| } |
| return pos + offset; |
| } |
| |
| Status MaybeWriteFile(const base::FilePath& file_path, |
| const base::StringPiece& data) { |
| base::File file(file_path, |
| base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| if (!file.IsValid()) { |
| return Status(error::NOT_FOUND, base::StrCat({"Could not open data file ", |
| file_path.MaybeAsASCII()})); |
| } |
| |
| const int write_count = file.Write(0, data.data(), data.size()); |
| if (write_count < 0 || static_cast<size_t>(write_count) < data.size()) { |
| return Status( |
| error::DATA_LOSS, |
| base::StrCat({"Failed to write data file ", file_path.MaybeAsASCII(), |
| " write count=", std::to_string(write_count)})); |
| } |
| |
| return Status::StatusOK(); |
| } |
| } // namespace reporting |