From 80fb66c1feb783ee6366aa3e1315395205060021 Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha Date: Mon, 24 Jun 2019 12:56:13 -0400 Subject: [PATCH] WIP support for large files on Win32 . Workaround for Windows shortcomings, reimplement fstat()/stat() on top of GetFileAttributesEx() and use 64 bits struct stat. --- configure | 11 ++++ src/include/port/win32_port.h | 62 +++++++++++++++--- src/port/dirmod.c | 52 --------------- src/port/stat_pg_fixed.c | 149 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 60 deletions(-) create mode 100644 src/port/stat_pg_fixed.c diff --git a/configure b/configure index 8d47071..e8dcfcb 100755 --- a/configure +++ b/configure @@ -16204,6 +16204,12 @@ fi esac case " $LIBOBJS " in + *" stat_pg_fixed.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS stat_pg_fixed.$ac_objext" + ;; +esac + + case " $LIBOBJS " in *" kill.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS kill.$ac_objext" ;; @@ -16282,6 +16288,11 @@ if test "$PORTNAME" = "cygwin"; then ;; esac + case " $LIBOBJS " in + *" stat_pg_fixed.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS stat_pg_fixed.$ac_objext" + ;; +esac fi ac_fn_c_check_func "$LINENO" "syslog" "ac_cv_func_syslog" diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h index f4841fb..fd1620b 100644 --- a/src/include/port/win32_port.h +++ b/src/include/port/win32_port.h @@ -52,7 +52,12 @@ #include #include /* for non-unicode version */ #undef near -#include /* needed before sys/stat hacking below */ +/* needed before sys/stat hacking below */ +#define fstat microsoft_native_fstat +#define stat microsoft_native_stat +#include +#undef fstat +#undef stat /* Must be here to avoid conflicting with prototype in windows.h */ #define mkdir(a,b) mkdir(a) @@ -254,15 +259,56 @@ typedef int pid_t; /* * stat() is not guaranteed to set the st_size field on win32, so we - * redefine it to our own implementation that is. + * redefine it to our own implementation. See src/port/stat_pg_fixed.c * - * Some frontends don't need the size from stat, so if UNSAFE_STAT_OK - * is defined we don't bother with this. + * The struct stat is 32 bit in MSVC so we redefine it as a copy of struct + * _stat64. This also fixes the struct size across Cygwin and MINGW builds. */ -#ifndef UNSAFE_STAT_OK -extern int pgwin32_safestat(const char *path, struct stat *buf); -#define stat(a,b) pgwin32_safestat(a,b) -#endif +struct stat /* It is actually _stat64 */ +{ + _dev_t st_dev; + _ino_t st_ino; + unsigned short st_mode; + short st_nlink; + short st_uid; + short st_gid; + _dev_t st_rdev; + __int64 st_size; + __time64_t st_atime; + __time64_t st_mtime; + __time64_t st_ctime; +}; +extern int _pgfstat64(int fileno, struct stat *buf); +extern int _pgstat64(char const *name, struct stat *buf); +#if defined(_MSC_VER) +static __inline int __CRTDECL +fstat(int _Filenumber, struct stat* _Stat) +{ + _STATIC_ASSERT(sizeof(struct stat) == sizeof(struct __stat64)); + return _pgfstat64(_Filenumber, _Stat); +} + +static __inline int __CRTDECL +stat(char* const _Filename, struct stat* _Stat) +{ + _STATIC_ASSERT(sizeof(struct stat) == sizeof(struct __stat64)); + return _pgstat64(_Filename, _Stat); +} +#else /* defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) */ +static __inline__ int +fstat(int _Filenumber, struct stat* _Stat) +{ + Assert(sizeof(struct stat) == sizeof(struct __stat64)); + return _pgfstat64(_Filenumber, _Stat); +} + +static __inline__ int +stat(char* const _Filename, struct stat* _Stat) +{ + Assert(sizeof(struct stat) == sizeof(struct __stat64)); + return _pgstat64(_Filename, _Stat); +} +#endif /* defined(_MSC_VER) */ /* These macros are not provided by older MinGW, nor by MSVC */ #ifndef S_IRUSR diff --git a/src/port/dirmod.c b/src/port/dirmod.c index d793240..ec4b47f 100644 --- a/src/port/dirmod.c +++ b/src/port/dirmod.c @@ -353,55 +353,3 @@ pgwin32_is_junction(const char *path) return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT); } #endif /* defined(WIN32) && !defined(__CYGWIN__) */ - - -#if defined(WIN32) && !defined(__CYGWIN__) - -#undef stat - -/* - * The stat() function in win32 is not guaranteed to update the st_size - * field when run. So we define our own version that uses the Win32 API - * to update this field. - */ -int -pgwin32_safestat(const char *path, struct stat *buf) -{ - int r; - WIN32_FILE_ATTRIBUTE_DATA attr; - - r = stat(path, buf); - if (r < 0) - { - if (GetLastError() == ERROR_DELETE_PENDING) - { - /* - * 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. - */ - errno = ENOENT; - return -1; - } - - return r; - } - - if (!GetFileAttributesEx(path, GetFileExInfoStandard, &attr)) - { - _dosmaperr(GetLastError()); - return -1; - } - - /* - * XXX no support for large files here, but we don't do that in general on - * Win32 yet. - */ - buf->st_size = attr.nFileSizeLow; - - return 0; -} - -#endif diff --git a/src/port/stat_pg_fixed.c b/src/port/stat_pg_fixed.c new file mode 100644 index 0000000..98de7c4 --- /dev/null +++ b/src/port/stat_pg_fixed.c @@ -0,0 +1,149 @@ +/*------------------------------------------------------------------------- + * + * stat_pg_fixed.c + * Replacements for functions using GetFileAttributesEx on + * Win32. + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/stat_pg_fixed.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 + +#include "c.h" +#include +#if defined(__CYGWIN__) +#include +#else +#include +#endif + +static const unsigned __int64 EpochShift = UINT64CONST(116444736000000000); + +/* Converts FILETIME to time_t */ +static __time64_t +cvt_ft2ut(FILETIME const * ft) +{ + ULARGE_INTEGER unified_ft; + + 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 ((__int64) unified_ft.HighPart) << 32 | + (__int64) unified_ft.LowPart; +} + +static unsigned short +cvt_attr2uxmode(int attr, const _TCHAR * name) +{ + unsigned short uxmode; + const _TCHAR * p; + + uxmode = (unsigned short)((attr & FILE_ATTRIBUTE_DIRECTORY) ? + (_S_IFDIR | _S_IEXEC) : (_S_IFREG)); + + uxmode |= (unsigned short)(attr & FILE_ATTRIBUTE_READONLY) ? + (_S_IREAD) : (_S_IREAD | _S_IWRITE); + + if (p = _tcsrchr(name, _T('.'))) + { + if (! _tcsicmp(p, _T(".exe")) || + ! _tcsicmp(p, _T(".cmd")) || + ! _tcsicmp(p, _T(".bat")) || + ! _tcsicmp(p, _T(".com"))) + uxmode |= _S_IEXEC; + } + + uxmode |= (uxmode & 0700) >> 3; + uxmode |= (uxmode & 0700) >> 6; + + return uxmode; +} + +int +_pgstat64(char const *name, struct stat *buf) +{ + WIN32_FILE_ATTRIBUTE_DATA faData; + + if (NULL == name || NULL == buf) + { + errno = EINVAL; + return -1; + } + + if (!GetFileAttributesEx(name, GetFileExInfoStandard, & faData)) + { + DWORD error = GetLastError(); + if (error== ERROR_DELETE_PENDING) + { + /* + * 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. + */ + errno = ENOENT; + } + _dosmaperr(error); + return -1; + } + + memset(buf, 0, sizeof(* buf)); + + if (faData.ftLastWriteTime.dwLowDateTime || + faData.ftLastWriteTime.dwHighDateTime) + buf->st_mtime = cvt_ft2ut( & faData.ftLastWriteTime ); + + if (faData.ftLastAccessTime.dwLowDateTime || + faData.ftLastAccessTime.dwHighDateTime) + buf->st_atime = cvt_ft2ut(& faData.ftLastAccessTime); + else + buf->st_atime = buf->st_mtime; + + if (faData.ftCreationTime.dwLowDateTime || + faData.ftCreationTime.dwHighDateTime) + buf->st_ctime = cvt_ft2ut(& faData.ftCreationTime); + else + buf->st_ctime = buf->st_mtime; + + buf->st_mode = cvt_attr2uxmode(faData.dwFileAttributes, name); + buf->st_nlink = 1; + + buf->st_size = ((__int64) faData.nFileSizeHigh) << 32 | + (__int64)(faData.nFileSizeLow); + + return 0; +} + +int +_pgfstat64(int fileno, struct stat *buf) +{ + const char filepath[MAX_PATH]; + FILE_NAME_INFO nameInfo; + + if (GetFileInformationByHandleEx((HANDLE) _get_osfhandle(fileno), + FileNameInfo, + &nameInfo, + sizeof(nameInfo))) + { + sprintf(filepath, "%ws", nameInfo.FileName); + return _pgstat64(filepath, buf); + } + else + return -1; +} + +#endif /* WIN32 */ -- 2.11.0