blob: 22340956c285989293f7b9a302bdd9ac9c04478b [file] [log] [blame]
// 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