xref: /PHP-7.1/win32/ioutil.c (revision 7f6387b5)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Anatol Belski <ab@php.net>                                   |
16    +----------------------------------------------------------------------+
17 */
18 
19 /* This file integrates several modified parts from the libuv project, which
20  * is copyrighted to
21  *
22  * Copyright Joyent, Inc. and other Node contributors. All rights reserved.
23  *
24  * Permission is hereby granted, free of charge, to any person obtaining a copy
25  * of this software and associated documentation files (the "Software"), to
26  * deal in the Software without restriction, including without limitation the
27  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
28  * sell copies of the Software, and to permit persons to whom the Software is
29  * furnished to do so, subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be included in
32  * all copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
39  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
40  * IN THE SOFTWARE.
41  */
42 
43 #include <assert.h>
44 #include <stdlib.h>
45 #include <direct.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <io.h>
49 #include <limits.h>
50 #include <sys/stat.h>
51 #include <sys/utime.h>
52 #include <stdio.h>
53 
54 #include "php.h"
55 #include "SAPI.h"
56 #include "win32/winutil.h"
57 #include "win32/time.h"
58 #include "win32/ioutil.h"
59 #include "win32/codepage.h"
60 
61 #include <pathcch.h>
62 
63 /*
64 #undef NONLS
65 #undef _WINNLS_
66 #include <winnls.h>
67 */
68 
69 typedef HRESULT (__stdcall *MyPathCchCanonicalizeEx)(wchar_t *pszPathOut, size_t cchPathOut, const wchar_t *pszPathIn, unsigned long dwFlags);
70 
71 static MyPathCchCanonicalizeEx canonicalize_path_w = NULL;
72 
php_win32_ioutil_posix_to_open_opts(int flags,mode_t mode,php_ioutil_open_opts * opts)73 PW32IO BOOL php_win32_ioutil_posix_to_open_opts(int flags, mode_t mode, php_ioutil_open_opts *opts)
74 {/*{{{*/
75 	int current_umask;
76 
77 	opts->attributes = 0;
78 
79 	/* Obtain the active umask. umask() never fails and returns the previous */
80 	/* umask. */
81 	current_umask = umask(0);
82 	umask(current_umask);
83 
84 	/* convert flags and mode to CreateFile parameters */
85 	switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
86 		case _O_RDONLY:
87 			opts->access = FILE_GENERIC_READ;
88 			/* XXX not opening dirs yet, see also at the bottom of this function. Should be evaluated properly. */
89 			/*opts->attributes |= FILE_FLAG_BACKUP_SEMANTICS;*/
90 			break;
91 		case _O_WRONLY:
92 			opts->access = FILE_GENERIC_WRITE;
93 			break;
94 		case _O_RDWR:
95 			opts->access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
96 			break;
97 		default:
98 			goto einval;
99 	}
100 
101 	if (flags & _O_APPEND) {
102 		/* XXX this might look wrong, but i just leave it here. Disabling FILE_WRITE_DATA prevents the current truncate behaviors for files opened with "a". */
103 		/* access &= ~FILE_WRITE_DATA;*/
104 		opts->access |= FILE_APPEND_DATA;
105 		opts->attributes &= ~FILE_FLAG_BACKUP_SEMANTICS;
106 	}
107 
108 	/*
109 	* Here is where we deviate significantly from what CRT's _open()
110 	* does. We indiscriminately use all the sharing modes, to match
111 	* UNIX semantics. In particular, this ensures that the file can
112 	* be deleted even whilst it's open.
113 	*/
114 	/* opts->share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; */
115 	/* XXX No UINX behavior  Good to know it's doable.
116 	   Not being done as this means a behavior change. Should be evaluated properly. */
117 	opts->share = FILE_SHARE_READ | FILE_SHARE_WRITE;
118 
119 	switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
120 		case 0:
121 		case _O_EXCL:
122 			opts->disposition = OPEN_EXISTING;
123 			break;
124 		case _O_CREAT:
125 			opts->disposition = OPEN_ALWAYS;
126 			break;
127 		case _O_CREAT | _O_EXCL:
128 		case _O_CREAT | _O_TRUNC | _O_EXCL:
129 			opts->disposition = CREATE_NEW;
130 			break;
131 		case _O_TRUNC:
132 		case _O_TRUNC | _O_EXCL:
133 			opts->disposition = TRUNCATE_EXISTING;
134 			break;
135 		case _O_CREAT | _O_TRUNC:
136 			opts->disposition = CREATE_ALWAYS;
137 			break;
138 		default:
139 			goto einval;
140 	}
141 
142 	opts->attributes |= FILE_ATTRIBUTE_NORMAL;
143 	if (flags & _O_CREAT) {
144 		if (!((mode & ~current_umask) & _S_IWRITE)) {
145 			opts->attributes |= FILE_ATTRIBUTE_READONLY;
146 		}
147 	}
148 
149 	if (flags & _O_TEMPORARY ) {
150 		opts->attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
151 		opts->access |= DELETE;
152 	}
153 
154 	if (flags & _O_SHORT_LIVED) {
155 		opts->attributes |= FILE_ATTRIBUTE_TEMPORARY;
156 	}
157 
158 	switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) {
159 		case 0:
160 			break;
161 		case _O_SEQUENTIAL:
162 			opts->attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
163 			break;
164 		case _O_RANDOM:
165 			opts->attributes |= FILE_FLAG_RANDOM_ACCESS;
166 			break;
167 		default:
168 			goto einval;
169 	}
170 
171 	/* Very compat options */
172 	/*if (flags & O_ASYNC) {
173 		opts->attributes |= FILE_FLAG_OVERLAPPED;
174 	} else if (flags & O_SYNC) {
175 		opts->attributes &= ~FILE_FLAG_OVERLAPPED;
176 	}*/
177 
178 	/* Setting this flag makes it possible to open a directory. */
179 	/* XXX not being done as this means a behavior change. Should be evaluated properly. */
180 	/* opts->attributes |= FILE_FLAG_BACKUP_SEMANTICS; */
181 
182 	return 1;
183 
184 einval:
185 	_set_errno(EINVAL);
186 	return 0;
187 }/*}}}*/
188 
php_win32_ioutil_open_w(const wchar_t * path,int flags,...)189 PW32IO int php_win32_ioutil_open_w(const wchar_t *path, int flags, ...)
190 {/*{{{*/
191 	php_ioutil_open_opts open_opts;
192 	HANDLE file;
193 	int fd;
194 	mode_t mode = 0;
195 
196 	PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
197 
198 	if (flags & O_CREAT) {
199 		va_list arg;
200 
201 		va_start(arg, flags);
202 		mode = (mode_t) va_arg(arg, int);
203 		va_end(arg);
204 	}
205 
206 	if (!php_win32_ioutil_posix_to_open_opts(flags, mode, &open_opts)) {
207 		goto einval;
208 	}
209 
210 	/* XXX care about security attributes here if needed, see tsrm_win32_access() */
211 	file = CreateFileW(path,
212 		open_opts.access,
213 		open_opts.share,
214 		NULL,
215 		open_opts.disposition,
216 		open_opts.attributes,
217 		NULL);
218 
219 	if (file == INVALID_HANDLE_VALUE) {
220 		DWORD error = GetLastError();
221 
222 		if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) &&
223 			!(flags & _O_EXCL)) {
224 			/* Special case: when ERROR_FILE_EXISTS happens and O_CREAT was */
225 			/* specified, it means the path referred to a directory. */
226 			_set_errno(EISDIR);
227 		} else {
228 			SET_ERRNO_FROM_WIN32_CODE(error);
229 		}
230 		return -1;
231 	}
232 
233 	fd = _open_osfhandle((intptr_t) file, flags);
234 	if (fd < 0) {
235 		DWORD error = GetLastError();
236 
237 		/* The only known failure mode for _open_osfhandle() is EMFILE, in which
238 		 * case GetLastError() will return zero. However we'll try to handle other
239 		 * errors as well, should they ever occur.
240 		 */
241 		if (errno == EMFILE) {
242 			_set_errno(EMFILE);
243 		} else if (error != ERROR_SUCCESS) {
244 			SET_ERRNO_FROM_WIN32_CODE(error);
245 		}
246 		CloseHandle(file);
247 		return -1;
248 	}
249 
250 	if (flags & _O_TEXT) {
251 		_setmode(fd, _O_TEXT);
252 	} else if (flags & _O_BINARY) {
253 		_setmode(fd, _O_BINARY);
254 	}
255 
256 	return fd;
257 
258 	einval:
259 		_set_errno(EINVAL);
260 		return -1;
261 }/*}}}*/
262 
php_win32_ioutil_close(int fd)263 PW32IO int php_win32_ioutil_close(int fd)
264 {/*{{{*/
265 	int result = -1;
266 
267 	if (-1 == fd) {
268 		_set_errno(EBADF);
269 		return result;
270 	}
271 
272 	if (fd > 2) {
273 		result = _close(fd);
274 	} else {
275 		result = 0;
276 	}
277 
278 	/* _close doesn't set _doserrno on failure, but it does always set errno
279 	* to EBADF on failure.
280 	*/
281 	if (result == -1) {
282 		_set_errno(EBADF);
283 	}
284 
285 	return result;
286 }/*}}}*/
287 
288 #if 0
289 PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode)
290 {/*{{{*/
291 	int ret = 0;
292 	DWORD err = 0;
293 
294 	PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
295 
296 	/* TODO extend with mode usage */
297 	if (!CreateDirectoryW(path, NULL)) {
298 		err = GetLastError();
299 		ret = -1;
300 		SET_ERRNO_FROM_WIN32_CODE(err);
301 	}
302 
303 	return ret;
304 }/*}}}*/
305 #endif
306 
php_win32_ioutil_mkdir(const char * path,mode_t mode)307 PW32IO int php_win32_ioutil_mkdir(const char *path, mode_t mode)
308 {/*{{{*/
309 	size_t pathw_len = 0;
310 	wchar_t *pathw = php_win32_ioutil_conv_any_to_w(path, 0, &pathw_len);
311 	int ret = 0;
312 	DWORD err = 0;
313 
314 	if (pathw_len < _MAX_PATH && pathw_len >= _MAX_PATH - 12) {
315 		/* Special case here. From the doc:
316 
317 		 "When using an API to create a directory, the specified path cannot be
318 		 so long that you cannot append an 8.3 file name ..."
319 
320 		 Thus, if the directory name length happens to be in this range, it
321 		 already needs to be a long path. The given path is already normalized
322 		 and prepared, need only to prefix it.
323 		 */
324 		wchar_t *tmp = (wchar_t *) malloc((pathw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t));
325 		if (!tmp) {
326 			free(pathw);
327 			SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
328 			return -1;
329 		}
330 
331 		memmove(tmp, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t));
332 		memmove(tmp+PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, pathw, pathw_len * sizeof(wchar_t));
333 		pathw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
334 		tmp[pathw_len] = L'\0';
335 
336 		free(pathw);
337 		pathw = tmp;
338 	}
339 
340 	/* TODO extend with mode usage */
341 	if (!pathw) {
342 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
343 		return -1;
344 	}
345 
346 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
347 
348 	if (!CreateDirectoryW(pathw, NULL)) {
349 		err = GetLastError();
350 		ret = -1;
351 	}
352 	free(pathw);
353 
354 	if (0 > ret) {
355 		SET_ERRNO_FROM_WIN32_CODE(err);
356 	}
357 
358 	return ret;
359 }/*}}}*/
360 
php_win32_ioutil_unlink_w(const wchar_t * path)361 PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path)
362 {/*{{{*/
363 	int ret = 0;
364 	DWORD err = 0;
365 
366 	PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
367 
368 	if (!DeleteFileW(path)) {
369 		err = GetLastError();
370 		ret = -1;
371 		SET_ERRNO_FROM_WIN32_CODE(err);
372 	}
373 
374 	return ret;
375 }/*}}}*/
376 
php_win32_ioutil_rmdir_w(const wchar_t * path)377 PW32IO int php_win32_ioutil_rmdir_w(const wchar_t *path)
378 {/*{{{*/
379 	int ret = 0;
380 	DWORD err = 0;
381 
382 	PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
383 
384 	if (!RemoveDirectoryW(path)) {
385 		err = GetLastError();
386 		ret = -1;
387 		SET_ERRNO_FROM_WIN32_CODE(err);
388 	}
389 
390 	return ret;
391 }/*}}}*/
392 
php_win32_ioutil_chdir_w(const wchar_t * path)393 PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path)
394 {/*{{{*/
395 	int ret = 0;
396 	DWORD err = 0;
397 
398 	if (!SetCurrentDirectoryW(path)) {
399 		err = GetLastError();
400 		ret = -1;
401 		SET_ERRNO_FROM_WIN32_CODE(err);
402 	}
403 
404 	return ret;
405 }/*}}}*/
406 
php_win32_ioutil_rename_w(const wchar_t * oldname,const wchar_t * newname)407 PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname)
408 {/*{{{*/
409 	int ret = 0;
410 	DWORD err = 0;
411 
412 	PHP_WIN32_IOUTIL_CHECK_PATH_W(oldname, -1, 0)
413 	PHP_WIN32_IOUTIL_CHECK_PATH_W(newname, -1, 0)
414 
415 
416 	if (!MoveFileExW(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED)) {
417 		err = GetLastError();
418 		ret = -1;
419 		SET_ERRNO_FROM_WIN32_CODE(err);
420 	}
421 
422 	return ret;
423 }/*}}}*/
424 
php_win32_ioutil_getcwd_w(const wchar_t * buf,int len)425 PW32IO wchar_t *php_win32_ioutil_getcwd_w(const wchar_t *buf, int len)
426 {/*{{{*/
427 	DWORD err = 0;
428 	wchar_t *tmp_buf = NULL;
429 
430 	/* If buf was NULL, the result has to be freed outside here. */
431 	if (!buf) {
432 		DWORD tmp_len = GetCurrentDirectoryW(0, NULL) + 1;
433 		if (!tmp_len) {
434 			err = GetLastError();
435 			SET_ERRNO_FROM_WIN32_CODE(err);
436 			return NULL;
437 		} else if (tmp_len > len) {
438 			SET_ERRNO_FROM_WIN32_CODE(ERROR_INSUFFICIENT_BUFFER);
439 			return NULL;
440 		}
441 
442 		len = tmp_len;
443 
444 		tmp_buf = (wchar_t *)malloc((len)*sizeof(wchar_t));
445 		if (!tmp_buf) {
446 			SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
447 			return NULL;
448 		}
449 		buf = tmp_buf;
450 	}
451 
452 	if (!GetCurrentDirectoryW(len, buf)) {
453 		err = GetLastError();
454 		SET_ERRNO_FROM_WIN32_CODE(err);
455 		free(tmp_buf);
456 		return NULL;
457 	}
458 
459 	return (wchar_t *)buf;
460 }/*}}}*/
461 
462 /* based on zend_dirname(). */
php_win32_ioutil_dirname(char * path,size_t len)463 PW32IO size_t php_win32_ioutil_dirname(char *path, size_t len)
464 {/*{{{*/
465 	char *ret = NULL, *start;
466 	size_t ret_len, len_adjust = 0, pathw_len;
467 	wchar_t *endw, *pathw, *startw;
468 
469 	if (len == 0) {
470 		return 0;
471 	}
472 
473 	start = path;
474 
475 	/* Don't really care about the path normalization, pure parsing here. */
476 	startw = pathw = php_win32_cp_conv_any_to_w(path, len, &pathw_len);
477 	if (!pathw) {
478 		return 0;
479 	}
480 
481 	endw = pathw + pathw_len - 1;
482 
483 	if ((2 <= len) && isalpha((int)((unsigned char *)path)[0]) && (':' == path[1])) {
484 		pathw += 2;
485 		path += 2;
486 		len_adjust += 2;
487 		if (2 == len) {
488 			free(startw);
489 			return len;
490 		}
491 	}
492 
493 	/* Strip trailing slashes */
494 	while (endw >= pathw && PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
495 		endw--;
496 	}
497 	if (endw < pathw) {
498 		free(startw);
499 		/* The path only contained slashes */
500 		path[0] = PHP_WIN32_IOUTIL_DEFAULT_SLASH;
501 		path[1] = '\0';
502 		return 1 + len_adjust;
503 	}
504 
505 	/* Strip filename */
506 	while (endw >= pathw && !PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
507 		endw--;
508 	}
509 	if (endw < pathw) {
510 		free(startw);
511 		path[0] = '.';
512 		path[1] = '\0';
513 		return 1 + len_adjust;
514 	}
515 
516 	/* Strip slashes which came before the file name */
517 	while (endw >= pathw && PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
518 		endw--;
519 	}
520 	if (endw < pathw) {
521 		free(startw);
522 		path[0] = PHP_WIN32_IOUTIL_DEFAULT_SLASH;
523 		path[1] = '\0';
524 		return 1 + len_adjust;
525 	}
526 	*(endw+1) = L'\0';
527 
528 	ret_len = (endw + 1 - startw);
529 	if (PHP_WIN32_IOUTIL_IS_LONG_PATHW(startw, ret_len)) {
530 		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);
531 	} else {
532 		ret = php_win32_ioutil_conv_w_to_any(startw, ret_len, &ret_len);
533 	}
534 	memmove(start, ret, ret_len+1);
535 	assert(start[ret_len] == '\0');
536 	free(ret);
537 	free(startw);
538 
539 	return ret_len;
540 }/*}}}*/
541 
542 /* Partial normalization can still be acceptable, explicit fail has to be caught. */
php_win32_ioutil_normalize_path_w(wchar_t ** buf,size_t len,size_t * new_len)543 PW32IO php_win32_ioutil_normalization_result php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len)
544 {/*{{{*/
545 	wchar_t *pos, *idx = *buf, canonicalw[MAXPATHLEN];
546 	size_t ret_len = len;
547 
548 	if (len >= MAXPATHLEN) {
549 		SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_LENGTH);
550 		*new_len = 0;
551 		return PHP_WIN32_IOUTIL_NORM_FAIL;
552 	}
553 
554 	while (NULL != (pos = wcschr(idx, PHP_WIN32_IOUTIL_FW_SLASHW)) && idx - *buf <= len) {
555 		*pos = PHP_WIN32_IOUTIL_DEFAULT_SLASHW;
556 		idx = pos++;
557 	}
558 
559 	if (S_OK != canonicalize_path_w(canonicalw, MAXPATHLEN, *buf, PATHCCH_ALLOW_LONG_PATHS)) {
560 		/* Length unchanged. */
561 		*new_len = len;
562 		return PHP_WIN32_IOUTIL_NORM_PARTIAL;
563 	}
564 	ret_len = wcslen(canonicalw);
565 	if (ret_len != len) {
566 		if (ret_len > len) {
567 			wchar_t *tmp = realloc(*buf, (ret_len + 1) * sizeof(wchar_t));
568 			if (!tmp) {
569 				SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
570 				/* Length unchanged. */
571 				*new_len = len;
572 				return PHP_WIN32_IOUTIL_NORM_PARTIAL;
573 			}
574 			*buf = tmp;
575 		}
576 		memmove(*buf, canonicalw, (ret_len + 1) * sizeof(wchar_t));
577 	}
578 	*new_len = ret_len;
579 
580 	return PHP_WIN32_IOUTIL_NORM_OK;
581 }/*}}}*/
582 
MyPathCchCanonicalizeExFallback(wchar_t * pszPathOut,size_t cchPathOut,const wchar_t * pszPathIn,unsigned long dwFlags)583 static HRESULT __stdcall MyPathCchCanonicalizeExFallback(wchar_t *pszPathOut, size_t cchPathOut, const wchar_t *pszPathIn, unsigned long dwFlags)
584 {/*{{{*/
585 	return -42;
586 }/*}}}*/
587 
php_win32_ioutil_init(void)588 BOOL php_win32_ioutil_init(void)
589 {/*{{{*/
590 	HMODULE hMod = GetModuleHandle("api-ms-win-core-path-l1-1-0");
591 
592 	if (hMod) {
593 		canonicalize_path_w = (MyPathCchCanonicalizeEx)GetProcAddress(hMod, "PathCchCanonicalizeEx");
594 		if (!canonicalize_path_w) {
595 			canonicalize_path_w = (MyPathCchCanonicalizeEx)MyPathCchCanonicalizeExFallback;
596 		}
597 	} else {
598 		canonicalize_path_w = (MyPathCchCanonicalizeEx)MyPathCchCanonicalizeExFallback;
599 	}
600 
601 	return TRUE;
602 }/*}}}*/
603 
604 /* an extended version could be implemented, for now direct functions can be used. */
605 #if 0
606 PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode)
607 {
608 	return _waccess(path, mode);
609 }
610 #endif
611 
612 #if 0
613 PW32IO HANDLE php_win32_ioutil_findfirstfile_w(char *path, WIN32_FIND_DATA *data)
614 {
615 	HANDLE ret = INVALID_HANDLE_VALUE;
616 	DWORD err;
617 
618 	if (!path) {
619 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
620 		return ret;
621 	}
622 
623 	pathw = php_win32_ioutil_any_to_w(path);
624 
625 	if (!pathw) {
626 		err = GetLastError();
627 		SET_ERRNO_FROM_WIN32_CODE(ret);
628 		return ret;
629 	}
630 
631 		ret = FindFirstFileW(pathw, data);
632 
633 	if (INVALID_HANDLE_VALUE == ret && path) {
634 		ret = FindFirstFileA(path, data);
635 	}
636 
637 	/* XXX set errno */
638 	return ret;
639 }
640 #endif
641 
642 /*
643  * Local variables:
644  * tab-width: 4
645  * c-basic-offset: 4
646  * End:
647  * vim600: sw=4 ts=4 fdm=marker
648  * vim<600: sw=4 ts=4
649  */
650