summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libbb.h2
-rw-r--r--libbb/xreadlink.c75
-rwxr-xr-xtestsuite/realpath.tests45
3 files changed, 91 insertions, 31 deletions
diff --git a/include/libbb.h b/include/libbb.h
index b72576f..296417d 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -578,7 +578,7 @@ DIR *xopendir(const char *path) FAST_FUNC;
DIR *warn_opendir(const char *path) FAST_FUNC;
char *xmalloc_realpath(const char *path) FAST_FUNC RETURNS_MALLOC;
-char *xmalloc_realpath_coreutils(const char *path) FAST_FUNC RETURNS_MALLOC;
+char *xmalloc_realpath_coreutils(char *path) FAST_FUNC RETURNS_MALLOC;
char *xmalloc_readlink(const char *path) FAST_FUNC RETURNS_MALLOC;
char *xmalloc_readlink_or_warn(const char *path) FAST_FUNC RETURNS_MALLOC;
/* !RETURNS_MALLOC: it's a realloc-like function */
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c
index a18dd07..2682f69 100644
--- a/libbb/xreadlink.c
+++ b/libbb/xreadlink.c
@@ -123,7 +123,7 @@ char* FAST_FUNC xmalloc_realpath(const char *path)
#endif
}
-char* FAST_FUNC xmalloc_realpath_coreutils(const char *path)
+char* FAST_FUNC xmalloc_realpath_coreutils(char *path)
{
char *buf;
@@ -137,32 +137,19 @@ char* FAST_FUNC xmalloc_realpath_coreutils(const char *path)
* (the directory must exist).
*/
if (!buf && errno == ENOENT) {
- char *last_slash = strrchr(path, '/');
- if (last_slash) {
- *last_slash++ = '\0';
- buf = xmalloc_realpath(path);
- if (buf) {
- unsigned len = strlen(buf);
- buf = xrealloc(buf, len + strlen(last_slash) + 2);
- buf[len++] = '/';
- strcpy(buf + len, last_slash);
- }
- } else {
- char *target = xmalloc_readlink(path);
- if (target) {
- char *cwd;
- if (target[0] == '/') {
- /*
- * $ ln -s /bin/qwe symlink # note: /bin is a link to /usr/bin
- * $ readlink -f symlink
- * /usr/bin/qwe/target_does_not_exist
- * $ realpath symlink
- * /usr/bin/qwe/target_does_not_exist
- */
- buf = xmalloc_realpath_coreutils(target);
- free(target);
- return buf;
- }
+ char *target, c, *last_slash;
+ size_t i;
+
+ target = xmalloc_readlink(path);
+ if (target) {
+ /*
+ * $ ln -s /bin/qwe symlink # note: /bin is a link to /usr/bin
+ * $ readlink -f symlink
+ * /usr/bin/qwe
+ * $ realpath symlink
+ * /usr/bin/qwe
+ */
+ if (target[0] != '/') {
/*
* $ ln -s target_does_not_exist symlink
* $ readlink -f symlink
@@ -170,13 +157,41 @@ char* FAST_FUNC xmalloc_realpath_coreutils(const char *path)
* $ realpath symlink
* /CURDIR/target_does_not_exist
*/
- cwd = xrealloc_getcwd_or_warn(NULL);
- buf = concat_path_file(cwd, target);
+ char *cwd = xrealloc_getcwd_or_warn(NULL);
+ char *tmp = concat_path_file(cwd, target);
free(cwd);
free(target);
- return buf;
+ target = tmp;
+ }
+ buf = xmalloc_realpath_coreutils(target);
+ free(target);
+ return buf;
+ }
+
+ /* ignore leading and trailing slashes */
+ while (path[0] == '/' && path[1] == '/')
+ ++path;
+ i = strlen(path) - 1;
+ while (i > 0 && path[i] == '/')
+ i--;
+ c = path[i + 1];
+ path[i + 1] = '\0';
+
+ last_slash = strrchr(path, '/');
+ if (last_slash == path)
+ buf = xstrdup(path);
+ else if (last_slash) {
+ *last_slash = '\0';
+ buf = xmalloc_realpath(path);
+ *last_slash++ = '/';
+ if (buf) {
+ unsigned len = strlen(buf);
+ buf = xrealloc(buf, len + strlen(last_slash) + 2);
+ buf[len++] = '/';
+ strcpy(buf + len, last_slash);
}
}
+ path[i + 1] = c;
}
return buf;
diff --git a/testsuite/realpath.tests b/testsuite/realpath.tests
new file mode 100755
index 0000000..0e68e02
--- /dev/null
+++ b/testsuite/realpath.tests
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# Realpath tests.
+# Copyright 2006 by Natanael Copa <n@tanael.org>
+# Copyright 2021 by Ron Yorston <rmy@pobox.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+unset LC_ALL
+unset LC_MESSAGES
+unset LANG
+unset LANGUAGE
+
+TESTDIR=realpath_testdir
+TESTLINK1="link1"
+TESTLINK2="link2"
+
+# create the dir and test files
+mkdir -p "./$TESTDIR"
+ln -s "./$TESTDIR/not_file" "./$TESTLINK1"
+ln -s "./$TESTDIR/not_file/not_dir" "./$TESTLINK2"
+
+# shell's $PWD may leave symlinks unresolved.
+# "pwd" may be a built-in and have the same problem.
+# External pwd _can't_ have that problem (current dir on Unix is physical).
+pwd=`which pwd`
+pwd=`$pwd`
+testing "realpath on non-existent absolute path 1" "realpath /not_file" "/not_file\n" "" ""
+testing "realpath on non-existent absolute path 2" "realpath /not_file/" "/not_file\n" "" ""
+testing "realpath on non-existent absolute path 3" "realpath //not_file" "/not_file\n" "" ""
+testing "realpath on non-existent absolute path 4" "realpath /not_dir/not_file 2>&1" "realpath: /not_dir/not_file: No such file or directory\n" "" ""
+
+testing "realpath on non-existent local file 1" "realpath $TESTDIR/not_file" "$pwd/$TESTDIR/not_file\n" "" ""
+testing "realpath on non-existent local file 2" "realpath $TESTDIR/not_dir/not_file 2>&1" "realpath: $TESTDIR/not_dir/not_file: No such file or directory\n" "" ""
+
+testing "realpath on link to non-existent file 1" "realpath $TESTLINK1" "$pwd/$TESTDIR/not_file\n" "" ""
+testing "realpath on link to non-existent file 2" "realpath $TESTLINK2 2>&1" "realpath: $TESTLINK2: No such file or directory\n" "" ""
+testing "realpath on link to non-existent file 3" "realpath ./$TESTLINK1" "$pwd/$TESTDIR/not_file\n" "" ""
+testing "realpath on link to non-existent file 4" "realpath ./$TESTLINK2 2>&1" "realpath: ./$TESTLINK2: No such file or directory\n" "" ""
+
+# clean up
+rm -r "$TESTLINK1" "$TESTLINK2" "$TESTDIR"
+
+exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))