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

Reply via email to