Blob Blame History Raw
From e52293aa7fcf283758f97bc9bcc945707ccbce0a Mon Sep 17 00:00:00 2001
From: =?utf8?q?R=C3=A9my=20Lefevre?= <lefevreremy@gmail.com>
Date: Tue, 2 Apr 2013 02:48:28 +0100
Subject: [PATCH 1/1] ln: --relative: fix updating of existing symlinks

Don't dereference an existing symlink being replaced.
I.E. generate the symlink relative to the symlink's containing dir,
rather than to some arbitrary place it points to.

* src/ln.c (convert_abs_rel): Don't consider the final component
of the symlink name when canonicalizing, as we want to avoid
dereferencing the final component.
* tests/ln/relative.sh: Add a test case.
Resolves http://bugs.gnu.org/14116
---
 src/ln.c             |   14 ++++++++------
 tests/ln/relative.sh |    5 +++++
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/src/ln.c b/src/ln.c
index 1aa1473..2489b9a 100644
--- a/src/ln.c
+++ b/src/ln.c
@@ -132,22 +132,24 @@ target_directory_operand (char const *file)
 static char *
 convert_abs_rel (const char *from, const char *target)
 {
-  char *realtarget = canonicalize_filename_mode (target, CAN_MISSING);
+  /* Get dirname to generate paths relative to.  We don't resolve
+     the full TARGET as the last component could be an existing symlink.  */
+  char *targetdir = dir_name (target);
+
+  char *realdest = canonicalize_filename_mode (targetdir, CAN_MISSING);
   char *realfrom = canonicalize_filename_mode (from, CAN_MISSING);
 
   /* Write to a PATH_MAX buffer.  */
   char *relative_from = xmalloc (PATH_MAX);
 
-  /* Get dirname to generate paths relative to.  */
-  realtarget[dir_len (realtarget)] = '\0';
-
-  if (!relpath (realfrom, realtarget, relative_from, PATH_MAX))
+  if (!relpath (realfrom, realdest, relative_from, PATH_MAX))
     {
       free (relative_from);
       relative_from = NULL;
     }
 
-  free (realtarget);
+  free (targetdir);
+  free (realdest);
   free (realfrom);
 
   return relative_from ? relative_from : xstrdup (from);
diff --git a/tests/ln/relative.sh b/tests/ln/relative.sh
index 0418b8a..818da83 100755
--- a/tests/ln/relative.sh
+++ b/tests/ln/relative.sh
@@ -29,4 +29,9 @@ test $(readlink usr/bin/foo) = '../lib/foo/foo' || fail=1
 ln -sr usr/bin/foo usr/lib/foo/link-to-foo
 test $(readlink usr/lib/foo/link-to-foo) = 'foo' || fail=1
 
+# Correctly update an existing link, which was broken in <= 8.21
+ln -s dir1/dir2/f existing_link
+ln -srf here existing_link
+test $(readlink existing_link) = 'here' || fail=1
+
 Exit $fail
-- 
1.7.2.5