blob: 7c4558de5ebc114425c0cb74388034d5c4ae0166 [file] [log] [blame]
Omid Tourzan64856322020-08-28 13:51:03 +10001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cros-disks/partition_manager.h"
6
7#include <utility>
8#include <unistd.h>
9#include <vector>
10
11#include <linux/capability.h>
12#include <base/files/file.h>
13#include <base/files/file_util.h>
14#include <base/logging.h>
15#include <base/stl_util.h>
16#include <base/strings/stringprintf.h>
17
18#include "cros-disks/disk.h"
19#include "cros-disks/disk_monitor.h"
20#include "cros-disks/quote.h"
21
22namespace cros_disks {
23
24namespace {
25
26const char kPartitionProgramPath[] = "/sbin/sfdisk";
27
28// MBR 2TB limit: (2^32 -1) partition size in sectors * 512 bytes/sectors
29const uint64_t kMBRMaxSize = 2199023255040ULL;
30
31// Initialises the process for partitionting and starts it.
32PartitionErrorType StartPartitionProcess(const base::FilePath& device_file,
33 const std::string& partition_program,
34 const std::string& label_type,
35 const std::string& partition_input,
36 SandboxedProcess* process) {
37 process->SetNoNewPrivileges();
38 process->NewMountNamespace();
39 process->NewIpcNamespace();
40 process->NewNetworkNamespace();
41 process->SetCapabilities(CAP_TO_MASK(CAP_SYS_ADMIN));
42
43 if (!process->EnterPivotRoot()) {
44 LOG(WARNING) << "Could not enter pivot root";
45 return PARTITION_ERROR_PROGRAM_FAILED;
46 }
47 if (!process->SetUpMinimalMounts()) {
48 LOG(WARNING) << "Could not set up minimal mounts for jail";
49 return PARTITION_ERROR_PROGRAM_FAILED;
50 }
51
52 // Open device_file so we can pass only the fd path to the partition program.
53 base::File dev_file(device_file, base::File::FLAG_OPEN |
54 base::File::FLAG_READ |
55 base::File::FLAG_WRITE);
56 if (!dev_file.IsValid()) {
57 LOG(WARNING) << "Could not open " << quote(device_file)
58 << " for partitioning";
59 return PARTITION_ERROR_PROGRAM_FAILED;
60 }
61
62 if (!process->PreserveFile(dev_file)) {
63 LOG(WARNING) << "Could not preserve device fd";
64 return PARTITION_ERROR_PROGRAM_FAILED;
65 }
66
67 process->CloseOpenFds();
68
69 process->AddArgument(partition_program);
70 process->AddArgument("--no-reread");
71 process->AddArgument("--label");
72 process->AddArgument(label_type);
73
74 process->AddArgument("--wipe");
75 process->AddArgument("always");
76 process->AddArgument("--wipe-partitions");
77 process->AddArgument("always");
78
79 process->AddArgument(
80 base::StringPrintf("/dev/fd/%d", dev_file.GetPlatformFile()));
81
82 process->SetStdIn(partition_input);
83
84 if (!process->Start()) {
85 LOG(WARNING) << "Cannot start process " << quote(partition_program)
86 << " to partition " << quote(device_file);
87 return PARTITION_ERROR_PROGRAM_FAILED;
88 }
89
90 return PARTITION_ERROR_NONE;
91}
92
93} // namespace
94
95void PartitionManager::StartSinglePartitionFormat(
96 const base::FilePath& device_path,
97 cros_disks::PartitionCompleteCallback callback) {
98 if (!base::PathExists(base::FilePath(kPartitionProgramPath))) {
99 LOG(WARNING) << "Could not find a partition program "
100 << quote(kPartitionProgramPath);
101 std::move(callback).Run(device_path, PARTITION_ERROR_PROGRAM_NOT_FOUND);
102 return;
103 }
104
105 if (device_path.empty()) {
106 LOG(ERROR) << "Device path is empty";
107 std::move(callback).Run(device_path, PARTITION_ERROR_INVALID_DEVICE_PATH);
108 return;
109 }
110
111 if (base::Contains(partition_process_, device_path)) {
112 LOG(WARNING) << "Device " << quote(device_path)
113 << " is already being partitioned";
114 std::move(callback).Run(device_path,
115 PARTITION_ERROR_DEVICE_BEING_PARTITIONED);
116 return;
117 }
118
119 Disk disk;
120 if (!disk_monitor_->GetDiskByDevicePath(device_path, &disk)) {
121 LOG(ERROR) << "Could not get the properties of device " +
122 device_path.value();
123 std::move(callback).Run(device_path, PARTITION_ERROR_UNKNOWN);
124 return;
125 }
126
127 std::string label_type;
128 std::string partition_type;
129 // MBR only supports <2TB disks.
130 if (disk.device_capacity < kMBRMaxSize) {
131 label_type = "mbr";
132 // Hex code for partition type of FAT32 with LBA
133 partition_type = "id=c";
134 } else {
135 label_type = "gpt";
136 // Basic data partition (BDP) GUID
137 partition_type = "type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
138 }
139
140 std::unique_ptr<SandboxedProcess> process = CreateSandboxedProcess();
141 partition_process_.insert(device_path);
142
143 PartitionErrorType error =
144 StartPartitionProcess(device_path, kPartitionProgramPath, label_type,
145 partition_type, process.get());
146 if (error != PARTITION_ERROR_NONE) {
147 partition_process_.erase(device_path);
148 std::move(callback).Run(device_path, error);
149 return;
150 } else {
151 process_reaper_->WatchForChild(
152 FROM_HERE, process->pid(),
153 base::BindOnce(&PartitionManager::OnPartitionProcessTerminated,
154 weak_ptr_factory_.GetWeakPtr(), device_path,
155 std::move(callback)));
156 }
157}
158
159void PartitionManager::OnPartitionProcessTerminated(
160 const base::FilePath& device_path,
161 cros_disks::PartitionCompleteCallback callback,
162 const siginfo_t& info) {
163 partition_process_.erase(device_path);
164 PartitionErrorType error_type = PARTITION_ERROR_UNKNOWN;
165 switch (info.si_code) {
166 case CLD_EXITED:
167 if (info.si_status == 0) {
168 error_type = PARTITION_ERROR_NONE;
169 LOG(INFO) << "Process " << info.si_pid << " for partitionting "
170 << quote(device_path) << " completed successfully";
171 } else {
172 error_type = PARTITION_ERROR_PROGRAM_FAILED;
173 LOG(ERROR) << "Process " << info.si_pid << " for partitionting "
174 << quote(device_path) << " exited with a status "
175 << info.si_status;
176 }
177 break;
178
179 case CLD_DUMPED:
180 case CLD_KILLED:
181 error_type = PARTITION_ERROR_PROGRAM_FAILED;
182 LOG(ERROR) << "Process " << info.si_pid << " for partitionting "
183 << quote(device_path) << " killed by a signal "
184 << info.si_status;
185 break;
186
187 default:
188 break;
189 }
190 std::move(callback).Run(device_path, error_type);
191}
192
193std::unique_ptr<SandboxedProcess> PartitionManager::CreateSandboxedProcess()
194 const {
195 return std::make_unique<SandboxedProcess>();
196}
197
198} // namespace cros_disks