]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tmpfiles: check relative L? targets beside the link
authordongshengyuan <545258830@qq.com>
Wed, 15 Jul 2026 05:23:23 +0000 (13:23 +0800)
committerdongshengyuan <545258830@qq.com>
Thu, 23 Jul 2026 08:56:06 +0000 (16:56 +0800)
Relative symlink targets are resolved from the directory containing the
link. Make the L? existence check use the same rule.

Reproducer:
  tmp=$(mktemp -d /tmp/tmpfiles-link.XXXXXX)
  conf=$(mktemp /tmp/tmpfiles-conf.XXXXXX)
  printf data >"$tmp/target"
  printf 'L? %s/link - - - - target\n' "$tmp" >"$conf"
  systemd-tmpfiles --create --dry-run "$conf"

Before:
  The target was checked root-relative, not beside the link.

Follow-up for b5dc805583bbb019c6bf4c73bf0814a396dc0f12.

src/tmpfiles/tmpfiles.c
test/units/TEST-22-TMPFILES.15.sh

index 0e3244fcebe69388dec92b6c00e194409dad24f9..42512da6041c4fecc42144c74a52c191b676d236 100644 (file)
@@ -2680,16 +2680,30 @@ static int create_symlink(Context *c, Item *i) {
         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);
+                _cleanup_free_ char *target = NULL;
+                ChaseFlags chase_flags = CHASE_SAFE|CHASE_NOFOLLOW;
+
+                if (!path_is_absolute(i->argument)) {
+                        _cleanup_free_ char *dn = NULL;
+
+                        r = path_extract_directory(i->path, &dn);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to extract directory from path '%s': %m", i->path);
+
+                        target = path_join(dn, i->argument);
+                        if (!target)
+                                return log_oom();
+                } else
+                        chase_flags |= CHASE_PREFIX_ROOT;
+
+                r = chase(target ?: i->argument, arg_root, chase_flags, /* ret_path= */ NULL, /* ret_fd= */ NULL);
                 if (r == -ENOENT) {
                         /* Silently skip over lines where the source file is missing. */
-                        log_debug_errno(r, "Symlink source path '%s/%s' does not exist, skipping line.",
-                                        strempty(arg_root), skip_leading_slash(i->argument));
+                        log_debug_errno(r, "Symlink source path '%s' does not exist, skipping line.", 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), skip_leading_slash(i->argument));
+                        return log_error_errno(r, "Failed to check if symlink source path '%s' exists: %m", i->argument);
         }
 
         r = path_extract_filename(i->path, &bn);
index 68c6c301bd6f5a9ef8e7d4ae2a3d90f07f8f9fc4..78be11357df4ea75ea67416d58c7748950ce8110 100755 (executable)
@@ -54,3 +54,36 @@ EOF
 )"
 [[ "$output" == *"Would create directory $root/run/test"* ]]
 [[ "$output" != *"$root$root"* ]]
+
+# Check that L? resolves relative targets from the symlink's parent directory.
+root='/tmp/L/4'
+rm -rf "$root"
+mkdir -p "$root"
+touch "$root/target"
+
+systemd-tmpfiles --create - <<EOF
+L?    $root/link    - - - - target
+EOF
+test "$(readlink "$root/link")" = "target"
+
+rm -f "$root/link" "$root/target"
+systemd-tmpfiles --create - <<EOF
+L?    $root/link    - - - - target
+EOF
+test ! -e "$root/link"
+
+# Check the same relative target lookup with --root=.
+rm -rf "$root"
+mkdir -p "$root/dir"
+touch "$root/dir/target"
+
+systemd-tmpfiles --create --root="$root" - <<EOF
+L?    /dir/link    - - - - target
+EOF
+test "$(readlink "$root/dir/link")" = "target"
+
+rm -f "$root/dir/link" "$root/dir/target"
+systemd-tmpfiles --create --root="$root" - <<EOF
+L?    /dir/link    - - - - target
+EOF
+test ! -e "$root/dir/link"