| /*------------------------------------------------------------------------- |
| * |
| * win32stat.c |
| * Replacements for <sys/stat.h> functions using GetFileInformationByHandle |
| * |
| * Portions Copyright (c) 1996-2021, 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) | |
| 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) |
| { |
| DWORD err = GetLastError(); |
| |
| CloseHandle(hFile); |
| _dosmaperr(err); |
| 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) |
| { |
| DWORD err = GetLastError(); |
| |
| CloseHandle(hFile); |
| _dosmaperr(err); |
| return -1; |
| } |
| |
| _NtQueryInformationFile = (PFN_NTQUERYINFORMATIONFILE) (pg_funcptr_t) |
| GetProcAddress(ntdll, "NtQueryInformationFile"); |
| if (_NtQueryInformationFile == NULL) |
| { |
| DWORD err = GetLastError(); |
| |
| CloseHandle(hFile); |
| _dosmaperr(err); |
| return -1; |
| } |
| } |
| |
| if (!NT_SUCCESS(_NtQueryInformationFile(hFile, &ioStatus, &standardInfo, |
| sizeof(standardInfo), |
| FileStandardInformation))) |
| { |
| DWORD err = GetLastError(); |
| |
| CloseHandle(hFile); |
| _dosmaperr(err); |
| return -1; |
| } |
| #else |
| if (!GetFileInformationByHandleEx(hFile, FileStandardInfo, &standardInfo, |
| sizeof(standardInfo))) |
| { |
| DWORD err = GetLastError(); |
| |
| CloseHandle(hFile); |
| _dosmaperr(err); |
| 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); |
| BY_HANDLE_FILE_INFORMATION fiData; |
| |
| if (hFile == INVALID_HANDLE_VALUE || buf == NULL) |
| { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| /* |
| * Check if the fileno is a data stream. If so, unless it has been |
| * redirected to a file, getting information through its HANDLE will fail, |
| * so emulate its stat information in the most appropriate way and return |
| * it instead. |
| */ |
| if ((fileno == _fileno(stdin) || |
| fileno == _fileno(stdout) || |
| fileno == _fileno(stderr)) && |
| !GetFileInformationByHandle(hFile, &fiData)) |
| { |
| memset(buf, 0, sizeof(*buf)); |
| buf->st_mode = _S_IFCHR; |
| buf->st_dev = fileno; |
| buf->st_rdev = fileno; |
| buf->st_nlink = 1; |
| return 0; |
| } |
| |
| /* |
| * 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 */ |