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

Reply via email to