]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
ln: --relative: fix updating of existing symlinks
authorRémy Lefevre <lefevreremy@gmail.com>
Tue, 2 Apr 2013 01:48:28 +0000 (02:48 +0100)
committerPádraig Brady <P@draigBrady.com>
Thu, 4 Apr 2013 01:46:35 +0000 (02:46 +0100)
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.
* NEWS: Mention the fix.
Resolves http://bugs.gnu.org/14116

NEWS
src/ln.c
tests/ln/relative.sh

diff --git a/NEWS b/NEWS
index 0c2daad3eccbcc20b850ade7218aafd0360f9fd0..b9fc6d2d99557599f90c7b6f2f3fd8b32aa00dcc 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   permissions.
   [This bug was present in "the beginning".]
 
+  ln --relative now updates existing symlinks correctly.  Previously it based
+  the relative link on the dereferenced path of an existing link.
+  [This bug was introduced when --relative was added in coreutils-8.16.]
+
 ** New features
 
   join accepts a new option: --zero-terminated (-z). As with the sort,uniq
index 1aa14732447e2b0936be1af4cded181261ba4c91..2489b9ac7ff16e8477df8c22993b5fff9ca68b5d 100644 (file)
--- 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);
index 0418b8a046f112d40935b85e2d4c9bbc781825bc..818da8392d1b41e65a2dbf66cea94bc824ea4ddd 100755 (executable)
@@ -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