diff options
-rw-r--r-- | include/libbb.h | 2 | ||||
-rw-r--r-- | libbb/xreadlink.c | 75 | ||||
-rwxr-xr-x | testsuite/realpath.tests | 45 |
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; | |||
578 | DIR *warn_opendir(const char *path) FAST_FUNC; | 578 | DIR *warn_opendir(const char *path) FAST_FUNC; |
579 | 579 | ||
580 | char *xmalloc_realpath(const char *path) FAST_FUNC RETURNS_MALLOC; | 580 | char *xmalloc_realpath(const char *path) FAST_FUNC RETURNS_MALLOC; |
581 | char *xmalloc_realpath_coreutils(const char *path) FAST_FUNC RETURNS_MALLOC; | 581 | char *xmalloc_realpath_coreutils(char *path) FAST_FUNC RETURNS_MALLOC; |
582 | char *xmalloc_readlink(const char *path) FAST_FUNC RETURNS_MALLOC; | 582 | char *xmalloc_readlink(const char *path) FAST_FUNC RETURNS_MALLOC; |
583 | char *xmalloc_readlink_or_warn(const char *path) FAST_FUNC RETURNS_MALLOC; | 583 | char *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 | ||
126 | char* FAST_FUNC xmalloc_realpath_coreutils(const char *path) | 126 | char* 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 | |||
10 | unset LC_ALL | ||
11 | unset LC_MESSAGES | ||
12 | unset LANG | ||
13 | unset LANGUAGE | ||
14 | |||
15 | TESTDIR=realpath_testdir | ||
16 | TESTLINK1="link1" | ||
17 | TESTLINK2="link2" | ||
18 | |||
19 | # create the dir and test files | ||
20 | mkdir -p "./$TESTDIR" | ||
21 | ln -s "./$TESTDIR/not_file" "./$TESTLINK1" | ||
22 | ln -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). | ||
27 | pwd=`which pwd` | ||
28 | pwd=`$pwd` | ||
29 | testing "realpath on non-existent absolute path 1" "realpath /not_file" "/not_file\n" "" "" | ||
30 | testing "realpath on non-existent absolute path 2" "realpath /not_file/" "/not_file\n" "" "" | ||
31 | testing "realpath on non-existent absolute path 3" "realpath //not_file" "/not_file\n" "" "" | ||
32 | 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" "" "" | ||
33 | |||
34 | testing "realpath on non-existent local file 1" "realpath $TESTDIR/not_file" "$pwd/$TESTDIR/not_file\n" "" "" | ||
35 | 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" "" "" | ||
36 | |||
37 | testing "realpath on link to non-existent file 1" "realpath $TESTLINK1" "$pwd/$TESTDIR/not_file\n" "" "" | ||
38 | testing "realpath on link to non-existent file 2" "realpath $TESTLINK2 2>&1" "realpath: $TESTLINK2: No such file or directory\n" "" "" | ||
39 | testing "realpath on link to non-existent file 3" "realpath ./$TESTLINK1" "$pwd/$TESTDIR/not_file\n" "" "" | ||
40 | testing "realpath on link to non-existent file 4" "realpath ./$TESTLINK2 2>&1" "realpath: ./$TESTLINK2: No such file or directory\n" "" "" | ||
41 | |||
42 | # clean up | ||
43 | rm -r "$TESTLINK1" "$TESTLINK2" "$TESTDIR" | ||
44 | |||
45 | exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255)) | ||