Upgrade /tmp when PrivateTmp=yes/DefaultDeps=no to disconnected (#39790)

This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-02-25 16:14:13 +01:00
committed by GitHub
9 changed files with 205 additions and 155 deletions
+15 -4
View File
@@ -63,15 +63,26 @@
<listitem><para>Units with <varname>PrivateTmp=yes</varname> automatically gain dependencies of type
<varname>Wants=</varname> and <varname>After=</varname> on all mounts required to access
<filename>/tmp/</filename> and <filename>/var/tmp/</filename>. They will also gain an automatic
<filename>/tmp/</filename> and <filename>/var/tmp/</filename> and an automatic
<varname>After=</varname> dependency on
<citerefentry><refentrytitle>systemd-tmpfiles-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
</para></listitem>
<citerefentry><refentrytitle>systemd-tmpfiles-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
unless <varname>DefaultDependencies=no</varname> is specified. If
<varname>DefaultDependencies=no</varname> is specified, and a
<varname>RequiresMountsFor=/tmp/</varname>, <varname>WantsMountsFor=/tmp/</varname>,
<varname>After=tmp.mount</varname>, or <varname>RootDirectory=</varname>/<varname>RootImage=</varname>
are not specified, <varname>PrivateTmp=yes</varname> is converted to
<varname>PrivateTmp=disconnected</varname>.</para></listitem>
<listitem><para>Units with <varname>PrivateTmp=disconnected</varname> automatically gain dependencies
of type <varname>Wants=</varname> and <varname>After=</varname> on the mount required to access
<filename>/var/</filename>, unless <varname>DefaultDependencies=no</varname> and/or
<varname>RootDirectory=/RootImage=</varname> are specified.</para></listitem>
<varname>RootDirectory=</varname>/<varname>RootImage=</varname> are specified. If
<varname>DefaultDependencies=no</varname> is specified, and a
<varname>RequiresMountsFor=/var/</varname>, <varname>WantsMountsFor=/var/</varname>,
<varname>After=var.mount</varname>, <varname>RootDirectory=</varname>/<varname>RootImage=</varname> are
not specified, the private mount on <filename>/tmp/</filename> is reused for
<filename>/var/tmp/</filename> by setting <varname>$TMPDIR</varname> appropriately.</para>
</listitem>
<listitem><para>Units whose standard output or error output is connected to <option>journal</option> or
<option>kmsg</option> (or their combinations with console output, see below) automatically acquire
+11 -11
View File
@@ -2089,7 +2089,7 @@ static int build_environment(
/* If this is D-Bus, tell the nss-systemd module, since it relies on being able to use blocking
* Varlink calls back to us for look up dynamic users in PID 1. Break the deadlock between D-Bus and
* PID 1 by disabling use of PID1' NSS interface for looking up dynamic users. */
* PID 1 by disabling use of PID1's NSS interface for looking up dynamic users. */
if (p->flags & EXEC_NSS_DYNAMIC_BYPASS) {
r = strv_extend_with_size(&e, &n, "SYSTEMD_NSS_DYNAMIC_BYPASS=1");
if (r < 0)
@@ -2097,8 +2097,8 @@ static int build_environment(
}
/* We query "root" if this is a system unit and User= is not specified. $USER is always set. $HOME
* could cause problem for e.g. getty, since login doesn't override $HOME, and $LOGNAME and $SHELL don't
* really make much sense since we're not logged in. Hence we conditionalize the three based on
* could cause problem for e.g. getty, since login doesn't override $HOME, and $LOGNAME and $SHELL
* don't really make much sense since we're not logged in. Hence we conditionalize the three based on
* SetLoginEnvironment= switch. */
if (!username && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
assert(!c->user);
@@ -2248,10 +2248,8 @@ static int build_environment(
}
assert(c->private_var_tmp >= 0 && c->private_var_tmp < _PRIVATE_TMP_MAX);
if (needs_sandboxing && c->private_tmp != c->private_var_tmp) {
assert(c->private_tmp == PRIVATE_TMP_DISCONNECTED);
assert(c->private_var_tmp == PRIVATE_TMP_NO);
if (needs_sandboxing &&
c->private_var_tmp == PRIVATE_TMP_NO && c->private_tmp != PRIVATE_TMP_NO) {
/* When private tmpfs is enabled only on /tmp/, then explicitly set $TMPDIR to suggest the
* service to use /tmp/. */
@@ -3907,14 +3905,16 @@ static int apply_mount_namespace(
if (needs_sandboxing) {
/* The runtime struct only contains the parent of the private /tmp, which is non-accessible
* to world users. Inside of it there's a /tmp that is sticky, and that's the one we want to
* use here. This does not apply when we are using /run/systemd/empty as fallback. */
* use here. This does not apply when we are using /run/systemd/empty as fallback. */
if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime->shared) {
if (streq_ptr(runtime->shared->tmp_dir, RUN_SYSTEMD_EMPTY))
tmp_dir = runtime->shared->tmp_dir;
else if (runtime->shared->tmp_dir)
tmp_dir = strjoina(runtime->shared->tmp_dir, "/tmp");
}
if (context->private_var_tmp == PRIVATE_TMP_CONNECTED && runtime->shared) {
if (streq_ptr(runtime->shared->var_tmp_dir, RUN_SYSTEMD_EMPTY))
var_tmp_dir = runtime->shared->var_tmp_dir;
else if (runtime->shared->var_tmp_dir)
@@ -4624,7 +4624,7 @@ static bool exec_needs_cap_sys_admin(const ExecContext *context, const ExecParam
return false;
return context->private_users != PRIVATE_USERS_NO ||
context->private_tmp != PRIVATE_TMP_NO ||
context->private_tmp != PRIVATE_TMP_NO || /* no need to check for private_var_tmp here, private_tmp is never demoted to "no" */
context->private_devices ||
context->private_network ||
context->user_namespace_path ||
@@ -4833,7 +4833,7 @@ static int setup_delegated_namespaces(
/* If PrivatePIDs= yes is configured, we're now running as pid 1 in a pid namespace! */
if (exec_needs_mount_namespace(context, params, runtime) &&
if (exec_needs_mount_namespace(context, params) &&
exec_namespace_is_delegated(context, params, have_cap_sys_admin, CLONE_NEWNS) == delegate) {
_cleanup_free_ char *error_path = NULL;
@@ -5829,7 +5829,7 @@ int exec_invoke(
return log_error_errno(r, "Failed to connect to nsresourced: %m");
}
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
needs_mount_namespace = exec_needs_mount_namespace(context, params);
for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
r = setup_exec_directory(context, params, uid, gid, dt, needs_mount_namespace, exit_status);
+16 -17
View File
@@ -256,11 +256,7 @@ bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *
return context->private_pids != PRIVATE_PIDS_NO && namespace_type_supported(NAMESPACE_PID);
}
bool exec_needs_mount_namespace(
const ExecContext *context,
const ExecParameters *params,
const ExecRuntime *runtime) {
bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters *params) {
assert(context);
if (context->root_image ||
@@ -295,13 +291,8 @@ bool exec_needs_mount_namespace(
if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
return true;
if (context->private_tmp == PRIVATE_TMP_DISCONNECTED)
return true;
if (context->private_tmp == PRIVATE_TMP_CONNECTED && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
return true;
if (context->private_devices ||
context->private_tmp != PRIVATE_TMP_NO || /* no need to check for private_var_tmp here, private_tmp is never demoted to "no" */
context->private_mounts > 0 ||
(context->private_mounts < 0 && exec_needs_network_namespace(context)) ||
context->protect_system != PROTECT_SYSTEM_NO ||
@@ -2394,7 +2385,6 @@ static int exec_shared_runtime_add(
if (r < 0)
return r;
assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
rt->tmp_dir = TAKE_PTR(*tmp_dir);
rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
@@ -2437,16 +2427,25 @@ static int exec_shared_runtime_make(
assert(id);
/* It is not necessary to create ExecSharedRuntime object. */
if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && c->private_tmp != PRIVATE_TMP_CONNECTED) {
if (!c->user_namespace_path && !exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) &&
c->private_tmp != PRIVATE_TMP_CONNECTED && c->private_var_tmp != PRIVATE_TMP_CONNECTED) {
*ret = NULL;
return 0;
}
if (c->private_tmp == PRIVATE_TMP_CONNECTED &&
!(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
(prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
!prefixed_path_strv_contains(c->inaccessible_paths, "/tmp")) {
r = setup_tmp_dir_one(id, "/tmp", &tmp_dir);
if (r < 0)
return r;
}
if (c->private_var_tmp == PRIVATE_TMP_CONNECTED &&
!prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") &&
!prefixed_path_strv_contains(c->inaccessible_paths, "/var")) {
r = setup_tmp_dir_one(id, "/var/tmp", &var_tmp_dir);
if (r < 0)
return r;
}
+4 -4
View File
@@ -333,9 +333,9 @@ typedef struct ExecContext {
int bind_log_sockets;
int memory_ksm;
MemoryTHP memory_thp;
PrivateTmp private_tmp;
PrivateTmp private_var_tmp; /* This is not an independent parameter, but calculated from other
* parameters in unit_patch_contexts(). */
PrivateTmp private_tmp; /* Those are not independent parameters, but are calculated from */
PrivateTmp private_var_tmp; /* other parameters in unit_patch_contexts(). */
bool private_network;
bool private_devices;
PrivateUsers private_users;
@@ -638,7 +638,7 @@ DECLARE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
DECLARE_STRING_TABLE_LOOKUP(memory_thp, MemoryTHP);
bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters *params, const ExecRuntime *runtime);
bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters *params);
bool exec_needs_network_namespace(const ExecContext *context);
bool exec_needs_ipc_namespace(const ExecContext *context);
bool exec_needs_pid_namespace(const ExecContext *context, const ExecParameters *params);
+93 -90
View File
@@ -751,59 +751,71 @@ static int append_tmpfs_mounts(MountList *ml, const TemporaryFileSystem *tmpfs,
return 0;
}
static int append_private_tmp(MountList *ml, const NamespaceParameters *p) {
MountEntry *me;
static int append_private_tmp_one(
MountList *ml,
PrivateTmp mode,
const char *path,
const char *connected_source) {
assert(ml);
assert(p);
assert(p->private_tmp == p->private_var_tmp ||
(p->private_tmp == PRIVATE_TMP_DISCONNECTED && p->private_var_tmp == PRIVATE_TMP_NO));
assert(mode >= 0 && mode < _PRIVATE_TMP_MAX);
assert(path);
if (p->tmp_dir) {
assert(p->private_tmp == PRIVATE_TMP_CONNECTED);
me = mount_list_extend(ml);
if (!me)
return log_oom_debug();
*me = (MountEntry) {
.path_const = "/tmp/",
.mode = MOUNT_PRIVATE_TMP,
.read_only = streq(p->tmp_dir, RUN_SYSTEMD_EMPTY),
.source_const = p->tmp_dir,
};
}
if (p->var_tmp_dir) {
assert(p->private_var_tmp == PRIVATE_TMP_CONNECTED);
me = mount_list_extend(ml);
if (!me)
return log_oom_debug();
*me = (MountEntry) {
.path_const = "/var/tmp/",
.mode = MOUNT_PRIVATE_TMP,
.read_only = streq(p->var_tmp_dir, RUN_SYSTEMD_EMPTY),
.source_const = p->var_tmp_dir,
};
}
if (p->private_tmp != PRIVATE_TMP_DISCONNECTED)
if (mode == PRIVATE_TMP_NO)
return 0;
if (p->private_var_tmp == PRIVATE_TMP_NO) {
me = mount_list_extend(ml);
if (!me)
return log_oom_debug();
if (mode == PRIVATE_TMP_CONNECTED && !connected_source)
/* Do nothing if the private tmp dir was suppressed as it would be made inaccessible anyways
* (see exec_shared_runtime_make()). */
return 0;
MountEntry *me = mount_list_extend(ml);
if (!me)
return log_oom_debug();
if (mode == PRIVATE_TMP_CONNECTED)
*me = (MountEntry) {
.path_const = "/tmp/",
.path_const = path,
.mode = MOUNT_PRIVATE_TMP,
.read_only = streq(connected_source, RUN_SYSTEMD_EMPTY),
.source_const = connected_source,
};
else
*me = (MountEntry) {
.path_const = path,
.mode = MOUNT_PRIVATE_TMPFS,
.options_const = "mode=0700" NESTED_TMPFS_LIMITS,
.flags = MS_NODEV|MS_STRICTATIME,
};
return 0;
}
static int append_private_tmp(MountList *ml, const NamespaceParameters *p) {
int r;
assert(ml);
assert(p);
assert(p->private_tmp >= 0 && p->private_tmp < _PRIVATE_TMP_MAX);
assert(p->private_var_tmp >= 0 && p->private_var_tmp < _PRIVATE_TMP_MAX);
if (p->private_tmp != PRIVATE_TMP_DISCONNECTED || p->private_var_tmp != PRIVATE_TMP_DISCONNECTED) {
r = append_private_tmp_one(ml, p->private_tmp, "/tmp/", p->tmp_dir);
if (r < 0)
return r;
r = append_private_tmp_one(ml, p->private_var_tmp, "/var/tmp/", p->var_tmp_dir);
if (r < 0)
return r;
return 0;
}
/* Fully disconnected private tmp: we mount a single tmpfs instance with two subdirs which are
* bind mounted to /tmp/ and /var/tmp/. */
MountEntry *me;
_cleanup_free_ char *tmpfs_dir = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
tmpfs_dir = path_join(p->private_namespace_dir, "unit-private-tmp");
tmp_dir = path_join(tmpfs_dir, "tmp");
@@ -3333,11 +3345,30 @@ int temporary_filesystem_add(
return 0;
}
char* namespace_cleanup_tmpdir(char *p) {
if (!p)
return NULL;
if (!streq(p, RUN_SYSTEMD_EMPTY)) {
_cleanup_free_ char *child = path_join(p, "tmp");
if (!child)
log_oom_debug();
else
(void) rmdir(child);
(void) rmdir(p);
}
return mfree(p);
}
static int make_tmp_prefix(const char *prefix) {
_cleanup_free_ char *t = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(prefix);
/* Don't do anything unless we know the dir is actually missing */
r = access(prefix, F_OK);
if (r >= 0)
@@ -3369,22 +3400,21 @@ static int make_tmp_prefix(const char *prefix) {
r = RET_NERRNO(rename(t, prefix));
if (r < 0) {
(void) rmdir(t);
return r == -EEXIST ? 0 : r; /* it's fine if someone else created the dir by now */
return IN_SET(r, -EEXIST, -ENOTEMPTY) ? 0 : r; /* it's fine if someone else created the dir by now */
}
return 0;
}
static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, char **tmp_path) {
_cleanup_free_ char *x = NULL;
_cleanup_free_ char *y = NULL;
int setup_tmp_dir_one(const char *id, const char *prefix, char **ret_path) {
_cleanup_free_ char *d = NULL;
sd_id128_t boot_id;
bool rw = true;
int r;
assert(id);
assert(prefix);
assert(path);
assert(ret_path);
/* We include the boot id in the directory so that after a
* reboot we can easily identify obsolete directories. */
@@ -3393,8 +3423,8 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
if (r < 0)
return r;
x = strjoin(prefix, "/systemd-private-", SD_ID128_TO_STRING(boot_id), "-", id, "-XXXXXX");
if (!x)
d = strjoin(prefix, "/systemd-private-", SD_ID128_TO_STRING(boot_id), "-", id, "-XXXXXX");
if (!d)
return -ENOMEM;
r = make_tmp_prefix(prefix);
@@ -3402,7 +3432,7 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
return r;
WITH_UMASK(0077)
if (!mkdtemp(x)) {
if (!mkdtemp(d)) {
if (errno == EROFS || ERRNO_IS_DISK_SPACE(errno))
rw = false;
else
@@ -3410,20 +3440,25 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
}
if (rw) {
y = strjoin(x, "/tmp");
if (!y)
_cleanup_free_ char *inner_dir = path_join(d, "tmp");
if (!inner_dir) {
(void) rmdir(d);
return -ENOMEM;
}
WITH_UMASK(0000)
if (mkdir(y, 0777 | S_ISVTX) < 0)
return -errno;
r = label_fix_full(AT_FDCWD, y, prefix, 0);
if (r < 0)
r = RET_NERRNO(mkdir(inner_dir, 0777 | S_ISVTX));
if (r < 0) {
(void) rmdir(d);
return r;
}
if (tmp_path)
*tmp_path = TAKE_PTR(y);
r = label_fix_full(AT_FDCWD, inner_dir, prefix, 0);
if (r < 0) {
(void) rmdir(inner_dir);
(void) rmdir(d);
return r;
}
} else {
/* Trouble: we failed to create the directory. Instead of failing, let's simulate /tmp being
* read-only. This way the service will get the EROFS result as if it was writing to the real
@@ -3433,44 +3468,12 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
if (r < 0)
return r;
r = free_and_strdup(&x, RUN_SYSTEMD_EMPTY);
r = free_and_strdup(&d, RUN_SYSTEMD_EMPTY);
if (r < 0)
return r;
}
*path = TAKE_PTR(x);
return 0;
}
char* namespace_cleanup_tmpdir(char *p) {
PROTECT_ERRNO;
if (!streq_ptr(p, RUN_SYSTEMD_EMPTY))
(void) rmdir(p);
return mfree(p);
}
int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
_cleanup_(namespace_cleanup_tmpdirp) char *a = NULL;
_cleanup_(rmdir_and_freep) char *a_tmp = NULL;
char *b;
int r;
assert(id);
assert(tmp_dir);
assert(var_tmp_dir);
r = setup_one_tmp_dir(id, "/tmp", &a, &a_tmp);
if (r < 0)
return r;
r = setup_one_tmp_dir(id, "/var/tmp", &b, NULL);
if (r < 0)
return r;
a_tmp = mfree(a_tmp); /* avoid rmdir */
*tmp_dir = TAKE_PTR(a);
*var_tmp_dir = TAKE_PTR(b);
*ret_path = TAKE_PTR(d);
return 0;
}
+1 -4
View File
@@ -232,10 +232,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path);
char* namespace_cleanup_tmpdir(char *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, namespace_cleanup_tmpdir);
int setup_tmp_dirs(
const char *id,
char **tmp_dir,
char **var_tmp_dir);
int setup_tmp_dir_one(const char *id, const char *prefix, char **ret_path);
int setup_shareable_ns(int ns_storage_socket[static 2], unsigned long nsflag);
int open_shareable_ns_path(int netns_storage_socket[static 2], const char *path, unsigned long nsflag);
+1 -1
View File
@@ -5800,7 +5800,7 @@ static int service_can_live_mount(Unit *u, sd_bus_error *reterr_error) {
Service *s = ASSERT_PTR(SERVICE(u));
/* Ensure that the unit runs in a private mount namespace */
if (!exec_needs_mount_namespace(&s->exec_context, /* params= */ NULL, s->exec_runtime))
if (!exec_needs_mount_namespace(&s->exec_context, /* params= */ NULL))
return sd_bus_error_setf(
reterr_error,
SD_BUS_ERROR_INVALID_ARGS,
+62 -23
View File
@@ -1310,20 +1310,15 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
assert(c->private_var_tmp >= 0 && c->private_var_tmp < _PRIVATE_TMP_MAX);
if (c->private_tmp == PRIVATE_TMP_CONNECTED) {
assert(c->private_var_tmp == PRIVATE_TMP_CONNECTED);
r = unit_add_mounts_for(u, "/tmp/", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS);
if (r < 0)
return r;
}
if (c->private_var_tmp == PRIVATE_TMP_CONNECTED) {
r = unit_add_mounts_for(u, "/var/tmp/", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS);
if (r < 0)
return r;
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
} else if (c->private_var_tmp == PRIVATE_TMP_DISCONNECTED && !exec_context_with_rootfs(c)) {
/* Even if PrivateTmp=disconnected, we still require /var/tmp/ mountpoint to be present,
* i.e. /var/ needs to be mounted. See comments in unit_patch_contexts(). */
@@ -1332,6 +1327,12 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
if (c->private_tmp == PRIVATE_TMP_CONNECTED || c->private_var_tmp == PRIVATE_TMP_CONNECTED) {
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
if (c->root_image || c->root_mstack) {
/* We need to wait for /dev/loopX to appear when doing RootImage=, hence let's add an
* implicit dependency on udev. (And for RootMStack= we might need it) */
@@ -4323,20 +4324,23 @@ static int unit_verify_contexts(const Unit *u) {
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC), "WorkingDirectory=~ is not allowed under DynamicUser=yes. Refusing.");
if (ec->working_directory && path_below_api_vfs(ec->working_directory) &&
exec_needs_mount_namespace(ec, /* params= */ NULL, /* runtime= */ NULL))
exec_needs_mount_namespace(ec, /* params= */ NULL))
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC), "WorkingDirectory= may not be below /proc/, /sys/ or /dev/ when using mount namespacing. Refusing.");
if (exec_needs_pid_namespace(ec, /* params= */ NULL) && !UNIT_VTABLE(u)->notify_pidref)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC), "PrivatePIDs= setting is only supported for service units. Refusing.");
if ((ec->user || ec->dynamic_user || ec->group || ec->pam_name) && ec->private_users == PRIVATE_USERS_MANAGED)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC), "PrivateUsers=managed may not be used in combination with User=/DynamicUser=/Group=/PAMName=, refusing.");
if (ec->user_namespace_path && ec->private_users != PRIVATE_USERS_NO)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC), "PrivateUsers= may not be used with custom UserNamespacePath=, refusing.");
const KillContext *kc = unit_get_kill_context(u);
if (ec->pam_name && kc && !IN_SET(kc->kill_mode, KILL_CONTROL_GROUP, KILL_MIXED))
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC), "Unit has PAM enabled. Kill mode must be set to 'control-group' or 'mixed'. Refusing.");
if ((ec->user || ec->dynamic_user || ec->group || ec->pam_name) && ec->private_users == PRIVATE_USERS_MANAGED)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC), "PrivateUsers=managed may not be used in combination with User=/DynamicUser=/Group=/PAMName=, refusing.");
return 0;
}
@@ -4346,8 +4350,8 @@ static PrivateTmp unit_get_private_var_tmp(const Unit *u, const ExecContext *c)
assert(c->private_tmp >= 0 && c->private_tmp < _PRIVATE_TMP_MAX);
/* Disable disconnected private tmpfs on /var/tmp/ when DefaultDependencies=no and
* RootImage=/RootDirectory= are not set, as /var/ may be a separated partition.
* See issue #37258. */
* RootImage=/RootDirectory= are not set, as /var/ may be a separate partition.
* See https://github.com/systemd/systemd/issues/37258. */
/* PrivateTmp=yes/no also enables/disables private tmpfs on /var/tmp/. */
if (c->private_tmp != PRIVATE_TMP_DISCONNECTED)
@@ -4369,22 +4373,50 @@ static PrivateTmp unit_get_private_var_tmp(const Unit *u, const ExecContext *c)
if (hashmap_contains(u->mounts_for[t], "/var/"))
return PRIVATE_TMP_DISCONNECTED;
/* Check the same but for After= with Requires=/Requisite=/Wants= or friends. */
/* Check the same but for After=. */
Unit *m = manager_get_unit(u->manager, "var.mount");
if (!m)
return PRIVATE_TMP_NO;
if (!unit_has_dependency(u, UNIT_ATOM_AFTER, m))
return PRIVATE_TMP_NO;
if (unit_has_dependency(u, UNIT_ATOM_PULL_IN_START, m) ||
unit_has_dependency(u, UNIT_ATOM_PULL_IN_VERIFY, m) ||
unit_has_dependency(u, UNIT_ATOM_PULL_IN_START_IGNORED, m))
if (m && unit_has_dependency(u, UNIT_ATOM_AFTER, m))
return PRIVATE_TMP_DISCONNECTED;
return PRIVATE_TMP_NO;
}
static PrivateTmp unit_get_private_tmp(const Unit *u, const ExecContext *c) {
assert(u);
assert(c);
assert(c->private_tmp >= 0 && c->private_tmp < _PRIVATE_TMP_MAX);
/* Upgrade "PrivateTmp=yes" (a.k.a. 'connected') to 'disconnected' when
* DefaultDependencies=no and RootImage=/RootDirectory= are not set, as /tmp/ may be a
* separate partition. See https://github.com/systemd/systemd/issues/28515.
*
* Note that the change goes in the opposite direction than unit_get_private_var_tmp()
* above. For /var/tmp/, we need to disable the setting, because we don't want to create
* the /var/tmp/ directory if /var/ is a mount point. We don't have this problem with
* /tmp/ because there is no nesting. */
if (c->private_tmp != PRIVATE_TMP_CONNECTED ||
u->default_dependencies ||
exec_context_with_rootfs(c))
return c->private_tmp;
/* Even if DefaultDependencies=no, honour tmpfs setting when
* RequiresMountsFor=/WantsMountsFor=/tmp/ is explicitly set. */
for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; t++)
if (hashmap_contains(u->mounts_for[t], "/tmp/"))
return c->private_tmp;
/* Check the same but for After=. */
Unit *m = manager_get_unit(u->manager, "tmp.mount");
if (!m)
return c->private_tmp;
if (unit_has_dependency(u, UNIT_ATOM_AFTER, m))
return c->private_tmp;
return PRIVATE_TMP_DISCONNECTED;
}
int unit_patch_contexts(Unit *u) {
CGroupContext *cc;
ExecContext *ec;
@@ -4461,7 +4493,14 @@ int unit_patch_contexts(Unit *u) {
ec->restrict_suid_sgid = true;
}
/* Table of possible combinations:
* /var/tmp /tmp
* PrivateTmp=no no no
* PrivateTmp=connected connected connected,disconnected
* PrivateTmp=disconnected disconnected,no disconnected
*/
ec->private_var_tmp = unit_get_private_var_tmp(u, ec);
ec->private_tmp = unit_get_private_tmp(u, ec);
FOREACH_ARRAY(d, ec->directories, _EXEC_DIRECTORY_TYPE_MAX)
exec_directory_sort(d);
+2 -1
View File
@@ -43,7 +43,8 @@ static void test_tmpdir_one(const char *id, const char *A, const char *B) {
struct stat x, y;
char *c, *d;
ASSERT_OK_ZERO(setup_tmp_dirs(id, &a, &b));
ASSERT_OK(setup_tmp_dir_one(id, "/tmp", &a));
ASSERT_OK(setup_tmp_dir_one(id, "/var/tmp", &b));
ASSERT_OK_ERRNO(stat(a, &x));
ASSERT_OK_ERRNO(stat(b, &y));