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 typedef struct {
78 DWORD access;
79 DWORD share;
80 DWORD disposition;
81 DWORD attributes;
82 } php_ioutil_open_opts;
83
84 typedef enum {
85 PHP_WIN32_IOUTIL_IS_ASCII,
86 PHP_WIN32_IOUTIL_IS_ANSI,
87 PHP_WIN32_IOUTIL_IS_UTF8
88 } php_win32_ioutil_encoding;
89
90 typedef enum {
91 PHP_WIN32_IOUTIL_NORM_OK,
92 PHP_WIN32_IOUTIL_NORM_PARTIAL,
93 PHP_WIN32_IOUTIL_NORM_FAIL,
94 } php_win32_ioutil_normalization_result;
95
96 #define PHP_WIN32_IOUTIL_FW_SLASHW L'/'
97 #define PHP_WIN32_IOUTIL_FW_SLASH '/'
98 #define PHP_WIN32_IOUTIL_BW_SLASHW L'\\'
99 #define PHP_WIN32_IOUTIL_BW_SLASH '\\'
100 #define PHP_WIN32_IOUTIL_DEFAULT_SLASHW PHP_WIN32_IOUTIL_BW_SLASHW
101 #define PHP_WIN32_IOUTIL_DEFAULT_SLASH PHP_WIN32_IOUTIL_BW_SLASH
102
103 #define PHP_WIN32_IOUTIL_DEFAULT_DIR_SEPARATORW L';'
104 #define PHP_WIN32_IOUTIL_IS_SLASHW(c) ((c) == PHP_WIN32_IOUTIL_BW_SLASHW || (c) == PHP_WIN32_IOUTIL_FW_SLASHW)
105 #define PHP_WIN32_IOUTIL_IS_LETTERW(c) (((c) >= L'a' && (c) <= L'z') || ((c) >= L'A' && (c) <= L'Z'))
106 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIXW L"\\??\\"
107 #define PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW 4
108 #define PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW L"\\\\?\\"
109 #define PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW 4
110 #define PHP_WIN32_IOUTIL_UNC_PATH_PREFIXW L"\\\\?\\UNC\\"
111 #define PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW 8
112
113 #define PHP_WIN32_IOUTIL_IS_LONG_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW \
114 && 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW))
115 #define PHP_WIN32_IOUTIL_IS_UNC_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW \
116 && 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_UNC_PATH_PREFIXW, PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW))
117 #define PHP_WIN32_IOUTIL_IS_JUNCTION_PATHW(pathw, path_lenw) (path_lenw >= PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW \
118 && 0 == wcsncmp((pathw), PHP_WIN32_IOUTIL_JUNCTION_PREFIXW, PHP_WIN32_IOUTIL_JUNCTION_PREFIX_LENW))
119 #define PHP_WIN32_IOUTIL_IS_ABSOLUTEW(pathw, path_lenw) (PHP_WIN32_IOUTIL_IS_LONG_PATHW(pathw, path_lenw) \
120 || path_lenw >= 3 && PHP_WIN32_IOUTIL_IS_LETTERW(pathw[0]) && L':' == pathw[1] && PHP_WIN32_IOUTIL_IS_SLASHW(pathw[2]))
121
122 #define PHP_WIN32_IOUTIL_INIT_W(path) \
123 wchar_t *pathw = php_win32_ioutil_any_to_w(path); \
124
125 #define PHP_WIN32_IOUTIL_CLEANUP_W() do { \
126 free(pathw); \
127 pathw = NULL; \
128 } while (0);
129
130 #define PHP_WIN32_IOUTIL_REINIT_W(path) do { \
131 PHP_WIN32_IOUTIL_CLEANUP_W() \
132 pathw = php_win32_ioutil_any_to_w(path); \
133 } while (0);
134
135 #define PHP_WIN32_IOUTIL_PATH_IS_OK_W(pathw, len) \
136 (!((len) >= 1 && L' ' == pathw[(len)-1] || \
137 (len) > 1 && !PHP_WIN32_IOUTIL_IS_SLASHW(pathw[(len)-2]) && L'.' != pathw[(len)-2] && L'.' == pathw[(len)-1]))
138
139 #define PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, ret, dealloc) do { \
140 if (!PHP_WIN32_IOUTIL_PATH_IS_OK_W(pathw, wcslen(pathw))) { \
141 if (dealloc) { \
142 free(pathw); \
143 } \
144 SET_ERRNO_FROM_WIN32_CODE(ERROR_ACCESS_DENIED); \
145 return ret; \
146 } \
147 } while (0);
148
149 PW32IO php_win32_ioutil_normalization_result php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len);
150 #ifdef PHP_EXPORTS
151 /* This symbols are needed only for the DllMain, but should not be exported
152 or be available when used with PHP binaries. */
153 BOOL php_win32_ioutil_init(void);
154 #endif
155
156 /* Keep these functions aliased for case some additional handling
157 is needed later. */
php_win32_ioutil_conv_any_to_w(const char * in,size_t in_len,size_t * out_len)158 __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, size_t in_len, size_t *out_len)
159 {/*{{{*/
160 wchar_t *mb, *ret;
161 size_t mb_len;
162
163 mb = php_win32_cp_conv_any_to_w(in, in_len, &mb_len);
164 if (!mb) {
165 return NULL;
166 }
167
168 /* Only prefix with long if it's needed. */
169 if (mb_len >= _MAX_PATH) {
170 size_t new_mb_len;
171
172 ret = (wchar_t *) malloc((mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t));
173 if (!ret) {
174 free(mb);
175 return NULL;
176 }
177
178 if (PHP_WIN32_IOUTIL_NORM_FAIL == php_win32_ioutil_normalize_path_w(&mb, mb_len, &new_mb_len)) {
179 free(ret);
180 free(mb);
181 return NULL;
182 }
183
184 if (new_mb_len > mb_len) {
185 wchar_t *tmp = (wchar_t *) realloc(ret, (new_mb_len + 1) * sizeof(wchar_t));
186 if (!tmp) {
187 free(ret);
188 free(mb);
189 return NULL;
190 }
191 ret = tmp;
192 mb_len = new_mb_len;
193 }
194
195 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)) {
196 memmove(ret, mb, mb_len * sizeof(wchar_t));
197 ret[mb_len] = L'\0';
198 } else {
199 memmove(ret, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t));
200 memmove(ret+PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, mb, mb_len * sizeof(wchar_t));
201 ret[mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW] = L'\0';
202
203 mb_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
204 }
205
206 free(mb);
207 } else {
208 ret = mb;
209 }
210
211 if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) {
212 *out_len = mb_len;
213 }
214
215 return ret;
216 }/*}}}*/
217 #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)
218
219 #define php_win32_ioutil_ascii_to_w php_win32_cp_ascii_to_w
220 #define php_win32_ioutil_utf8_to_w php_win32_cp_utf8_to_w
221 #define php_win32_ioutil_cur_to_w php_win32_cp_cur_to_w
222 #define php_win32_ioutil_w_to_any php_win32_cp_w_to_any
223 #define php_win32_ioutil_conv_w_to_any php_win32_cp_conv_w_to_any
224 /*__forceinline static char *php_win32_ioutil_w_to_any(wchar_t* w_source_ptr)
225 {
226 return php_win32_cp_w_to_any(w_source_ptr);
227 }*/
228 #define php_win32_ioutil_w_to_utf8 php_win32_cp_w_to_utf8
229 #define php_win32_ioutil_w_to_thread php_win32_cp_w_to_thread
230
231 PW32IO int php_win32_ioutil_close(int fd);
232 PW32IO BOOL php_win32_ioutil_posix_to_open_opts(int flags, mode_t mode, php_ioutil_open_opts *opts);
233 PW32IO int php_win32_ioutil_mkdir(const char *path, mode_t mode);
234 PW32IO size_t php_win32_ioutil_dirname(char *buf, size_t len);
235
236 PW32IO int php_win32_ioutil_open_w(const wchar_t *path, int flags, ...);
237 PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path);
238 PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname);
239 PW32IO wchar_t *php_win32_ioutil_getcwd_w(const wchar_t *buf, int len);
240
241 #if 0
242 PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode);
243 PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode);
244 #endif
245
246 #define php_win32_ioutil_access_cond(path, mode) _waccess(pathw, mode)
247 #define php_win32_ioutil_unlink_cond(path) php_win32_ioutil_unlink_w(pathw)
248 #define php_win32_ioutil_rmdir_cond(path) php_win32_ioutil_rmdir_w(pathw)
249
php_win32_ioutil_access(const char * path,mode_t mode)250 __forceinline static int php_win32_ioutil_access(const char *path, mode_t mode)
251 {/*{{{*/
252 PHP_WIN32_IOUTIL_INIT_W(path)
253 int ret, err;
254
255 if (!pathw) {
256 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
257 return -1;
258 }
259
260 PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
261
262 ret = _waccess(pathw, mode);
263 if (0 > ret) {
264 _get_errno(&err);
265 }
266 PHP_WIN32_IOUTIL_CLEANUP_W()
267
268 if (0 > ret) {
269 _set_errno(err);
270 }
271
272 return ret;
273 }/*}}}*/
274
php_win32_ioutil_open(const char * path,int flags,...)275 __forceinline static int php_win32_ioutil_open(const char *path, int flags, ...)
276 {/*{{{*/
277 mode_t mode = 0;
278 PHP_WIN32_IOUTIL_INIT_W(path)
279 int ret = -1;
280 DWORD err;
281
282 if (!pathw) {
283 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
284 return -1;
285 }
286
287 PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
288
289 if (flags & O_CREAT) {
290 va_list arg;
291
292 va_start(arg, flags);
293 mode = (mode_t) va_arg(arg, int);
294 va_end(arg);
295 }
296
297 ret = php_win32_ioutil_open_w(pathw, flags, mode);
298 if (0 > ret) {
299 err = GetLastError();
300 }
301 PHP_WIN32_IOUTIL_CLEANUP_W()
302
303 if (0 > ret) {
304 SET_ERRNO_FROM_WIN32_CODE(err);
305 }
306
307 return ret;
308 }/*}}}*/
309
php_win32_ioutil_unlink(const char * path)310 __forceinline static int php_win32_ioutil_unlink(const char *path)
311 {/*{{{*/
312 PHP_WIN32_IOUTIL_INIT_W(path)
313 int ret = 0;
314 DWORD err = 0;
315
316 if (!pathw) {
317 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
318 return -1;
319 }
320
321 PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
322
323 if (!DeleteFileW(pathw)) {
324 err = GetLastError();
325 ret = -1;
326 }
327 PHP_WIN32_IOUTIL_CLEANUP_W()
328
329 if (0 > ret) {
330 SET_ERRNO_FROM_WIN32_CODE(err);
331 }
332
333 return ret;
334 }/*}}}*/
335
php_win32_ioutil_rmdir(const char * path)336 __forceinline static int php_win32_ioutil_rmdir(const char *path)
337 {/*{{{*/
338 PHP_WIN32_IOUTIL_INIT_W(path)
339 int ret = 0;
340 DWORD err = 0;
341
342 if (!pathw) {
343 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
344 return -1;
345 }
346
347 PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
348
349 if (!RemoveDirectoryW(pathw)) {
350 err = GetLastError();
351 ret = -1;
352 }
353
354 PHP_WIN32_IOUTIL_CLEANUP_W()
355
356 if (0 > ret) {
357 SET_ERRNO_FROM_WIN32_CODE(err);
358 }
359
360 return ret;
361 }/*}}}*/
362
363 /* This needs to be improved once long path support is implemented. Use ioutil_open() and then
364 fdopen() might be the way, if we learn how to convert the mode options (maybe grab the routine
365 from the streams). That will allow to split for _a and _w. */
php_win32_ioutil_fopen(const char * patha,const char * modea)366 __forceinline static FILE *php_win32_ioutil_fopen(const char *patha, const char *modea)
367 {/*{{{*/
368 FILE *ret;
369 wchar_t *pathw;
370 wchar_t *modew;
371 int err = 0;
372
373 pathw = php_win32_ioutil_any_to_w(patha);
374 if (!pathw) {
375 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
376 return NULL;
377 }
378
379 PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, NULL, 1)
380
381 modew = php_win32_ioutil_ascii_to_w(modea);
382 if (!modew) {
383 free(pathw);
384 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
385 return NULL;
386 }
387
388 ret = _wfopen(pathw, modew);
389 if (!ret) {
390 _get_errno(&err);
391 }
392 free(pathw);
393 free(modew);
394
395 if (!ret) {
396 _set_errno(err);
397 }
398 return ret;
399 }/*}}}*/
400
php_win32_ioutil_rename(const char * oldnamea,const char * newnamea)401 __forceinline static int php_win32_ioutil_rename(const char *oldnamea, const char *newnamea)
402 {/*{{{*/
403 wchar_t *oldnamew;
404 wchar_t *newnamew;
405 int ret;
406 DWORD err = 0;
407
408 oldnamew = php_win32_ioutil_any_to_w(oldnamea);
409 if (!oldnamew) {
410 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
411 return -1;
412 }
413 PHP_WIN32_IOUTIL_CHECK_PATH_W(oldnamew, -1, 1)
414
415 newnamew = php_win32_ioutil_any_to_w(newnamea);
416 if (!newnamew) {
417 free(oldnamew);
418 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
419 return -1;
420 } else if (!PHP_WIN32_IOUTIL_PATH_IS_OK_W(newnamew, wcslen(newnamew))) {
421 free(oldnamew);
422 free(newnamew);
423 SET_ERRNO_FROM_WIN32_CODE(ERROR_ACCESS_DENIED);
424 return -1;
425 }
426
427 ret = php_win32_ioutil_rename_w(oldnamew, newnamew);
428 if (0 > ret) {
429 err = GetLastError();
430 }
431
432 free(oldnamew);
433 free(newnamew);
434
435 if (0 > ret) {
436 SET_ERRNO_FROM_WIN32_CODE(err);
437 }
438
439 return ret;
440 }/*}}}*/
441
php_win32_ioutil_chdir(const char * patha)442 __forceinline static int php_win32_ioutil_chdir(const char *patha)
443 {/*{{{*/
444 int ret;
445 wchar_t *pathw = php_win32_ioutil_any_to_w(patha);
446 DWORD err = 0;
447
448 if (!pathw) {
449 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
450 return -1;
451 }
452
453 ret = php_win32_ioutil_chdir_w(pathw);
454 if (0 > ret) {
455 err = GetLastError();
456 }
457
458 free(pathw);
459
460 if (0 > ret) {
461 SET_ERRNO_FROM_WIN32_CODE(err);
462 }
463
464 return ret;
465 }/*}}}*/
466
php_win32_ioutil_getcwd(char * buf,int len)467 __forceinline static char *php_win32_ioutil_getcwd(char *buf, int len)
468 {/*{{{*/
469 wchar_t tmp_bufw[PHP_WIN32_IOUTIL_MAXPATHLEN];
470 char *tmp_bufa = NULL;
471 size_t tmp_bufa_len;
472 DWORD err = 0;
473
474 if (php_win32_ioutil_getcwd_w(tmp_bufw, PHP_WIN32_IOUTIL_MAXPATHLEN) == NULL) {
475 err = GetLastError();
476 SET_ERRNO_FROM_WIN32_CODE(err);
477 return NULL;
478 }
479
480 tmp_bufa = php_win32_cp_conv_w_to_any(tmp_bufw, wcslen(tmp_bufw), &tmp_bufa_len);
481 if (!tmp_bufa) {
482 err = GetLastError();
483 SET_ERRNO_FROM_WIN32_CODE(err);
484 return NULL;
485 } else if (tmp_bufa_len + 1 > PHP_WIN32_IOUTIL_MAXPATHLEN) {
486 free(tmp_bufa);
487 SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_LENGTH);
488 return NULL;
489 }
490
491 if (!buf) {
492 /* If buf was NULL, the result has to be freed outside here. */
493 buf = tmp_bufa;
494 } else {
495 if (tmp_bufa_len + 1 > len) {
496 free(tmp_bufa);
497 SET_ERRNO_FROM_WIN32_CODE(ERROR_INSUFFICIENT_BUFFER);
498 return NULL;
499 }
500 memmove(buf, tmp_bufa, tmp_bufa_len + 1);
501 free(tmp_bufa);
502 }
503
504 return buf;
505 }/*}}}*/
506
507 /* TODO improve with usage of native APIs, split for _a and _w. */
php_win32_ioutil_chmod(const char * patha,int mode)508 __forceinline static int php_win32_ioutil_chmod(const char *patha, int mode)
509 {/*{{{*/
510 wchar_t *pathw = php_win32_ioutil_any_to_w(patha);
511 int err = 0;
512 int ret;
513
514 if (!pathw) {
515 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
516 return -1;
517 }
518
519 PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
520
521 ret = _wchmod(pathw, mode);
522 if (0 > ret) {
523 _get_errno(&err);
524 }
525
526 free(pathw);
527
528 if (0 > ret) {
529 _set_errno(err);
530 }
531
532 return ret;
533 }/*}}}*/
534
535 #ifdef __cplusplus
536 }
537 #endif
538
539 #endif /* PHP_WIN32_IOUTIL_H */
540
541 /*
542 * Local variables:
543 * tab-width: 4
544 * c-basic-offset: 4
545 * End:
546 * vim600: sw=4 ts=4 fdm=marker
547 * vim<600: sw=4 ts=4
548 */
549