| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <sys/mount.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <stack> |
| #include <string> |
| #include <utility> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/threading/platform_thread.h> |
| #include <base/values.h> |
| #include <brillo/process/process.h> |
| #include <libstorage/platform/platform.h> |
| |
| #include "init/startup/flags.h" |
| #include "init/startup/mount_helper.h" |
| #include "init/startup/mount_var_home_impl.h" |
| #include "init/startup/mount_var_home_interface.h" |
| #include "init/startup/startup_dep_impl.h" |
| |
| namespace startup { |
| |
| MountHelper::MountHelper(libstorage::Platform* platform, |
| StartupDep* startup_dep, |
| const Flags& flags, |
| const base::FilePath& root, |
| const base::FilePath& stateful, |
| std::unique_ptr<MountVarAndHomeChronosInterface> impl) |
| : platform_(platform), |
| startup_dep_(startup_dep), |
| flags_(flags), |
| root_(root), |
| stateful_(stateful), |
| impl_(std::move(impl)) {} |
| |
| MountHelper::MountHelper(libstorage::Platform* platform, |
| StartupDep* startup_dep, |
| const Flags& flags, |
| const base::FilePath& root, |
| const base::FilePath& stateful) |
| : MountHelper(platform, |
| startup_dep, |
| flags, |
| root, |
| stateful, |
| std::make_unique<MountVarAndHomeChronosImpl>( |
| platform, startup_dep, root, stateful)) {} |
| |
| MountHelper::~MountHelper() = default; |
| |
| // Adds mounts to undo_mount stack. |
| void MountHelper::RememberMount(const base::FilePath& mount) { |
| mount_stack_.push(mount); |
| } |
| |
| void MountHelper::CleanupMountsStack(std::vector<base::FilePath>* mnts) { |
| // On failure unmount all saved mount points and repair stateful. |
| base::FilePath encrypted = stateful_.Append("encrypted"); |
| while (!mount_stack_.empty()) { |
| base::FilePath mnt = mount_stack_.top(); |
| mnts->push_back(mnt); |
| if (mnt == encrypted) { |
| DoUmountVarAndHomeChronos(); |
| } else { |
| platform_->Unmount(mnt, false, nullptr); |
| } |
| mount_stack_.pop(); |
| } |
| } |
| |
| // Unmounts the incomplete mount setup during the failure path. Failure to |
| // set up mounts results in the entire stateful partition getting wiped |
| // using clobber-state. |
| void MountHelper::CleanupMounts(const std::string& msg) { |
| // On failure unmount all saved mount points and repair stateful. |
| std::vector<base::FilePath> mounts; |
| CleanupMountsStack(&mounts); |
| |
| // Leave /mnt/stateful_partition mounted for clobber-state to handle. |
| startup_dep_->BootAlert("self_repair"); |
| |
| std::string mounts_str; |
| for (base::FilePath mount : mounts) { |
| mounts_str.append(mount.value()); |
| mounts_str.append(", "); |
| } |
| std::string message = "Self-repair incoherent stateful partition: " + msg + |
| ". History: " + mounts_str; |
| LOG(INFO) << message; |
| startup_dep_->ClobberLog(message); |
| |
| base::FilePath tmpfiles = root_.Append("run/tmpfiles.log"); |
| std::unique_ptr<brillo::Process> append_log = |
| platform_->CreateProcessInstance(); |
| append_log->AddArg("/sbin/clobber-log"); |
| append_log->AddArg("--append_logfile"); |
| append_log->AddArg(tmpfiles.value()); |
| if (!append_log->Run()) { |
| LOG(WARNING) << "clobber-log --append_logfile failed"; |
| } |
| |
| std::vector<std::string> crash_args{"--clobber_state"}; |
| startup_dep_->AddClobberCrashReport(crash_args); |
| |
| std::vector<std::string> argv{"fast", "keepimg", "preserve_lvs"}; |
| startup_dep_->Clobber(argv); |
| } |
| |
| // Used to mount essential mount points for the system from the stateful |
| // or encrypted stateful partition. |
| // On failure, clobbers the stateful partition. |
| void MountHelper::BindMountOrFail(const base::FilePath& source, |
| const base::FilePath& target) { |
| if (platform_->DirectoryExists(source) && |
| platform_->DirectoryExists(target)) { |
| if (platform_->Mount(source, target, "", MS_BIND, "")) { |
| // Push it on the undo stack if we fail later. |
| RememberMount(target); |
| return; |
| } |
| } |
| std::string msg = |
| "Failed to bind mount " + source.value() + ", " + target.value(); |
| CleanupMounts(msg); |
| } |
| |
| bool MountHelper::MountVarAndHomeChronosEncrypted() { |
| return impl_->MountEncrypted(); |
| } |
| |
| bool MountHelper::UmountVarAndHomeChronosEncrypted() { |
| return impl_->UmountEncrypted(); |
| } |
| |
| bool MountHelper::MountVarAndHomeChronosUnencrypted() { |
| return impl_->MountUnencrypted(); |
| } |
| |
| bool MountHelper::UmountVarAndHomeChronosUnencrypted() { |
| return impl_->UmountUnencrypted(); |
| } |
| |
| bool MountHelper::MountVarAndHomeChronos() { |
| if (flags_.encstateful) { |
| return MountVarAndHomeChronosEncrypted(); |
| } |
| return MountVarAndHomeChronosUnencrypted(); |
| } |
| |
| bool MountHelper::DoUmountVarAndHomeChronos() { |
| if (flags_.encstateful) { |
| return UmountVarAndHomeChronosEncrypted(); |
| } |
| return UmountVarAndHomeChronosUnencrypted(); |
| } |
| |
| } // namespace startup |