aboutsummaryrefslogtreecommitdiff
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 b72576f28..296417dae 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -578,7 +578,7 @@ DIR *xopendir(const char *path) FAST_FUNC;
578DIR *warn_opendir(const char *path) FAST_FUNC; 578DIR *warn_opendir(const char *path) FAST_FUNC;
579 579
580char *xmalloc_realpath(const char *path) FAST_FUNC RETURNS_MALLOC; 580char *xmalloc_realpath(const char *path) FAST_FUNC RETURNS_MALLOC;
581char *xmalloc_realpath_coreutils(const char *path) FAST_FUNC RETURNS_MALLOC; 581char *xmalloc_realpath_coreutils(char *path) FAST_FUNC RETURNS_MALLOC;
582char *xmalloc_readlink(const char *path) FAST_FUNC RETURNS_MALLOC; 582char *xmalloc_readlink(const char *path) FAST_FUNC RETURNS_MALLOC;
583char *xmalloc_readlink_or_warn(const char *path) FAST_FUNC RETURNS_MALLOC; 583char *xmalloc_readlink_or_warn(const char *path) FAST_FUNC RETURNS_MALLOC;
584/* !RETURNS_MALLOC: it's a realloc-like function */ 584/* !RETURNS_MALLOC: it's a realloc-like function */
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c
index a18dd0748..2682f6975 100644
--- a/libbb/xreadlink.c
+++ b/libbb/xreadlink.c
@@ -123,7 +123,7 @@ char* FAST_FUNC xmalloc_realpath(const char *path)
123#endif 123#endif
124} 124}
125 125
126char* FAST_FUNC xmalloc_realpath_coreutils(const char *path) 126char* FAST_FUNC xmalloc_realpath_coreutils(char *path)
127{ 127{
128 char *buf; 128 char *buf;
129 129
@@ -137,32 +137,19 @@ char* FAST_FUNC xmalloc_realpath_coreutils(const char *path)
137 * (the directory must exist). 137 * (the directory must exist).
138 */ 138 */
139 if (!buf && errno == ENOENT) { 139 if (!buf && errno == ENOENT) {
140 char *last_slash = strrchr(path, '/'); 140 char *target, c, *last_slash;
141 if (last_slash) { 141 size_t i;
142 *last_slash++ = '\0'; 142
143 buf = xmalloc_realpath(path); 143 target = xmalloc_readlink(path);
144 if (buf) { 144 if (target) {
145 unsigned len = strlen(buf); 145 /*
146 buf = xrealloc(buf, len + strlen(last_slash) + 2); 146 * $ ln -s /bin/qwe symlink # note: /bin is a link to /usr/bin
147 buf[len++] = '/'; 147 * $ readlink -f symlink
148 strcpy(buf + len, last_slash); 148 * /usr/bin/qwe
149 } 149 * $ realpath symlink
150 } else { 150 * /usr/bin/qwe
151 char *target = xmalloc_readlink(path); 151 */
152 if (target) { 152 if (target[0] != '/') {
153 char *cwd;
154 if (target[0] == '/') {
155 /*
156 * $ ln -s /bin/qwe symlink # note: /bin is a link to /usr/bin
157 * $ readlink -f symlink
158 * /usr/bin/qwe/target_does_not_exist
159 * $ realpath symlink
160 * /usr/bin/qwe/target_does_not_exist
161 */
162 buf = xmalloc_realpath_coreutils(target);
163 free(target);
164 return buf;
165 }
166 /* 153 /*
167 * $ ln -s target_does_not_exist symlink 154 * $ ln -s target_does_not_exist symlink
168 * $ readlink -f symlink 155 * $ readlink -f symlink
@@ -170,13 +157,41 @@ char* FAST_FUNC xmalloc_realpath_coreutils(const char *path)
170 * $ realpath symlink 157 * $ realpath symlink
171 * /CURDIR/target_does_not_exist 158 * /CURDIR/target_does_not_exist
172 */ 159 */
173 cwd = xrealloc_getcwd_or_warn(NULL); 160 char *cwd = xrealloc_getcwd_or_warn(NULL);
174 buf = concat_path_file(cwd, target); 161 char *tmp = concat_path_file(cwd, target);
175 free(cwd); 162 free(cwd);
176 free(target); 163 free(target);
177 return buf; 164 target = tmp;
165 }
166 buf = xmalloc_realpath_coreutils(target);
167 free(target);
168 return buf;
169 }
170
171 /* ignore leading and trailing slashes */
172 while (path[0] == '/' && path[1] == '/')
173 ++path;
174 i = strlen(path) - 1;
175 while (i > 0 && path[i] == '/')
176 i--;
177 c = path[i + 1];
178 path[i + 1] = '\0';
179
180 last_slash = strrchr(path, '/');
181 if (last_slash == path)
182 buf = xstrdup(path);
183 else if (last_slash) {
184 *last_slash = '\0';
185 buf = xmalloc_realpath(path);
186 *last_slash++ = '/';
187 if (buf) {
188 unsigned len = strlen(buf);
189 buf = xrealloc(buf, len + strlen(last_slash) + 2);
190 buf[len++] = '/';
191 strcpy(buf + len, last_slash);
178 } 192 }
179 } 193 }
194 path[i + 1] = c;
180 } 195 }
181 196
182 return buf; 197 return buf;
diff --git a/testsuite/realpath.tests b/testsuite/realpath.tests
new file mode 100755
index 000000000..0e68e0279
--- /dev/null
+++ b/testsuite/realpath.tests
@@ -0,0 +1,45 @@
1#!/bin/sh
2
3# Realpath tests.
4# Copyright 2006 by Natanael Copa <n@tanael.org>
5# Copyright 2021 by Ron Yorston <rmy@pobox.com>
6# Licensed under GPLv2, see file LICENSE in this source tree.
7
8. ./testing.sh
9
10unset LC_ALL
11unset LC_MESSAGES
12unset LANG
13unset LANGUAGE
14
15TESTDIR=realpath_testdir
16TESTLINK1="link1"
17TESTLINK2="link2"
18
19# create the dir and test files
20mkdir -p "./$TESTDIR"
21ln -s "./$TESTDIR/not_file" "./$TESTLINK1"
22ln -s "./$TESTDIR/not_file/not_dir" "./$TESTLINK2"
23
24# shell's $PWD may leave symlinks unresolved.
25# "pwd" may be a built-in and have the same problem.
26# External pwd _can't_ have that problem (current dir on Unix is physical).
27pwd=`which pwd`
28pwd=`$pwd`
29testing "realpath on non-existent absolute path 1" "realpath /not_file" "/not_file\n" "" ""
30testing "realpath on non-existent absolute path 2" "realpath /not_file/" "/not_file\n" "" ""
31testing "realpath on non-existent absolute path 3" "realpath //not_file" "/not_file\n" "" ""
32testing "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" "" ""
33
34testing "realpath on non-existent local file 1" "realpath $TESTDIR/not_file" "$pwd/$TESTDIR/not_file\n" "" ""
35testing "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" "" ""
36
37testing "realpath on link to non-existent file 1" "realpath $TESTLINK1" "$pwd/$TESTDIR/not_file\n" "" ""
38testing "realpath on link to non-existent file 2" "realpath $TESTLINK2 2>&1" "realpath: $TESTLINK2: No such file or directory\n" "" ""
39testing "realpath on link to non-existent file 3" "realpath ./$TESTLINK1" "$pwd/$TESTDIR/not_file\n" "" ""
40testing "realpath on link to non-existent file 4" "realpath ./$TESTLINK2 2>&1" "realpath: ./$TESTLINK2: No such file or directory\n" "" ""
41
42# clean up
43rm -r "$TESTLINK1" "$TESTLINK2" "$TESTDIR"
44
45exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))