From 2fbdaec03cdbda901cbd1b69898a3e3f265536bc Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Thu, 7 Dec 2017 23:05:19 +0100 Subject: [PATCH] Revamp unlink() implementation and improve error handling --- .../tests/file/unlink_error-win32-mb.phpt | 2 +- .../tests/file/unlink_error-win32.phpt | 2 +- win32/ioutil.c | 62 ++++++++++++++++++- win32/ioutil.h | 11 ++-- win32/winutil.c | 1 + 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/ext/standard/tests/file/unlink_error-win32-mb.phpt b/ext/standard/tests/file/unlink_error-win32-mb.phpt index 5111f34b76..4ce4ca796a 100644 --- a/ext/standard/tests/file/unlink_error-win32-mb.phpt +++ b/ext/standard/tests/file/unlink_error-win32-mb.phpt @@ -108,6 +108,6 @@ bool(false) -- Testing unlink() on directory -- -Warning: unlink(%s/unlink_error): Permission denied in %s on line %d +Warning: unlink(%s/unlink_error): Is a directory in %s on line %d bool(false) Done diff --git a/ext/standard/tests/file/unlink_error-win32.phpt b/ext/standard/tests/file/unlink_error-win32.phpt index e55f6ed5cd..f7a123919f 100644 --- a/ext/standard/tests/file/unlink_error-win32.phpt +++ b/ext/standard/tests/file/unlink_error-win32.phpt @@ -105,6 +105,6 @@ bool(false) -- Testing unlink() on directory -- -Warning: unlink(%s/unlink_error): Permission denied in %s on line %d +Warning: unlink(%s/unlink_error): Is a directory in %s on line %d bool(false) Done diff --git a/win32/ioutil.c b/win32/ioutil.c index 68346a19bb..7f1f758456 100644 --- a/win32/ioutil.c +++ b/win32/ioutil.c @@ -359,16 +359,72 @@ PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path) {/*{{{*/ int ret = 0; DWORD err = 0; + HANDLE h; + BY_HANDLE_FILE_INFORMATION info; + FILE_DISPOSITION_INFO disposition; + BOOL status; PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0) - if (!DeleteFileW(path)) { + h = CreateFileW(path, + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE, + PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (INVALID_HANDLE_VALUE == h) { err = GetLastError(); - ret = -1; SET_ERRNO_FROM_WIN32_CODE(err); + return -1; } - return ret; + if (!GetFileInformationByHandle(h, &info)) { + err = GetLastError(); + CloseHandle(h); + SET_ERRNO_FROM_WIN32_CODE(err); + return -1; + } + + if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + /* TODO Handle possible reparse point. */ + CloseHandle(h); + SET_ERRNO_FROM_WIN32_CODE(ERROR_DIRECTORY_NOT_SUPPORTED); + return -1; + } + +#if 0 + /* XXX BC breach! */ + if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { + /* Remove read-only attribute */ + FILE_BASIC_INFO basic = { 0 }; + + basic.FileAttributes = info.dwFileAttributes & ~(FILE_ATTRIBUTE_READONLY); + + status = SetFileInformationByHandle(h, FileBasicInfo, &basic, sizeof basic); + if (!status) { + err = GetLastError(); + SET_ERRNO_FROM_WIN32_CODE(err); + CloseHandle(h); + return -1; + } + } +#endif + + /* Try to set the delete flag. */ + disposition.DeleteFile = TRUE; + status = SetFileInformationByHandle(h, FileDispositionInfo, &disposition, sizeof disposition); + if (!status) { + err = GetLastError(); + CloseHandle(h); + SET_ERRNO_FROM_WIN32_CODE(err); + return -1; + } + + CloseHandle(h); + + return 0; }/*}}}*/ PW32IO int php_win32_ioutil_rmdir_w(const wchar_t *path) diff --git a/win32/ioutil.h b/win32/ioutil.h index 71737f697b..5d06fd1940 100644 --- a/win32/ioutil.h +++ b/win32/ioutil.h @@ -241,6 +241,7 @@ PW32IO int php_win32_ioutil_open_w(const wchar_t *path, int flags, ...); PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path); PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname); PW32IO wchar_t *php_win32_ioutil_getcwd_w(wchar_t *buf, size_t len); +PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path); #if 0 PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode); @@ -314,19 +315,17 @@ __forceinline static int php_win32_ioutil_open(const char *path, int flags, ...) __forceinline static int php_win32_ioutil_unlink(const char *path) {/*{{{*/ PHP_WIN32_IOUTIL_INIT_W(path) - int ret = 0; - DWORD err = 0; + int ret = -1; + DWORD err; if (!pathw) { SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER); return -1; } - PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1) - - if (!DeleteFileW(pathw)) { + ret = php_win32_ioutil_unlink_w(pathw); + if (0 > ret) { err = GetLastError(); - ret = -1; } PHP_WIN32_IOUTIL_CLEANUP_W() diff --git a/win32/winutil.c b/win32/winutil.c index 34dda95498..e1f819ac5e 100644 --- a/win32/winutil.c +++ b/win32/winutil.c @@ -371,6 +371,7 @@ PHP_WINUTIL_API int php_win32_code_to_errno(unsigned long w32Err) /* 258 */ , { WAIT_TIMEOUT, ETIME} /* 267 */ , { ERROR_DIRECTORY , ENOTDIR } + /* 336 */ , { ERROR_DIRECTORY_NOT_SUPPORTED , EISDIR } /* 996 */ , { ERROR_IO_INCOMPLETE , EAGAIN } /* 997 */ , { ERROR_IO_PENDING , EAGAIN } -- 2.40.0