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>
This commit is contained in:
Corinna Vinschen 2020-03-27 12:12:31 +01:00 committed by Ken Brown
parent f2b68dd7de
commit 31c3c0e306
3 changed files with 83 additions and 0 deletions

View File

@ -2360,6 +2360,29 @@ check_reparse_point_string (PUNICODE_STRING subst)
return false; 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: /* Return values:
<0: Negative errno. <0: Negative errno.
0: No symlink. 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)) if (check_reparse_point_string (psymbuf))
return PATH_SYMLINK | PATH_REP; 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 #ifdef __WITH_AF_UNIX
else if (rp->ReparseTag == IO_REPARSE_TAG_CYGUNIX) else if (rp->ReparseTag == IO_REPARSE_TAG_CYGUNIX)
{ {

View File

@ -1,3 +1,9 @@
What changed:
-------------
- Support WSL symlinks.
Bug Fixes: Bug Fixes:
---------- ----------

View File

@ -82,6 +82,10 @@ be supported by upcoming mintty releases. For the reasoning, see
https://gitlab.freedesktop.org/terminal-wg/specifications/issues/9. https://gitlab.freedesktop.org/terminal-wg/specifications/issues/9.
</para></listitem> </para></listitem>
<listitem><para>
Support WSL symlinks.
</para></listitem>
</itemizedlist> </itemizedlist>
</sect2> </sect2>