xref: /PHP-8.3/win32/ioutil.h (revision 01b3fc03)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Anatol Belski <ab@php.net>                                   |
14    +----------------------------------------------------------------------+
15 */
16 
17 /* This file integrates several modified parts from the libuv project, which
18  * is copyrighted to
19  *
20  * Copyright Joyent, Inc. and other Node contributors. All rights reserved.
21  *
22  * Permission is hereby granted, free of charge, to any person obtaining a copy
23  * of this software and associated documentation files (the "Software"), to
24  * deal in the Software without restriction, including without limitation the
25  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
26  * sell copies of the Software, and to permit persons to whom the Software is
27  * furnished to do so, subject to the following conditions:
28  *
29  * The above copyright notice and this permission notice shall be included in
30  * all copies or substantial portions of the Software.
31  *
32  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
37  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
38  * IN THE SOFTWARE.
39  */
40 
41 #ifndef PHP_WIN32_IOUTIL_H
42 #define PHP_WIN32_IOUTIL_H
43 
44 #include <fcntl.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <io.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 
51 #include "win32/winutil.h"
52 #include "win32/codepage.h"
53 
54 #ifdef __cplusplus
55 extern "C" {
56 #endif
57 
58 #ifdef PHP_EXPORTS
59 # define PW32IO __declspec(dllexport)
60 #else
61 # define PW32IO __declspec(dllimport)
62 #endif
63 
64 #define PHP_WIN32_IOUTIL_MAXPATHLEN 2048
65 
66 #if !defined(MAXPATHLEN) || MAXPATHLEN < PHP_WIN32_IOUTIL_MAXPATHLEN
67 # undef MAXPATHLEN
68 # define MAXPATHLEN PHP_WIN32_IOUTIL_MAXPATHLEN
69 #endif
70 
71 #ifndef mode_t
72 typedef unsigned short mode_t;
73 #endif
74 
75 /* these are not defined in win32 headers */
76 #ifndef W_OK
77 #define W_OK 0x02
78 #endif
79 #ifndef R_OK
80 #define R_OK 0x04
81 #endif
82 #ifndef X_OK
83 #define X_OK 0x01
84 #endif
85 #ifndef F_OK
86 #define F_OK 0x00
87 #endif
88 
89 /* from ntifs.h */
90 #ifndef SYMLINK_FLAG_RELATIVE
91 #define SYMLINK_FLAG_RELATIVE 0x01
92 #endif
93 
94 typedef struct {
95 	DWORD access;
96 	DWORD share;
97 	DWORD disposition;
98 	DWORD attributes;
99 } php_ioutil_open_opts;
100 
101 typedef enum {
102 	PHP_WIN32_IOUTIL_IS_ASCII,
103 	PHP_WIN32_IOUTIL_IS_ANSI,
104 	PHP_WIN32_IOUTIL_IS_UTF8
105 } php_win32_ioutil_encoding;
106 
107 typedef enum {
108 	PHP_WIN32_IOUTIL_NORM_OK,
109 	PHP_WIN32_IOUTIL_NORM_PARTIAL,
110 	PHP_WIN32_IOUTIL_NORM_FAIL,
111 } php_win32_ioutil_normalization_result;
112 
113 #define PHP_WIN32_IOUTIL_FW_SLASHW L'/'
114 #define PHP_WIN32_IOUTIL_FW_SLASH '/'
115 #define PHP_WIN32_IOUTIL_BW_SLASHW L'\\'
116 #define PHP_WIN32_IOUTIL_BW_SLASH '\\'
117 #define PHP_WIN32_IOUTIL_DEFAULT_SLASHW PHP_WIN32_IOUTIL_BW_SLASHW
118 #define PHP_WIN32_IOUTIL_DEFAULT_SLASH PHP_WIN32_IOUTIL_BW_SLASH
119 
120 #define PHP_WIN32_IOUTIL_DEFAULT_DIR_SEPARATORW	L';'
121 #define PHP_WIN32_IOUTIL_IS_SLASHW(c) ((c) == PHP_WIN32_IOUTIL_BW_SLASHW || (c) == PHP_WIN32_IOUTIL_FW_SLASHW)
122 #define PHP_WIN32_IOUTIL_IS_LETTERW(c) (((c) >= L'a' && (c) <= L'z') || ((c) >= L'A' && (c) <= L'Z'))
123 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIXW L"\\??\\"
124 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW 4
125 #define PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW L"\\\\?\\"
126 #define PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW 4
127 #define PHP_WIN32_IOUTIL_UNC_PATH_PREFIXW L"\\\\?\\UNC\\"
128 #define PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW 8
129 
130 #define PHP_WIN32_IOUTIL_IS_LONG_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW \
131 	&& 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW))
132 #define PHP_WIN32_IOUTIL_IS_UNC_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW \
133 	&& 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_UNC_PATH_PREFIXW, PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW))
134 #define PHP_WIN32_IOUTIL_IS_JUNCTION_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW \
135 	&& 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_JUNCTION_PREFIXW, PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW))
136 #define PHP_WIN32_IOUTIL_IS_ABSOLUTEW(pathw, path_lenw) (PHP_WIN32_IOUTIL_IS_LONG_PATHW(pathw, path_lenw) \
137 	|| path_lenw >= 3 && PHP_WIN32_IOUTIL_IS_LETTERW(pathw[0]) && L':' == pathw[1] && PHP_WIN32_IOUTIL_IS_SLASHW(pathw[2]))
138 #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]) \
139 	|| 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))
140 
141 #define PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
142 
143 #define PHP_WIN32_IOUTIL_INIT_W(path) \
144 	wchar_t *pathw = php_win32_ioutil_any_to_w(path); \
145 
146 #define PHP_WIN32_IOUTIL_CLEANUP_W() do { \
147 		free(pathw); \
148 		pathw = NULL; \
149 } while (0);
150 
151 #define PHP_WIN32_IOUTIL_REINIT_W(path) do { \
152 	PHP_WIN32_IOUTIL_CLEANUP_W() \
153 	pathw = php_win32_ioutil_any_to_w(path); \
154 } while (0);
155 
156 #define PHP_WIN32_IOUTIL_PATH_IS_OK_W(pathw, len) \
157 	(!((len) >= 1 && L' ' == pathw[(len)-1] || \
158 	(len) > 1 && !PHP_WIN32_IOUTIL_IS_SLASHW(pathw[(len)-2]) && L'.' != pathw[(len)-2] && L'.' == pathw[(len)-1]))
159 
160 #define PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, ret, dealloc) do { \
161 		size_t _len = wcslen(pathw); \
162 		if (!PHP_WIN32_IOUTIL_PATH_IS_OK_W(pathw, _len)) { \
163 			if (dealloc) { \
164 				free((void *)pathw); \
165 			} \
166 			SET_ERRNO_FROM_WIN32_CODE(ERROR_ACCESS_DENIED); \
167 			return ret; \
168 		} \
169 } while (0);
170 
171 PW32IO php_win32_ioutil_normalization_result php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len);
172 #ifdef PHP_EXPORTS
173 /* This symbols are needed only for the DllMain, but should not be exported
174 	or be available when used with PHP binaries. */
175 BOOL php_win32_ioutil_init(void);
176 #endif
177 
178 /* Keep these functions aliased for case some additional handling
179    is needed later. */
php_win32_ioutil_conv_any_to_w(const char * in,size_t in_len,size_t * out_len)180 __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, size_t in_len, size_t *out_len)
181 {/*{{{*/
182 	wchar_t *mb, *ret;
183 	size_t mb_len;
184 
185 	mb = php_win32_cp_conv_any_to_w(in, in_len, &mb_len);
186 	if (!mb) {
187 		return NULL;
188 	}
189 
190 	/* Only prefix with long if it's needed. */
191 	if (mb_len >= _MAX_PATH) {
192 		size_t new_mb_len;
193 
194 		ret = (wchar_t *) malloc((mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t));
195 		if (!ret) {
196 			free(mb);
197 			return NULL;
198 		}
199 
200 		if (PHP_WIN32_IOUTIL_NORM_FAIL == php_win32_ioutil_normalize_path_w(&mb, mb_len, &new_mb_len)) {
201 				free(ret);
202 				free(mb);
203 				return NULL;
204 		}
205 
206 		if (new_mb_len > mb_len) {
207 			wchar_t *tmp = (wchar_t *) realloc(ret, (new_mb_len + 1) * sizeof(wchar_t));
208 			if (!tmp) {
209 				free(ret);
210 				free(mb);
211 				return NULL;
212 			}
213 			ret = tmp;
214 			mb_len = new_mb_len;
215 		}
216 
217 		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)) {
218 			memmove(ret, mb, mb_len * sizeof(wchar_t));
219 			ret[mb_len] = L'\0';
220 		} else {
221 			wchar_t *src = mb, *dst = ret + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
222 			memmove(ret, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t));
223 			while (src < mb + mb_len) {
224 				if (*src == PHP_WIN32_IOUTIL_FW_SLASHW) {
225 					*dst++ = PHP_WIN32_IOUTIL_DEFAULT_SLASHW;
226 					src++;
227 				} else {
228 					*dst++ = *src++;
229 				}
230 			}
231 			ret[mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW] = L'\0';
232 
233 			mb_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
234 		}
235 
236 		free(mb);
237 	} else {
238 		ret = mb;
239 	}
240 
241 	if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) {
242 		*out_len = mb_len;
243 	}
244 
245 	return ret;
246 }/*}}}*/
247 #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)
248 
249 #define php_win32_ioutil_ascii_to_w php_win32_cp_ascii_to_w
250 #define php_win32_ioutil_utf8_to_w php_win32_cp_utf8_to_w
251 #define php_win32_ioutil_cur_to_w php_win32_cp_cur_to_w
252 #define php_win32_ioutil_w_to_any php_win32_cp_w_to_any
253 #define php_win32_ioutil_conv_w_to_any php_win32_cp_conv_w_to_any
254 /*__forceinline static char *php_win32_ioutil_w_to_any(wchar_t* w_source_ptr)
255 {
256 	return php_win32_cp_w_to_any(w_source_ptr);
257 }*/
258 #define php_win32_ioutil_w_to_utf8 php_win32_cp_w_to_utf8
259 #define php_win32_ioutil_w_to_thread php_win32_cp_w_to_thread
260 
261 PW32IO int php_win32_ioutil_close(int fd);
262 PW32IO BOOL php_win32_ioutil_posix_to_open_opts(int flags, mode_t mode, php_ioutil_open_opts *opts);
263 PW32IO size_t php_win32_ioutil_dirname(char *buf, size_t len);
264 
265 PW32IO int php_win32_ioutil_open_w(const wchar_t *path, int flags, ...);
266 PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path);
267 PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname);
268 PW32IO wchar_t *php_win32_ioutil_getcwd_w(wchar_t *buf, size_t len);
269 PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path);
270 PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode);
271 PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode);
272 PW32IO FILE *php_win32_ioutil_fopen_w(const wchar_t *path, const wchar_t *mode);
273 PW32IO wchar_t *php_win32_ioutil_realpath_w(const wchar_t *path, wchar_t *resolved);
274 PW32IO wchar_t *php_win32_ioutil_realpath_w_ex0(const wchar_t *path, wchar_t *resolved, PBY_HANDLE_FILE_INFORMATION info);
275 PW32IO int php_win32_ioutil_symlink_w(const wchar_t *target, const wchar_t *link);
276 PW32IO int php_win32_ioutil_link_w(const wchar_t *target, const wchar_t *link);
277 
php_win32_ioutil_access(const char * path,mode_t mode)278 __forceinline static int php_win32_ioutil_access(const char *path, mode_t mode)
279 {/*{{{*/
280 	PHP_WIN32_IOUTIL_INIT_W(path)
281 	int ret, err;
282 
283 	if (!pathw) {
284 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
285 		return -1;
286 	}
287 
288 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
289 
290 	ret = php_win32_ioutil_access_w(pathw, mode);
291 	if (0 > ret) {
292 		err = GetLastError();
293 	}
294 	PHP_WIN32_IOUTIL_CLEANUP_W()
295 
296 	if (0 > ret) {
297 		SET_ERRNO_FROM_WIN32_CODE(err);
298 	}
299 
300 	return ret;
301 }/*}}}*/
302 
php_win32_ioutil_open(const char * path,int flags,...)303 __forceinline static int php_win32_ioutil_open(const char *path, int flags, ...)
304 {/*{{{*/
305 	mode_t mode = 0;
306 	PHP_WIN32_IOUTIL_INIT_W(path)
307 	int ret = -1;
308 	DWORD err;
309 
310 	if (!pathw) {
311 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
312 		return -1;
313 	}
314 
315 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
316 
317 	if (flags & O_CREAT) {
318 		va_list arg;
319 
320 		va_start(arg, flags);
321 		mode = (mode_t) va_arg(arg, int);
322 		va_end(arg);
323 	}
324 
325 	ret = php_win32_ioutil_open_w(pathw, flags, mode);
326 	if (0 > ret) {
327 		err = GetLastError();
328 	}
329 	PHP_WIN32_IOUTIL_CLEANUP_W()
330 
331 	if (0 > ret) {
332 		SET_ERRNO_FROM_WIN32_CODE(err);
333 	}
334 
335 	return ret;
336 }/*}}}*/
337 
php_win32_ioutil_unlink(const char * path)338 __forceinline static int php_win32_ioutil_unlink(const char *path)
339 {/*{{{*/
340 	PHP_WIN32_IOUTIL_INIT_W(path)
341 	int ret = -1;
342 	DWORD err;
343 
344 	if (!pathw) {
345 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
346 		return -1;
347 	}
348 
349 	ret = php_win32_ioutil_unlink_w(pathw);
350 	if (0 > ret) {
351 		err = GetLastError();
352 	}
353 	PHP_WIN32_IOUTIL_CLEANUP_W()
354 
355 	if (0 > ret) {
356 		SET_ERRNO_FROM_WIN32_CODE(err);
357 	}
358 
359 	return ret;
360 }/*}}}*/
361 
php_win32_ioutil_rmdir(const char * path)362 __forceinline static int php_win32_ioutil_rmdir(const char *path)
363 {/*{{{*/
364 	PHP_WIN32_IOUTIL_INIT_W(path)
365 	int ret = 0;
366 	DWORD err = 0;
367 
368 	if (!pathw) {
369 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
370 		return -1;
371 	}
372 
373 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
374 
375 	if (!RemoveDirectoryW(pathw)) {
376 		err = GetLastError();
377 		ret = -1;
378 	}
379 
380 	PHP_WIN32_IOUTIL_CLEANUP_W()
381 
382 	if (0 > ret) {
383 		SET_ERRNO_FROM_WIN32_CODE(err);
384 	}
385 
386 	return ret;
387 }/*}}}*/
388 
php_win32_ioutil_fopen(const char * patha,const char * modea)389 __forceinline static FILE *php_win32_ioutil_fopen(const char *patha, const char *modea)
390 {/*{{{*/
391 	FILE *ret;
392 	wchar_t modew[16] = {0};
393 	int i = 0;
394 
395 	PHP_WIN32_IOUTIL_INIT_W(patha)
396 	if (!pathw) {
397 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
398 		return NULL;
399 	}
400 
401 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, NULL, 1)
402 
403 	while (i < (sizeof(modew)-1)/sizeof(wchar_t) && modea[i]) {
404 		modew[i] = (wchar_t)modea[i];
405 		i++;
406 	}
407 
408 	ret = php_win32_ioutil_fopen_w(pathw, modew);
409 	if (!ret) {
410 		int err = GetLastError();
411 		PHP_WIN32_IOUTIL_CLEANUP_W()
412 		SET_ERRNO_FROM_WIN32_CODE(err);
413 		return NULL;
414 	}
415 
416 	PHP_WIN32_IOUTIL_CLEANUP_W()
417 
418 	return ret;
419 }/*}}}*/
420 
php_win32_ioutil_rename(const char * oldnamea,const char * newnamea)421 __forceinline static int php_win32_ioutil_rename(const char *oldnamea, const char *newnamea)
422 {/*{{{*/
423 	wchar_t *oldnamew;
424 	wchar_t *newnamew;
425 	int ret;
426 	DWORD err = 0;
427 
428 	oldnamew = php_win32_ioutil_any_to_w(oldnamea);
429 	if (!oldnamew) {
430 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
431 		return -1;
432 	}
433 	PHP_WIN32_IOUTIL_CHECK_PATH_W(oldnamew, -1, 1)
434 
435 	newnamew = php_win32_ioutil_any_to_w(newnamea);
436 	if (!newnamew) {
437 		free(oldnamew);
438 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
439 		return -1;
440 	} else {
441 		size_t newnamew_len = wcslen(newnamew);
442 		if (!PHP_WIN32_IOUTIL_PATH_IS_OK_W(newnamew, newnamew_len)) {
443 			free(oldnamew);
444 			free(newnamew);
445 			SET_ERRNO_FROM_WIN32_CODE(ERROR_ACCESS_DENIED);
446 			return -1;
447 		}
448 	}
449 
450 	ret = php_win32_ioutil_rename_w(oldnamew, newnamew);
451 	if (0 > ret) {
452 		err = GetLastError();
453 	}
454 
455 	free(oldnamew);
456 	free(newnamew);
457 
458 	if (0 > ret) {
459 		SET_ERRNO_FROM_WIN32_CODE(err);
460 	}
461 
462 	return ret;
463 }/*}}}*/
464 
php_win32_ioutil_chdir(const char * patha)465 __forceinline static int php_win32_ioutil_chdir(const char *patha)
466 {/*{{{*/
467 	int ret;
468 	wchar_t *pathw = php_win32_ioutil_any_to_w(patha);
469 	DWORD err = 0;
470 
471 	if (!pathw) {
472 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
473 		return -1;
474 	}
475 
476 	ret = php_win32_ioutil_chdir_w(pathw);
477 	if (0 > ret) {
478 		err = GetLastError();
479 	}
480 
481 	free(pathw);
482 
483 	if (0 > ret) {
484 		SET_ERRNO_FROM_WIN32_CODE(err);
485 	}
486 
487 	return ret;
488 }/*}}}*/
489 
php_win32_ioutil_getcwd(char * buf,size_t len)490 __forceinline static char *php_win32_ioutil_getcwd(char *buf, size_t len)
491 {/*{{{*/
492 	wchar_t tmp_bufw[PHP_WIN32_IOUTIL_MAXPATHLEN];
493 	char *tmp_bufa = NULL;
494 	size_t tmp_bufa_len;
495 	DWORD err = 0;
496 
497 	if (php_win32_ioutil_getcwd_w(tmp_bufw, len ? len : PHP_WIN32_IOUTIL_MAXPATHLEN) == NULL) {
498 		err = GetLastError();
499 		SET_ERRNO_FROM_WIN32_CODE(err);
500 		return NULL;
501 	}
502 
503 	tmp_bufa = php_win32_cp_conv_w_to_any(tmp_bufw, wcslen(tmp_bufw), &tmp_bufa_len);
504 	if (!tmp_bufa) {
505 		err = GetLastError();
506 		SET_ERRNO_FROM_WIN32_CODE(err);
507 		return NULL;
508 	} else if (tmp_bufa_len + 1 > PHP_WIN32_IOUTIL_MAXPATHLEN) {
509 		free(tmp_bufa);
510 		SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_LENGTH);
511 		return NULL;
512 	} else if (tmp_bufa_len + 1 > len) {
513 		free(tmp_bufa);
514 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INSUFFICIENT_BUFFER);
515 		return NULL;
516 	}
517 
518 	if (!buf && !len) {
519 		/* If buf was NULL, the result has to be freed outside here. */
520 		buf = tmp_bufa;
521 	} else {
522 		memmove(buf, tmp_bufa, tmp_bufa_len + 1);
523 		free(tmp_bufa);
524 	}
525 
526 	return buf;
527 }/*}}}*/
528 
529 /* TODO improve with usage of native APIs, split for _a and _w. */
php_win32_ioutil_chmod(const char * patha,int mode)530 __forceinline static int php_win32_ioutil_chmod(const char *patha, int mode)
531 {/*{{{*/
532 	wchar_t *pathw = php_win32_ioutil_any_to_w(patha);
533 	int err = 0;
534 	int ret;
535 
536 	if (!pathw) {
537 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
538 		return -1;
539 	}
540 
541 	PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
542 
543 	ret = _wchmod(pathw, mode);
544 	if (0 > ret) {
545 		_get_errno(&err);
546 	}
547 
548 	free(pathw);
549 
550 	if (0 > ret) {
551 		_set_errno(err);
552 	}
553 
554 	return ret;
555 }/*}}}*/
556 
php_win32_ioutil_mkdir(const char * path,mode_t mode)557 __forceinline static int php_win32_ioutil_mkdir(const char *path, mode_t mode)
558 {/*{{{*/
559 	int ret;
560 	DWORD err = 0;
561 
562 	PHP_WIN32_IOUTIL_INIT_W(path)
563 	if (!pathw) {
564 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
565 		return -1;
566 	}
567 
568 	ret = php_win32_ioutil_mkdir_w(pathw, mode);
569 	if (0 > ret) {
570 		err = GetLastError();
571 	}
572 
573 	PHP_WIN32_IOUTIL_CLEANUP_W()
574 
575 	if (0 > ret) {
576 		SET_ERRNO_FROM_WIN32_CODE(err);
577 	}
578 
579 	return ret;
580 }/*}}}*/
581 
php_win32_ioutil_symlink(const char * target,const char * link)582 __forceinline static int php_win32_ioutil_symlink(const char *target, const char *link)
583 {/*{{{*/
584 	wchar_t *targetw, *linkw;
585 	int ret;
586 
587 	targetw = php_win32_ioutil_any_to_w(target);
588 	if (!targetw) {
589 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
590 		return -1;
591 	}
592 
593 	linkw = php_win32_ioutil_any_to_w(link);
594 	if (!linkw) {
595 		free(targetw);
596 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
597 		return -1;
598 	}
599 
600 	ret = php_win32_ioutil_symlink_w(targetw, linkw);
601 
602 	free(targetw);
603 	free(linkw);
604 
605 	return ret;
606 }/*}}}*/
607 
php_win32_ioutil_link(const char * target,const char * link)608 __forceinline static int php_win32_ioutil_link(const char *target, const char *link)
609 {/*{{{*/
610 	wchar_t *targetw, *linkw;
611 	int ret;
612 
613 	targetw = php_win32_ioutil_any_to_w(target);
614 	if (!targetw) {
615 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
616 		return -1;
617 	}
618 	linkw = php_win32_ioutil_any_to_w(link);
619 	if (!linkw) {
620 		free(targetw);
621 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
622 		return -1;
623 	}
624 
625 	ret = php_win32_ioutil_link_w(targetw, linkw);
626 
627 	free(targetw);
628 	free(linkw);
629 
630 	return ret;
631 }/*}}}*/
632 
633 PW32IO char *realpath(const char *path, char *resolved);
634 
php_win32_ioutil_realpath_ex0(const char * path,char * resolved,PBY_HANDLE_FILE_INFORMATION info)635 __forceinline static char *php_win32_ioutil_realpath_ex0(const char *path, char *resolved, PBY_HANDLE_FILE_INFORMATION info)
636 {/*{{{*/
637 	wchar_t retw[PHP_WIN32_IOUTIL_MAXPATHLEN];
638 	char *reta;
639 	size_t reta_len;
640 
641 	PHP_WIN32_IOUTIL_INIT_W(path)
642 	if (!pathw) {
643 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
644 		return NULL;
645 	}
646 
647 	if (NULL == php_win32_ioutil_realpath_w_ex0(pathw, retw, info)) {
648 		DWORD err = GetLastError();
649 		PHP_WIN32_IOUTIL_CLEANUP_W()
650 		SET_ERRNO_FROM_WIN32_CODE(err);
651 		return NULL;
652 	}
653 
654 	reta = php_win32_cp_conv_w_to_any(retw, PHP_WIN32_CP_IGNORE_LEN, &reta_len);
655 	if (!reta || reta_len > PHP_WIN32_IOUTIL_MAXPATHLEN) {
656 		DWORD err = GetLastError();
657 		PHP_WIN32_IOUTIL_CLEANUP_W()
658 		SET_ERRNO_FROM_WIN32_CODE(err);
659 		return NULL;
660 	}
661 
662 	if (NULL == resolved) {
663 		/* ret is expected to be either NULL or a buffer of capable size. */
664 		resolved = (char *) malloc(reta_len + 1);
665 		if (!resolved) {
666 			free(reta);
667 			PHP_WIN32_IOUTIL_CLEANUP_W()
668 			SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
669 			return NULL;
670 		}
671 	}
672 	memmove(resolved, reta, reta_len+1);
673 
674 	PHP_WIN32_IOUTIL_CLEANUP_W()
675 	free(reta);
676 
677 	return resolved;
678 }/*}}}*/
679 
php_win32_ioutil_realpath(const char * path,char * resolved)680 __forceinline static char *php_win32_ioutil_realpath(const char *path, char *resolved)
681 {/*{{{*/
682 	return php_win32_ioutil_realpath_ex0(path, resolved, NULL);
683 }/*}}}*/
684 
685 #include <sys/stat.h>
686 #if _WIN64
687 typedef unsigned __int64 php_win32_ioutil_dev_t;
688 typedef unsigned __int64 php_win32_ioutil_ino_t;
689 typedef __time64_t php_win32_ioutil_time_t;
690 typedef __int64 php_win32_ioutil_size_t;
691 #else
692 typedef unsigned __int32 php_win32_ioutil_dev_t;
693 typedef unsigned __int32 php_win32_ioutil_ino_t;
694 typedef __time32_t php_win32_ioutil_time_t;
695 typedef __int32 php_win32_ioutil_size_t;
696 #endif
697 typedef struct {
698 	php_win32_ioutil_dev_t st_dev;
699 	php_win32_ioutil_ino_t st_ino;
700 	unsigned __int32 st_mode;
701 	unsigned __int32 st_nlink;
702 	unsigned short st_uid;
703 	unsigned short st_gid;
704 	php_win32_ioutil_dev_t st_rdev;
705 	php_win32_ioutil_size_t st_size;
706 #if 0
707 	__int32 st_blksize;
708 	__int32 st_blocks;
709 #endif
710 	php_win32_ioutil_time_t st_atime;
711 	php_win32_ioutil_time_t st_mtime;
712 	php_win32_ioutil_time_t st_ctime;
713 } php_win32_ioutil_stat_t;
714 
715 typedef struct {
716 	unsigned long  ReparseTag;
717 	unsigned short ReparseDataLength;
718 	unsigned short Reserved;
719 	union {
720 		struct {
721 			unsigned short SubstituteNameOffset;
722 			unsigned short SubstituteNameLength;
723 			unsigned short PrintNameOffset;
724 			unsigned short PrintNameLength;
725 			unsigned long  Flags;
726 			wchar_t        ReparseTarget[1];
727 		} SymbolicLinkReparseBuffer;
728 		struct {
729 			unsigned short SubstituteNameOffset;
730 			unsigned short SubstituteNameLength;
731 			unsigned short PrintNameOffset;
732 			unsigned short PrintNameLength;
733 			wchar_t        ReparseTarget[1];
734 		} MountPointReparseBuffer;
735 		struct {
736 			unsigned char  ReparseTarget[1];
737 		} GenericReparseBuffer;
738 	};
739 } PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER, *PHP_WIN32_IOUTIL_PREPARSE_DATA_BUFFER;
740 
741 PW32IO int php_win32_ioutil_stat_ex_w(const wchar_t *path, size_t path_len, php_win32_ioutil_stat_t *buf, int lstat);
742 PW32IO int php_win32_ioutil_fstat(int fd, php_win32_ioutil_stat_t *buf);
743 
php_win32_ioutil_stat_ex(const char * path,php_win32_ioutil_stat_t * buf,int lstat)744 __forceinline static int php_win32_ioutil_stat_ex(const char *path, php_win32_ioutil_stat_t *buf, int lstat)
745 {/*{{{*/
746 	size_t pathw_len;
747 	wchar_t *pathw = php_win32_ioutil_conv_any_to_w(path, PHP_WIN32_CP_IGNORE_LEN, &pathw_len);
748 	int ret;
749 
750 	if (!pathw) {
751 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
752 		return -1;
753 	}
754 
755 	ret = php_win32_ioutil_stat_ex_w(pathw, pathw_len, buf, lstat);
756 
757 	free(pathw);
758 
759 	return ret;
760 }/*}}}*/
761 #define php_win32_ioutil_stat(path, buf) php_win32_ioutil_stat_ex(path, buf, 0)
762 #define php_win32_ioutil_lstat(path, buf) php_win32_ioutil_stat_ex(path, buf, 1)
763 
764 PW32IO ssize_t php_win32_ioutil_readlink_w(const wchar_t *path, wchar_t *buf, size_t buf_len);
765 
php_win32_ioutil_readlink(const char * path,char * buf,size_t buf_len)766 __forceinline static ssize_t php_win32_ioutil_readlink(const char *path, char *buf, size_t buf_len)
767 {/*{{{*/
768 	size_t pathw_len, ret_buf_len;
769 	wchar_t *pathw = php_win32_ioutil_conv_any_to_w(path, PHP_WIN32_CP_IGNORE_LEN, &pathw_len);
770 	wchar_t retw[PHP_WIN32_IOUTIL_MAXPATHLEN];
771 	char *ret_buf;
772 	ssize_t ret;
773 
774 	if (!pathw) {
775 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
776 		return -1;
777 	}
778 
779 	ret = php_win32_ioutil_readlink_w(pathw, retw, sizeof(retw)-1);
780 	if (ret < 0) {
781 		DWORD _err = GetLastError();
782 		free(pathw);
783 		SET_ERRNO_FROM_WIN32_CODE(_err);
784 		return ret;
785 	}
786 
787 	ret_buf = php_win32_ioutil_conv_w_to_any(retw, ret, &ret_buf_len);
788 	if (!ret_buf || ret_buf_len >= buf_len || ret_buf_len >= MAXPATHLEN) {
789 		free(ret_buf);
790 		free(pathw);
791 		SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_PATHNAME);
792 		return -1;
793 	}
794 	memcpy(buf, ret_buf, ret_buf_len + 1);
795 
796 	free(ret_buf);
797 	free(pathw);
798 
799 	return ret_buf_len;
800 }/*}}}*/
801 
802 #ifdef __cplusplus
803 }
804 #endif
805 
806 #endif /* PHP_WIN32_IOUTIL_H */
807