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