mirror of
https://github.com/systemd/systemd.git
synced 2024-11-07 01:27:11 +03:00
278 lines
6.9 KiB
C
278 lines
6.9 KiB
C
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
|
|
|
/***
|
|
This file is part of systemd.
|
|
|
|
Copyright 2010 Lennart Poettering
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
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 <sys/mount.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sched.h>
|
|
#include <sys/syscall.h>
|
|
#include <limits.h>
|
|
#include <linux/fs.h>
|
|
|
|
#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;
|
|
} BindMount;
|
|
|
|
static int append_mounts(BindMount **p, char **strv, MountMode mode) {
|
|
char **i;
|
|
|
|
STRV_FOREACH(i, strv) {
|
|
|
|
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);
|
|
|
|
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)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int setup_tmpdirs(char **tmp_dir,
|
|
char **var_tmp_dir) {
|
|
int r = 0;
|
|
char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX",
|
|
var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX";
|
|
|
|
assert(tmp_dir);
|
|
assert(var_tmp_dir);
|
|
|
|
r = create_tmp_dir(tmp_dir_template, tmp_dir);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir);
|
|
if (r == 0)
|
|
return 0;
|
|
|
|
/* failure */
|
|
rmdir(*tmp_dir);
|
|
rmdir(tmp_dir_template);
|
|
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;
|
|
int r = 0;
|
|
|
|
if (!mount_flags)
|
|
mount_flags = MS_SHARED;
|
|
|
|
if (unshare(CLONE_NEWNS) < 0)
|
|
return -errno;
|
|
|
|
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;
|
|
}
|