Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 31 Jan 2019 05:20:11 +0000 (UTC)
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r343601 - in head/usr.bin/xinstall: . tests
Message-ID:  <201901310520.x0V5KBXj033765@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Thu Jan 31 05:20:11 2019
New Revision: 343601
URL: https://svnweb.freebsd.org/changeset/base/343601

Log:
  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

Modified:
  head/usr.bin/xinstall/tests/install_test.sh
  head/usr.bin/xinstall/xinstall.c

Modified: head/usr.bin/xinstall/tests/install_test.sh
==============================================================================
--- head/usr.bin/xinstall/tests/install_test.sh	Thu Jan 31 04:16:52 2019	(r343600)
+++ head/usr.bin/xinstall/tests/install_test.sh	Thu Jan 31 05:20:11 2019	(r343601)
@@ -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
 }

Modified: head/usr.bin/xinstall/xinstall.c
==============================================================================
--- head/usr.bin/xinstall/xinstall.c	Thu Jan 31 04:16:52 2019	(r343600)
+++ head/usr.bin/xinstall/xinstall.c	Thu Jan 31 05:20:11 2019	(r343601)
@@ -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--;
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201901310520.x0V5KBXj033765>