Index: libcxx/src/filesystem/operations.cpp =================================================================== --- libcxx/src/filesystem/operations.cpp +++ libcxx/src/filesystem/operations.cpp @@ -22,6 +22,7 @@ #define NOMINMAX #include #include +#include #else #include #include @@ -49,6 +50,36 @@ # pragma comment(lib, "rt") #endif +#if defined(_LIBCPP_WIN32API) +// This struct isn't defined in the normal Windows SDK, but only in the +// Windows Driver Kit. +struct LIBCPP_REPARSE_DATA_BUFFER { + unsigned long ReparseTag; + unsigned short ReparseDataLength; + unsigned short Reserved; + union { + struct { + unsigned short SubstituteNameOffset; + unsigned short SubstituteNameLength; + unsigned short PrintNameOffset; + unsigned short PrintNameLength; + unsigned long Flags; + wchar_t PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + unsigned short SubstituteNameOffset; + unsigned short SubstituteNameLength; + unsigned short PrintNameOffset; + unsigned short PrintNameLength; + wchar_t PathBuffer[1]; + } MountPointReparseBuffer; + struct { + unsigned char DataBuffer[1]; + } GenericReparseBuffer; + }; +}; +#endif + _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM namespace { @@ -1475,6 +1506,38 @@ path __read_symlink(const path& p, error_code* ec) { ErrorHandler err("read_symlink", ec, &p); +#if defined(_LIBCPP_WIN32API) + uint8_t buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + detail::WinHandle h(p.c_str(), FILE_READ_ATTRIBUTES, + FILE_FLAG_OPEN_REPARSE_POINT); + if (!h) + return err.report(detail::make_windows_error(GetLastError())); + DWORD out; + if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf, sizeof(buf), + &out, 0)) + return err.report(detail::make_windows_error(GetLastError())); + const auto *reparse = reinterpret_cast(buf); + size_t path_buf_offset = offsetof(LIBCPP_REPARSE_DATA_BUFFER, + SymbolicLinkReparseBuffer.PathBuffer[0]); + if (out < path_buf_offset) + return err.report(make_error_code(errc::invalid_argument)); + if (reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK) + return err.report(make_error_code(errc::invalid_argument)); + const auto &symlink = reparse->SymbolicLinkReparseBuffer; + unsigned short name_offset, name_length; + if (symlink.PrintNameLength == 0) { + name_offset = symlink.SubstituteNameOffset; + name_length = symlink.SubstituteNameLength; + } else { + name_offset = symlink.PrintNameOffset; + name_length = symlink.PrintNameLength; + } + // name_offset/length are expressed in bytes, not in wchar_t + if (path_buf_offset + name_offset + name_length > out) + return err.report(make_error_code(errc::invalid_argument)); + return {wstring(&symlink.PathBuffer[name_offset / sizeof(wchar_t)], + name_length / sizeof(wchar_t))}; +#else #ifdef PATH_MAX struct NullDeleter { void operator()(void*) const {} }; const size_t size = PATH_MAX + 1; @@ -1496,6 +1559,7 @@ return err.report(errc::value_too_large); buff[ret] = 0; return {buff.get()}; +#endif } bool __remove(const path& p, error_code* ec) {