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]
MFC after:	1 week
This commit is contained in:
Kyle Evans 2019-01-31 05:20:11 +00:00
parent 43f5d5a277
commit 6cbda6d943
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=343601
2 changed files with 37 additions and 2 deletions

View File

@ -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
}

View File

@ -673,7 +673,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 */
@ -709,8 +709,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--;