git: 8ce85300c792 - main - ln: Tweak append logic.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 15 Apr 2025 17:58:16 UTC
The branch main has been updated by des:
URL: https://cgit.FreeBSD.org/src/commit/?id=8ce85300c792ecfcb9a307577e3af96ea9a4544c
commit 8ce85300c792ecfcb9a307577e3af96ea9a4544c
Author: Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-04-15 17:57:52 +0000
Commit: Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-04-15 17:57:52 +0000
ln: Tweak append logic.
If the target is "." or ends in "/" or "/.", we always want to append
the source's basename, even in the Fflag case.
MFC after: never
Relnotes: yes
Sponsored by: Klara, Inc.
Reviewed by: allanjude
Differential Revision: https://reviews.freebsd.org/D49842
---
bin/ln/ln.c | 34 +++++++++++++++++++++++++++-------
bin/ln/tests/ln_test.sh | 6 +++++-
2 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/bin/ln/ln.c b/bin/ln/ln.c
index 6300658effa1..3055c7563cca 100644
--- a/bin/ln/ln.c
+++ b/bin/ln/ln.c
@@ -212,6 +212,13 @@ samedirent(const char *path1, const char *path2)
return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
}
+/*
+ * Create a link to source. If target is a directory (and some additional
+ * conditions apply, see comments within) the link will be created within
+ * target and have the basename of source. Otherwise, the link will be
+ * named target. If isdir is true, target has already been determined to
+ * be a directory; otherwise, we will check, if needed.
+ */
static int
linkit(const char *source, const char *target, bool isdir)
{
@@ -221,7 +228,7 @@ linkit(const char *source, const char *target, bool isdir)
struct stat sb;
const char *p;
int ch, first;
- bool exists;
+ bool append, exists;
if (!sflag) {
/* If source doesn't exist, quit now. */
@@ -238,14 +245,27 @@ linkit(const char *source, const char *target, bool isdir)
}
/*
- * If the target is a directory (and not a symlink if hflag),
- * append the source's name, unless Fflag is set.
+ * Append a slash and the source's basename if:
+ * - the target is "." or ends in "/" or "/.", or
+ * - the target is a directory (and not a symlink if hflag) and
+ * Fflag is not set
*/
- if (!Fflag && (isdir ||
- (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
- (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode)))) {
+ if ((p = strrchr(target, '/')) == NULL)
+ p = target;
+ else
+ p++;
+ append = false;
+ if (p[0] == '\0' || (p[0] == '.' && p[1] == '\0')) {
+ append = true;
+ } else if (!Fflag) {
+ if (isdir || (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
+ (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode))) {
+ append = true;
+ }
+ }
+ if (append) {
if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
- (p = basename(bbuf)) == NULL ||
+ (p = basename(bbuf)) == NULL /* can't happen */ ||
snprintf(path, sizeof(path), "%s/%s", target, p) >=
(ssize_t)sizeof(path)) {
errno = ENAMETOOLONG;
diff --git a/bin/ln/tests/ln_test.sh b/bin/ln/tests/ln_test.sh
index 82bc556842d8..78b4074aea18 100644
--- a/bin/ln/tests/ln_test.sh
+++ b/bin/ln/tests/ln_test.sh
@@ -170,11 +170,15 @@ sfF_flag_head()
}
sfF_flag_body()
{
- atf_check mkdir A B C
+ atf_check mkdir A B C D D/A
atf_check ln -sF A C
atf_check_symlink_to A C
atf_check ln -sfF B C
atf_check_symlink_to B C
+ atf_check ln -sfF A D/
+ atf_check_symlink_to A D/A
+ atf_check ln -sfF ../A .
+ atf_check_symlink_to ../A A
}
atf_test_case s_flag