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);