summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS4
-rw-r--r--src/ln.c14
-rwxr-xr-xtests/ln/relative.sh5
3 files changed, 17 insertions, 6 deletions
diff --git a/NEWS b/NEWS
index 0c2daad3e..b9fc6d2d9 100644
--- 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
diff --git a/src/ln.c b/src/ln.c
index 1aa147324..2489b9ac7 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 0418b8a04..818da8392 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