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..12e9c35cef 100644 --- a/src/port/dirmod.c +++ b/src/port/dirmod.c @@ -39,6 +39,146 @@ #endif #endif + +#if defined(WIN32) && !defined(__CYGWIN__) && defined(_WIN32_WINNT_WIN10) && _WIN32_WINNT >= _WIN32_WINNT_WIN10 + +#include + +/* + * Checks Windows version using RtlGetVersion + * Version 1607 (Build 14393) is required for SetFileInformationByHandle + * function with FILE_RENAME_FLAG_POSIX_SEMANTICS flag +*/ +typedef NTSYSAPI(NTAPI * PFN_RTLGETVERSION) +(OUT PRTL_OSVERSIONINFOEXW lpVersionInformation); + +static int isWin1607 = -1; +static int isWindows1607OrGreater() +{ + HMODULE ntdll; + PFN_RTLGETVERSION _RtlGetVersion = NULL; + OSVERSIONINFOEXW info; + if (isWin1607 >= 0) return isWin1607; + 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 && info.dwBuildNumber >= 14393) isWin1607 = 1; + else isWin1607 = 0; + FreeLibrary(ntdll); + return isWin1607; + +} + +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 (1607) 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]; + DWORD dwFlagsAndAttributes; + DWORD dwAttrib; + + + 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 flag as part of dwFlagsAndAttributes. + */ + dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; + + dwAttrib = GetFileAttributes(from_w); + if(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) dwFlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS; + + f_handle = CreateFileW(from_w, + GENERIC_READ | GENERIC_WRITE | DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + dwFlagsAndAttributes, + 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 +189,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 (isWindows1607OrGreater()>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