summaryrefslogtreecommitdiff
path: root/src/port/win32stat.c
diff options
context:
space:
mode:
authorThomas Munro <tmunro@postgresql.org>2021-12-10 16:13:14 +1300
committerAndrew Dunstan <andrew@dunslane.net>2024-11-08 09:44:46 +1030
commit56b39cce778f93cd95a01df0da083e937424662d (patch)
treeb26b71a1bde150efe5d9d943a04bb26bc2232108 /src/port/win32stat.c
parentb4363fc66e642b70f88455004e5bc6d67c65cf71 (diff)
Check for STATUS_DELETE_PENDING on Windows.
1. Update our open() wrapper to check for NT's STATUS_DELETE_PENDING and translate it to Unix-like errors. This is done with RtlGetLastNtStatus(), which is dynamically loaded from ntdll. A new file win32ntdll.c centralizes lookup of NT functions, in case we decide to add more in the future. 2. Remove non-working code that was trying to do something similar for stat(), and just reuse the open() wrapper code. As a side effect, stat() also gains resilience against "sharing violation" errors. 3. Since stat() is used very early in process startup, remove the requirement that the Win32 signal event has been created before pgwin32_open_handle() is reached. Instead, teach pg_usleep() to fall back to a non-interruptible sleep if reached before the signal event is available. This could be back-patched, but for now it's in master only. The problem has apparently been with us for a long time and generated only a few complaints. Proposed patches trigger it more often, which led to this investigation and fix. Reviewed-by: Andres Freund <andres@anarazel.de> Reviewed-by: Alexander Lakhin <exclusion@gmail.com> Reviewed-by: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com> Discussion: https://postgr.es/m/CA%2BhUKGJz_pZTF9mckn6XgSv69%2BjGwdgLkxZ6b3NWGLBCVjqUZA%40mail.gmail.com (cherry picked from commit e2f0f8ed251d02c1eda79e1ca3cb3db2681e7a86) Author: Thomas Munro <tmunro@postgresql.org> Author: Alexandra Wang <alexandra.wang.oss@gmail.com>
Diffstat (limited to 'src/port/win32stat.c')
-rw-r--r--src/port/win32stat.c78
1 files changed, 6 insertions, 72 deletions
diff --git a/src/port/win32stat.c b/src/port/win32stat.c
index e09589c165b..6f7cd09087d 100644
--- a/src/port/win32stat.c
+++ b/src/port/win32stat.c
@@ -115,84 +115,18 @@ int
_pgstat64(const char *name, struct stat *buf)
{
/*
- * We must use a handle so lstat() returns the information of the target
- * file. To have a reliable test for ERROR_DELETE_PENDING, this uses a
- * method similar to open() with a loop using stat() and some waits when
- * facing ERROR_ACCESS_DENIED.
+ * Our open wrapper will report STATUS_DELETE_PENDING as ENOENT. We
+ * request FILE_FLAG_BACKUP_SEMANTICS so that we can open directories too,
+ * for limited purposes. We use the private handle-based version, so we
+ * don't risk running out of fds.
*/
- SECURITY_ATTRIBUTES sa;
HANDLE hFile;
int ret;
- int loops = 0;
- if (name == NULL || buf == NULL)
- {
- errno = EINVAL;
- return -1;
- }
- /* fast not-exists check */
- if (GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES)
- {
- DWORD err = GetLastError();
-
- if (err != ERROR_ACCESS_DENIED)
- {
- _dosmaperr(err);
- return -1;
- }
- }
-
- /* get a file handle as lightweight as we can */
- sa.nLength = sizeof(SECURITY_ATTRIBUTES);
- sa.bInheritHandle = TRUE;
- sa.lpSecurityDescriptor = NULL;
- while ((hFile = CreateFile(name,
- GENERIC_READ,
- (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
- &sa,
- OPEN_EXISTING,
- (FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS |
- FILE_FLAG_OVERLAPPED),
- NULL)) == INVALID_HANDLE_VALUE)
- {
- DWORD err = GetLastError();
-
- /*
- * ERROR_ACCESS_DENIED is returned if the file is deleted but not yet
- * gone (Windows NT status code is STATUS_DELETE_PENDING). In that
- * case we want to wait a bit and try again, giving up after 1 second
- * (since this condition should never persist very long). However,
- * there are other commonly-hit cases that return ERROR_ACCESS_DENIED,
- * so care is needed. In particular that happens if we try to open a
- * directory, or of course if there's an actual file-permissions
- * problem. To distinguish these cases, try a stat(). In the
- * delete-pending case, it will either also get STATUS_DELETE_PENDING,
- * or it will see the file as gone and fail with ENOENT. In other
- * cases it will usually succeed. The only somewhat-likely case where
- * this coding will uselessly wait is if there's a permissions problem
- * with a containing directory, which we hope will never happen in any
- * performance-critical code paths.
- */
- if (err == ERROR_ACCESS_DENIED)
- {
- if (loops < 10)
- {
- struct microsoft_native_stat st;
-
- if (microsoft_native_stat(name, &st) != 0)
- {
- pg_usleep(100000);
- loops++;
- continue;
- }
- }
- }
-
- _dosmaperr(err);
+ hFile = pgwin32_open_handle(name, O_RDONLY, true);
+ if (hFile == INVALID_HANDLE_VALUE)
return -1;
- }
- /* At last we can invoke fileinfo_to_stat */
ret = fileinfo_to_stat(hFile, buf);
CloseHandle(hFile);