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] cygwin: unify reparse point checking code into single function


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

commit 3e80cefb16cab409c00d5b61be0f7a3befc688e3
Author: Corinna Vinschen <corinna@vinschen.de>
Date:   Mon Jul 24 17:33:20 2017 +0200

    cygwin: unify reparse point checking code into single function
    
    So far we had two functions checking the content of a reparse point,
    readdir_check_reparse_point in fhandler_disk_file.cc for the sake of
    readdir, and symlink_info::check_reparse_point for the sake of
    generic path checking.
    
    * Rename check_reparse_point_target helper to check_reparse_point_string
      and convert to static function.
    * Create new check_reparse_point_target helper containing the core
      reparse point checking code
    * Just call check_reparse_point_target from readdir_check_reparse_point
      and symlink_info::check_reparse_point and only perform the unique
      task in those functions.
    
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/fhandler_disk_file.cc | 67 +++++++---------------------
 winsup/cygwin/path.cc               | 89 ++++++++++++++++++++++---------------
 winsup/cygwin/path.h                |  3 +-
 3 files changed, 70 insertions(+), 89 deletions(-)

diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
index 6cc081d..2144a4c 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -161,62 +161,25 @@ path_conv::isgood_inode (ino_t ino) const
   return true;
 }
 
-/* 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() .
-   */
+/* Check reparse point to determine if it should be treated as a
+   posix symlink or as a normal file/directory.  Logic is explained
+   in detail in check_reparse_point_target in path.cc. */
 static inline bool
 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
 {
-  bool ret = false;
-  IO_STATUS_BLOCK io;
+  NTSTATUS status;
   HANDLE reph;
-  UNICODE_STRING subst;
-
-  if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, attr, &io,
-			      FILE_SHARE_VALID_FLAGS,
-			      FILE_OPEN_FOR_BACKUP_INTENT
-			      | FILE_OPEN_REPARSE_POINT)))
-    {
-      PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER)
-		  alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
-      if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
-		      &io, FSCTL_GET_REPARSE_POINT, NULL, 0,
-		      (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
-	{
-	  /* If reparse point is stored on a remote volume, lstat returns
-	     them as normal files or dirs, not as symlink.  For a description,
-	     see the comment preceeding remote check in
-	     symlink_info::check_reparse_point. */
-	  if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
-	    {
-	      RtlInitCountedUnicodeString (&subst,
-		  (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
-			    + rp->MountPointReparseBuffer.SubstituteNameOffset),
-		  rp->MountPointReparseBuffer.SubstituteNameLength);
-	      if (check_reparse_point_target (&subst))
-	        ret = true;
-	    }
-	  else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
-	    {
-	      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;
-		}
-	    }
-	}
+  IO_STATUS_BLOCK io;
+  tmp_pathbuf tp;
+  UNICODE_STRING symbuf;
+  bool ret = false;
+
+  status = NtOpenFile (&reph, READ_CONTROL, attr, &io, FILE_SHARE_VALID_FLAGS,
+		       FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
+  if (NT_SUCCESS (status))
+    {
+      PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
+      ret = (check_reparse_point_target (reph, remote, rp, &symbuf) > 0);
       NtClose (reph);
     }
   return ret;
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 29d60b8..475c995 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -1898,7 +1898,7 @@ symlink_worker (const char *oldpath, const char *newpath, bool isdevice)
 		     Win32 prefix for long pathnames!  So we have to tack off
 		     the prefix and convert the path to the "normal" syntax
 		     for ParseDisplayName.  */
-		  WCHAR *wc = wc_path + 4;
+		  PWCHAR wc = wc_path + 4;
 		  if (wc[1] != L':') /* native UNC path */
 		    *(wc += 2) = L'\\';
 		  HRESULT res;
@@ -2261,8 +2261,8 @@ symlink_info::check_sysfile (HANDLE h)
   return res;
 }
 
-bool
-check_reparse_point_target (PUNICODE_STRING subst)
+static bool
+check_reparse_point_string (PUNICODE_STRING subst)
 {
   /* Native mount points, or native non-relative symbolic links,
      can be treated as posix symlinks only if the SubstituteName
@@ -2286,15 +2286,17 @@ check_reparse_point_target (PUNICODE_STRING subst)
   return false;
 }
 
+/* Return values:
+    <0: Negative errno.
+     0: No symlink.
+     1: Symlink.
+*/
 int
-symlink_info::check_reparse_point (HANDLE h, bool remote)
+check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
+			    PUNICODE_STRING psymbuf)
 {
-  tmp_pathbuf tp;
   NTSTATUS status;
   IO_STATUS_BLOCK io;
-  PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
-  UNICODE_STRING subst;
-  char srcbuf[SYMLINK_MAX + 7];
 
   /* On remote drives or under heavy load, NtFsControlFile can return with
      STATUS_PENDING.  If so, instead of creating an event object, just set
@@ -2319,9 +2321,9 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
 	 the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
 	 returns with STATUS_NOT_A_REPARSE_POINT.  That's quite buggy, but
 	 we cope here with this scenario by not setting an error code. */
-      if (status != STATUS_NOT_A_REPARSE_POINT)
-	set_error (EIO);
-      return 0;
+      if (status == STATUS_NOT_A_REPARSE_POINT)
+	return 0;
+      return -EIO;
     }
   if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
     {
@@ -2329,18 +2331,13 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
          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;
-	}
+      RtlInitCountedUnicodeString (psymbuf,
+		(PWCHAR)((PBYTE) rp->SymbolicLinkReparseBuffer.PathBuffer
+			 + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
+		rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
+      if ((rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) ||
+          check_reparse_point_string (psymbuf))
+	return 1;
     }
   else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
     {
@@ -2349,28 +2346,48 @@ symlink_info::check_reparse_point (HANDLE h, bool remote)
 	 target of the junction is the remote directory it is supposed to
 	 point to.  If we handle it as symlink, it will be mistreated as
 	 pointing to a dir on the local system. */
-      RtlInitCountedUnicodeString (&subst,
-		  (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
-			  + rp->MountPointReparseBuffer.SubstituteNameOffset),
+      RtlInitCountedUnicodeString (psymbuf,
+		  (PWCHAR)((PBYTE) rp->MountPointReparseBuffer.PathBuffer
+			   + rp->MountPointReparseBuffer.SubstituteNameOffset),
 		  rp->MountPointReparseBuffer.SubstituteNameLength);
-      if (!check_reparse_point_target (&subst))
+      if (RtlEqualUnicodePathPrefix (psymbuf, &ro_u_volume, TRUE))
 	{
-	  /* 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;
+	  /* Volume mount point.  Not treated as symlink. The return
+	     value -EPERM is a hint for the caller to treat this as a
+	     volume mount point. */
+	  return -EPERM;
 	}
+      if (check_reparse_point_string (psymbuf))
+	return 1;
     }
-  else
+  return 0;
+}
+
+int
+symlink_info::check_reparse_point (HANDLE h, bool remote)
+{
+  tmp_pathbuf tp;
+  PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
+  UNICODE_STRING symbuf;
+  char srcbuf[SYMLINK_MAX + 7];
+
+  int ret = check_reparse_point_target (h, remote, rp, &symbuf);
+  if (ret <= 0)
     {
+      if (ret == -EIO)
+	{
+	  set_error (EIO);
+	  return 0;
+	}
       /* Maybe it's a reparse point, but it's certainly not one we recognize.
 	 Drop REPARSE attribute so we don't try to use the flag accidentally.
 	 It's just some arbitrary file or directory for us. */
       fileattr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
-      return 0;
+      return ret;
     }
-  sys_wcstombs (srcbuf, SYMLINK_MAX + 7, subst.Buffer,
-		subst.Length / sizeof (WCHAR));
+  /* ret is > 0, so it's a reparse point, path in symbuf. */
+  sys_wcstombs (srcbuf, SYMLINK_MAX + 7, symbuf.Buffer,
+		symbuf.Length / sizeof (WCHAR));
   pflags |= PATH_SYMLINK | PATH_REP;
   /* A symlink is never a directory. */
   fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
@@ -2993,7 +3010,7 @@ restart:
 		 filesystem information again, but with a NULL handle.
 		 This does what we want because fs_info::update opens the
 		 handle without FILE_OPEN_REPARSE_POINT. */
-	      if (res == -1)
+	      if (res < 0)
 		fs.update (&upath, NULL);
 	    }
 	}
diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h
index 0468928..469d607 100644
--- a/winsup/cygwin/path.h
+++ b/winsup/cygwin/path.h
@@ -88,7 +88,8 @@ enum path_types
 };
 
 NTSTATUS file_get_fai (HANDLE, PFILE_ALL_INFORMATION);
-bool check_reparse_point_target (PUNICODE_STRING);
+int check_reparse_point_target (HANDLE, bool, PREPARSE_DATA_BUFFER,
+				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]