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 #include <assert.h>
44 #include <stdlib.h>
45 #include <direct.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <io.h>
49 #include <limits.h>
50 #include <sys/stat.h>
51 #include <sys/utime.h>
52 #include <stdio.h>
53
54 #include "php.h"
55 #include "SAPI.h"
56 #include "win32/winutil.h"
57 #include "win32/time.h"
58 #include "win32/ioutil.h"
59 #include "win32/codepage.h"
60
61 #include <pathcch.h>
62
63 /*
64 #undef NONLS
65 #undef _WINNLS_
66 #include <winnls.h>
67 */
68
69 typedef HRESULT (__stdcall *MyPathCchCanonicalizeEx)(wchar_t *pszPathOut, size_t cchPathOut, const wchar_t *pszPathIn, unsigned long dwFlags);
70
71 static MyPathCchCanonicalizeEx canonicalize_path_w = NULL;
72
php_win32_ioutil_posix_to_open_opts(int flags,mode_t mode,php_ioutil_open_opts * opts)73 PW32IO BOOL php_win32_ioutil_posix_to_open_opts(int flags, mode_t mode, php_ioutil_open_opts *opts)
74 {/*{{{*/
75 int current_umask;
76
77 opts->attributes = 0;
78
79 /* Obtain the active umask. umask() never fails and returns the previous */
80 /* umask. */
81 current_umask = umask(0);
82 umask(current_umask);
83
84 /* convert flags and mode to CreateFile parameters */
85 switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
86 case _O_RDONLY:
87 opts->access = FILE_GENERIC_READ;
88 /* XXX not opening dirs yet, see also at the bottom of this function. Should be evaluated properly. */
89 /*opts->attributes |= FILE_FLAG_BACKUP_SEMANTICS;*/
90 break;
91 case _O_WRONLY:
92 opts->access = FILE_GENERIC_WRITE;
93 break;
94 case _O_RDWR:
95 opts->access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
96 break;
97 default:
98 goto einval;
99 }
100
101 if (flags & _O_APPEND) {
102 /* XXX this might look wrong, but i just leave it here. Disabling FILE_WRITE_DATA prevents the current truncate behaviors for files opened with "a". */
103 /* access &= ~FILE_WRITE_DATA;*/
104 opts->access |= FILE_APPEND_DATA;
105 opts->attributes &= ~FILE_FLAG_BACKUP_SEMANTICS;
106 }
107
108 /*
109 * Here is where we deviate significantly from what CRT's _open()
110 * does. We indiscriminately use all the sharing modes, to match
111 * UNIX semantics. In particular, this ensures that the file can
112 * be deleted even whilst it's open.
113 */
114 /* opts->share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; */
115 /* XXX No UINX behavior Good to know it's doable.
116 Not being done as this means a behavior change. Should be evaluated properly. */
117 opts->share = FILE_SHARE_READ | FILE_SHARE_WRITE;
118
119 switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
120 case 0:
121 case _O_EXCL:
122 opts->disposition = OPEN_EXISTING;
123 break;
124 case _O_CREAT:
125 opts->disposition = OPEN_ALWAYS;
126 break;
127 case _O_CREAT | _O_EXCL:
128 case _O_CREAT | _O_TRUNC | _O_EXCL:
129 opts->disposition = CREATE_NEW;
130 break;
131 case _O_TRUNC:
132 case _O_TRUNC | _O_EXCL:
133 opts->disposition = TRUNCATE_EXISTING;
134 break;
135 case _O_CREAT | _O_TRUNC:
136 opts->disposition = CREATE_ALWAYS;
137 break;
138 default:
139 goto einval;
140 }
141
142 opts->attributes |= FILE_ATTRIBUTE_NORMAL;
143 if (flags & _O_CREAT) {
144 if (!((mode & ~current_umask) & _S_IWRITE)) {
145 opts->attributes |= FILE_ATTRIBUTE_READONLY;
146 }
147 }
148
149 if (flags & _O_TEMPORARY ) {
150 opts->attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
151 opts->access |= DELETE;
152 }
153
154 if (flags & _O_SHORT_LIVED) {
155 opts->attributes |= FILE_ATTRIBUTE_TEMPORARY;
156 }
157
158 switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) {
159 case 0:
160 break;
161 case _O_SEQUENTIAL:
162 opts->attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
163 break;
164 case _O_RANDOM:
165 opts->attributes |= FILE_FLAG_RANDOM_ACCESS;
166 break;
167 default:
168 goto einval;
169 }
170
171 /* Very compat options */
172 /*if (flags & O_ASYNC) {
173 opts->attributes |= FILE_FLAG_OVERLAPPED;
174 } else if (flags & O_SYNC) {
175 opts->attributes &= ~FILE_FLAG_OVERLAPPED;
176 }*/
177
178 /* Setting this flag makes it possible to open a directory. */
179 /* XXX not being done as this means a behavior change. Should be evaluated properly. */
180 /* opts->attributes |= FILE_FLAG_BACKUP_SEMANTICS; */
181
182 return 1;
183
184 einval:
185 _set_errno(EINVAL);
186 return 0;
187 }/*}}}*/
188
php_win32_ioutil_open_w(const wchar_t * path,int flags,...)189 PW32IO int php_win32_ioutil_open_w(const wchar_t *path, int flags, ...)
190 {/*{{{*/
191 php_ioutil_open_opts open_opts;
192 HANDLE file;
193 int fd;
194 mode_t mode = 0;
195
196 PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
197
198 if (flags & O_CREAT) {
199 va_list arg;
200
201 va_start(arg, flags);
202 mode = (mode_t) va_arg(arg, int);
203 va_end(arg);
204 }
205
206 if (!php_win32_ioutil_posix_to_open_opts(flags, mode, &open_opts)) {
207 goto einval;
208 }
209
210 /* XXX care about security attributes here if needed, see tsrm_win32_access() */
211 file = CreateFileW(path,
212 open_opts.access,
213 open_opts.share,
214 NULL,
215 open_opts.disposition,
216 open_opts.attributes,
217 NULL);
218
219 if (file == INVALID_HANDLE_VALUE) {
220 DWORD error = GetLastError();
221
222 if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) &&
223 !(flags & _O_EXCL)) {
224 /* Special case: when ERROR_FILE_EXISTS happens and O_CREAT was */
225 /* specified, it means the path referred to a directory. */
226 _set_errno(EISDIR);
227 } else {
228 SET_ERRNO_FROM_WIN32_CODE(error);
229 }
230 return -1;
231 }
232
233 fd = _open_osfhandle((intptr_t) file, flags);
234 if (fd < 0) {
235 DWORD error = GetLastError();
236
237 /* The only known failure mode for _open_osfhandle() is EMFILE, in which
238 * case GetLastError() will return zero. However we'll try to handle other
239 * errors as well, should they ever occur.
240 */
241 if (errno == EMFILE) {
242 _set_errno(EMFILE);
243 } else if (error != ERROR_SUCCESS) {
244 SET_ERRNO_FROM_WIN32_CODE(error);
245 }
246 CloseHandle(file);
247 return -1;
248 }
249
250 if (flags & _O_TEXT) {
251 _setmode(fd, _O_TEXT);
252 } else if (flags & _O_BINARY) {
253 _setmode(fd, _O_BINARY);
254 }
255
256 return fd;
257
258 einval:
259 _set_errno(EINVAL);
260 return -1;
261 }/*}}}*/
262
php_win32_ioutil_close(int fd)263 PW32IO int php_win32_ioutil_close(int fd)
264 {/*{{{*/
265 int result = -1;
266
267 if (-1 == fd) {
268 _set_errno(EBADF);
269 return result;
270 }
271
272 if (fd > 2) {
273 result = _close(fd);
274 } else {
275 result = 0;
276 }
277
278 /* _close doesn't set _doserrno on failure, but it does always set errno
279 * to EBADF on failure.
280 */
281 if (result == -1) {
282 _set_errno(EBADF);
283 }
284
285 return result;
286 }/*}}}*/
287
288 #if 0
289 PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode)
290 {/*{{{*/
291 int ret = 0;
292 DWORD err = 0;
293
294 PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
295
296 /* TODO extend with mode usage */
297 if (!CreateDirectoryW(path, NULL)) {
298 err = GetLastError();
299 ret = -1;
300 SET_ERRNO_FROM_WIN32_CODE(err);
301 }
302
303 return ret;
304 }/*}}}*/
305 #endif
306
php_win32_ioutil_mkdir(const char * path,mode_t mode)307 PW32IO int php_win32_ioutil_mkdir(const char *path, mode_t mode)
308 {/*{{{*/
309 size_t pathw_len = 0;
310 wchar_t *pathw = php_win32_ioutil_conv_any_to_w(path, 0, &pathw_len);
311 int ret = 0;
312 DWORD err = 0;
313
314 if (pathw_len < _MAX_PATH && pathw_len >= _MAX_PATH - 12) {
315 /* Special case here. From the doc:
316
317 "When using an API to create a directory, the specified path cannot be
318 so long that you cannot append an 8.3 file name ..."
319
320 Thus, if the directory name length happens to be in this range, it
321 already needs to be a long path. The given path is already normalized
322 and prepared, need only to prefix it.
323 */
324 wchar_t *tmp = (wchar_t *) malloc((pathw_len + 1) * sizeof(wchar_t));
325 if (!tmp) {
326 free(pathw);
327 SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
328 return -1;
329 }
330 memmove(tmp, pathw, (pathw_len + 1) * sizeof(wchar_t));
331
332 if (PHP_WIN32_IOUTIL_NORM_FAIL == php_win32_ioutil_normalize_path_w(&tmp, pathw_len, &pathw_len)) {
333 free(tmp);
334 return -1;
335 }
336
337 free(pathw);
338 pathw = tmp;
339
340 if (!PHP_WIN32_IOUTIL_IS_LONG_PATHW(tmp, pathw_len)) {
341 wchar_t *_tmp = (wchar_t *) malloc((pathw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t));
342 if (!_tmp) {
343 SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
344 free(tmp);
345 return -1;
346 }
347 memmove(_tmp, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t));
348 memmove(_tmp+PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, tmp, pathw_len * sizeof(wchar_t));
349 pathw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
350 _tmp[pathw_len] = L'\0';
351 free(tmp);
352 tmp = _tmp;
353 }
354
355 pathw = tmp;
356
357 } else {
358 pathw = pathw;
359 }
360
361 /* TODO extend with mode usage */
362 if (!pathw) {
363 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
364 return -1;
365 }
366
367 PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, -1, 1)
368
369 if (!CreateDirectoryW(pathw, NULL)) {
370 err = GetLastError();
371 ret = -1;
372 }
373 free(pathw);
374
375 if (0 > ret) {
376 SET_ERRNO_FROM_WIN32_CODE(err);
377 }
378
379 return ret;
380 }/*}}}*/
381
php_win32_ioutil_unlink_w(const wchar_t * path)382 PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path)
383 {/*{{{*/
384 int ret = 0;
385 DWORD err = 0;
386
387 PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
388
389 if (!DeleteFileW(path)) {
390 err = GetLastError();
391 ret = -1;
392 SET_ERRNO_FROM_WIN32_CODE(err);
393 }
394
395 return ret;
396 }/*}}}*/
397
php_win32_ioutil_rmdir_w(const wchar_t * path)398 PW32IO int php_win32_ioutil_rmdir_w(const wchar_t *path)
399 {/*{{{*/
400 int ret = 0;
401 DWORD err = 0;
402
403 PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0)
404
405 if (!RemoveDirectoryW(path)) {
406 err = GetLastError();
407 ret = -1;
408 SET_ERRNO_FROM_WIN32_CODE(err);
409 }
410
411 return ret;
412 }/*}}}*/
413
php_win32_ioutil_chdir_w(const wchar_t * path)414 PW32IO int php_win32_ioutil_chdir_w(const wchar_t *path)
415 {/*{{{*/
416 int ret = 0;
417 DWORD err = 0;
418
419 if (!SetCurrentDirectoryW(path)) {
420 err = GetLastError();
421 ret = -1;
422 SET_ERRNO_FROM_WIN32_CODE(err);
423 }
424
425 return ret;
426 }/*}}}*/
427
php_win32_ioutil_rename_w(const wchar_t * oldname,const wchar_t * newname)428 PW32IO int php_win32_ioutil_rename_w(const wchar_t *oldname, const wchar_t *newname)
429 {/*{{{*/
430 int ret = 0;
431 DWORD err = 0;
432
433 PHP_WIN32_IOUTIL_CHECK_PATH_W(oldname, -1, 0)
434 PHP_WIN32_IOUTIL_CHECK_PATH_W(newname, -1, 0)
435
436
437 if (!MoveFileExW(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED)) {
438 err = GetLastError();
439 ret = -1;
440 SET_ERRNO_FROM_WIN32_CODE(err);
441 }
442
443 return ret;
444 }/*}}}*/
445
php_win32_ioutil_getcwd_w(wchar_t * buf,size_t len)446 PW32IO wchar_t *php_win32_ioutil_getcwd_w(wchar_t *buf, size_t len)
447 {/*{{{*/
448 DWORD err = 0;
449 wchar_t *tmp_buf = NULL;
450 DWORD tmp_len = (DWORD)len;
451
452 /* If buf was NULL, the result has to be freed outside here. */
453 if (!buf) {
454 tmp_len = GetCurrentDirectoryW(0, NULL) + 1;
455 if (!tmp_len) {
456 err = GetLastError();
457 SET_ERRNO_FROM_WIN32_CODE(err);
458 return NULL;
459 } else if (tmp_len > len) {
460 SET_ERRNO_FROM_WIN32_CODE(ERROR_INSUFFICIENT_BUFFER);
461 return NULL;
462 }
463
464 tmp_buf = (wchar_t *)malloc((tmp_len)*sizeof(wchar_t));
465 if (!tmp_buf) {
466 SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
467 return NULL;
468 }
469 buf = tmp_buf;
470 }
471
472 if (!GetCurrentDirectoryW(tmp_len, buf)) {
473 err = GetLastError();
474 SET_ERRNO_FROM_WIN32_CODE(err);
475 free(tmp_buf);
476 return NULL;
477 }
478
479 return (wchar_t *)buf;
480 }/*}}}*/
481
482 /* based on zend_dirname(). */
php_win32_ioutil_dirname(char * path,size_t len)483 PW32IO size_t php_win32_ioutil_dirname(char *path, size_t len)
484 {/*{{{*/
485 char *ret = NULL, *start;
486 size_t ret_len, len_adjust = 0, pathw_len;
487 wchar_t *endw, *pathw, *startw;
488
489 if (len == 0) {
490 return 0;
491 }
492
493 start = path;
494
495 /* Don't really care about the path normalization, pure parsing here. */
496 startw = pathw = php_win32_cp_conv_any_to_w(path, len, &pathw_len);
497 if (!pathw) {
498 return 0;
499 }
500
501 endw = pathw + pathw_len - 1;
502
503 if ((2 <= pathw_len) && iswalpha((wint_t)(pathw)[0]) && (L':' == pathw[1])) {
504 pathw += 2;
505 path += 2;
506 len_adjust += 2;
507 if (2 == len) {
508 free(startw);
509 return len;
510 }
511 }
512
513 /* Strip trailing slashes */
514 while (endw >= pathw && PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
515 endw--;
516 }
517 if (endw < pathw) {
518 free(startw);
519 /* The path only contained slashes */
520 path[0] = PHP_WIN32_IOUTIL_DEFAULT_SLASH;
521 path[1] = '\0';
522 return 1 + len_adjust;
523 }
524
525 /* Strip filename */
526 while (endw >= pathw && !PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
527 endw--;
528 }
529 if (endw < pathw) {
530 free(startw);
531 path[0] = '.';
532 path[1] = '\0';
533 return 1 + len_adjust;
534 }
535
536 /* Strip slashes which came before the file name */
537 while (endw >= pathw && PHP_WIN32_IOUTIL_IS_SLASHW(*endw)) {
538 endw--;
539 }
540 if (endw < pathw) {
541 free(startw);
542 path[0] = PHP_WIN32_IOUTIL_DEFAULT_SLASH;
543 path[1] = '\0';
544 return 1 + len_adjust;
545 }
546 *(endw+1) = L'\0';
547
548 ret_len = (endw + 1 - startw);
549 if (PHP_WIN32_IOUTIL_IS_LONG_PATHW(startw, ret_len)) {
550 ret = php_win32_ioutil_conv_w_to_any(startw + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, ret_len - PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, &ret_len);
551 } else {
552 ret = php_win32_ioutil_conv_w_to_any(startw, ret_len, &ret_len);
553 }
554 memmove(start, ret, ret_len+1);
555 assert(start[ret_len] == '\0');
556 free(ret);
557 free(startw);
558
559 return ret_len;
560 }/*}}}*/
561
562 /* Partial normalization can still be acceptable, explicit fail has to be caught. */
php_win32_ioutil_normalize_path_w(wchar_t ** buf,size_t len,size_t * new_len)563 PW32IO php_win32_ioutil_normalization_result php_win32_ioutil_normalize_path_w(wchar_t **buf, size_t len, size_t *new_len)
564 {/*{{{*/
565 wchar_t *idx = *buf, canonicalw[MAXPATHLEN], _tmp[MAXPATHLEN], *pos = _tmp;
566 size_t ret_len = len;
567
568 if (len >= MAXPATHLEN) {
569 SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_LENGTH);
570 *new_len = 0;
571 return PHP_WIN32_IOUTIL_NORM_FAIL;
572 }
573
574 for (; (size_t)(idx - *buf) <= len; idx++, pos++) {
575 *pos = *idx;
576 if (PHP_WIN32_IOUTIL_FW_SLASHW == *pos) {
577 *pos = PHP_WIN32_IOUTIL_DEFAULT_SLASHW;
578 }
579 while (PHP_WIN32_IOUTIL_IS_SLASHW(*idx) && PHP_WIN32_IOUTIL_IS_SLASHW(*(idx+1))) {
580 idx++;
581 }
582 }
583
584 if (S_OK != canonicalize_path_w(canonicalw, MAXPATHLEN, _tmp, PATHCCH_ALLOW_LONG_PATHS)) {
585 /* Length unchanged. */
586 *new_len = len;
587 return PHP_WIN32_IOUTIL_NORM_PARTIAL;
588 }
589 ret_len = wcslen(canonicalw);
590 if (ret_len != len) {
591 if (ret_len > len) {
592 wchar_t *tmp = realloc(*buf, (ret_len + 1) * sizeof(wchar_t));
593 if (!tmp) {
594 SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
595 /* Length unchanged. */
596 *new_len = len;
597 return PHP_WIN32_IOUTIL_NORM_PARTIAL;
598 }
599 *buf = tmp;
600 }
601 memmove(*buf, canonicalw, (ret_len + 1) * sizeof(wchar_t));
602 }
603 *new_len = ret_len;
604
605 return PHP_WIN32_IOUTIL_NORM_OK;
606 }/*}}}*/
607
MyPathCchCanonicalizeExFallback(wchar_t * pszPathOut,size_t cchPathOut,const wchar_t * pszPathIn,unsigned long dwFlags)608 static HRESULT __stdcall MyPathCchCanonicalizeExFallback(wchar_t *pszPathOut, size_t cchPathOut, const wchar_t *pszPathIn, unsigned long dwFlags)
609 {/*{{{*/
610 return -42;
611 }/*}}}*/
612
php_win32_ioutil_init(void)613 BOOL php_win32_ioutil_init(void)
614 {/*{{{*/
615 HMODULE hMod = GetModuleHandle("api-ms-win-core-path-l1-1-0");
616
617 if (hMod) {
618 canonicalize_path_w = (MyPathCchCanonicalizeEx)GetProcAddress(hMod, "PathCchCanonicalizeEx");
619 if (!canonicalize_path_w) {
620 canonicalize_path_w = (MyPathCchCanonicalizeEx)MyPathCchCanonicalizeExFallback;
621 }
622 } else {
623 canonicalize_path_w = (MyPathCchCanonicalizeEx)MyPathCchCanonicalizeExFallback;
624 }
625
626 return TRUE;
627 }/*}}}*/
628
629 /* an extended version could be implemented, for now direct functions can be used. */
630 #if 0
631 PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode)
632 {
633 return _waccess(path, mode);
634 }
635 #endif
636
637 #if 0
638 PW32IO HANDLE php_win32_ioutil_findfirstfile_w(char *path, WIN32_FIND_DATA *data)
639 {
640 HANDLE ret = INVALID_HANDLE_VALUE;
641 DWORD err;
642
643 if (!path) {
644 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
645 return ret;
646 }
647
648 pathw = php_win32_ioutil_any_to_w(path);
649
650 if (!pathw) {
651 err = GetLastError();
652 SET_ERRNO_FROM_WIN32_CODE(ret);
653 return ret;
654 }
655
656 ret = FindFirstFileW(pathw, data);
657
658 if (INVALID_HANDLE_VALUE == ret && path) {
659 ret = FindFirstFileA(path, data);
660 }
661
662 /* XXX set errno */
663 return ret;
664 }
665 #endif
666
667 /*
668 * Local variables:
669 * tab-width: 4
670 * c-basic-offset: 4
671 * End:
672 * vim600: sw=4 ts=4 fdm=marker
673 * vim<600: sw=4 ts=4
674 */
675