Annotation of embedaddon/php/TSRM/tsrm_virtual_cwd.c, revision 1.1.1.3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>