diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp --- a/libcxx/src/filesystem/operations.cpp +++ b/libcxx/src/filesystem/operations.cpp @@ -636,20 +636,20 @@ ErrorHandler err("canonical", ec, &orig_p, &cwd); path p = __do_absolute(orig_p, &cwd, ec); -#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112 - std::unique_ptr - hold(::realpath(p.c_str(), nullptr), &::free); +#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || defined(_LIBCPP_WIN32API) + std::unique_ptr + hold(detail::realpath(p.c_str(), nullptr), &::free); if (hold.get() == nullptr) return err.report(capture_errno()); return {hold.get()}; #else #if defined(__MVS__) && !defined(PATH_MAX) - char buff[ _XOPEN_PATH_MAX + 1 ]; + path::value_type buff[ _XOPEN_PATH_MAX + 1 ]; #else - char buff[PATH_MAX + 1]; + path::value_type buff[PATH_MAX + 1]; #endif - char* ret; - if ((ret = ::realpath(p.c_str(), buff)) == nullptr) + path::value_type* ret; + if ((ret = detail::realpath(p.c_str(), buff)) == nullptr) return err.report(capture_errno()); return {ret}; #endif diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h --- a/libcxx/src/filesystem/posix_compat.h +++ b/libcxx/src/filesystem/posix_compat.h @@ -312,6 +312,43 @@ } wchar_t *getcwd(wchar_t *buff, size_t size) { return _wgetcwd(buff, size); } + +wchar_t *realpath(const wchar_t *path, wchar_t *resolved_name) { + // Only expected to be used with us allocating the buffer. + _LIBCPP_ASSERT(resolved_name == nullptr, + "Windows realpath() assumes a null resolved_name"); + + WinHandle h(path, FILE_READ_ATTRIBUTES, 0); + if (!h) { + set_errno(); + return nullptr; + } + size_t buff_size = MAX_PATH + 10; + std::unique_ptr buff( + static_cast(malloc(buff_size * sizeof(wchar_t))), &::free); + DWORD retval = GetFinalPathNameByHandleW( + h, buff.get(), buff_size, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + if (retval > buff_size) { + buff_size = retval; + buff.reset(static_cast(malloc(buff_size * sizeof(wchar_t)))); + retval = GetFinalPathNameByHandleW(h, buff.get(), buff_size, + FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + } + if (!retval) { + set_errno(); + return nullptr; + } + wchar_t *ptr = buff.get(); + if (!wcsncmp(ptr, L"\\\\?\\", 4)) { + if (ptr[5] == ':') { // \\?\X: -> X: + memmove(&ptr[0], &ptr[4], (wcslen(&ptr[4]) + 1) * sizeof(wchar_t)); + } else if (!wcsncmp(&ptr[4], L"UNC\\", 4)) { // \\?\UNC\server -> \\server + wcscpy(&ptr[0], L"\\\\"); + memmove(&ptr[2], &ptr[8], (wcslen(&ptr[8]) + 1) * sizeof(wchar_t)); + } + } + return buff.release(); +} #else int symlink_file(const char *oldname, const char *newname) { return ::symlink(oldname, newname); @@ -328,6 +365,7 @@ using ::lstat; using ::mkdir; using ::open; +using ::realpath; using ::remove; using ::rename; using ::stat;