[newlib-cygwin] Cygwin: symlinks: support WSL symlinks

Corinna Vinschen corinna@sourceware.org
Fri Mar 27 11:12:38 GMT 2020


https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=4a36897af392c3bc0805f4802246b5d098ed19b7

commit 4a36897af392c3bc0805f4802246b5d098ed19b7
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Fri Mar 27 12:12:31 2020 +0100

    Cygwin: symlinks: support WSL symlinks
    
    Treat WSL symlinks just like other symlinks.  Convert
    absolute paths pointing to Windows drives via
    /mnt/<driveletter> to Windows-style paths <driveletter>:
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/path.cc       | 73 +++++++++++++++++++++++++++++++++++++++++++++
 winsup/cygwin/release/3.1.5 |  6 ++++
 winsup/doc/new-features.xml |  4 +++
 3 files changed, 83 insertions(+)

diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index bda3ca1b5..7fa2bc5b5 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -2360,6 +2360,29 @@ check_reparse_point_string (PUNICODE_STRING subst)
   return false;
 }
 
+#ifndef IO_REPARSE_TAG_LX_SYMLINK
+#define IO_REPARSE_TAG_LX_SYMLINK	(0xa000001d)
+#endif
+
+typedef struct _REPARSE_LX_SYMLINK_BUFFER
+{
+  DWORD	ReparseTag;
+  WORD	ReparseDataLength;
+  WORD	Reserved;
+  struct {
+    DWORD FileType;	/* Take member name with a grain of salt.  Value is
+			   apparently always 2 for symlinks. */
+    char  PathBuffer[1];/* POSIX path as given to symlink(2).
+			   Path is not \0 terminated.
+			   Length is ReparseDataLength - sizeof (FileType).
+			   Always UTF-8.
+			   Chars given in incompatible codesets, e. g. umlauts
+			   in ISO-8859-x, are converted to the Unicode
+			   REPLACEMENT CHARACTER 0xfffd == \xef\xbf\bd */
+  } LxSymlinkReparseBuffer;
+} REPARSE_LX_SYMLINK_BUFFER,*PREPARSE_LX_SYMLINK_BUFFER;
+
+
 /* Return values:
     <0: Negative errno.
      0: No symlink.
@@ -2434,6 +2457,56 @@ check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
       if (check_reparse_point_string (psymbuf))
 	return PATH_SYMLINK | PATH_REP;
     }
+  else if (rp->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK)
+    {
+      /* WSL symlink.  Problem: We have to convert the path to UTF-16 for
+	 the caller.  Reparse points are 16K max.  The buffer given to rp
+	 is 32K.  So there's enough trailing space in the buffer to convert
+	 to UTF-16 and let psymbuf point to it. */
+      PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) rp;
+      char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
+      DWORD path_len = rpl->ReparseDataLength - sizeof (DWORD);
+      PBYTE utf16_ptr;
+      PWCHAR utf16_buf;
+      int utf16_bufsize;
+      bool full_path = false;
+      const size_t drv_prefix_len = strlen ("/mnt");
+
+      /* Compute buffer for path converted to UTF-16. */
+      utf16_ptr = (PBYTE) rpl + sizeof (REPARSE_LX_SYMLINK_BUFFER)
+		  + rp->ReparseDataLength;
+      while ((intptr_t) utf16_ptr % sizeof (WCHAR))
+	++utf16_ptr;
+      utf16_buf = (PWCHAR) utf16_ptr;
+      utf16_bufsize = NT_MAX_PATH - (utf16_buf - (PWCHAR) rpl);
+      /* Check for abs path /mnt/x. Convert to x: after conversion to UTF-16. */
+      if (path_len >= drv_prefix_len + 2
+	  && !strncmp (path_buf, "/mnt/", drv_prefix_len + 1)
+	  && islower (path_buf[drv_prefix_len + 1])
+	  && (path_len == drv_prefix_len + 2
+	      || path_buf[drv_prefix_len + 2] == '/'))
+	{
+	  /* Skip forward to the slash leading the drive letter.  That leaves
+	     room for adding the colon. */
+	  path_buf += drv_prefix_len;
+	  path_len -= drv_prefix_len;
+	  full_path = true;
+	}
+      utf16_bufsize = MultiByteToWideChar (CP_UTF8, 0, path_buf, path_len,
+					  utf16_buf, utf16_bufsize);
+      if (utf16_bufsize)
+	{
+	  if (full_path)
+	    {
+	      utf16_buf[0] = utf16_buf[1];	/* Move drive letter to front */
+	      utf16_buf[1] = L':';		/* Add colon */
+	    }
+	  RtlInitCountedUnicodeString (psymbuf, utf16_buf,
+				       utf16_bufsize * sizeof (WCHAR));
+	  return PATH_SYMLINK | PATH_REP;
+	}
+      return -EIO;
+    }
 #ifdef __WITH_AF_UNIX
   else if (rp->ReparseTag == IO_REPARSE_TAG_CYGUNIX)
     {
diff --git a/winsup/cygwin/release/3.1.5 b/winsup/cygwin/release/3.1.5
index e567ecb32..6f16aad93 100644
--- a/winsup/cygwin/release/3.1.5
+++ b/winsup/cygwin/release/3.1.5
@@ -1,3 +1,9 @@
+What changed:
+-------------
+
+- Support WSL symlinks.
+
+
 Bug Fixes:
 ----------
 
diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml
index 9086302d3..a200300f2 100644
--- a/winsup/doc/new-features.xml
+++ b/winsup/doc/new-features.xml
@@ -82,6 +82,10 @@ be supported by upcoming mintty releases.  For the reasoning, see
 https://gitlab.freedesktop.org/terminal-wg/specifications/issues/9.
 </para></listitem>
 
+<listitem><para>
+Support WSL symlinks.
+</para></listitem>
+
 </itemizedlist>
 
 </sect2>


More information about the Cygwin-cvs mailing list