From 028a5af1c1f392af98102bd7a403df51e19d78fc Mon Sep 17 00:00:00 2001 From: Juan Jose Santamaria Flecha Date: Fri, 28 Jun 2019 03:53:44 -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 | 35 +++++++--- src/port/dirmod.c | 52 -------------- src/port/win32_stat.c | 154 ++++++++++++++++++++++++++++++++++++++++++ src/tools/msvc/Mkvcbuild.pm | 2 +- 5 files changed, 193 insertions(+), 61 deletions(-) create mode 100644 src/port/win32_stat.c diff --git a/configure b/configure index 8d47071..4bcec22 100755 --- a/configure +++ b/configure @@ -16204,6 +16204,12 @@ fi esac case " $LIBOBJS " in + *" win32_stat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS win32_stat.$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 + *" win32_stat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS win32_stat.$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..d84e2d8 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,29 @@ 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/win32_stat.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); +#define fstat(fileno, sb) _pgfstat64(fileno, sb) +#define stat(path, sb) _pgstat64(path, sb) /* 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/win32_stat.c b/src/port/win32_stat.c new file mode 100644 index 0000000..63fbf82 --- /dev/null +++ b/src/port/win32_stat.c @@ -0,0 +1,154 @@ +/*------------------------------------------------------------------------- + * + * win32_stat.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/win32_stat.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 + +#include "c.h" +#include +#if defined(__CYGWIN__) +#include +#else +#include +#endif + +static const unsigned __int64 EpochShift = UINT64CONST(116444736000000000); + +/* + * Converts a FILETIME struct into a 64 bit time_t + */ +static __time64_t +filetime_to_time(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; +} + +/* + * Converts WIN32 file attributes to unix mode + */ +static unsigned short +fileattr_to_unixmode(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; +} + +/* + * GetFileAttributesEx minimum supported version: Windows XP and + * Windows Server 2003 + */ +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; + } + + if (faData.ftLastWriteTime.dwLowDateTime || + faData.ftLastWriteTime.dwHighDateTime) + buf->st_mtime = filetime_to_time(&faData.ftLastWriteTime); + + if (faData.ftLastAccessTime.dwLowDateTime || + faData.ftLastAccessTime.dwHighDateTime) + buf->st_atime = filetime_to_time(&faData.ftLastAccessTime); + else + buf->st_atime = buf->st_mtime; + + if (faData.ftCreationTime.dwLowDateTime || + faData.ftCreationTime.dwHighDateTime) + buf->st_ctime = filetime_to_time(&faData.ftCreationTime); + else + buf->st_ctime = buf->st_mtime; + + buf->st_mode = fileattr_to_unixmode(faData.dwFileAttributes, name); + buf->st_nlink = 1; + + buf->st_size = ((__int64) faData.nFileSizeHigh) << 32 | + (__int64)(faData.nFileSizeLow); + + return 0; +} + +/* + * Wrapper for _pgstat64 + * GetFinalPathNameByHandle minimum supported version: Windows Vista and + * Windows Server 2008 + */ +int +_pgfstat64(int fileno, struct stat *buf) +{ + const char filepath[_MAX_PATH]; + + if(GetFinalPathNameByHandle((HANDLE)_get_osfhandle(fileno), + filepath, _MAX_PATH, VOLUME_NAME_DOS)) + return _pgstat64(filepath, buf); + return -1; +} + +#endif /* WIN32 */ diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index e07e459..4a14b24 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -101,7 +101,7 @@ sub mkvcbuild pg_strong_random.c pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pqsignal.c mkdtemp.c qsort.c qsort_arg.c quotes.c system.c sprompt.c strerror.c tar.c thread.c - win32env.c win32error.c win32security.c win32setlocale.c); + win32env.c win32error.c win32security.c win32setlocale.c win32_stat.c); push(@pgportfiles, 'rint.c') if ($vsVersion < '12.00'); -- 2.11.0