summaryrefslogtreecommitdiff
path: root/src/core/namespace.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-11-23 22:21:40 +0100
committerLennart Poettering <lennart@poettering.net>2016-12-14 00:54:10 +0100
commitd2d6c096f6373a76f3b303a7a116e7cfe7139c4d (patch)
tree090a728bbf4f98d5758806f6c21f958a8d9e982c /src/core/namespace.c
parent8fceda937f3a177d9e27b403fb5e1b34138b05f5 (diff)
core: add ability to define arbitrary bind mounts for services
This adds two new settings BindPaths= and BindReadOnlyPaths=. They allow defining arbitrary bind mounts specific to particular services. This is particularly useful for services with RootDirectory= set as this permits making specific bits of the host directory available to chrooted services. The two new settings follow the concepts nspawn already possess in --bind= and --bind-ro=, as well as the .nspawn settings Bind= and BindReadOnly= (and these latter options should probably be renamed to BindPaths= and BindReadOnlyPaths= too). Fixes: #3439
Diffstat (limited to 'src/core/namespace.c')
-rw-r--r--src/core/namespace.c127
1 files changed, 116 insertions, 11 deletions
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 746ebc45b0..834883267c 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -50,6 +50,8 @@
typedef enum MountMode {
/* This is ordered by priority! */
INACCESSIBLE,
+ BIND_MOUNT,
+ BIND_MOUNT_RECURSIVE,
READONLY,
PRIVATE_TMP,
PRIVATE_VAR_TMP,
@@ -64,6 +66,8 @@ typedef struct MountEntry {
bool has_prefix:1; /* Already is prefixed by the root dir? */
bool read_only:1; /* Shall this mount point be read-only? */
char *path_malloc; /* Use this instead of 'path' if we had to allocate memory */
+ const char *source_const; /* The source path, for bind mounts */
+ char *source_malloc;
} MountEntry;
/*
@@ -166,6 +170,12 @@ static bool mount_entry_read_only(const MountEntry *p) {
return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE);
}
+static const char *mount_entry_source(const MountEntry *p) {
+ assert(p);
+
+ return p->source_malloc ?: p->source_const;
+}
+
static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) {
char **i;
@@ -201,6 +211,25 @@ static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) {
return 0;
}
+static int append_bind_mounts(MountEntry **p, const BindMount *binds, unsigned n) {
+ unsigned i;
+
+ assert(p);
+
+ for (i = 0; i < n; i++) {
+ const BindMount *b = binds + i;
+
+ *((*p)++) = (MountEntry) {
+ .path_const = b->destination,
+ .mode = b->recursive ? BIND_MOUNT_RECURSIVE : BIND_MOUNT,
+ .read_only = b->read_only,
+ .source_const = b->source,
+ };
+ }
+
+ return 0;
+}
+
static int append_static_mounts(MountEntry **p, const MountEntry *mounts, unsigned n, bool ignore_protect) {
unsigned i;
@@ -568,27 +597,33 @@ fail:
return r;
}
-static int mount_entry_chase(MountEntry *m, const char *root_directory) {
+static int mount_entry_chase(
+ const char *root_directory,
+ MountEntry *m,
+ const char *path,
+ char **location) {
+
char *chased;
int r;
assert(m);
/* Since mount() will always follow symlinks and we need to take the different root directory into account we
- * chase the symlinks on our own first. */
+ * chase the symlinks on our own first. This is called for the destination path, as well as the source path (if
+ * that applies). The result is stored in "location". */
- r = chase_symlinks(mount_entry_path(m), root_directory, 0, &chased);
+ r = chase_symlinks(path, root_directory, 0, &chased);
if (r == -ENOENT && m->ignore) {
- log_debug_errno(r, "Path %s does not exist, ignoring.", mount_entry_path(m));
+ log_debug_errno(r, "Path %s does not exist, ignoring.", path);
return 0;
}
if (r < 0)
- return log_debug_errno(r, "Failed to follow symlinks on %s: %m", mount_entry_path(m));
+ return log_debug_errno(r, "Failed to follow symlinks on %s: %m", path);
- log_debug("Followed symlinks %s → %s.", mount_entry_path(m), chased);
+ log_debug("Followed symlinks %s → %s.", path, chased);
- free(m->path_malloc);
- m->path_malloc = chased;
+ free(*location);
+ *location = chased;
return 1;
}
@@ -600,11 +635,12 @@ static int apply_mount(
const char *var_tmp_dir) {
const char *what;
+ bool rbind = true;
int r;
assert(m);
- r = mount_entry_chase(m, root_directory);
+ r = mount_entry_chase(root_directory, m, mount_entry_path(m), &m->path_malloc);
if (r <= 0)
return r;
@@ -633,7 +669,6 @@ static int apply_mount(
case READONLY:
case READWRITE:
-
r = path_is_mount_point(mount_entry_path(m), root_directory, 0);
if (r < 0)
return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m));
@@ -643,6 +678,19 @@ static int apply_mount(
what = mount_entry_path(m);
break;
+ case BIND_MOUNT:
+ rbind = false;
+ /* fallthrough */
+
+ case BIND_MOUNT_RECURSIVE:
+ /* Also chase the source mount */
+ r = mount_entry_chase(root_directory, m, mount_entry_source(m), &m->source_malloc);
+ if (r <= 0)
+ return r;
+
+ what = mount_entry_source(m);
+ break;
+
case PRIVATE_TMP:
what = tmp_dir;
break;
@@ -660,7 +708,7 @@ static int apply_mount(
assert(what);
- if (mount(what, mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0)
+ if (mount(what, mount_entry_path(m), NULL, MS_BIND|(rbind ? MS_REC : 0), NULL) < 0)
return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, mount_entry_path(m));
log_debug("Successfully mounted %s to %s", what, mount_entry_path(m));
@@ -695,6 +743,8 @@ static unsigned namespace_calculate_mounts(
char** read_write_paths,
char** read_only_paths,
char** inaccessible_paths,
+ const BindMount *bind_mounts,
+ unsigned n_bind_mounts,
const char* tmp_dir,
const char* var_tmp_dir,
ProtectHome protect_home,
@@ -719,6 +769,7 @@ static unsigned namespace_calculate_mounts(
strv_length(read_write_paths) +
strv_length(read_only_paths) +
strv_length(inaccessible_paths) +
+ n_bind_mounts +
ns_info->private_dev +
(ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) +
(ns_info->protect_control_groups ? 1 : 0) +
@@ -732,6 +783,8 @@ int setup_namespace(
char** read_write_paths,
char** read_only_paths,
char** inaccessible_paths,
+ const BindMount *bind_mounts,
+ unsigned n_bind_mounts,
const char* tmp_dir,
const char* var_tmp_dir,
ProtectHome protect_home,
@@ -751,6 +804,7 @@ int setup_namespace(
read_write_paths,
read_only_paths,
inaccessible_paths,
+ bind_mounts, n_bind_mounts,
tmp_dir, var_tmp_dir,
protect_home, protect_system);
@@ -772,6 +826,10 @@ int setup_namespace(
if (r < 0)
goto finish;
+ r = append_bind_mounts(&m, bind_mounts, n_bind_mounts);
+ if (r < 0)
+ goto finish;
+
if (tmp_dir) {
*(m++) = (MountEntry) {
.path_const = "/tmp",
@@ -911,6 +969,53 @@ finish:
return r;
}
+void bind_mount_free_many(BindMount *b, unsigned n) {
+ unsigned i;
+
+ assert(b || n == 0);
+
+ for (i = 0; i < n; i++) {
+ free(b[i].source);
+ free(b[i].destination);
+ }
+
+ free(b);
+}
+
+int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item) {
+ _cleanup_free_ char *s = NULL, *d = NULL;
+ BindMount *c;
+
+ assert(b);
+ assert(n);
+ assert(item);
+
+ s = strdup(item->source);
+ if (!s)
+ return -ENOMEM;
+
+ d = strdup(item->destination);
+ if (!d)
+ return -ENOMEM;
+
+ c = realloc_multiply(*b, sizeof(BindMount), *n + 1);
+ if (!c)
+ return -ENOMEM;
+
+ *b = c;
+
+ c[(*n) ++] = (BindMount) {
+ .source = s,
+ .destination = d,
+ .read_only = item->read_only,
+ .recursive = item->recursive,
+ .ignore_enoent = item->ignore_enoent,
+ };
+
+ s = d = NULL;
+ return 0;
+}
+
static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
_cleanup_free_ char *x = NULL;
char bid[SD_ID128_STRING_MAX];