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