From 5c5197df8668ba58ec7799ccc418d124a6c6a49e Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Wed, 6 Mar 2019 10:05:06 +0000 Subject: win32: implement readlink(2) Provide an implementation of readlink(2) based on code from Git for Windows. This version only supports symbolic links, not mount points, as the latter seem to work well enough as-is. With this change the ls and stat applets can display the targets of symbolic links. The readlink applet has been enabled in the default configuration. --- Config.in | 10 ++++++++ configs/mingw32_defconfig | 8 +++--- configs/mingw64_defconfig | 8 +++--- coreutils/ls.c | 2 +- coreutils/stat.c | 4 +-- include/mingw.h | 4 +++ win32/mingw.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 92 insertions(+), 9 deletions(-) diff --git a/Config.in b/Config.in index 2b4b81515..100f6e491 100644 --- a/Config.in +++ b/Config.in @@ -473,6 +473,16 @@ config FEATURE_IDENTIFY_OWNER Try to determine if files belong to the current user. If they don't they're listed as belonging to root. +config FEATURE_READLINK2 + bool "Read the contents of symbolic links (1.1 kb)" + default y + depends on PLATFORM_MINGW32 + help + Implement the readlink(2) system call to allow applets to read + the contents of symbolic links. With this feature ls and stat + can display the target of symbolic links and it makes sense to + enable the readlink applet. + comment 'Build Options' config STATIC diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig index c30ffa1b5..a29cc164a 100644 --- a/configs/mingw32_defconfig +++ b/configs/mingw32_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Busybox version: 1.31.0.git -# Fri Mar 1 08:50:17 2019 +# Wed Mar 6 09:32:59 2019 # CONFIG_HAVE_DOT_CONFIG=y # CONFIG_PLATFORM_POSIX is not set @@ -50,6 +50,7 @@ CONFIG_FEATURE_ICON_ALL=y CONFIG_FEATURE_EURO=y CONFIG_FEATURE_EXTRA_FILE_DATA=y CONFIG_FEATURE_IDENTIFY_OWNER=y +CONFIG_FEATURE_READLINK2=y # # Build Options @@ -295,8 +296,8 @@ CONFIG_PASTE=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y -# CONFIG_READLINK is not set -# CONFIG_FEATURE_READLINK_FOLLOW is not set +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y @@ -1113,6 +1114,7 @@ CONFIG_ASH_HELP=y CONFIG_ASH_GETOPTS=y CONFIG_ASH_CMDCMD=y CONFIG_ASH_NOCONSOLE=y +CONFIG_ASH_NOCASEGLOB=y # CONFIG_CTTYHACK is not set # CONFIG_HUSH is not set # CONFIG_HUSH_BASH_COMPAT is not set diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index 46cb6941f..3d6102f0a 100644 --- a/configs/mingw64_defconfig +++ b/configs/mingw64_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Busybox version: 1.31.0.git -# Fri Mar 1 08:50:17 2019 +# Wed Mar 6 09:32:59 2019 # CONFIG_HAVE_DOT_CONFIG=y # CONFIG_PLATFORM_POSIX is not set @@ -50,6 +50,7 @@ CONFIG_FEATURE_ICON_ALL=y CONFIG_FEATURE_EURO=y CONFIG_FEATURE_EXTRA_FILE_DATA=y CONFIG_FEATURE_IDENTIFY_OWNER=y +CONFIG_FEATURE_READLINK2=y # # Build Options @@ -295,8 +296,8 @@ CONFIG_PASTE=y CONFIG_PRINTENV=y CONFIG_PRINTF=y CONFIG_PWD=y -# CONFIG_READLINK is not set -# CONFIG_FEATURE_READLINK_FOLLOW is not set +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y @@ -1113,6 +1114,7 @@ CONFIG_ASH_HELP=y CONFIG_ASH_GETOPTS=y CONFIG_ASH_CMDCMD=y CONFIG_ASH_NOCONSOLE=y +CONFIG_ASH_NOCASEGLOB=y # CONFIG_CTTYHACK is not set # CONFIG_HUSH is not set # CONFIG_HUSH_BASH_COMPAT is not set diff --git a/coreutils/ls.c b/coreutils/ls.c index c5402de30..6be3eb291 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c @@ -492,7 +492,7 @@ static NOINLINE unsigned display_single(const struct dnode *dn) /* Do readlink early, so that if it fails, error message * does not appear *inside* the "ls -l" line */ lpath = NULL; -#if !ENABLE_PLATFORM_MINGW32 +#if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_READLINK2 if (opt & OPT_l) if (S_ISLNK(dn->dn_mode)) lpath = xmalloc_readlink_or_warn(dn->fullname); diff --git a/coreutils/stat.c b/coreutils/stat.c index 32bc5e2d3..cf13af0b6 100644 --- a/coreutils/stat.c +++ b/coreutils/stat.c @@ -317,7 +317,7 @@ static void FAST_FUNC print_stat(char *pformat, const char m, printfs(pformat, filename); } else if (m == 'N') { strcatc(pformat, 's'); -#if !ENABLE_PLATFORM_MINGW32 +#if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_READLINK2 if (S_ISLNK(statbuf->st_mode)) { char *linkname = xmalloc_readlink_or_warn(filename); if (linkname == NULL) @@ -712,7 +712,7 @@ static bool do_stat(const char *filename, const char *format) gw_ent = getgrgid(statbuf.st_gid); pw_ent = getpwuid(statbuf.st_uid); -#if !ENABLE_PLATFORM_MINGW32 +#if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_READLINK2 if (S_ISLNK(statbuf.st_mode)) linkname = xmalloc_readlink_or_warn(filename); if (linkname) { diff --git a/include/mingw.h b/include/mingw.h index 188d4eecf..b5324e49b 100644 --- a/include/mingw.h +++ b/include/mingw.h @@ -407,7 +407,11 @@ int mingw_xopen(const char *filename, int oflags); ssize_t mingw_read(int fd, void *buf, size_t count); int mingw_close(int fd); int pipe(int filedes[2]); +#if ENABLE_FEATURE_READLINK2 +ssize_t readlink(const char *pathname, char *buf, size_t bufsiz); +#else NOIMPL(readlink,const char *path UNUSED_PARAM, char *buf UNUSED_PARAM, size_t bufsiz UNUSED_PARAM); +#endif NOIMPL(setgid,gid_t gid UNUSED_PARAM); NOIMPL(setegid,gid_t gid UNUSED_PARAM); NOIMPL(setsid,void); diff --git a/win32/mingw.c b/win32/mingw.c index dda2e9a60..c42dbed97 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -4,6 +4,9 @@ #if ENABLE_FEATURE_IDENTIFY_OWNER #include #endif +#if ENABLE_FEATURE_READLINK2 +#include +#endif #if defined(__MINGW64_VERSION_MAJOR) #if ENABLE_GLOBBING @@ -1060,6 +1063,68 @@ char *realpath(const char *path, char *resolved_path) return NULL; } +#if ENABLE_FEATURE_READLINK2 +static wchar_t *normalize_ntpath(wchar_t *wbuf) +{ + int i; + /* fix absolute path prefixes */ + if (wbuf[0] == '\\') { + /* strip NT namespace prefixes */ + if (!wcsncmp(wbuf, L"\\??\\", 4) || + !wcsncmp(wbuf, L"\\\\?\\", 4)) + wbuf += 4; + else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12)) + wbuf += 12; + /* replace remaining '...UNC\' with '\\' */ + if (!wcsnicmp(wbuf, L"UNC\\", 4)) { + wbuf += 2; + *wbuf = '\\'; + } + } + /* convert backslashes to slashes */ + for (i = 0; wbuf[i]; i++) + if (wbuf[i] == '\\') + wbuf[i] = '/'; + return wbuf; +} + +#define SRPB rptr->SymbolicLinkReparseBuffer +ssize_t readlink(const char *pathname, char *buf, size_t bufsiz) +{ + HANDLE h; + + h = CreateFile(pathname, 0, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (h != INVALID_HANDLE_VALUE) { + DWORD nbytes; + BYTE rbuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + PREPARSE_DATA_BUFFER rptr = (PREPARSE_DATA_BUFFER)rbuf; + BOOL status; + + status = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, + rptr, sizeof(rbuf), &nbytes, NULL); + CloseHandle(h); + + if (status && rptr->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + size_t len = SRPB.SubstituteNameLength/sizeof(WCHAR); + WCHAR *name = SRPB.PathBuffer + + SRPB.SubstituteNameOffset/sizeof(WCHAR); + + name[len] = 0; + name = normalize_ntpath(name); + len = wcslen(name); + if (len > bufsiz) + len = bufsiz; + if (WideCharToMultiByte(CP_ACP, 0, name, len, buf, bufsiz, 0, 0)) { + return len; + } + } + } + errno = err_win_to_posix(); + return -1; +} +#endif + const char *get_busybox_exec_path(void) { static char *path = NULL; -- cgit v1.2.3-55-g6feb