aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-07-31 13:51:57 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2021-10-09 01:47:12 +0200
commit94eb1c4dc6556932e1a12a0ce7734512ac95985e (patch)
tree610126c63688ebd23617d011912f8d6dd6972973
parent86ba007b84ae1ebe35e88c57e023caac3d2d9903 (diff)
downloadbusybox-w32-94eb1c4dc6556932e1a12a0ce7734512ac95985e.tar.gz
busybox-w32-94eb1c4dc6556932e1a12a0ce7734512ac95985e.tar.bz2
busybox-w32-94eb1c4dc6556932e1a12a0ce7734512ac95985e.zip
libbb: better coreutils compatibility for realpath
Add some tests which coreutils realpath pass but BusyBox realpath fails (bar one). Adjust xmalloc_realpath_coreutils() so the tests pass: - Expand symbolic links before testing whether the last path component exists. - When the link target is a relative path canonicalize it by passing it through xmalloc_realpath_coreutils() as already happens for absolute paths. - Ignore trailing slashes when finding the last path component and correctly handle the case where the only slash is at the start of the path. This requires ignoring superfluous leading slashes. - Undo all changes to the path so error messages from the caller show the original filename. function old new delta xmalloc_realpath_coreutils 214 313 +99 Signed-off-by: Ron Yorston <rmy@pobox.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-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))