// Copyright 2014 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/feedback/feedback_util.h" #include #include "base/bind.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "build/build_config.h" #include "components/feedback/feedback_report.h" #include "third_party/zlib/google/zip.h" namespace { constexpr char kMultilineIndicatorString[] = "\n"; constexpr char kMultilineStartString[] = "---------- START ----------\n"; constexpr char kMultilineEndString[] = "---------- END ----------\n\n"; } // namespace namespace feedback_util { bool ZipString(const base::FilePath& filename, const std::string& data, std::string* compressed_logs) { base::ScopedTempDir temp_dir; base::FilePath zip_file; // Create a temporary directory, put the logs into a file in it. Create // another temporary file to receive the zip file in. if (!temp_dir.CreateUniqueTempDir()) return false; if (base::WriteFile(temp_dir.GetPath().Append(filename), data.c_str(), data.size()) == -1) { return false; } bool succeed = base::CreateTemporaryFile(&zip_file) && zip::Zip(temp_dir.GetPath(), zip_file, false) && base::ReadFileToString(zip_file, compressed_logs); base::DeleteFile(zip_file); return succeed; } std::string LogsToString(const FeedbackCommon::SystemLogsMap& sys_info) { std::string syslogs_string; for (const auto& iter : sys_info) { std::string key = iter.first; base::TrimString(key, "\n ", &key); if (key == feedback::FeedbackReport::kCrashReportIdsKey || key == feedback::FeedbackReport::kAllCrashReportIdsKey) { // Avoid adding the crash IDs to the system_logs.txt file for privacy // reasons. They should just be part of the product specific data. continue; } std::string value = iter.second; base::TrimString(value, "\n ", &value); if (value.find("\n") != std::string::npos) { syslogs_string.append(key + "=" + kMultilineIndicatorString + kMultilineStartString + value + "\n" + kMultilineEndString); } else { syslogs_string.append(key + "=" + value + "\n"); } } return syslogs_string; } // Note: This function is excluded from win build because its unit tests do // not pass on OS_WIN. // This function is only called on ChromeOS and Lacros build. // See https://crbug.com/1119560. #if !BUILDFLAG(IS_WIN) bool ReadEndOfFile(const base::FilePath& path, size_t max_size, std::string* contents) { if (!contents) { LOG(ERROR) << "contents buffer is null."; return false; } if (path.ReferencesParent()) { LOG(ERROR) << "ReadEndOfFile can't be called on file paths with parent " "references."; return false; } base::ScopedFILE fp(base::OpenFile(path, "r")); if (!fp) { PLOG(ERROR) << "Failed to open file " << path.value(); return false; } std::unique_ptr chunk(new char[max_size]); std::unique_ptr last_chunk(new char[max_size]); chunk[0] = '\0'; last_chunk[0] = '\0'; size_t total_bytes_read = 0; size_t bytes_read = 0; // Since most logs are not seekable, read until the end keeping tracking of // last two chunks. while ((bytes_read = fread(chunk.get(), 1, max_size, fp.get())) == max_size) { total_bytes_read += bytes_read; last_chunk.swap(chunk); chunk[0] = '\0'; } total_bytes_read += bytes_read; if (total_bytes_read < max_size) { // File is smaller than max_size contents->assign(chunk.get(), bytes_read); } else if (bytes_read == 0) { // File is exactly max_size or a multiple of max_size contents->assign(last_chunk.get(), max_size); } else { // Number of bytes to keep from last_chunk size_t bytes_from_last = max_size - bytes_read; // Shift left last_chunk by size of chunk and fit it in the back of // last_chunk. memmove(last_chunk.get(), last_chunk.get() + bytes_read, bytes_from_last); memcpy(last_chunk.get() + bytes_from_last, chunk.get(), bytes_read); contents->assign(last_chunk.get(), max_size); } return true; } #endif // !BUILDFLAG(IS_WIN) } // namespace feedback_util