blob: f42c3c34d75a69fabcfa4afb153da7802a296a95 [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>
Mike Frysinger05e594e2017-01-10 02:11:08 -050010#if USE_device_mapper
11#include <libdevmapper.h>
12#endif
Dylan Reid837c74a2016-01-22 17:25:21 -080013#include <malloc.h>
14#include <signal.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sys/mount.h>
19#include <sys/stat.h>
20#include <sys/types.h>
Dylan Reid2bd9ea92016-04-07 20:57:47 -070021#include <sys/wait.h>
Dylan Reid837c74a2016-01-22 17:25:21 -080022#include <unistd.h>
23
Mike Frysinger412dbd22017-01-06 01:50:34 -050024#include <linux/loop.h>
25
Dylan Reid837c74a2016-01-22 17:25:21 -080026#include "container_cgroup.h"
27#include "libcontainer.h"
28#include "libminijail.h"
29
Luis Hector Chavez479b95f2016-06-06 08:01:05 -070030#define FREE_AND_NULL(ptr) \
31do { \
32 free(ptr); \
33 ptr = NULL; \
34} while(0)
35
Yusuke Sato91f11f02016-12-02 16:15:13 -080036#define MAX_NUM_SETFILES_ARGS 128
37
Mike Frysinger412dbd22017-01-06 01:50:34 -050038static const char loopdev_ctl[] = "/dev/loop-control";
Mike Frysinger05e594e2017-01-10 02:11:08 -050039#if USE_device_mapper
40static const char dm_dev_prefix[] = "/dev/mapper/";
41#endif
Mike Frysinger412dbd22017-01-06 01:50:34 -050042
Luis Hector Chavez945af482016-06-03 08:39:34 -070043static int container_teardown(struct container *c);
44
Luis Hector Chavez479b95f2016-06-06 08:01:05 -070045static int strdup_and_free(char **dest, const char *src)
46{
47 char *copy = strdup(src);
48 if (!copy)
49 return -ENOMEM;
50 if (*dest)
51 free(*dest);
52 *dest = copy;
53 return 0;
54}
55
Dylan Reid837c74a2016-01-22 17:25:21 -080056struct container_mount {
57 char *name;
58 char *source;
59 char *destination;
60 char *type;
61 char *data;
Mike Frysinger05e594e2017-01-10 02:11:08 -050062 char *verity;
Dylan Reid837c74a2016-01-22 17:25:21 -080063 int flags;
64 int uid;
65 int gid;
66 int mode;
67 int mount_in_ns; /* True if mount should happen in new vfs ns */
68 int create; /* True if target should be created if it doesn't exist */
Mike Frysinger412dbd22017-01-06 01:50:34 -050069 int loopback; /* True if target should be mounted via loopback */
Dylan Reid837c74a2016-01-22 17:25:21 -080070};
71
72struct container_device {
73 char type; /* 'c' or 'b' for char or block */
74 char *path;
75 int fs_permissions;
76 int major;
77 int minor;
Dylan Reid355d5e42016-04-29 16:53:31 -070078 int copy_minor; /* Copy the minor from existing node, ignores |minor| */
Dylan Reid837c74a2016-01-22 17:25:21 -080079 int uid;
80 int gid;
Dylan Reid4843d6b2017-03-31 18:14:30 -070081};
82
83struct container_cgroup_device {
84 int allow;
85 char type;
86 int major; /* -1 means all */
87 int minor; /* -1 means all */
88 int read;
89 int write;
90 int modify;
Dylan Reid837c74a2016-01-22 17:25:21 -080091};
92
Chinyue Chenfac909e2016-06-24 14:17:42 +080093struct container_cpu_cgroup {
94 int shares;
95 int quota;
96 int period;
97 int rt_runtime;
98 int rt_period;
99};
100
Dylan Reid837c74a2016-01-22 17:25:21 -0800101/*
102 * Structure that configures how the container is run.
103 *
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500104 * config_root - Path to the root of the container itself.
Dylan Reid837c74a2016-01-22 17:25:21 -0800105 * rootfs - Path to the root of the container's filesystem.
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -0700106 * rootfs_mount_flags - Flags that will be passed to mount() for the rootfs.
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700107 * premounted_runfs - Path to where the container will be run.
108 * pid_file_path - Path to the file where the pid should be written.
Dylan Reid837c74a2016-01-22 17:25:21 -0800109 * program_argv - The program to run and args, e.g. "/sbin/init".
110 * num_args - Number of args in program_argv.
Dylan Reid1874feb2016-06-22 17:53:50 -0700111 * uid - The uid the container will run as.
Dylan Reid837c74a2016-01-22 17:25:21 -0800112 * uid_map - Mapping of UIDs in the container, e.g. "0 100000 1024"
Dylan Reid1874feb2016-06-22 17:53:50 -0700113 * gid - The gid the container will run as.
Dylan Reid837c74a2016-01-22 17:25:21 -0800114 * gid_map - Mapping of GIDs in the container, e.g. "0 100000 1024"
115 * alt_syscall_table - Syscall table to use or NULL if none.
116 * mounts - Filesystems to mount in the new namespace.
117 * num_mounts - Number of above.
118 * devices - Device nodes to create.
119 * num_devices - Number of above.
Dylan Reid4843d6b2017-03-31 18:14:30 -0700120 * cgroup_devices - Device node cgroup permissions.
121 * num_cgroup_devices - Number of above.
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700122 * run_setfiles - Should run setfiles on mounts to enable selinux.
Chinyue Chenfac909e2016-06-24 14:17:42 +0800123 * cpu_cgparams - CPU cgroup params.
Dylan Reid9e724af2016-07-21 09:58:07 -0700124 * cgroup_parent - Parent dir for cgroup creation
125 * cgroup_owner - uid to own the created cgroups
Dmitry Torokhov14eef722016-09-27 16:40:37 -0700126 * cgroup_group - gid to own the created cgroups
Keshav Santhanam1b6bf672016-08-10 18:35:12 -0700127 * share_host_netns - Enable sharing of the host network namespace.
Dylan Reidc4335842016-11-11 10:24:52 -0800128 * keep_fds_open - Allow the child process to keep open FDs (for stdin/out/err).
Dylan Reid837c74a2016-01-22 17:25:21 -0800129 */
130struct container_config {
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500131 char *config_root;
Dylan Reid837c74a2016-01-22 17:25:21 -0800132 char *rootfs;
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -0700133 unsigned long rootfs_mount_flags;
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700134 char *premounted_runfs;
135 char *pid_file_path;
Dylan Reid837c74a2016-01-22 17:25:21 -0800136 char **program_argv;
137 size_t num_args;
Dylan Reid1874feb2016-06-22 17:53:50 -0700138 uid_t uid;
Dylan Reid837c74a2016-01-22 17:25:21 -0800139 char *uid_map;
Dylan Reid1874feb2016-06-22 17:53:50 -0700140 gid_t gid;
Dylan Reid837c74a2016-01-22 17:25:21 -0800141 char *gid_map;
142 char *alt_syscall_table;
143 struct container_mount *mounts;
144 size_t num_mounts;
145 struct container_device *devices;
146 size_t num_devices;
Dylan Reid4843d6b2017-03-31 18:14:30 -0700147 struct container_cgroup_device *cgroup_devices;
148 size_t num_cgroup_devices;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700149 char *run_setfiles;
Chinyue Chenfac909e2016-06-24 14:17:42 +0800150 struct container_cpu_cgroup cpu_cgparams;
Dylan Reid9e724af2016-07-21 09:58:07 -0700151 char *cgroup_parent;
152 uid_t cgroup_owner;
Dmitry Torokhov14eef722016-09-27 16:40:37 -0700153 gid_t cgroup_group;
Keshav Santhanam1b6bf672016-08-10 18:35:12 -0700154 int share_host_netns;
Dylan Reidc4335842016-11-11 10:24:52 -0800155 int keep_fds_open;
Dylan Reid837c74a2016-01-22 17:25:21 -0800156};
157
158struct container_config *container_config_create()
159{
160 return calloc(1, sizeof(struct container_config));
161}
162
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700163static void container_free_program_args(struct container_config *c)
164{
165 int i;
166
167 if (!c->program_argv)
168 return;
169 for (i = 0; i < c->num_args; ++i) {
170 FREE_AND_NULL(c->program_argv[i]);
171 }
172 FREE_AND_NULL(c->program_argv);
173}
174
175static void container_config_free_mount(struct container_mount *mount)
176{
177 FREE_AND_NULL(mount->name);
178 FREE_AND_NULL(mount->source);
179 FREE_AND_NULL(mount->destination);
180 FREE_AND_NULL(mount->type);
181 FREE_AND_NULL(mount->data);
182}
183
184static void container_config_free_device(struct container_device *device)
185{
186 FREE_AND_NULL(device->path);
187}
188
Dylan Reid837c74a2016-01-22 17:25:21 -0800189void container_config_destroy(struct container_config *c)
190{
191 size_t i;
192
193 if (c == NULL)
194 return;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700195 FREE_AND_NULL(c->rootfs);
196 container_free_program_args(c);
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700197 FREE_AND_NULL(c->premounted_runfs);
198 FREE_AND_NULL(c->pid_file_path);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700199 FREE_AND_NULL(c->uid_map);
200 FREE_AND_NULL(c->gid_map);
201 FREE_AND_NULL(c->alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -0800202 for (i = 0; i < c->num_mounts; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700203 container_config_free_mount(&c->mounts[i]);
Dylan Reid837c74a2016-01-22 17:25:21 -0800204 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700205 FREE_AND_NULL(c->mounts);
Dylan Reid837c74a2016-01-22 17:25:21 -0800206 for (i = 0; i < c->num_devices; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700207 container_config_free_device(&c->devices[i]);
Dylan Reid837c74a2016-01-22 17:25:21 -0800208 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700209 FREE_AND_NULL(c->devices);
Dylan Reida34f8162017-05-10 11:33:11 -0700210 FREE_AND_NULL(c->cgroup_devices);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700211 FREE_AND_NULL(c->run_setfiles);
Dylan Reid9e724af2016-07-21 09:58:07 -0700212 FREE_AND_NULL(c->cgroup_parent);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700213 FREE_AND_NULL(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800214}
215
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500216int container_config_config_root(struct container_config *c,
217 const char *config_root)
218{
219 return strdup_and_free(&c->config_root, config_root);
220}
221
222const char *container_config_get_config_root(const struct container_config *c)
223{
224 return c->config_root;
225}
226
Dylan Reid837c74a2016-01-22 17:25:21 -0800227int container_config_rootfs(struct container_config *c, const char *rootfs)
228{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700229 return strdup_and_free(&c->rootfs, rootfs);
Dylan Reid837c74a2016-01-22 17:25:21 -0800230}
231
Dylan Reid11456722016-05-02 11:24:50 -0700232const char *container_config_get_rootfs(const struct container_config *c)
233{
234 return c->rootfs;
235}
236
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -0700237void container_config_rootfs_mount_flags(struct container_config *c,
238 unsigned long rootfs_mount_flags)
239{
240 /* Since we are going to add MS_REMOUNT anyways, add it here so we can
241 * simply check against zero later. MS_BIND is also added to avoid
242 * re-mounting the original filesystem, since the rootfs is always
243 * bind-mounted.
244 */
245 c->rootfs_mount_flags = MS_REMOUNT | MS_BIND | rootfs_mount_flags;
246}
247
248unsigned long container_config_get_rootfs_mount_flags(
249 const struct container_config *c)
250{
251 return c->rootfs_mount_flags;
252}
253
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700254int container_config_premounted_runfs(struct container_config *c, const char *runfs)
255{
256 return strdup_and_free(&c->premounted_runfs, runfs);
257}
258
259const char *container_config_get_premounted_runfs(const struct container_config *c)
260{
261 return c->premounted_runfs;
262}
263
264int container_config_pid_file(struct container_config *c, const char *path)
265{
266 return strdup_and_free(&c->pid_file_path, path);
267}
268
269const char *container_config_get_pid_file(const struct container_config *c)
270{
271 return c->pid_file_path;
272}
273
Dylan Reid837c74a2016-01-22 17:25:21 -0800274int container_config_program_argv(struct container_config *c,
Dylan Reid17fd53f2016-11-18 19:14:41 -0800275 const char **argv, size_t num_args)
Dylan Reid837c74a2016-01-22 17:25:21 -0800276{
277 size_t i;
278
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700279 container_free_program_args(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800280 c->num_args = num_args;
281 c->program_argv = calloc(num_args + 1, sizeof(char *));
282 if (!c->program_argv)
283 return -ENOMEM;
284 for (i = 0; i < num_args; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700285 if (strdup_and_free(&c->program_argv[i], argv[i]))
286 goto error_free_return;
Dylan Reid837c74a2016-01-22 17:25:21 -0800287 }
288 c->program_argv[num_args] = NULL;
289 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700290
291error_free_return:
292 container_free_program_args(c);
293 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800294}
295
Dylan Reid11456722016-05-02 11:24:50 -0700296size_t container_config_get_num_program_args(const struct container_config *c)
297{
298 return c->num_args;
299}
300
301const char *container_config_get_program_arg(const struct container_config *c,
302 size_t index)
303{
304 if (index >= c->num_args)
305 return NULL;
306 return c->program_argv[index];
307}
308
Dylan Reid1874feb2016-06-22 17:53:50 -0700309void container_config_uid(struct container_config *c, uid_t uid)
310{
311 c->uid = uid;
312}
313
314uid_t container_config_get_uid(const struct container_config *c)
315{
316 return c->uid;
317}
318
Dylan Reid837c74a2016-01-22 17:25:21 -0800319int container_config_uid_map(struct container_config *c, const char *uid_map)
320{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700321 return strdup_and_free(&c->uid_map, uid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -0800322}
323
Dylan Reid1874feb2016-06-22 17:53:50 -0700324void container_config_gid(struct container_config *c, gid_t gid)
325{
326 c->gid = gid;
327}
328
329gid_t container_config_get_gid(const struct container_config *c)
330{
331 return c->gid;
332}
333
Dylan Reid837c74a2016-01-22 17:25:21 -0800334int container_config_gid_map(struct container_config *c, const char *gid_map)
335{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700336 return strdup_and_free(&c->gid_map, gid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -0800337}
338
339int container_config_alt_syscall_table(struct container_config *c,
340 const char *alt_syscall_table)
341{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700342 return strdup_and_free(&c->alt_syscall_table, alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -0800343}
344
345int container_config_add_mount(struct container_config *c,
346 const char *name,
347 const char *source,
348 const char *destination,
349 const char *type,
350 const char *data,
Mike Frysinger05e594e2017-01-10 02:11:08 -0500351 const char *verity,
Dylan Reid837c74a2016-01-22 17:25:21 -0800352 int flags,
353 int uid,
354 int gid,
355 int mode,
356 int mount_in_ns,
Mike Frysinger412dbd22017-01-06 01:50:34 -0500357 int create,
358 int loopback)
Dylan Reid837c74a2016-01-22 17:25:21 -0800359{
360 struct container_mount *mount_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700361 struct container_mount *current_mount;
Dylan Reid837c74a2016-01-22 17:25:21 -0800362
363 if (name == NULL || source == NULL ||
364 destination == NULL || type == NULL)
365 return -EINVAL;
366
367 mount_ptr = realloc(c->mounts,
368 sizeof(c->mounts[0]) * (c->num_mounts + 1));
369 if (!mount_ptr)
370 return -ENOMEM;
371 c->mounts = mount_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700372 current_mount = &c->mounts[c->num_mounts];
373 memset(current_mount, 0, sizeof(struct container_mount));
374
375 if (strdup_and_free(&current_mount->name, name))
376 goto error_free_return;
377 if (strdup_and_free(&current_mount->source, source))
378 goto error_free_return;
379 if (strdup_and_free(&current_mount->destination, destination))
380 goto error_free_return;
381 if (strdup_and_free(&current_mount->type, type))
382 goto error_free_return;
383 if (data && strdup_and_free(&current_mount->data, data))
384 goto error_free_return;
Mike Frysinger05e594e2017-01-10 02:11:08 -0500385 if (verity && strdup_and_free(&current_mount->verity, verity))
386 goto error_free_return;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700387 current_mount->flags = flags;
388 current_mount->uid = uid;
389 current_mount->gid = gid;
390 current_mount->mode = mode;
391 current_mount->mount_in_ns = mount_in_ns;
392 current_mount->create = create;
Mike Frysinger412dbd22017-01-06 01:50:34 -0500393 current_mount->loopback = loopback;
Dylan Reid837c74a2016-01-22 17:25:21 -0800394 ++c->num_mounts;
395 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700396
397error_free_return:
398 container_config_free_mount(current_mount);
399 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800400}
401
Dylan Reid4843d6b2017-03-31 18:14:30 -0700402int container_config_add_cgroup_device(struct container_config *c,
403 int allow,
404 char type,
405 int major,
406 int minor,
407 int read,
408 int write,
409 int modify)
410{
411 struct container_cgroup_device *dev_ptr;
412 struct container_cgroup_device *current_dev;
413
414 dev_ptr = realloc(c->cgroup_devices,
415 sizeof(c->cgroup_devices[0]) *
416 (c->num_cgroup_devices + 1));
417 if (!dev_ptr)
418 return -ENOMEM;
419 c->cgroup_devices = dev_ptr;
420
421 current_dev = &c->cgroup_devices[c->num_cgroup_devices];
422 memset(current_dev, 0, sizeof(struct container_cgroup_device));
423 current_dev->allow = allow;
424 current_dev->type = type;
425 current_dev->major = major;
426 current_dev->minor = minor;
427 current_dev->read = read;
428 current_dev->write = write;
429 current_dev->modify = modify;
430 ++c->num_cgroup_devices;
431
432 return 0;
433}
434
Dylan Reid837c74a2016-01-22 17:25:21 -0800435int container_config_add_device(struct container_config *c,
436 char type,
437 const char *path,
438 int fs_permissions,
439 int major,
440 int minor,
Dylan Reid355d5e42016-04-29 16:53:31 -0700441 int copy_minor,
Dylan Reid837c74a2016-01-22 17:25:21 -0800442 int uid,
443 int gid,
444 int read_allowed,
445 int write_allowed,
446 int modify_allowed)
447{
448 struct container_device *dev_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700449 struct container_device *current_dev;
Dylan Reid837c74a2016-01-22 17:25:21 -0800450
451 if (path == NULL)
452 return -EINVAL;
Dylan Reid355d5e42016-04-29 16:53:31 -0700453 /* If using a dynamic minor number, ensure that minor is -1. */
454 if (copy_minor && (minor != -1))
455 return -EINVAL;
456
Dylan Reid837c74a2016-01-22 17:25:21 -0800457 dev_ptr = realloc(c->devices,
458 sizeof(c->devices[0]) * (c->num_devices + 1));
459 if (!dev_ptr)
460 return -ENOMEM;
461 c->devices = dev_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700462 current_dev = &c->devices[c->num_devices];
463 memset(current_dev, 0, sizeof(struct container_device));
464
465 current_dev->type = type;
466 if (strdup_and_free(&current_dev->path, path))
467 goto error_free_return;
468 current_dev->fs_permissions = fs_permissions;
469 current_dev->major = major;
470 current_dev->minor = minor;
471 current_dev->copy_minor = copy_minor;
472 current_dev->uid = uid;
473 current_dev->gid = gid;
Dylan Reid4843d6b2017-03-31 18:14:30 -0700474 if (read_allowed || write_allowed || modify_allowed) {
475 if (container_config_add_cgroup_device(c,
476 1,
477 type,
478 major,
479 minor,
480 read_allowed,
481 write_allowed,
482 modify_allowed))
483 goto error_free_return;
484 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800485 ++c->num_devices;
486 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700487
488error_free_return:
489 container_config_free_device(current_dev);
490 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800491}
492
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700493int container_config_run_setfiles(struct container_config *c,
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700494 const char *setfiles_cmd)
495{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700496 return strdup_and_free(&c->run_setfiles, setfiles_cmd);
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700497}
Dylan Reid837c74a2016-01-22 17:25:21 -0800498
Dylan Reid11456722016-05-02 11:24:50 -0700499const char *container_config_get_run_setfiles(const struct container_config *c)
500{
501 return c->run_setfiles;
502}
503
Chinyue Chenfac909e2016-06-24 14:17:42 +0800504int container_config_set_cpu_shares(struct container_config *c, int shares)
505{
506 /* CPU shares must be 2 or higher. */
507 if (shares < 2)
508 return -EINVAL;
509
510 c->cpu_cgparams.shares = shares;
511 return 0;
512}
513
514int container_config_set_cpu_cfs_params(struct container_config *c,
515 int quota,
516 int period)
517{
518 /*
519 * quota could be set higher than period to utilize more than one CPU.
520 * quota could also be set as -1 to indicate the cgroup does not adhere
521 * to any CPU time restrictions.
522 */
523 if (quota <= 0 && quota != -1)
524 return -EINVAL;
525 if (period <= 0)
526 return -EINVAL;
527
528 c->cpu_cgparams.quota = quota;
529 c->cpu_cgparams.period = period;
530 return 0;
531}
532
533int container_config_set_cpu_rt_params(struct container_config *c,
534 int rt_runtime,
535 int rt_period)
536{
537 /*
538 * rt_runtime could be set as 0 to prevent the cgroup from using
539 * realtime CPU.
540 */
541 if (rt_runtime < 0 || rt_runtime >= rt_period)
542 return -EINVAL;
543
544 c->cpu_cgparams.rt_runtime = rt_runtime;
545 c->cpu_cgparams.rt_period = rt_period;
546 return 0;
547}
548
Chinyue Chen4f3fd682016-07-01 14:11:42 +0800549int container_config_get_cpu_shares(struct container_config *c)
550{
551 return c->cpu_cgparams.shares;
552}
553
554int container_config_get_cpu_quota(struct container_config *c)
555{
556 return c->cpu_cgparams.quota;
557}
558
559int container_config_get_cpu_period(struct container_config *c)
560{
561 return c->cpu_cgparams.period;
562}
563
564int container_config_get_cpu_rt_runtime(struct container_config *c)
565{
566 return c->cpu_cgparams.rt_runtime;
567}
568
569int container_config_get_cpu_rt_period(struct container_config *c)
570{
571 return c->cpu_cgparams.rt_period;
572}
573
Dylan Reid9e724af2016-07-21 09:58:07 -0700574int container_config_set_cgroup_parent(struct container_config *c,
575 const char *parent,
Dmitry Torokhov14eef722016-09-27 16:40:37 -0700576 uid_t cgroup_owner, gid_t cgroup_group)
Dylan Reid9e724af2016-07-21 09:58:07 -0700577{
578 c->cgroup_owner = cgroup_owner;
Dmitry Torokhov14eef722016-09-27 16:40:37 -0700579 c->cgroup_group = cgroup_group;
Dylan Reid9e724af2016-07-21 09:58:07 -0700580 return strdup_and_free(&c->cgroup_parent, parent);
581}
582
583const char *container_config_get_cgroup_parent(struct container_config *c)
584{
585 return c->cgroup_parent;
586}
587
Keshav Santhanam1b6bf672016-08-10 18:35:12 -0700588void container_config_share_host_netns(struct container_config *c)
589{
590 c->share_host_netns = 1;
591}
592
593int get_container_config_share_host_netns(struct container_config *c)
594{
595 return c->share_host_netns;
596}
597
Dylan Reidc4335842016-11-11 10:24:52 -0800598void container_config_keep_fds_open(struct container_config *c)
599{
600 c->keep_fds_open = 1;
601}
602
Dylan Reid837c74a2016-01-22 17:25:21 -0800603/*
604 * Container manipulation
605 */
606struct container {
Dylan Reid837c74a2016-01-22 17:25:21 -0800607 struct container_cgroup *cgroup;
608 struct minijail *jail;
609 pid_t init_pid;
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500610 char *config_root;
Dylan Reid837c74a2016-01-22 17:25:21 -0800611 char *runfs;
612 char *rundir;
613 char *runfsroot;
614 char *pid_file_path;
Dylan Reide040c6b2016-05-02 18:49:02 -0700615 char **ext_mounts; /* Mounts made outside of the minijail */
616 size_t num_ext_mounts;
Mike Frysinger412dbd22017-01-06 01:50:34 -0500617 char **loopdevs;
618 size_t num_loopdevs;
Mike Frysinger05e594e2017-01-10 02:11:08 -0500619 char **device_mappers;
620 size_t num_device_mappers;
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700621 char *name;
Dylan Reid837c74a2016-01-22 17:25:21 -0800622};
623
624struct container *container_new(const char *name,
Dylan Reide040c6b2016-05-02 18:49:02 -0700625 const char *rundir)
Dylan Reid837c74a2016-01-22 17:25:21 -0800626{
627 struct container *c;
628
Dylan Reid837c74a2016-01-22 17:25:21 -0800629 c = calloc(1, sizeof(*c));
Dylan Reidb435c682016-04-12 04:17:49 -0700630 if (!c)
631 return NULL;
Dylan Reid837c74a2016-01-22 17:25:21 -0800632 c->rundir = strdup(rundir);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700633 c->name = strdup(name);
Dylan Reida9966422016-07-21 10:11:34 -0700634 if (!c->rundir || !c->name) {
Dylan Reid684975e2016-05-02 15:44:47 -0700635 container_destroy(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800636 return NULL;
Dylan Reidb435c682016-04-12 04:17:49 -0700637 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800638 return c;
639}
640
641void container_destroy(struct container *c)
642{
Dylan Reid684975e2016-05-02 15:44:47 -0700643 if (c->cgroup)
644 container_cgroup_destroy(c->cgroup);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700645 if (c->jail)
646 minijail_destroy(c->jail);
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500647 FREE_AND_NULL(c->config_root);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700648 FREE_AND_NULL(c->name);
649 FREE_AND_NULL(c->rundir);
650 FREE_AND_NULL(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800651}
652
Stephen Barber1a398c72017-01-23 12:39:44 -0800653/*
654 * Given a uid/gid map of "inside1 outside1 length1, ...", and an id
655 * inside of the user namespace, return the equivalent outside id, or
656 * return < 0 on error.
657 */
658static int get_userns_outside_id(const char *map, int id)
659{
660 char *map_copy, *mapping, *saveptr1, *saveptr2;
661 int inside, outside, length;
662 int result = 0;
663 errno = 0;
664
665 if (asprintf(&map_copy, "%s", map) < 0)
666 return -ENOMEM;
667
668 mapping = strtok_r(map_copy, ",", &saveptr1);
669 while (mapping) {
670 inside = strtol(strtok_r(mapping, " ", &saveptr2), NULL, 10);
671 outside = strtol(strtok_r(NULL, " ", &saveptr2), NULL, 10);
672 length = strtol(strtok_r(NULL, "\0", &saveptr2), NULL, 10);
673 if (errno) {
674 goto error_free_return;
675 } else if (inside < 0 || outside < 0 || length < 0) {
676 errno = EINVAL;
677 goto error_free_return;
678 }
679
680 if (id >= inside && id <= (inside + length)) {
681 result = (id - inside) + outside;
682 goto exit;
683 }
684
685 mapping = strtok_r(NULL, ",", &saveptr1);
686 }
687 errno = EINVAL;
688
689error_free_return:
690 result = -errno;
691exit:
692 free(map_copy);
693 return result;
694}
695
Dylan Reid837c74a2016-01-22 17:25:21 -0800696static int make_dir(const char *path, int uid, int gid, int mode)
697{
698 if (mkdir(path, mode))
699 return -errno;
700 if (chmod(path, mode))
701 return -errno;
702 if (chown(path, uid, gid))
703 return -errno;
704 return 0;
705}
706
707static int touch_file(const char *path, int uid, int gid, int mode)
708{
709 int rc;
710 int fd = open(path, O_RDWR | O_CREAT, mode);
711 if (fd < 0)
712 return -errno;
713 rc = fchown(fd, uid, gid);
714 close(fd);
715
716 if (rc)
717 return -errno;
718 return 0;
719}
720
721/* Make sure the mount target exists in the new rootfs. Create if needed and
722 * possible.
723 */
Stephen Barber1a398c72017-01-23 12:39:44 -0800724static int setup_mount_destination(const struct container_config *config,
725 const struct container_mount *mnt,
Dylan Reid2149be92016-04-28 18:38:57 -0700726 const char *source,
Dylan Reid837c74a2016-01-22 17:25:21 -0800727 const char *dest)
728{
Stephen Barber1a398c72017-01-23 12:39:44 -0800729 int uid_userns, gid_userns;
Dylan Reid837c74a2016-01-22 17:25:21 -0800730 int rc;
731 struct stat st_buf;
732
733 rc = stat(dest, &st_buf);
734 if (rc == 0) /* destination exists */
735 return 0;
736
737 /* Try to create the destination. Either make directory or touch a file
738 * depending on the source type.
739 */
Stephen Barber1a398c72017-01-23 12:39:44 -0800740 uid_userns = get_userns_outside_id(config->uid_map, mnt->uid);
741 if (uid_userns < 0)
742 return uid_userns;
743 gid_userns = get_userns_outside_id(config->gid_map, mnt->gid);
744 if (gid_userns < 0)
745 return gid_userns;
746
Dylan Reid2149be92016-04-28 18:38:57 -0700747 rc = stat(source, &st_buf);
Dylan Reid837c74a2016-01-22 17:25:21 -0800748 if (rc || S_ISDIR(st_buf.st_mode) || S_ISBLK(st_buf.st_mode))
Stephen Barber1a398c72017-01-23 12:39:44 -0800749 return make_dir(dest, uid_userns, gid_userns, mnt->mode);
Dylan Reid837c74a2016-01-22 17:25:21 -0800750
Stephen Barber1a398c72017-01-23 12:39:44 -0800751 return touch_file(dest, uid_userns, gid_userns, mnt->mode);
Dylan Reid837c74a2016-01-22 17:25:21 -0800752}
753
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700754/* Fork and exec the setfiles command to configure the selinux policy. */
Dylan Reide040c6b2016-05-02 18:49:02 -0700755static int run_setfiles_command(const struct container *c,
756 const struct container_config *config,
Yusuke Sato91f11f02016-12-02 16:15:13 -0800757 char *const *destinations, size_t num_destinations)
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700758{
759 int rc;
760 int status;
761 int pid;
762 char *context_path;
763
Dylan Reide040c6b2016-05-02 18:49:02 -0700764 if (!config->run_setfiles)
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700765 return 0;
766
767 if (asprintf(&context_path, "%s/file_contexts",
768 c->runfsroot) < 0)
769 return -errno;
770
771 pid = fork();
772 if (pid == 0) {
Yusuke Sato91f11f02016-12-02 16:15:13 -0800773 size_t i;
774 size_t arg_index = 0;
775 const char *argv[MAX_NUM_SETFILES_ARGS];
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700776 const char *env[] = {
777 NULL,
778 };
779
Yusuke Sato91f11f02016-12-02 16:15:13 -0800780 argv[arg_index++] = config->run_setfiles;
781 argv[arg_index++] = "-r";
782 argv[arg_index++] = c->runfsroot;
783 argv[arg_index++] = context_path;
784 if (arg_index + num_destinations >= MAX_NUM_SETFILES_ARGS)
785 _exit(-E2BIG);
786 for (i = 0; i < num_destinations; ++i) {
787 argv[arg_index++] = destinations[i];
788 }
789 argv[arg_index] = NULL;
790
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700791 execve(argv[0], (char *const*)argv, (char *const*)env);
792
793 /* Command failed to exec if execve returns. */
794 _exit(-errno);
795 }
796 free(context_path);
797 if (pid < 0)
798 return -errno;
799 do {
800 rc = waitpid(pid, &status, 0);
801 } while (rc == -1 && errno == EINTR);
802 if (rc < 0)
803 return -errno;
804 return status;
805}
806
Mike Frysinger412dbd22017-01-06 01:50:34 -0500807/* Find a free loop device and attach it. */
808static int loopdev_setup(char **loopdev_ret, const char *source)
809{
810 int ret = 0;
811 int source_fd = -1;
812 int control_fd = -1;
813 int loop_fd = -1;
814 char *loopdev = NULL;
815
816 source_fd = open(source, O_RDONLY|O_CLOEXEC);
817 if (source_fd < 0)
818 goto error;
819
820 control_fd = open(loopdev_ctl, O_RDWR|O_NOFOLLOW|O_CLOEXEC);
821 if (control_fd < 0)
822 goto error;
823
824 while (1) {
825 int num = ioctl(control_fd, LOOP_CTL_GET_FREE);
826 if (num < 0)
827 goto error;
828
829 if (asprintf(&loopdev, "/dev/loop%i", num) < 0)
830 goto error;
831
832 loop_fd = open(loopdev, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
833 if (loop_fd < 0)
834 goto error;
835
836 if (ioctl(loop_fd, LOOP_SET_FD, source_fd) == 0)
837 break;
838
839 if (errno != EBUSY)
840 goto error;
841
842 /* Clean up resources for the next pass. */
843 free(loopdev);
844 close(loop_fd);
845 }
846
847 *loopdev_ret = loopdev;
848 goto exit;
849
850error:
851 ret = -errno;
852 free(loopdev);
853exit:
854 if (source_fd != -1)
855 close(source_fd);
856 if (control_fd != -1)
857 close(control_fd);
858 if (loop_fd != -1)
859 close(loop_fd);
860 return ret;
861}
862
863/* Detach the specified loop device. */
864static int loopdev_detach(const char *loopdev)
865{
866 int ret = 0;
867 int fd;
868
869 fd = open(loopdev, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
870 if (fd < 0)
871 goto error;
872 if (ioctl(fd, LOOP_CLR_FD) < 0)
873 goto error;
874
875 goto exit;
876
877error:
878 ret = -errno;
879exit:
880 if (fd != -1)
881 close(fd);
882 return ret;
883}
884
Mike Frysinger05e594e2017-01-10 02:11:08 -0500885/* Create a new device mapper target for the source. */
886static int dm_setup(char **dm_path_ret, char **dm_name_ret, const char *source,
887 const char *verity_cmdline)
888{
889 int ret = 0;
890#if USE_device_mapper
891 char *p;
892 char *dm_path = NULL;
893 char *dm_name = NULL;
894 char *verity = NULL;
895 struct dm_task *dmt = NULL;
896 uint32_t cookie = 0;
897
898 /* Normalize the name into something unique-esque. */
899 if (asprintf(&dm_name, "cros-containers-%s", source) < 0)
900 goto error;
901 p = dm_name;
902 while ((p = strchr(p, '/')) != NULL)
903 *p++ = '_';
904
905 /* Get the /dev path for the higher levels to mount. */
906 if (asprintf(&dm_path, "%s%s", dm_dev_prefix, dm_name) < 0)
907 goto error;
908
909 /* Insert the source path in the verity command line. */
910 size_t source_len = strlen(source);
911 verity = malloc(strlen(verity_cmdline) + source_len * 2 + 1);
912 strcpy(verity, verity_cmdline);
913 while ((p = strstr(verity, "@DEV@")) != NULL) {
914 memmove(p + source_len, p + 5, strlen(p + 5) + 1);
915 memcpy(p, source, source_len);
916 }
917
918 /* Extract the first three parameters for dm-verity settings. */
919 char ttype[20];
920 unsigned long long start, size;
921 int n;
922 if (sscanf(verity, "%llu %llu %10s %n", &start, &size, ttype, &n) != 3)
923 goto error;
924
925 /* Finally create the device mapper. */
926 dmt = dm_task_create(DM_DEVICE_CREATE);
927 if (dmt == NULL)
928 goto error;
929
930 if (!dm_task_set_name(dmt, dm_name))
931 goto error;
932
933 if (!dm_task_set_ro(dmt))
934 goto error;
935
936 if (!dm_task_add_target(dmt, start, size, ttype, verity + n))
937 goto error;
938
939 if (!dm_task_set_cookie(dmt, &cookie, 0))
940 goto error;
941
942 if (!dm_task_run(dmt))
943 goto error;
944
945 /* Make sure the node exists before we continue. */
946 dm_udev_wait(cookie);
947
948 *dm_path_ret = dm_path;
949 *dm_name_ret = dm_name;
950 goto exit;
951
952error:
953 ret = -errno;
954 free(dm_name);
955 free(dm_path);
956exit:
957 free(verity);
958 if (dmt)
959 dm_task_destroy(dmt);
960#endif
961 return ret;
962}
963
964/* Tear down the device mapper target. */
965static int dm_detach(const char *dm_name)
966{
967 int ret = 0;
968#if USE_device_mapper
969 struct dm_task *dmt;
970
971 dmt = dm_task_create(DM_DEVICE_REMOVE);
972 if (dmt == NULL)
973 goto error;
974
975 if (!dm_task_set_name(dmt, dm_name))
976 goto error;
977
978 if (!dm_task_run(dmt))
979 goto error;
980
981 goto exit;
982
983error:
984 ret = -errno;
985exit:
986 dm_task_destroy(dmt);
987#endif
988 return ret;
989}
990
Dylan Reide040c6b2016-05-02 18:49:02 -0700991/*
992 * Unmounts anything we mounted in this mount namespace in the opposite order
993 * that they were mounted.
994 */
995static int unmount_external_mounts(struct container *c)
996{
997 int ret = 0;
998
999 while (c->num_ext_mounts) {
1000 c->num_ext_mounts--;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001001 if (!c->ext_mounts[c->num_ext_mounts])
1002 continue;
Dylan Reide040c6b2016-05-02 18:49:02 -07001003 if (umount(c->ext_mounts[c->num_ext_mounts]))
1004 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001005 FREE_AND_NULL(c->ext_mounts[c->num_ext_mounts]);
Dylan Reide040c6b2016-05-02 18:49:02 -07001006 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001007 FREE_AND_NULL(c->ext_mounts);
Mike Frysinger412dbd22017-01-06 01:50:34 -05001008
1009 while (c->num_loopdevs) {
1010 c->num_loopdevs--;
1011 if (loopdev_detach(c->loopdevs[c->num_loopdevs]))
1012 ret = -errno;
1013 FREE_AND_NULL(c->loopdevs[c->num_loopdevs]);
1014 }
1015 FREE_AND_NULL(c->loopdevs);
1016
Mike Frysinger05e594e2017-01-10 02:11:08 -05001017 while (c->num_device_mappers) {
1018 c->num_device_mappers--;
1019 if (dm_detach(c->device_mappers[c->num_device_mappers]))
1020 ret = -errno;
1021 FREE_AND_NULL(c->device_mappers[c->num_device_mappers]);
1022 }
1023 FREE_AND_NULL(c->device_mappers);
1024
Dylan Reide040c6b2016-05-02 18:49:02 -07001025 return ret;
1026}
1027
Junichi Uekawa5d272772016-07-21 16:07:19 +09001028/*
1029 * Match mount_one in minijail, mount one mountpoint with
1030 * consideration for combination of MS_BIND/MS_RDONLY flag.
1031 */
1032static int mount_external(const char *src, const char *dest, const char *type,
1033 unsigned long flags, const void *data)
1034{
1035 int remount_ro = 0;
1036
1037 /*
1038 * R/O bind mounts have to be remounted since 'bind' and 'ro'
1039 * can't both be specified in the original bind mount.
1040 * Remount R/O after the initial mount.
1041 */
1042 if ((flags & MS_BIND) && (flags & MS_RDONLY)) {
1043 remount_ro = 1;
1044 flags &= ~MS_RDONLY;
1045 }
1046
1047 if (mount(src, dest, type, flags, data) == -1)
1048 return -1;
1049
1050 if (remount_ro) {
1051 flags |= MS_RDONLY;
1052 if (mount(src, dest, NULL, flags | MS_REMOUNT, data) == -1)
1053 return -1;
1054 }
1055
1056 return 0;
1057}
1058
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001059static int do_container_mount(struct container *c,
Stephen Barber1a398c72017-01-23 12:39:44 -08001060 const struct container_config *config,
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001061 const struct container_mount *mnt)
1062{
Mike Frysinger05e594e2017-01-10 02:11:08 -05001063 char *dm_source = NULL;
Mike Frysinger412dbd22017-01-06 01:50:34 -05001064 char *loop_source = NULL;
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001065 char *source = NULL;
1066 char *dest = NULL;
1067 int rc = 0;
1068
1069 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
1070 return -errno;
1071
1072 /*
1073 * If it's a bind mount relative to rootfs, append source to
1074 * rootfs path, otherwise source path is absolute.
1075 */
1076 if ((mnt->flags & MS_BIND) && mnt->source[0] != '/') {
1077 if (asprintf(&source, "%s/%s", c->runfsroot, mnt->source) < 0)
1078 goto error_free_return;
Mike Frysingerb22acdf2017-01-08 02:02:35 -05001079 } else if (mnt->loopback && mnt->source[0] != '/' && c->config_root) {
1080 if (asprintf(&source, "%s/%s", c->config_root, mnt->source) < 0)
1081 goto error_free_return;
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001082 } else {
1083 if (asprintf(&source, "%s", mnt->source) < 0)
1084 goto error_free_return;
1085 }
1086
1087 if (mnt->create) {
Stephen Barber1a398c72017-01-23 12:39:44 -08001088 rc = setup_mount_destination(config, mnt, source, dest);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001089 if (rc)
1090 goto error_free_return;
1091 }
Mike Frysinger412dbd22017-01-06 01:50:34 -05001092 if (mnt->loopback) {
1093 /* Record this loopback file for cleanup later. */
1094 loop_source = source;
1095 source = NULL;
1096 rc = loopdev_setup(&source, loop_source);
1097 if (rc)
1098 goto error_free_return;
1099
Mike Frysinger05e594e2017-01-10 02:11:08 -05001100 /* Save this to cleanup when shutting down. */
Mike Frysinger412dbd22017-01-06 01:50:34 -05001101 rc = strdup_and_free(&c->loopdevs[c->num_loopdevs], source);
1102 if (rc)
1103 goto error_free_return;
1104 c->num_loopdevs++;
1105 }
Mike Frysinger05e594e2017-01-10 02:11:08 -05001106 if (mnt->verity) {
1107 /* Set this device up via dm-verity. */
1108 char *dm_name;
1109 dm_source = source;
1110 source = NULL;
1111 rc = dm_setup(&source, &dm_name, dm_source, mnt->verity);
1112 if (rc)
1113 goto error_free_return;
1114
1115 /* Save this to cleanup when shutting down. */
1116 rc = strdup_and_free(&c->device_mappers[c->num_device_mappers],
1117 dm_name);
1118 free(dm_name);
1119 if (rc)
1120 goto error_free_return;
1121 c->num_device_mappers++;
1122 }
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001123 if (mnt->mount_in_ns) {
1124 /* We can mount this with minijail. */
Dylan Reid36b9c012016-06-24 18:27:08 -07001125 rc = minijail_mount_with_data(c->jail, source, mnt->destination,
1126 mnt->type, mnt->flags, mnt->data);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001127 if (rc)
1128 goto error_free_return;
1129 } else {
1130 /* Mount this externally and unmount it on exit. */
Junichi Uekawa5d272772016-07-21 16:07:19 +09001131 if (mount_external(source, dest, mnt->type, mnt->flags,
1132 mnt->data))
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001133 goto error_free_return;
1134 /* Save this to unmount when shutting down. */
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001135 rc = strdup_and_free(&c->ext_mounts[c->num_ext_mounts], dest);
1136 if (rc)
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001137 goto error_free_return;
1138 c->num_ext_mounts++;
1139 }
1140
1141 goto exit;
1142
1143error_free_return:
1144 if (!rc)
1145 rc = -errno;
1146exit:
Mike Frysinger05e594e2017-01-10 02:11:08 -05001147 free(dm_source);
Mike Frysinger412dbd22017-01-06 01:50:34 -05001148 free(loop_source);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001149 free(source);
1150 free(dest);
1151 return rc;
1152}
1153
Dylan Reide040c6b2016-05-02 18:49:02 -07001154static int do_container_mounts(struct container *c,
1155 const struct container_config *config)
Dylan Reid7daf9982016-04-28 16:55:42 -07001156{
1157 unsigned int i;
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -07001158 int rc = 0;
Dylan Reid7daf9982016-04-28 16:55:42 -07001159
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001160 unmount_external_mounts(c);
Dylan Reide040c6b2016-05-02 18:49:02 -07001161 /*
1162 * Allocate space to track anything we mount in our mount namespace.
1163 * This over-allocates as it has space for all mounts.
1164 */
1165 c->ext_mounts = calloc(config->num_mounts, sizeof(*c->ext_mounts));
1166 if (!c->ext_mounts)
1167 return -errno;
Mike Frysinger412dbd22017-01-06 01:50:34 -05001168 c->loopdevs = calloc(config->num_mounts, sizeof(*c->loopdevs));
1169 if (!c->loopdevs)
1170 return -errno;
Mike Frysinger05e594e2017-01-10 02:11:08 -05001171 c->device_mappers = calloc(config->num_mounts, sizeof(*c->device_mappers));
1172 if (!c->device_mappers)
1173 return -errno;
Dylan Reide040c6b2016-05-02 18:49:02 -07001174
1175 for (i = 0; i < config->num_mounts; ++i) {
Stephen Barber1a398c72017-01-23 12:39:44 -08001176 rc = do_container_mount(c, config, &config->mounts[i]);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001177 if (rc)
1178 goto error_free_return;
Dylan Reid7daf9982016-04-28 16:55:42 -07001179 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001180
Dylan Reid7daf9982016-04-28 16:55:42 -07001181 return 0;
Dylan Reid2149be92016-04-28 18:38:57 -07001182
1183error_free_return:
Dylan Reide040c6b2016-05-02 18:49:02 -07001184 unmount_external_mounts(c);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -07001185 return rc;
Dylan Reid7daf9982016-04-28 16:55:42 -07001186}
1187
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001188static int container_create_device(const struct container *c,
Stephen Barber1a398c72017-01-23 12:39:44 -08001189 const struct container_config *config,
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001190 const struct container_device *dev,
1191 int minor)
1192{
1193 char *path = NULL;
1194 int rc = 0;
1195 int mode;
Stephen Barber1a398c72017-01-23 12:39:44 -08001196 int uid_userns, gid_userns;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001197
1198 switch (dev->type) {
1199 case 'b':
1200 mode = S_IFBLK;
1201 break;
1202 case 'c':
1203 mode = S_IFCHR;
1204 break;
1205 default:
1206 return -EINVAL;
1207 }
1208 mode |= dev->fs_permissions;
1209
Stephen Barber1a398c72017-01-23 12:39:44 -08001210 uid_userns = get_userns_outside_id(config->uid_map, dev->uid);
1211 if (uid_userns < 0)
1212 return uid_userns;
1213 gid_userns = get_userns_outside_id(config->gid_map, dev->gid);
1214 if (gid_userns < 0)
1215 return gid_userns;
1216
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001217 if (asprintf(&path, "%s%s", c->runfsroot, dev->path) < 0)
1218 goto error_free_return;
1219 if (mknod(path, mode, makedev(dev->major, minor)) && errno != EEXIST)
1220 goto error_free_return;
Stephen Barber1a398c72017-01-23 12:39:44 -08001221 if (chown(path, uid_userns, gid_userns))
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001222 goto error_free_return;
1223 if (chmod(path, dev->fs_permissions))
1224 goto error_free_return;
1225
1226 goto exit;
1227
1228error_free_return:
1229 rc = -errno;
1230exit:
1231 free(path);
1232 return rc;
1233}
1234
Stephen Barber1a398c72017-01-23 12:39:44 -08001235
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001236static int mount_runfs(struct container *c, const struct container_config *config)
Dylan Reid837c74a2016-01-22 17:25:21 -08001237{
Dylan Reidb3621832016-03-24 10:24:57 -07001238 static const mode_t root_dir_mode = 0660;
Dylan Reide040c6b2016-05-02 18:49:02 -07001239 const char *rootfs = config->rootfs;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001240 char *runfs_template = NULL;
Stephen Barber1a398c72017-01-23 12:39:44 -08001241 int uid_userns, gid_userns;
Dylan Reid837c74a2016-01-22 17:25:21 -08001242
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001243 if (asprintf(&runfs_template, "%s/%s_XXXXXX", c->rundir, c->name) < 0)
1244 return -ENOMEM;
1245
1246 c->runfs = mkdtemp(runfs_template);
1247 if (!c->runfs) {
1248 free(runfs_template);
1249 return -errno;
1250 }
1251
Stephen Barber1a398c72017-01-23 12:39:44 -08001252 uid_userns = get_userns_outside_id(config->uid_map, config->uid);
1253 if (uid_userns < 0)
1254 return uid_userns;
1255 gid_userns = get_userns_outside_id(config->gid_map, config->gid);
1256 if (gid_userns < 0)
1257 return gid_userns;
1258
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001259 /* Make sure the container uid can access the rootfs. */
1260 if (chmod(c->runfs, 0700))
1261 return -errno;
Stephen Barber1a398c72017-01-23 12:39:44 -08001262 if (chown(c->runfs, uid_userns, gid_userns))
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001263 return -errno;
1264
1265 if (asprintf(&c->runfsroot, "%s/root", c->runfs) < 0)
1266 return -errno;
1267
1268 if (mkdir(c->runfsroot, root_dir_mode))
1269 return -errno;
1270 if (chmod(c->runfsroot, root_dir_mode))
1271 return -errno;
1272
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -07001273 if (mount(rootfs, c->runfsroot, "", MS_BIND, NULL))
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001274 return -errno;
1275
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -07001276 /* MS_BIND ignores any flags passed to it (except MS_REC). We need a
1277 * second call to mount() to actually set them.
1278 */
1279 if (config->rootfs_mount_flags &&
1280 mount(rootfs, c->runfsroot, "",
1281 config->rootfs_mount_flags, NULL)) {
1282 return -errno;
1283 }
1284
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001285 return 0;
1286}
1287
Dylan Reidacedff92017-03-31 17:41:40 -07001288static int device_setup(struct container *c,
1289 const struct container_config *config)
1290{
1291 int rc, i;
1292
1293 c->cgroup->ops->deny_all_devices(c->cgroup);
1294
Dylan Reid4843d6b2017-03-31 18:14:30 -07001295 for (i = 0; i < config->num_cgroup_devices; i++) {
1296 const struct container_cgroup_device *dev =
1297 &config->cgroup_devices[i];
1298 rc = c->cgroup->ops->add_device(c->cgroup,
1299 dev->allow,
1300 dev->major,
1301 dev->minor,
1302 dev->read,
1303 dev->write,
1304 dev->modify,
1305 dev->type);
1306 if (rc)
1307 return rc;
1308 }
1309
Dylan Reidacedff92017-03-31 17:41:40 -07001310 for (i = 0; i < config->num_devices; i++) {
1311 const struct container_device *dev = &config->devices[i];
1312 int minor = dev->minor;
1313
1314 if (dev->copy_minor) {
1315 struct stat st_buff;
1316 if (stat(dev->path, &st_buff) < 0)
1317 continue;
1318 minor = minor(st_buff.st_rdev);
1319 }
1320 if (minor >= 0) {
1321 rc = container_create_device(c, config, dev, minor);
1322 if (rc)
1323 return rc;
1324 }
Dylan Reidacedff92017-03-31 17:41:40 -07001325 }
1326
1327 for (i = 0; i < c->num_loopdevs; ++i) {
1328 struct stat st;
1329
1330 if (stat(c->loopdevs[i], &st) < 0)
1331 return rc;
Dylan Reid4843d6b2017-03-31 18:14:30 -07001332 rc = c->cgroup->ops->add_device(c->cgroup, 1, major(st.st_rdev),
Dylan Reidacedff92017-03-31 17:41:40 -07001333 minor(st.st_rdev),
1334 1, 0, 0, 'b');
1335 if (rc)
1336 return rc;
1337 }
1338
1339 return 0;
1340}
1341
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001342int container_start(struct container *c, const struct container_config *config)
1343{
1344 int rc = 0;
1345 unsigned int i;
Stephen Barber1a398c72017-01-23 12:39:44 -08001346 int cgroup_uid, cgroup_gid;
Yusuke Sato91f11f02016-12-02 16:15:13 -08001347 char **destinations;
1348 size_t num_destinations;
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001349
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001350 if (!c)
1351 return -EINVAL;
Dylan Reide040c6b2016-05-02 18:49:02 -07001352 if (!config)
1353 return -EINVAL;
1354 if (!config->program_argv || !config->program_argv[0])
1355 return -EINVAL;
1356
Mike Frysingerb22acdf2017-01-08 02:02:35 -05001357 if (config->config_root) {
1358 c->config_root = strdup(config->config_root);
1359 if (!c->config_root) {
1360 rc = -ENOMEM;
1361 goto error_rmdir;
1362 }
1363 }
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001364 if (config->premounted_runfs) {
1365 c->runfs = NULL;
1366 c->runfsroot = strdup(config->premounted_runfs);
1367 if (!c->runfsroot) {
1368 rc = -ENOMEM;
1369 goto error_rmdir;
1370 }
1371 } else {
1372 rc = mount_runfs(c, config);
1373 if (rc)
1374 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001375 }
Dylan Reid837c74a2016-01-22 17:25:21 -08001376
1377 c->jail = minijail_new();
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001378 if (!c->jail)
Luis Hector Chavez945af482016-06-03 08:39:34 -07001379 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001380
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -07001381 rc = do_container_mounts(c, config);
1382 if (rc)
Dylan Reid7daf9982016-04-28 16:55:42 -07001383 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001384
Stephen Barber1a398c72017-01-23 12:39:44 -08001385 cgroup_uid = get_userns_outside_id(config->uid_map,
1386 config->cgroup_owner);
1387 if (cgroup_uid < 0) {
1388 rc = cgroup_uid;
1389 goto error_rmdir;
1390 }
1391 cgroup_gid = get_userns_outside_id(config->gid_map,
1392 config->cgroup_group);
1393 if (cgroup_gid < 0) {
1394 rc = cgroup_gid;
1395 goto error_rmdir;
1396 }
1397
Dylan Reida9966422016-07-21 10:11:34 -07001398 c->cgroup = container_cgroup_new(c->name,
1399 "/sys/fs/cgroup",
1400 config->cgroup_parent,
Stephen Barber1a398c72017-01-23 12:39:44 -08001401 cgroup_uid,
1402 cgroup_gid);
Dylan Reida9966422016-07-21 10:11:34 -07001403 if (!c->cgroup)
1404 goto error_rmdir;
1405
Keshav Santhanam268fa032016-07-14 09:59:24 -07001406 /* Must be root to modify device cgroup or mknod */
1407 if (getuid() == 0) {
Dylan Reidacedff92017-03-31 17:41:40 -07001408 if (device_setup(c, config))
1409 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001410 }
1411
Dylan Reidd7229582016-04-27 17:08:40 -07001412 /* Potentailly run setfiles on mounts configured outside of the jail */
Yusuke Sato91f11f02016-12-02 16:15:13 -08001413 destinations = calloc(config->num_mounts, sizeof(char *));
1414 num_destinations = 0;
Dylan Reide040c6b2016-05-02 18:49:02 -07001415 for (i = 0; i < config->num_mounts; i++) {
1416 const struct container_mount *mnt = &config->mounts[i];
Yusuke Sato91f11f02016-12-02 16:15:13 -08001417 char* dest = mnt->destination;
Dylan Reidd7229582016-04-27 17:08:40 -07001418
1419 if (mnt->mount_in_ns)
1420 continue;
Junichi Uekawa5d272772016-07-21 16:07:19 +09001421 if (mnt->flags & MS_RDONLY)
1422 continue;
Yusuke Sato91f11f02016-12-02 16:15:13 -08001423
Yusuke Satod33db432016-12-05 16:24:37 -08001424 /* A hack to avoid setfiles on /data and /cache. */
1425 if (!strcmp(dest, "/data") || !strcmp(dest, "/cache"))
Yusuke Sato91f11f02016-12-02 16:15:13 -08001426 continue;
1427
1428 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0) {
1429 size_t j;
1430 for (j = 0; j < num_destinations; ++j) {
1431 free(destinations[j]);
1432 }
1433 free(destinations);
Dylan Reidd7229582016-04-27 17:08:40 -07001434 goto error_rmdir;
Yusuke Sato91f11f02016-12-02 16:15:13 -08001435 }
1436
1437 destinations[num_destinations++] = dest;
Dylan Reidd7229582016-04-27 17:08:40 -07001438 }
Yusuke Sato91f11f02016-12-02 16:15:13 -08001439 if (num_destinations) {
1440 size_t i;
1441 rc = run_setfiles_command(c, config, destinations, num_destinations);
1442 for (i = 0; i < num_destinations; ++i) {
1443 free(destinations[i]);
1444 }
1445 }
1446 free(destinations);
1447 if (rc)
1448 goto error_rmdir;
Dylan Reidd7229582016-04-27 17:08:40 -07001449
Chinyue Chenfac909e2016-06-24 14:17:42 +08001450 /* Setup CPU cgroup params. */
1451 if (config->cpu_cgparams.shares) {
1452 rc = c->cgroup->ops->set_cpu_shares(
1453 c->cgroup, config->cpu_cgparams.shares);
1454 if (rc)
1455 goto error_rmdir;
1456 }
1457 if (config->cpu_cgparams.period) {
1458 rc = c->cgroup->ops->set_cpu_quota(
1459 c->cgroup, config->cpu_cgparams.quota);
1460 if (rc)
1461 goto error_rmdir;
1462 rc = c->cgroup->ops->set_cpu_period(
1463 c->cgroup, config->cpu_cgparams.period);
1464 if (rc)
1465 goto error_rmdir;
1466 }
1467 if (config->cpu_cgparams.rt_period) {
1468 rc = c->cgroup->ops->set_cpu_rt_runtime(
1469 c->cgroup, config->cpu_cgparams.rt_runtime);
1470 if (rc)
1471 goto error_rmdir;
1472 rc = c->cgroup->ops->set_cpu_rt_period(
1473 c->cgroup, config->cpu_cgparams.rt_period);
1474 if (rc)
1475 goto error_rmdir;
1476 }
1477
Dylan Reid837c74a2016-01-22 17:25:21 -08001478 /* Setup and start the container with libminijail. */
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001479 if (config->pid_file_path) {
1480 c->pid_file_path = strdup(config->pid_file_path);
1481 if (!c->pid_file_path) {
1482 rc = -ENOMEM;
1483 goto error_rmdir;
1484 }
1485 } else if (c->runfs) {
1486 if (asprintf(&c->pid_file_path, "%s/container.pid", c->runfs) < 0) {
1487 rc = -ENOMEM;
1488 goto error_rmdir;
1489 }
1490 }
1491
1492 if (c->pid_file_path)
1493 minijail_write_pid_file(c->jail, c->pid_file_path);
Dylan Reid837c74a2016-01-22 17:25:21 -08001494 minijail_reset_signal_mask(c->jail);
1495
1496 /* Setup container namespaces. */
1497 minijail_namespace_ipc(c->jail);
1498 minijail_namespace_vfs(c->jail);
Keshav Santhanam1b6bf672016-08-10 18:35:12 -07001499 if (!config->share_host_netns)
1500 minijail_namespace_net(c->jail);
Dylan Reid837c74a2016-01-22 17:25:21 -08001501 minijail_namespace_pids(c->jail);
Dylan Reid837c74a2016-01-22 17:25:21 -08001502 minijail_namespace_user(c->jail);
Mike Frysingerfbd60552017-01-03 17:28:48 -05001503 if (getuid() != 0)
1504 minijail_namespace_user_disable_setgroups(c->jail);
Dylan Reidc6ca1042016-07-11 15:03:27 -07001505 minijail_namespace_cgroups(c->jail);
Dylan Reide040c6b2016-05-02 18:49:02 -07001506 rc = minijail_uidmap(c->jail, config->uid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -08001507 if (rc)
1508 goto error_rmdir;
Dylan Reide040c6b2016-05-02 18:49:02 -07001509 rc = minijail_gidmap(c->jail, config->gid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -08001510 if (rc)
1511 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001512
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001513 /* Set the UID/GID inside the container if not 0. */
Stephen Barber1a398c72017-01-23 12:39:44 -08001514 if (get_userns_outside_id(config->uid_map, config->uid) < 0)
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001515 goto error_rmdir;
Stephen Barber1a398c72017-01-23 12:39:44 -08001516 else if (config->uid > 0)
1517 minijail_change_uid(c->jail, config->uid);
1518 if (get_userns_outside_id(config->gid_map, config->gid) < 0)
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001519 goto error_rmdir;
Stephen Barber1a398c72017-01-23 12:39:44 -08001520 else if (config->gid > 0)
1521 minijail_change_gid(c->jail, config->gid);
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001522
Dylan Reid837c74a2016-01-22 17:25:21 -08001523 rc = minijail_enter_pivot_root(c->jail, c->runfsroot);
1524 if (rc)
1525 goto error_rmdir;
1526
1527 /* Add the cgroups configured above. */
Dmitry Torokhov0d253a62017-01-05 09:41:33 -08001528 for (i = 0; i < NUM_CGROUP_TYPES; i++) {
1529 if (c->cgroup->cgroup_tasks_paths[i]) {
1530 rc = minijail_add_to_cgroup(c->jail,
1531 c->cgroup->cgroup_tasks_paths[i]);
1532 if (rc)
1533 goto error_rmdir;
1534 }
1535 }
Dylan Reid837c74a2016-01-22 17:25:21 -08001536
Dylan Reide040c6b2016-05-02 18:49:02 -07001537 if (config->alt_syscall_table)
1538 minijail_use_alt_syscall(c->jail, config->alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -08001539
1540 minijail_run_as_init(c->jail);
1541
Dylan Reid3da683b2016-04-05 03:35:35 -07001542 /* TODO(dgreid) - remove this once shared mounts are cleaned up. */
1543 minijail_skip_remount_private(c->jail);
1544
Dylan Reidc4335842016-11-11 10:24:52 -08001545 if (!config->keep_fds_open)
1546 minijail_close_open_fds(c->jail);
Luis Hector Chaveze18e7d42016-10-12 07:35:32 -07001547
Dylan Reid837c74a2016-01-22 17:25:21 -08001548 rc = minijail_run_pid_pipes_no_preload(c->jail,
Dylan Reide040c6b2016-05-02 18:49:02 -07001549 config->program_argv[0],
1550 config->program_argv,
Dylan Reid837c74a2016-01-22 17:25:21 -08001551 &c->init_pid, NULL, NULL,
1552 NULL);
1553 if (rc)
1554 goto error_rmdir;
1555 return 0;
1556
1557error_rmdir:
Luis Hector Chavez945af482016-06-03 08:39:34 -07001558 if (!rc)
1559 rc = -errno;
1560 container_teardown(c);
Dylan Reid837c74a2016-01-22 17:25:21 -08001561 return rc;
1562}
1563
1564const char *container_root(struct container *c)
1565{
1566 return c->runfs;
1567}
1568
1569int container_pid(struct container *c)
1570{
1571 return c->init_pid;
1572}
1573
1574static int container_teardown(struct container *c)
1575{
Dylan Reid837c74a2016-01-22 17:25:21 -08001576 int ret = 0;
1577
Dylan Reide040c6b2016-05-02 18:49:02 -07001578 unmount_external_mounts(c);
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001579 if (c->runfsroot && c->runfs) {
Luis Hector Chavez945af482016-06-03 08:39:34 -07001580 if (umount(c->runfsroot))
1581 ret = -errno;
1582 if (rmdir(c->runfsroot))
1583 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001584 FREE_AND_NULL(c->runfsroot);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001585 }
1586 if (c->pid_file_path) {
1587 if (unlink(c->pid_file_path))
1588 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001589 FREE_AND_NULL(c->pid_file_path);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001590 }
1591 if (c->runfs) {
1592 if (rmdir(c->runfs))
1593 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001594 FREE_AND_NULL(c->runfs);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001595 }
Dylan Reid837c74a2016-01-22 17:25:21 -08001596 return ret;
1597}
1598
1599int container_wait(struct container *c)
1600{
Dylan Reidcf745c52016-04-22 10:18:03 -07001601 int rc;
1602
1603 do {
1604 rc = minijail_wait(c->jail);
Luis Hector Chavez4641e852016-06-02 15:40:19 -07001605 } while (rc == -EINTR);
Dylan Reidcf745c52016-04-22 10:18:03 -07001606
Luis Hector Chavez945af482016-06-03 08:39:34 -07001607 // If the process had already been reaped, still perform teardown.
1608 if (rc == -ECHILD || rc >= 0) {
Dylan Reidcf745c52016-04-22 10:18:03 -07001609 rc = container_teardown(c);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001610 }
Dylan Reidcf745c52016-04-22 10:18:03 -07001611 return rc;
Dylan Reid837c74a2016-01-22 17:25:21 -08001612}
1613
1614int container_kill(struct container *c)
1615{
Luis Hector Chavez945af482016-06-03 08:39:34 -07001616 if (kill(c->init_pid, SIGKILL) && errno != ESRCH)
Dylan Reid837c74a2016-01-22 17:25:21 -08001617 return -errno;
1618 return container_wait(c);
1619}