namespace.c revision 7f112f50fea585411ea2d493b3582bea77eb4d6e
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <limits.h>
#include "strv.h"
#include "util.h"
#include "path-util.h"
#include "namespace.h"
#include "missing.h"
#include "execute.h"
#include "loopback-setup.h"
#include "mkdir.h"
#include "dev-setup.h"
#include "def.h"
typedef enum MountMode {
/* This is ordered by priority! */
} MountMode;
typedef struct BindMount {
const char *path;
bool done;
bool ignore;
} BindMount;
char **i;
assert(p);
STRV_FOREACH(i, strv) {
(*p)->ignore = false;
(*p)->ignore = true;
(*i)++;
}
if (!path_is_absolute(*i))
return -EINVAL;
(*p)->path = *i;
(*p)++;
}
return 0;
}
static int mount_path_compare(const void *a, const void *b) {
const BindMount *p = a, *q = b;
/* If the paths are equal, check the mode */
return -1;
return 1;
return 0;
}
/* If the paths are not equal, then order prefixes first */
return 1;
return -1;
return 0;
}
static void drop_duplicates(BindMount *m, unsigned *n) {
assert(m);
assert(n);
/* The first one wins */
continue;
previous = t;
t++;
}
*n = t - m;
}
static const char devnodes[] =
const char *d;
unsigned n = 0;
int r;
assert(m);
u = umask(0000);
/* First: record device mode_t and dev_t */
NULSTR_FOREACH(d, devnodes) {
r = stat(d, &devnodes_stat[n]);
if (r < 0) {
return -errno;
} else {
return -EINVAL;
}
n++;
}
if (r < 0)
r = mount("devpts", "/dev/pts", "devpts", MS_NOSUID|MS_NOEXEC, "newinstance,ptmxmode=0666,mode=620,gid=" STRINGIFY(TTY_GID));
if (r < 0)
if (r < 0)
/* Second: actually create it */
n = 0;
NULSTR_FOREACH(d, devnodes) {
if (devnodes_stat[n].st_rdev == 0)
continue;
if (r < 0)
n++;
}
return 0;
}
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 PRIVATE_DEV:
return mount_dev(m);
case INACCESSIBLE:
what = "/run/systemd/inaccessible";
break;
case READONLY:
case READWRITE:
break;
case PRIVATE_TMP:
break;
case PRIVATE_VAR_TMP:
what = var_tmp_dir;
break;
default:
assert_not_reached("Unknown mode");
}
if (r >= 0)
r = 0;
return r;
}
static int make_read_only(BindMount *m) {
int r;
assert(m);
return 0;
return -errno;
return 0;
}
int setup_namespace(
char** read_write_dirs,
char** read_only_dirs,
char** inaccessible_dirs,
char* tmp_dir,
char* var_tmp_dir,
bool private_dev,
unsigned mount_flags) {
unsigned n;
int r = 0;
if (mount_flags == 0)
if (unshare(CLONE_NEWNS) < 0)
return -errno;
n = !!tmp_dir + !!var_tmp_dir +
if (n > 0) {
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (tmp_dir) {
m->path = "/tmp";
m->mode = PRIVATE_TMP;
m++;
}
if (var_tmp_dir) {
m->mode = PRIVATE_VAR_TMP;
m++;
}
if (private_dev) {
m->path = "/dev";
m->mode = PRIVATE_DEV;
m++;
}
drop_duplicates(mounts, &n);
}
/* Remount / as SLAVE so that nothing now mounted in the namespace
shows up in the parent */
return -errno;
if (r < 0)
goto fail;
}
r = make_read_only(m);
if (r < 0)
goto fail;
}
/* Remount / as the desired mode */
r = -errno;
goto fail;
}
return 0;
fail:
if (m->done)
return r;
}
_cleanup_free_ char *x = NULL;
char bid[SD_ID128_STRING_MAX];
int r;
/* We include the boot id in the directory so that after a
* reboot we can easily identify obsolete directories. */
r = sd_id128_get_boot(&boot_id);
if (r < 0)
return r;
x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX", NULL);
if (!x)
return -ENOMEM;
RUN_WITH_UMASK(0077)
if (!mkdtemp(x))
return -errno;
RUN_WITH_UMASK(0000) {
char *y;
y = strappenda(x, "/tmp");
return -errno;
}
*path = x;
x = NULL;
return 0;
}
char *a, *b;
int r;
if (r < 0)
return r;
if (r < 0) {
char *t;
t = strappenda(a, "/tmp");
rmdir(t);
rmdir(a);
free(a);
return r;
}
*tmp_dir = a;
*var_tmp_dir = b;
return 0;
}
union {
} control = {};
.msg_control = &control,
.msg_controllen = sizeof(control),
};
int r;
assert(netns_storage_socket[0] >= 0);
/* We use the passed socketpair as a storage buffer for our
* namespace reference fd. Whatever process runs this first
* shall create a new namespace, all others should just join
* it. To serialize that we use a file lock on the socket
* pair.
*
* It's a bit crazy, but hey, works great! */
return -errno;
r = -errno;
goto fail;
}
/* Nothing stored yet, so let's create a new namespace */
if (unshare(CLONE_NEWNET) < 0) {
r = -errno;
goto fail;
}
if (netns < 0) {
r = -errno;
goto fail;
}
r = 1;
} else {
/* Yay, found something, so let's join the namespace */
}
}
r = -errno;
goto fail;
}
r = 0;
}
r = -errno;
goto fail;
}
fail:
return r;
}