[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC PATCH] ln --relative: do not dereference target symlinks unless -L

From: Dmitry V. Levin
Subject: [RFC PATCH] ln --relative: do not dereference target symlinks unless -L is given
Date: Wed, 17 Jul 2013 02:11:13 +0400

I received a bug report
( saying that
'ln --relative' behaviour is counterintuitive with regard to
dereferencing target symlinks: ln -r should not dereference
source file symlinks unless -L option is given.

I agree with this bug report; --relative is a new option, so changing its
default behaviour wrt dereferencing target symlinks may be considered as a
bug fix, but I'm not quite sure about that.  If there is an understanding
that 'ln -P -r' (and maybe 'ln -r') should be changed to not dereference
source file symlinks, I'll update documentation describing -L, -P and -r

 src/ln.c             | 11 ++++++++++-
 tests/ln/ | 17 ++++++++++++++---
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/ln.c b/src/ln.c
index 2489b9a..8cdbc3d 100644
--- a/src/ln.c
+++ b/src/ln.c
@@ -53,6 +53,10 @@ static bool relative;
 /* If true, hard links are logical rather than physical.  */
 static bool logical = !!LINK_FOLLOWS_SYMLINKS;
+/* If true, canonicalizing source files will follow symlinks
+   when making relative symbolic links.  */
+static bool relative_follow_symlinks;
 /* If true, ask the user before removing existing files.  */
 static bool interactive;
@@ -137,7 +141,10 @@ convert_abs_rel (const char *from, const char *target)
   char *targetdir = dir_name (target);
   char *realdest = canonicalize_filename_mode (targetdir, CAN_MISSING);
-  char *realfrom = canonicalize_filename_mode (from, CAN_MISSING);
+  int from_can_mode =
+    relative_follow_symlinks ? CAN_MISSING : CAN_MISSING | CAN_NOLINKS;
+  char *realfrom = canonicalize_filename_mode (from, from_can_mode);
   /* Write to a PATH_MAX buffer.  */
   char *relative_from = xmalloc (PATH_MAX);
@@ -494,12 +501,14 @@ main (int argc, char **argv)
         case 'L':
           logical = true;
+          relative_follow_symlinks = true;
         case 'n':
           dereference_dest_dir_symlinks = false;
         case 'P':
           logical = false;
+          relative_follow_symlinks = false;
         case 'r':
           relative = true;
diff --git a/tests/ln/ b/tests/ln/
index 8d4f1e7..64e3d41 100755
--- a/tests/ln/
+++ b/tests/ln/
@@ -27,7 +27,13 @@ ln -sr usr/lib/foo/foo usr/bin/foo
 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
+test $(readlink usr/lib/foo/link-to-foo) = '../../bin/foo' || fail=1
+ln -Psr usr/bin/foo usr/lib/foo/P-link-to-foo
+test $(readlink usr/lib/foo/P-link-to-foo) = '../../bin/foo' || fail=1
+ln -Lsr usr/bin/foo usr/lib/foo/L-link-to-foo
+test $(readlink usr/lib/foo/L-link-to-foo) = 'foo' || fail=1
 # Correctly update an existing link, which was broken in <= 8.21
 ln -s dir1/dir2/f existing_link
@@ -35,7 +41,8 @@ ln -srf here existing_link
 test $(readlink existing_link) = 'here' || fail=1
 # Demonstrate resolved symlinks used to generate relative links
-# so here, 'web/latest' will not be linked to the intermediate 'latest' link.
+# so here, 'web/latest' will be linked to the intermediate 'latest' link
+# unless -L option was given.
 # You'd probably want to use realpath(1) in conjunction
 # with ln(1) without --relative to give greater control.
 ln -s release1 alpha
@@ -43,6 +50,10 @@ ln -s release2 beta
 ln -s beta latest
 mkdir web
 ln -sr latest web/latest
-test $(readlink web/latest) = '../release2' || fail=1
+test $(readlink web/latest) = '../latest' || fail=1
+ln -Psr latest web/P-latest
+test $(readlink web/P-latest) = '../latest' || fail=1
+ln -Lsr latest web/L-latest
+test $(readlink web/L-latest) = '../release2' || fail=1
 Exit $fail


reply via email to

[Prev in Thread] Current Thread [Next in Thread]