This is the mail archive of the cygwin-cvs@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[newlib-cygwin] Compatibility improvements to reparse point handling.


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

commit 7a4e299a18a48280f36d29a86f23384ab821fc36
Author: Joe_Lowe <joe@pismotec.com>
Date:   Wed Jun 14 13:01:28 2017 -0700

    Compatibility improvements to reparse point handling.

Diff:
---
 winsup/cygwin/fhandler_disk_file.cc | 63 +++++++++++++++++++++++--------------
 winsup/cygwin/path.cc               | 59 +++++++++++++++++++++++++++-------
 winsup/cygwin/path.h                |  1 +
 3 files changed, 87 insertions(+), 36 deletions(-)

diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index 01a9afe..f8adcaa 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -161,15 +161,19 @@ path_conv::isgood_inode (ino_t ino) const
   return true;
 }
 
-/* Check reparse point for type.  IO_REPARSE_TAG_MOUNT_POINT types are
-   either volume mount points, which are treated as directories, or they
-   are directory mount points, which are treated as symlinks.
-   IO_REPARSE_TAG_SYMLINK types are always symlinks.  We don't know
-   anything about other reparse points, so they are treated as unknown.  */
-static inline uint8_t
-readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
+/* Check reparse point to determine if it should be treated as a posix symlink
+   or as a normal file/directory. Mount points are treated as normal directories
+   to match behavior of other systems. Unknown reparse tags are used for
+   things other than links (HSM, compression, dedup), and generally should be
+   treated as a normal file/directory. Native symlinks and mount points are
+   treated as posix symlinks, depending on the prefix of the target name.
+   This logic needs to agree with equivalent logic in path.cc
+   symlink_info::check_reparse_point() .
+   */
+static inline bool
+readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
 {
-  uint8_t ret = DT_UNKNOWN;
+  bool ret = false;
   IO_STATUS_BLOCK io;
   HANDLE reph;
   UNICODE_STRING subst;
@@ -185,20 +189,29 @@ readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
 		      &io, FSCTL_GET_REPARSE_POINT, NULL, 0,
 		      (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
 	{
-	  if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
+	  if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
 	    {
 	      RtlInitCountedUnicodeString (&subst,
 		  (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
 			    + rp->MountPointReparseBuffer.SubstituteNameOffset),
 		  rp->MountPointReparseBuffer.SubstituteNameLength);
-	      /* Only volume mountpoints are treated as directories. */
-	      if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
-		ret = DT_DIR;
-	      else
-		ret = DT_LNK;
+	      if (check_reparse_point_target (&subst))
+	        ret = true;
 	    }
 	  else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
-	    ret = DT_LNK;
+	    {
+	      if (rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE)
+		ret = true;
+	      else
+		{
+		  RtlInitCountedUnicodeString (&subst,
+		      (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
+			    + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
+		      rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
+		  if (check_reparse_point_target (&subst))
+		    ret = true;
+		}
+	    }
 	  NtClose (reph);
 	}
     }
@@ -1995,8 +2008,7 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
   /* Set d_type if type can be determined from file attributes.  For .lnk
      symlinks, d_type will be reset below.  Reparse points can be NTFS
      symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
-  if (attr &&
-      !(attr & (~FILE_ATTRIBUTE_VALID_FLAGS | FILE_ATTRIBUTE_REPARSE_POINT)))
+  if (attr && !(attr & ~FILE_ATTRIBUTE_VALID_FLAGS))
     {
       if (attr & FILE_ATTRIBUTE_DIRECTORY)
 	de->d_type = DT_DIR;
@@ -2005,19 +2017,22 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
 	de->d_type = DT_REG;
     }
 
-  /* Check for directory reparse point. These may be treated as a posix
-     symlink, or as mount point, so need to figure out whether to return
-     a directory or link type. In all cases, returning the INO of the
-     reparse point (not of the target) matches behavior of posix systems.
+  /* Check for reparse points that can be treated as posix symlinks.
+     Mountpoints and unknown or unhandled reparse points will be treated
+     as normal file/directory/unknown. In all cases, returning the INO of
+     the reparse point (not of the target) matches behavior of posix systems.
      */
-  if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
-      == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
+  if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
     {
       OBJECT_ATTRIBUTES oattr;
 
       InitializeObjectAttributes (&oattr, fname, pc.objcaseinsensitive (),
 				  get_handle (), NULL);
-      de->d_type = readdir_check_reparse_point (&oattr);
+      /* FUTURE: Ideally would know at this point if reparse point
+         is stored on a remote volume. Without this, may return DT_LNK
+         for remote names that end up lstat-ing as a normal directory. */
+      if (readdir_check_reparse_point (&oattr, false/*remote*/))
+        de->d_type = DT_LNK;
     }
 
   /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 7d1d23d..53cbf49 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -2261,6 +2261,31 @@ symlink_info::check_sysfile (HANDLE h)
   return res;
 }
 
+bool
+check_reparse_point_target (PUNICODE_STRING subst)
+{
+  /* Native mount points, or native non-relative symbolic links,
+     can be treated as posix symlinks only if the SubstituteName
+     can be converted from a native NT object namespace name to
+     a win32 name. We only know how to convert names with two
+     prefixes :
+       "\??\UNC\..."
+       "\??\X:..."
+     Other reparse points will be treated as files or
+     directories, not as posix symlinks.
+     */
+  if (RtlEqualUnicodePathPrefix (subst, &ro_u_natp, FALSE))
+    {
+      if (subst->Length >= 6*sizeof(WCHAR) && subst->Buffer[5] == L':' &&
+          (subst->Length == 6*sizeof(WCHAR) || subst->Buffer[6] == L'\\'))
+        return true;
+      else if (subst->Length >= 8*sizeof(WCHAR) &&
+          wcsncmp (subst->Buffer + 4, L"UNC\\", 4) == 0)
+        return true;
+    }
+  return false;
+}
+
 int
 symlink_info::check_reparse_point (HANDLE h, bool remote)
 {
@@ -2299,14 +2324,24 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
       return 0;
     }
   if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
-    /* Windows evaluates native symlink literally.  If a remote symlink points
-       to, say, C:\foo, it will be handled as if the target is the local file
-       C:\foo.  That comes in handy since that's how symlinks are treated under
-       POSIX as well. */
-    RtlInitCountedUnicodeString (&subst,
-		  (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
-			+ rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
-		  rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
+    {
+      /* Windows evaluates native symlink literally.  If a remote symlink points
+         to, say, C:\foo, it will be handled as if the target is the local file
+         C:\foo.  That comes in handy since that's how symlinks are treated under
+         POSIX as well. */
+      RtlInitCountedUnicodeString (&subst,
+		    (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
+			  + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
+		    rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
+      if (!(rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) &&
+          !check_reparse_point_target (&subst))
+	{
+	  /* Unsupport native symlink target prefix. Not treated as symlink.
+	     The return value of -1 indicates name needs to be opened without
+	     FILE_OPEN_REPARSE_POINT flag. */
+	  return -1;
+	}
+    }
   else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
     {
       /* Don't handle junctions on remote filesystems as symlinks.  This type
@@ -2318,11 +2353,11 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
 		  (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
 			  + rp->MountPointReparseBuffer.SubstituteNameOffset),
 		  rp->MountPointReparseBuffer.SubstituteNameLength);
-      if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
+      if (!check_reparse_point_target (&subst))
 	{
-	  /* Volume mount point.  Not treated as symlink. The return
-	     value of -1 is a hint for the caller to treat this as a
-	     volume mount point. */
+	  /* Volume mount point, or unsupported native target prefix. Not
+	     treated as symlink. The return value of -1 indicates name needs
+	     to be opened without FILE_OPEN_REPARSE_POINT flag. */
 	  return -1;
 	}
     }
diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h
index c6b2d2b..0468928 100644
--- a/winsup/cygwin/path.h
+++ b/winsup/cygwin/path.h
@@ -88,6 +88,7 @@ enum path_types
 };
 
 NTSTATUS file_get_fai (HANDLE, PFILE_ALL_INFORMATION);
+bool check_reparse_point_target (PUNICODE_STRING);
 
 class symlink_info;


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]