1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2007 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,v 1.74.2.9.2.25 2007/04/03 20:20:56 tony2001 Exp $ */
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 : #endif
39 :
40 : #ifdef NETWARE
41 : #include <fsio.h>
42 : #endif
43 :
44 : #ifndef HAVE_REALPATH
45 : #define realpath(x,y) strcpy(y,x)
46 : #endif
47 :
48 : #define VIRTUAL_CWD_DEBUG 0
49 :
50 : #include "TSRM.h"
51 :
52 : /* Only need mutex for popen() in Windows and NetWare because it doesn't chdir() on UNIX */
53 : #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
54 : MUTEX_T cwd_mutex;
55 : #endif
56 :
57 : #ifdef ZTS
58 : ts_rsrc_id cwd_globals_id;
59 : #else
60 : virtual_cwd_globals cwd_globals;
61 : #endif
62 :
63 : cwd_state main_cwd_state; /* True global */
64 :
65 : #ifndef TSRM_WIN32
66 : #include <unistd.h>
67 : #else
68 : #include <direct.h>
69 : #endif
70 :
71 : #ifndef S_ISDIR
72 : #define S_ISDIR(mode) ((mode) & _S_IFDIR)
73 : #endif
74 :
75 : #ifndef S_ISREG
76 : #define S_ISREG(mode) ((mode) & _S_IFREG)
77 : #endif
78 :
79 : #ifdef TSRM_WIN32
80 : #include <tchar.h>
81 : #define tsrm_strtok_r(a,b,c) _tcstok((a),(b))
82 : #define TOKENIZER_STRING "/\\"
83 :
84 : static int php_check_dots(const char *element, int n)
85 : {
86 : while (n-- > 0) if (element[n] != '.') break;
87 :
88 : return (n != -1);
89 : }
90 :
91 : #define IS_DIRECTORY_UP(element, len) \
92 : (len >= 2 && !php_check_dots(element, len))
93 :
94 : #define IS_DIRECTORY_CURRENT(element, len) \
95 : (len == 1 && element[0] == '.')
96 :
97 : #elif defined(NETWARE)
98 : /* NetWare has strtok() (in LibC) and allows both slashes in paths, like Windows --
99 : but rest of the stuff is like Unix */
100 : /* strtok() call in LibC is abending when used in a different address space -- hence using
101 : PHP's version itself for now */
102 : /*#define tsrm_strtok_r(a,b,c) strtok((a),(b))*/
103 : #define TOKENIZER_STRING "/\\"
104 :
105 : #else
106 : #define TOKENIZER_STRING "/"
107 : #endif
108 :
109 :
110 : /* default macros */
111 :
112 : #ifndef IS_DIRECTORY_UP
113 : #define IS_DIRECTORY_UP(element, len) \
114 : (len == 2 && element[0] == '.' && element[1] == '.')
115 : #endif
116 :
117 : #ifndef IS_DIRECTORY_CURRENT
118 : #define IS_DIRECTORY_CURRENT(element, len) \
119 : (len == 1 && element[0] == '.')
120 : #endif
121 :
122 : /* define this to check semantics */
123 : #define IS_DIR_OK(s) (1)
124 :
125 : #ifndef IS_DIR_OK
126 : #define IS_DIR_OK(state) (php_is_dir_ok(state) == 0)
127 : #endif
128 :
129 :
130 : #define CWD_STATE_COPY(d, s) \
131 : (d)->cwd_length = (s)->cwd_length; \
132 : (d)->cwd = (char *) malloc((s)->cwd_length+1); \
133 : memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1);
134 :
135 : #define CWD_STATE_FREE(s) \
136 : free((s)->cwd);
137 :
138 : #ifdef TSRM_WIN32
139 : CWD_API int php_sys_stat(const char *path, struct stat *buf)
140 : {
141 : WIN32_FILE_ATTRIBUTE_DATA data;
142 : __int64 t;
143 :
144 : if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) {
145 : return stat(path, buf);
146 : }
147 :
148 : if (path[1] == ':') {
149 : if (path[0] >= 'A' && path[0] <= 'Z') {
150 : buf->st_dev = buf->st_rdev = path[0] - 'A';
151 : } else {
152 : buf->st_dev = buf->st_rdev = path[0] - 'a';
153 : }
154 : } else {
155 : char cur_path[MAXPATHLEN+1];
156 : DWORD len = sizeof(cur_path);
157 : char *tmp = cur_path;
158 :
159 : while(1) {
160 : DWORD r = GetCurrentDirectory(len, tmp);
161 : if (r < len) {
162 : if (tmp[1] == ':') {
163 : if (path[0] >= 'A' && path[0] <= 'Z') {
164 : buf->st_dev = buf->st_rdev = path[0] - 'A';
165 : } else {
166 : buf->st_dev = buf->st_rdev = path[0] - 'a';
167 : }
168 : } else {
169 : buf->st_dev = buf->st_rdev = -1;
170 : }
171 : break;
172 : } else if (!r) {
173 : buf->st_dev = buf->st_rdev = -1;
174 : break;
175 : } else {
176 : len = r+1;
177 : tmp = (char*)malloc(len);
178 : }
179 : }
180 : if (tmp != cur_path) {
181 : free(tmp);
182 : }
183 : }
184 : buf->st_uid = buf->st_gid = buf->st_ino = 0;
185 : buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG;
186 : 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));
187 : if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
188 : int len = strlen(path);
189 :
190 : if (path[len-4] == '.') {
191 : if (_memicmp(path+len-3, "exe", 3) == 0 ||
192 : _memicmp(path+len-3, "com", 3) == 0 ||
193 : _memicmp(path+len-3, "bat", 3) == 0 ||
194 : _memicmp(path+len-3, "cmd", 3) == 0) {
195 : buf->st_mode |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
196 : }
197 : }
198 : }
199 : buf->st_nlink = 1;
200 : t = data.nFileSizeHigh;
201 : t = t << 32;
202 : t |= data.nFileSizeLow;
203 : buf->st_size = t;
204 : t = data.ftLastAccessTime.dwHighDateTime;
205 : t = t << 32;
206 : t |= data.ftLastAccessTime.dwLowDateTime;
207 : buf->st_atime = (unsigned long)((t / 10000000) - 11644473600);
208 : t = data.ftCreationTime.dwHighDateTime;
209 : t = t << 32;
210 : t |= data.ftCreationTime.dwLowDateTime;
211 : buf->st_ctime = (unsigned long)((t / 10000000) - 11644473600);
212 : t = data.ftLastWriteTime.dwHighDateTime;
213 : t = t << 32;
214 : t |= data.ftLastWriteTime.dwLowDateTime;
215 : buf->st_mtime = (unsigned long)((t / 10000000) - 11644473600);
216 : return 0;
217 : }
218 : #endif
219 :
220 : static int php_is_dir_ok(const cwd_state *state)
221 0 : {
222 : struct stat buf;
223 :
224 0 : if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode))
225 0 : return (0);
226 :
227 0 : return (1);
228 : }
229 :
230 : static int php_is_file_ok(const cwd_state *state)
231 0 : {
232 : struct stat buf;
233 :
234 0 : if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode))
235 0 : return (0);
236 :
237 0 : return (1);
238 : }
239 :
240 : static void cwd_globals_ctor(virtual_cwd_globals *cwd_globals TSRMLS_DC)
241 220 : {
242 220 : CWD_STATE_COPY(&cwd_globals->cwd, &main_cwd_state);
243 220 : cwd_globals->realpath_cache_size = 0;
244 220 : cwd_globals->realpath_cache_size_limit = REALPATH_CACHE_SIZE;
245 220 : cwd_globals->realpath_cache_ttl = REALPATH_CACHE_TTL;
246 220 : memset(cwd_globals->realpath_cache, 0, sizeof(cwd_globals->realpath_cache));
247 220 : }
248 :
249 : static void cwd_globals_dtor(virtual_cwd_globals *cwd_globals TSRMLS_DC)
250 219 : {
251 219 : CWD_STATE_FREE(&cwd_globals->cwd);
252 219 : realpath_cache_clean(TSRMLS_C);
253 219 : }
254 :
255 : static char *tsrm_strndup(const char *s, size_t length)
256 239 : {
257 : char *p;
258 :
259 239 : p = (char *) malloc(length+1);
260 239 : if (!p) {
261 0 : return (char *)NULL;
262 : }
263 239 : if (length) {
264 239 : memcpy(p,s,length);
265 : }
266 239 : p[length]=0;
267 239 : return p;
268 : }
269 :
270 : CWD_API void virtual_cwd_startup(void)
271 220 : {
272 : char cwd[MAXPATHLEN];
273 : char *result;
274 :
275 : #ifdef NETWARE
276 : result = getcwdpath(cwd, NULL, 1);
277 : if(result)
278 : {
279 : char *c=cwd;
280 : while(c = strchr(c, '\\'))
281 : {
282 : *c='/';
283 : ++c;
284 : }
285 : }
286 : #else
287 220 : result = getcwd(cwd, sizeof(cwd));
288 : #endif
289 220 : if (!result) {
290 0 : cwd[0] = '\0';
291 : }
292 220 : main_cwd_state.cwd = strdup(cwd);
293 220 : main_cwd_state.cwd_length = strlen(cwd);
294 :
295 : #ifdef ZTS
296 : ts_allocate_id(&cwd_globals_id, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor);
297 : #else
298 220 : cwd_globals_ctor(&cwd_globals TSRMLS_CC);
299 : #endif
300 :
301 : #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
302 : cwd_mutex = tsrm_mutex_alloc();
303 : #endif
304 220 : }
305 :
306 : CWD_API void virtual_cwd_shutdown(void)
307 219 : {
308 : #ifndef ZTS
309 219 : cwd_globals_dtor(&cwd_globals TSRMLS_CC);
310 : #endif
311 : #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
312 : tsrm_mutex_free(cwd_mutex);
313 : #endif
314 :
315 219 : free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */
316 219 : }
317 :
318 : CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC)
319 0 : {
320 : cwd_state *state;
321 :
322 0 : state = &CWDG(cwd);
323 :
324 0 : if (state->cwd_length == 0) {
325 : char *retval;
326 :
327 0 : *length = 1;
328 0 : retval = (char *) malloc(2);
329 0 : retval[0] = DEFAULT_SLASH;
330 0 : retval[1] = '\0';
331 0 : return retval;
332 : }
333 :
334 : #ifdef TSRM_WIN32
335 : /* If we have something like C: */
336 : if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') {
337 : char *retval;
338 :
339 : *length = state->cwd_length+1;
340 : retval = (char *) malloc(*length+1);
341 : memcpy(retval, state->cwd, *length);
342 : retval[*length-1] = DEFAULT_SLASH;
343 : retval[*length] = '\0';
344 : return retval;
345 : }
346 : #endif
347 0 : *length = state->cwd_length;
348 0 : return strdup(state->cwd);
349 : }
350 :
351 :
352 : /* Same semantics as UNIX getcwd() */
353 : CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC)
354 0 : {
355 : size_t length;
356 : char *cwd;
357 :
358 0 : cwd = virtual_getcwd_ex(&length TSRMLS_CC);
359 :
360 0 : if (buf == NULL) {
361 0 : return cwd;
362 : }
363 0 : if (length > size-1) {
364 0 : free(cwd);
365 0 : errno = ERANGE; /* Is this OK? */
366 0 : return NULL;
367 : }
368 0 : memcpy(buf, cwd, length+1);
369 0 : free(cwd);
370 0 : return buf;
371 : }
372 :
373 : static inline unsigned long realpath_cache_key(const char *path, int path_len)
374 2259 : {
375 : register unsigned long h;
376 2259 : const char *e = path + path_len;
377 :
378 116916 : for (h = 2166136261U; path < e;) {
379 112398 : h *= 16777619;
380 112398 : h ^= *path++;
381 : }
382 :
383 2259 : return h;
384 : }
385 :
386 : CWD_API void realpath_cache_clean(TSRMLS_D)
387 474 : {
388 : int i;
389 :
390 485850 : for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) {
391 485376 : realpath_cache_bucket *p = CWDG(realpath_cache)[i];
392 971820 : while (p != NULL) {
393 1068 : realpath_cache_bucket *r = p;
394 1068 : p = p->next;
395 1068 : free(r);
396 : }
397 485376 : CWDG(realpath_cache)[i] = NULL;
398 : }
399 474 : CWDG(realpath_cache_size) = 0;
400 474 : }
401 :
402 : CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC)
403 0 : {
404 0 : unsigned long key = realpath_cache_key(path, path_len);
405 0 : unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
406 0 : realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
407 :
408 0 : while (*bucket != NULL) {
409 0 : if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
410 : memcmp(path, (*bucket)->path, path_len) == 0) {
411 0 : realpath_cache_bucket *r = *bucket;
412 0 : *bucket = (*bucket)->next;
413 0 : CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
414 0 : free(r);
415 0 : return;
416 : } else {
417 0 : bucket = &(*bucket)->next;
418 : }
419 : }
420 : }
421 :
422 : static inline void realpath_cache_add(const char *path, int path_len, const char *realpath, int realpath_len, time_t t TSRMLS_DC)
423 1068 : {
424 1068 : long size = sizeof(realpath_cache_bucket) + path_len + 1 + realpath_len + 1;
425 1068 : if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) {
426 1068 : realpath_cache_bucket *bucket = malloc(size);
427 : unsigned long n;
428 :
429 1068 : bucket->key = realpath_cache_key(path, path_len);
430 1068 : bucket->path = (char*)bucket + sizeof(realpath_cache_bucket);
431 1068 : memcpy(bucket->path, path, path_len+1);
432 1068 : bucket->path_len = path_len;
433 1068 : bucket->realpath = bucket->path + (path_len + 1);
434 1068 : memcpy(bucket->realpath, realpath, realpath_len+1);
435 1068 : bucket->realpath_len = realpath_len;
436 1068 : bucket->expires = t + CWDG(realpath_cache_ttl);
437 1068 : n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
438 1068 : bucket->next = CWDG(realpath_cache)[n];
439 1068 : CWDG(realpath_cache)[n] = bucket;
440 1068 : CWDG(realpath_cache_size) += size;
441 : }
442 1068 : }
443 :
444 : static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC)
445 1191 : {
446 1191 : unsigned long key = realpath_cache_key(path, path_len);
447 1191 : unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
448 1191 : realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
449 :
450 2408 : while (*bucket != NULL) {
451 149 : if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) {
452 0 : realpath_cache_bucket *r = *bucket;
453 0 : *bucket = (*bucket)->next;
454 0 : CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
455 0 : free(r);
456 149 : } else if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
457 : memcmp(path, (*bucket)->path, path_len) == 0) {
458 123 : return *bucket;
459 : } else {
460 26 : bucket = &(*bucket)->next;
461 : }
462 : }
463 1068 : return NULL;
464 : }
465 :
466 :
467 : /* Resolve path relatively to state and put the real path into state */
468 : /* returns 0 for ok, 1 for error */
469 : CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath)
470 1192 : {
471 1192 : int path_length = strlen(path);
472 : cwd_state old_state;
473 : char orig_path[MAXPATHLEN];
474 : realpath_cache_bucket *bucket;
475 1192 : time_t t = 0;
476 : int ret;
477 : int use_cache;
478 1192 : int use_relative_path = 0;
479 : TSRMLS_FETCH();
480 :
481 1192 : use_cache = ((use_realpath != CWD_EXPAND) && CWDG(realpath_cache_size_limit));
482 :
483 1192 : if (path_length == 0)
484 1 : return (0);
485 1191 : if (path_length >= MAXPATHLEN)
486 0 : return (1);
487 :
488 : #if VIRTUAL_CWD_DEBUG
489 : fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path);
490 : #endif
491 :
492 : /* cwd_length can be 0 when getcwd() fails.
493 : * This can happen under solaris when a dir does not have read permissions
494 : * but *does* have execute permissions */
495 1191 : if (!IS_ABSOLUTE_PATH(path, path_length)) {
496 286 : if (state->cwd_length == 0) {
497 0 : use_cache = 0;
498 0 : use_relative_path = 1;
499 : } else {
500 : int orig_path_len;
501 286 : int state_cwd_length = state->cwd_length;
502 :
503 : #ifdef TSRM_WIN32
504 : if (IS_SLASH(path[0])) {
505 : state_cwd_length = 2;
506 : }
507 : #endif
508 286 : orig_path_len = path_length + state_cwd_length + 1;
509 286 : if (orig_path_len >= MAXPATHLEN) {
510 0 : return 1;
511 : }
512 286 : memcpy(orig_path, state->cwd, state_cwd_length);
513 286 : orig_path[state_cwd_length] = DEFAULT_SLASH;
514 286 : memcpy(orig_path + state_cwd_length + 1, path, path_length + 1);
515 286 : path = orig_path;
516 286 : path_length = orig_path_len;
517 : }
518 : }
519 :
520 1191 : if (use_cache) {
521 1191 : t = CWDG(realpath_cache_ttl)?time(NULL):0;
522 1191 : if ((bucket = realpath_cache_find(path, path_length, t TSRMLS_CC)) != NULL) {
523 123 : int len = bucket->realpath_len;
524 :
525 123 : CWD_STATE_COPY(&old_state, state);
526 123 : state->cwd = (char *) realloc(state->cwd, len+1);
527 123 : memcpy(state->cwd, bucket->realpath, len+1);
528 123 : state->cwd_length = len;
529 123 : if (verify_path && verify_path(state)) {
530 0 : CWD_STATE_FREE(state);
531 0 : *state = old_state;
532 0 : return 1;
533 : } else {
534 123 : CWD_STATE_FREE(&old_state);
535 123 : return 0;
536 : }
537 : }
538 : }
539 :
540 1068 : if (use_realpath != CWD_EXPAND) {
541 : #if !defined(TSRM_WIN32) && !defined(NETWARE)
542 : char resolved_path[MAXPATHLEN];
543 :
544 1068 : if (!realpath(path, resolved_path)) { /* Note: Not threadsafe on older *BSD's */
545 239 : if (use_realpath == CWD_REALPATH) {
546 0 : return 1;
547 : }
548 239 : goto no_realpath;
549 : }
550 829 : CWD_STATE_COPY(&old_state, state);
551 :
552 829 : state->cwd_length = strlen(resolved_path);
553 829 : state->cwd = (char *) realloc(state->cwd, state->cwd_length+1);
554 829 : memcpy(state->cwd, resolved_path, state->cwd_length+1);
555 : #else
556 : goto no_realpath;
557 : #endif
558 : } else {
559 : char *ptr, *path_copy, *free_path;
560 : char *tok;
561 : int ptr_length;
562 : #ifdef TSRM_WIN32
563 : int is_unc;
564 : #endif
565 239 : no_realpath:
566 :
567 239 : free_path = path_copy = tsrm_strndup(path, path_length);
568 239 : CWD_STATE_COPY(&old_state, state);
569 :
570 : #ifdef TSRM_WIN32
571 : is_unc = 0;
572 : if (path_length >= 2 && path[1] == ':') {
573 : state->cwd = (char *) realloc(state->cwd, 2 + 1);
574 : state->cwd[0] = toupper(path[0]);
575 : state->cwd[1] = ':';
576 : state->cwd[2] = '\0';
577 : state->cwd_length = 2;
578 : path_copy += 2;
579 : } else if (IS_UNC_PATH(path, path_length)) {
580 : state->cwd = (char *) realloc(state->cwd, 1 + 1);
581 : state->cwd[0] = DEFAULT_SLASH;
582 : state->cwd[1] = '\0';
583 : state->cwd_length = 1;
584 : path_copy += 2;
585 : is_unc = 2;
586 : } else {
587 : #endif
588 239 : state->cwd = (char *) realloc(state->cwd, 1);
589 239 : state->cwd[0] = '\0';
590 239 : state->cwd_length = 0;
591 : #ifdef TSRM_WIN32
592 : }
593 : #endif
594 :
595 239 : tok = NULL;
596 239 : ptr = tsrm_strtok_r(path_copy, TOKENIZER_STRING, &tok);
597 2147 : while (ptr) {
598 1669 : ptr_length = strlen(ptr);
599 :
600 1669 : if (IS_DIRECTORY_UP(ptr, ptr_length)) {
601 : char save;
602 :
603 0 : if (use_relative_path) {
604 0 : CWD_STATE_FREE(state);
605 0 : *state = old_state;
606 0 : return 1;
607 : }
608 :
609 0 : save = DEFAULT_SLASH;
610 :
611 : #define PREVIOUS state->cwd[state->cwd_length - 1]
612 :
613 0 : while (IS_ABSOLUTE_PATH(state->cwd, state->cwd_length) &&
614 : !IS_SLASH(PREVIOUS)) {
615 0 : save = PREVIOUS;
616 0 : PREVIOUS = '\0';
617 0 : state->cwd_length--;
618 : }
619 :
620 0 : if (!IS_ABSOLUTE_PATH(state->cwd, state->cwd_length)) {
621 0 : state->cwd[state->cwd_length++] = save;
622 0 : state->cwd[state->cwd_length] = '\0';
623 : } else {
624 0 : PREVIOUS = '\0';
625 0 : state->cwd_length--;
626 : }
627 1669 : } else if (!IS_DIRECTORY_CURRENT(ptr, ptr_length)) {
628 1669 : if (use_relative_path) {
629 0 : state->cwd = (char *) realloc(state->cwd, state->cwd_length+ptr_length+1);
630 0 : use_relative_path = 0;
631 : } else {
632 1669 : state->cwd = (char *) realloc(state->cwd, state->cwd_length+ptr_length+1+1);
633 : #ifdef TSRM_WIN32
634 : /* Windows 9x will consider C:\\Foo as a network path. Avoid it. */
635 : if (state->cwd_length < 2 ||
636 : (state->cwd[state->cwd_length-1]!='\\' && state->cwd[state->cwd_length-1]!='/') ||
637 : IsDBCSLeadByte(state->cwd[state->cwd_length-2])) {
638 : state->cwd[state->cwd_length++] = DEFAULT_SLASH;
639 : }
640 : #elif defined(NETWARE)
641 : /*
642 : Below code keeps appending to state->cwd a File system seperator
643 : cases where this appending should not happen is given below,
644 : a) sys: should just be left as it is
645 : b) sys:system should just be left as it is,
646 : Colon is allowed only in the first token as volume names alone can have the : in their names.
647 : Files and Directories cannot have : in their names
648 : So the check goes like this,
649 : For second token and above simply append the DEFAULT_SLASH to the state->cwd.
650 : For first token check for the existence of :
651 : if it exists don't append the DEFAULT_SLASH to the state->cwd.
652 : */
653 : if(((state->cwd_length == 0) && (strchr(ptr, ':') == NULL)) || (state->cwd_length > 0)) {
654 : state->cwd[state->cwd_length++] = DEFAULT_SLASH;
655 : }
656 : #else
657 1669 : state->cwd[state->cwd_length++] = DEFAULT_SLASH;
658 : #endif
659 : }
660 1669 : memcpy(&state->cwd[state->cwd_length], ptr, ptr_length+1);
661 :
662 : #ifdef TSRM_WIN32
663 : if (use_realpath != CWD_EXPAND) {
664 : WIN32_FIND_DATA data;
665 : HANDLE hFind;
666 :
667 : if ((hFind = FindFirstFile(state->cwd, &data)) != INVALID_HANDLE_VALUE) {
668 : int length = strlen(data.cFileName);
669 :
670 : if (length != ptr_length) {
671 : state->cwd = (char *) realloc(state->cwd, state->cwd_length+length+1);
672 : }
673 : memcpy(&state->cwd[state->cwd_length], data.cFileName, length+1);
674 : ptr_length = length;
675 : FindClose(hFind);
676 : ret = 0;
677 : } else if (use_realpath == CWD_REALPATH) {
678 : if (is_unc) {
679 : /* skip share name */
680 : is_unc--;
681 : ret = 0;
682 : } else {
683 : ret = 1;
684 : }
685 : }
686 : }
687 : #endif
688 :
689 1669 : state->cwd_length += ptr_length;
690 : }
691 1669 : ptr = tsrm_strtok_r(NULL, TOKENIZER_STRING, &tok);
692 : }
693 :
694 239 : free(free_path);
695 :
696 239 : if ((use_realpath == CWD_REALPATH) && ret) {
697 0 : CWD_STATE_FREE(state);
698 0 : *state = old_state;
699 0 : return 1;
700 : }
701 :
702 239 : if (state->cwd_length == COPY_WHEN_ABSOLUTE(state->cwd)) {
703 0 : state->cwd = (char *) realloc(state->cwd, state->cwd_length+1+1);
704 0 : state->cwd[state->cwd_length] = DEFAULT_SLASH;
705 0 : state->cwd[state->cwd_length+1] = '\0';
706 0 : state->cwd_length++;
707 : }
708 : }
709 :
710 1068 : if (use_cache) {
711 1068 : realpath_cache_add(path, path_length, state->cwd, state->cwd_length, t TSRMLS_CC);
712 : }
713 :
714 1068 : if (verify_path && verify_path(state)) {
715 0 : CWD_STATE_FREE(state);
716 0 : *state = old_state;
717 0 : ret = 1;
718 : } else {
719 1068 : CWD_STATE_FREE(&old_state);
720 1068 : ret = 0;
721 : }
722 :
723 : #if VIRTUAL_CWD_DEBUG
724 : fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd);
725 : #endif
726 1068 : return (ret);
727 : }
728 :
729 : CWD_API int virtual_chdir(const char *path TSRMLS_DC)
730 0 : {
731 0 : return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH)?-1:0;
732 : }
733 :
734 : CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC)
735 218 : {
736 218 : int length = strlen(path);
737 : char *temp;
738 : int retval;
739 :
740 218 : if (length == 0) {
741 0 : return 1; /* Can't cd to empty string */
742 : }
743 5030 : while(--length >= 0 && !IS_SLASH(path[length])) {
744 : }
745 :
746 218 : if (length == -1) {
747 : /* No directory only file name */
748 0 : errno = ENOENT;
749 0 : return -1;
750 : }
751 :
752 218 : if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */
753 0 : length++;
754 : }
755 218 : temp = (char *) tsrm_do_alloca(length+1);
756 218 : memcpy(temp, path, length);
757 218 : temp[length] = 0;
758 : #if VIRTUAL_CWD_DEBUG
759 : fprintf (stderr, "Changing directory to %s\n", temp);
760 : #endif
761 218 : retval = p_chdir(temp TSRMLS_CC);
762 : tsrm_free_alloca(temp);
763 218 : return retval;
764 : }
765 :
766 : CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC)
767 0 : {
768 : cwd_state new_state;
769 : char *retval;
770 :
771 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
772 :
773 0 : if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)==0) {
774 0 : int len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length;
775 :
776 0 : memcpy(real_path, new_state.cwd, len);
777 0 : real_path[len] = '\0';
778 0 : retval = real_path;
779 : } else {
780 0 : retval = NULL;
781 : }
782 :
783 0 : CWD_STATE_FREE(&new_state);
784 :
785 0 : return retval;
786 : }
787 :
788 : CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC)
789 0 : {
790 : cwd_state new_state;
791 : int retval;
792 :
793 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
794 0 : retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH);
795 :
796 0 : *filepath = new_state.cwd;
797 :
798 0 : return retval;
799 :
800 : }
801 :
802 : CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC)
803 0 : {
804 0 : return virtual_filepath_ex(path, filepath, php_is_file_ok TSRMLS_CC);
805 : }
806 :
807 : CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC)
808 0 : {
809 : cwd_state new_state;
810 : FILE *f;
811 :
812 0 : if (path[0] == '\0') { /* Fail to open empty path */
813 0 : return NULL;
814 : }
815 :
816 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
817 0 : if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
818 0 : CWD_STATE_FREE(&new_state);
819 0 : return NULL;
820 : }
821 :
822 0 : f = fopen(new_state.cwd, mode);
823 :
824 0 : CWD_STATE_FREE(&new_state);
825 0 : return f;
826 : }
827 :
828 : CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC)
829 0 : {
830 : cwd_state new_state;
831 : int ret;
832 :
833 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
834 0 : if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
835 0 : CWD_STATE_FREE(&new_state);
836 0 : return -1;
837 : }
838 :
839 : #if defined(TSRM_WIN32)
840 : ret = tsrm_win32_access(new_state.cwd, mode);
841 : #else
842 0 : ret = access(new_state.cwd, mode);
843 : #endif
844 :
845 0 : CWD_STATE_FREE(&new_state);
846 :
847 0 : return ret;
848 : }
849 :
850 :
851 : #if HAVE_UTIME
852 : CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC)
853 0 : {
854 : cwd_state new_state;
855 : int ret;
856 :
857 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
858 0 : if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
859 0 : CWD_STATE_FREE(&new_state);
860 0 : return -1;
861 : }
862 :
863 0 : ret = utime(new_state.cwd, buf);
864 :
865 0 : CWD_STATE_FREE(&new_state);
866 0 : return ret;
867 : }
868 : #endif
869 :
870 : CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC)
871 0 : {
872 : cwd_state new_state;
873 : int ret;
874 :
875 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
876 0 : if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
877 0 : CWD_STATE_FREE(&new_state);
878 0 : return -1;
879 : }
880 :
881 0 : ret = chmod(new_state.cwd, mode);
882 :
883 0 : CWD_STATE_FREE(&new_state);
884 0 : return ret;
885 : }
886 :
887 : #if !defined(TSRM_WIN32) && !defined(NETWARE)
888 : CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC)
889 0 : {
890 : cwd_state new_state;
891 : int ret;
892 :
893 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
894 0 : if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
895 0 : CWD_STATE_FREE(&new_state);
896 0 : return -1;
897 : }
898 :
899 0 : if (link) {
900 : #if HAVE_LCHOWN
901 0 : ret = lchown(new_state.cwd, owner, group);
902 : #else
903 : ret = -1;
904 : #endif
905 : } else {
906 0 : ret = chown(new_state.cwd, owner, group);
907 : }
908 :
909 0 : CWD_STATE_FREE(&new_state);
910 0 : return ret;
911 : }
912 : #endif
913 :
914 : CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...)
915 0 : {
916 : cwd_state new_state;
917 : int f;
918 :
919 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
920 0 : if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
921 0 : CWD_STATE_FREE(&new_state);
922 0 : return -1;
923 : }
924 :
925 0 : if (flags & O_CREAT) {
926 : mode_t mode;
927 : va_list arg;
928 :
929 0 : va_start(arg, flags);
930 0 : mode = (mode_t) va_arg(arg, int);
931 0 : va_end(arg);
932 :
933 0 : f = open(new_state.cwd, flags, mode);
934 : } else {
935 0 : f = open(new_state.cwd, flags);
936 : }
937 0 : CWD_STATE_FREE(&new_state);
938 0 : return f;
939 : }
940 :
941 : CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC)
942 0 : {
943 : cwd_state new_state;
944 : int f;
945 :
946 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
947 0 : if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
948 0 : CWD_STATE_FREE(&new_state);
949 0 : return -1;
950 : }
951 :
952 0 : f = creat(new_state.cwd, mode);
953 :
954 0 : CWD_STATE_FREE(&new_state);
955 0 : return f;
956 : }
957 :
958 : CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC)
959 0 : {
960 : cwd_state old_state;
961 : cwd_state new_state;
962 : int retval;
963 :
964 0 : CWD_STATE_COPY(&old_state, &CWDG(cwd));
965 0 : if (virtual_file_ex(&old_state, oldname, NULL, CWD_REALPATH)) {
966 0 : CWD_STATE_FREE(&old_state);
967 0 : return -1;
968 : }
969 0 : oldname = old_state.cwd;
970 :
971 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
972 0 : if (virtual_file_ex(&new_state, newname, NULL, CWD_FILEPATH)) {
973 0 : CWD_STATE_FREE(&old_state);
974 0 : CWD_STATE_FREE(&new_state);
975 0 : return -1;
976 : }
977 0 : newname = new_state.cwd;
978 :
979 0 : retval = rename(oldname, newname);
980 :
981 0 : CWD_STATE_FREE(&old_state);
982 0 : CWD_STATE_FREE(&new_state);
983 :
984 0 : return retval;
985 : }
986 :
987 : CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC)
988 0 : {
989 : cwd_state new_state;
990 : int retval;
991 :
992 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
993 0 : if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
994 0 : CWD_STATE_FREE(&new_state);
995 0 : return -1;
996 : }
997 :
998 0 : retval = php_sys_stat(new_state.cwd, buf);
999 :
1000 0 : CWD_STATE_FREE(&new_state);
1001 0 : return retval;
1002 : }
1003 :
1004 : #if !defined(TSRM_WIN32)
1005 : CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC)
1006 0 : {
1007 : cwd_state new_state;
1008 : int retval;
1009 :
1010 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
1011 0 : if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
1012 0 : CWD_STATE_FREE(&new_state);
1013 0 : return -1;
1014 : }
1015 :
1016 0 : retval = lstat(new_state.cwd, buf);
1017 :
1018 0 : CWD_STATE_FREE(&new_state);
1019 0 : return retval;
1020 : }
1021 : #endif
1022 :
1023 : CWD_API int virtual_unlink(const char *path TSRMLS_DC)
1024 0 : {
1025 : cwd_state new_state;
1026 : int retval;
1027 :
1028 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
1029 0 : if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
1030 0 : CWD_STATE_FREE(&new_state);
1031 0 : return -1;
1032 : }
1033 :
1034 0 : retval = unlink(new_state.cwd);
1035 :
1036 0 : CWD_STATE_FREE(&new_state);
1037 0 : return retval;
1038 : }
1039 :
1040 : CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC)
1041 0 : {
1042 : cwd_state new_state;
1043 : int retval;
1044 :
1045 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
1046 0 : if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH)) {
1047 0 : CWD_STATE_FREE(&new_state);
1048 0 : return -1;
1049 : }
1050 :
1051 : #ifdef TSRM_WIN32
1052 : retval = mkdir(new_state.cwd);
1053 : #else
1054 0 : retval = mkdir(new_state.cwd, mode);
1055 : #endif
1056 0 : CWD_STATE_FREE(&new_state);
1057 0 : return retval;
1058 : }
1059 :
1060 : CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC)
1061 0 : {
1062 : cwd_state new_state;
1063 : int retval;
1064 :
1065 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
1066 0 : if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND)) {
1067 0 : CWD_STATE_FREE(&new_state);
1068 0 : return -1;
1069 : }
1070 :
1071 0 : retval = rmdir(new_state.cwd);
1072 :
1073 0 : CWD_STATE_FREE(&new_state);
1074 0 : return retval;
1075 : }
1076 :
1077 : #ifdef TSRM_WIN32
1078 : DIR *opendir(const char *name);
1079 : #endif
1080 :
1081 : CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC)
1082 0 : {
1083 : cwd_state new_state;
1084 : DIR *retval;
1085 :
1086 0 : CWD_STATE_COPY(&new_state, &CWDG(cwd));
1087 0 : if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
1088 0 : CWD_STATE_FREE(&new_state);
1089 0 : return NULL;
1090 : }
1091 :
1092 0 : retval = opendir(new_state.cwd);
1093 :
1094 0 : CWD_STATE_FREE(&new_state);
1095 0 : return retval;
1096 : }
1097 :
1098 : #ifdef TSRM_WIN32
1099 :
1100 : CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC)
1101 : {
1102 : return popen_ex(command, type, CWDG(cwd).cwd, NULL);
1103 : }
1104 :
1105 : #elif defined(NETWARE)
1106 :
1107 : /* On NetWare, the trick of prepending "cd cwd; " doesn't work so we need to perform
1108 : a VCWD_CHDIR() and mutex it
1109 : */
1110 : CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC)
1111 : {
1112 : char prev_cwd[MAXPATHLEN];
1113 : char *getcwd_result;
1114 : FILE *retval;
1115 :
1116 : getcwd_result = VCWD_GETCWD(prev_cwd, MAXPATHLEN);
1117 : if (!getcwd_result) {
1118 : return NULL;
1119 : }
1120 :
1121 : #ifdef ZTS
1122 : tsrm_mutex_lock(cwd_mutex);
1123 : #endif
1124 :
1125 : VCWD_CHDIR(CWDG(cwd).cwd);
1126 : retval = popen(command, type);
1127 : VCWD_CHDIR(prev_cwd);
1128 :
1129 : #ifdef ZTS
1130 : tsrm_mutex_unlock(cwd_mutex);
1131 : #endif
1132 :
1133 : return retval;
1134 : }
1135 :
1136 : #else /* Unix */
1137 :
1138 : CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC)
1139 0 : {
1140 : int command_length;
1141 0 : int dir_length, extra = 0;
1142 : char *command_line;
1143 : char *ptr, *dir;
1144 : FILE *retval;
1145 :
1146 0 : command_length = strlen(command);
1147 :
1148 0 : dir_length = CWDG(cwd).cwd_length;
1149 0 : dir = CWDG(cwd).cwd;
1150 0 : while (dir_length > 0) {
1151 0 : if (*dir == '\'') extra+=3;
1152 0 : dir++;
1153 0 : dir_length--;
1154 : }
1155 0 : dir_length = CWDG(cwd).cwd_length;
1156 0 : dir = CWDG(cwd).cwd;
1157 :
1158 0 : ptr = command_line = (char *) malloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1);
1159 0 : if (!command_line) {
1160 0 : return NULL;
1161 : }
1162 0 : memcpy(ptr, "cd ", sizeof("cd ")-1);
1163 0 : ptr += sizeof("cd ")-1;
1164 :
1165 0 : if (CWDG(cwd).cwd_length == 0) {
1166 0 : *ptr++ = DEFAULT_SLASH;
1167 : } else {
1168 0 : *ptr++ = '\'';
1169 0 : while (dir_length > 0) {
1170 0 : switch (*dir) {
1171 : case '\'':
1172 0 : *ptr++ = '\'';
1173 0 : *ptr++ = '\\';
1174 0 : *ptr++ = '\'';
1175 : /* fall-through */
1176 : default:
1177 0 : *ptr++ = *dir;
1178 : }
1179 0 : dir++;
1180 0 : dir_length--;
1181 : }
1182 0 : *ptr++ = '\'';
1183 : }
1184 :
1185 0 : *ptr++ = ' ';
1186 0 : *ptr++ = ';';
1187 0 : *ptr++ = ' ';
1188 :
1189 0 : memcpy(ptr, command, command_length+1);
1190 0 : retval = popen(command_line, type);
1191 :
1192 0 : free(command_line);
1193 0 : return retval;
1194 : }
1195 :
1196 : #endif
1197 :
1198 : CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC)
1199 330 : {
1200 : cwd_state new_state;
1201 : char cwd[MAXPATHLEN];
1202 :
1203 332 : if (!IS_ABSOLUTE_PATH(path, strlen(path)) &&
1204 : VCWD_GETCWD(cwd, MAXPATHLEN)) {
1205 2 : new_state.cwd = strdup(cwd);
1206 2 : new_state.cwd_length = strlen(cwd);
1207 : } else {
1208 328 : new_state.cwd = (char*)malloc(1);
1209 328 : new_state.cwd[0] = '\0';
1210 328 : new_state.cwd_length = 0;
1211 : }
1212 :
1213 330 : if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
1214 0 : free(new_state.cwd);
1215 0 : return NULL;
1216 : }
1217 :
1218 330 : if (real_path) {
1219 222 : int copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length;
1220 222 : memcpy(real_path, new_state.cwd, copy_len);
1221 222 : real_path[copy_len] = '\0';
1222 222 : free(new_state.cwd);
1223 222 : return real_path;
1224 : } else {
1225 108 : return new_state.cwd;
1226 : }
1227 : }
1228 :
1229 :
1230 : /*
1231 : * Local variables:
1232 : * tab-width: 4
1233 : * c-basic-offset: 4
1234 : * End:
1235 : */
|