blob: 9c440d1f6ade9eb11d1511b5a4abe2a3a1898681 [file] [log] [blame]
Luis Hector Chavez81efb332017-09-18 14:01:29 -07001// Copyright 2017 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 "libcontainer/libcontainer_util.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#if USE_device_mapper
10#include <libdevmapper.h>
11#endif
12#include <linux/loop.h>
Luis Hector Chavez644d2042017-09-19 18:56:44 -070013#include <sched.h>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070014#include <sys/mount.h>
15#include <sys/stat.h>
Luis Hector Chavez644d2042017-09-19 18:56:44 -070016#include <sys/wait.h>
Luis Hector Chavez92278e82017-10-16 11:30:27 -070017#include <unistd.h>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070018
19#include <memory>
Luis Hector Chavez644d2042017-09-19 18:56:44 -070020#include <utility>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070021#include <vector>
22
23#include <base/bind.h>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070024#include <base/callback_helpers.h>
Luis Hector Chavez92278e82017-10-16 11:30:27 -070025#include <base/files/file_util.h>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070026#include <base/files/scoped_file.h>
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070027#include <base/logging.h>
Luis Hector Chavez644d2042017-09-19 18:56:44 -070028#include <base/macros.h>
29#include <base/posix/eintr_wrapper.h>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070030#include <base/strings/string_number_conversions.h>
31#include <base/strings/string_split.h>
32#include <base/strings/string_util.h>
33#include <base/strings/stringprintf.h>
34
Luis Hector Chavez644d2042017-09-19 18:56:44 -070035// New cgroup namespace might not be in linux-headers yet.
36#ifndef CLONE_NEWCGROUP
37#define CLONE_NEWCGROUP 0x02000000
38#endif
39
Luis Hector Chavez81efb332017-09-18 14:01:29 -070040namespace libcontainer {
41
42namespace {
43
Satoru Takabayashicf730fb2018-08-03 16:42:36 +090044constexpr char kLoopdevCtlPath[] = "/dev/loop-control";
Luis Hector Chavez81efb332017-09-18 14:01:29 -070045#if USE_device_mapper
Satoru Takabayashicf730fb2018-08-03 16:42:36 +090046constexpr char kDevMapperPath[] = "/dev/mapper/";
Luis Hector Chavez81efb332017-09-18 14:01:29 -070047#endif
48
Luis Hector Chavez644d2042017-09-19 18:56:44 -070049// Gets the namespace name for |nstype|.
50std::string GetNamespaceNameForType(int nstype) {
51 switch (nstype) {
52 case CLONE_NEWCGROUP:
53 return "cgroup";
54 case CLONE_NEWIPC:
55 return "ipc";
56 case CLONE_NEWNET:
57 return "net";
58 case CLONE_NEWNS:
59 return "mnt";
60 case CLONE_NEWPID:
61 return "pid";
62 case CLONE_NEWUSER:
63 return "user";
64 case CLONE_NEWUTS:
65 return "uts";
66 }
67 return std::string();
68}
69
70// Helper function that runs |callback| in all the namespaces identified by
71// |nstypes|.
72bool RunInNamespacesHelper(HookCallback callback,
73 std::vector<int> nstypes,
74 pid_t container_pid) {
75 pid_t child = fork();
76 if (child < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070077 PLOG(ERROR) << "Failed to fork()";
Luis Hector Chavez644d2042017-09-19 18:56:44 -070078 return false;
79 }
80
81 if (child == 0) {
82 for (const int nstype : nstypes) {
83 std::string nstype_name = GetNamespaceNameForType(nstype);
84 if (nstype_name.empty()) {
85 LOG(ERROR) << "Invalid namespace type " << nstype;
86 _exit(-1);
87 }
88 base::FilePath ns_path = base::FilePath(base::StringPrintf(
89 "/proc/%d/ns/%s", container_pid, nstype_name.c_str()));
90 base::ScopedFD ns_fd(open(ns_path.value().c_str(), O_RDONLY));
91 if (!ns_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070092 PLOG(ERROR) << "Failed to open " << ns_path.value();
Luis Hector Chavez644d2042017-09-19 18:56:44 -070093 _exit(-1);
94 }
95 if (setns(ns_fd.get(), nstype)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070096 PLOG(ERROR) << "Failed to enter PID " << container_pid << "'s "
97 << nstype_name << " namespace";
Luis Hector Chavez644d2042017-09-19 18:56:44 -070098 _exit(-1);
99 }
100 }
101
102 // Preserve normal POSIX semantics of calling exit(2) with 0 for success and
103 // non-zero for failure.
104 _exit(callback.Run(container_pid) ? 0 : 1);
105 }
106
107 int status;
108 if (HANDLE_EINTR(waitpid(child, &status, 0)) < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700109 PLOG(ERROR) << "Failed to wait for callback";
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700110 return false;
111 }
112 if (!WIFEXITED(status)) {
113 LOG(ERROR) << "Callback terminated abnormally: " << std::hex << status;
114 return false;
115 }
116 return static_cast<int8_t>(WEXITSTATUS(status)) == 0;
117}
118
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700119// Helper function that runs a program execve(2)-style.
120bool ExecveCallbackHelper(base::FilePath filename,
121 std::vector<std::string> args,
122 base::ScopedFD stdin_fd,
123 base::ScopedFD stdout_fd,
124 base::ScopedFD stderr_fd,
125 pid_t container_pid) {
126 pid_t child = fork();
127 if (child < 0) {
128 PLOG(ERROR) << "Failed to fork()";
129 return false;
130 }
131
132 if (child == 0) {
133 if (stdin_fd.is_valid()) {
134 if (dup2(stdin_fd.get(), STDIN_FILENO) == -1) {
135 PLOG(ERROR) << "Failed to dup2() stdin fd";
136 _exit(-1);
137 }
138 }
139 if (stdout_fd.is_valid()) {
140 if (dup2(stdout_fd.get(), STDOUT_FILENO) == -1) {
141 PLOG(ERROR) << "Failed to dup2() stdout fd";
142 _exit(-1);
143 }
144 }
145 if (stderr_fd.is_valid()) {
146 if (dup2(stderr_fd.get(), STDERR_FILENO) == -1) {
147 PLOG(ERROR) << "Failed to dup2() stderr fd";
148 _exit(-1);
149 }
150 }
151
Qijiang Fan26fd9222020-03-31 20:16:40 +0900152 std::string pid_str = base::NumberToString(container_pid);
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700153 std::vector<const char*> argv;
154 argv.reserve(args.size() + 1);
155 for (const auto& arg : args) {
156 if (arg == "$PID") {
157 argv.emplace_back(pid_str.c_str());
158 continue;
159 }
160 argv.emplace_back(arg.c_str());
161 }
162 argv.emplace_back(nullptr);
163
164 execve(filename.value().c_str(), const_cast<char**>(argv.data()), environ);
165
166 // Only happens when execve(2) fails.
167 _exit(-1);
168 }
169
170 int status;
171 if (HANDLE_EINTR(waitpid(child, &status, 0)) < 0) {
172 PLOG(ERROR) << "Failed to wait for hook";
173 return false;
174 }
175 if (!WIFEXITED(status)) {
176 LOG(ERROR) << "Hook terminated abnormally: " << std::hex << status;
177 return false;
178 }
179 return static_cast<int8_t>(WEXITSTATUS(status)) == 0;
180}
181
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700182// Immediately removes the loop device from the system.
183void RemoveLoopDevice(int control_fd, int32_t device) {
184 if (ioctl(control_fd, LOOP_CTL_REMOVE, device) < 0)
185 PLOG(ERROR) << "Failed to free /dev/loop" << device;
186}
187
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700188} // namespace
189
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700190WaitablePipe::WaitablePipe() {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700191 if (pipe2(pipe_fds, O_CLOEXEC) < 0)
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700192 PLOG(FATAL) << "Failed to create pipe";
193}
194
195WaitablePipe::~WaitablePipe() {
196 if (pipe_fds[0] != -1)
197 close(pipe_fds[0]);
198 if (pipe_fds[1] != -1)
199 close(pipe_fds[1]);
200}
201
202WaitablePipe::WaitablePipe(WaitablePipe&& other) {
203 pipe_fds[0] = pipe_fds[1] = -1;
204 std::swap(pipe_fds, other.pipe_fds);
205}
206
207void WaitablePipe::Wait() {
208 char buf;
209
210 close(pipe_fds[1]);
211 HANDLE_EINTR(read(pipe_fds[0], &buf, sizeof(buf)));
212 close(pipe_fds[0]);
213
214 pipe_fds[0] = pipe_fds[1] = -1;
215}
216
217void WaitablePipe::Signal() {
218 close(pipe_fds[0]);
219 close(pipe_fds[1]);
220
221 pipe_fds[0] = pipe_fds[1] = -1;
222}
223
224HookState::HookState() = default;
225HookState::~HookState() = default;
226
227HookState::HookState(HookState&& state) = default;
228
229bool HookState::InstallHook(struct minijail* j, minijail_hook_event_t event) {
230 if (installed_) {
231 LOG(ERROR) << "Failed to install hook: already installed";
232 return false;
233 }
234
235 // All these fds will be closed in WaitHook in the child process.
236 for (size_t i = 0; i < 2; ++i) {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700237 if (minijail_preserve_fd(j, reached_pipe_.pipe_fds[i],
238 reached_pipe_.pipe_fds[i]) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700239 LOG(ERROR) << "Failed to preserve reached pipe FDs to install hook";
240 return false;
241 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700242 if (minijail_preserve_fd(j, ready_pipe_.pipe_fds[i],
243 ready_pipe_.pipe_fds[i]) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700244 LOG(ERROR) << "Failed to preserve ready pipe FDs to install hook";
245 return false;
246 }
247 }
248
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700249 if (minijail_add_hook(j, &HookState::WaitHook, this, event) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700250 LOG(ERROR) << "Failed to add hook";
251 return false;
252 }
253
254 installed_ = true;
255 return true;
256}
257
258bool HookState::WaitForHookAndRun(const std::vector<HookCallback>& callbacks,
259 pid_t container_pid) {
260 if (!installed_) {
261 LOG(ERROR) << "Failed to wait for hook: not installed";
262 return false;
263 }
264 reached_pipe_.Wait();
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700265
266 for (auto& callback : callbacks) {
267 bool success = callback.Run(container_pid);
268 if (!success)
269 return false;
270 }
Chris Morina8cff682019-02-27 20:08:00 -0800271
272 ready_pipe_.Signal();
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700273 return true;
274}
275
276// static
277int HookState::WaitHook(void* payload) {
278 HookState* self = reinterpret_cast<HookState*>(payload);
279
280 self->reached_pipe_.Signal();
281 self->ready_pipe_.Wait();
282
283 return 0;
284}
285
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700286bool GetUsernsOutsideId(const std::string& map, int id, int* id_out) {
287 if (map.empty()) {
288 if (id_out)
289 *id_out = id;
290 return true;
291 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700292
293 std::string map_copy = map;
294 base::StringPiece map_piece(map_copy);
295
296 for (const auto& mapping : base::SplitStringPiece(
297 map_piece, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
298 std::vector<base::StringPiece> tokens = base::SplitStringPiece(
299 mapping, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
300
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700301 if (tokens.size() != 3) {
302 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700303 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700304 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700305
306 uint32_t inside, outside, length;
307 if (!base::StringToUint(tokens[0], &inside) ||
308 !base::StringToUint(tokens[1], &outside) ||
309 !base::StringToUint(tokens[2], &length)) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700310 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700311 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700312 }
313
314 if (id >= inside && id <= (inside + length)) {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700315 if (id_out)
316 *id_out = (id - inside) + outside;
317 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700318 }
319 }
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700320 VLOG(1) << "ugid " << id << " not found in mapping";
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700321
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700322 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700323}
324
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700325bool MakeDir(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700326 if (mkdir(path.value().c_str(), mode)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700327 PLOG(ERROR) << "Failed to mkdir " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700328 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700329 }
330 if (chmod(path.value().c_str(), mode)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700331 PLOG(ERROR) << "Failed to chmod " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700332 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700333 }
334 if (chown(path.value().c_str(), uid, gid)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700335 PLOG(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700336 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700337 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700338 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700339}
340
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700341bool TouchFile(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700342 base::ScopedFD fd(open(path.value().c_str(), O_RDWR | O_CREAT, mode));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700343 if (!fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700344 PLOG(ERROR) << "Failed to create " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700345 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700346 }
347 if (fchown(fd.get(), uid, gid)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700348 PLOG(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700349 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700350 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700351 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700352}
353
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700354bool LoopdevSetup(const base::FilePath& source, Loopdev* loopdev_out) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700355 base::ScopedFD source_fd(open(source.value().c_str(), O_RDONLY | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700356 if (!source_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700357 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700358 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700359 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700360
361 base::ScopedFD control_fd(
362 open(kLoopdevCtlPath, O_RDWR | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700363 if (!control_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700364 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700365 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700366 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700367
368 while (true) {
369 int num = ioctl(control_fd.get(), LOOP_CTL_GET_FREE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700370 if (num < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700371 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700372 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700373 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700374
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700375 // Cleanup in case the setup fails. This frees |num| altogether.
376 base::ScopedClosureRunner loop_device_cleanup(
377 base::Bind(&RemoveLoopDevice, control_fd.get(), num));
378
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700379 base::FilePath loopdev_path(base::StringPrintf("/dev/loop%i", num));
380 base::ScopedFD loop_fd(
381 open(loopdev_path.value().c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700382 if (!loop_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700383 PLOG(ERROR) << "Failed to open " << loopdev_path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700384 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700385 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700386
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700387 if (ioctl(loop_fd.get(), LOOP_SET_FD, source_fd.get()) < 0) {
388 if (errno != EBUSY) {
389 PLOG(ERROR) << "Failed to ioctl(LOOP_SET_FD) " << loopdev_path.value();
390 return false;
391 }
392 continue;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700393 }
394
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700395 // Set the autoclear flag on the loop device, which will release it when
396 // there are no more references to it.
397 struct loop_info64 loop_info = {};
398 if (ioctl(loop_fd.get(), LOOP_GET_STATUS64, &loop_info) < 0) {
399 PLOG(ERROR) << "Failed to ioctl(LOOP_GET_STATUS64) "
400 << loopdev_path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700401 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700402 }
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700403 loop_info.lo_flags |= LO_FLAGS_AUTOCLEAR;
404 if (ioctl(loop_fd.get(), LOOP_SET_STATUS64, &loop_info) < 0) {
405 PLOG(ERROR) << "Failed to ioctl(LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR) "
406 << loopdev_path.value();
407 return false;
408 }
409
410 ignore_result(loop_device_cleanup.Release());
411 loopdev_out->path = loopdev_path;
412 loopdev_out->fd = std::move(loop_fd);
413 loopdev_out->info = loop_info;
414 break;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700415 }
416
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700417 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700418}
419
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700420bool LoopdevDetach(Loopdev* loopdev) {
421 if (ioctl(loopdev->fd.get(), LOOP_CLR_FD) < 0) {
422 PLOG(ERROR) << "Failed to ioctl(LOOP_CLR_FD) for " << loopdev->path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700423 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700424 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700425
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700426 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700427}
428
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700429bool DeviceMapperSetup(const base::FilePath& source,
430 const std::string& verity_cmdline,
431 base::FilePath* dm_path_out,
432 std::string* dm_name_out) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700433#if USE_device_mapper
434 // Normalize the name into something unique-esque.
435 std::string dm_name =
436 base::StringPrintf("cros-containers-%s", source.value().c_str());
437 base::ReplaceChars(dm_name, "/", "_", &dm_name);
438
439 // Get the /dev path for the higher levels to mount.
440 base::FilePath dm_path = base::FilePath(kDevMapperPath).Append(dm_name);
441
442 // Insert the source path in the verity command line.
443 std::string verity = verity_cmdline;
444 base::ReplaceSubstringsAfterOffset(&verity, 0, "@DEV@", source.value());
445
446 // Extract the first three parameters for dm-verity settings.
447 char ttype[20];
448 unsigned long long start, size;
449 int n;
450 if (sscanf(verity.c_str(), "%llu %llu %10s %n", &start, &size, ttype, &n) !=
451 3) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700452 PLOG(ERROR) << "Malformed verity string " << verity;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700453 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700454 }
455
456 /* Finally create the device mapper. */
457 std::unique_ptr<struct dm_task, decltype(&dm_task_destroy)> dmt(
458 dm_task_create(DM_DEVICE_CREATE), &dm_task_destroy);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700459 if (dmt == nullptr) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700460 PLOG(ERROR) << "Failed to dm_task_create() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700461 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700462 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700463
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700464 if (dm_task_set_name(dmt.get(), dm_name.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700465 PLOG(ERROR) << "Failed to dm_task_set_name() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700466 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700467 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700468
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700469 if (dm_task_set_ro(dmt.get()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700470 PLOG(ERROR) << "Failed to dm_task_set_ro() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700471 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700472 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700473
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700474 if (dm_task_add_target(dmt.get(), start, size, ttype, verity.c_str() + n) !=
475 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700476 PLOG(ERROR) << "Failed to dm_task_add_target() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700477 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700478 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700479
480 uint32_t cookie = 0;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700481 if (dm_task_set_cookie(dmt.get(), &cookie, 0) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700482 PLOG(ERROR) << "Failed to dm_task_set_cookie() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700483 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700484 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700485
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700486 if (dm_task_run(dmt.get()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700487 PLOG(ERROR) << "Failed to dm_task_run() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700488 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700489 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700490
491 /* Make sure the node exists before we continue. */
492 dm_udev_wait(cookie);
493
494 *dm_path_out = dm_path;
495 *dm_name_out = dm_name;
496#endif
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700497 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700498}
499
500// Tear down the device mapper target.
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700501bool DeviceMapperDetach(const std::string& dm_name) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700502#if USE_device_mapper
503 struct dm_task* dmt = dm_task_create(DM_DEVICE_REMOVE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700504 if (dmt == nullptr) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700505 PLOG(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700506 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700507 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700508
509 base::ScopedClosureRunner teardown(
510 base::Bind(base::IgnoreResult(&dm_task_destroy), base::Unretained(dmt)));
511
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700512 if (dm_task_set_name(dmt, dm_name.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700513 PLOG(ERROR) << "Failed to dm_task_set_name() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700514 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700515 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700516
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700517 if (dm_task_run(dmt) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700518 PLOG(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700519 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700520 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700521#endif
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700522 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700523}
524
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700525bool MountExternal(const std::string& src,
526 const std::string& dest,
527 const std::string& type,
528 unsigned long flags,
529 const std::string& data) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700530 bool remount_ro = false;
531
532 // R/O bind mounts have to be remounted since 'bind' and 'ro' can't both be
533 // specified in the original bind mount. Remount R/O after the initial mount.
534 if ((flags & MS_BIND) && (flags & MS_RDONLY)) {
535 remount_ro = true;
536 flags &= ~MS_RDONLY;
537 }
538
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700539 if (mount(src.c_str(), dest.c_str(), type.c_str(), flags,
540 data.empty() ? nullptr : data.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700541 PLOG(ERROR) << "Failed to mount " << src << " to " << dest;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700542 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700543 }
544
545 if (remount_ro) {
546 flags |= MS_RDONLY;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700547 if (mount(src.c_str(), dest.c_str(), nullptr, flags | MS_REMOUNT,
548 data.empty() ? nullptr : data.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700549 PLOG(ERROR) << "Failed to remount " << src << " to " << dest;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700550 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700551 }
552 }
553
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700554 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700555}
556
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700557bool Pipe2(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe, int flags) {
558 int fds[2];
559 if (pipe2(fds, flags) != 0)
560 return false;
561 read_pipe->reset(fds[0]);
562 write_pipe->reset(fds[1]);
563 return true;
564}
565
566HookCallback CreateExecveCallback(base::FilePath filename,
567 std::vector<std::string> args,
568 base::ScopedFD stdin_fd,
569 base::ScopedFD stdout_fd,
570 base::ScopedFD stderr_fd) {
571 return base::Bind(
572 &ExecveCallbackHelper, filename, args, base::Passed(std::move(stdin_fd)),
573 base::Passed(std::move(stdout_fd)), base::Passed(std::move(stderr_fd)));
574}
575
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700576HookCallback AdaptCallbackToRunInNamespaces(HookCallback callback,
577 std::vector<int> nstypes) {
Tom Hughes25f969a2020-08-27 15:57:27 -0700578 return base::Bind(&RunInNamespacesHelper, base::Passed(std::move(callback)),
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700579 base::Passed(std::move(nstypes)));
580}
581
Luis Hector Chavez92278e82017-10-16 11:30:27 -0700582bool CreateDirectoryOwnedBy(const base::FilePath& full_path,
583 mode_t mode,
584 uid_t uid,
585 gid_t gid) {
586 if (base::DirectoryExists(full_path))
587 return true;
588
589 // Collect a list of all missing directories.
590 base::FilePath last_path = full_path;
591 std::vector<base::FilePath> missing_subpaths{full_path};
592 for (base::FilePath path = full_path.DirName();
593 path != last_path && !base::DirectoryExists(path);
594 path = path.DirName()) {
595 missing_subpaths.push_back(path);
596 last_path = path;
597 }
598
599 // Iterate through the missing parents, creating them.
600 for (std::vector<base::FilePath>::reverse_iterator i =
601 missing_subpaths.rbegin();
602 i != missing_subpaths.rend(); ++i) {
603 if (mkdir(i->value().c_str(), mode) != 0)
604 return false;
605 if (chown(i->value().c_str(), uid, gid) != 0)
606 return false;
607 }
608 return true;
609}
610
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700611} // namespace libcontainer