[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
(https://bugzilla.altlinux.org/show_bug.cgi?id=29115) 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
options.
---
src/ln.c | 11 ++++++++++-
tests/ln/relative.sh | 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)
break;
case 'L':
logical = true;
+ relative_follow_symlinks = true;
break;
case 'n':
dereference_dest_dir_symlinks = false;
break;
case 'P':
logical = false;
+ relative_follow_symlinks = false;
break;
case 'r':
relative = true;
diff --git a/tests/ln/relative.sh b/tests/ln/relative.sh
index 8d4f1e7..64e3d41 100755
--- a/tests/ln/relative.sh
+++ b/tests/ln/relative.sh
@@ -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
--
ldv
- [RFC PATCH] ln --relative: do not dereference target symlinks unless -L is given,
Dmitry V. Levin <=