| // 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 "cros-disks/partition_manager.h" |
| |
| #include <utility> |
| #include <unistd.h> |
| #include <vector> |
| |
| #include <linux/capability.h> |
| #include <base/files/file.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/containers/contains.h> |
| #include <base/strings/stringprintf.h> |
| |
| #include "cros-disks/disk.h" |
| #include "cros-disks/disk_monitor.h" |
| #include "cros-disks/quote.h" |
| |
| namespace cros_disks { |
| |
| namespace { |
| |
| const char kPartitionProgramPath[] = "/sbin/sfdisk"; |
| |
| // MBR 2TB limit: (2^32 -1) partition size in sectors * 512 bytes/sectors |
| const uint64_t kMBRMaxSize = 2199023255040ULL; |
| |
| // Initialises the process for partitionting and starts it. |
| PartitionErrorType StartPartitionProcess(const base::FilePath& device_file, |
| const std::string& partition_program, |
| const std::string& label_type, |
| const std::string& partition_input, |
| SandboxedProcess* process) { |
| process->SetNoNewPrivileges(); |
| process->NewMountNamespace(); |
| process->NewIpcNamespace(); |
| process->NewNetworkNamespace(); |
| process->SetCapabilities(CAP_TO_MASK(CAP_SYS_ADMIN)); |
| |
| if (!process->EnterPivotRoot()) { |
| LOG(WARNING) << "Could not enter pivot root"; |
| return PARTITION_ERROR_PROGRAM_FAILED; |
| } |
| if (!process->SetUpMinimalMounts()) { |
| LOG(WARNING) << "Could not set up minimal mounts for jail"; |
| return PARTITION_ERROR_PROGRAM_FAILED; |
| } |
| |
| // Open device_file so we can pass only the fd path to the partition program. |
| base::File dev_file(device_file, base::File::FLAG_OPEN | |
| base::File::FLAG_READ | |
| base::File::FLAG_WRITE); |
| if (!dev_file.IsValid()) { |
| LOG(WARNING) << "Could not open " << quote(device_file) |
| << " for partitioning"; |
| return PARTITION_ERROR_PROGRAM_FAILED; |
| } |
| |
| if (!process->PreserveFile(dev_file)) { |
| LOG(WARNING) << "Could not preserve device fd"; |
| return PARTITION_ERROR_PROGRAM_FAILED; |
| } |
| |
| process->CloseOpenFds(); |
| |
| process->AddArgument(partition_program); |
| process->AddArgument("--no-reread"); |
| process->AddArgument("--label"); |
| process->AddArgument(label_type); |
| |
| process->AddArgument("--wipe"); |
| process->AddArgument("always"); |
| process->AddArgument("--wipe-partitions"); |
| process->AddArgument("always"); |
| |
| process->AddArgument( |
| base::StringPrintf("/dev/fd/%d", dev_file.GetPlatformFile())); |
| |
| process->SetStdIn(partition_input); |
| |
| if (!process->Start()) { |
| LOG(WARNING) << "Cannot start process " << quote(partition_program) |
| << " to partition " << quote(device_file); |
| return PARTITION_ERROR_PROGRAM_FAILED; |
| } |
| |
| return PARTITION_ERROR_NONE; |
| } |
| |
| } // namespace |
| |
| void PartitionManager::StartSinglePartitionFormat( |
| const base::FilePath& device_path, |
| cros_disks::PartitionCompleteCallback callback) { |
| if (!base::PathExists(base::FilePath(kPartitionProgramPath))) { |
| LOG(WARNING) << "Could not find a partition program " |
| << quote(kPartitionProgramPath); |
| std::move(callback).Run(device_path, PARTITION_ERROR_PROGRAM_NOT_FOUND); |
| return; |
| } |
| |
| if (device_path.empty()) { |
| LOG(ERROR) << "Device path is empty"; |
| std::move(callback).Run(device_path, PARTITION_ERROR_INVALID_DEVICE_PATH); |
| return; |
| } |
| |
| if (base::Contains(partition_process_, device_path)) { |
| LOG(WARNING) << "Device " << quote(device_path) |
| << " is already being partitioned"; |
| std::move(callback).Run(device_path, |
| PARTITION_ERROR_DEVICE_BEING_PARTITIONED); |
| return; |
| } |
| |
| Disk disk; |
| if (!disk_monitor_->GetDiskByDevicePath(device_path, &disk)) { |
| LOG(ERROR) << "Could not get the properties of device " + |
| device_path.value(); |
| std::move(callback).Run(device_path, PARTITION_ERROR_UNKNOWN); |
| return; |
| } |
| |
| std::string label_type; |
| std::string partition_type; |
| // MBR only supports <2TB disks. |
| if (disk.device_capacity < kMBRMaxSize) { |
| label_type = "mbr"; |
| // Hex code for partition type of FAT32 with LBA |
| partition_type = "id=c"; |
| } else { |
| label_type = "gpt"; |
| // Basic data partition (BDP) GUID |
| partition_type = "type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"; |
| } |
| |
| std::unique_ptr<SandboxedProcess> process = CreateSandboxedProcess(); |
| partition_process_.insert(device_path); |
| |
| PartitionErrorType error = |
| StartPartitionProcess(device_path, kPartitionProgramPath, label_type, |
| partition_type, process.get()); |
| if (error != PARTITION_ERROR_NONE) { |
| partition_process_.erase(device_path); |
| std::move(callback).Run(device_path, error); |
| return; |
| } else { |
| process_reaper_->WatchForChild( |
| FROM_HERE, process->pid(), |
| base::BindOnce(&PartitionManager::OnPartitionProcessTerminated, |
| weak_ptr_factory_.GetWeakPtr(), device_path, |
| std::move(callback))); |
| } |
| } |
| |
| void PartitionManager::OnPartitionProcessTerminated( |
| const base::FilePath& device_path, |
| cros_disks::PartitionCompleteCallback callback, |
| const siginfo_t& info) { |
| partition_process_.erase(device_path); |
| PartitionErrorType error_type = PARTITION_ERROR_UNKNOWN; |
| switch (info.si_code) { |
| case CLD_EXITED: |
| if (info.si_status == 0) { |
| error_type = PARTITION_ERROR_NONE; |
| LOG(INFO) << "Process " << info.si_pid << " for partitionting " |
| << quote(device_path) << " completed successfully"; |
| } else { |
| error_type = PARTITION_ERROR_PROGRAM_FAILED; |
| LOG(ERROR) << "Process " << info.si_pid << " for partitionting " |
| << quote(device_path) << " exited with a status " |
| << info.si_status; |
| } |
| break; |
| |
| case CLD_DUMPED: |
| case CLD_KILLED: |
| error_type = PARTITION_ERROR_PROGRAM_FAILED; |
| LOG(ERROR) << "Process " << info.si_pid << " for partitionting " |
| << quote(device_path) << " killed by a signal " |
| << info.si_status; |
| break; |
| |
| default: |
| break; |
| } |
| std::move(callback).Run(device_path, error_type); |
| } |
| |
| std::unique_ptr<SandboxedProcess> PartitionManager::CreateSandboxedProcess() |
| const { |
| return std::make_unique<SandboxedProcess>(); |
| } |
| |
| } // namespace cros_disks |