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 | Authors: Andi Gutmans <andi@php.net> |
16 | Sascha Schumann <sascha@schumann.cx> |
17 | Pierre Joye <pierre@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #include <time.h>
30
31 #include "zend.h"
32 #include "zend_virtual_cwd.h"
33
34 #ifdef ZEND_WIN32
35 #include <io.h>
36 #include "tsrm_win32.h"
37 # ifndef IO_REPARSE_TAG_SYMLINK
38 # define IO_REPARSE_TAG_SYMLINK 0xA000000C
39 # endif
40
41 # ifndef IO_REPARSE_TAG_DEDUP
42 # define IO_REPARSE_TAG_DEDUP 0x80000013
43 # endif
44
45 # ifndef IO_REPARSE_TAG_CLOUD
46 # define IO_REPARSE_TAG_CLOUD (0x9000001AL)
47 # endif
48 /* IO_REPARSE_TAG_CLOUD_1 through IO_REPARSE_TAG_CLOUD_F have values of 0x9000101AL
49 to 0x9000F01AL, they can be checked against the mask. */
50 #ifndef IO_REPARSE_TAG_CLOUD_MASK
51 #define IO_REPARSE_TAG_CLOUD_MASK (0x0000F000L)
52 #endif
53
54 #ifndef IO_REPARSE_TAG_ONEDRIVE
55 #define IO_REPARSE_TAG_ONEDRIVE (0x80000021L)
56 #endif
57
58 # ifndef IO_REPARSE_TAG_ACTIVISION_HSM
59 # define IO_REPARSE_TAG_ACTIVISION_HSM (0x00000047L)
60 # endif
61
62 # ifndef IO_REPARSE_TAG_PROJFS
63 # define IO_REPARSE_TAG_PROJFS (0x9000001CL)
64 # endif
65
66 # ifndef VOLUME_NAME_NT
67 # define VOLUME_NAME_NT 0x2
68 # endif
69
70 # ifndef VOLUME_NAME_DOS
71 # define VOLUME_NAME_DOS 0x0
72 # endif
73
74 # include <winioctl.h>
75 # include <winnt.h>
76 #endif
77
78 #ifndef HAVE_REALPATH
79 #define realpath(x,y) strcpy(y,x)
80 #endif
81
82 #define VIRTUAL_CWD_DEBUG 0
83
84 #include "TSRM.h"
85
86 /* Only need mutex for popen() in Windows because it doesn't chdir() on UNIX */
87 #if defined(ZEND_WIN32) && defined(ZTS)
88 MUTEX_T cwd_mutex;
89 #endif
90
91 #ifdef ZTS
92 ts_rsrc_id cwd_globals_id;
93 size_t cwd_globals_offset;
94 #else
95 virtual_cwd_globals cwd_globals;
96 #endif
97
98 static cwd_state main_cwd_state; /* True global */
99
100 #ifndef ZEND_WIN32
101 #include <unistd.h>
102 #else
103 #include <direct.h>
104 #include "zend_globals.h"
105 #include "zend_globals_macros.h"
106 #endif
107
108 #define CWD_STATE_COPY(d, s) \
109 (d)->cwd_length = (s)->cwd_length; \
110 (d)->cwd = (char *) emalloc((s)->cwd_length+1); \
111 memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1);
112
113 #define CWD_STATE_FREE(s) \
114 efree((s)->cwd); \
115 (s)->cwd_length = 0;
116
117 #ifdef ZEND_WIN32
118 # define CWD_STATE_FREE_ERR(state) do { \
119 DWORD last_error = GetLastError(); \
120 CWD_STATE_FREE(state); \
121 SetLastError(last_error); \
122 } while (0)
123 #else
124 # define CWD_STATE_FREE_ERR(state) CWD_STATE_FREE(state)
125 #endif
126
php_is_dir_ok(const cwd_state * state)127 static int php_is_dir_ok(const cwd_state *state) /* {{{ */
128 {
129 zend_stat_t buf;
130
131 if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode))
132 return (0);
133
134 return (1);
135 }
136 /* }}} */
137
php_is_file_ok(const cwd_state * state)138 static int php_is_file_ok(const cwd_state *state) /* {{{ */
139 {
140 zend_stat_t buf;
141
142 if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode))
143 return (0);
144
145 return (1);
146 }
147 /* }}} */
148
cwd_globals_ctor(virtual_cwd_globals * cwd_g)149 static void cwd_globals_ctor(virtual_cwd_globals *cwd_g) /* {{{ */
150 {
151 CWD_STATE_COPY(&cwd_g->cwd, &main_cwd_state);
152 cwd_g->realpath_cache_size = 0;
153 cwd_g->realpath_cache_size_limit = REALPATH_CACHE_SIZE;
154 cwd_g->realpath_cache_ttl = REALPATH_CACHE_TTL;
155 memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache));
156 }
157 /* }}} */
158
realpath_cache_clean_helper(uint32_t max_entries,realpath_cache_bucket ** cache,zend_long * cache_size)159 static void realpath_cache_clean_helper(uint32_t max_entries, realpath_cache_bucket **cache, zend_long *cache_size)
160 {
161 uint32_t i;
162
163 for (i = 0; i < max_entries; i++) {
164 realpath_cache_bucket *p = cache[i];
165 while (p != NULL) {
166 realpath_cache_bucket *r = p;
167 p = p->next;
168 free(r);
169 }
170 cache[i] = NULL;
171 }
172 *cache_size = 0;
173 }
174
cwd_globals_dtor(virtual_cwd_globals * cwd_g)175 static void cwd_globals_dtor(virtual_cwd_globals *cwd_g) /* {{{ */
176 {
177 realpath_cache_clean_helper(sizeof(cwd_g->realpath_cache)/sizeof(cwd_g->realpath_cache[0]), cwd_g->realpath_cache, &cwd_g->realpath_cache_size);
178 }
179 /* }}} */
180
virtual_cwd_main_cwd_init(uint8_t reinit)181 void virtual_cwd_main_cwd_init(uint8_t reinit) /* {{{ */
182 {
183 char cwd[MAXPATHLEN];
184 char *result;
185
186 if (reinit) {
187 free(main_cwd_state.cwd);
188 }
189
190 #ifdef ZEND_WIN32
191 ZeroMemory(&cwd, sizeof(cwd));
192 result = php_win32_ioutil_getcwd(cwd, sizeof(cwd));
193 #else
194 result = getcwd(cwd, sizeof(cwd));
195 #endif
196
197 if (!result) {
198 cwd[0] = '\0';
199 }
200
201 main_cwd_state.cwd_length = strlen(cwd);
202 #ifdef ZEND_WIN32
203 if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') {
204 cwd[0] = toupper(cwd[0]);
205 }
206 #endif
207 main_cwd_state.cwd = strdup(cwd);
208 }
209 /* }}} */
210
virtual_cwd_startup(void)211 CWD_API void virtual_cwd_startup(void) /* {{{ */
212 {
213 virtual_cwd_main_cwd_init(0);
214 #ifdef ZTS
215 ts_allocate_fast_id(&cwd_globals_id, &cwd_globals_offset, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor);
216 #else
217 cwd_globals_ctor(&cwd_globals);
218 #endif
219
220 #if (defined(ZEND_WIN32)) && defined(ZTS)
221 cwd_mutex = tsrm_mutex_alloc();
222 #endif
223 }
224 /* }}} */
225
virtual_cwd_shutdown(void)226 CWD_API void virtual_cwd_shutdown(void) /* {{{ */
227 {
228 #ifndef ZTS
229 cwd_globals_dtor(&cwd_globals);
230 #endif
231 #if (defined(ZEND_WIN32)) && defined(ZTS)
232 tsrm_mutex_free(cwd_mutex);
233 #endif
234
235 free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */
236 }
237 /* }}} */
238
virtual_cwd_activate(void)239 CWD_API int virtual_cwd_activate(void) /* {{{ */
240 {
241 if (CWDG(cwd).cwd == NULL) {
242 CWD_STATE_COPY(&CWDG(cwd), &main_cwd_state);
243 }
244 return 0;
245 }
246 /* }}} */
247
virtual_cwd_deactivate(void)248 CWD_API int virtual_cwd_deactivate(void) /* {{{ */
249 {
250 if (CWDG(cwd).cwd != NULL) {
251 CWD_STATE_FREE(&CWDG(cwd));
252 CWDG(cwd).cwd = NULL;
253 }
254 return 0;
255 }
256 /* }}} */
257
virtual_getcwd_ex(size_t * length)258 CWD_API char *virtual_getcwd_ex(size_t *length) /* {{{ */
259 {
260 cwd_state *state;
261
262 state = &CWDG(cwd);
263
264 if (state->cwd_length == 0) {
265 char *retval;
266
267 *length = 1;
268 retval = (char *) emalloc(2);
269 retval[0] = DEFAULT_SLASH;
270 retval[1] = '\0';
271 return retval;
272 }
273
274 #ifdef ZEND_WIN32
275 /* If we have something like C: */
276 if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') {
277 char *retval;
278
279 *length = state->cwd_length+1;
280 retval = (char *) emalloc(*length+1);
281 memcpy(retval, state->cwd, *length);
282 retval[0] = toupper(retval[0]);
283 retval[*length-1] = DEFAULT_SLASH;
284 retval[*length] = '\0';
285 return retval;
286 }
287 #endif
288 if (!state->cwd) {
289 *length = 0;
290 return NULL;
291 }
292
293 *length = state->cwd_length;
294 return estrdup(state->cwd);
295 }
296 /* }}} */
297
298 /* Same semantics as UNIX getcwd() */
virtual_getcwd(char * buf,size_t size)299 CWD_API char *virtual_getcwd(char *buf, size_t size) /* {{{ */
300 {
301 size_t length;
302 char *cwd;
303
304 cwd = virtual_getcwd_ex(&length);
305
306 if (buf == NULL) {
307 return cwd;
308 }
309 if (length > size-1) {
310 efree(cwd);
311 errno = ERANGE; /* Is this OK? */
312 return NULL;
313 }
314 if (!cwd) {
315 return NULL;
316 }
317 memcpy(buf, cwd, length+1);
318 efree(cwd);
319 return buf;
320 }
321 /* }}} */
322
323 #ifdef ZEND_WIN32
realpath_cache_key(const char * path,size_t path_len)324 static inline zend_ulong realpath_cache_key(const char *path, size_t path_len) /* {{{ */
325 {
326 register zend_ulong h;
327 size_t bucket_key_len;
328 char *bucket_key_start = tsrm_win32_get_path_sid_key(path, path_len, &bucket_key_len);
329 char *bucket_key = (char *)bucket_key_start;
330 const char *e;
331
332 if (!bucket_key) {
333 return 0;
334 }
335
336 e = bucket_key + bucket_key_len;
337 for (h = Z_UL(2166136261); bucket_key < e;) {
338 h *= Z_UL(16777619);
339 h ^= *bucket_key++;
340 }
341 if (bucket_key_start != path) {
342 HeapFree(GetProcessHeap(), 0, (LPVOID)bucket_key_start);
343 }
344 return h;
345 }
346 /* }}} */
347 #else
realpath_cache_key(const char * path,size_t path_len)348 static inline zend_ulong realpath_cache_key(const char *path, size_t path_len) /* {{{ */
349 {
350 register zend_ulong h;
351 const char *e = path + path_len;
352
353 for (h = Z_UL(2166136261); path < e;) {
354 h *= Z_UL(16777619);
355 h ^= *path++;
356 }
357
358 return h;
359 }
360 /* }}} */
361 #endif /* defined(ZEND_WIN32) */
362
realpath_cache_clean(void)363 CWD_API void realpath_cache_clean(void) /* {{{ */
364 {
365 realpath_cache_clean_helper(sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]), CWDG(realpath_cache), &CWDG(realpath_cache_size));
366 }
367 /* }}} */
368
realpath_cache_del(const char * path,size_t path_len)369 CWD_API void realpath_cache_del(const char *path, size_t path_len) /* {{{ */
370 {
371 zend_ulong key = realpath_cache_key(path, path_len);
372 zend_ulong n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
373 realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
374
375 while (*bucket != NULL) {
376 if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
377 memcmp(path, (*bucket)->path, path_len) == 0) {
378 realpath_cache_bucket *r = *bucket;
379 *bucket = (*bucket)->next;
380
381 /* if the pointers match then only subtract the length of the path */
382 if(r->path == r->realpath) {
383 CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1;
384 } else {
385 CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
386 }
387
388 free(r);
389 return;
390 } else {
391 bucket = &(*bucket)->next;
392 }
393 }
394 }
395 /* }}} */
396
realpath_cache_add(const char * path,size_t path_len,const char * realpath,size_t realpath_len,int is_dir,time_t t)397 static inline void realpath_cache_add(const char *path, size_t path_len, const char *realpath, size_t realpath_len, int is_dir, time_t t) /* {{{ */
398 {
399 zend_long size = sizeof(realpath_cache_bucket) + path_len + 1;
400 int same = 1;
401
402 if (realpath_len != path_len ||
403 memcmp(path, realpath, path_len) != 0) {
404 size += realpath_len + 1;
405 same = 0;
406 }
407
408 if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) {
409 realpath_cache_bucket *bucket = malloc(size);
410 zend_ulong n;
411
412 if (bucket == NULL) {
413 return;
414 }
415
416 bucket->key = realpath_cache_key(path, path_len);
417 bucket->path = (char*)bucket + sizeof(realpath_cache_bucket);
418 memcpy(bucket->path, path, path_len+1);
419 bucket->path_len = path_len;
420 if (same) {
421 bucket->realpath = bucket->path;
422 } else {
423 bucket->realpath = bucket->path + (path_len + 1);
424 memcpy(bucket->realpath, realpath, realpath_len+1);
425 }
426 bucket->realpath_len = realpath_len;
427 bucket->is_dir = is_dir > 0;
428 #ifdef ZEND_WIN32
429 bucket->is_rvalid = 0;
430 bucket->is_readable = 0;
431 bucket->is_wvalid = 0;
432 bucket->is_writable = 0;
433 #endif
434 bucket->expires = t + CWDG(realpath_cache_ttl);
435 n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
436 bucket->next = CWDG(realpath_cache)[n];
437 CWDG(realpath_cache)[n] = bucket;
438 CWDG(realpath_cache_size) += size;
439 }
440 }
441 /* }}} */
442
realpath_cache_find(const char * path,size_t path_len,time_t t)443 static inline realpath_cache_bucket* realpath_cache_find(const char *path, size_t path_len, time_t t) /* {{{ */
444 {
445 zend_ulong key = realpath_cache_key(path, path_len);
446 zend_ulong n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
447 realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
448
449 while (*bucket != NULL) {
450 if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) {
451 realpath_cache_bucket *r = *bucket;
452 *bucket = (*bucket)->next;
453
454 /* if the pointers match then only subtract the length of the path */
455 if(r->path == r->realpath) {
456 CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1;
457 } else {
458 CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
459 }
460 free(r);
461 } else if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
462 memcmp(path, (*bucket)->path, path_len) == 0) {
463 return *bucket;
464 } else {
465 bucket = &(*bucket)->next;
466 }
467 }
468 return NULL;
469 }
470 /* }}} */
471
realpath_cache_lookup(const char * path,size_t path_len,time_t t)472 CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, size_t path_len, time_t t) /* {{{ */
473 {
474 return realpath_cache_find(path, path_len, t);
475 }
476 /* }}} */
477
realpath_cache_size(void)478 CWD_API zend_long realpath_cache_size(void)
479 {
480 return CWDG(realpath_cache_size);
481 }
482
realpath_cache_max_buckets(void)483 CWD_API zend_long realpath_cache_max_buckets(void)
484 {
485 return (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
486 }
487
realpath_cache_get_buckets(void)488 CWD_API realpath_cache_bucket** realpath_cache_get_buckets(void)
489 {
490 return CWDG(realpath_cache);
491 }
492
493
494 #undef LINK_MAX
495 #define LINK_MAX 32
496
tsrm_realpath_r(char * path,size_t start,size_t len,int * ll,time_t * t,int use_realpath,int is_dir,int * link_is_dir)497 static size_t tsrm_realpath_r(char *path, size_t start, size_t len, int *ll, time_t *t, int use_realpath, int is_dir, int *link_is_dir) /* {{{ */
498 {
499 size_t i, j;
500 int directory = 0, save;
501 #ifdef ZEND_WIN32
502 WIN32_FIND_DATAW dataw;
503 HANDLE hFind = INVALID_HANDLE_VALUE;
504 ALLOCA_FLAG(use_heap_large)
505 wchar_t *pathw = NULL;
506 int may_retry_reparse_point;
507 #define FREE_PATHW() \
508 do { free(pathw); } while(0);
509
510 #else
511 zend_stat_t st;
512 #endif
513 realpath_cache_bucket *bucket;
514 char *tmp;
515 ALLOCA_FLAG(use_heap)
516
517 while (1) {
518 if (len <= start) {
519 if (link_is_dir) {
520 *link_is_dir = 1;
521 }
522 return start;
523 }
524
525 i = len;
526 while (i > start && !IS_SLASH(path[i-1])) {
527 i--;
528 }
529 assert(i < MAXPATHLEN);
530
531 if (i == len ||
532 (i + 1 == len && path[i] == '.')) {
533 /* remove double slashes and '.' */
534 len = EXPECTED(i > 0) ? i - 1 : 0;
535 is_dir = 1;
536 continue;
537 } else if (i + 2 == len && path[i] == '.' && path[i+1] == '.') {
538 /* remove '..' and previous directory */
539 is_dir = 1;
540 if (link_is_dir) {
541 *link_is_dir = 1;
542 }
543 if (i <= start + 1) {
544 return start ? start : len;
545 }
546 j = tsrm_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL);
547 if (j > start && j != (size_t)-1) {
548 j--;
549 assert(i < MAXPATHLEN);
550 while (j > start && !IS_SLASH(path[j])) {
551 j--;
552 }
553 assert(i < MAXPATHLEN);
554 if (!start) {
555 /* leading '..' must not be removed in case of relative path */
556 if (j == 0 && path[0] == '.' && path[1] == '.' &&
557 IS_SLASH(path[2])) {
558 path[3] = '.';
559 path[4] = '.';
560 path[5] = DEFAULT_SLASH;
561 j = 5;
562 } else if (j > 0 &&
563 path[j+1] == '.' && path[j+2] == '.' &&
564 IS_SLASH(path[j+3])) {
565 j += 4;
566 path[j++] = '.';
567 path[j++] = '.';
568 path[j] = DEFAULT_SLASH;
569 }
570 }
571 } else if (!start && !j) {
572 /* leading '..' must not be removed in case of relative path */
573 path[0] = '.';
574 path[1] = '.';
575 path[2] = DEFAULT_SLASH;
576 j = 2;
577 }
578 return j;
579 }
580
581 path[len] = 0;
582
583 save = (use_realpath != CWD_EXPAND);
584
585 if (start && save && CWDG(realpath_cache_size_limit)) {
586 /* cache lookup for absolute path */
587 if (!*t) {
588 *t = time(0);
589 }
590 if ((bucket = realpath_cache_find(path, len, *t)) != NULL) {
591 if (is_dir && !bucket->is_dir) {
592 /* not a directory */
593 return (size_t)-1;
594 } else {
595 if (link_is_dir) {
596 *link_is_dir = bucket->is_dir;
597 }
598 memcpy(path, bucket->realpath, bucket->realpath_len + 1);
599 return bucket->realpath_len;
600 }
601 }
602 }
603
604 #ifdef ZEND_WIN32
605 retry_reparse_point:
606 may_retry_reparse_point = 0;
607 if (save) {
608 pathw = php_win32_ioutil_any_to_w(path);
609 if (!pathw) {
610 return (size_t)-1;
611 }
612 hFind = FindFirstFileExW(pathw, FindExInfoBasic, &dataw, FindExSearchNameMatch, NULL, 0);
613 if (INVALID_HANDLE_VALUE == hFind) {
614 if (use_realpath == CWD_REALPATH) {
615 /* file not found */
616 FREE_PATHW()
617 return (size_t)-1;
618 }
619 /* continue resolution anyway but don't save result in the cache */
620 save = 0;
621 } else {
622 FindClose(hFind);
623 }
624 }
625
626 tmp = do_alloca(len+1, use_heap);
627 memcpy(tmp, path, len+1);
628
629 retry_reparse_tag_cloud:
630 if(save &&
631 !(IS_UNC_PATH(path, len) && len >= 3 && path[2] != '?') &&
632 (dataw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
633 ) {
634 /* File is a reparse point. Get the target */
635 HANDLE hLink = NULL;
636 PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER * pbuffer;
637 DWORD retlength = 0;
638 size_t bufindex = 0;
639 uint8_t isabsolute = 0;
640 wchar_t * reparsetarget;
641 BOOL isVolume = FALSE;
642 #if VIRTUAL_CWD_DEBUG
643 char *printname = NULL;
644 #endif
645 char *substitutename = NULL;
646 size_t substitutename_len;
647 size_t substitutename_off = 0;
648 wchar_t tmpsubstname[MAXPATHLEN];
649
650 if(++(*ll) > LINK_MAX) {
651 free_alloca(tmp, use_heap);
652 FREE_PATHW()
653 return (size_t)-1;
654 }
655
656 hLink = CreateFileW(pathw,
657 0,
658 PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE,
659 NULL,
660 OPEN_EXISTING,
661 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS,
662 NULL);
663 if(hLink == INVALID_HANDLE_VALUE) {
664 free_alloca(tmp, use_heap);
665 FREE_PATHW()
666 return (size_t)-1;
667 }
668
669 pbuffer = (PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER *)do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
670 if (pbuffer == NULL) {
671 CloseHandle(hLink);
672 free_alloca(tmp, use_heap);
673 FREE_PATHW()
674 return (size_t)-1;
675 }
676 if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
677 BY_HANDLE_FILE_INFORMATION fileInformation;
678
679 free_alloca(pbuffer, use_heap_large);
680 if ((dataw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
681 (dataw.dwReserved0 & ~IO_REPARSE_TAG_CLOUD_MASK) == IO_REPARSE_TAG_CLOUD &&
682 EG(windows_version_info).dwMajorVersion >= 10 &&
683 EG(windows_version_info).dwMinorVersion == 0 &&
684 EG(windows_version_info).dwBuildNumber >= 18362 &&
685 GetFileInformationByHandle(hLink, &fileInformation) &&
686 !(fileInformation.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
687 dataw.dwFileAttributes = fileInformation.dwFileAttributes;
688 CloseHandle(hLink);
689 (*ll)--;
690 goto retry_reparse_tag_cloud;
691 }
692 free_alloca(tmp, use_heap);
693 CloseHandle(hLink);
694 FREE_PATHW()
695 return (size_t)-1;
696 }
697
698 CloseHandle(hLink);
699
700 if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
701 may_retry_reparse_point = 1;
702 reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget;
703 isabsolute = pbuffer->SymbolicLinkReparseBuffer.Flags == 0;
704 #if VIRTUAL_CWD_DEBUG
705 printname = php_win32_ioutil_w_to_any(reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR));
706 if (!printname) {
707 free_alloca(pbuffer, use_heap_large);
708 free_alloca(tmp, use_heap);
709 FREE_PATHW()
710 return (size_t)-1;
711 }
712 #endif
713
714 substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
715 if (substitutename_len >= MAXPATHLEN) {
716 free_alloca(pbuffer, use_heap_large);
717 free_alloca(tmp, use_heap);
718 FREE_PATHW()
719 return (size_t)-1;
720 }
721 memmove(tmpsubstname, reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), pbuffer->MountPointReparseBuffer.SubstituteNameLength);
722 tmpsubstname[substitutename_len] = L'\0';
723 substitutename = php_win32_cp_conv_w_to_any(tmpsubstname, substitutename_len, &substitutename_len);
724 if (!substitutename || substitutename_len >= MAXPATHLEN) {
725 free_alloca(pbuffer, use_heap_large);
726 free_alloca(tmp, use_heap);
727 free(substitutename);
728 #if VIRTUAL_CWD_DEBUG
729 free(printname);
730 #endif
731 FREE_PATHW()
732 return (size_t)-1;
733 }
734 }
735 else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
736 isabsolute = 1;
737 reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget;
738 #if VIRTUAL_CWD_DEBUG
739 printname = php_win32_ioutil_w_to_any(reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR));
740 if (!printname) {
741 free_alloca(pbuffer, use_heap_large);
742 free_alloca(tmp, use_heap);
743 FREE_PATHW()
744 return (size_t)-1;
745 }
746 #endif
747
748
749 substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
750 if (substitutename_len >= MAXPATHLEN) {
751 free_alloca(pbuffer, use_heap_large);
752 free_alloca(tmp, use_heap);
753 FREE_PATHW()
754 return (size_t)-1;
755 }
756 memmove(tmpsubstname, reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), pbuffer->MountPointReparseBuffer.SubstituteNameLength);
757 tmpsubstname[substitutename_len] = L'\0';
758 substitutename = php_win32_cp_conv_w_to_any(tmpsubstname, substitutename_len, &substitutename_len);
759 if (!substitutename || substitutename_len >= MAXPATHLEN) {
760 free_alloca(pbuffer, use_heap_large);
761 free_alloca(tmp, use_heap);
762 free(substitutename);
763 #if VIRTUAL_CWD_DEBUG
764 free(printname);
765 #endif
766 FREE_PATHW()
767 return (size_t)-1;
768 }
769 }
770 else if (pbuffer->ReparseTag == IO_REPARSE_TAG_DEDUP ||
771 /* Starting with 1709. */
772 (pbuffer->ReparseTag & ~IO_REPARSE_TAG_CLOUD_MASK) == IO_REPARSE_TAG_CLOUD ||
773 IO_REPARSE_TAG_ONEDRIVE == pbuffer->ReparseTag ||
774 IO_REPARSE_TAG_ACTIVISION_HSM == pbuffer->ReparseTag ||
775 IO_REPARSE_TAG_PROJFS == pbuffer->ReparseTag) {
776 isabsolute = 1;
777 substitutename = malloc((len + 1) * sizeof(char));
778 if (!substitutename) {
779 free_alloca(pbuffer, use_heap_large);
780 free_alloca(tmp, use_heap);
781 FREE_PATHW()
782 return (size_t)-1;
783 }
784 memcpy(substitutename, path, len + 1);
785 substitutename_len = len;
786 } else {
787 /* XXX this might be not the end, restart handling with REPARSE_GUID_DATA_BUFFER should be implemented. */
788 free_alloca(pbuffer, use_heap_large);
789 free_alloca(tmp, use_heap);
790 FREE_PATHW()
791 return (size_t)-1;
792 }
793
794 if(isabsolute && substitutename_len > 4) {
795 /* Do not resolve volumes (for now). A mounted point can
796 target a volume without a drive, it is not certain that
797 all IO functions we use in php and its deps support
798 path with volume GUID instead of the DOS way, like:
799 d:\test\mnt\foo
800 \\?\Volume{62d1c3f8-83b9-11de-b108-806e6f6e6963}\foo
801 */
802 if (strncmp(substitutename, "\\??\\Volume{",11) == 0
803 || strncmp(substitutename, "\\\\?\\Volume{",11) == 0
804 || strncmp(substitutename, "\\??\\UNC\\", 8) == 0
805 ) {
806 isVolume = TRUE;
807 substitutename_off = 0;
808 } else
809 /* do not use the \??\ and \\?\ prefix*/
810 if (strncmp(substitutename, "\\??\\", 4) == 0
811 || strncmp(substitutename, "\\\\?\\", 4) == 0) {
812 substitutename_off = 4;
813 }
814 }
815
816 if (!isVolume) {
817 char * tmp2 = substitutename + substitutename_off;
818 for (bufindex = 0; bufindex + substitutename_off < substitutename_len; bufindex++) {
819 *(path + bufindex) = *(tmp2 + bufindex);
820 }
821
822 *(path + bufindex) = 0;
823 j = bufindex;
824 } else {
825 j = len;
826 }
827
828
829 #if VIRTUAL_CWD_DEBUG
830 fprintf(stderr, "reparse: print: %s ", printname);
831 fprintf(stderr, "sub: %s ", substitutename);
832 fprintf(stderr, "resolved: %s ", path);
833 free(printname);
834 #endif
835 free_alloca(pbuffer, use_heap_large);
836 free(substitutename);
837
838 if (may_retry_reparse_point) {
839 DWORD attrs;
840
841 FREE_PATHW()
842 pathw = php_win32_ioutil_any_to_w(path);
843 if (!pathw) {
844 return (size_t)-1;
845 }
846 attrs = GetFileAttributesW(pathw);
847 if (!isVolume && attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_REPARSE_POINT)) {
848 free_alloca(tmp, use_heap);
849 FREE_PATHW()
850 goto retry_reparse_point;
851 }
852 }
853
854 if(isabsolute == 1) {
855 if (!((j == 3) && (path[1] == ':') && (path[2] == '\\'))) {
856 /* use_realpath is 0 in the call below coz path is absolute*/
857 j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory);
858 if(j == (size_t)-1) {
859 free_alloca(tmp, use_heap);
860 FREE_PATHW()
861 return (size_t)-1;
862 }
863 }
864 }
865 else {
866 if(i + j >= MAXPATHLEN - 1) {
867 free_alloca(tmp, use_heap);
868 FREE_PATHW()
869 return (size_t)-1;
870 }
871
872 memmove(path+i, path, j+1);
873 memcpy(path, tmp, i-1);
874 path[i-1] = DEFAULT_SLASH;
875 j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory);
876 if(j == (size_t)-1) {
877 free_alloca(tmp, use_heap);
878 FREE_PATHW()
879 return (size_t)-1;
880 }
881 }
882 directory = (dataw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
883
884 if(link_is_dir) {
885 *link_is_dir = directory;
886 }
887 }
888 else {
889 if (save) {
890 directory = (dataw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
891 if (is_dir && !directory) {
892 /* not a directory */
893 free_alloca(tmp, use_heap);
894 FREE_PATHW()
895 return (size_t)-1;
896 }
897 }
898 #else
899 if (save && php_sys_lstat(path, &st) < 0) {
900 if (use_realpath == CWD_REALPATH) {
901 /* file not found */
902 return (size_t)-1;
903 }
904 /* continue resolution anyway but don't save result in the cache */
905 save = 0;
906 }
907
908 tmp = do_alloca(len+1, use_heap);
909 memcpy(tmp, path, len+1);
910
911 if (save && S_ISLNK(st.st_mode)) {
912 if (++(*ll) > LINK_MAX || (j = (size_t)php_sys_readlink(tmp, path, MAXPATHLEN)) == (size_t)-1) {
913 /* too many links or broken symlinks */
914 free_alloca(tmp, use_heap);
915 return (size_t)-1;
916 }
917 path[j] = 0;
918 if (IS_ABSOLUTE_PATH(path, j)) {
919 j = tsrm_realpath_r(path, 1, j, ll, t, use_realpath, is_dir, &directory);
920 if (j == (size_t)-1) {
921 free_alloca(tmp, use_heap);
922 return (size_t)-1;
923 }
924 } else {
925 if (i + j >= MAXPATHLEN-1) {
926 free_alloca(tmp, use_heap);
927 return (size_t)-1; /* buffer overflow */
928 }
929 memmove(path+i, path, j+1);
930 memcpy(path, tmp, i-1);
931 path[i-1] = DEFAULT_SLASH;
932 j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory);
933 if (j == (size_t)-1) {
934 free_alloca(tmp, use_heap);
935 return (size_t)-1;
936 }
937 }
938 if (link_is_dir) {
939 *link_is_dir = directory;
940 }
941 } else {
942 if (save) {
943 directory = S_ISDIR(st.st_mode);
944 if (link_is_dir) {
945 *link_is_dir = directory;
946 }
947 if (is_dir && !directory) {
948 /* not a directory */
949 free_alloca(tmp, use_heap);
950 return (size_t)-1;
951 }
952 }
953 #endif
954 if (i <= start + 1) {
955 j = start;
956 } else {
957 /* some leading directories may be unaccessable */
958 j = tsrm_realpath_r(path, start, i-1, ll, t, save ? CWD_FILEPATH : use_realpath, 1, NULL);
959 if (j > start && j != (size_t)-1) {
960 path[j++] = DEFAULT_SLASH;
961 }
962 }
963 #ifdef ZEND_WIN32
964 if (j == (size_t)-1 || j + len >= MAXPATHLEN - 1 + i) {
965 free_alloca(tmp, use_heap);
966 FREE_PATHW()
967 return (size_t)-1;
968 }
969 if (save) {
970 size_t sz;
971 char *tmp_path = php_win32_ioutil_conv_w_to_any(dataw.cFileName, PHP_WIN32_CP_IGNORE_LEN, &sz);
972 if (!tmp_path) {
973 free_alloca(tmp, use_heap);
974 FREE_PATHW()
975 return (size_t)-1;
976 }
977 i = sz;
978 memcpy(path+j, tmp_path, i+1);
979 free(tmp_path);
980 j += i;
981 } else {
982 /* use the original file or directory name as it wasn't found */
983 memcpy(path+j, tmp+i, len-i+1);
984 j += (len-i);
985 }
986 }
987 #else
988 if (j == (size_t)-1 || j + len >= MAXPATHLEN - 1 + i) {
989 free_alloca(tmp, use_heap);
990 return (size_t)-1;
991 }
992 memcpy(path+j, tmp+i, len-i+1);
993 j += (len-i);
994 }
995 #endif
996
997 if (save && start && CWDG(realpath_cache_size_limit)) {
998 /* save absolute path in the cache */
999 realpath_cache_add(tmp, len, path, j, directory, *t);
1000 }
1001
1002 free_alloca(tmp, use_heap);
1003 #ifdef ZEND_WIN32
1004 FREE_PATHW()
1005 #undef FREE_PATHW
1006 #endif
1007 return j;
1008 }
1009 }
1010 /* }}} */
1011
1012 /* Resolve path relatively to state and put the real path into state */
1013 /* returns 0 for ok, 1 for error */
virtual_file_ex(cwd_state * state,const char * path,verify_path_func verify_path,int use_realpath)1014 CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) /* {{{ */
1015 {
1016 size_t path_length = strlen(path);
1017 char resolved_path[MAXPATHLEN] = {0};
1018 size_t start = 1;
1019 int ll = 0;
1020 time_t t;
1021 int ret;
1022 int add_slash;
1023 void *tmp;
1024
1025 if (!path_length || path_length >= MAXPATHLEN-1) {
1026 #ifdef ZEND_WIN32
1027 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
1028 #else
1029 errno = EINVAL;
1030 #endif
1031 return 1;
1032 }
1033
1034 #if VIRTUAL_CWD_DEBUG
1035 fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path);
1036 #endif
1037
1038 /* cwd_length can be 0 when getcwd() fails.
1039 * This can happen under solaris when a dir does not have read permissions
1040 * but *does* have execute permissions */
1041 if (!IS_ABSOLUTE_PATH(path, path_length)) {
1042 if (state->cwd_length == 0) {
1043 /* resolve relative path */
1044 start = 0;
1045 memcpy(resolved_path , path, path_length + 1);
1046 } else {
1047 size_t state_cwd_length = state->cwd_length;
1048
1049 #ifdef ZEND_WIN32
1050 if (IS_SLASH(path[0])) {
1051 if (state->cwd[1] == ':') {
1052 /* Copy only the drive name */
1053 state_cwd_length = 2;
1054 } else if (IS_UNC_PATH(state->cwd, state->cwd_length)) {
1055 /* Copy only the share name */
1056 state_cwd_length = 2;
1057 while (IS_SLASH(state->cwd[state_cwd_length])) {
1058 state_cwd_length++;
1059 }
1060 while (state->cwd[state_cwd_length] &&
1061 !IS_SLASH(state->cwd[state_cwd_length])) {
1062 state_cwd_length++;
1063 }
1064 while (IS_SLASH(state->cwd[state_cwd_length])) {
1065 state_cwd_length++;
1066 }
1067 while (state->cwd[state_cwd_length] &&
1068 !IS_SLASH(state->cwd[state_cwd_length])) {
1069 state_cwd_length++;
1070 }
1071 }
1072 }
1073 #endif
1074 if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) {
1075 #ifdef ZEND_WIN32
1076 SET_ERRNO_FROM_WIN32_CODE(ERROR_BUFFER_OVERFLOW);
1077 #else
1078 errno = ENAMETOOLONG;
1079 #endif
1080 return 1;
1081 }
1082 memcpy(resolved_path, state->cwd, state_cwd_length);
1083 if (resolved_path[state_cwd_length-1] == DEFAULT_SLASH) {
1084 memcpy(resolved_path + state_cwd_length, path, path_length + 1);
1085 path_length += state_cwd_length;
1086 } else {
1087 resolved_path[state_cwd_length] = DEFAULT_SLASH;
1088 memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1);
1089 path_length += state_cwd_length + 1;
1090 }
1091 }
1092 } else {
1093 #ifdef ZEND_WIN32
1094 if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) {
1095 resolved_path[0] = path[0];
1096 resolved_path[1] = ':';
1097 resolved_path[2] = DEFAULT_SLASH;
1098 memcpy(resolved_path + 3, path + 2, path_length - 1);
1099 path_length++;
1100 } else
1101 #endif
1102 memcpy(resolved_path, path, path_length + 1);
1103 }
1104
1105 #ifdef ZEND_WIN32
1106 if (memchr(resolved_path, '*', path_length) ||
1107 memchr(resolved_path, '?', path_length)) {
1108 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_NAME);
1109 return 1;
1110 }
1111 #endif
1112
1113 #ifdef ZEND_WIN32
1114 if (IS_UNC_PATH(resolved_path, path_length)) {
1115 /* skip UNC name */
1116 resolved_path[0] = DEFAULT_SLASH;
1117 resolved_path[1] = DEFAULT_SLASH;
1118 start = 2;
1119 while (!IS_SLASH(resolved_path[start])) {
1120 if (resolved_path[start] == 0) {
1121 goto verify;
1122 }
1123 resolved_path[start] = toupper(resolved_path[start]);
1124 start++;
1125 }
1126 resolved_path[start++] = DEFAULT_SLASH;
1127 while (!IS_SLASH(resolved_path[start])) {
1128 if (resolved_path[start] == 0) {
1129 goto verify;
1130 }
1131 resolved_path[start] = toupper(resolved_path[start]);
1132 start++;
1133 }
1134 resolved_path[start++] = DEFAULT_SLASH;
1135 } else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
1136 /* skip DRIVE name */
1137 resolved_path[0] = toupper(resolved_path[0]);
1138 resolved_path[2] = DEFAULT_SLASH;
1139 start = 3;
1140 }
1141 #endif
1142
1143 add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]);
1144 t = CWDG(realpath_cache_ttl) ? 0 : -1;
1145 path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL);
1146
1147 if (path_length == (size_t)-1) {
1148 errno = ENOENT;
1149 return 1;
1150 }
1151
1152 if (!start && !path_length) {
1153 resolved_path[path_length++] = '.';
1154 }
1155
1156 if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) {
1157 if (path_length >= MAXPATHLEN-1) {
1158 return -1;
1159 }
1160 resolved_path[path_length++] = DEFAULT_SLASH;
1161 }
1162 resolved_path[path_length] = 0;
1163
1164 #ifdef ZEND_WIN32
1165 verify:
1166 #endif
1167 if (verify_path) {
1168 cwd_state old_state;
1169
1170 CWD_STATE_COPY(&old_state, state);
1171 state->cwd_length = path_length;
1172
1173 tmp = erealloc(state->cwd, state->cwd_length+1);
1174 state->cwd = (char *) tmp;
1175
1176 memcpy(state->cwd, resolved_path, state->cwd_length+1);
1177 if (verify_path(state)) {
1178 CWD_STATE_FREE(state);
1179 *state = old_state;
1180 ret = 1;
1181 } else {
1182 CWD_STATE_FREE(&old_state);
1183 ret = 0;
1184 }
1185 } else {
1186 state->cwd_length = path_length;
1187 tmp = erealloc(state->cwd, state->cwd_length+1);
1188 state->cwd = (char *) tmp;
1189
1190 memcpy(state->cwd, resolved_path, state->cwd_length+1);
1191 ret = 0;
1192 }
1193
1194 #if VIRTUAL_CWD_DEBUG
1195 fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd);
1196 #endif
1197 return (ret);
1198 }
1199 /* }}} */
1200
virtual_chdir(const char * path)1201 CWD_API int virtual_chdir(const char *path) /* {{{ */
1202 {
1203 return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH)?-1:0;
1204 }
1205 /* }}} */
1206
virtual_chdir_file(const char * path,int (* p_chdir)(const char * path))1207 CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path)) /* {{{ */
1208 {
1209 size_t length = strlen(path);
1210 char *temp;
1211 int retval;
1212 ALLOCA_FLAG(use_heap)
1213
1214 if (length == 0) {
1215 return 1; /* Can't cd to empty string */
1216 }
1217 while(--length < SIZE_MAX && !IS_SLASH(path[length])) {
1218 }
1219
1220 if (length == SIZE_MAX) {
1221 /* No directory only file name */
1222 errno = ENOENT;
1223 return -1;
1224 }
1225
1226 if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */
1227 length++;
1228 }
1229 temp = (char *) do_alloca(length+1, use_heap);
1230 memcpy(temp, path, length);
1231 temp[length] = 0;
1232 #if VIRTUAL_CWD_DEBUG
1233 fprintf (stderr, "Changing directory to %s\n", temp);
1234 #endif
1235 retval = p_chdir(temp);
1236 free_alloca(temp, use_heap);
1237 return retval;
1238 }
1239 /* }}} */
1240
virtual_realpath(const char * path,char * real_path)1241 CWD_API char *virtual_realpath(const char *path, char *real_path) /* {{{ */
1242 {
1243 cwd_state new_state;
1244 char *retval;
1245 char cwd[MAXPATHLEN];
1246
1247 /* realpath("") returns CWD */
1248 if (!*path) {
1249 new_state.cwd = (char*)emalloc(1);
1250 new_state.cwd[0] = '\0';
1251 new_state.cwd_length = 0;
1252 if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
1253 path = cwd;
1254 }
1255 } else if (!IS_ABSOLUTE_PATH(path, strlen(path))) {
1256 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1257 } else {
1258 new_state.cwd = (char*)emalloc(1);
1259 new_state.cwd[0] = '\0';
1260 new_state.cwd_length = 0;
1261 }
1262
1263 if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)==0) {
1264 size_t len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length;
1265
1266 memcpy(real_path, new_state.cwd, len);
1267 real_path[len] = '\0';
1268 retval = real_path;
1269 } else {
1270 retval = NULL;
1271 }
1272
1273 CWD_STATE_FREE(&new_state);
1274 return retval;
1275 }
1276 /* }}} */
1277
virtual_filepath_ex(const char * path,char ** filepath,verify_path_func verify_path)1278 CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path) /* {{{ */
1279 {
1280 cwd_state new_state;
1281 int retval;
1282
1283 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1284 retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH);
1285
1286 *filepath = new_state.cwd;
1287
1288 return retval;
1289
1290 }
1291 /* }}} */
1292
virtual_filepath(const char * path,char ** filepath)1293 CWD_API int virtual_filepath(const char *path, char **filepath) /* {{{ */
1294 {
1295 return virtual_filepath_ex(path, filepath, php_is_file_ok);
1296 }
1297 /* }}} */
1298
virtual_fopen(const char * path,const char * mode)1299 CWD_API FILE *virtual_fopen(const char *path, const char *mode) /* {{{ */
1300 {
1301 cwd_state new_state;
1302 FILE *f;
1303
1304 if (path[0] == '\0') { /* Fail to open empty path */
1305 return NULL;
1306 }
1307
1308 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1309 if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
1310 CWD_STATE_FREE_ERR(&new_state);
1311 return NULL;
1312 }
1313
1314 #ifdef ZEND_WIN32
1315 f = php_win32_ioutil_fopen(new_state.cwd, mode);
1316 #else
1317 f = fopen(new_state.cwd, mode);
1318 #endif
1319
1320 CWD_STATE_FREE_ERR(&new_state);
1321
1322 return f;
1323 }
1324 /* }}} */
1325
virtual_access(const char * pathname,int mode)1326 CWD_API int virtual_access(const char *pathname, int mode) /* {{{ */
1327 {
1328 cwd_state new_state;
1329 int ret;
1330
1331 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1332 if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
1333 CWD_STATE_FREE_ERR(&new_state);
1334 return -1;
1335 }
1336
1337 #if defined(ZEND_WIN32)
1338 ret = tsrm_win32_access(new_state.cwd, mode);
1339 #else
1340 ret = access(new_state.cwd, mode);
1341 #endif
1342
1343 CWD_STATE_FREE_ERR(&new_state);
1344
1345 return ret;
1346 }
1347 /* }}} */
1348
1349 #if HAVE_UTIME
virtual_utime(const char * filename,struct utimbuf * buf)1350 CWD_API int virtual_utime(const char *filename, struct utimbuf *buf) /* {{{ */
1351 {
1352 cwd_state new_state;
1353 int ret;
1354
1355 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1356 if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
1357 CWD_STATE_FREE_ERR(&new_state);
1358 return -1;
1359 }
1360
1361 #ifdef ZEND_WIN32
1362 ret = win32_utime(new_state.cwd, buf);
1363 #else
1364 ret = utime(new_state.cwd, buf);
1365 #endif
1366
1367 CWD_STATE_FREE_ERR(&new_state);
1368 return ret;
1369 }
1370 /* }}} */
1371 #endif
1372
virtual_chmod(const char * filename,mode_t mode)1373 CWD_API int virtual_chmod(const char *filename, mode_t mode) /* {{{ */
1374 {
1375 cwd_state new_state;
1376 int ret;
1377
1378 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1379 if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
1380 CWD_STATE_FREE_ERR(&new_state);
1381 return -1;
1382 }
1383
1384 #ifdef ZEND_WIN32
1385 {
1386 mode_t _tmp = mode;
1387
1388 mode = 0;
1389
1390 if (_tmp & _S_IREAD) {
1391 mode |= _S_IREAD;
1392 }
1393 if (_tmp & _S_IWRITE) {
1394 mode |= _S_IWRITE;
1395 }
1396 ret = php_win32_ioutil_chmod(new_state.cwd, mode);
1397 }
1398 #else
1399 ret = chmod(new_state.cwd, mode);
1400 #endif
1401
1402 CWD_STATE_FREE_ERR(&new_state);
1403 return ret;
1404 }
1405 /* }}} */
1406
1407 #if !defined(ZEND_WIN32)
virtual_chown(const char * filename,uid_t owner,gid_t group,int link)1408 CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link) /* {{{ */
1409 {
1410 cwd_state new_state;
1411 int ret;
1412
1413 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1414 if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
1415 CWD_STATE_FREE_ERR(&new_state);
1416 return -1;
1417 }
1418
1419 if (link) {
1420 #if HAVE_LCHOWN
1421 ret = lchown(new_state.cwd, owner, group);
1422 #else
1423 ret = -1;
1424 #endif
1425 } else {
1426 ret = chown(new_state.cwd, owner, group);
1427 }
1428
1429 CWD_STATE_FREE_ERR(&new_state);
1430 return ret;
1431 }
1432 /* }}} */
1433 #endif
1434
virtual_open(const char * path,int flags,...)1435 CWD_API int virtual_open(const char *path, int flags, ...) /* {{{ */
1436 {
1437 cwd_state new_state;
1438 int f;
1439
1440 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1441 if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
1442 CWD_STATE_FREE_ERR(&new_state);
1443 return -1;
1444 }
1445
1446 if (flags & O_CREAT) {
1447 mode_t mode;
1448 va_list arg;
1449
1450 va_start(arg, flags);
1451 mode = (mode_t) va_arg(arg, int);
1452 va_end(arg);
1453
1454 #ifdef ZEND_WIN32
1455 f = php_win32_ioutil_open(new_state.cwd, flags, mode);
1456 #else
1457 f = open(new_state.cwd, flags, mode);
1458 #endif
1459 } else {
1460 #ifdef ZEND_WIN32
1461 f = php_win32_ioutil_open(new_state.cwd, flags);
1462 #else
1463 f = open(new_state.cwd, flags);
1464 #endif
1465 }
1466 CWD_STATE_FREE_ERR(&new_state);
1467 return f;
1468 }
1469 /* }}} */
1470
virtual_creat(const char * path,mode_t mode)1471 CWD_API int virtual_creat(const char *path, mode_t mode) /* {{{ */
1472 {
1473 cwd_state new_state;
1474 int f;
1475
1476 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1477 if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
1478 CWD_STATE_FREE_ERR(&new_state);
1479 return -1;
1480 }
1481
1482 f = creat(new_state.cwd, mode);
1483
1484 CWD_STATE_FREE_ERR(&new_state);
1485 return f;
1486 }
1487 /* }}} */
1488
virtual_rename(const char * oldname,const char * newname)1489 CWD_API int virtual_rename(const char *oldname, const char *newname) /* {{{ */
1490 {
1491 cwd_state old_state;
1492 cwd_state new_state;
1493 int retval;
1494
1495 CWD_STATE_COPY(&old_state, &CWDG(cwd));
1496 if (virtual_file_ex(&old_state, oldname, NULL, CWD_EXPAND)) {
1497 CWD_STATE_FREE_ERR(&old_state);
1498 return -1;
1499 }
1500 oldname = old_state.cwd;
1501
1502 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1503 if (virtual_file_ex(&new_state, newname, NULL, CWD_EXPAND)) {
1504 CWD_STATE_FREE_ERR(&old_state);
1505 CWD_STATE_FREE_ERR(&new_state);
1506 return -1;
1507 }
1508 newname = new_state.cwd;
1509
1510 /* rename on windows will fail if newname already exists.
1511 MoveFileEx has to be used */
1512 #ifdef ZEND_WIN32
1513 /* MoveFileEx returns 0 on failure, other way 'round for this function */
1514 retval = php_win32_ioutil_rename(oldname, newname);
1515 #else
1516 retval = rename(oldname, newname);
1517 #endif
1518
1519 CWD_STATE_FREE_ERR(&old_state);
1520 CWD_STATE_FREE_ERR(&new_state);
1521
1522 return retval;
1523 }
1524 /* }}} */
1525
virtual_stat(const char * path,zend_stat_t * buf)1526 CWD_API int virtual_stat(const char *path, zend_stat_t *buf) /* {{{ */
1527 {
1528 cwd_state new_state;
1529 int retval;
1530
1531 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1532 if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
1533 CWD_STATE_FREE_ERR(&new_state);
1534 return -1;
1535 }
1536
1537 retval = php_sys_stat(new_state.cwd, buf);
1538
1539 CWD_STATE_FREE_ERR(&new_state);
1540 return retval;
1541 }
1542 /* }}} */
1543
virtual_lstat(const char * path,zend_stat_t * buf)1544 CWD_API int virtual_lstat(const char *path, zend_stat_t *buf) /* {{{ */
1545 {
1546 cwd_state new_state;
1547 int retval;
1548
1549 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1550 if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
1551 CWD_STATE_FREE_ERR(&new_state);
1552 return -1;
1553 }
1554
1555 retval = php_sys_lstat(new_state.cwd, buf);
1556
1557 CWD_STATE_FREE_ERR(&new_state);
1558 return retval;
1559 }
1560 /* }}} */
1561
virtual_unlink(const char * path)1562 CWD_API int virtual_unlink(const char *path) /* {{{ */
1563 {
1564 cwd_state new_state;
1565 int retval;
1566
1567 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1568 if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
1569 CWD_STATE_FREE_ERR(&new_state);
1570 return -1;
1571 }
1572
1573 #ifdef ZEND_WIN32
1574 retval = php_win32_ioutil_unlink(new_state.cwd);
1575 #else
1576 retval = unlink(new_state.cwd);
1577 #endif
1578
1579 CWD_STATE_FREE_ERR(&new_state);
1580 return retval;
1581 }
1582 /* }}} */
1583
virtual_mkdir(const char * pathname,mode_t mode)1584 CWD_API int virtual_mkdir(const char *pathname, mode_t mode) /* {{{ */
1585 {
1586 cwd_state new_state;
1587 int retval;
1588
1589 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1590 if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH)) {
1591 CWD_STATE_FREE_ERR(&new_state);
1592 return -1;
1593 }
1594
1595 #ifdef ZEND_WIN32
1596 retval = php_win32_ioutil_mkdir(new_state.cwd, mode);
1597 #else
1598 retval = mkdir(new_state.cwd, mode);
1599 #endif
1600 CWD_STATE_FREE_ERR(&new_state);
1601 return retval;
1602 }
1603 /* }}} */
1604
virtual_rmdir(const char * pathname)1605 CWD_API int virtual_rmdir(const char *pathname) /* {{{ */
1606 {
1607 cwd_state new_state;
1608 int retval;
1609
1610 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1611 if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND)) {
1612 CWD_STATE_FREE_ERR(&new_state);
1613 return -1;
1614 }
1615
1616 #ifdef ZEND_WIN32
1617 retval = php_win32_ioutil_rmdir(new_state.cwd);
1618 #else
1619 retval = rmdir(new_state.cwd);
1620 #endif
1621 CWD_STATE_FREE_ERR(&new_state);
1622 return retval;
1623 }
1624 /* }}} */
1625
1626 #ifdef ZEND_WIN32
1627 DIR *opendir(const char *name);
1628 #endif
1629
virtual_opendir(const char * pathname)1630 CWD_API DIR *virtual_opendir(const char *pathname) /* {{{ */
1631 {
1632 cwd_state new_state;
1633 DIR *retval;
1634
1635 CWD_STATE_COPY(&new_state, &CWDG(cwd));
1636 if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
1637 CWD_STATE_FREE_ERR(&new_state);
1638 return NULL;
1639 }
1640
1641 retval = opendir(new_state.cwd);
1642
1643 CWD_STATE_FREE_ERR(&new_state);
1644 return retval;
1645 }
1646 /* }}} */
1647
1648 #ifdef ZEND_WIN32
virtual_popen(const char * command,const char * type)1649 CWD_API FILE *virtual_popen(const char *command, const char *type) /* {{{ */
1650 {
1651 return popen_ex(command, type, CWDG(cwd).cwd, NULL);
1652 }
1653 /* }}} */
1654 #else /* Unix */
virtual_popen(const char * command,const char * type)1655 CWD_API FILE *virtual_popen(const char *command, const char *type) /* {{{ */
1656 {
1657 size_t command_length;
1658 int dir_length, extra = 0;
1659 char *command_line;
1660 char *ptr, *dir;
1661 FILE *retval;
1662
1663 command_length = strlen(command);
1664
1665 dir_length = CWDG(cwd).cwd_length;
1666 dir = CWDG(cwd).cwd;
1667 while (dir_length > 0) {
1668 if (*dir == '\'') extra+=3;
1669 dir++;
1670 dir_length--;
1671 }
1672 dir_length = CWDG(cwd).cwd_length;
1673 dir = CWDG(cwd).cwd;
1674
1675 ptr = command_line = (char *) emalloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1);
1676 memcpy(ptr, "cd ", sizeof("cd ")-1);
1677 ptr += sizeof("cd ")-1;
1678
1679 if (CWDG(cwd).cwd_length == 0) {
1680 *ptr++ = DEFAULT_SLASH;
1681 } else {
1682 *ptr++ = '\'';
1683 while (dir_length > 0) {
1684 switch (*dir) {
1685 case '\'':
1686 *ptr++ = '\'';
1687 *ptr++ = '\\';
1688 *ptr++ = '\'';
1689 /* fall-through */
1690 default:
1691 *ptr++ = *dir;
1692 }
1693 dir++;
1694 dir_length--;
1695 }
1696 *ptr++ = '\'';
1697 }
1698
1699 *ptr++ = ' ';
1700 *ptr++ = ';';
1701 *ptr++ = ' ';
1702
1703 memcpy(ptr, command, command_length+1);
1704 retval = popen(command_line, type);
1705
1706 efree(command_line);
1707 return retval;
1708 }
1709 /* }}} */
1710 #endif
1711
tsrm_realpath(const char * path,char * real_path)1712 CWD_API char *tsrm_realpath(const char *path, char *real_path) /* {{{ */
1713 {
1714 cwd_state new_state;
1715 char cwd[MAXPATHLEN];
1716
1717 /* realpath("") returns CWD */
1718 if (!*path) {
1719 new_state.cwd = (char*)emalloc(1);
1720 new_state.cwd[0] = '\0';
1721 new_state.cwd_length = 0;
1722 if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
1723 path = cwd;
1724 }
1725 } else if (!IS_ABSOLUTE_PATH(path, strlen(path)) &&
1726 VCWD_GETCWD(cwd, MAXPATHLEN)) {
1727 new_state.cwd = estrdup(cwd);
1728 new_state.cwd_length = strlen(cwd);
1729 } else {
1730 new_state.cwd = (char*)emalloc(1);
1731 new_state.cwd[0] = '\0';
1732 new_state.cwd_length = 0;
1733 }
1734
1735 if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
1736 efree(new_state.cwd);
1737 return NULL;
1738 }
1739
1740 if (real_path) {
1741 size_t copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length;
1742 memcpy(real_path, new_state.cwd, copy_len);
1743 real_path[copy_len] = '\0';
1744 efree(new_state.cwd);
1745 return real_path;
1746 } else {
1747 return new_state.cwd;
1748 }
1749 }
1750 /* }}} */
1751