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