#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"
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;
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) {
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
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);
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);
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_load_credential_hash_ops, char, string_hash_func, string_compare_func, ExecLoadCredential, exec_load_credential_free);