1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00

tmpfiles: Implement L? to only create symlinks if source exists

This allows a single tmpfiles snippet with lines to symlink directories
from /usr/share/factory to be shared across many different configurations
while making sure symlinks only get created if the source actually exists.
This commit is contained in:
Daan De Meyer 2024-11-04 12:21:21 +01:00
parent c52f6c1f33
commit b5dc805583
3 changed files with 56 additions and 10 deletions

View File

@ -299,15 +299,13 @@ L /tmp/foobar - - - - /dev/null</programlisting>
<varlistentry>
<term><varname>L</varname></term>
<term><varname>L+</varname></term>
<listitem><para>Create a symlink if it does not exist
yet. If suffixed with <varname>+</varname> and a file or
directory already exists where the symlink is to be created,
it will be removed and be replaced by the symlink. If the
argument is omitted, symlinks to files with the same name
residing in the directory
<filename>/usr/share/factory/</filename> are created. Note
that permissions on symlinks are ignored.
</para></listitem>
<term><varname>L?</varname></term>
<listitem><para>Create a symlink if it does not exist yet. If suffixed with <varname>+</varname>
and a file or directory already exists where the symlink is to be created, it will be removed and
be replaced by the symlink. If suffixed with <varname>?</varname> and the source path does not
exist, the symlink is not created. If the argument is omitted, symlinks to files with the same name
residing in the directory <filename>/usr/share/factory/</filename> are created. Note that
permissions on symlinks are ignored.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -172,6 +172,8 @@ typedef struct Item {
bool purge:1;
bool ignore_if_target_missing:1;
OperationMask done;
} Item;
@ -440,6 +442,10 @@ static bool takes_ownership(ItemType t) {
RECURSIVE_REMOVE_PATH);
}
static bool supports_ignore_if_target_missing(ItemType t) {
return t == CREATE_SYMLINK;
}
static struct Item* find_glob(OrderedHashmap *h, const char *match) {
ItemArray *j;
@ -2400,6 +2406,17 @@ static int create_symlink(Context *c, Item *i) {
assert(c);
assert(i);
if (i->ignore_if_target_missing) {
r = chase(i->argument, arg_root, CHASE_SAFE|CHASE_PREFIX_ROOT|CHASE_NOFOLLOW, /*ret_path=*/ NULL, /*ret_fd=*/ NULL);
if (r == -ENOENT) {
/* Silently skip over lines where the source file is missing. */
log_info("Symlink source path '%s%s' does not exist, skipping line.", strempty(arg_root), i->argument);
return 0;
}
if (r < 0)
return log_error_errno(r, "Failed to check if symlink source path '%s%s' exists: %m", strempty(arg_root), i->argument);
}
r = path_extract_filename(i->path, &bn);
if (r < 0)
return log_error_errno(r, "Failed to extract filename from path '%s': %m", i->path);
@ -3593,7 +3610,8 @@ static int parse_line(
ItemArray *existing;
OrderedHashmap *h;
bool append_or_force = false, boot = false, allow_failure = false, try_replace = false,
unbase64 = false, from_cred = false, missing_user_or_group = false, purge = false;
unbase64 = false, from_cred = false, missing_user_or_group = false, purge = false,
ignore_if_target_missing = false;
int r;
assert(fname);
@ -3661,6 +3679,8 @@ static int parse_line(
from_cred = true;
else if (action[pos] == '$' && !purge)
purge = true;
else if (action[pos] == '?' && !ignore_if_target_missing)
ignore_if_target_missing = true;
else {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
@ -3678,6 +3698,7 @@ static int parse_line(
i.allow_failure = allow_failure;
i.try_replace = try_replace;
i.purge = purge;
i.ignore_if_target_missing = ignore_if_target_missing;
r = specifier_printf(path, PATH_MAX-1, specifier_table, arg_root, NULL, &i.path);
if (ERRNO_IS_NEG_NOINFO(r))
@ -3838,6 +3859,12 @@ static int parse_line(
"Purge flag '$' combined with line type '%c' which does not support purging.", (char) i.type);
}
if (i.ignore_if_target_missing && !supports_ignore_if_target_missing(i.type)) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
"Modifier '?' combined with line type '%c' which does not support this modifier.", (char) i.type);
}
if (!should_include_path(i.path))
return 0;
@ -3861,6 +3888,7 @@ static int parse_line(
if (!i.argument)
return log_oom();
}
break;
case COPY_FILES:

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2235
set -eux
# Test L?
rm -rf /tmp/tmpfiles
root="/tmp/tmpfiles"
mkdir "$root"
touch "$root/abc"
SYSTEMD_LOG_LEVEL=debug systemd-tmpfiles --create - --root=$root <<EOF
L? /i-dont-exist - - - - /def
L? /i-do-exist - - - - /abc
EOF
(! test -L "$root/i-dont-exist")
test -L "$root/i-do-exist"