From 98c1344f7cb7057d67df87f4ee72688fb369e415 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Sat, 24 Oct 2026 10:47:43 -0500 Subject: [PATCH] Fixed --simulate=diff to show the new target of a replaced symlink When a symlink in the changes chroot replaced a link with a wrong target or an obstructing file, the diff report read the link from the real filesystem instead of from the changes chroot. A wrong link showed its old target with the first characters cut off, and an obstruction showed "Invalid target" because the original was a regular file. Ticket: ENT-14713 Changelog: Title --- cf-agent/simulate_mode.c | 4 +- .../29_simulate_mode/diff_mode_symlinks.cf | 60 +++++++++++++++++++ .../diff_mode_symlinks.cf.sub | 15 +++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 tests/acceptance/29_simulate_mode/diff_mode_symlinks.cf create mode 100644 tests/acceptance/29_simulate_mode/diff_mode_symlinks.cf.sub diff --git a/cf-agent/simulate_mode.c b/cf-agent/simulate_mode.c index b70274a9c13..7e36182d941 100644 --- a/cf-agent/simulate_mode.c +++ b/cf-agent/simulate_mode.c @@ -447,7 +447,7 @@ static bool DiffFile(const char *path) GetFileTypeDescription(st_orig.st_mode), GetFileTypeDescription(st_chrooted.st_mode)); ManifestStatInfo(&st_chrooted); - ManifestFileDetails(path, &st_chrooted, true); + ManifestFileDetails(chrooted_path, &st_chrooted, true); return true; } else @@ -459,7 +459,7 @@ static bool DiffFile(const char *path) default: printf("'%s' is a %s\n", path, GetFileTypeDescription(st_chrooted.st_mode)); ManifestStatInfo(&st_chrooted); - ManifestFileDetails(path, &st_chrooted, true); + ManifestFileDetails(chrooted_path, &st_chrooted, true); return true; } } diff --git a/tests/acceptance/29_simulate_mode/diff_mode_symlinks.cf b/tests/acceptance/29_simulate_mode/diff_mode_symlinks.cf new file mode 100644 index 00000000000..b993816a6f1 --- /dev/null +++ b/tests/acceptance/29_simulate_mode/diff_mode_symlinks.cf @@ -0,0 +1,60 @@ +body common control +{ + inputs => { "../default.sub.cf" }; + bundlesequence => { default("$(this.promise_filename)") }; + version => "1.0"; +} + +bundle agent init +{ + files: + "$(G.testroot)/target-old" create => "true"; + "$(G.testroot)/target-new" create => "true"; + + "$(G.testroot)/wrong-target" + link_from => linkfrom("$(G.testroot)/target-old", "symlink"); + + "$(G.testroot)/obstruction" + create => "true", + content => "I am in the way"; +} + +bundle agent test +{ + meta: + "test_soft_fail" + string => "(solaris|aix|hpux|windows)", + # ENT-6540 exotics fail to delete chroot + # ENT-10254 tests fail on Windows due to CRLF + meta => { "ENT-6540", "ENT-10254" }; + + "description" -> { "ENT-14713" } + string => "Test that --simulate=diff shows the new target of a symlink that replaced a wrong link or an obstructing file"; +} + +bundle agent check +{ + vars: + "command" + string => "$(sys.cf_agent) -Kf $(this.promise_filename).sub --simulate=diff"; + + "new_target" string => "\QTarget: '$(G.testroot)/target-new'\E"; + + # Bound the gap to one file's section (6 output lines today, +1 slack) + "same_section" string => "\n(?:[^\n]*\n){0,7}"; + + "wanted" + string => concat( + ".*\Q'$(G.testroot)/wrong-target' is a symbolic link\E$(same_section)$(new_target)", + ".*\Q'$(G.testroot)/obstruction' changed type from regular file to symbolic link\E$(same_section)$(new_target).*" + ); + + methods: + "check" + usebundle => dcs_passif_output( + $(wanted), + ".*(Invalid target|Target: '[^/]).*", + $(command), + $(this.promise_filename) + ); +} diff --git a/tests/acceptance/29_simulate_mode/diff_mode_symlinks.cf.sub b/tests/acceptance/29_simulate_mode/diff_mode_symlinks.cf.sub new file mode 100644 index 00000000000..41568112b95 --- /dev/null +++ b/tests/acceptance/29_simulate_mode/diff_mode_symlinks.cf.sub @@ -0,0 +1,15 @@ +body common control +{ + inputs => { "../default.sub.cf" }; +} + +bundle agent main +{ + files: + "$(G.testroot)/wrong-target" + link_from => linkfrom("$(G.testroot)/target-new", "symlink"), + move_obstructions => "true"; + "$(G.testroot)/obstruction" + link_from => linkfrom("$(G.testroot)/target-new", "symlink"), + move_obstructions => "true"; +}