]> granicus.if.org Git - php/commitdiff
Refactor stat implementation
authorAnatol Belski <ab@php.net>
Sun, 15 Jul 2018 07:33:14 +0000 (09:33 +0200)
committerAnatol Belski <ab@php.net>
Sun, 26 Aug 2018 20:30:06 +0000 (22:30 +0200)
- move relevant parts into win32
- general cleanup
- use Windows API and fallback to POSIX
- improve filetime to timestamp conversion
- improve stat/fsat
- handle ino by using file index
- handle st_dev by using volume serial number

The inode implementation is based on file indexes from NTFS. On 32-bit,
fake inodes are shown, that may lead to unexpeted results. 64-bit
implementation is most reliable.

Zend/zend_stream.h
Zend/zend_virtual_cwd.c
Zend/zend_virtual_cwd.h
ext/standard/file.c
ext/standard/filestat.c
ext/standard/tests/file/stat_basic-win32-mb.phpt
ext/standard/tests/file/stat_basic-win32.phpt
ext/standard/tests/file/stat_variation7-win32.phpt
win32/ioutil.c
win32/ioutil.h

index 8279d69c4db7576d8a4f57117a202df1e5cbe96c..976afb979f2d1692aef0481074cf9d195243a9a4 100644 (file)
@@ -79,20 +79,27 @@ ZEND_API void zend_file_handle_dtor(zend_file_handle *fh);
 ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2);
 END_EXTERN_C()
 
+#ifdef ZEND_WIN32
+# include "win32/ioutil.h"
+typedef php_win32_ioutil_stat_t zend_stat_t;
 #ifdef _WIN64
-# define zend_fseek _fseeki64
-# define zend_ftell _ftelli64
-# define zend_lseek _lseeki64
-# define zend_fstat _fstat64
-# define zend_stat  _stat64
-typedef struct __stat64 zend_stat_t;
+#  define zend_fseek _fseeki64
+#  define zend_ftell _ftelli64
+#  define zend_lseek _lseeki64
+# else
+#  define zend_fseek fseek
+#  define zend_ftell ftell
+#  define zend_lseek lseek
+# endif
+# define zend_fstat php_win32_ioutil_fstat
+# define zend_stat php_win32_ioutil_stat
 #else
+typedef struct stat zend_stat_t;
 # define zend_fseek fseek
 # define zend_ftell ftell
 # define zend_lseek lseek
 # define zend_fstat fstat
 # define zend_stat stat
-typedef struct stat zend_stat_t;
 #endif
 
 #endif
index acaa0551b26e3b1f83f1a24dc12e75792fa66c8c..935a529a50de842dd359392b8cf4e63680c43274 100644 (file)
@@ -62,6 +62,9 @@
 # ifndef VOLUME_NAME_DOS
 #  define VOLUME_NAME_DOS 0x0
 # endif
+
+# include <winioctl.h>
+# include <winnt.h>
 #endif
 
 #ifndef HAVE_REALPATH
@@ -111,64 +114,6 @@ static cwd_state main_cwd_state; /* True global */
 #endif
 
 #ifdef ZEND_WIN32
-
-#ifdef CTL_CODE
-#undef CTL_CODE
-#endif
-#define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
-#define FILE_DEVICE_FILE_SYSTEM 0x00000009
-#define METHOD_BUFFERED                0
-#define FILE_ANY_ACCESS        0
-#define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
-#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 )
-
-typedef struct {
-       unsigned long  ReparseTag;
-       unsigned short ReparseDataLength;
-       unsigned short Reserved;
-       union {
-               struct {
-                       unsigned short SubstituteNameOffset;
-                       unsigned short SubstituteNameLength;
-                       unsigned short PrintNameOffset;
-                       unsigned short PrintNameLength;
-                       unsigned long  Flags;
-                       wchar_t        ReparseTarget[1];
-               } SymbolicLinkReparseBuffer;
-               struct {
-                       unsigned short SubstituteNameOffset;
-                       unsigned short SubstituteNameLength;
-                       unsigned short PrintNameOffset;
-                       unsigned short PrintNameLength;
-                       wchar_t        ReparseTarget[1];
-               } MountPointReparseBuffer;
-               struct {
-                       unsigned char  ReparseTarget[1];
-               } GenericReparseBuffer;
-       };
-} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
-
-#define SECS_BETWEEN_EPOCHS (__int64)11644473600
-#define SECS_TO_100NS (__int64)10000000
-static inline time_t FileTimeToUnixTime(const FILETIME *FileTime)
-{
-       __int64 UnixTime;
-       SYSTEMTIME SystemTime;
-       FileTimeToSystemTime(FileTime, &SystemTime);
-
-       UnixTime = ((__int64)FileTime->dwHighDateTime << 32) +
-       FileTime->dwLowDateTime;
-
-       UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
-
-       UnixTime /= SECS_TO_100NS; /* now convert to seconds */
-
-       if ((time_t)UnixTime != UnixTime) {
-               UnixTime = 0;
-       }
-       return (time_t)UnixTime;
-}
-
 CWD_API ssize_t php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */
        HANDLE hFile;
        wchar_t *linkw = php_win32_ioutil_any_to_w(link), targetw[MAXPATHLEN];
@@ -238,129 +183,6 @@ CWD_API ssize_t php_sys_readlink(const char *link, char *target, size_t target_l
        return (ssize_t)ret_len;
 }
 /* }}} */
-
-CWD_API int php_sys_stat_ex(const char *path, zend_stat_t *buf, int lstat) /* {{{ */
-{
-       WIN32_FILE_ATTRIBUTE_DATA data;
-       LARGE_INTEGER t;
-       size_t pathw_len = 0;
-       ALLOCA_FLAG(use_heap_large)
-       wchar_t *pathw = php_win32_ioutil_conv_any_to_w(path, PHP_WIN32_CP_IGNORE_LEN, &pathw_len);
-
-       if (!pathw) {
-               return -1;
-       }
-
-       if (!GetFileAttributesExW(pathw, GetFileExInfoStandard, &data)) {
-               int ret;
-#if ZEND_ENABLE_ZVAL_LONG64
-               ret = _wstat64(pathw, buf);
-#else
-               ret = _wstat(pathw, (struct _stat32 *)buf);
-#endif
-               free(pathw);
-
-               return ret;
-       }
-
-       if (pathw_len >= 1 && pathw[1] == L':') {
-               if (pathw[0] >= L'A' && pathw[0] <= L'Z') {
-                       buf->st_dev = buf->st_rdev = pathw[0] - L'A';
-               } else {
-                       buf->st_dev = buf->st_rdev = pathw[0] - L'a';
-               }
-       } else if (PHP_WIN32_IOUTIL_IS_UNC(pathw, pathw_len)) {
-               buf->st_dev = buf->st_rdev = 0;
-       } else {
-               wchar_t cur_path[MAXPATHLEN+1];
-
-               if (NULL != _wgetcwd(cur_path, sizeof(cur_path)/sizeof(wchar_t))) {
-                       if (cur_path[1] == L':') {
-                               if (pathw[0] >= L'A' && pathw[0] <= L'Z') {
-                                       buf->st_dev = buf->st_rdev = pathw[0] - L'A';
-                               } else {
-                                       buf->st_dev = buf->st_rdev = pathw[0] - L'a';
-                               }
-                       } else {
-                               buf->st_dev = buf->st_rdev = -1;
-                       }
-               } else {
-                       buf->st_dev = buf->st_rdev = -1;
-               }
-       }
-
-       buf->st_uid = buf->st_gid = buf->st_ino = 0;
-
-       if (lstat && data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-               /* File is a reparse point. Get the target */
-               HANDLE hLink = NULL;
-               REPARSE_DATA_BUFFER * pbuffer;
-               DWORD retlength = 0;
-
-               hLink = CreateFileW(pathw,
-                               FILE_READ_ATTRIBUTES,
-                               PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE,
-                               NULL,
-                               OPEN_EXISTING,
-                               FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS,
-                               NULL);
-               if(hLink == INVALID_HANDLE_VALUE) {
-                       free(pathw);
-                       return -1;
-               }
-
-               pbuffer = (REPARSE_DATA_BUFFER *)do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
-               if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
-                       free_alloca(pbuffer, use_heap_large);
-                       CloseHandle(hLink);
-                       free(pathw);
-                       return -1;
-               }
-
-               CloseHandle(hLink);
-
-               if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
-                       buf->st_mode = S_IFLNK;
-                       buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
-               }
-
-#if 0 /* Not used yet */
-               else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
-                       buf->st_mode |=;
-               }
-#endif
-               free_alloca(pbuffer, use_heap_large);
-       } else {
-               buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG;
-               buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
-       }
-
-       if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
-               if (pathw_len >= 4 && pathw[pathw_len-4] == L'.') {
-                       if (_wcsnicmp(pathw+pathw_len-3, L"exe", 3) == 0 ||
-                               _wcsnicmp(pathw+pathw_len-3, L"com", 3) == 0 ||
-                               _wcsnicmp(pathw+pathw_len-3, L"bat", 3) == 0 ||
-                               _wcsnicmp(pathw+pathw_len-3, L"cmd", 3) == 0) {
-                               buf->st_mode  |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
-                       }
-               }
-       }
-
-       buf->st_nlink = 1;
-       t.HighPart = data.nFileSizeHigh;
-       t.LowPart = data.nFileSizeLow;
-       /* It's an overflow on 32 bit, however it won't fix as long
-       as zend_long is 32 bit. */
-       buf->st_size = (zend_long)t.QuadPart;
-       buf->st_atime = FileTimeToUnixTime(&data.ftLastAccessTime);
-       buf->st_ctime = FileTimeToUnixTime(&data.ftCreationTime);
-       buf->st_mtime = FileTimeToUnixTime(&data.ftLastWriteTime);
-
-       free(pathw);
-
-       return 0;
-}
-/* }}} */
 #endif
 
 static int php_is_dir_ok(const cwd_state *state)  /* {{{ */
@@ -863,7 +685,7 @@ static size_t tsrm_realpath_r(char *path, size_t start, size_t len, int *ll, tim
                                ) {
                        /* File is a reparse point. Get the target */
                        HANDLE hLink = NULL;
-                       REPARSE_DATA_BUFFER * pbuffer;
+                       PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER * pbuffer;
                        DWORD retlength = 0;
                        size_t bufindex = 0;
                        uint8_t isabsolute = 0;
@@ -896,7 +718,7 @@ static size_t tsrm_realpath_r(char *path, size_t start, size_t len, int *ll, tim
                                return (size_t)-1;
                        }
 
-                       pbuffer = (REPARSE_DATA_BUFFER *)do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
+                       pbuffer = (PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER *)do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
                        if (pbuffer == NULL) {
                                CloseHandle(hLink);
                                free_alloca(tmp, use_heap);
index feee04a745ab01fbfc2a8bef33f2199f1d14c4c9..05ef03d99d7c9c69119e4ccdd7a51467fa45e694 100644 (file)
@@ -116,15 +116,17 @@ typedef unsigned short mode_t;
 #endif
 
 #ifdef ZEND_WIN32
-CWD_API int php_sys_stat_ex(const char *path, zend_stat_t *buf, int lstat);
-# define php_sys_stat(path, buf) php_sys_stat_ex(path, buf, 0)
-# define php_sys_lstat(path, buf) php_sys_stat_ex(path, buf, 1)
+# define php_sys_stat_ex php_win32_ioutil_stat_ex
+# define php_sys_stat php_win32_ioutil_stat
+# define php_sys_lstat php_win32_ioutil_lstat
+# define php_sys_fstat php_win32_ioutil_fstat
 CWD_API ssize_t php_sys_readlink(const char *link, char *target, size_t target_len);
 # define php_sys_symlink php_win32_ioutil_symlink
 # define php_sys_link php_win32_ioutil_link
 #else
 # define php_sys_stat stat
 # define php_sys_lstat lstat
+# define php_sys_fstat fstat
 # ifdef HAVE_SYMLINK
 # define php_sys_readlink(link, target, target_len) readlink(link, target, target_len)
 # define php_sys_symlink symlink
index 4aae5022322b866c960c2cf9b84226afdde86a29..4fa498cdf4df6915dad1e01e48974e8d88758a13 100644 (file)
@@ -1591,19 +1591,7 @@ PHP_NAMED_FUNCTION(php_if_fstat)
        ZVAL_LONG(&stat_uid, stat_ssb.sb.st_uid);
        ZVAL_LONG(&stat_gid, stat_ssb.sb.st_gid);
 #ifdef HAVE_STRUCT_STAT_ST_RDEV
-# ifdef PHP_WIN32
-       /* It is unsigned, so if a negative came from userspace, it'll
-          convert to UINT_MAX, but we want to keep the userspace value.
-          Almost the same as in php_fstat. This is ugly, but otherwise
-          we would have to maintain a fully compatible struct stat. */
-       if ((int)stat_ssb.sb.st_rdev < 0) {
-               ZVAL_LONG(&stat_rdev, (int)stat_ssb.sb.st_rdev);
-       } else {
-               ZVAL_LONG(&stat_rdev, stat_ssb.sb.st_rdev);
-       }
-# else
        ZVAL_LONG(&stat_rdev, stat_ssb.sb.st_rdev);
-# endif
 #else
        ZVAL_LONG(&stat_rdev, -1);
 #endif
index 68644ac03c549a31f8eb06b4ab45d955461bde62..6822e055a82ced3bcd7c69c6f777f8c58f59c004 100644 (file)
@@ -924,18 +924,7 @@ PHPAPI void php_stat(const char *filename, size_t filename_length, int type, zva
                ZVAL_LONG(&stat_uid, stat_sb->st_uid);
                ZVAL_LONG(&stat_gid, stat_sb->st_gid);
 #ifdef HAVE_STRUCT_STAT_ST_RDEV
-# ifdef PHP_WIN32
-       /* It is unsigned, so if a negative came from userspace, it'll
-          convert to UINT_MAX, but we want to keep the userspace value.
-          Almost the same as in php_if_fstat. */
-       if ((int)stat_sb->st_rdev < 0) {
-               ZVAL_LONG(&stat_rdev, (int)stat_sb->st_rdev);
-       } else {
                ZVAL_LONG(&stat_rdev, stat_sb->st_rdev);
-       }
-# else
-       ZVAL_LONG(&stat_rdev, stat_sb->st_rdev);
-# endif
 #else
                ZVAL_LONG(&stat_rdev, -1);
 #endif
index 334eb9ea657458b09ff9dcdbaea0a2feb3f3e904..0c45f5d22a668a25f79df5e9ff033e64c193a3e4 100644 (file)
@@ -78,9 +78,9 @@ bool(true)
 -- comparing difference in dir stats before and after creating file in it --
 array(26) {
   [0]=>
-  int(%d)
+  int(%i)
   [1]=>
-  int(0)
+  int(%d)
   [2]=>
   int(%d)
   [3]=>
@@ -104,9 +104,9 @@ array(26) {
   [12]=>
   int(-1)
   ["dev"]=>
-  int(%d)
+  int(%i)
   ["ino"]=>
-  int(0)
+  int(%d)
   ["mode"]=>
   int(%d)
   ["nlink"]=>
@@ -132,7 +132,7 @@ array(26) {
 }
 array(26) {
   [0]=>
-  int(%d)
+  int(%i)
   [1]=>
   int(%d)
   [2]=>
@@ -158,7 +158,7 @@ array(26) {
   [12]=>
   int(-1)
   ["dev"]=>
-  int(%d)
+  int(%i)
   ["ino"]=>
   int(%d)
   ["mode"]=>
index e3e7ea7eb52086b5b98d22585b04849193e8db84..9eb36208ba51cd80f1638650621950200cc06e91 100644 (file)
@@ -78,9 +78,9 @@ bool(true)
 -- comparing difference in dir stats before and after creating file in it --
 array(26) {
   [0]=>
-  int(%d)
+  int(%i)
   [1]=>
-  int(0)
+  int(%d)
   [2]=>
   int(%d)
   [3]=>
@@ -104,9 +104,9 @@ array(26) {
   [12]=>
   int(-1)
   ["dev"]=>
-  int(%d)
+  int(%i)
   ["ino"]=>
-  int(0)
+  int(%d)
   ["mode"]=>
   int(%d)
   ["nlink"]=>
@@ -132,7 +132,7 @@ array(26) {
 }
 array(26) {
   [0]=>
-  int(%d)
+  int(%i)
   [1]=>
   int(%d)
   [2]=>
@@ -158,7 +158,7 @@ array(26) {
   [12]=>
   int(-1)
   ["dev"]=>
-  int(%d)
+  int(%i)
   ["ino"]=>
   int(%d)
   ["mode"]=>
index 0a5e74d36aa85408f100202652971c1d1d53f615..67589ea02aff2d99766c20aade11bd16572dfe52 100644 (file)
@@ -77,7 +77,7 @@ rmdir("$file_path/stat_variation7a");
 -- Testing stat() on filename stored inside an object --
 array(26) {
   [0]=>
-  int(%d)
+  int(%i)
   [1]=>
   int(%d)
   [2]=>
@@ -103,7 +103,7 @@ array(26) {
   [12]=>
   int(-%d)
   ["dev"]=>
-  int(%d)
+  int(%i)
   ["ino"]=>
   int(%d)
   ["mode"]=>
@@ -131,7 +131,7 @@ array(26) {
 }
 array(26) {
   [0]=>
-  int(%d)
+  int(%i)
   [1]=>
   int(%d)
   [2]=>
@@ -157,7 +157,7 @@ array(26) {
   [12]=>
   int(-%d)
   ["dev"]=>
-  int(%d)
+  int(%i)
   ["ino"]=>
   int(%d)
   ["mode"]=>
@@ -187,7 +187,7 @@ array(26) {
 -- Testing stat() on directory name stored inside an object --
 array(26) {
   [0]=>
-  int(%d)
+  int(%i)
   [1]=>
   int(%d)
   [2]=>
@@ -213,7 +213,7 @@ array(26) {
   [12]=>
   int(-%d)
   ["dev"]=>
-  int(%d)
+  int(%i)
   ["ino"]=>
   int(%d)
   ["mode"]=>
@@ -241,7 +241,7 @@ array(26) {
 }
 array(26) {
   [0]=>
-  int(%d)
+  int(%i)
   [1]=>
   int(%d)
   [2]=>
@@ -267,7 +267,7 @@ array(26) {
   [12]=>
   int(-%d)
   ["dev"]=>
-  int(%d)
+  int(%i)
   ["ino"]=>
   int(%d)
   ["mode"]=>
index d3fb2bdb59d81c2ccca4b6e9a9e7491f24e539db..99eccd511fb99edee0aaa2a95f9d2f016289a1ec 100644 (file)
@@ -60,6 +60,8 @@
 #include "main/streams/php_stream_plain_wrapper.h"
 
 #include <pathcch.h>
+#include <winioctl.h>
+#include <winnt.h>
 
 /*
 #undef NONLS
@@ -817,6 +819,169 @@ PW32IO int php_win32_ioutil_link_w(const wchar_t *target, const wchar_t *link)
        return 0;
 }/*}}}*/
 
+#define FILETIME_TO_UINT(filetime)                                          \
+   (*((uint64_t*) &(filetime)) - 116444736000000000ULL)
+
+#define FILETIME_TO_TIME_T(filetime)                                        \
+   (time_t)(FILETIME_TO_UINT(filetime) / 10000000ULL)
+
+static int php_win32_ioutil_fstat_int(HANDLE h, php_win32_ioutil_stat_t *buf, const wchar_t *pathw, size_t pathw_len, PBY_HANDLE_FILE_INFORMATION dp)
+{/*{{{*/
+       BY_HANDLE_FILE_INFORMATION d;
+       PBY_HANDLE_FILE_INFORMATION data;
+       LARGE_INTEGER t;
+       wchar_t mypath[MAXPATHLEN];
+       uint8_t is_dir;
+
+       data = !dp ? &d : dp;
+
+       if (!pathw) {
+               pathw_len = GetFinalPathNameByHandleW(h, mypath, MAXPATHLEN, VOLUME_NAME_DOS);
+               if (pathw_len >= MAXPATHLEN || pathw_len == 0) {
+                       pathw_len = 0;
+                       pathw = NULL;
+               } else {
+                       pathw = mypath;
+               }
+       }
+
+       if(!GetFileInformationByHandle(h, data)) {
+               if (INVALID_HANDLE_VALUE != h) {
+                       /* Perhaps it's a fileless stream like stdio, reuse the normal stat info. */
+                       struct __stat64 _buf;
+                       if (_fstat64(_open_osfhandle((intptr_t)h, 0), &_buf)) {
+                               return -1;
+                       }
+                       buf->st_dev = _buf.st_dev;
+                       buf->st_ino = _buf.st_ino;
+                       buf->st_mode = _buf.st_mode;
+                       buf->st_nlink = _buf.st_nlink;
+                       buf->st_uid = _buf.st_uid;
+                       buf->st_gid = _buf.st_gid;
+                       buf->st_rdev = _buf.st_rdev;
+                       buf->st_size = _buf.st_size;
+                       buf->st_atime = _buf.st_atime;
+                       buf->st_mtime = _buf.st_mtime;
+                       buf->st_ctime = _buf.st_ctime;
+                       return 0;
+               } else if(h == INVALID_HANDLE_VALUE && pathw_len > 0) {
+                       /* An abnormal situation it is. For example, the user is the file
+                               owner, but the file has an empty DACL. In that case, it is
+                               possible CreateFile would fail, but the attributes still can
+                               be read. Some info is still going to be missing. */
+                       WIN32_FILE_ATTRIBUTE_DATA _data;
+                       if (!GetFileAttributesExW(pathw, GetFileExInfoStandard, &_data)) {
+                               DWORD err = GetLastError();
+                               SET_ERRNO_FROM_WIN32_CODE(err);
+                               return -1;
+                       }
+                       data->dwFileAttributes = _data.dwFileAttributes;
+                       data->ftCreationTime = _data.ftCreationTime;
+                       data->ftLastAccessTime = _data.ftLastAccessTime;
+                       data->ftLastWriteTime = _data.ftLastWriteTime;
+                       data->nFileSizeHigh = _data.nFileSizeHigh;
+                       data->nFileSizeLow = _data.nFileSizeLow;
+                       data->dwVolumeSerialNumber = 0;
+                       data->nNumberOfLinks = 1;
+                       data->nFileIndexHigh = 0;
+                       data->nFileIndexLow = 0;
+               } else {
+                       DWORD err = GetLastError();
+                       SET_ERRNO_FROM_WIN32_CODE(err);
+                       return -1;
+               }
+       }
+
+       is_dir = (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
+
+       buf->st_dev = data->dwVolumeSerialNumber;
+
+       buf->st_rdev = buf->st_uid = buf->st_gid = 0;
+
+       buf->st_ino = (((uint64_t)data->nFileIndexHigh) << 32) + data->nFileIndexLow;
+
+       buf->st_mode = 0;
+
+       if (!is_dir) {
+               DWORD type;
+               if (GetBinaryTypeW(pathw, &type)) {
+                               buf->st_mode  |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
+               }
+       }
+
+       if ((data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
+               buf->st_mode |= is_dir ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG;
+               buf->st_mode |= (data->dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
+       }
+
+       buf->st_nlink = data->nNumberOfLinks;
+       t.HighPart = data->nFileSizeHigh;
+       t.LowPart = data->nFileSizeLow;
+       /* It's an overflow on 32 bit, however it won't fix as long
+       as zend_long is 32 bit. */
+       buf->st_size = (zend_long)t.QuadPart;
+       buf->st_atime = FILETIME_TO_TIME_T(data->ftLastAccessTime);
+       buf->st_ctime = FILETIME_TO_TIME_T(data->ftCreationTime);
+       buf->st_mtime = FILETIME_TO_TIME_T(data->ftLastWriteTime);
+
+       return 0;
+}/*}}}*/
+
+PW32IO int php_win32_ioutil_stat_ex_w(const wchar_t *path, size_t path_len, php_win32_ioutil_stat_t *buf, int lstat)
+{/*{{{*/
+       BY_HANDLE_FILE_INFORMATION data;
+       HANDLE hLink = NULL;
+       DWORD flags_and_attrs = lstat ? FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT : FILE_FLAG_BACKUP_SEMANTICS;
+       int ret;
+       ALLOCA_FLAG(use_heap_large)
+
+       hLink = CreateFileW(path,
+                       FILE_READ_ATTRIBUTES,
+                       PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE,
+                       NULL,
+                       OPEN_EXISTING,
+                       flags_and_attrs,
+                       NULL
+       );
+
+       ret = php_win32_ioutil_fstat_int(hLink, buf, path, path_len, &data);
+
+       if (lstat && data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+               /* File is a reparse point. Get the target */
+               PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER * pbuffer;
+               DWORD retlength = 0;
+
+               pbuffer = (PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER *)do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
+               if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
+                       free_alloca(pbuffer, use_heap_large);
+                       CloseHandle(hLink);
+                       return -1;
+               }
+
+               if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+                       buf->st_mode = S_IFLNK;
+                       buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
+               }
+
+#if 0 /* Not used yet */
+               else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
+                       buf->st_mode |=;
+               }
+#endif
+               free_alloca(pbuffer, use_heap_large);
+       }
+
+       CloseHandle(hLink);
+
+       return ret;
+
+}/*}}}*/
+
+PW32IO int php_win32_ioutil_fstat(int fd, php_win32_ioutil_stat_t *buf)
+{/*{{{*/
+       return php_win32_ioutil_fstat_int((HANDLE)_get_osfhandle(fd), buf, NULL, 0, NULL);
+}/*}}}*/
+
 /*
  * Local variables:
  * tab-width: 4
index 8c0275cad61ffdfd40c42a2b6514f495e217f9e9..f4c175ac66cbec86d7095d0aff7896b1105f7e43 100644 (file)
@@ -677,6 +677,85 @@ __forceinline static char *php_win32_ioutil_realpath(const char *path, char *res
        return php_win32_ioutil_realpath_ex0(path, resolved, NULL);
 }/*}}}*/
 
+#include <sys/stat.h>
+#if _WIN64
+typedef unsigned __int64 php_win32_ioutil_dev_t;
+typedef unsigned __int64 php_win32_ioutil_ino_t;
+typedef __time64_t php_win32_ioutil_time_t;
+typedef __int64 php_win32_ioutil_size_t;
+#else
+typedef unsigned __int32 php_win32_ioutil_dev_t;
+typedef unsigned __int32 php_win32_ioutil_ino_t;
+typedef __time32_t php_win32_ioutil_time_t;
+typedef __int32 php_win32_ioutil_size_t;
+#endif
+typedef struct {
+       php_win32_ioutil_dev_t st_dev;
+       php_win32_ioutil_ino_t st_ino;
+       unsigned __int32 st_mode;
+       unsigned __int32 st_nlink;
+       unsigned short st_uid;
+       unsigned short st_gid;
+       php_win32_ioutil_dev_t st_rdev;
+       php_win32_ioutil_size_t st_size;
+#if 0
+       __int32 st_blksize;
+       __int32 st_blocks;
+#endif
+       php_win32_ioutil_time_t st_atime;
+       php_win32_ioutil_time_t st_mtime;
+       php_win32_ioutil_time_t st_ctime;
+} php_win32_ioutil_stat_t;
+
+typedef struct {
+       unsigned long  ReparseTag;
+       unsigned short ReparseDataLength;
+       unsigned short Reserved;
+       union {
+               struct {
+                       unsigned short SubstituteNameOffset;
+                       unsigned short SubstituteNameLength;
+                       unsigned short PrintNameOffset;
+                       unsigned short PrintNameLength;
+                       unsigned long  Flags;
+                       wchar_t        ReparseTarget[1];
+               } SymbolicLinkReparseBuffer;
+               struct {
+                       unsigned short SubstituteNameOffset;
+                       unsigned short SubstituteNameLength;
+                       unsigned short PrintNameOffset;
+                       unsigned short PrintNameLength;
+                       wchar_t        ReparseTarget[1];
+               } MountPointReparseBuffer;
+               struct {
+                       unsigned char  ReparseTarget[1];
+               } GenericReparseBuffer;
+       };
+} PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER, *PHP_WIN32_IOUTIL_PREPARSE_DATA_BUFFER;
+
+PW32IO int php_win32_ioutil_stat_ex_w(const wchar_t *path, size_t path_len, php_win32_ioutil_stat_t *buf, int lstat);
+PW32IO int php_win32_ioutil_fstat(int fd, php_win32_ioutil_stat_t *buf);
+
+__forceinline static int php_win32_ioutil_stat_ex(const char *path, php_win32_ioutil_stat_t *buf, int lstat)
+{/*{{{*/
+       size_t pathw_len;
+       wchar_t *pathw = php_win32_ioutil_conv_any_to_w(path, PHP_WIN32_CP_IGNORE_LEN, &pathw_len);
+       int ret;
+
+       if (!pathw) {
+               SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
+               return -1;
+       }
+
+       ret = php_win32_ioutil_stat_ex_w(pathw, pathw_len, buf, lstat);
+
+       free(pathw);
+
+       return ret;
+}/*}}}*/
+#define php_win32_ioutil_stat(path, buf) php_win32_ioutil_stat_ex(path, buf, 0)
+#define php_win32_ioutil_lstat(path, buf) php_win32_ioutil_stat_ex(path, buf, 1)
+
 #ifdef __cplusplus
 }
 #endif