blob: d17718bf1b26f5fab5bf608df0d06e5752743e18 [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
45constexpr base::FilePath::CharType kLoopdevCtlPath[] =
46 FILE_PATH_LITERAL("/dev/loop-control");
47#if USE_device_mapper
48constexpr base::FilePath::CharType kDevMapperPath[] =
49 FILE_PATH_LITERAL("/dev/mapper/");
50#endif
51
Luis Hector Chavez644d2042017-09-19 18:56:44 -070052// Gets the namespace name for |nstype|.
53std::string GetNamespaceNameForType(int nstype) {
54 switch (nstype) {
55 case CLONE_NEWCGROUP:
56 return "cgroup";
57 case CLONE_NEWIPC:
58 return "ipc";
59 case CLONE_NEWNET:
60 return "net";
61 case CLONE_NEWNS:
62 return "mnt";
63 case CLONE_NEWPID:
64 return "pid";
65 case CLONE_NEWUSER:
66 return "user";
67 case CLONE_NEWUTS:
68 return "uts";
69 }
70 return std::string();
71}
72
73// Helper function that runs |callback| in all the namespaces identified by
74// |nstypes|.
75bool RunInNamespacesHelper(HookCallback callback,
76 std::vector<int> nstypes,
77 pid_t container_pid) {
78 pid_t child = fork();
79 if (child < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070080 PLOG(ERROR) << "Failed to fork()";
Luis Hector Chavez644d2042017-09-19 18:56:44 -070081 return false;
82 }
83
84 if (child == 0) {
85 for (const int nstype : nstypes) {
86 std::string nstype_name = GetNamespaceNameForType(nstype);
87 if (nstype_name.empty()) {
88 LOG(ERROR) << "Invalid namespace type " << nstype;
89 _exit(-1);
90 }
91 base::FilePath ns_path = base::FilePath(base::StringPrintf(
92 "/proc/%d/ns/%s", container_pid, nstype_name.c_str()));
93 base::ScopedFD ns_fd(open(ns_path.value().c_str(), O_RDONLY));
94 if (!ns_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070095 PLOG(ERROR) << "Failed to open " << ns_path.value();
Luis Hector Chavez644d2042017-09-19 18:56:44 -070096 _exit(-1);
97 }
98 if (setns(ns_fd.get(), nstype)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -070099 PLOG(ERROR) << "Failed to enter PID " << container_pid << "'s "
100 << nstype_name << " namespace";
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700101 _exit(-1);
102 }
103 }
104
105 // Preserve normal POSIX semantics of calling exit(2) with 0 for success and
106 // non-zero for failure.
107 _exit(callback.Run(container_pid) ? 0 : 1);
108 }
109
110 int status;
111 if (HANDLE_EINTR(waitpid(child, &status, 0)) < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700112 PLOG(ERROR) << "Failed to wait for callback";
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700113 return false;
114 }
115 if (!WIFEXITED(status)) {
116 LOG(ERROR) << "Callback terminated abnormally: " << std::hex << status;
117 return false;
118 }
119 return static_cast<int8_t>(WEXITSTATUS(status)) == 0;
120}
121
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700122// Helper function that runs a program execve(2)-style.
123bool ExecveCallbackHelper(base::FilePath filename,
124 std::vector<std::string> args,
125 base::ScopedFD stdin_fd,
126 base::ScopedFD stdout_fd,
127 base::ScopedFD stderr_fd,
128 pid_t container_pid) {
129 pid_t child = fork();
130 if (child < 0) {
131 PLOG(ERROR) << "Failed to fork()";
132 return false;
133 }
134
135 if (child == 0) {
136 if (stdin_fd.is_valid()) {
137 if (dup2(stdin_fd.get(), STDIN_FILENO) == -1) {
138 PLOG(ERROR) << "Failed to dup2() stdin fd";
139 _exit(-1);
140 }
141 }
142 if (stdout_fd.is_valid()) {
143 if (dup2(stdout_fd.get(), STDOUT_FILENO) == -1) {
144 PLOG(ERROR) << "Failed to dup2() stdout fd";
145 _exit(-1);
146 }
147 }
148 if (stderr_fd.is_valid()) {
149 if (dup2(stderr_fd.get(), STDERR_FILENO) == -1) {
150 PLOG(ERROR) << "Failed to dup2() stderr fd";
151 _exit(-1);
152 }
153 }
154
155 std::string pid_str = base::IntToString(container_pid);
156 std::vector<const char*> argv;
157 argv.reserve(args.size() + 1);
158 for (const auto& arg : args) {
159 if (arg == "$PID") {
160 argv.emplace_back(pid_str.c_str());
161 continue;
162 }
163 argv.emplace_back(arg.c_str());
164 }
165 argv.emplace_back(nullptr);
166
167 execve(filename.value().c_str(), const_cast<char**>(argv.data()), environ);
168
169 // Only happens when execve(2) fails.
170 _exit(-1);
171 }
172
173 int status;
174 if (HANDLE_EINTR(waitpid(child, &status, 0)) < 0) {
175 PLOG(ERROR) << "Failed to wait for hook";
176 return false;
177 }
178 if (!WIFEXITED(status)) {
179 LOG(ERROR) << "Hook terminated abnormally: " << std::hex << status;
180 return false;
181 }
182 return static_cast<int8_t>(WEXITSTATUS(status)) == 0;
183}
184
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700185// Immediately removes the loop device from the system.
186void RemoveLoopDevice(int control_fd, int32_t device) {
187 if (ioctl(control_fd, LOOP_CTL_REMOVE, device) < 0)
188 PLOG(ERROR) << "Failed to free /dev/loop" << device;
189}
190
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700191} // namespace
192
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700193WaitablePipe::WaitablePipe() {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700194 if (pipe2(pipe_fds, O_CLOEXEC) < 0)
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700195 PLOG(FATAL) << "Failed to create pipe";
196}
197
198WaitablePipe::~WaitablePipe() {
199 if (pipe_fds[0] != -1)
200 close(pipe_fds[0]);
201 if (pipe_fds[1] != -1)
202 close(pipe_fds[1]);
203}
204
205WaitablePipe::WaitablePipe(WaitablePipe&& other) {
206 pipe_fds[0] = pipe_fds[1] = -1;
207 std::swap(pipe_fds, other.pipe_fds);
208}
209
210void WaitablePipe::Wait() {
211 char buf;
212
213 close(pipe_fds[1]);
214 HANDLE_EINTR(read(pipe_fds[0], &buf, sizeof(buf)));
215 close(pipe_fds[0]);
216
217 pipe_fds[0] = pipe_fds[1] = -1;
218}
219
220void WaitablePipe::Signal() {
221 close(pipe_fds[0]);
222 close(pipe_fds[1]);
223
224 pipe_fds[0] = pipe_fds[1] = -1;
225}
226
227HookState::HookState() = default;
228HookState::~HookState() = default;
229
230HookState::HookState(HookState&& state) = default;
231
232bool HookState::InstallHook(struct minijail* j, minijail_hook_event_t event) {
233 if (installed_) {
234 LOG(ERROR) << "Failed to install hook: already installed";
235 return false;
236 }
237
238 // All these fds will be closed in WaitHook in the child process.
239 for (size_t i = 0; i < 2; ++i) {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700240 if (minijail_preserve_fd(j, reached_pipe_.pipe_fds[i],
241 reached_pipe_.pipe_fds[i]) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700242 LOG(ERROR) << "Failed to preserve reached pipe FDs to install hook";
243 return false;
244 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700245 if (minijail_preserve_fd(j, ready_pipe_.pipe_fds[i],
246 ready_pipe_.pipe_fds[i]) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700247 LOG(ERROR) << "Failed to preserve ready pipe FDs to install hook";
248 return false;
249 }
250 }
251
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700252 if (minijail_add_hook(j, &HookState::WaitHook, this, event) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700253 LOG(ERROR) << "Failed to add hook";
254 return false;
255 }
256
257 installed_ = true;
258 return true;
259}
260
261bool HookState::WaitForHookAndRun(const std::vector<HookCallback>& callbacks,
262 pid_t container_pid) {
263 if (!installed_) {
264 LOG(ERROR) << "Failed to wait for hook: not installed";
265 return false;
266 }
267 reached_pipe_.Wait();
268 base::ScopedClosureRunner teardown(
269 base::Bind(&WaitablePipe::Signal, base::Unretained(&ready_pipe_)));
270
271 for (auto& callback : callbacks) {
272 bool success = callback.Run(container_pid);
273 if (!success)
274 return false;
275 }
276 return true;
277}
278
279// static
280int HookState::WaitHook(void* payload) {
281 HookState* self = reinterpret_cast<HookState*>(payload);
282
283 self->reached_pipe_.Signal();
284 self->ready_pipe_.Wait();
285
286 return 0;
287}
288
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700289bool GetUsernsOutsideId(const std::string& map, int id, int* id_out) {
290 if (map.empty()) {
291 if (id_out)
292 *id_out = id;
293 return true;
294 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700295
296 std::string map_copy = map;
297 base::StringPiece map_piece(map_copy);
298
299 for (const auto& mapping : base::SplitStringPiece(
300 map_piece, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
301 std::vector<base::StringPiece> tokens = base::SplitStringPiece(
302 mapping, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
303
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700304 if (tokens.size() != 3) {
305 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700306 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700307 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700308
309 uint32_t inside, outside, length;
310 if (!base::StringToUint(tokens[0], &inside) ||
311 !base::StringToUint(tokens[1], &outside) ||
312 !base::StringToUint(tokens[2], &length)) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700313 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700314 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700315 }
316
317 if (id >= inside && id <= (inside + length)) {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700318 if (id_out)
319 *id_out = (id - inside) + outside;
320 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700321 }
322 }
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700323 VLOG(1) << "ugid " << id << " not found in mapping";
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700324
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700325 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700326}
327
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700328bool MakeDir(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700329 if (mkdir(path.value().c_str(), mode)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700330 PLOG(ERROR) << "Failed to mkdir " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700331 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700332 }
333 if (chmod(path.value().c_str(), mode)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700334 PLOG(ERROR) << "Failed to chmod " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700335 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700336 }
337 if (chown(path.value().c_str(), uid, gid)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700338 PLOG(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700339 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700340 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700341 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700342}
343
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700344bool TouchFile(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700345 base::ScopedFD fd(open(path.value().c_str(), O_RDWR | O_CREAT, mode));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700346 if (!fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700347 PLOG(ERROR) << "Failed to create " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700348 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700349 }
350 if (fchown(fd.get(), uid, gid)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700351 PLOG(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700352 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700353 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700354 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700355}
356
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700357bool LoopdevSetup(const base::FilePath& source, Loopdev* loopdev_out) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700358 base::ScopedFD source_fd(open(source.value().c_str(), O_RDONLY | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700359 if (!source_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700360 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700361 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700362 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700363
364 base::ScopedFD control_fd(
365 open(kLoopdevCtlPath, O_RDWR | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700366 if (!control_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700367 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700368 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700369 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700370
371 while (true) {
372 int num = ioctl(control_fd.get(), LOOP_CTL_GET_FREE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700373 if (num < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700374 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700375 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700376 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700377
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700378 // Cleanup in case the setup fails. This frees |num| altogether.
379 base::ScopedClosureRunner loop_device_cleanup(
380 base::Bind(&RemoveLoopDevice, control_fd.get(), num));
381
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700382 base::FilePath loopdev_path(base::StringPrintf("/dev/loop%i", num));
383 base::ScopedFD loop_fd(
384 open(loopdev_path.value().c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700385 if (!loop_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700386 PLOG(ERROR) << "Failed to open " << loopdev_path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700387 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700388 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700389
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700390 if (ioctl(loop_fd.get(), LOOP_SET_FD, source_fd.get()) < 0) {
391 if (errno != EBUSY) {
392 PLOG(ERROR) << "Failed to ioctl(LOOP_SET_FD) " << loopdev_path.value();
393 return false;
394 }
395 continue;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700396 }
397
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700398 // Set the autoclear flag on the loop device, which will release it when
399 // there are no more references to it.
400 struct loop_info64 loop_info = {};
401 if (ioctl(loop_fd.get(), LOOP_GET_STATUS64, &loop_info) < 0) {
402 PLOG(ERROR) << "Failed to ioctl(LOOP_GET_STATUS64) "
403 << loopdev_path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700404 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700405 }
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700406 loop_info.lo_flags |= LO_FLAGS_AUTOCLEAR;
407 if (ioctl(loop_fd.get(), LOOP_SET_STATUS64, &loop_info) < 0) {
408 PLOG(ERROR) << "Failed to ioctl(LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR) "
409 << loopdev_path.value();
410 return false;
411 }
412
413 ignore_result(loop_device_cleanup.Release());
414 loopdev_out->path = loopdev_path;
415 loopdev_out->fd = std::move(loop_fd);
416 loopdev_out->info = loop_info;
417 break;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700418 }
419
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700420 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700421}
422
Luis Hector Chavez5cf71ed2018-05-07 14:45:43 -0700423bool LoopdevDetach(Loopdev* loopdev) {
424 if (ioctl(loopdev->fd.get(), LOOP_CLR_FD) < 0) {
425 PLOG(ERROR) << "Failed to ioctl(LOOP_CLR_FD) for " << loopdev->path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700426 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700427 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700428
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700429 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700430}
431
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700432bool DeviceMapperSetup(const base::FilePath& source,
433 const std::string& verity_cmdline,
434 base::FilePath* dm_path_out,
435 std::string* dm_name_out) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700436#if USE_device_mapper
437 // Normalize the name into something unique-esque.
438 std::string dm_name =
439 base::StringPrintf("cros-containers-%s", source.value().c_str());
440 base::ReplaceChars(dm_name, "/", "_", &dm_name);
441
442 // Get the /dev path for the higher levels to mount.
443 base::FilePath dm_path = base::FilePath(kDevMapperPath).Append(dm_name);
444
445 // Insert the source path in the verity command line.
446 std::string verity = verity_cmdline;
447 base::ReplaceSubstringsAfterOffset(&verity, 0, "@DEV@", source.value());
448
449 // Extract the first three parameters for dm-verity settings.
450 char ttype[20];
451 unsigned long long start, size;
452 int n;
453 if (sscanf(verity.c_str(), "%llu %llu %10s %n", &start, &size, ttype, &n) !=
454 3) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700455 PLOG(ERROR) << "Malformed verity string " << verity;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700456 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700457 }
458
459 /* Finally create the device mapper. */
460 std::unique_ptr<struct dm_task, decltype(&dm_task_destroy)> dmt(
461 dm_task_create(DM_DEVICE_CREATE), &dm_task_destroy);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700462 if (dmt == nullptr) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700463 PLOG(ERROR) << "Failed to dm_task_create() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700464 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700465 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700466
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700467 if (dm_task_set_name(dmt.get(), dm_name.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700468 PLOG(ERROR) << "Failed to dm_task_set_name() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700469 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700470 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700471
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700472 if (dm_task_set_ro(dmt.get()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700473 PLOG(ERROR) << "Failed to dm_task_set_ro() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700474 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700475 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700476
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700477 if (dm_task_add_target(dmt.get(), start, size, ttype, verity.c_str() + n) !=
478 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700479 PLOG(ERROR) << "Failed to dm_task_add_target() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700480 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700481 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700482
483 uint32_t cookie = 0;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700484 if (dm_task_set_cookie(dmt.get(), &cookie, 0) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700485 PLOG(ERROR) << "Failed to dm_task_set_cookie() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700486 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700487 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700488
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700489 if (dm_task_run(dmt.get()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700490 PLOG(ERROR) << "Failed to dm_task_run() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700491 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700492 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700493
494 /* Make sure the node exists before we continue. */
495 dm_udev_wait(cookie);
496
497 *dm_path_out = dm_path;
498 *dm_name_out = dm_name;
499#endif
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700500 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700501}
502
503// Tear down the device mapper target.
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700504bool DeviceMapperDetach(const std::string& dm_name) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700505#if USE_device_mapper
506 struct dm_task* dmt = dm_task_create(DM_DEVICE_REMOVE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700507 if (dmt == nullptr) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700508 PLOG(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700509 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700510 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700511
512 base::ScopedClosureRunner teardown(
513 base::Bind(base::IgnoreResult(&dm_task_destroy), base::Unretained(dmt)));
514
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700515 if (dm_task_set_name(dmt, dm_name.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700516 PLOG(ERROR) << "Failed to dm_task_set_name() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700517 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700518 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700519
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700520 if (dm_task_run(dmt) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700521 PLOG(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700522 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700523 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700524#endif
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700525 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700526}
527
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700528bool MountExternal(const std::string& src,
529 const std::string& dest,
530 const std::string& type,
531 unsigned long flags,
532 const std::string& data) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700533 bool remount_ro = false;
534
535 // R/O bind mounts have to be remounted since 'bind' and 'ro' can't both be
536 // specified in the original bind mount. Remount R/O after the initial mount.
537 if ((flags & MS_BIND) && (flags & MS_RDONLY)) {
538 remount_ro = true;
539 flags &= ~MS_RDONLY;
540 }
541
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700542 if (mount(src.c_str(), dest.c_str(), type.c_str(), flags,
543 data.empty() ? nullptr : data.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700544 PLOG(ERROR) << "Failed to mount " << src << " to " << dest;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700545 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700546 }
547
548 if (remount_ro) {
549 flags |= MS_RDONLY;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700550 if (mount(src.c_str(), dest.c_str(), nullptr, flags | MS_REMOUNT,
551 data.empty() ? nullptr : data.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700552 PLOG(ERROR) << "Failed to remount " << src << " to " << dest;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700553 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700554 }
555 }
556
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700557 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700558}
559
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700560bool Pipe2(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe, int flags) {
561 int fds[2];
562 if (pipe2(fds, flags) != 0)
563 return false;
564 read_pipe->reset(fds[0]);
565 write_pipe->reset(fds[1]);
566 return true;
567}
568
569HookCallback CreateExecveCallback(base::FilePath filename,
570 std::vector<std::string> args,
571 base::ScopedFD stdin_fd,
572 base::ScopedFD stdout_fd,
573 base::ScopedFD stderr_fd) {
574 return base::Bind(
575 &ExecveCallbackHelper, filename, args, base::Passed(std::move(stdin_fd)),
576 base::Passed(std::move(stdout_fd)), base::Passed(std::move(stderr_fd)));
577}
578
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700579HookCallback AdaptCallbackToRunInNamespaces(HookCallback callback,
580 std::vector<int> nstypes) {
581 return base::Bind(&RunInNamespacesHelper,
582 base::Passed(std::move(callback)),
583 base::Passed(std::move(nstypes)));
584}
585
Luis Hector Chavez92278e82017-10-16 11:30:27 -0700586bool CreateDirectoryOwnedBy(const base::FilePath& full_path,
587 mode_t mode,
588 uid_t uid,
589 gid_t gid) {
590 if (base::DirectoryExists(full_path))
591 return true;
592
593 // Collect a list of all missing directories.
594 base::FilePath last_path = full_path;
595 std::vector<base::FilePath> missing_subpaths{full_path};
596 for (base::FilePath path = full_path.DirName();
597 path != last_path && !base::DirectoryExists(path);
598 path = path.DirName()) {
599 missing_subpaths.push_back(path);
600 last_path = path;
601 }
602
603 // Iterate through the missing parents, creating them.
604 for (std::vector<base::FilePath>::reverse_iterator i =
605 missing_subpaths.rbegin();
606 i != missing_subpaths.rend(); ++i) {
607 if (mkdir(i->value().c_str(), mode) != 0)
608 return false;
609 if (chown(i->value().c_str(), uid, gid) != 0)
610 return false;
611 }
612 return true;
613}
614
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700615} // namespace libcontainer