Merge pull request #24784 from yuwata/core-exec-directory

core: do not create symlink to private directory if parent already exists
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2022-10-12 09:37:16 +02:00
committed by GitHub
7 changed files with 247 additions and 64 deletions
+85 -15
View File
@@ -91,6 +91,7 @@
#include "signal-util.h"
#include "smack-util.h"
#include "socket-util.h"
#include "sort-util.h"
#include "special.h"
#include "stat-util.h"
#include "string-table.h"
@@ -2412,12 +2413,24 @@ static int setup_exec_directory(
goto fail;
}
/* And link it up from the original place. Note that if a mount namespace is going to be
* used, then this symlink remains on the host, and a new one for the child namespace will
* be created later. */
r = symlink_idempotent(pp, p, true);
if (r < 0)
goto fail;
if (!context->directories[type].items[i].only_create) {
/* And link it up from the original place.
* Notes
* 1) If a mount namespace is going to be used, then this symlink remains on
* the host, and a new one for the child namespace will be created later.
* 2) It is not necessary to create this symlink when one of its parent
* directories is specified and already created. E.g.
* StateDirectory=foo foo/bar
* In that case, the inode points to pp and p for "foo/bar" are the same:
* pp = "/var/lib/private/foo/bar"
* p = "/var/lib/foo/bar"
* and, /var/lib/foo is a symlink to /var/lib/private/foo. So, not only
* we do not need to create the symlink, but we cannot create the symlink.
* See issue #24783. */
r = symlink_idempotent(pp, p, true);
if (r < 0)
goto fail;
}
} else {
_cleanup_free_ char *target = NULL;
@@ -3289,7 +3302,8 @@ static int compile_bind_mounts(
if (!params->prefix[t])
continue;
n += context->directories[t].n_items;
for (size_t i = 0; i < context->directories[t].n_items; i++)
n += !context->directories[t].items[i].only_create;
}
if (n <= 0) {
@@ -3358,6 +3372,11 @@ static int compile_bind_mounts(
for (size_t i = 0; i < context->directories[t].n_items; i++) {
char *s, *d;
/* When one of the parent directories is in the list, we cannot create the symlink
* for the child directory. See also the comments in setup_exec_directory(). */
if (context->directories[t].items[i].only_create)
continue;
if (exec_directory_is_private(context, t))
s = path_join(params->prefix[t], "private", context->directories[t].items[i].path);
else
@@ -3437,7 +3456,9 @@ static int compile_symlinks(
return r;
}
if (!exec_directory_is_private(context, dt) || exec_context_with_rootfs(context))
if (!exec_directory_is_private(context, dt) ||
exec_context_with_rootfs(context) ||
context->directories[dt].items[i].only_create)
continue;
private_path = path_join(params->prefix[dt], "private", context->directories[dt].items[i].path);
@@ -7041,33 +7062,82 @@ void exec_directory_done(ExecDirectory *d) {
d->mode = 0755;
}
int exec_directory_add(ExecDirectoryItem **d, size_t *n, const char *path, char **symlinks) {
static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path) {
assert(d);
assert(path);
for (size_t i = 0; i < d->n_items; i++)
if (path_equal(d->items[i].path, path))
return &d->items[i];
return NULL;
}
int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink) {
_cleanup_strv_free_ char **s = NULL;
_cleanup_free_ char *p = NULL;
ExecDirectoryItem *existing;
int r;
assert(d);
assert(n);
assert(path);
existing = exec_directory_find(d, path);
if (existing) {
r = strv_extend(&existing->symlinks, symlink);
if (r < 0)
return r;
return 0; /* existing item is updated */
}
p = strdup(path);
if (!p)
return -ENOMEM;
if (symlinks) {
s = strv_copy(symlinks);
if (symlink) {
s = strv_new(symlink);
if (!s)
return -ENOMEM;
}
if (!GREEDY_REALLOC(*d, *n + 1))
if (!GREEDY_REALLOC(d->items, d->n_items + 1))
return -ENOMEM;
(*d)[(*n) ++] = (ExecDirectoryItem) {
d->items[d->n_items++] = (ExecDirectoryItem) {
.path = TAKE_PTR(p),
.symlinks = TAKE_PTR(s),
};
return 0;
return 1; /* new item is added */
}
static int exec_directory_item_compare_func(const ExecDirectoryItem *a, const ExecDirectoryItem *b) {
assert(a);
assert(b);
return path_compare(a->path, b->path);
}
void exec_directory_sort(ExecDirectory *d) {
assert(d);
/* Sort the exec directories to make always parent directories processed at first in
* setup_exec_directory(), e.g., even if StateDirectory=foo/bar foo, we need to create foo at first,
* then foo/bar. Also, set .only_create flag if one of the parent directories is contained in the
* list. See also comments in setup_exec_directory() and issue #24783. */
if (d->n_items <= 1)
return;
typesafe_qsort(d->items, d->n_items, exec_directory_item_compare_func);
for (size_t i = 1; i < d->n_items; i++)
for (size_t j = 0; j < i; j++)
if (path_startswith(d->items[i].path, d->items[j].path)) {
d->items[i].only_create = true;
break;
}
}
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, ExecSetCredential, exec_set_credential_free);