xref: /PHP-7.4/win32/ioutil.h (revision bb735c9e)
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 #ifndef PHP_WIN32_IOUTIL_H
44 #define PHP_WIN32_IOUTIL_H
45 
46 #include <fcntl.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <io.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 
53 #include "win32/winutil.h"
54 #include "win32/codepage.h"
55 
56 #ifdef __cplusplus
57 extern "C" {
58 #endif
59 
60 #ifdef PHP_EXPORTS
61 # define PW32IO __declspec(dllexport)
62 #else
63 # define PW32IO __declspec(dllimport)
64 #endif
65 
66 #define PHP_WIN32_IOUTIL_MAXPATHLEN 2048
67 
68 #if !defined(MAXPATHLEN) || MAXPATHLEN < PHP_WIN32_IOUTIL_MAXPATHLEN
69 # undef MAXPATHLEN
70 # define MAXPATHLEN PHP_WIN32_IOUTIL_MAXPATHLEN
71 #endif
72 
73 #ifndef mode_t
74 typedef unsigned short mode_t;
75 #endif
76 
77 /* these are not defined in win32 headers */
78 #ifndef W_OK
79 #define W_OK 0x02
80 #endif
81 #ifndef R_OK
82 #define R_OK 0x04
83 #endif
84 #ifndef X_OK
85 #define X_OK 0x01
86 #endif
87 #ifndef F_OK
88 #define F_OK 0x00
89 #endif
90 
91 typedef struct {
92 	DWORD access;
93 	DWORD share;
94 	DWORD disposition;
95 	DWORD attributes;
96 } php_ioutil_open_opts;
97 
98 typedef enum {
99 	PHP_WIN32_IOUTIL_IS_ASCII,
100 	PHP_WIN32_IOUTIL_IS_ANSI,
101 	PHP_WIN32_IOUTIL_IS_UTF8
102 } php_win32_ioutil_encoding;
103 
104 typedef enum {
105 	PHP_WIN32_IOUTIL_NORM_OK,
106 	PHP_WIN32_IOUTIL_NORM_PARTIAL,
107 	PHP_WIN32_IOUTIL_NORM_FAIL,
108 } php_win32_ioutil_normalization_result;
109 
110 #define PHP_WIN32_IOUTIL_FW_SLASHW L'/'
111 #define PHP_WIN32_IOUTIL_FW_SLASH '/'
112 #define PHP_WIN32_IOUTIL_BW_SLASHW L'\\'
113 #define PHP_WIN32_IOUTIL_BW_SLASH '\\'
114 #define PHP_WIN32_IOUTIL_DEFAULT_SLASHW PHP_WIN32_IOUTIL_BW_SLASHW
115 #define PHP_WIN32_IOUTIL_DEFAULT_SLASH PHP_WIN32_IOUTIL_BW_SLASH
116 
117 #define PHP_WIN32_IOUTIL_DEFAULT_DIR_SEPARATORW	L';'
118 #define PHP_WIN32_IOUTIL_IS_SLASHW(c) ((c) == PHP_WIN32_IOUTIL_BW_SLASHW || (c) == PHP_WIN32_IOUTIL_FW_SLASHW)
119 #define PHP_WIN32_IOUTIL_IS_LETTERW(c) (((c) >= L'a' && (c) <= L'z') || ((c) >= L'A' && (c) <= L'Z'))
120 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIXW L"\\??\\"
121 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW 4
122 #define PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW L"\\\\?\\"
123 #define PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW 4
124 #define PHP_WIN32_IOUTIL_UNC_PATH_PREFIXW L"\\\\?\\UNC\\"
125 #define PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW 8
126 
127 #define PHP_WIN32_IOUTIL_IS_LONG_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW \
128 	&& 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW))
129 #define PHP_WIN32_IOUTIL_IS_UNC_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW \
130 	&& 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_UNC_PATH_PREFIXW, PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW))
131 #define PHP_WIN32_IOUTIL_IS_JUNCTION_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW \
132 	&& 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_JUNCTION_PREFIXW, PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW))
133 #define PHP_WIN32_IOUTIL_IS_ABSOLUTEW(pathw, path_lenw) (PHP_WIN32_IOUTIL_IS_LONG_PATHW(pathw, path_lenw) \
134 	|| path_lenw >= 3 && PHP_WIN32_IOUTIL_IS_LETTERW(pathw[0]) && L':' == pathw[1] && PHP_WIN32_IOUTIL_IS_SLASHW(pathw[2]))
135 #define PHP_WIN32_IOUTIL_IS_UNC(pathw, path_lenw) (path_lenw >= 2 && PHP_WIN32_IOUTIL_IS_SLASHW(pathw[0]) && PHP_WIN32_IOUTIL_IS_SLASHW(pathw[1]) \
136 	|| path_lenw >= PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW && 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_UNC_PATH_PREFIXW, PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW))
137 
138 #define PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
139 
140 #define PHP_WIN32_IOUTIL_INIT_W(path) \
141 	wchar_t *pathw = php_win32_ioutil_any_to_w(path); \
142 
143 #define PHP_WIN32_IOUTIL_CLEANUP_W() do { \
144 		free(pathw); \
145 		pathw = NULL; \
146 } while (0);
147 
148 #define PHP_WIN32_IOUTIL_REINIT_W(path) do { \
149 	PHP_WIN32_IOUTIL_CLEANUP_W() \
150 	pathw = php_win32_ioutil_any_to_w(path); \
151 } while (0);
152 
153 #define PHP_WIN32_IOUTIL_PATH_IS_OK_W(pathw, len) \
154 	(!((len) >= 1 && L' ' == pathw[(len)-1] || \
155 	(len) > 1 && !PHP_WIN32_IOUTIL_IS_SLASHW(pathw[(len)-2]) && L'.' != pathw[(len)-2] && L'.' == pathw[(len)-1]))
156 
157 #define PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, ret, dealloc) do { \
158 		size_t _len = wcslen(pathw); \
159 		if (!PHP_WIN32_IOUTIL_PATH_IS_OK_W(pathw, _len)) { \
160 			if (dealloc) { \
161 				free((void *)pathw); \
162 			} \
163 			SET_ERRNO_FROM_WIN32_CODE(ERROR_ACCESS_DENIED); \
164 			return ret; \
165 		} \
166 } while (0);
167 
168 PW32IO php_win32_ioutil_normalization_result php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len);
169 #ifdef PHP_EXPORTS
170 /* This symbols are needed only for the DllMain, but should not be exported
171 	or be available when used with PHP binaries. */
172 BOOL php_win32_ioutil_init(void);
173 #endif
174 
175 /* Keep these functions aliased for case some additional handling
176    is needed later. */
177 __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, size_t in_len, size_t *out_len)
178 {/*{{{*/
179 	wchar_t *mb, *ret;
180 	size_t mb_len;
181 
182 	mb = php_win32_cp_conv_any_to_w(in, in_len, &mb_len);
183 	if (!mb) {
184 		return NULL;
185 	}
186 
187 	/* Only prefix with long if it's needed. */
188 	if (mb_len >= _MAX_PATH) {
189 		size_t new_mb_len;
190 
191 		ret = (wchar_t *) malloc((mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t));
192 		if (!ret) {
193 			free(mb);
194 			return NULL;
195 		}
196 
197 		if (PHP_WIN32_IOUTIL_NORM_FAIL == php_win32_ioutil_normalize_path_w(&mb, mb_len, &new_mb_len)) {
198 				free(ret);
199 				free(mb);
200 				return NULL;
201 		}
202 
203 		if (new_mb_len > mb_len) {
204 			wchar_t *tmp = (wchar_t *) realloc(ret, (new_mb_len + 1) * sizeof(wchar_t));
205 			if (!tmp) {
206 				free(ret);
207 				free(mb);
208 				return NULL;
209 			}
210 			ret = tmp;
211 			mb_len = new_mb_len;
212 		}
213 
214 		if (PHP_WIN32_IOUTIL_IS_LONG_PATHW(mb, mb_len) || PHP_WIN32_IOUTIL_IS_JUNCTION_PATHW(mb, mb_len) || PHP_WIN32_IOUTIL_IS_UNC_PATHW(mb, mb_len)) {
215 			memmove(ret, mb, mb_len * sizeof(wchar_t));
216 			ret[mb_len] = L'\0';
217 		} else {
218 			wchar_t *src = mb, *dst = ret + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
219 			memmove(ret, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t));
220 			while (src < mb + mb_len) {
221 				if (*src == PHP_WIN32_IOUTIL_FW_SLASHW) {
222 					*dst++ = PHP_WIN32_IOUTIL_DEFAULT_SLASHW;
223 					src++;
224 				} else {
225 					*dst++ = *src++;
226 				}
227 			}
228 			ret[mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW] = L'\0';
229 
230 			mb_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
231 		}
232 
233 		free(mb);
234 	} else {
235 		ret = mb;
236 	}
237 
238 	if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) {
239 		*out_len = mb_len;
240 	}
241 
242 	return ret;
243 }/*}}}*/
244 #define php_win32_ioutil_any_to_w(in) php_win32_ioutil_conv_any_to_w(in, PHP_WIN32_CP_IGNORE_LEN, PHP_WIN32_CP_IGNORE_LEN_P)
245 
246 #define php_win32_ioutil_ascii_to_w php_win32_cp_ascii_to_w
247 #define php_win32_ioutil_utf8_to_w php_win32_cp_utf8_to_w
248 #define php_win32_ioutil_cur_to_w php_win32_cp_cur_to_w
249 #define php_win32_ioutil_w_to_any php_win32_cp_w_to_any
250 #define php_win32_ioutil_conv_w_to_any php_win32_cp_conv_w_to_any
251 /*__forceinline static char *php_win32_ioutil_w_to_any(wchar_t* w_source_ptr)
252 {
253 	return php_win32_cp_w_to_any(w_source_ptr);
254 }*/
255 #define php_win32_ioutil_w_to_utf8 php_win32_cp_w_to_utf8
256 #define php_win32_ioutil_w_to_thread php_win32_cp_w_to_thread
257 
258 PW32IO int php_win32_ioutil_close(int fd);
259 PW32IO BOOL php_win32_ioutil_posix_to_open_opts(int flags, mode_t mode, php_ioutil_open_opts *opts);
260 PW32IO size_t php_win32_ioutil_dirname(char *buf, size_t len);
261 
262 PW32IO int php_win32_ioutil_open_w(const wchar_t *path, int flags, ...);
263 PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path);
264 PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname);
265 PW32IO wchar_t *php_win32_ioutil_getcwd_w(wchar_t *buf, size_t len);
266 PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path);
267 PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode);
268 PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode);
269 PW32IO FILE *php_win32_ioutil_fopen_w(const wchar_t *path, const wchar_t *mode);
270 PW32IO wchar_t *php_win32_ioutil_realpath_w(const wchar_t *path, wchar_t *resolved);
271 PW32IO wchar_t *php_win32_ioutil_realpath_w_ex0(const wchar_t *path, wchar_t *resolved, PBY_HANDLE_FILE_INFORMATION info);
272 
273 __forceinline static int php_win32_ioutil_access(const char *path, mode_t mode)
274 {/*{{{*/
275 	PHP_WIN32_IOUTIL_INIT_W(path)
276 	int ret, err;
277 
278 	if (!pathw) {
279 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
280 		return -1;
281 	}
282 
283 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
284 
285 	ret = php_win32_ioutil_access_w(pathw, mode);
286 	if (0 > ret) {
287 		err = GetLastError();
288 	}
289 	PHP_WIN32_IOUTIL_CLEANUP_W()
290 
291 	if (0 > ret) {
292 		SET_ERRNO_FROM_WIN32_CODE(err);
293 	}
294 
295 	return ret;
296 }/*}}}*/
297 
298 __forceinline static int php_win32_ioutil_open(const char *path, int flags, ...)
299 {/*{{{*/
300 	mode_t mode = 0;
301 	PHP_WIN32_IOUTIL_INIT_W(path)
302 	int ret = -1;
303 	DWORD err;
304 
305 	if (!pathw) {
306 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
307 		return -1;
308 	}
309 
310 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
311 
312 	if (flags & O_CREAT) {
313 		va_list arg;
314 
315 		va_start(arg, flags);
316 		mode = (mode_t) va_arg(arg, int);
317 		va_end(arg);
318 	}
319 
320 	ret = php_win32_ioutil_open_w(pathw, flags, mode);
321 	if (0 > ret) {
322 		err = GetLastError();
323 	}
324 	PHP_WIN32_IOUTIL_CLEANUP_W()
325 
326 	if (0 > ret) {
327 		SET_ERRNO_FROM_WIN32_CODE(err);
328 	}
329 
330 	return ret;
331 }/*}}}*/
332 
333 __forceinline static int php_win32_ioutil_unlink(const char *path)
334 {/*{{{*/
335 	PHP_WIN32_IOUTIL_INIT_W(path)
336 	int ret = -1;
337 	DWORD err;
338 
339 	if (!pathw) {
340 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
341 		return -1;
342 	}
343 
344 	ret = php_win32_ioutil_unlink_w(pathw);
345 	if (0 > ret) {
346 		err = GetLastError();
347 	}
348 	PHP_WIN32_IOUTIL_CLEANUP_W()
349 
350 	if (0 > ret) {
351 		SET_ERRNO_FROM_WIN32_CODE(err);
352 	}
353 
354 	return ret;
355 }/*}}}*/
356 
357 __forceinline static int php_win32_ioutil_rmdir(const char *path)
358 {/*{{{*/
359 	PHP_WIN32_IOUTIL_INIT_W(path)
360 	int ret = 0;
361 	DWORD err = 0;
362 
363 	if (!pathw) {
364 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
365 		return -1;
366 	}
367 
368 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
369 
370 	if (!RemoveDirectoryW(pathw)) {
371 		err = GetLastError();
372 		ret = -1;
373 	}
374 
375 	PHP_WIN32_IOUTIL_CLEANUP_W()
376 
377 	if (0 > ret) {
378 		SET_ERRNO_FROM_WIN32_CODE(err);
379 	}
380 
381 	return ret;
382 }/*}}}*/
383 
384 __forceinline static FILE *php_win32_ioutil_fopen(const char *patha, const char *modea)
385 {/*{{{*/
386 	FILE *ret;
387 	wchar_t modew[16] = {0};
388 	int i = 0;
389 
390 	PHP_WIN32_IOUTIL_INIT_W(patha)
391 	if (!pathw) {
392 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
393 		return NULL;
394 	}
395 
396 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, NULL, 1)
397 
398 	while (i < (sizeof(modew)-1)/sizeof(wchar_t) && modea[i]) {
399 		modew[i] = (wchar_t)modea[i];
400 		i++;
401 	}
402 
403 	ret = php_win32_ioutil_fopen_w(pathw, modew);
404 	if (!ret) {
405 		int err = GetLastError();
406 		PHP_WIN32_IOUTIL_CLEANUP_W()
407 		SET_ERRNO_FROM_WIN32_CODE(err);
408 		return NULL;
409 	}
410 
411 	PHP_WIN32_IOUTIL_CLEANUP_W()
412 
413 	return ret;
414 }/*}}}*/
415 
416 __forceinline static int php_win32_ioutil_rename(const char *oldnamea, const char *newnamea)
417 {/*{{{*/
418 	wchar_t *oldnamew;
419 	wchar_t *newnamew;
420 	int ret;
421 	DWORD err = 0;
422 
423 	oldnamew = php_win32_ioutil_any_to_w(oldnamea);
424 	if (!oldnamew) {
425 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
426 		return -1;
427 	}
428 	PHP_WIN32_IOUTIL_CHECK_PATH_W(oldnamew, -1, 1)
429 
430 	newnamew = php_win32_ioutil_any_to_w(newnamea);
431 	if (!newnamew) {
432 		free(oldnamew);
433 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
434 		return -1;
435 	} else {
436 		size_t newnamew_len = wcslen(newnamew);
437 		if (!PHP_WIN32_IOUTIL_PATH_IS_OK_W(newnamew, newnamew_len)) {
438 			free(oldnamew);
439 			free(newnamew);
440 			SET_ERRNO_FROM_WIN32_CODE(ERROR_ACCESS_DENIED);
441 			return -1;
442 		}
443 	}
444 
445 	ret = php_win32_ioutil_rename_w(oldnamew, newnamew);
446 	if (0 > ret) {
447 		err = GetLastError();
448 	}
449 
450 	free(oldnamew);
451 	free(newnamew);
452 
453 	if (0 > ret) {
454 		SET_ERRNO_FROM_WIN32_CODE(err);
455 	}
456 
457 	return ret;
458 }/*}}}*/
459 
460 __forceinline static int php_win32_ioutil_chdir(const char *patha)
461 {/*{{{*/
462 	int ret;
463 	wchar_t *pathw = php_win32_ioutil_any_to_w(patha);
464 	DWORD err = 0;
465 
466 	if (!pathw) {
467 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
468 		return -1;
469 	}
470 
471 	ret = php_win32_ioutil_chdir_w(pathw);
472 	if (0 > ret) {
473 		err = GetLastError();
474 	}
475 
476 	free(pathw);
477 
478 	if (0 > ret) {
479 		SET_ERRNO_FROM_WIN32_CODE(err);
480 	}
481 
482 	return ret;
483 }/*}}}*/
484 
485 __forceinline static char *php_win32_ioutil_getcwd(char *buf, size_t len)
486 {/*{{{*/
487 	wchar_t tmp_bufw[PHP_WIN32_IOUTIL_MAXPATHLEN];
488 	char *tmp_bufa = NULL;
489 	size_t tmp_bufa_len;
490 	DWORD err = 0;
491 
492 	if (php_win32_ioutil_getcwd_w(tmp_bufw, len ? len : PHP_WIN32_IOUTIL_MAXPATHLEN) == NULL) {
493 		err = GetLastError();
494 		SET_ERRNO_FROM_WIN32_CODE(err);
495 		return NULL;
496 	}
497 
498 	tmp_bufa = php_win32_cp_conv_w_to_any(tmp_bufw, wcslen(tmp_bufw), &tmp_bufa_len);
499 	if (!tmp_bufa) {
500 		err = GetLastError();
501 		SET_ERRNO_FROM_WIN32_CODE(err);
502 		return NULL;
503 	} else if (tmp_bufa_len + 1 > PHP_WIN32_IOUTIL_MAXPATHLEN) {
504 		free(tmp_bufa);
505 		SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_LENGTH);
506 		return NULL;
507 	} else if (tmp_bufa_len + 1 > len) {
508 		free(tmp_bufa);
509 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INSUFFICIENT_BUFFER);
510 		return NULL;
511 	}
512 
513 	if (!buf && !len) {
514 		/* If buf was NULL, the result has to be freed outside here. */
515 		buf = tmp_bufa;
516 	} else {
517 		memmove(buf, tmp_bufa, tmp_bufa_len + 1);
518 		free(tmp_bufa);
519 	}
520 
521 	return buf;
522 }/*}}}*/
523 
524 /* TODO improve with usage of native APIs, split for _a and _w. */
525 __forceinline static int php_win32_ioutil_chmod(const char *patha, int mode)
526 {/*{{{*/
527 	wchar_t *pathw = php_win32_ioutil_any_to_w(patha);
528 	int err = 0;
529 	int ret;
530 
531 	if (!pathw) {
532 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
533 		return -1;
534 	}
535 
536 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
537 
538 	ret = _wchmod(pathw, mode);
539 	if (0 > ret) {
540 		_get_errno(&err);
541 	}
542 
543 	free(pathw);
544 
545 	if (0 > ret) {
546 		_set_errno(err);
547 	}
548 
549 	return ret;
550 }/*}}}*/
551 
552 __forceinline static int php_win32_ioutil_mkdir(const char *path, mode_t mode)
553 {/*{{{*/
554 	int ret;
555 	DWORD err = 0;
556 
557 	PHP_WIN32_IOUTIL_INIT_W(path)
558 	if (!pathw) {
559 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
560 		return -1;
561 	}
562 
563 	ret = php_win32_ioutil_mkdir_w(pathw, mode);
564 	if (0 > ret) {
565 		err = GetLastError();
566 	}
567 
568 	PHP_WIN32_IOUTIL_CLEANUP_W()
569 
570 	if (0 > ret) {
571 		SET_ERRNO_FROM_WIN32_CODE(err);
572 	}
573 
574 	return ret;
575 }/*}}}*/
576 
577 #define HAVE_REALPATH 1
578 PW32IO char *realpath(const char *path, char *resolved);
579 
580 __forceinline static char *php_win32_ioutil_realpath_ex0(const char *path, char *resolved, PBY_HANDLE_FILE_INFORMATION info)
581 {/*{{{*/
582 	wchar_t retw[PHP_WIN32_IOUTIL_MAXPATHLEN];
583 	char *reta;
584 	size_t reta_len;
585 
586 	PHP_WIN32_IOUTIL_INIT_W(path)
587 	if (!pathw) {
588 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
589 		return NULL;
590 	}
591 
592 	if (NULL == php_win32_ioutil_realpath_w_ex0(pathw, retw, info)) {
593 		DWORD err = GetLastError();
594 		PHP_WIN32_IOUTIL_CLEANUP_W()
595 		SET_ERRNO_FROM_WIN32_CODE(err);
596 		return NULL;
597 	}
598 
599 	reta = php_win32_cp_conv_w_to_any(retw, PHP_WIN32_CP_IGNORE_LEN, &reta_len);
600 	if (!reta || reta_len > PHP_WIN32_IOUTIL_MAXPATHLEN) {
601 		DWORD err = GetLastError();
602 		PHP_WIN32_IOUTIL_CLEANUP_W()
603 		SET_ERRNO_FROM_WIN32_CODE(err);
604 		return NULL;
605 	}
606 
607 	if (NULL == resolved) {
608 		/* ret is expected to be either NULL or a buffer of capable size. */
609 		resolved = (char *) malloc(reta_len + 1);
610 		if (!resolved) {
611 			free(reta);
612 			PHP_WIN32_IOUTIL_CLEANUP_W()
613 			SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
614 			return NULL;
615 		}
616 	}
617 	memmove(resolved, reta, reta_len+1);
618 
619 	PHP_WIN32_IOUTIL_CLEANUP_W()
620 	free(reta);
621 
622 	return resolved;
623 }/*}}}*/
624 
625 __forceinline static char *php_win32_ioutil_realpath(const char *path, char *resolved)
626 {/*{{{*/
627 	return php_win32_ioutil_realpath_ex0(path, resolved, NULL);
628 }/*}}}*/
629 
630 #ifdef __cplusplus
631 }
632 #endif
633 
634 #endif /* PHP_WIN32_IOUTIL_H */
635 
636 /*
637  * Local variables:
638  * tab-width: 4
639  * c-basic-offset: 4
640  * End:
641  * vim600: sw=4 ts=4 fdm=marker
642  * vim<600: sw=4 ts=4
643  */
644