diff options
| author | Anatol Belski <ab@php.net> | 2018-07-15 09:33:14 +0200 |
|---|---|---|
| committer | Anatol Belski <ab@php.net> | 2018-08-26 22:30:06 +0200 |
| commit | e42e8b1051a8abeaa8e6053653a4ff43438766e2 (patch) | |
| tree | 75b7a444f90fd4ecc64e6956acd262e69193c957 | |
| parent | 2dbd21c12f656663cdbcae9e64b76a7316566c18 (diff) | |
| download | php-git-e42e8b1051a8abeaa8e6053653a4ff43438766e2.tar.gz | |
Refactor stat implementation
- 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.
| -rw-r--r-- | Zend/zend_stream.h | 21 | ||||
| -rw-r--r-- | Zend/zend_virtual_cwd.c | 188 | ||||
| -rw-r--r-- | Zend/zend_virtual_cwd.h | 8 | ||||
| -rw-r--r-- | ext/standard/file.c | 12 | ||||
| -rw-r--r-- | ext/standard/filestat.c | 11 | ||||
| -rw-r--r-- | ext/standard/tests/file/stat_basic-win32-mb.phpt | 12 | ||||
| -rw-r--r-- | ext/standard/tests/file/stat_basic-win32.phpt | 12 | ||||
| -rw-r--r-- | ext/standard/tests/file/stat_variation7-win32.phpt | 16 | ||||
| -rw-r--r-- | win32/ioutil.c | 165 | ||||
| -rw-r--r-- | win32/ioutil.h | 79 |
10 files changed, 288 insertions, 236 deletions
diff --git a/Zend/zend_stream.h b/Zend/zend_stream.h index 8279d69c4d..976afb979f 100644 --- a/Zend/zend_stream.h +++ b/Zend/zend_stream.h @@ -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 diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c index acaa0551b2..935a529a50 100644 --- a/Zend/zend_virtual_cwd.c +++ b/Zend/zend_virtual_cwd.c @@ -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); diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h index feee04a745..05ef03d99d 100644 --- a/Zend/zend_virtual_cwd.h +++ b/Zend/zend_virtual_cwd.h @@ -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 diff --git a/ext/standard/file.c b/ext/standard/file.c index 4aae502232..4fa498cdf4 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -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 diff --git a/ext/standard/filestat.c b/ext/standard/filestat.c index 68644ac03c..6822e055a8 100644 --- a/ext/standard/filestat.c +++ b/ext/standard/filestat.c @@ -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 diff --git a/ext/standard/tests/file/stat_basic-win32-mb.phpt b/ext/standard/tests/file/stat_basic-win32-mb.phpt index 334eb9ea65..0c45f5d22a 100644 --- a/ext/standard/tests/file/stat_basic-win32-mb.phpt +++ b/ext/standard/tests/file/stat_basic-win32-mb.phpt @@ -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"]=> diff --git a/ext/standard/tests/file/stat_basic-win32.phpt b/ext/standard/tests/file/stat_basic-win32.phpt index e3e7ea7eb5..9eb36208ba 100644 --- a/ext/standard/tests/file/stat_basic-win32.phpt +++ b/ext/standard/tests/file/stat_basic-win32.phpt @@ -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"]=> diff --git a/ext/standard/tests/file/stat_variation7-win32.phpt b/ext/standard/tests/file/stat_variation7-win32.phpt index 0a5e74d36a..67589ea02a 100644 --- a/ext/standard/tests/file/stat_variation7-win32.phpt +++ b/ext/standard/tests/file/stat_variation7-win32.phpt @@ -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"]=> diff --git a/win32/ioutil.c b/win32/ioutil.c index d3fb2bdb59..99eccd511f 100644 --- a/win32/ioutil.c +++ b/win32/ioutil.c @@ -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 diff --git a/win32/ioutil.h b/win32/ioutil.h index 8c0275cad6..f4c175ac66 100644 --- a/win32/ioutil.h +++ b/win32/ioutil.h @@ -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 |
