mirror of https://github.com/postgres/postgres
Hack things so that our idea of "struct stat" is equivalent to Windows' struct __stat64, allowing it to have a wide enough st_size field. Instead of relying on native stat(), use GetFileInformationByHandle(). This avoids a number of issues with Microsoft's multiple and rather slipshod emulations of stat(). We still need to jump through hoops to deal with ERROR_DELETE_PENDING, though :-( Pull the relevant support code out of dirmod.c and put it into its own file, win32stat.c. Still TODO: do we need to do something different with lstat(), rather than treating it identically to stat()? Juan José Santamaría Flecha, reviewed by Emil Iggland; based on prior work by Michael Paquier, Sergey Zubkovsky, and others Discussion: https://postgr.es/m/1803D792815FC24D871C00D17AE95905CF5099@g01jpexmbkw24 Discussion: https://postgr.es/m/15858-9572469fd3b73263@postgresql.orgpull/57/head
parent
f13f2e4841
commit
bed90759fc
@ -0,0 +1,299 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* |
||||||
|
* win32stat.c |
||||||
|
* Replacements for <sys/stat.h> functions using GetFileInformationByHandle |
||||||
|
* |
||||||
|
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group |
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California |
||||||
|
* |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* src/port/win32stat.c |
||||||
|
* |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifdef WIN32 |
||||||
|
|
||||||
|
#include "c.h" |
||||||
|
#include <windows.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
* In order to support MinGW and MSVC2013 we use NtQueryInformationFile as an |
||||||
|
* alternative for GetFileInformationByHandleEx. It is loaded from the ntdll |
||||||
|
* library. |
||||||
|
*/ |
||||||
|
#if _WIN32_WINNT < 0x0600 |
||||||
|
#include <winternl.h> |
||||||
|
|
||||||
|
#if !defined(__MINGW32__) && !defined(__MINGW64__) |
||||||
|
/* MinGW includes this in <winternl.h>, but it is missing in MSVC */ |
||||||
|
typedef struct _FILE_STANDARD_INFORMATION |
||||||
|
{ |
||||||
|
LARGE_INTEGER AllocationSize; |
||||||
|
LARGE_INTEGER EndOfFile; |
||||||
|
ULONG NumberOfLinks; |
||||||
|
BOOLEAN DeletePending; |
||||||
|
BOOLEAN Directory; |
||||||
|
} FILE_STANDARD_INFORMATION; |
||||||
|
#define FileStandardInformation 5 |
||||||
|
#endif /* !defined(__MINGW32__) && |
||||||
|
* !defined(__MINGW64__) */ |
||||||
|
|
||||||
|
typedef NTSTATUS(NTAPI * PFN_NTQUERYINFORMATIONFILE) |
||||||
|
( |
||||||
|
IN HANDLE FileHandle, |
||||||
|
OUT PIO_STATUS_BLOCK IoStatusBlock, |
||||||
|
OUT PVOID FileInformation, |
||||||
|
IN ULONG Length, |
||||||
|
IN FILE_INFORMATION_CLASS FileInformationClass |
||||||
|
); |
||||||
|
|
||||||
|
static PFN_NTQUERYINFORMATIONFILE _NtQueryInformationFile = NULL; |
||||||
|
|
||||||
|
static HMODULE ntdll = NULL; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Load DLL file just once regardless of how many functions we load/call in it. |
||||||
|
*/ |
||||||
|
static void |
||||||
|
LoadNtdll(void) |
||||||
|
{ |
||||||
|
if (ntdll != NULL) |
||||||
|
return; |
||||||
|
ntdll = LoadLibraryEx("ntdll.dll", NULL, 0); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* _WIN32_WINNT < 0x0600 */ |
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a FILETIME struct into a 64 bit time_t. |
||||||
|
*/ |
||||||
|
static __time64_t |
||||||
|
filetime_to_time(const FILETIME *ft) |
||||||
|
{ |
||||||
|
ULARGE_INTEGER unified_ft = {0}; |
||||||
|
static const uint64 EpochShift = UINT64CONST(116444736000000000); |
||||||
|
|
||||||
|
unified_ft.LowPart = ft->dwLowDateTime; |
||||||
|
unified_ft.HighPart = ft->dwHighDateTime; |
||||||
|
|
||||||
|
if (unified_ft.QuadPart < EpochShift) |
||||||
|
return -1; |
||||||
|
|
||||||
|
unified_ft.QuadPart -= EpochShift; |
||||||
|
unified_ft.QuadPart /= 10 * 1000 * 1000; |
||||||
|
|
||||||
|
return unified_ft.QuadPart; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert WIN32 file attributes to a Unix-style mode. |
||||||
|
* |
||||||
|
* Only owner permissions are set. |
||||||
|
*/ |
||||||
|
static unsigned short |
||||||
|
fileattr_to_unixmode(int attr) |
||||||
|
{ |
||||||
|
unsigned short uxmode = 0; |
||||||
|
|
||||||
|
uxmode |= (unsigned short) ((attr & FILE_ATTRIBUTE_DIRECTORY) ? |
||||||
|
(_S_IFDIR) : (_S_IFREG)); |
||||||
|
|
||||||
|
uxmode |= (unsigned short) (attr & FILE_ATTRIBUTE_READONLY) ? |
||||||
|
(_S_IREAD) : (_S_IREAD | _S_IWRITE); |
||||||
|
|
||||||
|
/* there is no need to simulate _S_IEXEC using CMD's PATHEXT extensions */ |
||||||
|
uxmode |= _S_IEXEC; |
||||||
|
|
||||||
|
return uxmode; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert WIN32 file information (from a HANDLE) to a struct stat. |
||||||
|
*/ |
||||||
|
static int |
||||||
|
fileinfo_to_stat(HANDLE hFile, struct stat *buf) |
||||||
|
{ |
||||||
|
BY_HANDLE_FILE_INFORMATION fiData; |
||||||
|
|
||||||
|
memset(buf, 0, sizeof(*buf)); |
||||||
|
|
||||||
|
/*
|
||||||
|
* GetFileInformationByHandle minimum supported version: Windows XP and |
||||||
|
* Windows Server 2003, so it exists everywhere we care about. |
||||||
|
*/ |
||||||
|
if (!GetFileInformationByHandle(hFile, &fiData)) |
||||||
|
{ |
||||||
|
_dosmaperr(GetLastError()); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
if (fiData.ftLastWriteTime.dwLowDateTime || |
||||||
|
fiData.ftLastWriteTime.dwHighDateTime) |
||||||
|
buf->st_mtime = filetime_to_time(&fiData.ftLastWriteTime); |
||||||
|
|
||||||
|
if (fiData.ftLastAccessTime.dwLowDateTime || |
||||||
|
fiData.ftLastAccessTime.dwHighDateTime) |
||||||
|
buf->st_atime = filetime_to_time(&fiData.ftLastAccessTime); |
||||||
|
else |
||||||
|
buf->st_atime = buf->st_mtime; |
||||||
|
|
||||||
|
if (fiData.ftCreationTime.dwLowDateTime || |
||||||
|
fiData.ftCreationTime.dwHighDateTime) |
||||||
|
buf->st_ctime = filetime_to_time(&fiData.ftCreationTime); |
||||||
|
else |
||||||
|
buf->st_ctime = buf->st_mtime; |
||||||
|
|
||||||
|
buf->st_mode = fileattr_to_unixmode(fiData.dwFileAttributes); |
||||||
|
buf->st_nlink = fiData.nNumberOfLinks; |
||||||
|
|
||||||
|
buf->st_size = (((uint64) fiData.nFileSizeHigh) << 32) | |
||||||
|
(uint64) fiData.nFileSizeLow; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows implementation of stat(). |
||||||
|
* |
||||||
|
* This currently also implements lstat(), though perhaps that should change. |
||||||
|
*/ |
||||||
|
int |
||||||
|
_pgstat64(const char *name, struct stat *buf) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* We must use a handle so lstat() returns the information of the target |
||||||
|
* file. To have a reliable test for ERROR_DELETE_PENDING, we use |
||||||
|
* NtQueryInformationFile from Windows 2000 or |
||||||
|
* GetFileInformationByHandleEx from Server 2008 / Vista. |
||||||
|
*/ |
||||||
|
SECURITY_ATTRIBUTES sa; |
||||||
|
HANDLE hFile; |
||||||
|
int ret; |
||||||
|
#if _WIN32_WINNT < 0x0600 |
||||||
|
IO_STATUS_BLOCK ioStatus; |
||||||
|
FILE_STANDARD_INFORMATION standardInfo; |
||||||
|
#else |
||||||
|
FILE_STANDARD_INFO standardInfo; |
||||||
|
#endif |
||||||
|
|
||||||
|
if (name == NULL || buf == NULL) |
||||||
|
{ |
||||||
|
errno = EINVAL; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/* fast not-exists check */ |
||||||
|
if (GetFileAttributes(name) == INVALID_FILE_ATTRIBUTES) |
||||||
|
{ |
||||||
|
_dosmaperr(GetLastError()); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/* get a file handle as lightweight as we can */ |
||||||
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
||||||
|
sa.bInheritHandle = TRUE; |
||||||
|
sa.lpSecurityDescriptor = NULL; |
||||||
|
hFile = CreateFile(name, |
||||||
|
GENERIC_READ, |
||||||
|
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), |
||||||
|
&sa, |
||||||
|
OPEN_EXISTING, |
||||||
|
(FILE_FLAG_NO_BUFFERING | FILE_FLAG_BACKUP_SEMANTICS | |
||||||
|
FILE_FLAG_OVERLAPPED), |
||||||
|
NULL); |
||||||
|
if (hFile == INVALID_HANDLE_VALUE) |
||||||
|
{ |
||||||
|
CloseHandle(hFile); |
||||||
|
errno = ENOENT; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
memset(&standardInfo, 0, sizeof(standardInfo)); |
||||||
|
|
||||||
|
#if _WIN32_WINNT < 0x0600 |
||||||
|
if (_NtQueryInformationFile == NULL) |
||||||
|
{ |
||||||
|
/* First time through: load ntdll.dll and find NtQueryInformationFile */ |
||||||
|
LoadNtdll(); |
||||||
|
if (ntdll == NULL) |
||||||
|
{ |
||||||
|
_dosmaperr(GetLastError()); |
||||||
|
CloseHandle(hFile); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
_NtQueryInformationFile = (PFN_NTQUERYINFORMATIONFILE) |
||||||
|
GetProcAddress(ntdll, "NtQueryInformationFile"); |
||||||
|
if (_NtQueryInformationFile == NULL) |
||||||
|
{ |
||||||
|
_dosmaperr(GetLastError()); |
||||||
|
CloseHandle(hFile); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!NT_SUCCESS(_NtQueryInformationFile(hFile, &ioStatus, &standardInfo, |
||||||
|
sizeof(standardInfo), |
||||||
|
FileStandardInformation))) |
||||||
|
{ |
||||||
|
_dosmaperr(GetLastError()); |
||||||
|
CloseHandle(hFile); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
#else |
||||||
|
if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &standardInfo, |
||||||
|
sizeof(standardInfo))) |
||||||
|
{ |
||||||
|
_dosmaperr(GetLastError()); |
||||||
|
CloseHandle(hFile); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
#endif /* _WIN32_WINNT < 0x0600 */ |
||||||
|
|
||||||
|
if (standardInfo.DeletePending) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* File has been deleted, but is not gone from the filesystem yet. |
||||||
|
* This can happen when some process with FILE_SHARE_DELETE has it |
||||||
|
* open, and it will be fully removed once that handle is closed. |
||||||
|
* Meanwhile, we can't open it, so indicate that the file just doesn't |
||||||
|
* exist. |
||||||
|
*/ |
||||||
|
CloseHandle(hFile); |
||||||
|
errno = ENOENT; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/* At last we can invoke fileinfo_to_stat */ |
||||||
|
ret = fileinfo_to_stat(hFile, buf); |
||||||
|
|
||||||
|
CloseHandle(hFile); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows implementation of fstat(). |
||||||
|
*/ |
||||||
|
int |
||||||
|
_pgfstat64(int fileno, struct stat *buf) |
||||||
|
{ |
||||||
|
HANDLE hFile = (HANDLE) _get_osfhandle(fileno); |
||||||
|
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE || buf == NULL) |
||||||
|
{ |
||||||
|
errno = EINVAL; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Since we already have a file handle there is no need to check for |
||||||
|
* ERROR_DELETE_PENDING. |
||||||
|
*/ |
||||||
|
|
||||||
|
return fileinfo_to_stat(hFile, buf); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* WIN32 */ |
Loading…
Reference in new issue