From 4daf57b4fdc80422d6448c0a7914699fdedb95b8 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Wed, 13 Oct 2021 16:22:21 +0100 Subject: realpath: improved support for Windows paths Upstream commit 94eb1c4dc (libbb: better coreutils compatibility for realpath) made some changes to xmalloc_realpath_coreutils(). This now needs to be updated to handle Windows paths. - Expose the macro is_unc_path() and part of the recent change to bb_get_last_path_component_nostrip() as a separate funtion, get_last_slash(); - Convert a couple of errors relating to network filesystems to ENOENT; - Adjust xmalloc_realpath_coreutils() to handle Windows directory separators, relative paths and UNC paths. --- include/mingw.h | 3 +++ libbb/get_last_path_component.c | 23 ++++++++++++++++------- libbb/xreadlink.c | 34 ++++++++++++++++++++++++++++++++++ win32/mingw.c | 5 ++--- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/include/mingw.h b/include/mingw.h index 6ace91470..06fb3b289 100644 --- a/include/mingw.h +++ b/include/mingw.h @@ -518,6 +518,8 @@ int utimes(const char *file_name, const struct timeval times[2]); * MinGW specific */ #define is_dir_sep(c) ((c) == '/' || (c) == '\\') +#define is_unc_path(x) (strlen(x) > 4 && is_dir_sep(x[0]) && \ + is_dir_sep(x[1]) && !is_dir_sep(x[2])) pid_t FAST_FUNC mingw_spawn(char **argv); pid_t FAST_FUNC mingw_spawn_detach(char **argv); @@ -580,3 +582,4 @@ int skip_ansi_emulation(int reset); int unix_path(const char *path); int has_path(const char *file); int is_relative_path(const char *path); +char *get_last_slash(const char *path); diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c index 06033e139..254aabafd 100644 --- a/libbb/get_last_path_component.c +++ b/libbb/get_last_path_component.c @@ -23,6 +23,21 @@ const char* FAST_FUNC bb_basename(const char *name) return name; } +#if ENABLE_PLATFORM_MINGW32 +char *get_last_slash(const char *path) +{ + const char *start = path + root_len(path); + char *slash = strrchr(start, '/'); + char *bslash = strrchr(start, '\\'); + + if (slash && bslash) + slash = MAX(slash, bslash); + else if (!slash) + slash = bslash; + return slash; +} +#endif + /* * "/" -> "/" * "abc" -> "abc" @@ -33,13 +48,7 @@ char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path) { #if ENABLE_PLATFORM_MINGW32 const char *start = path + root_len(path); - char *slash = strrchr(start, '/'); - char *bslash = strrchr(start, '\\'); - - if (slash && bslash) - slash = MAX(slash, bslash); - else if (!slash) - slash = bslash; + char *slash = get_last_slash(path); if (!slash && has_dos_drive_prefix(path) && path[2] != '\0') return (char *)path + 2; diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c index 024ee9047..e6cf90310 100644 --- a/libbb/xreadlink.c +++ b/libbb/xreadlink.c @@ -156,7 +156,11 @@ char* FAST_FUNC xmalloc_realpath_coreutils(char *path) * $ realpath symlink * /usr/bin/qwe */ +#if ENABLE_PLATFORM_MINGW32 + if (is_relative_path(target)) { +#else if (target[0] != '/') { +#endif /* * $ ln -s target_does_not_exist symlink * $ readlink -f symlink @@ -175,6 +179,35 @@ char* FAST_FUNC xmalloc_realpath_coreutils(char *path) return buf; } +#if ENABLE_PLATFORM_MINGW32 + /* ignore leading and trailing slashes */ + /* but keep leading slashes of UNC path */ + if (!is_unc_path(path)) { + while (is_dir_sep(path[0]) && is_dir_sep(path[1])) + ++path; + } + i = strlen(path) - 1; + while (i > 0 && is_dir_sep(path[i])) + i--; + c = path[i + 1]; + path[i + 1] = '\0'; + + last_slash = get_last_slash(path); + if (last_slash == path + root_len(path)) + buf = xstrdup(path); + else if (last_slash) { + char c2 = *last_slash; + *last_slash = '\0'; + buf = xmalloc_realpath(path); + *last_slash++ = c2; + if (buf) { + unsigned len = strlen(buf); + buf = xrealloc(buf, len + strlen(last_slash) + 2); + buf[len++] = c2; + strcpy(buf + len, last_slash); + } + } +#else /* ignore leading and trailing slashes */ while (path[0] == '/' && path[1] == '/') ++path; @@ -198,6 +231,7 @@ char* FAST_FUNC xmalloc_realpath_coreutils(char *path) strcpy(buf + len, last_slash); } } +#endif path[i + 1] = c; } diff --git a/win32/mingw.c b/win32/mingw.c index fa68ea608..d4855a7e7 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -64,6 +64,8 @@ int err_win_to_posix(void) case ERROR_BAD_FORMAT: error = ENOEXEC; break; case ERROR_BAD_LENGTH: error = EINVAL; break; case ERROR_BAD_PATHNAME: error = ENOENT; break; + case ERROR_BAD_NET_NAME: error = ENOENT; break; + case ERROR_BAD_NETPATH: error = ENOENT; break; case ERROR_BAD_PIPE: error = EPIPE; break; case ERROR_BAD_UNIT: error = ENODEV; break; case ERROR_BAD_USERNAME: error = EINVAL; break; @@ -1877,9 +1879,6 @@ void hide_console(void) } #endif -#define is_unc_path(x) (strlen(x) > 4 && is_dir_sep(x[0]) && \ - is_dir_sep(x[1]) && !is_dir_sep(x[2])) - /* Return the length of the root of a UNC path, i.e. the '//host/share' * component, or 0 if the path doesn't look like that. */ int unc_root_len(const char *dir) -- cgit v1.2.3-55-g6feb