| // Copyright 2020 The Chromium OS 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 "crash-reporter/mount_failure_collector.h" |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/rand_util.h> |
| #include <base/strings/stringprintf.h> |
| |
| #include "crash-reporter/constants.h" |
| #include "crash-reporter/paths.h" |
| #include "crash-reporter/util.h" |
| |
| namespace { |
| const char kEncryptedStatefulDeviceLabel[] = "encstateful"; |
| const char kStatefulDeviceLabel[] = "stateful"; |
| const char kCryptohomeDeviceLabel[] = "cryptohome"; |
| const char kInvalidDeviceLabel[] = "invalid"; |
| |
| std::vector<std::string> ConstructLoggingCommands(StorageDeviceType device_type, |
| bool is_mount_failure) { |
| std::vector<std::string> cmds; |
| switch (device_type) { |
| case StorageDeviceType::kStateful: |
| if (is_mount_failure) |
| cmds = {"dumpe2fs_stateful", "kernel-warning", "console-ramoops"}; |
| else |
| cmds = {"shutdown_umount_failure_state", "umount-encrypted"}; |
| break; |
| case StorageDeviceType::kEncryptedStateful: |
| cmds = {"dumpe2fs_encstateful", "kernel-warning", "console-ramoops", |
| "mount-encrypted"}; |
| break; |
| case StorageDeviceType::kCryptohome: |
| cmds = {"cryptohome", "kernel-warning"}; |
| break; |
| default: |
| break; |
| } |
| return cmds; |
| } |
| |
| } // namespace |
| |
| MountFailureCollector::MountFailureCollector(StorageDeviceType device_type, |
| bool testonly_send_all) |
| : CrashCollector("mount_failure_collector"), |
| device_type_(device_type), |
| testonly_send_all_(testonly_send_all) {} |
| |
| // static |
| StorageDeviceType MountFailureCollector::ValidateStorageDeviceType( |
| const std::string& device_label) { |
| if (device_label == kStatefulDeviceLabel) |
| return StorageDeviceType::kStateful; |
| else if (device_label == kEncryptedStatefulDeviceLabel) |
| return StorageDeviceType::kEncryptedStateful; |
| else if (device_label == kCryptohomeDeviceLabel) |
| return StorageDeviceType::kCryptohome; |
| else |
| return StorageDeviceType::kInvalidDevice; |
| } |
| |
| // static |
| std::string MountFailureCollector::StorageDeviceTypeToString( |
| StorageDeviceType device_type) { |
| switch (device_type) { |
| case StorageDeviceType::kStateful: |
| return kStatefulDeviceLabel; |
| case StorageDeviceType::kEncryptedStateful: |
| return kEncryptedStatefulDeviceLabel; |
| case StorageDeviceType::kCryptohome: |
| return kCryptohomeDeviceLabel; |
| default: |
| return kInvalidDeviceLabel; |
| } |
| } |
| |
| bool MountFailureCollector::Collect(bool is_mount_failure) { |
| if (device_type_ == StorageDeviceType::kInvalidDevice) { |
| LOG(ERROR) << "Invalid storage device."; |
| return true; |
| } |
| |
| if (device_type_ == StorageDeviceType::kStateful && !is_mount_failure) { |
| // Sample umount_failure_stateful crashes, as they're a significant source |
| // of noise. |
| if (!testonly_send_all_ && |
| base::RandGenerator(util::GetUmountStatefulFailureWeight()) != 0) { |
| LOG(INFO) << "Skipping umount failure"; |
| return true; |
| } |
| AddCrashMetaUploadData( |
| "weight", |
| base::StringPrintf("%d", util::GetUmountStatefulFailureWeight())); |
| } |
| |
| std::string device_label = StorageDeviceTypeToString(device_type_); |
| std::string exec_name = (is_mount_failure ? "mount" : "umount"); |
| exec_name += "_failure_" + device_label; |
| std::string dump_basename = FormatDumpBasename(exec_name, time(nullptr), 0); |
| |
| auto logging_cmds = ConstructLoggingCommands(device_type_, is_mount_failure); |
| |
| base::FilePath crash_directory; |
| if (!GetCreatedCrashDirectoryByEuid(constants::kRootUid, &crash_directory, |
| nullptr)) { |
| return true; |
| } |
| |
| // Use exec name as the crash signature. |
| AddCrashMetaData("sig", exec_name); |
| |
| base::FilePath log_path = GetCrashPath(crash_directory, dump_basename, "log"); |
| base::FilePath meta_path = |
| GetCrashPath(crash_directory, dump_basename, "meta"); |
| |
| bool result = |
| GetMultipleLogContents(log_config_path_, logging_cmds, log_path); |
| if (result) { |
| FinishCrash(meta_path, exec_name, log_path.BaseName().value()); |
| } |
| |
| return true; |
| } |
| |
| // static |
| CollectorInfo MountFailureCollector::GetHandlerInfo( |
| const std::string& mount_device, |
| bool testonly_send_all, |
| bool mount_failure, |
| bool umount_failure) { |
| auto mount_failure_collector = std::make_shared<MountFailureCollector>( |
| ValidateStorageDeviceType(mount_device), testonly_send_all); |
| return { |
| .collector = mount_failure_collector, |
| .handlers = {{ |
| .should_handle = mount_failure || umount_failure, |
| .cb = base::BindRepeating(&MountFailureCollector::Collect, |
| mount_failure_collector, mount_failure), |
| }}, |
| }; |
| } |