blob: fab073b8ee17cd0ea4aa498860345174d3577f13 [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>
24#include <base/bind_helpers.h>
25#include <base/callback_helpers.h>
Luis Hector Chavez92278e82017-10-16 11:30:27 -070026#include <base/files/file_util.h>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070027#include <base/files/scoped_file.h>
Luis Hector Chavez835d39e2017-09-19 15:16:31 -070028#include <base/logging.h>
Luis Hector Chavez644d2042017-09-19 18:56:44 -070029#include <base/macros.h>
30#include <base/posix/eintr_wrapper.h>
Luis Hector Chavez81efb332017-09-18 14:01:29 -070031#include <base/strings/string_number_conversions.h>
32#include <base/strings/string_split.h>
33#include <base/strings/string_util.h>
34#include <base/strings/stringprintf.h>
35
Luis Hector Chavez644d2042017-09-19 18:56:44 -070036// New cgroup namespace might not be in linux-headers yet.
37#ifndef CLONE_NEWCGROUP
38#define CLONE_NEWCGROUP 0x02000000
39#endif
40
Luis Hector Chavez81efb332017-09-18 14:01:29 -070041namespace libcontainer {
42
43namespace {
44
Satoru Takabayashicf730fb2018-08-03 16:42:36 +090045constexpr char kLoopdevCtlPath[] = "/dev/loop-control";
Luis Hector Chavez81efb332017-09-18 14:01:29 -070046#if USE_device_mapper
Satoru Takabayashicf730fb2018-08-03 16:42:36 +090047constexpr char kDevMapperPath[] = "/dev/mapper/";
Luis Hector Chavez81efb332017-09-18 14:01:29 -070048#endif
49
Luis Hector Chavez644d2042017-09-19 18:56:44 -070050// Gets the namespace name for |nstype|.
51std::string GetNamespaceNameForType(int nstype) {
52 switch (nstype) {
53 case CLONE_NEWCGROUP:
54 return "cgroup";
55 case CLONE_NEWIPC:
56 return "ipc";
57 case CLONE_NEWNET:
58 return "net";
59 case CLONE_NEWNS:
60 return "mnt";
61 case CLONE_NEWPID:
62 return "pid";
63 case CLONE_NEWUSER:
64 return "user";
65 case CLONE_NEWUTS:
66 return "uts";
67 }
68 return std::string();
69}
70
71// Helper function that runs |callback| in all the namespaces identified by
72// |nstypes|.
73bool RunInNamespacesHelper(HookCallback callback,
74 std::vector<int> nstypes,
75 pid_t container_pid) {
76 pid_t child = fork();
77 if (child < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070078 PLOG(ERROR) << "Failed to fork()";
Luis Hector Chavez644d2042017-09-19 18:56:44 -070079 return false;
80 }
81
82 if (child == 0) {
83 for (const int nstype : nstypes) {
84 std::string nstype_name = GetNamespaceNameForType(nstype);
85 if (nstype_name.empty()) {
86 LOG(ERROR) << "Invalid namespace type " << nstype;
87 _exit(-1);
88 }
89 base::FilePath ns_path = base::FilePath(base::StringPrintf(
90 "/proc/%d/ns/%s", container_pid, nstype_name.c_str()));
91 base::ScopedFD ns_fd(open(ns_path.value().c_str(), O_RDONLY));
92 if (!ns_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070093 PLOG(ERROR) << "Failed to open " << ns_path.value();
Luis Hector Chavez644d2042017-09-19 18:56:44 -070094 _exit(-1);
95 }
96 if (setns(ns_fd.get(), nstype)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070097 PLOG(ERROR) << "Failed to enter PID " << container_pid << "'s "
98 << nstype_name << " namespace";
Luis Hector Chavez644d2042017-09-19 18:56:44 -070099 _exit(-1);
100 }
101 }
102
103 // Preserve normal POSIX semantics of calling exit(2) with 0 for success and
104 // non-zero for failure.
105 _exit(callback.Run(container_pid) ? 0 : 1);
106 }
107
108 int status;
109 if (HANDLE_EINTR(waitpid(child, &status, 0)) < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700110 PLOG(ERROR) << "Failed to wait for callback";
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700111 return false;
112 }
113 if (!WIFEXITED(status)) {
114 LOG(ERROR) << "Callback terminated abnormally: " << std::hex << status;
115 return false;
116 }
117 return static_cast<int8_t>(WEXITSTATUS(status)) == 0;
118}
119
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700120// Helper function that runs a program execve(2)-style.
121bool ExecveCallbackHelper(base::FilePath filename,
122 std::vector<std::string> args,
123 base::ScopedFD stdin_fd,
124 base::ScopedFD stdout_fd,
125 base::ScopedFD stderr_fd,
126 pid_t container_pid) {
127 pid_t child = fork();
128 if (child < 0) {
129 PLOG(ERROR) << "Failed to fork()";
130 return false;
131 }
132
133 if (child == 0) {
134 if (stdin_fd.is_valid()) {
135 if (dup2(stdin_fd.get(), STDIN_FILENO) == -1) {
136 PLOG(ERROR) << "Failed to dup2() stdin fd";
137 _exit(-1);
138 }
139 }
140 if (stdout_fd.is_valid()) {
141 if (dup2(stdout_fd.get(), STDOUT_FILENO) == -1) {
142 PLOG(ERROR) << "Failed to dup2() stdout fd";
143 _exit(-1);
144 }
145 }
146 if (stderr_fd.is_valid()) {
147 if (dup2(stderr_fd.get(), STDERR_FILENO) == -1) {
148 PLOG(ERROR) << "Failed to dup2() stderr fd";
149 _exit(-1);
150 }
151 }
152
Qijiang Fan26fd9222020-03-31 20:16:40 +0900153 std::string pid_str = base::NumberToString(container_pid);
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700154 std::vector<const char*> argv;
155 argv.reserve(args.size() + 1);
156 for (const auto& arg : args) {
157 if (arg == "$PID") {
158 argv.emplace_back(pid_str.c_str());
159 continue;
160 }
161 argv.emplace_back(arg.c_str());
162 }
163 argv.emplace_back(nullptr);
164
165 execve(filename.value().c_str(), const_cast<char**>(argv.data()), environ);
166
167 // Only happens when execve(2) fails.
168 _exit(-1);
169 }
170
171 int status;
172 if (HANDLE_EINTR(waitpid(child, &status, 0)) < 0) {
173 PLOG(ERROR) << "Failed to wait for hook";
174 return false;
175 }
176 if (!WIFEXITED(status)) {
177 LOG(ERROR) << "Hook terminated abnormally: " << std::hex << status;
178 return false;
179 }
180 return static_cast<int8_t>(WEXITSTATUS(status)) == 0;
181}
182
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700183// Immediately removes the loop device from the system.
184void RemoveLoopDevice(int control_fd, int32_t device) {
185 if (ioctl(control_fd, LOOP_CTL_REMOVE, device) < 0)
186 PLOG(ERROR) << "Failed to free /dev/loop" << device;
187}
188
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700189} // namespace
190
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700191WaitablePipe::WaitablePipe() {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700192 if (pipe2(pipe_fds, O_CLOEXEC) < 0)
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700193 PLOG(FATAL) << "Failed to create pipe";
194}
195
196WaitablePipe::~WaitablePipe() {
197 if (pipe_fds[0] != -1)
198 close(pipe_fds[0]);
199 if (pipe_fds[1] != -1)
200 close(pipe_fds[1]);
201}
202
203WaitablePipe::WaitablePipe(WaitablePipe&& other) {
204 pipe_fds[0] = pipe_fds[1] = -1;
205 std::swap(pipe_fds, other.pipe_fds);
206}
207
208void WaitablePipe::Wait() {
209 char buf;
210
211 close(pipe_fds[1]);
212 HANDLE_EINTR(read(pipe_fds[0], &buf, sizeof(buf)));
213 close(pipe_fds[0]);
214
215 pipe_fds[0] = pipe_fds[1] = -1;
216}
217
218void WaitablePipe::Signal() {
219 close(pipe_fds[0]);
220 close(pipe_fds[1]);
221
222 pipe_fds[0] = pipe_fds[1] = -1;
223}
224
225HookState::HookState() = default;
226HookState::~HookState() = default;
227
228HookState::HookState(HookState&& state) = default;
229
230bool HookState::InstallHook(struct minijail* j, minijail_hook_event_t event) {
231 if (installed_) {
232 LOG(ERROR) << "Failed to install hook: already installed";
233 return false;
234 }
235
236 // All these fds will be closed in WaitHook in the child process.
237 for (size_t i = 0; i < 2; ++i) {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700238 if (minijail_preserve_fd(j, reached_pipe_.pipe_fds[i],
239 reached_pipe_.pipe_fds[i]) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700240 LOG(ERROR) << "Failed to preserve reached pipe FDs to install hook";
241 return false;
242 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700243 if (minijail_preserve_fd(j, ready_pipe_.pipe_fds[i],
244 ready_pipe_.pipe_fds[i]) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700245 LOG(ERROR) << "Failed to preserve ready pipe FDs to install hook";
246 return false;
247 }
248 }
249
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700250 if (minijail_add_hook(j, &HookState::WaitHook, this, event) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700251 LOG(ERROR) << "Failed to add hook";
252 return false;
253 }
254
255 installed_ = true;
256 return true;
257}
258
259bool HookState::WaitForHookAndRun(const std::vector<HookCallback>& callbacks,
260 pid_t container_pid) {
261 if (!installed_) {
262 LOG(ERROR) << "Failed to wait for hook: not installed";
263 return false;
264 }
265 reached_pipe_.Wait();
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700266
267 for (auto& callback : callbacks) {
268 bool success = callback.Run(container_pid);
269 if (!success)
270 return false;
271 }
Chris Morina8cff682019-02-27 20:08:00 -0800272
273 ready_pipe_.Signal();
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700274 return true;
275}
276
277// static
278int HookState::WaitHook(void* payload) {
279 HookState* self = reinterpret_cast<HookState*>(payload);
280
281 self->reached_pipe_.Signal();
282 self->ready_pipe_.Wait();
283
284 return 0;
285}
286
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700287bool GetUsernsOutsideId(const std::string& map, int id, int* id_out) {
288 if (map.empty()) {
289 if (id_out)
290 *id_out = id;
291 return true;
292 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700293
294 std::string map_copy = map;
295 base::StringPiece map_piece(map_copy);
296
297 for (const auto& mapping : base::SplitStringPiece(
298 map_piece, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
299 std::vector<base::StringPiece> tokens = base::SplitStringPiece(
300 mapping, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
301
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700302 if (tokens.size() != 3) {
303 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700304 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700305 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700306
307 uint32_t inside, outside, length;
308 if (!base::StringToUint(tokens[0], &inside) ||
309 !base::StringToUint(tokens[1], &outside) ||
310 !base::StringToUint(tokens[2], &length)) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700311 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700312 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700313 }
314
315 if (id >= inside && id <= (inside + length)) {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700316 if (id_out)
317 *id_out = (id - inside) + outside;
318 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700319 }
320 }
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700321 VLOG(1) << "ugid " << id << " not found in mapping";
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700322
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700323 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700324}
325
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700326bool MakeDir(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700327 if (mkdir(path.value().c_str(), mode)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700328 PLOG(ERROR) << "Failed to mkdir " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700329 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700330 }
331 if (chmod(path.value().c_str(), mode)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700332 PLOG(ERROR) << "Failed to chmod " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700333 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700334 }
335 if (chown(path.value().c_str(), uid, gid)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700336 PLOG(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700337 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700338 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700339 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700340}
341
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700342bool TouchFile(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700343 base::ScopedFD fd(open(path.value().c_str(), O_RDWR | O_CREAT, mode));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700344 if (!fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700345 PLOG(ERROR) << "Failed to create " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700346 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700347 }
348 if (fchown(fd.get(), uid, gid)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700349 PLOG(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700350 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700351 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700352 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700353}
354
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700355bool LoopdevSetup(const base::FilePath& source, Loopdev* loopdev_out) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700356 base::ScopedFD source_fd(open(source.value().c_str(), O_RDONLY | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700357 if (!source_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700358 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700359 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700360 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700361
362 base::ScopedFD control_fd(
363 open(kLoopdevCtlPath, O_RDWR | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700364 if (!control_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700365 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700366 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700367 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700368
369 while (true) {
370 int num = ioctl(control_fd.get(), LOOP_CTL_GET_FREE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700371 if (num < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700372 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700373 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700374 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700375
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700376 // Cleanup in case the setup fails. This frees |num| altogether.
377 base::ScopedClosureRunner loop_device_cleanup(
378 base::Bind(&RemoveLoopDevice, control_fd.get(), num));
379
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700380 base::FilePath loopdev_path(base::StringPrintf("/dev/loop%i", num));
381 base::ScopedFD loop_fd(
382 open(loopdev_path.value().c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700383 if (!loop_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700384 PLOG(ERROR) << "Failed to open " << loopdev_path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700385 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700386 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700387
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700388 if (ioctl(loop_fd.get(), LOOP_SET_FD, source_fd.get()) < 0) {
389 if (errno != EBUSY) {
390 PLOG(ERROR) << "Failed to ioctl(LOOP_SET_FD) " << loopdev_path.value();
391 return false;
392 }
393 continue;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700394 }
395
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700396 // Set the autoclear flag on the loop device, which will release it when
397 // there are no more references to it.
398 struct loop_info64 loop_info = {};
399 if (ioctl(loop_fd.get(), LOOP_GET_STATUS64, &loop_info) < 0) {
400 PLOG(ERROR) << "Failed to ioctl(LOOP_GET_STATUS64) "
401 << loopdev_path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700402 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700403 }
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700404 loop_info.lo_flags |= LO_FLAGS_AUTOCLEAR;
405 if (ioctl(loop_fd.get(), LOOP_SET_STATUS64, &loop_info) < 0) {
406 PLOG(ERROR) << "Failed to ioctl(LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR) "
407 << loopdev_path.value();
408 return false;
409 }
410
411 ignore_result(loop_device_cleanup.Release());
412 loopdev_out->path = loopdev_path;
413 loopdev_out->fd = std::move(loop_fd);
414 loopdev_out->info = loop_info;
415 break;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700416 }
417
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700418 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700419}
420
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700421bool LoopdevDetach(Loopdev* loopdev) {
422 if (ioctl(loopdev->fd.get(), LOOP_CLR_FD) < 0) {
423 PLOG(ERROR) << "Failed to ioctl(LOOP_CLR_FD) for " << loopdev->path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700424 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700425 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700426
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700427 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700428}
429
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700430bool DeviceMapperSetup(const base::FilePath& source,
431 const std::string& verity_cmdline,
432 base::FilePath* dm_path_out,
433 std::string* dm_name_out) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700434#if USE_device_mapper
435 // Normalize the name into something unique-esque.
436 std::string dm_name =
437 base::StringPrintf("cros-containers-%s", source.value().c_str());
438 base::ReplaceChars(dm_name, "/", "_", &dm_name);
439
440 // Get the /dev path for the higher levels to mount.
441 base::FilePath dm_path = base::FilePath(kDevMapperPath).Append(dm_name);
442
443 // Insert the source path in the verity command line.
444 std::string verity = verity_cmdline;
445 base::ReplaceSubstringsAfterOffset(&verity, 0, "@DEV@", source.value());
446
447 // Extract the first three parameters for dm-verity settings.
448 char ttype[20];
449 unsigned long long start, size;
450 int n;
451 if (sscanf(verity.c_str(), "%llu %llu %10s %n", &start, &size, ttype, &n) !=
452 3) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700453 PLOG(ERROR) << "Malformed verity string " << verity;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700454 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700455 }
456
457 /* Finally create the device mapper. */
458 std::unique_ptr<struct dm_task, decltype(&dm_task_destroy)> dmt(
459 dm_task_create(DM_DEVICE_CREATE), &dm_task_destroy);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700460 if (dmt == nullptr) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700461 PLOG(ERROR) << "Failed to dm_task_create() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700462 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700463 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700464
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700465 if (dm_task_set_name(dmt.get(), dm_name.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700466 PLOG(ERROR) << "Failed to dm_task_set_name() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700467 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700468 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700469
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700470 if (dm_task_set_ro(dmt.get()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700471 PLOG(ERROR) << "Failed to dm_task_set_ro() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700472 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700473 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700474
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700475 if (dm_task_add_target(dmt.get(), start, size, ttype, verity.c_str() + n) !=
476 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700477 PLOG(ERROR) << "Failed to dm_task_add_target() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700478 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700479 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700480
481 uint32_t cookie = 0;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700482 if (dm_task_set_cookie(dmt.get(), &cookie, 0) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700483 PLOG(ERROR) << "Failed to dm_task_set_cookie() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700484 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700485 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700486
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700487 if (dm_task_run(dmt.get()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700488 PLOG(ERROR) << "Failed to dm_task_run() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700489 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700490 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700491
492 /* Make sure the node exists before we continue. */
493 dm_udev_wait(cookie);
494
495 *dm_path_out = dm_path;
496 *dm_name_out = dm_name;
497#endif
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700498 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700499}
500
501// Tear down the device mapper target.
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700502bool DeviceMapperDetach(const std::string& dm_name) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700503#if USE_device_mapper
504 struct dm_task* dmt = dm_task_create(DM_DEVICE_REMOVE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700505 if (dmt == nullptr) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700506 PLOG(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700507 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700508 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700509
510 base::ScopedClosureRunner teardown(
511 base::Bind(base::IgnoreResult(&dm_task_destroy), base::Unretained(dmt)));
512
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700513 if (dm_task_set_name(dmt, dm_name.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700514 PLOG(ERROR) << "Failed to dm_task_set_name() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700515 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700516 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700517
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700518 if (dm_task_run(dmt) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700519 PLOG(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700520 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700521 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700522#endif
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700523 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700524}
525
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700526bool MountExternal(const std::string& src,
527 const std::string& dest,
528 const std::string& type,
529 unsigned long flags,
530 const std::string& data) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700531 bool remount_ro = false;
532
533 // R/O bind mounts have to be remounted since 'bind' and 'ro' can't both be
534 // specified in the original bind mount. Remount R/O after the initial mount.
535 if ((flags & MS_BIND) && (flags & MS_RDONLY)) {
536 remount_ro = true;
537 flags &= ~MS_RDONLY;
538 }
539
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700540 if (mount(src.c_str(), dest.c_str(), type.c_str(), flags,
541 data.empty() ? nullptr : data.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700542 PLOG(ERROR) << "Failed to mount " << src << " to " << dest;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700543 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700544 }
545
546 if (remount_ro) {
547 flags |= MS_RDONLY;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700548 if (mount(src.c_str(), dest.c_str(), nullptr, flags | MS_REMOUNT,
549 data.empty() ? nullptr : data.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700550 PLOG(ERROR) << "Failed to remount " << src << " to " << dest;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700551 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700552 }
553 }
554
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700555 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700556}
557
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700558bool Pipe2(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe, int flags) {
559 int fds[2];
560 if (pipe2(fds, flags) != 0)
561 return false;
562 read_pipe->reset(fds[0]);
563 write_pipe->reset(fds[1]);
564 return true;
565}
566
567HookCallback CreateExecveCallback(base::FilePath filename,
568 std::vector<std::string> args,
569 base::ScopedFD stdin_fd,
570 base::ScopedFD stdout_fd,
571 base::ScopedFD stderr_fd) {
572 return base::Bind(
573 &ExecveCallbackHelper, filename, args, base::Passed(std::move(stdin_fd)),
574 base::Passed(std::move(stdout_fd)), base::Passed(std::move(stderr_fd)));
575}
576
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700577HookCallback AdaptCallbackToRunInNamespaces(HookCallback callback,
578 std::vector<int> nstypes) {
Tom Hughes25f969a2020-08-27 15:57:27 -0700579 return base::Bind(&RunInNamespacesHelper, base::Passed(std::move(callback)),
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700580 base::Passed(std::move(nstypes)));
581}
582
Luis Hector Chavez92278e82017-10-16 11:30:27 -0700583bool CreateDirectoryOwnedBy(const base::FilePath& full_path,
584 mode_t mode,
585 uid_t uid,
586 gid_t gid) {
587 if (base::DirectoryExists(full_path))
588 return true;
589
590 // Collect a list of all missing directories.
591 base::FilePath last_path = full_path;
592 std::vector<base::FilePath> missing_subpaths{full_path};
593 for (base::FilePath path = full_path.DirName();
594 path != last_path && !base::DirectoryExists(path);
595 path = path.DirName()) {
596 missing_subpaths.push_back(path);
597 last_path = path;
598 }
599
600 // Iterate through the missing parents, creating them.
601 for (std::vector<base::FilePath>::reverse_iterator i =
602 missing_subpaths.rbegin();
603 i != missing_subpaths.rend(); ++i) {
604 if (mkdir(i->value().c_str(), mode) != 0)
605 return false;
606 if (chown(i->value().c_str(), uid, gid) != 0)
607 return false;
608 }
609 return true;
610}
611
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700612} // namespace libcontainer