From 94ae14f2b0413baf83a1d57ce23d3b10bd13af85 Mon Sep 17 00:00:00 2001 From: Jeremy Drake Date: Fri, 31 Jan 2025 16:00:56 -0800 Subject: [PATCH] fixup! Instead of creating Cygwin symlinks, use deep copy by default Factor out deepcopy symlink to its own worker function, like wsl, native, and nfs. Move it up into the beginning switch with them, so the fallback behavior is more obvious. See also #113/#114. --- winsup/cygwin/path.cc | 174 ++++++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 84 deletions(-) diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 1de352e199..23c8e41182 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -2144,6 +2144,88 @@ 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; + + /* The symlink target is relative to the directory in which the + symlink gets created, not relative to the cwd. Therefore we + have to mangle the path quite a bit before calling path_conv.*/ + if (isabspath (oldpath)) + win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes); + else + { + size_t len = strrchr (win32_newpath.get_posix (), '/') + - win32_newpath.get_posix () + 1; + char *absoldpath = tp.t_get (); + stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (), len), + oldpath); + win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes); + } + 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 (!CopyFileExW (w_oldpath->Buffer, w_newpath->Buffer, NULL, NULL, NULL, + COPY_FILE_COPY_SYMLINK| + (isdirlink ? COPY_FILE_DIRECTORY : 0))) + { + __seterrno (); + return -1; + } + else + { + return 0; + } + } +} + int symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice) { @@ -2212,6 +2294,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 default symlink type */ + wsym_type = WSYM_default; + goto handle_default; case WSYM_native: case WSYM_nativestrict: res = symlink_native (oldpath, win32_newpath); @@ -2228,6 +2317,7 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice) wsym_type = WSYM_default; fallthrough; case WSYM_default: + handle_default: if (win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS) { res = symlink_wsl (oldpath, win32_newpath); @@ -2380,90 +2470,6 @@ symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice) } else /* wsym_type == WSYM_sysfile */ { - if (wsym_type == WSYM_deepcopy) - { - path_conv win32_oldpath; - /* The symlink target is relative to the directory in which the - symlink gets created, not relative to the cwd. Therefore we - have to mangle the path quite a bit before calling path_conv.*/ - if (isabspath (oldpath)) - win32_oldpath.check (oldpath, - PC_SYM_NOFOLLOW, - stat_suffixes); - else - { - len = strrchr (win32_newpath.get_posix (), '/') - - win32_newpath.get_posix () + 1; - char *absoldpath = tp.t_get (); - stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (), - len), - oldpath); - win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, - stat_suffixes); - } - if (win32_oldpath.error) - { - set_errno (win32_oldpath.error); - __leave; - } - if (!win32_oldpath.isspecial ()) - { - /* MSYS copy file instead make symlink */ - /* As a MSYS limitation, the source path must exist. */ - if (!win32_oldpath.exists ()) - { - set_errno (ENOENT); - __leave; - } - - 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); - res = 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 (); - __leave; - } - isdirlink = attr & FILE_ATTRIBUTE_DIRECTORY; - } - if (!CopyFileExW (w_oldpath->Buffer, w_newpath->Buffer, - NULL, NULL, NULL, - COPY_FILE_COPY_SYMLINK| - (isdirlink ? COPY_FILE_DIRECTORY : 0))) - { - __seterrno (); - } - else - { - res = 0; - } - } - __leave; - } - } - /* Default technique creating a symlink. */ buf = tp.t_get (); cp = stpcpy (buf, SYMLINK_COOKIE);