namespace.c revision d8c9d3a468e61ee2a2b2c3454e662398b0885411
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering/***
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering This file is part of systemd.
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering Copyright 2010 Lennart Poettering
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering systemd is free software; you can redistribute it and/or modify it
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering under the terms of the GNU Lesser General Public License as published by
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering (at your option) any later version.
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering systemd is distributed in the hope that it will be useful, but
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering Lesser General Public License for more details.
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering You should have received a copy of the GNU Lesser General Public License
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering***/
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <errno.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <sys/mount.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <string.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <stdio.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <unistd.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <sys/stat.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <sys/types.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <sched.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <sys/syscall.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <limits.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include <linux/fs.h>
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering
618234a5258768359cb1086b152c5f08aaf89754Lennart Poettering#include "strv.h"
#include "util.h"
#include "path-util.h"
#include "namespace.h"
#include "missing.h"
#include "execute.h"
typedef enum MountMode {
/* This is ordered by priority! */
INACCESSIBLE,
READONLY,
PRIVATE_TMP,
PRIVATE_VAR_TMP,
READWRITE
} MountMode;
typedef struct BindMount {
const char *path;
MountMode mode;
bool done;
bool ignore;
} BindMount;
static int append_mounts(BindMount **p, char **strv, MountMode mode) {
char **i;
STRV_FOREACH(i, strv) {
(*p)->ignore = false;
if ((mode == INACCESSIBLE || mode == READONLY) && (*i)[0] == '-') {
(*p)->ignore = true;
(*i)++;
}
if (!path_is_absolute(*i))
return -EINVAL;
(*p)->path = *i;
(*p)->mode = mode;
(*p)++;
}
return 0;
}
static int mount_path_compare(const void *a, const void *b) {
const BindMount *p = a, *q = b;
if (path_equal(p->path, q->path)) {
/* If the paths are equal, check the mode */
if (p->mode < q->mode)
return -1;
if (p->mode > q->mode)
return 1;
return 0;
}
/* If the paths are not equal, then order prefixes first */
if (path_startswith(p->path, q->path))
return 1;
if (path_startswith(q->path, p->path))
return -1;
return 0;
}
static void drop_duplicates(BindMount *m, unsigned *n) {
BindMount *f, *t, *previous;
assert(m);
assert(n);
for (f = m, t = m, previous = NULL; f < m+*n; f++) {
/* The first one wins */
if (previous && path_equal(f->path, previous->path))
continue;
t->path = f->path;
t->mode = f->mode;
previous = t;
t++;
}
*n = t - m;
}
static int apply_mount(
BindMount *m,
const char *tmp_dir,
const char *var_tmp_dir) {
const char *what;
int r;
assert(m);
switch (m->mode) {
case INACCESSIBLE:
what = "/run/systemd/inaccessible";
break;
case READONLY:
case READWRITE:
what = m->path;
break;
case PRIVATE_TMP:
what = tmp_dir;
break;
case PRIVATE_VAR_TMP:
what = var_tmp_dir;
break;
default:
assert_not_reached("Unknown mode");
}
assert(what);
r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
if (r >= 0)
log_debug("Successfully mounted %s to %s", what, m->path);
else if (m->ignore && errno == ENOENT)
r = 0;
return r;
}
static int make_read_only(BindMount *m) {
int r;
assert(m);
if (m->mode != INACCESSIBLE && m->mode != READONLY)
return 0;
r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
if (r < 0 && !(m->ignore && errno == ENOENT))
return -errno;
return 0;
}
int setup_tmpdirs(const char *unit_id,
char **tmp_dir,
char **var_tmp_dir) {
int r = 0;
_cleanup_free_ char *tmp = NULL, *var = NULL;
assert(tmp_dir);
assert(var_tmp_dir);
tmp = strjoin("/tmp/systemd-", unit_id, "-XXXXXXX", NULL);
var = strjoin("/var/tmp/systemd-", unit_id, "-XXXXXXX", NULL);
r = create_tmp_dir(tmp, tmp_dir);
if (r < 0)
return r;
r = create_tmp_dir(var, var_tmp_dir);
if (r == 0)
return 0;
/* failure */
rmdir(*tmp_dir);
rmdir(tmp);
free(*tmp_dir);
*tmp_dir = NULL;
return r;
}
int setup_namespace(char** read_write_dirs,
char** read_only_dirs,
char** inaccessible_dirs,
char* tmp_dir,
char* var_tmp_dir,
bool private_tmp,
unsigned mount_flags) {
unsigned n = strv_length(read_write_dirs) +
strv_length(read_only_dirs) +
strv_length(inaccessible_dirs) +
(private_tmp ? 2 : 0);
BindMount *m, *mounts = NULL;
int r = 0;
if (!mount_flags)
mount_flags = MS_SHARED;
if (unshare(CLONE_NEWNS) < 0)
return -errno;
if (n) {
m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
(r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
(r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
return r;
if (private_tmp) {
m->path = "/tmp";
m->mode = PRIVATE_TMP;
m++;
m->path = "/var/tmp";
m->mode = PRIVATE_VAR_TMP;
m++;
}
assert(mounts + n == m);
qsort(mounts, n, sizeof(BindMount), mount_path_compare);
drop_duplicates(mounts, &n);
}
/* Remount / as SLAVE so that nothing now mounted in the namespace
shows up in the parent */
if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
return -errno;
for (m = mounts; m < mounts + n; ++m) {
r = apply_mount(m, tmp_dir, var_tmp_dir);
if (r < 0)
goto undo_mounts;
}
for (m = mounts; m < mounts + n; ++m) {
r = make_read_only(m);
if (r < 0)
goto undo_mounts;
}
/* Remount / as the desired mode */
if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
r = -errno;
goto undo_mounts;
}
return 0;
undo_mounts:
for (m = mounts; m < mounts + n; ++m) {
if (m->done)
umount2(m->path, MNT_DETACH);
}
return r;
}