https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=827743ab7684a09bc077c91f6206e16ea85881b4
commit 827743ab7684a09bc077c91f6206e16ea85881b4 Author: Johannes Schindelin <johannes.schinde...@gmx.de> AuthorDate: Fri Jun 20 12:03:11 2025 +0200 Commit: Corinna Vinschen <cori...@vinschen.de> CommitDate: Mon Jun 23 10:31:24 2025 +0200 Cygwin: symlink_native: allow linking to `..` When running CYGWIN=winsymlinks:nativestrict ln -s .. abc the counter-intuitive result is _not_ a symbolic link to `..`, but instead to `../../$(basename "$PWD")`. The reason for this is that the search for the longest common prefix assumes that the link target is not a strict prefix of the parent directory of the link itself. Let's fix that. Signed-off-by: Johannes Schindelin <johannes.schinde...@gmx.de> Diff: --- winsup/cygwin/path.cc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index d26f99ee7fb8..310876b5accd 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -1855,9 +1855,18 @@ symlink_native (const char *oldpath, path_conv &win32_newpath) while (towupper (*++c_old) == towupper (*++c_new)) ; /* The last component could share a common prefix, so make sure we end - up on the first char after the last common backslash. */ - while (c_old[-1] != L'\\') - --c_old, --c_new; + up on the first char after the last common backslash. + + However, if c_old is a strict prefix of c_new (at a component + boundary), or vice versa, then do not try to find the last common + backslash. */ + if ((!*c_old || *c_old == L'\\') && (!*c_new || *c_new == L'\\')) + c_old += !!*c_old, c_new += !!*c_new; + else + { + while (c_old[-1] != L'\\') + --c_old, --c_new; + } /* 2. Check if prefix is long enough. The prefix must at least points to a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum @@ -1882,8 +1891,10 @@ symlink_native (const char *oldpath, path_conv &win32_newpath) final_oldpath = &final_oldpath_buf; final_oldpath->Buffer = tp.w_get (); PWCHAR e_old = final_oldpath->Buffer; - while (num-- > 0) - e_old = wcpcpy (e_old, L"..\\"); + while (num > 1 || (num == 1 && *c_old)) + e_old = wcpcpy (e_old, L"..\\"), num--; + if (num > 0) + e_old = wcpcpy (e_old, L".."); wcpcpy (e_old, c_old); } }