This corresponds to https://github.com/msys2/msys2-runtime/pull/304 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
341 lines
10 KiB
Diff
341 lines
10 KiB
Diff
From 242443bf1f2cfabbcb0ea5202f1b5f7d196fd24c Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B8=CC=86=20=D0=9F?=
|
|
=?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=BE=D0=B2?= <alexey.pawlow@gmail.com>
|
|
Date: Sun, 14 Apr 2019 21:47:21 +0300
|
|
Subject: [PATCH 06/N] Instead of creating Cygwin symlinks, use deep copy by
|
|
default
|
|
|
|
The new `winsymlinks` mode `deepcopy` (which is made the default) lets
|
|
calls to `symlink()` create (deep) copies of the source file/directory.
|
|
|
|
This is necessary because unlike Cygwin, MSYS2 does not try to be its
|
|
own little ecosystem that lives its life separate from regular Win32
|
|
programs: the latter have _no idea_ about Cygwin-emulated symbolic links
|
|
(i.e. system files whose contents start with `!<symlink>\xff\xfe` and
|
|
the remainder consists of the NUL-terminated, UTF-16LE-encoded symlink
|
|
target).
|
|
|
|
To support Cygwin-style symlinks, the new mode `sysfile` is introduced.
|
|
|
|
Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
|
|
Co-authored-by: Jeremy Drake <github@jdrake.com>
|
|
---
|
|
winsup/cygwin/environ.cc | 4 +
|
|
winsup/cygwin/globals.cc | 3 +-
|
|
winsup/cygwin/path.cc | 252 +++++++++++++++++++++++++++++++++++++++
|
|
3 files changed, 258 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc
|
|
index b9f7e05..5fb3f53 100644
|
|
--- a/winsup/cygwin/environ.cc
|
|
+++ b/winsup/cygwin/environ.cc
|
|
@@ -88,6 +88,10 @@ set_winsymlinks (const char *buf)
|
|
else if (ascii_strncasematch (buf, "native", 6))
|
|
allow_winsymlinks = ascii_strcasematch (buf + 6, "strict")
|
|
? WSYM_nativestrict : WSYM_native;
|
|
+ else if (ascii_strncasematch (buf, "deepcopy", 8))
|
|
+ allow_winsymlinks = WSYM_deepcopy;
|
|
+ else
|
|
+ allow_winsymlinks = WSYM_sysfile;
|
|
}
|
|
|
|
/* The structure below is used to set up an array which is used to
|
|
diff --git a/winsup/cygwin/globals.cc b/winsup/cygwin/globals.cc
|
|
index d8e058f..b7e0e21 100644
|
|
--- a/winsup/cygwin/globals.cc
|
|
+++ b/winsup/cygwin/globals.cc
|
|
@@ -57,6 +57,7 @@ enum winsym_t
|
|
WSYM_nativestrict,
|
|
WSYM_nfs,
|
|
WSYM_sysfile,
|
|
+ WSYM_deepcopy
|
|
};
|
|
|
|
exit_states NO_COPY exit_state;
|
|
@@ -70,7 +71,7 @@ bool ignore_case_with_glob;
|
|
bool pipe_byte = true; /* Default to byte mode so that C# programs work. */
|
|
bool reset_com;
|
|
bool wincmdln;
|
|
-winsym_t allow_winsymlinks = WSYM_default;
|
|
+winsym_t allow_winsymlinks = WSYM_deepcopy;
|
|
bool disable_pcon;
|
|
bool winjitdebug = false;
|
|
|
|
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
|
|
index 8ef347c..aae10fe 100644
|
|
--- a/winsup/cygwin/path.cc
|
|
+++ b/winsup/cygwin/path.cc
|
|
@@ -1722,6 +1722,173 @@ conv_path_list (const char *src, char *dst, size_t size,
|
|
|
|
/********************** Symbolic Link Support **************************/
|
|
|
|
+static int
|
|
+recursiveCopyCheckSymlink(PUNICODE_STRING src, bool& isdirlink)
|
|
+{
|
|
+ path_conv pc (src, PC_SYM_NOFOLLOW|PC_SYM_NOFOLLOW_REP);
|
|
+ if (pc.error)
|
|
+ {
|
|
+ set_errno (pc.error);
|
|
+ return -1;
|
|
+ }
|
|
+ isdirlink = pc.issymlink ();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ Create a deep copy of src as dst, while avoiding descending in origpath.
|
|
+*/
|
|
+static int
|
|
+recursiveCopy (PUNICODE_STRING src, PUNICODE_STRING dst, USHORT origsrclen,
|
|
+ USHORT origdstlen, PWIN32_FIND_DATAW dHfile = NULL)
|
|
+{
|
|
+ HANDLE dH = INVALID_HANDLE_VALUE;
|
|
+ NTSTATUS status;
|
|
+ int srcpos = src->Length;
|
|
+ int dstpos = dst->Length;
|
|
+ int res = -1;
|
|
+ bool freedHfile = false;
|
|
+
|
|
+ if (!dHfile)
|
|
+ {
|
|
+ dHfile = (PWIN32_FIND_DATAW) cmalloc_abort (HEAP_STR, sizeof (*dHfile));
|
|
+ freedHfile = true;
|
|
+ }
|
|
+
|
|
+ debug_printf ("recursiveCopy (%S, %S)", src, dst);
|
|
+
|
|
+ /* Create the destination directory */
|
|
+ if (!CreateDirectoryExW (src->Buffer, dst->Buffer, NULL))
|
|
+ {
|
|
+ debug_printf ("CreateDirectoryExW(%S, %S, 0) failed", src, dst);
|
|
+ __seterrno ();
|
|
+ goto done;
|
|
+ }
|
|
+ /* Descend into the source directory */
|
|
+ if (src->Buffer[(src->Length - 1) / sizeof (WCHAR)] != L'\\')
|
|
+ {
|
|
+ status = RtlAppendUnicodeToString (src, L"\\*");
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ status = RtlAppendUnicodeToString (src, L"*");
|
|
+ srcpos -= sizeof (WCHAR);
|
|
+ }
|
|
+ if (!NT_SUCCESS (status))
|
|
+ {
|
|
+ __seterrno_from_nt_status (status);
|
|
+ goto done;
|
|
+ }
|
|
+ if (dst->Buffer[(dst->Length - 1) / sizeof (WCHAR)] != L'\\')
|
|
+ status = RtlAppendUnicodeToString (dst, L"\\");
|
|
+ else
|
|
+ dstpos -= sizeof (WCHAR);
|
|
+ if (!NT_SUCCESS (status))
|
|
+ {
|
|
+ __seterrno_from_nt_status (status);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ dH = FindFirstFileExW (src->Buffer, FindExInfoBasic, dHfile,
|
|
+ FindExSearchNameMatch, NULL,
|
|
+ FIND_FIRST_EX_LARGE_FETCH);
|
|
+ if (dH == INVALID_HANDLE_VALUE)
|
|
+ {
|
|
+ __seterrno ();
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ do
|
|
+ {
|
|
+ bool isdirlink = false;
|
|
+ debug_printf ("dHfile: %W", dHfile->cFileName);
|
|
+ if (dHfile->cFileName[0] == L'.' &&
|
|
+ (!dHfile->cFileName[1] ||
|
|
+ (dHfile->cFileName[1] == L'.' && !dHfile->cFileName[2])))
|
|
+ continue;
|
|
+ /* Append the directory item filename to both source and destination */
|
|
+ src->Length = srcpos + sizeof (WCHAR);
|
|
+ dst->Length = dstpos + sizeof (WCHAR);
|
|
+ status = RtlAppendUnicodeToString (src, dHfile->cFileName);
|
|
+ if (!NT_SUCCESS (status))
|
|
+ {
|
|
+ __seterrno_from_nt_status (status);
|
|
+ goto done;
|
|
+ }
|
|
+ status = RtlAppendUnicodeToString (dst, dHfile->cFileName);
|
|
+ if (!NT_SUCCESS (status))
|
|
+ {
|
|
+ __seterrno_from_nt_status (status);
|
|
+ goto done;
|
|
+ }
|
|
+ debug_printf ("%S -> %S", src, dst);
|
|
+ if ((dHfile->dwFileAttributes &
|
|
+ (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT)) ==
|
|
+ (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_REPARSE_POINT))
|
|
+ {
|
|
+ /* I was really hoping to avoid using path_conv in the recursion,
|
|
+ but maybe putting it in its own function will prevent it from
|
|
+ taking up space in the stack frame */
|
|
+ if (recursiveCopyCheckSymlink (src, isdirlink))
|
|
+ goto done;
|
|
+ }
|
|
+ if (isdirlink)
|
|
+ {
|
|
+ /* CreateDirectoryEx seems to "copy" directory reparse points, which
|
|
+ CopyFileEx can only do with a flag introduced in 19041. */
|
|
+ if (!CreateDirectoryExW (src->Buffer, dst->Buffer, NULL))
|
|
+ {
|
|
+ debug_printf ("CreateDirectoryExW(%S, %S, 0) failed", src, dst);
|
|
+ __seterrno ();
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ else if (dHfile->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
+ {
|
|
+ /* Recurse into the child directory */
|
|
+ /* avoids endless recursion */
|
|
+ if (src->Length <= origsrclen ||
|
|
+ (!wcsncmp (src->Buffer, dst->Buffer, origdstlen / sizeof (WCHAR)) &&
|
|
+ (!src->Buffer[origdstlen / sizeof (WCHAR)] ||
|
|
+ iswdirsep(src->Buffer[origdstlen / sizeof (WCHAR)]))))
|
|
+ {
|
|
+ set_errno (ELOOP);
|
|
+ goto done;
|
|
+ }
|
|
+ if (recursiveCopy (src, dst, origsrclen, origdstlen, dHfile))
|
|
+ goto done;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Just copy the file */
|
|
+ if (!CopyFileExW (src->Buffer, dst->Buffer, NULL, NULL, NULL,
|
|
+ COPY_FILE_COPY_SYMLINK))
|
|
+ {
|
|
+ __seterrno ();
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ while (FindNextFileW (dH, dHfile));
|
|
+
|
|
+ if (GetLastError() != ERROR_NO_MORE_FILES)
|
|
+ {
|
|
+ __seterrno ();
|
|
+ goto done;
|
|
+ }
|
|
+ res = 0;
|
|
+
|
|
+done:
|
|
+
|
|
+ if (dH != INVALID_HANDLE_VALUE)
|
|
+ FindClose (dH);
|
|
+
|
|
+ if (freedHfile)
|
|
+ cfree (dHfile);
|
|
+
|
|
+ return res;
|
|
+}
|
|
+
|
|
/* Create a symlink from FROMPATH to TOPATH. */
|
|
|
|
extern "C" int
|
|
@@ -2045,6 +2212,84 @@ symlink_wsl (const char *oldpath, path_conv &win32_newpath)
|
|
return 0;
|
|
}
|
|
|
|
+int
|
|
+symlink_deepcopy (const char *oldpath, path_conv &win32_newpath)
|
|
+{
|
|
+ tmp_pathbuf tp;
|
|
+ path_conv win32_oldpath;
|
|
+
|
|
+ resolve_symlink_target (oldpath, win32_newpath, win32_oldpath);
|
|
+ if (win32_oldpath.error)
|
|
+ {
|
|
+ set_errno (win32_oldpath.error);
|
|
+ return -1;
|
|
+ }
|
|
+ if (win32_oldpath.isspecial ())
|
|
+ return -2;
|
|
+
|
|
+ /* MSYS copy file instead make symlink */
|
|
+ /* As a MSYS limitation, the source path must exist. */
|
|
+ if (!win32_oldpath.exists ())
|
|
+ {
|
|
+ set_errno (ENOENT);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ PUNICODE_STRING w_oldpath = win32_oldpath.get_nt_native_path ();
|
|
+ PUNICODE_STRING w_newpath = win32_newpath.get_nt_native_path ();
|
|
+ if (w_oldpath->Buffer[1] == L'?')
|
|
+ w_oldpath->Buffer[1] = L'\\';
|
|
+ if (w_newpath->Buffer[1] == L'?')
|
|
+ w_newpath->Buffer[1] = L'\\';
|
|
+ if (win32_oldpath.isdir ())
|
|
+ {
|
|
+ /* we need a larger UNICODE_STRING MaximumLength than
|
|
+ get_nt_native_path allocates for the recursive copy */
|
|
+ UNICODE_STRING u_oldpath, u_newpath;
|
|
+ RtlCopyUnicodeString (tp.u_get (&u_oldpath), w_oldpath);
|
|
+ RtlCopyUnicodeString (tp.u_get (&u_newpath), w_newpath);
|
|
+ return recursiveCopy (&u_oldpath, &u_newpath,
|
|
+ u_oldpath.Length, u_newpath.Length);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ bool isdirlink = false;
|
|
+ if (win32_oldpath.issymlink () &&
|
|
+ win32_oldpath.is_known_reparse_point ())
|
|
+ {
|
|
+ /* Is there a better way to know this? */
|
|
+ DWORD attr = getfileattr (win32_oldpath.get_win32 (),
|
|
+ !!win32_oldpath.objcaseinsensitive ());
|
|
+ if (attr == INVALID_FILE_ATTRIBUTES)
|
|
+ {
|
|
+ __seterrno ();
|
|
+ return -1;
|
|
+ }
|
|
+ isdirlink = attr & FILE_ATTRIBUTE_DIRECTORY;
|
|
+ }
|
|
+ if (isdirlink)
|
|
+ {
|
|
+ /* CreateDirectoryEx seems to "copy" directory reparse points, which
|
|
+ CopyFileEx can only do with a flag introduced in 19041. */
|
|
+ if (!CreateDirectoryExW (w_oldpath->Buffer, w_newpath->Buffer, NULL))
|
|
+ {
|
|
+ debug_printf ("CreateDirectoryExW(%S, %S, 0) failed", w_oldpath,
|
|
+ w_newpath);
|
|
+ __seterrno ();
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ else if (!CopyFileExW (w_oldpath->Buffer, w_newpath->Buffer, NULL, NULL,
|
|
+ NULL, COPY_FILE_COPY_SYMLINK))
|
|
+ {
|
|
+ __seterrno ();
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
int
|
|
symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
|
|
{
|
|
@@ -2112,6 +2357,13 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
|
|
case WSYM_nfs:
|
|
res = symlink_nfs (oldpath, win32_newpath);
|
|
__leave;
|
|
+ case WSYM_deepcopy:
|
|
+ res = symlink_deepcopy (oldpath, win32_newpath);
|
|
+ if (!res || res == -1)
|
|
+ __leave;
|
|
+ /* fall back to sysfile symlink type */
|
|
+ wsym_type = WSYM_sysfile;
|
|
+ break;
|
|
case WSYM_native:
|
|
case WSYM_nativestrict:
|
|
res = symlink_native (oldpath, win32_newpath);
|