xref: /PHP-7.2/win32/ioutil.c (revision 5477d683)
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 + 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 		memmove(tmp, pathw, (pathw_len + 1) * sizeof(wchar_t));
331 
332 		if (PHP_WIN32_IOUTIL_NORM_FAIL == php_win32_ioutil_normalize_path_w(&tmp, pathw_len, &pathw_len)) {
333 			free(tmp);
334 			return -1;
335 		}
336 
337 		free(pathw);
338 		pathw = tmp;
339 
340 		if (!PHP_WIN32_IOUTIL_IS_LONG_PATHW(tmp, pathw_len)) {
341 			wchar_t *_tmp = (wchar_t *) malloc((pathw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t));
342 			if (!_tmp) {
343 				SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
344 				free(tmp);
345 				return -1;
346 			}
347 			memmove(_tmp, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t));
348 			memmove(_tmp+PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, tmp, pathw_len * sizeof(wchar_t));
349 			pathw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
350 			_tmp[pathw_len] = L'\0';
351 			free(tmp);
352 			tmp = _tmp;
353 		}
354 
355 		pathw = tmp;
356 
357 	} else {
358 		pathw = pathw;
359 	}
360 
361 	/* TODO extend with mode usage */
362 	if (!pathw) {
363 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
364 		return -1;
365 	}
366 
367 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
368 
369 	if (!CreateDirectoryW(pathw, NULL)) {
370 		err = GetLastError();
371 		ret = -1;
372 	}
373 	free(pathw);
374 
375 	if (0 > ret) {
376 		SET_ERRNO_FROM_WIN32_CODE(err);
377 	}
378 
379 	return ret;
380 }/*}}}*/
381 
php_win32_ioutil_unlink_w(const wchar_t * path)382 PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path)
383 {/*{{{*/
384 	int ret = 0;
385 	DWORD err = 0;
386 
387 	PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
388 
389 	if (!DeleteFileW(path)) {
390 		err = GetLastError();
391 		ret = -1;
392 		SET_ERRNO_FROM_WIN32_CODE(err);
393 	}
394 
395 	return ret;
396 }/*}}}*/
397 
php_win32_ioutil_rmdir_w(const wchar_t * path)398 PW32IO int php_win32_ioutil_rmdir_w(const wchar_t *path)
399 {/*{{{*/
400 	int ret = 0;
401 	DWORD err = 0;
402 
403 	PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
404 
405 	if (!RemoveDirectoryW(path)) {
406 		err = GetLastError();
407 		ret = -1;
408 		SET_ERRNO_FROM_WIN32_CODE(err);
409 	}
410 
411 	return ret;
412 }/*}}}*/
413 
php_win32_ioutil_chdir_w(const wchar_t * path)414 PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path)
415 {/*{{{*/
416 	int ret = 0;
417 	DWORD err = 0;
418 
419 	if (!SetCurrentDirectoryW(path)) {
420 		err = GetLastError();
421 		ret = -1;
422 		SET_ERRNO_FROM_WIN32_CODE(err);
423 	}
424 
425 	return ret;
426 }/*}}}*/
427 
php_win32_ioutil_rename_w(const wchar_t * oldname,const wchar_t * newname)428 PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname)
429 {/*{{{*/
430 	int ret = 0;
431 	DWORD err = 0;
432 
433 	PHP_WIN32_IOUTIL_CHECK_PATH_W(oldname, -1, 0)
434 	PHP_WIN32_IOUTIL_CHECK_PATH_W(newname, -1, 0)
435 
436 
437 	if (!MoveFileExW(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED)) {
438 		err = GetLastError();
439 		ret = -1;
440 		SET_ERRNO_FROM_WIN32_CODE(err);
441 	}
442 
443 	return ret;
444 }/*}}}*/
445 
php_win32_ioutil_getcwd_w(wchar_t * buf,size_t len)446 PW32IO wchar_t *php_win32_ioutil_getcwd_w(wchar_t *buf, size_t len)
447 {/*{{{*/
448 	DWORD err = 0;
449 	wchar_t *tmp_buf = NULL;
450 	DWORD tmp_len = (DWORD)len;
451 
452 	/* If buf was NULL, the result has to be freed outside here. */
453 	if (!buf) {
454 		tmp_len = GetCurrentDirectoryW(0, NULL) + 1;
455 		if (!tmp_len) {
456 			err = GetLastError();
457 			SET_ERRNO_FROM_WIN32_CODE(err);
458 			return NULL;
459 		} else if (tmp_len > len) {
460 			SET_ERRNO_FROM_WIN32_CODE(ERROR_INSUFFICIENT_BUFFER);
461 			return NULL;
462 		}
463 
464 		tmp_buf = (wchar_t *)malloc((tmp_len)*sizeof(wchar_t));
465 		if (!tmp_buf) {
466 			SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
467 			return NULL;
468 		}
469 		buf = tmp_buf;
470 	}
471 
472 	if (!GetCurrentDirectoryW(tmp_len, buf)) {
473 		err = GetLastError();
474 		SET_ERRNO_FROM_WIN32_CODE(err);
475 		free(tmp_buf);
476 		return NULL;
477 	}
478 
479 	return (wchar_t *)buf;
480 }/*}}}*/
481 
482 /* based on zend_dirname(). */
php_win32_ioutil_dirname(char * path,size_t len)483 PW32IO size_t php_win32_ioutil_dirname(char *path, size_t len)
484 {/*{{{*/
485 	char *ret = NULL, *start;
486 	size_t ret_len, len_adjust = 0, pathw_len;
487 	wchar_t *endw, *pathw, *startw;
488 
489 	if (len == 0) {
490 		return 0;
491 	}
492 
493 	start = path;
494 
495 	/* Don't really care about the path normalization, pure parsing here. */
496 	startw = pathw = php_win32_cp_conv_any_to_w(path, len, &pathw_len);
497 	if (!pathw) {
498 		return 0;
499 	}
500 
501 	endw = pathw + pathw_len - 1;
502 
503 	if ((2 <= pathw_len) && iswalpha((wint_t)(pathw)[0]) && (L':' == pathw[1])) {
504 		pathw += 2;
505 		path += 2;
506 		len_adjust += 2;
507 		if (2 == len) {
508 			free(startw);
509 			return len;
510 		}
511 	}
512 
513 	/* Strip trailing slashes */
514 	while (endw >= pathw && PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
515 		endw--;
516 	}
517 	if (endw < pathw) {
518 		free(startw);
519 		/* The path only contained slashes */
520 		path[0] = PHP_WIN32_IOUTIL_DEFAULT_SLASH;
521 		path[1] = '\0';
522 		return 1 + len_adjust;
523 	}
524 
525 	/* Strip filename */
526 	while (endw >= pathw && !PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
527 		endw--;
528 	}
529 	if (endw < pathw) {
530 		free(startw);
531 		path[0] = '.';
532 		path[1] = '\0';
533 		return 1 + len_adjust;
534 	}
535 
536 	/* Strip slashes which came before the file name */
537 	while (endw >= pathw && PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
538 		endw--;
539 	}
540 	if (endw < pathw) {
541 		free(startw);
542 		path[0] = PHP_WIN32_IOUTIL_DEFAULT_SLASH;
543 		path[1] = '\0';
544 		return 1 + len_adjust;
545 	}
546 	*(endw+1) = L'\0';
547 
548 	ret_len = (endw + 1 - startw);
549 	if (PHP_WIN32_IOUTIL_IS_LONG_PATHW(startw, ret_len)) {
550 		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);
551 	} else {
552 		ret = php_win32_ioutil_conv_w_to_any(startw, ret_len, &ret_len);
553 	}
554 	memmove(start, ret, ret_len+1);
555 	assert(start[ret_len] == '\0');
556 	free(ret);
557 	free(startw);
558 
559 	return ret_len;
560 }/*}}}*/
561 
562 /* 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)563 PW32IO php_win32_ioutil_normalization_result php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len)
564 {/*{{{*/
565 	wchar_t *idx = *buf, canonicalw[MAXPATHLEN], _tmp[MAXPATHLEN], *pos = _tmp;
566 	size_t ret_len = len;
567 
568 	if (len >= MAXPATHLEN) {
569 		SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_LENGTH);
570 		*new_len = 0;
571 		return PHP_WIN32_IOUTIL_NORM_FAIL;
572 	}
573 
574 	for (; (size_t)(idx - *buf) <= len; idx++, pos++) {
575 		*pos = *idx;
576 		if (PHP_WIN32_IOUTIL_FW_SLASHW == *pos) {
577 			*pos = PHP_WIN32_IOUTIL_DEFAULT_SLASHW;
578 		}
579 		while (PHP_WIN32_IOUTIL_IS_SLASHW(*idx) && PHP_WIN32_IOUTIL_IS_SLASHW(*(idx+1))) {
580 			idx++;
581 		}
582 	}
583 
584 	if (S_OK != canonicalize_path_w(canonicalw, MAXPATHLEN, _tmp, PATHCCH_ALLOW_LONG_PATHS)) {
585 		/* Length unchanged. */
586 		*new_len = len;
587 		return PHP_WIN32_IOUTIL_NORM_PARTIAL;
588 	}
589 	ret_len = wcslen(canonicalw);
590 	if (ret_len != len) {
591 		if (ret_len > len) {
592 			wchar_t *tmp = realloc(*buf, (ret_len + 1) * sizeof(wchar_t));
593 			if (!tmp) {
594 				SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
595 				/* Length unchanged. */
596 				*new_len = len;
597 				return PHP_WIN32_IOUTIL_NORM_PARTIAL;
598 			}
599 			*buf = tmp;
600 		}
601 		memmove(*buf, canonicalw, (ret_len + 1) * sizeof(wchar_t));
602 	}
603 	*new_len = ret_len;
604 
605 	return PHP_WIN32_IOUTIL_NORM_OK;
606 }/*}}}*/
607 
MyPathCchCanonicalizeExFallback(wchar_t * pszPathOut,size_t cchPathOut,const wchar_t * pszPathIn,unsigned long dwFlags)608 static HRESULT __stdcall MyPathCchCanonicalizeExFallback(wchar_t *pszPathOut, size_t cchPathOut, const wchar_t *pszPathIn, unsigned long dwFlags)
609 {/*{{{*/
610 	return -42;
611 }/*}}}*/
612 
php_win32_ioutil_init(void)613 BOOL php_win32_ioutil_init(void)
614 {/*{{{*/
615 	HMODULE hMod = GetModuleHandle("api-ms-win-core-path-l1-1-0");
616 
617 	if (hMod) {
618 		canonicalize_path_w = (MyPathCchCanonicalizeEx)GetProcAddress(hMod, "PathCchCanonicalizeEx");
619 		if (!canonicalize_path_w) {
620 			canonicalize_path_w = (MyPathCchCanonicalizeEx)MyPathCchCanonicalizeExFallback;
621 		}
622 	} else {
623 		canonicalize_path_w = (MyPathCchCanonicalizeEx)MyPathCchCanonicalizeExFallback;
624 	}
625 
626 	return TRUE;
627 }/*}}}*/
628 
629 /* an extended version could be implemented, for now direct functions can be used. */
630 #if 0
631 PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode)
632 {
633 	return _waccess(path, mode);
634 }
635 #endif
636 
637 #if 0
638 PW32IO HANDLE php_win32_ioutil_findfirstfile_w(char *path, WIN32_FIND_DATA *data)
639 {
640 	HANDLE ret = INVALID_HANDLE_VALUE;
641 	DWORD err;
642 
643 	if (!path) {
644 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
645 		return ret;
646 	}
647 
648 	pathw = php_win32_ioutil_any_to_w(path);
649 
650 	if (!pathw) {
651 		err = GetLastError();
652 		SET_ERRNO_FROM_WIN32_CODE(ret);
653 		return ret;
654 	}
655 
656 		ret = FindFirstFileW(pathw, data);
657 
658 	if (INVALID_HANDLE_VALUE == ret && path) {
659 		ret = FindFirstFileA(path, data);
660 	}
661 
662 	/* XXX set errno */
663 	return ret;
664 }
665 #endif
666 
667 /*
668  * Local variables:
669  * tab-width: 4
670  * c-basic-offset: 4
671  * End:
672  * vim600: sw=4 ts=4 fdm=marker
673  * vim<600: sw=4 ts=4
674  */
675