Cygwin: symlinks: create WSL symlinks on supporting filesystems
WSL symlinks are reparse points containing a POSIX path in UTF-8. On filesystems supporting reparse points, use this symlink type. On other filesystems, or in case of error, fall back to the good old plain SYSTEM file. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
parent
fb834beebe
commit
44da5e4b8c
|
@ -1845,6 +1845,97 @@ symlink_native (const char *oldpath, path_conv &win32_newpath)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
static int
|
||||||
|
symlink_wsl (const char *oldpath, path_conv &win32_newpath)
|
||||||
|
{
|
||||||
|
tmp_pathbuf tp;
|
||||||
|
PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) tp.c_get ();
|
||||||
|
char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
|
||||||
|
const int max_pathlen = MAXIMUM_REPARSE_DATA_BUFFER_SIZE
|
||||||
|
- offsetof (REPARSE_LX_SYMLINK_BUFFER,
|
||||||
|
LxSymlinkReparseBuffer.PathBuffer);
|
||||||
|
PWCHAR utf16 = tp.w_get ();
|
||||||
|
NTSTATUS status;
|
||||||
|
IO_STATUS_BLOCK io;
|
||||||
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
HANDLE fh;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
rpl->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
|
||||||
|
rpl->Reserved = 0;
|
||||||
|
rpl->LxSymlinkReparseBuffer.FileType = 2;
|
||||||
|
/* Convert cygdrive prefix to "/mnt" for WSL compatibility. */
|
||||||
|
if (path_prefix_p (mount_table->cygdrive, oldpath,
|
||||||
|
mount_table->cygdrive_len, false))
|
||||||
|
stpcpy (stpcpy (path_buf, "/mnt"),
|
||||||
|
oldpath + mount_table->cygdrive_len - 1);
|
||||||
|
else
|
||||||
|
*stpncpy (path_buf, oldpath, max_pathlen) = '\0';
|
||||||
|
/* Convert target path to UTF-16 and then back to UTF-8 to make sure the
|
||||||
|
WSL symlink is in UTF-8, independet of the current Cygwin codeset. */
|
||||||
|
sys_mbstowcs (utf16, NT_MAX_PATH, path_buf);
|
||||||
|
len = WideCharToMultiByte (CP_UTF8, 0, utf16, -1, path_buf, max_pathlen,
|
||||||
|
NULL, NULL);
|
||||||
|
/* Length is omitting trailing \0. */
|
||||||
|
rpl->ReparseDataLength = sizeof (DWORD) + len - 1;
|
||||||
|
/* Create reparse point. */
|
||||||
|
status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE
|
||||||
|
| READ_CONTROL | WRITE_DAC,
|
||||||
|
win32_newpath.get_object_attr (attr, sec_none_nih),
|
||||||
|
&io, NULL, FILE_ATTRIBUTE_NORMAL,
|
||||||
|
FILE_SHARE_VALID_FLAGS, FILE_CREATE,
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT
|
||||||
|
| FILE_NON_DIRECTORY_FILE
|
||||||
|
| FILE_OPEN_FOR_BACKUP_INTENT
|
||||||
|
| FILE_OPEN_REPARSE_POINT,
|
||||||
|
NULL, 0);
|
||||||
|
if (!NT_SUCCESS (status))
|
||||||
|
{
|
||||||
|
SetLastError (RtlNtStatusToDosError (status));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
set_created_file_access (fh, win32_newpath, S_IFLNK | STD_RBITS | STD_WBITS);
|
||||||
|
status = NtFsControlFile (fh, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT,
|
||||||
|
(LPVOID) rpl, REPARSE_DATA_BUFFER_HEADER_SIZE
|
||||||
|
+ rpl->ReparseDataLength,
|
||||||
|
NULL, 0);
|
||||||
|
if (!NT_SUCCESS (status))
|
||||||
|
{
|
||||||
|
SetLastError (RtlNtStatusToDosError (status));
|
||||||
|
FILE_DISPOSITION_INFORMATION fdi = { TRUE };
|
||||||
|
status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
|
||||||
|
FileDispositionInformation);
|
||||||
|
NtClose (fh);
|
||||||
|
if (!NT_SUCCESS (status))
|
||||||
|
debug_printf ("Setting delete dispostion failed, status = %y", status);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NtClose (fh);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
|
symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
|
||||||
{
|
{
|
||||||
|
@ -1908,7 +1999,7 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
|
||||||
__leave;
|
__leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle NFS and native symlinks in their own functions. */
|
/* Handle NFS, native symlinks and WSL symlinks in their own functions. */
|
||||||
switch (wsym_type)
|
switch (wsym_type)
|
||||||
{
|
{
|
||||||
case WSYM_nfs:
|
case WSYM_nfs:
|
||||||
|
@ -1928,6 +2019,17 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
|
||||||
}
|
}
|
||||||
/* Otherwise, fall back to default symlink type. */
|
/* Otherwise, fall back to default symlink type. */
|
||||||
wsym_type = WSYM_sysfile;
|
wsym_type = WSYM_sysfile;
|
||||||
|
/*FALLTHRU*/
|
||||||
|
case WSYM_sysfile:
|
||||||
|
if (win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)
|
||||||
|
{
|
||||||
|
res = symlink_wsl (oldpath, win32_newpath);
|
||||||
|
if (!res)
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
/* On FSes not supporting reparse points, or in case of an error
|
||||||
|
creating the WSL symlink, fall back to creating the plain old
|
||||||
|
SYSTEM file symlink. */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -2360,29 +2462,6 @@ 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.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
What changed:
|
What changed:
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
- Support WSL symlinks.
|
- Support WSL symlinks. Create those by default now.
|
||||||
|
|
||||||
|
|
||||||
Bug Fixes:
|
Bug Fixes:
|
||||||
|
|
|
@ -83,7 +83,7 @@ https://gitlab.freedesktop.org/terminal-wg/specifications/issues/9.
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
|
|
||||||
<listitem><para>
|
<listitem><para>
|
||||||
Support WSL symlinks.
|
Support WSL symlinks. Create those by default now.
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
|
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
Loading…
Reference in New Issue