public inbox for cygwin-cvs@sourceware.org
help / color / mirror / Atom feed
From: Corinna Vinschen <corinna@sourceware.org>
To: cygwin-cvs@sourceware.org
Subject: [newlib-cygwin/main] Cygwin: try to avoid recalling offline files
Date: Fri,  8 Mar 2024 19:57:39 +0000 (GMT)	[thread overview]
Message-ID: <20240308195739.27EA93858D33@sourceware.org> (raw)

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

commit f6b56abec1865f8da7632644210bae36b395569b
Author:     Corinna Vinschen <corinna@vinschen.de>
AuthorDate: Fri Mar 8 20:57:06 2024 +0100
Commit:     Corinna Vinschen <corinna@vinschen.de>
CommitDate: Fri Mar 8 20:57:06 2024 +0100

    Cygwin: try to avoid recalling offline files
    
    Chances are high that Cygwin recalls offline files from remote
    storage, even if the file is only accessed during stat(2) or
    readdir(3).
    
    To avoid this
    - make sure Cygwin is placeholder-aware,
    - open files in path_conv handling, as well as in stat(2)/readdir(3)
      scenarios with FILE_OPEN_NO_RECALL, and
    - during symlink checking or testing for executablility, don't even
      try to open the file if one of the OFFLINE attributes is set.
    
    Reported-by: Marcin Wisnicki <mwisnicki@gmail.com>
    Signed-off-by: Corinna Vinschen <corinna@vinschen.de>

Diff:
---
 winsup/cygwin/autoload.cc              |  1 +
 winsup/cygwin/dcrt0.cc                 |  3 +++
 winsup/cygwin/fhandler/disk_file.cc    | 20 ++++++++++++++------
 winsup/cygwin/local_includes/ntdll.h   |  8 ++++++++
 winsup/cygwin/local_includes/path.h    | 16 +++++++++++++++-
 winsup/cygwin/local_includes/winlean.h |  7 +++++++
 winsup/cygwin/path.cc                  | 19 ++++++++++++++-----
 7 files changed, 62 insertions(+), 12 deletions(-)

diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc
index 65e906e8b9d0..cc9dafe1b621 100644
--- a/winsup/cygwin/autoload.cc
+++ b/winsup/cygwin/autoload.cc
@@ -479,6 +479,7 @@ LoadDLLfuncEx (SetThreadDescription, KernelBase, 1)
 LoadDLLfunc (VirtualAlloc2, KernelBase)
 
 LoadDLLfunc (NtMapViewOfSectionEx, ntdll)
+LoadDLLfuncEx (RtlSetProcessPlaceholderCompatibilityMode, ntdll, 1)
 
 LoadDLLfunc (ldap_bind_s, wldap32)
 LoadDLLfunc (ldap_count_entries, wldap32)
diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
index a40129c22232..7229377eb3fe 100644
--- a/winsup/cygwin/dcrt0.cc
+++ b/winsup/cygwin/dcrt0.cc
@@ -809,6 +809,9 @@ dll_crt0_1 (void *)
   if (dynamically_loaded)
     sigproc_init ();
 
+  /* Call this before accessing any files. */
+  RtlSetProcessPlaceholderCompatibilityMode (PHCM_EXPOSE_PLACEHOLDERS);
+
   check_sanity_and_sync (user_data);
 
   /* Initialize malloc and then call user_shared_initialize since it relies
diff --git a/winsup/cygwin/fhandler/disk_file.cc b/winsup/cygwin/fhandler/disk_file.cc
index d08fe9160d35..f3592148fc5b 100644
--- a/winsup/cygwin/fhandler/disk_file.cc
+++ b/winsup/cygwin/fhandler/disk_file.cc
@@ -176,7 +176,9 @@ readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
   bool ret = false;
 
   status = NtOpenFile (&reph, READ_CONTROL, attr, &io, FILE_SHARE_VALID_FLAGS,
-		       FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
+		       FILE_OPEN_NO_RECALL
+		       | FILE_OPEN_FOR_BACKUP_INTENT
+		       | FILE_OPEN_REPARSE_POINT);
   if (NT_SUCCESS (status))
     {
       PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
@@ -328,6 +330,7 @@ fhandler_base::fstat_by_name (struct stat *buf)
       status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
 			   &attr, &io, FILE_SHARE_VALID_FLAGS,
 			   FILE_SYNCHRONOUS_IO_NONALERT
+			   | FILE_OPEN_NO_RECALL
 			   | FILE_OPEN_FOR_BACKUP_INTENT
 			   | FILE_DIRECTORY_FILE);
       if (!NT_SUCCESS (status))
@@ -616,7 +619,8 @@ fhandler_disk_file::fstatvfs (struct statvfs *sfs)
       opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
 				       pc.get_object_attr (attr, sec_none_nih),
 				       &io, FILE_SHARE_VALID_FLAGS,
-				       FILE_OPEN_FOR_BACKUP_INTENT));
+				       FILE_OPEN_NO_RECALL
+				       | FILE_OPEN_FOR_BACKUP_INTENT));
       if (!opened)
 	{
 	  /* Can't open file.  Try again with parent dir. */
@@ -625,7 +629,8 @@ fhandler_disk_file::fstatvfs (struct statvfs *sfs)
 	  attr.ObjectName = &dirname;
 	  opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
 					   FILE_SHARE_VALID_FLAGS,
-					   FILE_OPEN_FOR_BACKUP_INTENT));
+					   FILE_OPEN_NO_RECALL
+					   | FILE_OPEN_FOR_BACKUP_INTENT));
 	  if (!opened)
 	    goto out;
 	}
@@ -2323,7 +2328,8 @@ readdir_get_ino (const char *path, bool dot_dot)
 	   || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
 				      pc.get_object_attr (attr, sec_none_nih),
 				      &io, FILE_SHARE_VALID_FLAGS,
-				      FILE_OPEN_FOR_BACKUP_INTENT
+				      FILE_OPEN_NO_RECALL
+				      | FILE_OPEN_FOR_BACKUP_INTENT
 				      | (pc.is_known_reparse_point ()
 				      ? FILE_OPEN_REPARSE_POINT : 0)))
 	  )
@@ -2372,8 +2378,9 @@ fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
      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.
+     Unless the file is OFFLINE. *.
      */
-  if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
+  if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && !isoffline (attr))
     {
       OBJECT_ATTRIBUTES oattr;
 
@@ -2618,7 +2625,8 @@ go_ahead:
 					 &nfs_aol_ffei, sizeof nfs_aol_ffei)
 			 : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
 				       FILE_SHARE_VALID_FLAGS,
-				       FILE_OPEN_FOR_BACKUP_INTENT
+				       FILE_OPEN_NO_RECALL
+				       | FILE_OPEN_FOR_BACKUP_INTENT
 				       | FILE_OPEN_REPARSE_POINT);
 	      if (NT_SUCCESS (f_status))
 		{
diff --git a/winsup/cygwin/local_includes/ntdll.h b/winsup/cygwin/local_includes/ntdll.h
index 9605784e32a8..7737ae503c18 100644
--- a/winsup/cygwin/local_includes/ntdll.h
+++ b/winsup/cygwin/local_includes/ntdll.h
@@ -169,6 +169,13 @@ extern GUID __cygwin_socket_guid;
 #define FILE_VC_QUOTAS_REBUILDING       0x00000200
 #define FILE_VC_VALID_MASK              0x000003ff
 
+#define PHCM_APPLICATION_DEFAULT	 0
+#define PHCM_DISGUISE_PLACEHOLDER	 1
+#define PHCM_EXPOSE_PLACEHOLDERS	 2
+#define PHCM_MAX			 2
+#define PHCM_ERROR_INVALID_PARAMETER	-1
+#define PHCM_ERROR_NO_TEB		-2
+
 /* IOCTL code to impersonate client of named pipe. */
 
 #define FSCTL_PIPE_DISCONNECT	CTL_CODE(FILE_DEVICE_NAMED_PIPE, 1, \
@@ -1639,6 +1646,7 @@ extern "C"
 					 BOOLEAN);
   NTSTATUS RtlSetGroupSecurityDescriptor (PSECURITY_DESCRIPTOR, PSID, BOOLEAN);
   NTSTATUS RtlSetOwnerSecurityDescriptor (PSECURITY_DESCRIPTOR, PSID, BOOLEAN);
+  CHAR RtlSetProcessPlaceholderCompatibilityMode (CHAR);
   PUCHAR RtlSubAuthorityCountSid (PSID);
   PULONG RtlSubAuthoritySid (PSID, ULONG);
   ULONG RtlUnicodeStringToAnsiSize (PUNICODE_STRING);
diff --git a/winsup/cygwin/local_includes/path.h b/winsup/cygwin/local_includes/path.h
index 4e62287f6a6e..cb8c4ca9f4d6 100644
--- a/winsup/cygwin/local_includes/path.h
+++ b/winsup/cygwin/local_includes/path.h
@@ -23,6 +23,14 @@ has_attribute (DWORD attributes, DWORD attribs_to_test)
 	 && (attributes & attribs_to_test);
 }
 
+extern inline bool
+isoffline (DWORD attributes)
+{
+  return has_attribute (attributes, FILE_ATTRIBUTE_OFFLINE
+				    | FILE_ATTRIBUTE_RECALL_ON_OPEN
+				    | FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS);
+}
+
 enum executable_states
 {
   is_executable,
@@ -235,6 +243,12 @@ class path_conv
   bool exists () const {return fileattr != INVALID_FILE_ATTRIBUTES;}
   bool has_attribute (DWORD x) const {return exists () && (fileattr & x);}
   int isdir () const {return has_attribute (FILE_ATTRIBUTE_DIRECTORY);}
+  bool isoffline () const
+  {
+    return has_attribute (FILE_ATTRIBUTE_OFFLINE
+			  | FILE_ATTRIBUTE_RECALL_ON_OPEN
+			  | FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS);
+  }
   executable_states exec_state ()
   {
     extern int _check_for_executable;
@@ -242,7 +256,7 @@ class path_conv
       return is_executable;
     if (mount_flags & MOUNT_NOTEXEC)
       return not_executable;
-    if (!_check_for_executable)
+    if (isoffline () || !_check_for_executable)
       return dont_care_if_executable;
     return dont_know_if_executable;
   }
diff --git a/winsup/cygwin/local_includes/winlean.h b/winsup/cygwin/local_includes/winlean.h
index 113b2c6b0d3f..947109bdeee4 100644
--- a/winsup/cygwin/local_includes/winlean.h
+++ b/winsup/cygwin/local_includes/winlean.h
@@ -97,6 +97,13 @@ details. */
 #define FILE_SUPPORTS_GHOSTING                  0x40000000
 #endif
 
+#ifndef FILE_ATTRIBUTE_RECALL_ON_OPEN
+#define FILE_ATTRIBUTE_RECALL_ON_OPEN		0x00040000
+#endif
+#ifndef FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS
+#define FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS	0x00400000
+#endif
+
 /* So-called "Microsoft Account" SIDs (S-1-11-...) have a netbios domain name
    "MicrosoftAccounts".  The new "Application Container SIDs" (S-1-15-...)
    have a netbios domain name "APPLICATION PACKAGE AUTHORITY"
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index 68639f323972..ddea4b3889eb 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -608,6 +608,7 @@ getfileattr (const char *path, bool caseinsensitive) /* path has to be always ab
       status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
 			   &attr, &io, FILE_SHARE_VALID_FLAGS,
 			   FILE_SYNCHRONOUS_IO_NONALERT
+			   | FILE_OPEN_NO_RECALL
 			   | FILE_OPEN_FOR_BACKUP_INTENT
 			   | FILE_DIRECTORY_FILE);
       if (NT_SUCCESS (status))
@@ -3208,7 +3209,8 @@ restart:
 	    }
 	  status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
 			       &attr, &io, FILE_SHARE_VALID_FLAGS,
-			       FILE_OPEN_REPARSE_POINT
+			       FILE_OPEN_NO_RECALL
+			       | FILE_OPEN_REPARSE_POINT
 			       | FILE_OPEN_FOR_BACKUP_INTENT);
 	  debug_printf ("%y = NtOpenFile (no-EAs %S)", status, &upath);
 	}
@@ -3336,6 +3338,7 @@ restart:
 	      status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
 				   &dattr, &io, FILE_SHARE_VALID_FLAGS,
 				   FILE_SYNCHRONOUS_IO_NONALERT
+				   | FILE_OPEN_NO_RECALL
 				   | FILE_OPEN_FOR_BACKUP_INTENT
 				   | FILE_DIRECTORY_FILE);
 	      if (!NT_SUCCESS (status))
@@ -3426,12 +3429,16 @@ restart:
 	 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;
+	goto file_not_symlink;
+
+      /* Offline files, even if reparse points, are not symlinks. */
+      if (isoffline (fileattr ()))
+	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. */
-      if ((fileattr () & FILE_ATTRIBUTE_REPARSE_POINT))
+      if (fileattr () & FILE_ATTRIBUTE_REPARSE_POINT)
 	{
 	  res = check_reparse_point (h, fs.is_remote_drive ());
 	  if (res > 0)
@@ -3474,7 +3481,8 @@ restart:
 
 	  status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
 			       FILE_SHARE_VALID_FLAGS,
-			       FILE_OPEN_FOR_BACKUP_INTENT
+			       FILE_OPEN_NO_RECALL
+			       | FILE_OPEN_FOR_BACKUP_INTENT
 			       | FILE_SYNCHRONOUS_IO_NONALERT);
 	  if (!NT_SUCCESS (status))
 	    res = 0;
@@ -3529,7 +3537,8 @@ restart:
 
 	  status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
 			       FILE_SHARE_VALID_FLAGS,
-			       FILE_OPEN_FOR_BACKUP_INTENT
+			       FILE_OPEN_NO_RECALL
+			       | FILE_OPEN_FOR_BACKUP_INTENT
 			       | FILE_SYNCHRONOUS_IO_NONALERT);
 
 	  if (!NT_SUCCESS (status))

                 reply	other threads:[~2024-03-08 19:57 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240308195739.27EA93858D33@sourceware.org \
    --to=corinna@sourceware.org \
    --cc=cygwin-cvs@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).