public inbox for cygwin-cvs@sourceware.org
help / color / mirror / Atom feed
* [newlib-cygwin] Cygwin: path handling: never substitute virtual drive with target path
@ 2022-12-12 20:59 Corinna Vinschen
  0 siblings, 0 replies; only message in thread
From: Corinna Vinschen @ 2022-12-12 20:59 UTC (permalink / raw)
  To: cygwin-cvs

https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=5ba5e09b9d39feaedc9be0dcd88d085b256c5d40

commit 5ba5e09b9d39feaedc9be0dcd88d085b256c5d40
Author:     Corinna Vinschen <corinna@vinschen.de>
AuthorDate: Mon Dec 12 21:56:14 2022 +0100
Commit:     Corinna Vinschen <corinna@vinschen.de>
CommitDate: Mon Dec 12 21:56:14 2022 +0100

    Cygwin: path handling: never substitute virtual drive with target path
    
    Move the drive substitution code after the call to
    GetFinalPathNameByHandleW into a local function revert_virtual_drive
    and add code to handle non-remote virtual drives, i. e., those
    created with the subst command.  (Try to) make sure that virtual
    drives are never treated like symlinks.
    
    Fixes: 19d59ce75d53 ("Cygwin: path_conv: Rework handling native symlinks as inner path components")
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/path.cc | 149 +++++++++++++++++++++++++++++---------------------
 1 file changed, 87 insertions(+), 62 deletions(-)

diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index e7619270a269..c044d5295ed5 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -3024,6 +3024,65 @@ symlink_info::parse_device (const char *contents)
   return isdevice = true;
 }
 
+/* Probably we have a virtual drive input path and the resulting full path
+   starts with the substitution.  Retrieve the target path of the virtual
+   drive and try to revert what GetFinalPathNameByHandleW did to the
+   drive letter. */
+static bool
+revert_virtual_drive (PUNICODE_STRING upath, PUNICODE_STRING fpath,
+		      bool is_remote, ULONG ci_flag)
+{
+  /* Get the drive's target path. */
+  WCHAR drive[3] = {(WCHAR) towupper (upath->Buffer[4]), L':', L'\0'};
+  WCHAR target[MAX_PATH];
+  UNICODE_STRING tpath;
+  WCHAR *p;
+
+  DWORD remlen = QueryDosDeviceW (drive, target, MAX_PATH);
+  if (remlen < 3)
+    return false;
+  remlen -= 2; /* Two L'\0' */
+
+  if (target[remlen - 1] == L'\\')
+    remlen--;
+  RtlInitCountedUnicodeString (&tpath, target, remlen * sizeof (WCHAR));
+
+  const USHORT uncp_len = is_remote ? ro_u_uncp.Length / sizeof (WCHAR) - 1 : 0;
+
+  if (is_remote)
+    {
+      /* target path starts with \??\UNC\. */
+      if (RtlEqualUnicodePathPrefix (&tpath, &ro_u_uncp, TRUE))
+	{
+	  remlen -= uncp_len;
+	  p = target + uncp_len;
+	}
+      /* target path starts with \Device\<redirector>. */
+      else if ((p = wcschr (target, L';'))
+	       && p + 3 < target + remlen
+	       && wcsncmp (p + 1, drive, 2) == 0
+	       && (p = wcschr (p + 3, L'\\')))
+	remlen -= p - target;
+      else
+	return false;
+      if (wcsncasecmp (fpath->Buffer + uncp_len, p, remlen))
+	return false;
+    }
+  else if (!RtlEqualUnicodePathPrefix (fpath, &tpath, TRUE))
+    return false;
+  /* Replace fpath with source drive letter and append reminder of
+     final path after skipping target path */
+  fpath->Buffer[4] = drive[0]; /* Drive letter */
+  fpath->Buffer[5] = L':';
+  WCHAR *to = fpath->Buffer + 6; /* Next to L':' */
+  WCHAR *from = fpath->Buffer + uncp_len + remlen;
+  memmove (to, from, (wcslen (from) + 1) * sizeof (WCHAR));
+  fpath->Length -= (from - to) * sizeof (WCHAR);
+  if (RtlEqualUnicodeString (upath, fpath, !!ci_flag))
+    return false;
+  return true;
+}
+
 /* Check if PATH is a symlink.  PATH must be a valid Win32 path name.
 
    If PATH is a symlink, put the value of the symlink--the file to
@@ -3343,6 +3402,14 @@ restart:
 	  continue;
 	}
 
+      /* Consider the situation where a virtual drive points to a native
+         symlink.  Opening the virtual drive with FILE_OPEN_REPARSE_POINT
+	 actually opens the symlink.  If this symlink points to another
+	 directory using a relative path, symlink evaluation goes totally
+	 awry.  We never want a virtual drive evaluated as symlink. */
+      if (upath.Length <= 14)
+	  goto file_not_symlink;
+
       /* Reparse points are potentially symlinks.  This check must be
 	 performed before checking the SYSTEM attribute for sysfile
 	 symlinks, since reparse points can have this flag set, too. */
@@ -3490,80 +3557,38 @@ restart:
 	      RtlInitCountedUnicodeString (&fpath, fpbuf, ret * sizeof (WCHAR));
 	      if (!RtlEqualUnicodeString (&upath, &fpath, !!ci_flag))
 	        {
-		  /* Check if the final path is an UNC path and the incoming
-		     path isn't.  If so... */
-		  if (RtlEqualUnicodePathPrefix (&fpath, &ro_u_uncp, TRUE)
-		      && !RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE))
+		  /* If the incoming path is a local drive letter path... */
+		  if (!RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE))
 		    {
-		      /* ...get the remote path, replace remote path
-			 with drive letter, check again. */
-		      WCHAR drive[3] =
-			{(WCHAR) towupper (upath.Buffer[4]), L':', L'\0'};
-		      WCHAR remote[MAX_PATH];
-
-		      DWORD remlen = QueryDosDeviceW (drive, remote, MAX_PATH);
-		      if (remlen < 3)
-			goto file_not_symlink; /* fallback */
-		      remlen -= 2; /* Two L'\0' */
-
-		      if (remote[remlen - 1] == L'\\')
-			remlen--;
-		      WCHAR *p;
-		      UNICODE_STRING rpath;
-		      RtlInitCountedUnicodeString (&rpath, remote,
-						   remlen * sizeof (WCHAR));
-		      const USHORT uncp_len =
-			ro_u_uncp.Length / sizeof (WCHAR) - 1;
-		      if (RtlEqualUnicodePathPrefix (&rpath, &ro_u_uncp, TRUE))
+		      /* ...and the final path is an UNC path, revert to the
+			 drive letter path syntax. */
+		      if (RtlEqualUnicodePathPrefix (&fpath, &ro_u_uncp, TRUE))
+			{
+			  if (!revert_virtual_drive (&upath, &fpath, true,
+						     ci_flag))
+			    goto file_not_symlink;
+			}
+		      /* ...otherwise, if the final path changes the drive
+			 letter, let revert_virtual_drive check for a
+			 virtual drive and revert that. */
+		      else if (upath.Buffer[5] == L':'
+			       && (WCHAR) towupper (upath.Buffer[4])
+				  != (WCHAR) towupper (fpath.Buffer[4]))
 			{
-			  remlen -= uncp_len;
-			  p = remote + uncp_len;
+			  if (!revert_virtual_drive (&upath, &fpath, false,
+						     ci_flag))
+			    goto file_not_symlink;
 			}
-		      else if ((p = wcschr (remote, L';'))
-			       && p + 3 < remote + remlen
-			       && wcsncmp (p + 1, drive, 2) == 0
-			       && (p = wcschr (p + 3, L'\\')))
-			remlen -= p - remote;
-		      else
-			goto file_not_symlink; /* fallback */
-		      if (wcsncasecmp (fpath.Buffer + uncp_len, p, remlen))
-			goto file_not_symlink; /* fallback (not expected) */
-		      /* Hackfest */
-		      fpath.Buffer[4] = drive[0]; /* Drive letter */
-		      fpath.Buffer[5] = L':';
-		      WCHAR *to = fpath.Buffer + 6; /* Next to L':' */
-		      WCHAR *from = fpath.Buffer + uncp_len + remlen;
-		      memmove (to, from,
-			       (wcslen (from) + 1) * sizeof (WCHAR));
-		      fpath.Length -= (from - to) * sizeof (WCHAR);
-		      if (RtlEqualUnicodeString (&upath, &fpath, !!ci_flag))
-			goto file_not_symlink;
 		    }
 		  issymlink = true;
 		  /* upath.Buffer is big enough and unused from this point on.
 		     Reuse it here, avoiding yet another buffer allocation. */
 		  char *nfpath = (char *) upath.Buffer;
 		  sys_wcstombs (nfpath, NT_MAX_PATH, fpbuf);
-		  res = posixify (nfpath);
-
-		  /* If the incoming path consisted of a drive prefix only,
-		     we just handle a virtual drive, created with, e.g.
-
-		       subst X: C:\foo\bar
-
-		     Treat it like a symlink.  This is required to tell an
-		     lstat caller that the "drive" is actually pointing
-		     somewhere else, thus, it's a symlink in POSIX speak. */
-		  if (upath.Length == 14)	/* \??\X:\ */
-		    {
-		      fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
-		      path_flags |= PATH_SYMLINK;
-		    }
 		  /* For final paths differing in inner path components return
 		     length as negative value.  This informs path_conv::check
 		     to skip realpath handling on the last path component. */
-		  else
-		    res = -res;
+		  res = -posixify (nfpath);
 		  break;
 	        }
 	    }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-12-12 20:59 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-12 20:59 [newlib-cygwin] Cygwin: path handling: never substitute virtual drive with target path Corinna Vinschen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).