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