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 { @@ -1146,10 +1177,13 @@ if (ec && *ec) { return; } - // NOTE: proposal says you should detect if you should call - // create_symlink or create_directory_symlink. I don't think this - // is needed with POSIX - __create_symlink(real_path, new_symlink, ec); +#if defined(_LIBCPP_WIN32API) + error_code local_ec; + if (is_directory(real_path, local_ec)) + __create_directory_symlink(real_path, new_symlink, ec); + else +#endif + __create_symlink(real_path, new_symlink, ec); } bool __create_directories(const path& p, error_code* ec) { @@ -1457,6 +1491,31 @@ 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 + sizeof(wchar_t)]; + 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())); + LIBCPP_REPARSE_DATA_BUFFER *reparse = + reinterpret_cast(buf); + if (reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK) + return err.report(make_error_code(errc::invalid_argument)); + auto &symlink = reparse->SymbolicLinkReparseBuffer; + if (symlink.PrintNameLength == 0) { + return {wstring( + &symlink.PathBuffer[symlink.SubstituteNameOffset / sizeof(wchar_t)], + symlink.SubstituteNameLength / sizeof(wchar_t))}; + } else { + return { + wstring(&symlink.PathBuffer[symlink.PrintNameOffset / sizeof(wchar_t)], + symlink.PrintNameLength / sizeof(wchar_t))}; + } +#else #ifdef PATH_MAX struct NullDeleter { void operator()(void*) const {} }; const size_t size = PATH_MAX + 1; @@ -1478,6 +1537,7 @@ return err.report(errc::value_too_large); buff[ret] = 0; return {buff.get()}; +#endif } bool __remove(const path& p, error_code* ec) {