blob: caee04f24b1ca9e9adfad09477979aacb65b94b3 [file] [log] [blame]
Dylan Reid837c74a2016-01-22 17:25:21 -08001/* Copyright 2016 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
6#define _GNU_SOURCE /* For asprintf */
7
8#include <errno.h>
9#include <fcntl.h>
10#include <malloc.h>
11#include <signal.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/mount.h>
16#include <sys/stat.h>
17#include <sys/types.h>
Dylan Reid2bd9ea92016-04-07 20:57:47 -070018#include <sys/wait.h>
Dylan Reid837c74a2016-01-22 17:25:21 -080019#include <unistd.h>
20
21#include "container_cgroup.h"
22#include "libcontainer.h"
23#include "libminijail.h"
24
25struct container_mount {
26 char *name;
27 char *source;
28 char *destination;
29 char *type;
30 char *data;
31 int flags;
32 int uid;
33 int gid;
34 int mode;
35 int mount_in_ns; /* True if mount should happen in new vfs ns */
36 int create; /* True if target should be created if it doesn't exist */
37};
38
39struct container_device {
40 char type; /* 'c' or 'b' for char or block */
41 char *path;
42 int fs_permissions;
43 int major;
44 int minor;
Dylan Reid355d5e42016-04-29 16:53:31 -070045 int copy_minor; /* Copy the minor from existing node, ignores |minor| */
Dylan Reid837c74a2016-01-22 17:25:21 -080046 int uid;
47 int gid;
48 int read_allowed;
49 int write_allowed;
50 int modify_allowed;
51};
52
53/*
54 * Structure that configures how the container is run.
55 *
56 * rootfs - Path to the root of the container's filesystem.
57 * program_argv - The program to run and args, e.g. "/sbin/init".
58 * num_args - Number of args in program_argv.
59 * uid_map - Mapping of UIDs in the container, e.g. "0 100000 1024"
60 * gid_map - Mapping of GIDs in the container, e.g. "0 100000 1024"
61 * alt_syscall_table - Syscall table to use or NULL if none.
62 * mounts - Filesystems to mount in the new namespace.
63 * num_mounts - Number of above.
64 * devices - Device nodes to create.
65 * num_devices - Number of above.
Dylan Reid2bd9ea92016-04-07 20:57:47 -070066 * run_setfiles - Should run setfiles on mounts to enable selinux.
Dylan Reid837c74a2016-01-22 17:25:21 -080067 */
68struct container_config {
69 char *rootfs;
70 char **program_argv;
71 size_t num_args;
72 char *uid_map;
73 char *gid_map;
74 char *alt_syscall_table;
75 struct container_mount *mounts;
76 size_t num_mounts;
77 struct container_device *devices;
78 size_t num_devices;
Dylan Reid2bd9ea92016-04-07 20:57:47 -070079 const char *run_setfiles;
Dylan Reid837c74a2016-01-22 17:25:21 -080080};
81
82struct container_config *container_config_create()
83{
84 return calloc(1, sizeof(struct container_config));
85}
86
87void container_config_destroy(struct container_config *c)
88{
89 size_t i;
90
91 if (c == NULL)
92 return;
93 free(c->rootfs);
94 for (i = 0; i < c->num_args; ++i)
95 free(c->program_argv[i]);
96 free(c->program_argv);
97 free(c->uid_map);
98 free(c->gid_map);
99 free(c->alt_syscall_table);
100 for (i = 0; i < c->num_mounts; ++i) {
101 free(c->mounts[i].name);
102 free(c->mounts[i].source);
103 free(c->mounts[i].destination);
104 free(c->mounts[i].type);
105 free(c->mounts[i].data);
106 }
107 free(c->mounts);
108 for (i = 0; i < c->num_devices; ++i) {
109 free(c->devices[i].path);
110 }
111 free(c->devices);
112 free(c);
113}
114
115int container_config_rootfs(struct container_config *c, const char *rootfs)
116{
117 c->rootfs = strdup(rootfs);
118 if (!c->rootfs)
119 return -ENOMEM;
120 return 0;
121}
122
Dylan Reid11456722016-05-02 11:24:50 -0700123const char *container_config_get_rootfs(const struct container_config *c)
124{
125 return c->rootfs;
126}
127
Dylan Reid837c74a2016-01-22 17:25:21 -0800128int container_config_program_argv(struct container_config *c,
129 char **argv, size_t num_args)
130{
131 size_t i;
132
133 c->num_args = num_args;
134 c->program_argv = calloc(num_args + 1, sizeof(char *));
135 if (!c->program_argv)
136 return -ENOMEM;
137 for (i = 0; i < num_args; ++i) {
138 c->program_argv[i] = strdup(argv[i]);
139 if (!c->program_argv[i])
140 return -ENOMEM;
141 }
142 c->program_argv[num_args] = NULL;
143 return 0;
144}
145
Dylan Reid11456722016-05-02 11:24:50 -0700146size_t container_config_get_num_program_args(const struct container_config *c)
147{
148 return c->num_args;
149}
150
151const char *container_config_get_program_arg(const struct container_config *c,
152 size_t index)
153{
154 if (index >= c->num_args)
155 return NULL;
156 return c->program_argv[index];
157}
158
Dylan Reid837c74a2016-01-22 17:25:21 -0800159int container_config_uid_map(struct container_config *c, const char *uid_map)
160{
161 c->uid_map = strdup(uid_map);
162 if (!c->uid_map)
163 return -ENOMEM;
164 return 0;
165}
166
167int container_config_gid_map(struct container_config *c, const char *gid_map)
168{
169 c->gid_map = strdup(gid_map);
170 if (!c->gid_map)
171 return -ENOMEM;
172 return 0;
173}
174
175int container_config_alt_syscall_table(struct container_config *c,
176 const char *alt_syscall_table)
177{
178 c->alt_syscall_table = strdup(alt_syscall_table);
179 if (!c->alt_syscall_table)
180 return -ENOMEM;
181 return 0;
182}
183
184int container_config_add_mount(struct container_config *c,
185 const char *name,
186 const char *source,
187 const char *destination,
188 const char *type,
189 const char *data,
190 int flags,
191 int uid,
192 int gid,
193 int mode,
194 int mount_in_ns,
195 int create)
196{
197 struct container_mount *mount_ptr;
198
199 if (name == NULL || source == NULL ||
200 destination == NULL || type == NULL)
201 return -EINVAL;
202
203 mount_ptr = realloc(c->mounts,
204 sizeof(c->mounts[0]) * (c->num_mounts + 1));
205 if (!mount_ptr)
206 return -ENOMEM;
207 c->mounts = mount_ptr;
208 c->mounts[c->num_mounts].name = strdup(name);
209 if (!c->mounts[c->num_mounts].name)
210 return -ENOMEM;
211 c->mounts[c->num_mounts].source = strdup(source);
212 if (!c->mounts[c->num_mounts].source)
213 return -ENOMEM;
214 c->mounts[c->num_mounts].destination = strdup(destination);
215 if (!c->mounts[c->num_mounts].destination)
216 return -ENOMEM;
217 c->mounts[c->num_mounts].type = strdup(type);
218 if (!c->mounts[c->num_mounts].type)
219 return -ENOMEM;
220 if (data) {
221 c->mounts[c->num_mounts].data = strdup(data);
222 if (!c->mounts[c->num_mounts].data)
223 return -ENOMEM;
224 } else {
225 c->mounts[c->num_mounts].data = NULL;
226 }
227 c->mounts[c->num_mounts].flags = flags;
228 c->mounts[c->num_mounts].uid = uid;
229 c->mounts[c->num_mounts].gid = gid;
230 c->mounts[c->num_mounts].mode = mode;
231 c->mounts[c->num_mounts].mount_in_ns = mount_in_ns;
232 c->mounts[c->num_mounts].create = create;
233 ++c->num_mounts;
234 return 0;
235}
236
237int container_config_add_device(struct container_config *c,
238 char type,
239 const char *path,
240 int fs_permissions,
241 int major,
242 int minor,
Dylan Reid355d5e42016-04-29 16:53:31 -0700243 int copy_minor,
Dylan Reid837c74a2016-01-22 17:25:21 -0800244 int uid,
245 int gid,
246 int read_allowed,
247 int write_allowed,
248 int modify_allowed)
249{
250 struct container_device *dev_ptr;
251
252 if (path == NULL)
253 return -EINVAL;
Dylan Reid355d5e42016-04-29 16:53:31 -0700254 /* If using a dynamic minor number, ensure that minor is -1. */
255 if (copy_minor && (minor != -1))
256 return -EINVAL;
257
Dylan Reid837c74a2016-01-22 17:25:21 -0800258 dev_ptr = realloc(c->devices,
259 sizeof(c->devices[0]) * (c->num_devices + 1));
260 if (!dev_ptr)
261 return -ENOMEM;
262 c->devices = dev_ptr;
263 c->devices[c->num_devices].type = type;
264 c->devices[c->num_devices].path = strdup(path);
265 if (!c->devices[c->num_devices].path)
266 return -ENOMEM;
267 c->devices[c->num_devices].fs_permissions = fs_permissions;
268 c->devices[c->num_devices].major = major;
269 c->devices[c->num_devices].minor = minor;
Dylan Reid355d5e42016-04-29 16:53:31 -0700270 c->devices[c->num_devices].copy_minor = copy_minor;
Dylan Reid837c74a2016-01-22 17:25:21 -0800271 c->devices[c->num_devices].uid = uid;
272 c->devices[c->num_devices].gid = gid;
273 c->devices[c->num_devices].read_allowed = read_allowed;
274 c->devices[c->num_devices].write_allowed = write_allowed;
275 c->devices[c->num_devices].modify_allowed = modify_allowed;
276 ++c->num_devices;
277 return 0;
278}
279
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700280void container_config_run_setfiles(struct container_config *c,
281 const char *setfiles_cmd)
282{
283 c->run_setfiles = setfiles_cmd;
284}
Dylan Reid837c74a2016-01-22 17:25:21 -0800285
Dylan Reid11456722016-05-02 11:24:50 -0700286const char *container_config_get_run_setfiles(const struct container_config *c)
287{
288 return c->run_setfiles;
289}
290
Dylan Reid837c74a2016-01-22 17:25:21 -0800291/*
292 * Container manipulation
293 */
294struct container {
Dylan Reid837c74a2016-01-22 17:25:21 -0800295 struct container_cgroup *cgroup;
296 struct minijail *jail;
297 pid_t init_pid;
298 char *runfs;
299 char *rundir;
300 char *runfsroot;
301 char *pid_file_path;
Dylan Reide040c6b2016-05-02 18:49:02 -0700302 char **ext_mounts; /* Mounts made outside of the minijail */
303 size_t num_ext_mounts;
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700304 char *name;
Dylan Reid837c74a2016-01-22 17:25:21 -0800305};
306
307struct container *container_new(const char *name,
Dylan Reide040c6b2016-05-02 18:49:02 -0700308 const char *rundir)
Dylan Reid837c74a2016-01-22 17:25:21 -0800309{
310 struct container *c;
311
Dylan Reid837c74a2016-01-22 17:25:21 -0800312 c = calloc(1, sizeof(*c));
Dylan Reidb435c682016-04-12 04:17:49 -0700313 if (!c)
314 return NULL;
Dylan Reid837c74a2016-01-22 17:25:21 -0800315 c->cgroup = container_cgroup_new(name, "/sys/fs/cgroup");
316 c->rundir = strdup(rundir);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700317 c->name = strdup(name);
318 if (!c->cgroup || !c->rundir || !c->name) {
Dylan Reid684975e2016-05-02 15:44:47 -0700319 container_destroy(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800320 return NULL;
Dylan Reidb435c682016-04-12 04:17:49 -0700321 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800322 return c;
323}
324
325void container_destroy(struct container *c)
326{
Dylan Reid684975e2016-05-02 15:44:47 -0700327 if (c->cgroup)
328 container_cgroup_destroy(c->cgroup);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700329 if (c->jail)
330 minijail_destroy(c->jail);
331 free(c->name);
Dylan Reid837c74a2016-01-22 17:25:21 -0800332 free(c->rundir);
333 free(c);
334}
335
336static int make_dir(const char *path, int uid, int gid, int mode)
337{
338 if (mkdir(path, mode))
339 return -errno;
340 if (chmod(path, mode))
341 return -errno;
342 if (chown(path, uid, gid))
343 return -errno;
344 return 0;
345}
346
347static int touch_file(const char *path, int uid, int gid, int mode)
348{
349 int rc;
350 int fd = open(path, O_RDWR | O_CREAT, mode);
351 if (fd < 0)
352 return -errno;
353 rc = fchown(fd, uid, gid);
354 close(fd);
355
356 if (rc)
357 return -errno;
358 return 0;
359}
360
361/* Make sure the mount target exists in the new rootfs. Create if needed and
362 * possible.
363 */
364static int setup_mount_destination(const struct container_mount *mnt,
Dylan Reid2149be92016-04-28 18:38:57 -0700365 const char *source,
Dylan Reid837c74a2016-01-22 17:25:21 -0800366 const char *dest)
367{
368 int rc;
369 struct stat st_buf;
370
371 rc = stat(dest, &st_buf);
372 if (rc == 0) /* destination exists */
373 return 0;
374
375 /* Try to create the destination. Either make directory or touch a file
376 * depending on the source type.
377 */
Dylan Reid2149be92016-04-28 18:38:57 -0700378 rc = stat(source, &st_buf);
Dylan Reid837c74a2016-01-22 17:25:21 -0800379 if (rc || S_ISDIR(st_buf.st_mode) || S_ISBLK(st_buf.st_mode))
380 return make_dir(dest, mnt->uid, mnt->gid, mnt->mode);
381
382 return touch_file(dest, mnt->uid, mnt->gid, mnt->mode);
383}
384
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700385/* Fork and exec the setfiles command to configure the selinux policy. */
Dylan Reide040c6b2016-05-02 18:49:02 -0700386static int run_setfiles_command(const struct container *c,
387 const struct container_config *config,
388 const char *dest)
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700389{
390 int rc;
391 int status;
392 int pid;
393 char *context_path;
394
Dylan Reide040c6b2016-05-02 18:49:02 -0700395 if (!config->run_setfiles)
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700396 return 0;
397
Dylan Reidb3621832016-03-24 10:24:57 -0700398 /* Really gross hack to avoid setfiles on /data, this should be removed
399 * when data isn't under /home/chronos/user where we can't access it as
400 * the android user.
401 * TODO(b/28705740) - Fix permission to the data directory.
402 */
403 if (strlen(dest) >= 5 && !strcmp(&dest[strlen(dest) - 5], "/data"))
404 return 0;
405
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700406 if (asprintf(&context_path, "%s/file_contexts",
407 c->runfsroot) < 0)
408 return -errno;
409
410 pid = fork();
411 if (pid == 0) {
412 const char *argv[] = {
Dylan Reide040c6b2016-05-02 18:49:02 -0700413 config->run_setfiles,
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700414 "-r",
415 c->runfsroot,
416 context_path,
417 dest,
418 NULL,
419 };
420 const char *env[] = {
421 NULL,
422 };
423
424 execve(argv[0], (char *const*)argv, (char *const*)env);
425
426 /* Command failed to exec if execve returns. */
427 _exit(-errno);
428 }
429 free(context_path);
430 if (pid < 0)
431 return -errno;
432 do {
433 rc = waitpid(pid, &status, 0);
434 } while (rc == -1 && errno == EINTR);
435 if (rc < 0)
436 return -errno;
437 return status;
438}
439
Dylan Reide040c6b2016-05-02 18:49:02 -0700440/*
441 * Unmounts anything we mounted in this mount namespace in the opposite order
442 * that they were mounted.
443 */
444static int unmount_external_mounts(struct container *c)
445{
446 int ret = 0;
447
448 while (c->num_ext_mounts) {
449 c->num_ext_mounts--;
450 if (umount(c->ext_mounts[c->num_ext_mounts]))
451 ret = -errno;
452 free(c->ext_mounts[c->num_ext_mounts]);
453 }
454 free(c->ext_mounts);
455 return ret;
456}
457
458static int do_container_mounts(struct container *c,
459 const struct container_config *config)
Dylan Reid7daf9982016-04-28 16:55:42 -0700460{
461 unsigned int i;
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700462 int rc = 0;
Dylan Reid2149be92016-04-28 18:38:57 -0700463 char *source;
464 char *dest;
Dylan Reid7daf9982016-04-28 16:55:42 -0700465
Dylan Reide040c6b2016-05-02 18:49:02 -0700466 /*
467 * Allocate space to track anything we mount in our mount namespace.
468 * This over-allocates as it has space for all mounts.
469 */
470 c->ext_mounts = calloc(config->num_mounts, sizeof(*c->ext_mounts));
471 if (!c->ext_mounts)
472 return -errno;
473
474 for (i = 0; i < config->num_mounts; ++i) {
475 const struct container_mount *mnt = &config->mounts[i];
Dylan Reid7daf9982016-04-28 16:55:42 -0700476
Dylan Reid2149be92016-04-28 18:38:57 -0700477 source = NULL;
478 dest = NULL;
Dylan Reid7daf9982016-04-28 16:55:42 -0700479 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
480 return -errno;
481
Dylan Reid2149be92016-04-28 18:38:57 -0700482 /*
483 * If it's a bind mount relative to rootfs, append source to
484 * rootfs path, otherwise source path is absolute.
485 */
486 if ((mnt->flags & MS_BIND) && mnt->source[0] != '/') {
487 if (asprintf(&source, "%s/%s", c->runfsroot,
488 mnt->source) < 0)
489 goto error_free_return;
490 } else {
491 if (asprintf(&source, "%s", mnt->source) < 0)
492 goto error_free_return;
493 }
494
Dylan Reid7daf9982016-04-28 16:55:42 -0700495 if (mnt->create) {
Dylan Reid2149be92016-04-28 18:38:57 -0700496 if (setup_mount_destination(mnt, source, dest))
497 goto error_free_return;
Dylan Reid7daf9982016-04-28 16:55:42 -0700498 }
499 if (mnt->mount_in_ns) {
Dylan Reid2149be92016-04-28 18:38:57 -0700500 /* We can mount this with minijail. */
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700501 rc = minijail_mount(c->jail, source, mnt->destination,
502 mnt->type, mnt->flags);
503 if (rc)
Dylan Reid2149be92016-04-28 18:38:57 -0700504 goto error_free_return;
Dylan Reid7daf9982016-04-28 16:55:42 -0700505 } else {
Dylan Reidb3621832016-03-24 10:24:57 -0700506 /* Mount this externally and unmount it on exit. */
507 if (mount(source, dest, mnt->type, mnt->flags,
508 mnt->data))
Dylan Reid2149be92016-04-28 18:38:57 -0700509 goto error_free_return;
Dylan Reide040c6b2016-05-02 18:49:02 -0700510 /* Save this to unmount when shutting down. */
511 c->ext_mounts[c->num_ext_mounts] = strdup(dest);
512 c->num_ext_mounts++;
Dylan Reid7daf9982016-04-28 16:55:42 -0700513 }
Dylan Reid2149be92016-04-28 18:38:57 -0700514 free(source);
Dylan Reid7daf9982016-04-28 16:55:42 -0700515 free(dest);
516 }
517 return 0;
Dylan Reid2149be92016-04-28 18:38:57 -0700518
519error_free_return:
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700520 if (!rc)
521 rc = -errno;
Dylan Reid2149be92016-04-28 18:38:57 -0700522 free(dest);
523 free(source);
Dylan Reide040c6b2016-05-02 18:49:02 -0700524 unmount_external_mounts(c);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700525 return rc;
Dylan Reid7daf9982016-04-28 16:55:42 -0700526}
527
Dylan Reide040c6b2016-05-02 18:49:02 -0700528int container_start(struct container *c, const struct container_config *config)
Dylan Reid837c74a2016-01-22 17:25:21 -0800529{
Dylan Reidb3621832016-03-24 10:24:57 -0700530 static const mode_t root_dir_mode = 0660;
Dylan Reid837c74a2016-01-22 17:25:21 -0800531 int rc;
532 unsigned int i;
Dylan Reide040c6b2016-05-02 18:49:02 -0700533 const char *rootfs = config->rootfs;
Dylan Reid837c74a2016-01-22 17:25:21 -0800534 char *runfs_template;
535
Dylan Reide040c6b2016-05-02 18:49:02 -0700536 if (!config)
537 return -EINVAL;
538 if (!config->program_argv || !config->program_argv[0])
539 return -EINVAL;
540
Dylan Reid837c74a2016-01-22 17:25:21 -0800541 if (asprintf(&runfs_template, "%s/%s_XXXXXX", c->rundir, c->name) < 0)
542 return -errno;
543
544 c->runfs = mkdtemp(runfs_template);
545 if (!c->runfs) {
546 free(runfs_template);
547 return -errno;
548 }
Dylan Reidb3621832016-03-24 10:24:57 -0700549 /* Make sure the container uid can access the rootfs. */
550 rc = chmod(c->runfs, 0755);
551 if (rc)
552 goto error_rmdir;
553
Dylan Reid837c74a2016-01-22 17:25:21 -0800554 if (asprintf(&c->runfsroot, "%s/root", c->runfs) < 0) {
555 free(runfs_template);
556 return -errno;
557 }
558
Dylan Reidb3621832016-03-24 10:24:57 -0700559 rc = mkdir(c->runfsroot, root_dir_mode);
560 if (rc)
561 goto error_rmdir;
562 rc = chmod(c->runfsroot, root_dir_mode);
Dylan Reid837c74a2016-01-22 17:25:21 -0800563 if (rc)
564 goto error_rmdir;
565
Dylan Reidb3621832016-03-24 10:24:57 -0700566 rc = mount(rootfs, c->runfsroot, "", MS_BIND | MS_RDONLY, NULL);
Dylan Reid837c74a2016-01-22 17:25:21 -0800567 if (rc)
568 goto error_rmdir;
569
570 c->jail = minijail_new();
571
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700572 rc = do_container_mounts(c, config);
573 if (rc)
Dylan Reid7daf9982016-04-28 16:55:42 -0700574 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -0800575
576 c->cgroup->ops->deny_all_devices(c->cgroup);
577
Dylan Reide040c6b2016-05-02 18:49:02 -0700578 for (i = 0; i < config->num_devices; i++) {
579 const struct container_device *dev = &config->devices[i];
Dylan Reid837c74a2016-01-22 17:25:21 -0800580 int mode;
Dylan Reid355d5e42016-04-29 16:53:31 -0700581 int minor = dev->minor;
Dylan Reid837c74a2016-01-22 17:25:21 -0800582
583 switch (dev->type) {
584 case 'b':
585 mode = S_IFBLK;
586 break;
587 case 'c':
588 mode = S_IFCHR;
589 break;
590 default:
591 goto error_rmdir;
592 }
593 mode |= dev->fs_permissions;
594
Dylan Reid355d5e42016-04-29 16:53:31 -0700595 if (dev->copy_minor) {
596 struct stat st_buff;
597 if (stat(dev->path, &st_buff) < 0)
598 goto error_rmdir;
599 /* Use the minor macro to extract the device number. */
600 minor = minor(st_buff.st_rdev);
601 }
602 if (minor >= 0) {
603 char *path;
604
605 if (asprintf(&path, "%s%s", c->runfsroot, dev->path) < 0)
606 goto error_rmdir;
607 rc = mknod(path, mode, makedev(dev->major, minor));
Dylan Reid837c74a2016-01-22 17:25:21 -0800608 if (rc && errno != EEXIST) {
609 free(path);
610 goto error_rmdir;
611 }
612 rc = chown(path, dev->uid, dev->gid);
613 if (rc) {
614 free(path);
615 goto error_rmdir;
616 }
617 rc = chmod(path, dev->fs_permissions);
618 free(path);
619 if (rc)
620 goto error_rmdir;
621 }
622
623 rc = c->cgroup->ops->add_device(c->cgroup, dev->major,
Dylan Reid355d5e42016-04-29 16:53:31 -0700624 minor, dev->read_allowed,
Dylan Reid837c74a2016-01-22 17:25:21 -0800625 dev->write_allowed,
626 dev->modify_allowed, dev->type);
627 if (rc)
628 goto error_rmdir;
629 }
630
Dylan Reidd7229582016-04-27 17:08:40 -0700631 /* Potentailly run setfiles on mounts configured outside of the jail */
Dylan Reide040c6b2016-05-02 18:49:02 -0700632 for (i = 0; i < config->num_mounts; i++) {
633 const struct container_mount *mnt = &config->mounts[i];
Dylan Reidd7229582016-04-27 17:08:40 -0700634 char *dest;
635
636 if (mnt->mount_in_ns)
637 continue;
638 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
639 goto error_rmdir;
Dylan Reide040c6b2016-05-02 18:49:02 -0700640 rc = run_setfiles_command(c, config, dest);
Dylan Reidd7229582016-04-27 17:08:40 -0700641 free(dest);
642 if (rc)
643 goto error_rmdir;
644 }
645
Dylan Reid837c74a2016-01-22 17:25:21 -0800646 /* Setup and start the container with libminijail. */
647 if (asprintf(&c->pid_file_path, "%s/container.pid", c->runfs) < 0)
648 goto error_rmdir;
649 minijail_write_pid_file(c->jail, c->pid_file_path);
650 minijail_reset_signal_mask(c->jail);
651
652 /* Setup container namespaces. */
653 minijail_namespace_ipc(c->jail);
654 minijail_namespace_vfs(c->jail);
655 minijail_namespace_net(c->jail);
656 minijail_namespace_pids(c->jail);
Dylan Reid837c74a2016-01-22 17:25:21 -0800657 minijail_namespace_user(c->jail);
Dylan Reide040c6b2016-05-02 18:49:02 -0700658 rc = minijail_uidmap(c->jail, config->uid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -0800659 if (rc)
660 goto error_rmdir;
Dylan Reide040c6b2016-05-02 18:49:02 -0700661 rc = minijail_gidmap(c->jail, config->gid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -0800662 if (rc)
663 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -0800664
665 rc = minijail_enter_pivot_root(c->jail, c->runfsroot);
666 if (rc)
667 goto error_rmdir;
668
669 /* Add the cgroups configured above. */
670 rc = minijail_add_to_cgroup(c->jail, cgroup_cpu_tasks_path(c->cgroup));
671 if (rc)
672 goto error_rmdir;
673 rc = minijail_add_to_cgroup(c->jail,
674 cgroup_cpuacct_tasks_path(c->cgroup));
675 if (rc)
676 goto error_rmdir;
677 rc = minijail_add_to_cgroup(c->jail,
678 cgroup_devices_tasks_path(c->cgroup));
679 if (rc)
680 goto error_rmdir;
681 rc = minijail_add_to_cgroup(c->jail,
682 cgroup_freezer_tasks_path(c->cgroup));
683 if (rc)
684 goto error_rmdir;
685
Dylan Reide040c6b2016-05-02 18:49:02 -0700686 if (config->alt_syscall_table)
687 minijail_use_alt_syscall(c->jail, config->alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -0800688
689 minijail_run_as_init(c->jail);
690
Dylan Reid3da683b2016-04-05 03:35:35 -0700691 /* TODO(dgreid) - remove this once shared mounts are cleaned up. */
692 minijail_skip_remount_private(c->jail);
693
Dylan Reid837c74a2016-01-22 17:25:21 -0800694 rc = minijail_run_pid_pipes_no_preload(c->jail,
Dylan Reide040c6b2016-05-02 18:49:02 -0700695 config->program_argv[0],
696 config->program_argv,
Dylan Reid837c74a2016-01-22 17:25:21 -0800697 &c->init_pid, NULL, NULL,
698 NULL);
699 if (rc)
700 goto error_rmdir;
701 return 0;
702
703error_rmdir:
704 umount(c->runfsroot);
705 rmdir(c->runfsroot);
Dylan Reid5fa22082016-05-05 09:20:43 -0700706 if (c->pid_file_path)
707 unlink(c->pid_file_path);
Dylan Reid837c74a2016-01-22 17:25:21 -0800708 free(c->pid_file_path);
709 rmdir(c->runfs);
710 free(c->runfsroot);
711 free(c->runfs);
712 return rc;
713}
714
715const char *container_root(struct container *c)
716{
717 return c->runfs;
718}
719
720int container_pid(struct container *c)
721{
722 return c->init_pid;
723}
724
725static int container_teardown(struct container *c)
726{
Dylan Reid837c74a2016-01-22 17:25:21 -0800727 int ret = 0;
728
Dylan Reide040c6b2016-05-02 18:49:02 -0700729 unmount_external_mounts(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800730 if (umount(c->runfsroot))
731 ret = -errno;
732 if (rmdir(c->runfsroot))
733 ret = -errno;
Dylan Reid5fa22082016-05-05 09:20:43 -0700734 if (c->pid_file_path && unlink(c->pid_file_path))
Dylan Reid837c74a2016-01-22 17:25:21 -0800735 ret = -errno;
736 if (rmdir(c->runfs))
737 ret = -errno;
738 free(c->pid_file_path);
739 free(c->runfsroot);
740 free(c->runfs);
741 return ret;
742}
743
744int container_wait(struct container *c)
745{
Dylan Reidcf745c52016-04-22 10:18:03 -0700746 int rc;
747
748 do {
749 rc = minijail_wait(c->jail);
Luis Hector Chavez4641e852016-06-02 15:40:19 -0700750 } while (rc == -EINTR);
Dylan Reidcf745c52016-04-22 10:18:03 -0700751
Dylan Reid0025ab02016-06-01 10:54:44 -0700752 if (rc >= 0)
Dylan Reidcf745c52016-04-22 10:18:03 -0700753 rc = container_teardown(c);
754 return rc;
Dylan Reid837c74a2016-01-22 17:25:21 -0800755}
756
757int container_kill(struct container *c)
758{
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700759 if (kill(c->init_pid, SIGKILL))
Dylan Reid837c74a2016-01-22 17:25:21 -0800760 return -errno;
761 return container_wait(c);
762}