#include <assert.h>
#include <stdlib.h>
#include <direct.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/utime.h>
#include <stdio.h>
#include "php.h"
#include "SAPI.h"
#include "win32/winutil.h"
#include "win32/time.h"
#include "win32/ioutil.h"
#include "win32/codepage.h"
#include <pathcch.h>
typedef HRESULT (__stdcall *MyPathCchCanonicalizeEx)(wchar_t *pszPathOut, size_t cchPathOut, const wchar_t *pszPathIn, unsigned long dwFlags);
static MyPathCchCanonicalizeEx canonicalize_path_w = NULL;
PW32IO BOOL php_win32_ioutil_posix_to_open_opts(int flags, mode_t mode, php_ioutil_open_opts *opts)
{
int current_umask;
opts->attributes = 0;
current_umask = umask(0);
umask(current_umask);
switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
case _O_RDONLY:
opts->access = FILE_GENERIC_READ;
break;
case _O_WRONLY:
opts->access = FILE_GENERIC_WRITE;
break;
case _O_RDWR:
opts->access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
break;
default:
goto einval;
}
if (flags & _O_APPEND) {
opts->access |= FILE_APPEND_DATA;
opts->attributes &= ~FILE_FLAG_BACKUP_SEMANTICS;
}
opts->share = FILE_SHARE_READ | FILE_SHARE_WRITE;
switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
case 0:
case _O_EXCL:
opts->disposition = OPEN_EXISTING;
break;
case _O_CREAT:
opts->disposition = OPEN_ALWAYS;
break;
case _O_CREAT | _O_EXCL:
case _O_CREAT | _O_TRUNC | _O_EXCL:
opts->disposition = CREATE_NEW;
break;
case _O_TRUNC:
case _O_TRUNC | _O_EXCL:
opts->disposition = TRUNCATE_EXISTING;
break;
case _O_CREAT | _O_TRUNC:
opts->disposition = CREATE_ALWAYS;
break;
default:
goto einval;
}
opts->attributes |= FILE_ATTRIBUTE_NORMAL;
if (flags & _O_CREAT) {
if (!((mode & ~current_umask) & _S_IWRITE)) {
opts->attributes |= FILE_ATTRIBUTE_READONLY;
}
}
if (flags & _O_TEMPORARY ) {
opts->attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
opts->access |= DELETE;
}
if (flags & _O_SHORT_LIVED) {
opts->attributes |= FILE_ATTRIBUTE_TEMPORARY;
}
switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) {
case 0:
break;
case _O_SEQUENTIAL:
opts->attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
break;
case _O_RANDOM:
opts->attributes |= FILE_FLAG_RANDOM_ACCESS;
break;
default:
goto einval;
}
return 1;
einval:
_set_errno(EINVAL);
return 0;
}
PW32IO int php_win32_ioutil_open_w(const wchar_t *path, int flags, ...)
{
php_ioutil_open_opts open_opts;
HANDLE file;
int fd;
mode_t mode = 0;
PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
if (flags & O_CREAT) {
va_list arg;
va_start(arg, flags);
mode = (mode_t) va_arg(arg, int);
va_end(arg);
}
if (!php_win32_ioutil_posix_to_open_opts(flags, mode, &open_opts)) {
goto einval;
}
file = CreateFileW(path,
open_opts.access,
open_opts.share,
NULL,
open_opts.disposition,
open_opts.attributes,
NULL);
if (file == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) &&
!(flags & _O_EXCL)) {
_set_errno(EISDIR);
} else {
SET_ERRNO_FROM_WIN32_CODE(error);
}
return -1;
}
fd = _open_osfhandle((intptr_t) file, flags);
if (fd < 0) {
DWORD error = GetLastError();
if (errno == EMFILE) {
_set_errno(EMFILE);
} else if (error != ERROR_SUCCESS) {
SET_ERRNO_FROM_WIN32_CODE(error);
}
CloseHandle(file);
return -1;
}
if (flags & _O_TEXT) {
_setmode(fd, _O_TEXT);
} else if (flags & _O_BINARY) {
_setmode(fd, _O_BINARY);
}
return fd;
einval:
_set_errno(EINVAL);
return -1;
}
PW32IO int php_win32_ioutil_close(int fd)
{
int result = -1;
if (-1 == fd) {
_set_errno(EBADF);
return result;
}
if (fd > 2) {
result = _close(fd);
} else {
result = 0;
}
if (result == -1) {
_set_errno(EBADF);
}
return result;
}
#if 0
PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode)
{
int ret = 0;
DWORD err = 0;
PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
if (!CreateDirectoryW(path, NULL)) {
err = GetLastError();
ret = -1;
SET_ERRNO_FROM_WIN32_CODE(err);
}
return ret;
}
#endif
PW32IO int php_win32_ioutil_mkdir(const char *path, mode_t mode)
{
wchar_t *pathw = php_win32_ioutil_any_to_w(path);
int ret = 0;
DWORD err = 0;
if (!pathw) {
SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
return -1;
}
PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
if (!CreateDirectoryW(pathw, NULL)) {
err = GetLastError();
ret = -1;
}
free(pathw);
if (0 > ret) {
SET_ERRNO_FROM_WIN32_CODE(err);
}
return ret;
}
PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path)
{
int ret = 0;
DWORD err = 0;
PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
if (!DeleteFileW(path)) {
err = GetLastError();
ret = -1;
SET_ERRNO_FROM_WIN32_CODE(err);
}
return ret;
}
PW32IO int php_win32_ioutil_rmdir_w(const wchar_t *path)
{
int ret = 0;
DWORD err = 0;
PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
if (!RemoveDirectoryW(path)) {
err = GetLastError();
ret = -1;
SET_ERRNO_FROM_WIN32_CODE(err);
}
return ret;
}
PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path)
{
int ret = 0;
DWORD err = 0;
if (!SetCurrentDirectoryW(path)) {
err = GetLastError();
ret = -1;
SET_ERRNO_FROM_WIN32_CODE(err);
}
return ret;
}
PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname)
{
int ret = 0;
DWORD err = 0;
PHP_WIN32_IOUTIL_CHECK_PATH_W(oldname, -1, 0)
PHP_WIN32_IOUTIL_CHECK_PATH_W(newname, -1, 0)
if (!MoveFileExW(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED)) {
err = GetLastError();
ret = -1;
SET_ERRNO_FROM_WIN32_CODE(err);
}
return ret;
}
PW32IO wchar_t *php_win32_ioutil_getcwd_w(const wchar_t *buf, int len)
{
DWORD err = 0;
wchar_t *tmp_buf = NULL;
if (!buf) {
DWORD tmp_len = GetCurrentDirectoryW(0, NULL) + 1;
if (!tmp_len) {
err = GetLastError();
SET_ERRNO_FROM_WIN32_CODE(err);
return NULL;
} else if (tmp_len > len) {
SET_ERRNO_FROM_WIN32_CODE(ERROR_INSUFFICIENT_BUFFER);
return NULL;
}
len = tmp_len;
tmp_buf = (wchar_t *)malloc((len)*sizeof(wchar_t));
if (!tmp_buf) {
SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
buf = tmp_buf;
}
if (!GetCurrentDirectoryW(len, buf)) {
err = GetLastError();
SET_ERRNO_FROM_WIN32_CODE(err);
free(tmp_buf);
return NULL;
}
return (wchar_t *)buf;
}
PW32IO size_t php_win32_ioutil_dirname(char *path, size_t len)
{
char *ret = NULL, *start;
size_t ret_len, len_adjust = 0, pathw_len;
wchar_t *endw, *pathw, *startw;
if (len == 0) {
return 0;
}
start = path;
startw = pathw = php_win32_cp_conv_any_to_w(path, len, &pathw_len);
if (!pathw) {
return 0;
}
endw = pathw + pathw_len - 1;
if ((2 <= len) && isalpha((int)((unsigned char *)path)[0]) && (':' == path[1])) {
pathw += 2;
path += 2;
len_adjust += 2;
if (2 == len) {
free(startw);
return len;
}
}
while (endw >= pathw && PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
endw--;
}
if (endw < pathw) {
free(startw);
path[0] = PHP_WIN32_IOUTIL_DEFAULT_SLASH;
path[1] = '\0';
return 1 + len_adjust;
}
while (endw >= pathw && !PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
endw--;
}
if (endw < pathw) {
free(startw);
path[0] = '.';
path[1] = '\0';
return 1 + len_adjust;
}
while (endw >= pathw && PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
endw--;
}
if (endw < pathw) {
free(startw);
path[0] = PHP_WIN32_IOUTIL_DEFAULT_SLASH;
path[1] = '\0';
return 1 + len_adjust;
}
*(endw+1) = L'\0';
ret_len = (endw + 1 - startw);
if (PHP_WIN32_IOUTIL_IS_LONG_PATHW(startw, ret_len)) {
ret = php_win32_ioutil_conv_w_to_any(startw + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, ret_len - PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, &ret_len);
} else {
ret = php_win32_ioutil_conv_w_to_any(startw, ret_len, &ret_len);
}
memmove(start, ret, ret_len+1);
assert(start[ret_len] == '\0');
free(ret);
free(startw);
return ret_len;
}
PW32IO php_win32_ioutil_normalization_result php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len)
{
wchar_t *pos, *idx = *buf, canonicalw[MAXPATHLEN];
size_t ret_len = len;
if (len >= MAXPATHLEN) {
SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_LENGTH);
return PHP_WIN32_IOUTIL_NORM_FAIL;
}
while (NULL != (pos = wcschr(idx, PHP_WIN32_IOUTIL_FW_SLASHW)) && idx - *buf <= len) {
*pos = PHP_WIN32_IOUTIL_DEFAULT_SLASHW;
idx = pos++;
}
if (S_OK != canonicalize_path_w(canonicalw, MAXPATHLEN, *buf, PATHCCH_ALLOW_LONG_PATHS)) {
return PHP_WIN32_IOUTIL_NORM_PARTIAL;
}
ret_len = wcslen(canonicalw);
if (ret_len != len) {
if (ret_len > len) {
wchar_t *tmp = realloc(*buf, (ret_len + 1) * sizeof(wchar_t));
if (!tmp) {
SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
return PHP_WIN32_IOUTIL_NORM_PARTIAL;
}
*buf = tmp;
}
memmove(*buf, canonicalw, (ret_len + 1) * sizeof(wchar_t));
}
*new_len = ret_len;
return PHP_WIN32_IOUTIL_NORM_OK;
}
static HRESULT __stdcall MyPathCchCanonicalizeExFallback(wchar_t *pszPathOut, size_t cchPathOut, const wchar_t *pszPathIn, unsigned long dwFlags)
{
return -42;
}
BOOL php_win32_ioutil_init(void)
{
HMODULE hMod = GetModuleHandle("api-ms-win-core-path-l1-1-0");
if (hMod) {
canonicalize_path_w = (MyPathCchCanonicalizeEx)GetProcAddress(hMod, "PathCchCanonicalizeEx");
if (!canonicalize_path_w) {
canonicalize_path_w = (MyPathCchCanonicalizeEx)MyPathCchCanonicalizeExFallback;
}
} else {
canonicalize_path_w = (MyPathCchCanonicalizeEx)MyPathCchCanonicalizeExFallback;
}
return TRUE;
}
#if 0
PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode)
{
return _waccess(path, mode);
}
#endif
#if 0
PW32IO HANDLE php_win32_ioutil_findfirstfile_w(char *path, WIN32_FIND_DATA *data)
{
HANDLE ret = INVALID_HANDLE_VALUE;
DWORD err;
if (!path) {
SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
return ret;
}
pathw = php_win32_ioutil_any_to_w(path);
if (!pathw) {
err = GetLastError();
SET_ERRNO_FROM_WIN32_CODE(ret);
return ret;
}
ret = FindFirstFileW(pathw, data);
if (INVALID_HANDLE_VALUE == ret && path) {
ret = FindFirstFileA(path, data);
}
return ret;
}
#endif