| // 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 <string> |
| |
| #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 "components/feedback/feedback_report.h" |
| #include "third_party/zlib/google/zip.h" |
| |
| namespace { |
| |
| constexpr char kMultilineIndicatorString[] = "<multiline>\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 !defined(OS_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<char[]> chunk(new char[max_size]); |
| std::unique_ptr<char[]> 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 // !OS_WIN |
| |
| } // namespace feedback_util |