From 1a542ccd3e6c706b8b4f10068681aeb8e64cb076 Mon Sep 17 00:00:00 2001 From: kevans Date: Sat, 16 Feb 2019 04:49:10 +0000 Subject: [PATCH] MFC r343601: install(1): Fix relative path calculation with partial common dest/src For example, from the referenced PR [1]: $ mkdir /tmp/lib/ /tmp/libexec $ touch /tmp/lib/foo.so $ install -lrs /tmp/lib/foo.so /tmp/libexec/ The common path identification bits terminate src at /tmp/lib/ and the destination at /tmp/libe. The subsequent backtracking is then incorrect, as it traverses the destination and backtraces exactly one level while eating the 'libexec' because it was previously (falsely) identified as common with 'lib'. The obvious fix would be to make sure we've actually terminated just after directory separators and rewind a character if we haven't. In the above example, we would end up rewinding to /tmp/ and subsequently doing the right thing. Test case added. PR: 235330 [1] --- usr.bin/xinstall/tests/install_test.sh | 24 ++++++++++++++++++++++++ usr.bin/xinstall/xinstall.c | 15 +++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/usr.bin/xinstall/tests/install_test.sh b/usr.bin/xinstall/tests/install_test.sh index 4332f9b8268..92044f34c20 100755 --- a/usr.bin/xinstall/tests/install_test.sh +++ b/usr.bin/xinstall/tests/install_test.sh @@ -377,6 +377,29 @@ mkdir_simple_body() { atf_check install -d dir1/dir2/dir3 } +atf_test_case symbolic_link_relative_absolute_common +symbolic_link_relative_absolute_common_head() { + atf_set "descr" "Verify -l rs with absolute paths having common components" +} +symbolic_link_relative_absolute_common_body() { + filename=foo.so + src_path=lib + src_path_prefixed=$PWD/$src_path + dest_path=$PWD/libexec/ + src_file=$src_path_prefixed/$filename + dest_file=$dest_path/$filename + + atf_check mkdir $src_path_prefixed $dest_path + atf_check touch $src_file + atf_check install -l sr $src_file $dest_path + + dest_path_relative=$(readlink $dest_file) + src_path_relative="../lib/$filename" + if [ "$src_path_relative" != "$dest_path_relative" ]; then + atf_fail "unexpected symlink contents ('$src_path_relative' != '$dest_path_relative')" + fi +} + atf_init_test_cases() { atf_add_test_case copy_to_nonexistent atf_add_test_case copy_to_nonexistent_safe @@ -415,5 +438,6 @@ atf_init_test_cases() { atf_add_test_case symbolic_link_relative_absolute_source_and_dest1 atf_add_test_case symbolic_link_relative_absolute_source_and_dest1_double_slash atf_add_test_case symbolic_link_relative_absolute_source_and_dest2 + atf_add_test_case symbolic_link_relative_absolute_common atf_add_test_case mkdir_simple } diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c index 16eba7768d8..6688591c4da 100644 --- a/usr.bin/xinstall/xinstall.c +++ b/usr.bin/xinstall/xinstall.c @@ -667,7 +667,7 @@ makelink(const char *from_name, const char *to_name, } if (dolink & LN_RELATIVE) { - char *to_name_copy, *cp, *d, *s; + char *to_name_copy, *cp, *d, *ld, *ls, *s; if (*from_name != '/') { /* this is already a relative link */ @@ -703,8 +703,19 @@ makelink(const char *from_name, const char *to_name, free(to_name_copy); /* Trim common path components. */ - for (s = src, d = dst; *s == *d; s++, d++) + ls = ld = NULL; + for (s = src, d = dst; *s == *d; ls = s, ld = d, s++, d++) continue; + /* + * If we didn't end after a directory separator, then we've + * falsely matched the last component. For example, if one + * invoked install -lrs /lib/foo.so /libexec/ then the source + * would terminate just after the separator while the + * destination would terminate in the middle of 'libexec', + * leading to a full directory getting falsely eaten. + */ + if ((ls != NULL && *ls != '/') || (ld != NULL && *ld != '/')) + s--, d--; while (*s != '/') s--, d--; -- 2.45.0