Hi
Updated patch: we use the posix semantic features in Windows build 17763
and up.
We found an issue with this feature on Windows Server 2016 without
updates (Windows 1607 Build 14393)
Victor Spirin
Postgres Professional:http://www.postgrespro.com
The Russian Postgres Company
05.07.2021 16:53, Victor Spirin пишет:
Hi
I used the SetFileInformationByHandle function with the
FILE_RENAME_FLAG_POSIX_SEMANTICS flag for the file rename function..
1) The _WIN32_WINNT variable needs to be increased to 0x0A00 (Windows
10). Fixed conflict with #undef CHECKSUM_TYPE_NONE
2) The SetFileInformationByHandle function works correctly only on
Windows 10 and higher.
The app must have a manifest to check the Windows version using the
IsWindows10OrGreater() function. I added a manifest to all postgres
projects and disabled the GenerateManifest option on windows projects.
This patch related to this post:
https://www.postgresql.org/message-id/CAEepm%3D0FV-k%2B%3Dd9z08cW%3DZXoR1%3Dkw9wdpkP6WAuOrKJdz-8ujg%40mail.gmail.com
diff --git a/src/include/common/checksum_helper.h
b/src/include/common/checksum_helper.h
index 23816cac21..59e819f9ca 100644
--- a/src/include/common/checksum_helper.h
+++ b/src/include/common/checksum_helper.h
@@ -26,6 +26,13 @@
* MD5 here because any new that does need a cryptographically strong checksum
* should use something better.
*/
+
+ /*
+ * CHECKSUM_TYPE_NONE defined in the winioctl.h when _WIN32_WINNT >=
_WIN32_WINNT_WIN10
+ */
+#ifdef CHECKSUM_TYPE_NONE
+#undef CHECKSUM_TYPE_NONE
+#endif
typedef enum pg_checksum_type
{
CHECKSUM_TYPE_NONE,
diff --git a/src/include/port/win32.h b/src/include/port/win32.h
index c6213c77c3..c48a6ce3a1 100644
--- a/src/include/port/win32.h
+++ b/src/include/port/win32.h
@@ -12,12 +12,13 @@
/*
* Make sure _WIN32_WINNT has the minimum required value.
* Leave a higher value in place. When building with at least Visual
- * Studio 2015 the minimum requirement is Windows Vista (0x0600) to
- * get support for GetLocaleInfoEx() with locales. For everything else
+ * Studio 2015 the minimum requirement is Windows 10 (0x0A00) to get support
for SetFileInformationByHandle.
+ * The minimum requirement is Windows Vista (0x0600) get support for
GetLocaleInfoEx() with locales.
+ * For everything else
* the minimum version is Windows XP (0x0501).
*/
#if defined(_MSC_VER) && _MSC_VER >= 1900
-#define MIN_WINNT 0x0600
+#define MIN_WINNT 0x0A00
#else
#define MIN_WINNT 0x0501
#endif
diff --git a/src/port/dirmod.c b/src/port/dirmod.c
index 7ce042e75d..199b285a8f 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -39,6 +39,200 @@
#endif
#endif
+
+#if defined(WIN32) && !defined(__CYGWIN__) && defined(_WIN32_WINNT_WIN10) &&
_WIN32_WINNT >= _WIN32_WINNT_WIN10
+
+#include <winternl.h>
+
+/*
+ * Checks Windows version using RtlGetVersion
+ * Version 1809 (Build 17763) is required for SetFileInformationByHandle
+ * function with FILE_RENAME_FLAG_POSIX_SEMANTICS flag
+*/
+typedef NTSYSAPI(NTAPI* PFN_RTLGETVERSION)
+(OUT PRTL_OSVERSIONINFOEXW lpVersionInformation);
+
+static int windowsPosixSemanticsUsable = -1;
+
+static bool
+is_windows_posix_semantics_usable()
+{
+ HMODULE ntdll;
+
+ PFN_RTLGETVERSION _RtlGetVersion = NULL;
+ OSVERSIONINFOEXW info;
+
+ if (windowsPosixSemanticsUsable >= 0)
+ return (windowsPosixSemanticsUsable > 0);
+
+ ntdll = LoadLibraryEx("ntdll.dll", NULL, 0);
+ if (ntdll == NULL)
+ {
+ DWORD err = GetLastError();
+
+ _dosmaperr(err);
+ return false;
+ }
+
+ _RtlGetVersion = (PFN_RTLGETVERSION)(pg_funcptr_t)
+ GetProcAddress(ntdll, "RtlGetVersion");
+ if (_RtlGetVersion == NULL)
+ {
+ DWORD err = GetLastError();
+
+ FreeLibrary(ntdll);
+ _dosmaperr(err);
+ return false;
+ }
+ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+ if (!NT_SUCCESS(_RtlGetVersion(&info)))
+ {
+ DWORD err = GetLastError();
+
+ FreeLibrary(ntdll);
+ _dosmaperr(err);
+ return false;
+ }
+
+ if (info.dwMajorVersion >= 10 && info.dwBuildNumber >= 17763)
+ windowsPosixSemanticsUsable = 1;
+ else
+ windowsPosixSemanticsUsable = 0;
+ FreeLibrary(ntdll);
+
+ return (windowsPosixSemanticsUsable > 0);
+}
+
+typedef struct _FILE_RENAME_INFO_EXT {
+ FILE_RENAME_INFO fri;
+ WCHAR extra_space[MAX_PATH];
+} FILE_RENAME_INFO_EXT;
+
+/*
+ * pgrename_windows_posix_semantics - uses SetFileInformationByHandle function
+ * with FILE_RENAME_FLAG_POSIX_SEMANTICS flag for atomic rename file
+ * working only on Windows 10 (1809) or later and _WIN32_WINNT must be >=
_WIN32_WINNT_WIN10
+ */
+static int
+pgrename_windows_posix_semantics(const char *from, const char *to)
+{
+ int err;
+ FILE_RENAME_INFO_EXT rename_info;
+ PFILE_RENAME_INFO prename_info;
+ HANDLE f_handle;
+ wchar_t from_w[MAX_PATH];
+
+ prename_info = (PFILE_RENAME_INFO)&rename_info;
+
+ if (MultiByteToWideChar(CP_ACP, 0, (LPCCH)from, -1, (LPWSTR)from_w,
MAX_PATH) == 0) {
+ err = GetLastError();
+ _dosmaperr(err);
+ return -1;
+ }
+ if (MultiByteToWideChar(CP_ACP, 0, (LPCCH)to, -1,
(LPWSTR)prename_info->FileName, MAX_PATH) == 0) {
+ err = GetLastError();
+ _dosmaperr(err);
+ return -1;
+ }
+ /*
+ * To open a directory using CreateFile, specify the
FILE_FLAG_BACKUP_SEMANTICS.
+ * We use the FILE_FLAG_OPEN_REPARSE_POINT flag
+ * If FILE_FLAG_OPEN_REPARSE_POINT is not specified: If an existing
file is opened and it is
+ * a symbolic link, the handle returned is a handle to the target.
+ */
+ f_handle = CreateFileW(from_w,
+ GENERIC_READ | GENERIC_WRITE | DELETE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+
+
+ if (f_handle == INVALID_HANDLE_VALUE)
+ {
+ err = GetLastError();
+
+ _dosmaperr(err);
+
+ return -1;
+ }
+
+
+ prename_info->ReplaceIfExists = TRUE;
+ prename_info->Flags = FILE_RENAME_FLAG_POSIX_SEMANTICS |
FILE_RENAME_FLAG_REPLACE_IF_EXISTS;
+
+ prename_info->RootDirectory = NULL;
+ prename_info->FileNameLength = wcslen(prename_info->FileName);
+
+ if (!SetFileInformationByHandle(f_handle, FileRenameInfoEx,
prename_info, sizeof(FILE_RENAME_INFO_EXT)))
+ {
+ err = GetLastError();
+
+ _dosmaperr(err);
+ CloseHandle(f_handle);
+ return -1;
+ }
+
+ CloseHandle(f_handle);
+ return 0;
+
+}
+
+/*
+ * pgunlink_windows_posix_semantics function
+ */
+#define FILE_DISPOSITION_DELETE 0x00000001
+#define FILE_DISPOSITION_POSIX_SEMANTICS 0x00000002
+
+typedef struct _FILE_DISPOSITION_INFORMATION_EX {
+ ULONG Flags;
+} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
+
+/*
+ * pgunlink_windows_posix_semantics - uses SetFileInformationByHandle function
+ * with FILE_DISPOSITION_POSIX_SEMANTICS flag for delete file
+ * working only on Windows 10 (1809) or later and _WIN32_WINNT must be >=
_WIN32_WINNT_WIN10
+ */
+int
+pgunlink_windows_posix_semantics(const char *path)
+{
+ int err;
+ HANDLE hFile;
+ FILE_DISPOSITION_INFO_EX fdi;
+
+ hFile = CreateFile(path, DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE |
FILE_SHARE_DELETE, 0, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
+
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ err = GetLastError();
+ _dosmaperr(err);
+ return -1;
+ }
+
+ fdi.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS;
+
+ if (!SetFileInformationByHandle(hFile, FileDispositionInfoEx, &fdi,
sizeof(fdi)))
+ {
+ err = GetLastError();
+
+ _dosmaperr(err);
+ CloseHandle(hFile);
+ return -1;
+ }
+
+ CloseHandle(hFile);
+
+ return 0;
+
+}
+
+
+#endif /* #if
defined(WIN32) && !defined(__CYGWIN__) && defined(_WIN32_WINNT_WIN10) &&
_WIN32_WINNT >= _WIN32_WINNT_WIN10 */
+
+
#if defined(WIN32) || defined(__CYGWIN__)
/*
@@ -49,6 +243,21 @@ pgrename(const char *from, const char *to)
{
int loops = 0;
+ /*
+ * Calls pgrename_windows_posix_semantics on Windows 10 and later when
+ * _WIN32_WINNT >= _WIN32_WINNT_WIN10.
+ */
+#if defined(_WIN32_WINNT_WIN10) \
+ && _WIN32_WINNT >= _WIN32_WINNT_WIN10 \
+ && !defined(__CYGWIN__)
+ if (is_windows_posix_semantics_usable())
+ {
+ if (pgrename_windows_posix_semantics(from, to) == 0)
+ return 0;
+ }
+#endif
+
+
/*
* We need to loop because even though PostgreSQL uses flags that allow
* rename while the file is open, other applications might have the file
@@ -99,6 +308,16 @@ int
pgunlink(const char *path)
{
int loops = 0;
+ /*
+ * Calls pgunlink_windows_posix_semantics on Windows 10 and later when
_WIN32_WINNT >= _WIN32_WINNT_WIN10.
+ */
+#if defined(_WIN32_WINNT_WIN10) && _WIN32_WINNT >= _WIN32_WINNT_WIN10 &&
!defined(__CYGWIN__)
+ if (is_windows_posix_semantics_usable())
+ {
+ if (pgunlink_windows_posix_semantics(path) == 0)
+ return 0;
+ }
+#endif
/*
* We need to loop because even though PostgreSQL uses flags that allow