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