[newlib-cygwin/cygwin-3_6-branch] Cygwin: symlink_native: allow linking to `..`

Corinna Vinschen via Cygwin-cvs <[email protected]>
Newsgroups gmane.os.cygwin.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=5979f22b9094a22d07cc2382e129f3f858008c88

commit 5979f22b9094a22d07cc2382e129f3f858008c88
Author:     Johannes Schindelin <[email protected]>
AuthorDate: Fri Jun 20 12:03:11 2025 +0200
Commit:     Corinna Vinschen <[email protected]>
CommitDate: Mon Jun 23 10:34:02 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 <[email protected]>
    (cherry picked from commit 827743ab7684a09bc077c91f6206e16ea85881b4)

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 42919a7cf551..ed083989306a 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);
 	}
     }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.