[libcxx-commits] [libcxx] cdc60a3 - [libcxx] Implement the read_symlink function for windows
Martin Storsjö via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Feb 2 23:28:20 PST 2021
Author: Martin Storsjö
Date: 2021-02-03T09:23:58+02:00
New Revision: cdc60a3b9aa523b49329a7a5e4c1774d3b9e3db9
URL: https://github.com/llvm/llvm-project/commit/cdc60a3b9aa523b49329a7a5e4c1774d3b9e3db9
DIFF: https://github.com/llvm/llvm-project/commit/cdc60a3b9aa523b49329a7a5e4c1774d3b9e3db9.diff
LOG: [libcxx] Implement the read_symlink function for windows
Differential Revision: https://reviews.llvm.org/D91172
Added:
Modified:
libcxx/src/filesystem/operations.cpp
libcxx/src/filesystem/posix_compat.h
Removed:
################################################################################
diff --git a/libcxx/src/filesystem/operations.cpp b/libcxx/src/filesystem/operations.cpp
index 47cd5e23c092..a5463e48f4d7 100644
--- a/libcxx/src/filesystem/operations.cpp
+++ b/libcxx/src/filesystem/operations.cpp
@@ -1227,21 +1227,25 @@ void __permissions(const path& p, perms prms, perm_options opts,
path __read_symlink(const path& p, error_code* ec) {
ErrorHandler<path> err("read_symlink", ec, &p);
-#ifdef PATH_MAX
+#if defined(PATH_MAX) || defined(MAX_SYMLINK_SIZE)
struct NullDeleter { void operator()(void*) const {} };
+#ifdef MAX_SYMLINK_SIZE
+ const size_t size = MAX_SYMLINK_SIZE + 1;
+#else
const size_t size = PATH_MAX + 1;
- char stack_buff[size];
- auto buff = std::unique_ptr<char[], NullDeleter>(stack_buff);
+#endif
+ path::value_type stack_buff[size];
+ auto buff = std::unique_ptr<path::value_type[], NullDeleter>(stack_buff);
#else
StatT sb;
if (detail::lstat(p.c_str(), &sb) == -1) {
return err.report(capture_errno());
}
const size_t size = sb.st_size + 1;
- auto buff = unique_ptr<char[]>(new char[size]);
+ auto buff = unique_ptr<path::value_type[]>(new path::value_type[size]);
#endif
- ::ssize_t ret;
- if ((ret = ::readlink(p.c_str(), buff.get(), size)) == -1)
+ detail::SSizeT ret;
+ if ((ret = detail::readlink(p.c_str(), buff.get(), size)) == -1)
return err.report(capture_errno());
_LIBCPP_ASSERT(ret > 0, "TODO");
if (static_cast<size_t>(ret) >= size)
diff --git a/libcxx/src/filesystem/posix_compat.h b/libcxx/src/filesystem/posix_compat.h
index af4a3691db33..8062bd65ce00 100644
--- a/libcxx/src/filesystem/posix_compat.h
+++ b/libcxx/src/filesystem/posix_compat.h
@@ -32,6 +32,7 @@
# define NOMINMAX
# include <windows.h>
# include <io.h>
+# include <winioctl.h>
#else
# include <unistd.h>
# include <sys/stat.h>
@@ -39,6 +40,36 @@
#endif
#include <time.h>
+#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 detail {
@@ -400,6 +431,52 @@ int fchmod(int fd, int perms) {
return fchmod_handle(h, perms);
}
+#define MAX_SYMLINK_SIZE MAXIMUM_REPARSE_DATA_BUFFER_SIZE
+using SSizeT = ::int64_t;
+
+SSizeT readlink(const wchar_t *path, wchar_t *ret_buf, size_t bufsize) {
+ uint8_t buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ detail::WinHandle h(path, FILE_READ_ATTRIBUTES, FILE_FLAG_OPEN_REPARSE_POINT);
+ if (!h)
+ return set_errno();
+ DWORD out;
+ if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf, sizeof(buf),
+ &out, 0))
+ return set_errno();
+ const auto *reparse = reinterpret_cast<LIBCPP_REPARSE_DATA_BUFFER *>(buf);
+ size_t path_buf_offset = offsetof(LIBCPP_REPARSE_DATA_BUFFER,
+ SymbolicLinkReparseBuffer.PathBuffer[0]);
+ if (out < path_buf_offset) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+ errno = EINVAL;
+ return -1;
+ }
+ 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) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (name_length / sizeof(wchar_t) > bufsize) {
+ errno = ENOMEM;
+ return -1;
+ }
+ memcpy(ret_buf, &symlink.PathBuffer[name_offset / sizeof(wchar_t)],
+ name_length);
+ return name_length / sizeof(wchar_t);
+}
+
#else
int symlink_file(const char *oldname, const char *newname) {
return ::symlink(oldname, newname);
@@ -418,6 +495,7 @@ using ::link;
using ::lstat;
using ::mkdir;
using ::open;
+using ::readlink;
using ::realpath;
using ::remove;
using ::rename;
@@ -429,6 +507,7 @@ using ::truncate;
using StatVFS = struct statvfs;
using ModeT = ::mode_t;
+using SSizeT = ::ssize_t;
#endif
More information about the libcxx-commits
mailing list