Thank you,

Fixed FILE_RENAME_INFO structure

I prepared 2 versions of the patch:

1) with manifest and IsWindows10OrGreater() function
2) without manifest and RtlGetVersion function from ntdll.dll

What's better?

Victor Spirin
Postgres Professional:http://www.postgrespro.com
The Russian Postgres Company

23.09.2021 14:46, Thomas Munro пишет:
On Wed, Sep 8, 2021 at 9:40 AM Victor Spirin <v.spi...@postgrespro.ru> wrote:
Is this code better? Maybe there is another correct method?
Hmm, if we want to use the system header's struct definition, add some
space for a path at the end, and avoid heap allocation, perhaps we
could do something like:

struct {
     FILE_RENAME_INFO fri;
     WCHAR extra_space[MAX_PATH];
} x;


diff --git a/src/include/common/checksum_helper.h 
b/src/include/common/checksum_helper.h
index cac7570ea1..2d533c93a6 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 d8ae49e22d..d91555f5c0 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 763bc5f915..548b450b41 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -39,6 +39,87 @@
 #endif
 #endif
 
+
+#if defined(WIN32) && !defined(__CYGWIN__) && defined(_WIN32_WINNT_WIN10) && 
_WIN32_WINNT >= _WIN32_WINNT_WIN10
+
+#include <versionhelpers.h>
+
+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 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;
+       }
+
+       f_handle = CreateFileW(from_w,
+               GENERIC_READ | GENERIC_WRITE | DELETE,
+               FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+               NULL,
+               OPEN_EXISTING,
+               FILE_ATTRIBUTE_NORMAL,
+               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;
+
+}
+
+#endif                                                         /* #if 
defined(WIN32) && !defined(__CYGWIN__) && defined(_WIN32_WINNT_WIN10) && 
_WIN32_WINNT >= _WIN32_WINNT_WIN10 */
+
+
 #if defined(WIN32) || defined(__CYGWIN__)
 
 /*
@@ -49,6 +130,16 @@ 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 (IsWindows10OrGreater()) {
+               return pgrename_windows_posix_semantics(from, to);
+       }
+#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
diff --git a/src/tools/msvc/MSBuildProject.pm b/src/tools/msvc/MSBuildProject.pm
index fdd22e89eb..2fb39b9576 100644
--- a/src/tools/msvc/MSBuildProject.pm
+++ b/src/tools/msvc/MSBuildProject.pm
@@ -375,6 +375,9 @@ EOF
     <ResourceCompile>
       
<AdditionalIncludeDirectories>src\\include;\%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ResourceCompile>
+    <Manifest>
+      
<AdditionalManifestFiles>src/port/windows.manifest</AdditionalManifestFiles>
+    </Manifest>
 EOF
        if ($self->{builddef})
        {
diff --git a/src/port/windows.manifest b/src/port/windows.manifest
new file mode 100644
index 00000000000..fd3a344bee0
--- /dev/null
+++ b/src/port/windows.manifest
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
+
+  <!-- Enable use of version 6 of the common controls (Win XP and later) -->
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity type="win32"
+                        name="Microsoft.Windows.Common-Controls"
+                        version="6.0.0.0"
+                        processorArchitecture="*"
+                        publicKeyToken="6595b64144ccf1df"
+                        language="*" />
+    </dependentAssembly>
+  </dependency>
+
+  <!-- Indicate UAC compliance, with no need for elevated privileges (Win 
Vista and later) -->
+  <!-- (if you need enhanced privileges, set the level to "highestAvailable" 
or "requireAdministrator") -->
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <!-- Indicate high API awareness (Win Vista and later) -->
+  <!-- (if you support per-monitor high DPI, set this to "True/PM") -->
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware 
xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings";>false</dpiAware>
+    </windowsSettings>
+  </application>
+
+  <!-- Declare support for various versions of Windows -->
+  <ms_compatibility:compatibility 
xmlns:ms_compatibility="urn:schemas-microsoft-com:compatibility.v1" 
xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <ms_compatibility:application>
+      <!-- Windows Vista/Server 2008 -->
+      <ms_compatibility:supportedOS 
Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
+      <!-- Windows 7/Server 2008 R2 -->
+      <ms_compatibility:supportedOS 
Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+      <!-- Windows 8/Server 2012 -->
+      <ms_compatibility:supportedOS 
Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+      <!-- Windows 8.1/Server 2012 R2 -->
+      <ms_compatibility:supportedOS 
Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+      <!-- Windows 10 -->
+      <ms_compatibility:supportedOS 
Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+    </ms_compatibility:application>
+  </ms_compatibility:compatibility>
+
+</assembly>
diff --git a/src/include/common/checksum_helper.h 
b/src/include/common/checksum_helper.h
index cac7570ea1..2d533c93a6 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 d8ae49e22d..d91555f5c0 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 763bc5f915..680be08055 100644
--- a/src/port/dirmod.c
+++ b/src/port/dirmod.c
@@ -39,6 +39,136 @@
 #endif
 #endif
 
+
+#if defined(WIN32) && !defined(__CYGWIN__) && defined(_WIN32_WINNT_WIN10) && 
_WIN32_WINNT >= _WIN32_WINNT_WIN10
+
+//#include <versionhelpers.h>
+#include <winternl.h>
+
+/*
+ * Checks Windows version using RtlGetVersion
+*/
+typedef NTSYSAPI(NTAPI * PFN_RTLGETVERSION)
+(OUT PRTL_OSVERSIONINFOEXW lpVersionInformation);
+
+static int isWin10 = -1;
+static int isWindows10OrMore()
+{
+       HMODULE ntdll;
+       PFN_RTLGETVERSION _RtlGetVersion = NULL;
+       OSVERSIONINFOEXW info;
+       if (isWin10 >= 0) return isWin10;
+       ntdll = LoadLibraryEx("ntdll.dll", NULL, 0);
+       if (ntdll == NULL)
+       {
+               DWORD           err = GetLastError();
+
+               _dosmaperr(err);
+               return -1;
+       }
+
+       _RtlGetVersion = (PFN_RTLGETVERSION)(pg_funcptr_t)
+               GetProcAddress(ntdll, "RtlGetVersion");
+       if (_RtlGetVersion == NULL)
+       {
+               DWORD           err = GetLastError();
+
+               FreeLibrary(ntdll);
+               _dosmaperr(err);
+               return -1;
+       }
+       info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+       if (!NT_SUCCESS(_RtlGetVersion(&info)))
+       {
+               DWORD           err = GetLastError();
+
+               FreeLibrary(ntdll);
+               _dosmaperr(err);
+               return -1;
+       }
+
+       if (info.dwMajorVersion >= 10) isWin10 = 1;
+       else isWin10 = 0;
+       FreeLibrary(ntdll);
+       return isWin10;
+
+}
+
+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 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;
+       }
+
+       f_handle = CreateFileW(from_w,
+               GENERIC_READ | GENERIC_WRITE | DELETE,
+               FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+               NULL,
+               OPEN_EXISTING,
+               FILE_ATTRIBUTE_NORMAL,
+               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;
+
+}
+
+#endif                                                         /* #if 
defined(WIN32) && !defined(__CYGWIN__) && defined(_WIN32_WINNT_WIN10) && 
_WIN32_WINNT >= _WIN32_WINNT_WIN10 */
+
+
 #if defined(WIN32) || defined(__CYGWIN__)
 
 /*
@@ -49,6 +179,16 @@ 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 (isWindows10OrMore()>0) {
+               return pgrename_windows_posix_semantics(from, to);
+       }
+#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

Reply via email to