blob: f61d78b46ad946e6876b10075b4ae9a557163d23 [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 Chavez81efb332017-09-18 14:01:29 -0700185} // namespace
186
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700187WaitablePipe::WaitablePipe() {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700188 if (pipe2(pipe_fds, O_CLOEXEC) < 0)
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700189 PLOG(FATAL) << "Failed to create pipe";
190}
191
192WaitablePipe::~WaitablePipe() {
193 if (pipe_fds[0] != -1)
194 close(pipe_fds[0]);
195 if (pipe_fds[1] != -1)
196 close(pipe_fds[1]);
197}
198
199WaitablePipe::WaitablePipe(WaitablePipe&& other) {
200 pipe_fds[0] = pipe_fds[1] = -1;
201 std::swap(pipe_fds, other.pipe_fds);
202}
203
204void WaitablePipe::Wait() {
205 char buf;
206
207 close(pipe_fds[1]);
208 HANDLE_EINTR(read(pipe_fds[0], &buf, sizeof(buf)));
209 close(pipe_fds[0]);
210
211 pipe_fds[0] = pipe_fds[1] = -1;
212}
213
214void WaitablePipe::Signal() {
215 close(pipe_fds[0]);
216 close(pipe_fds[1]);
217
218 pipe_fds[0] = pipe_fds[1] = -1;
219}
220
221HookState::HookState() = default;
222HookState::~HookState() = default;
223
224HookState::HookState(HookState&& state) = default;
225
226bool HookState::InstallHook(struct minijail* j, minijail_hook_event_t event) {
227 if (installed_) {
228 LOG(ERROR) << "Failed to install hook: already installed";
229 return false;
230 }
231
232 // All these fds will be closed in WaitHook in the child process.
233 for (size_t i = 0; i < 2; ++i) {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700234 if (minijail_preserve_fd(j, reached_pipe_.pipe_fds[i],
235 reached_pipe_.pipe_fds[i]) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700236 LOG(ERROR) << "Failed to preserve reached pipe FDs to install hook";
237 return false;
238 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700239 if (minijail_preserve_fd(j, ready_pipe_.pipe_fds[i],
240 ready_pipe_.pipe_fds[i]) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700241 LOG(ERROR) << "Failed to preserve ready pipe FDs to install hook";
242 return false;
243 }
244 }
245
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700246 if (minijail_add_hook(j, &HookState::WaitHook, this, event) != 0) {
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700247 LOG(ERROR) << "Failed to add hook";
248 return false;
249 }
250
251 installed_ = true;
252 return true;
253}
254
255bool HookState::WaitForHookAndRun(const std::vector<HookCallback>& callbacks,
256 pid_t container_pid) {
257 if (!installed_) {
258 LOG(ERROR) << "Failed to wait for hook: not installed";
259 return false;
260 }
261 reached_pipe_.Wait();
262 base::ScopedClosureRunner teardown(
263 base::Bind(&WaitablePipe::Signal, base::Unretained(&ready_pipe_)));
264
265 for (auto& callback : callbacks) {
266 bool success = callback.Run(container_pid);
267 if (!success)
268 return false;
269 }
270 return true;
271}
272
273// static
274int HookState::WaitHook(void* payload) {
275 HookState* self = reinterpret_cast<HookState*>(payload);
276
277 self->reached_pipe_.Signal();
278 self->ready_pipe_.Wait();
279
280 return 0;
281}
282
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700283bool GetUsernsOutsideId(const std::string& map, int id, int* id_out) {
284 if (map.empty()) {
285 if (id_out)
286 *id_out = id;
287 return true;
288 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700289
290 std::string map_copy = map;
291 base::StringPiece map_piece(map_copy);
292
293 for (const auto& mapping : base::SplitStringPiece(
294 map_piece, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
295 std::vector<base::StringPiece> tokens = base::SplitStringPiece(
296 mapping, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
297
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700298 if (tokens.size() != 3) {
299 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700300 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700301 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700302
303 uint32_t inside, outside, length;
304 if (!base::StringToUint(tokens[0], &inside) ||
305 !base::StringToUint(tokens[1], &outside) ||
306 !base::StringToUint(tokens[2], &length)) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700307 LOG(ERROR) << "Malformed ugid mapping: '" << mapping << "'";
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700308 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700309 }
310
311 if (id >= inside && id <= (inside + length)) {
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700312 if (id_out)
313 *id_out = (id - inside) + outside;
314 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700315 }
316 }
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700317 VLOG(1) << "ugid " << id << " not found in mapping";
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700318
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700319 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700320}
321
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700322bool MakeDir(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700323 if (mkdir(path.value().c_str(), mode)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700324 PLOG(ERROR) << "Failed to mkdir " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700325 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700326 }
327 if (chmod(path.value().c_str(), mode)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700328 PLOG(ERROR) << "Failed to chmod " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700329 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700330 }
331 if (chown(path.value().c_str(), uid, gid)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700332 PLOG(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700333 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700334 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700335 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700336}
337
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700338bool TouchFile(const base::FilePath& path, int uid, int gid, int mode) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700339 base::ScopedFD fd(open(path.value().c_str(), O_RDWR | O_CREAT, mode));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700340 if (!fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700341 PLOG(ERROR) << "Failed to create " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700342 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700343 }
344 if (fchown(fd.get(), uid, gid)) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700345 PLOG(ERROR) << "Failed to chown " << path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700346 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700347 }
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700348 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700349}
350
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700351bool LoopdevSetup(const base::FilePath& source,
352 base::FilePath* loopdev_path_out) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700353 base::ScopedFD source_fd(open(source.value().c_str(), O_RDONLY | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700354 if (!source_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700355 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700356 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700357 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700358
359 base::ScopedFD control_fd(
360 open(kLoopdevCtlPath, O_RDWR | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700361 if (!control_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700362 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700363 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700364 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700365
366 while (true) {
367 int num = ioctl(control_fd.get(), LOOP_CTL_GET_FREE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700368 if (num < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700369 PLOG(ERROR) << "Failed to open " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700370 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700371 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700372
373 base::FilePath loopdev_path(base::StringPrintf("/dev/loop%i", num));
374 base::ScopedFD loop_fd(
375 open(loopdev_path.value().c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700376 if (!loop_fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700377 PLOG(ERROR) << "Failed to open " << loopdev_path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700378 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700379 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700380
381 if (ioctl(loop_fd.get(), LOOP_SET_FD, source_fd.get()) == 0) {
382 *loopdev_path_out = loopdev_path;
383 break;
384 }
385
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700386 if (errno != EBUSY) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700387 PLOG(ERROR) << "Failed to ioctl(LOOP_SET_FD) " << loopdev_path.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700388 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700389 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700390 }
391
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700392 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700393}
394
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700395bool LoopdevDetach(const base::FilePath& loopdev) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700396 base::ScopedFD fd(
397 open(loopdev.value().c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700398 if (!fd.is_valid()) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700399 PLOG(ERROR) << "Failed to open " << loopdev.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700400 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700401 }
402 if (ioctl(fd.get(), LOOP_CLR_FD) < 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700403 PLOG(ERROR) << "Failed to ioctl(LOOP_CLR_FD) for " << loopdev.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700404 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700405 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700406
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700407 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700408}
409
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700410bool DeviceMapperSetup(const base::FilePath& source,
411 const std::string& verity_cmdline,
412 base::FilePath* dm_path_out,
413 std::string* dm_name_out) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700414#if USE_device_mapper
415 // Normalize the name into something unique-esque.
416 std::string dm_name =
417 base::StringPrintf("cros-containers-%s", source.value().c_str());
418 base::ReplaceChars(dm_name, "/", "_", &dm_name);
419
420 // Get the /dev path for the higher levels to mount.
421 base::FilePath dm_path = base::FilePath(kDevMapperPath).Append(dm_name);
422
423 // Insert the source path in the verity command line.
424 std::string verity = verity_cmdline;
425 base::ReplaceSubstringsAfterOffset(&verity, 0, "@DEV@", source.value());
426
427 // Extract the first three parameters for dm-verity settings.
428 char ttype[20];
429 unsigned long long start, size;
430 int n;
431 if (sscanf(verity.c_str(), "%llu %llu %10s %n", &start, &size, ttype, &n) !=
432 3) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700433 PLOG(ERROR) << "Malformed verity string " << verity;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700434 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700435 }
436
437 /* Finally create the device mapper. */
438 std::unique_ptr<struct dm_task, decltype(&dm_task_destroy)> dmt(
439 dm_task_create(DM_DEVICE_CREATE), &dm_task_destroy);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700440 if (dmt == nullptr) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700441 PLOG(ERROR) << "Failed to dm_task_create() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700442 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700443 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700444
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700445 if (dm_task_set_name(dmt.get(), dm_name.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700446 PLOG(ERROR) << "Failed to dm_task_set_name() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700447 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700448 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700449
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700450 if (dm_task_set_ro(dmt.get()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700451 PLOG(ERROR) << "Failed to dm_task_set_ro() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700452 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700453 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700454
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700455 if (dm_task_add_target(dmt.get(), start, size, ttype, verity.c_str() + n) !=
456 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700457 PLOG(ERROR) << "Failed to dm_task_add_target() for " << source.value();
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700458 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700459 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700460
461 uint32_t cookie = 0;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700462 if (dm_task_set_cookie(dmt.get(), &cookie, 0) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700463 PLOG(ERROR) << "Failed to dm_task_set_cookie() 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_run(dmt.get()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700468 PLOG(ERROR) << "Failed to dm_task_run() 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
472 /* Make sure the node exists before we continue. */
473 dm_udev_wait(cookie);
474
475 *dm_path_out = dm_path;
476 *dm_name_out = dm_name;
477#endif
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700478 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700479}
480
481// Tear down the device mapper target.
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700482bool DeviceMapperDetach(const std::string& dm_name) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700483#if USE_device_mapper
484 struct dm_task* dmt = dm_task_create(DM_DEVICE_REMOVE);
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700485 if (dmt == nullptr) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700486 PLOG(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700487 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700488 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700489
490 base::ScopedClosureRunner teardown(
491 base::Bind(base::IgnoreResult(&dm_task_destroy), base::Unretained(dmt)));
492
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700493 if (dm_task_set_name(dmt, dm_name.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700494 PLOG(ERROR) << "Failed to dm_task_set_name() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700495 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700496 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700497
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700498 if (dm_task_run(dmt) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700499 PLOG(ERROR) << "Failed to dm_task_run() for " << dm_name;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700500 return false;
Luis Hector Chavez835d39e2017-09-19 15:16:31 -0700501 }
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700502#endif
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700503 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700504}
505
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700506bool MountExternal(const std::string& src,
507 const std::string& dest,
508 const std::string& type,
509 unsigned long flags,
510 const std::string& data) {
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700511 bool remount_ro = false;
512
513 // R/O bind mounts have to be remounted since 'bind' and 'ro' can't both be
514 // specified in the original bind mount. Remount R/O after the initial mount.
515 if ((flags & MS_BIND) && (flags & MS_RDONLY)) {
516 remount_ro = true;
517 flags &= ~MS_RDONLY;
518 }
519
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700520 if (mount(src.c_str(), dest.c_str(), type.c_str(), flags,
521 data.empty() ? nullptr : data.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700522 PLOG(ERROR) << "Failed to mount " << src << " to " << dest;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700523 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700524 }
525
526 if (remount_ro) {
527 flags |= MS_RDONLY;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700528 if (mount(src.c_str(), dest.c_str(), nullptr, flags | MS_REMOUNT,
529 data.empty() ? nullptr : data.c_str()) != 0) {
Luis Hector Chavezdc61f8d2017-10-02 11:12:46 -0700530 PLOG(ERROR) << "Failed to remount " << src << " to " << dest;
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700531 return false;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700532 }
533 }
534
Luis Hector Chavez1f7e60c2017-09-27 22:03:48 -0700535 return true;
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700536}
537
Luis Hector Chaveze03926a2017-09-28 17:28:49 -0700538bool Pipe2(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe, int flags) {
539 int fds[2];
540 if (pipe2(fds, flags) != 0)
541 return false;
542 read_pipe->reset(fds[0]);
543 write_pipe->reset(fds[1]);
544 return true;
545}
546
547HookCallback CreateExecveCallback(base::FilePath filename,
548 std::vector<std::string> args,
549 base::ScopedFD stdin_fd,
550 base::ScopedFD stdout_fd,
551 base::ScopedFD stderr_fd) {
552 return base::Bind(
553 &ExecveCallbackHelper, filename, args, base::Passed(std::move(stdin_fd)),
554 base::Passed(std::move(stdout_fd)), base::Passed(std::move(stderr_fd)));
555}
556
Luis Hector Chavez644d2042017-09-19 18:56:44 -0700557HookCallback AdaptCallbackToRunInNamespaces(HookCallback callback,
558 std::vector<int> nstypes) {
559 return base::Bind(&RunInNamespacesHelper,
560 base::Passed(std::move(callback)),
561 base::Passed(std::move(nstypes)));
562}
563
Luis Hector Chavez92278e82017-10-16 11:30:27 -0700564bool CreateDirectoryOwnedBy(const base::FilePath& full_path,
565 mode_t mode,
566 uid_t uid,
567 gid_t gid) {
568 if (base::DirectoryExists(full_path))
569 return true;
570
571 // Collect a list of all missing directories.
572 base::FilePath last_path = full_path;
573 std::vector<base::FilePath> missing_subpaths{full_path};
574 for (base::FilePath path = full_path.DirName();
575 path != last_path && !base::DirectoryExists(path);
576 path = path.DirName()) {
577 missing_subpaths.push_back(path);
578 last_path = path;
579 }
580
581 // Iterate through the missing parents, creating them.
582 for (std::vector<base::FilePath>::reverse_iterator i =
583 missing_subpaths.rbegin();
584 i != missing_subpaths.rend(); ++i) {
585 if (mkdir(i->value().c_str(), mode) != 0)
586 return false;
587 if (chown(i->value().c_str(), uid, gid) != 0)
588 return false;
589 }
590 return true;
591}
592
Luis Hector Chavez81efb332017-09-18 14:01:29 -0700593} // namespace libcontainer