]> granicus.if.org Git - php/commitdiff
- MF53:
authorPierre Joye <pajoye@php.net>
Fri, 26 Jun 2009 07:44:57 +0000 (07:44 +0000)
committerPierre Joye <pajoye@php.net>
Fri, 26 Jun 2009 07:44:57 +0000 (07:44 +0000)
 - Windows ACL cache support, update existing tests and add a new one
 - #48535,  file_exists returns false when impersonate is used

TSRM/tsrm_virtual_cwd.c
TSRM/tsrm_virtual_cwd.h
TSRM/tsrm_win32.c

index bcc6029f20531cab3a1f6b90effb3c34c1c64dc9..72307f144b26133f6041a60d03c744b6188c52e2 100644 (file)
@@ -35,6 +35,9 @@
 #ifdef TSRM_WIN32
 #include <io.h>
 #include "tsrm_win32.h"
+# ifndef IO_REPARSE_TAG_SYMLINK
+#  define IO_REPARSE_TAG_SYMLINK 0xA000000C
+# endif
 #endif
 
 #ifdef NETWARE
@@ -137,6 +140,42 @@ static int php_check_dots(const char *element, int n)
        
 #ifdef TSRM_WIN32
 
+#ifdef CTL_CODE
+#undef CTL_CODE
+#endif
+#define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
+#define FILE_DEVICE_FILE_SYSTEM 0x00000009
+#define METHOD_BUFFERED                0
+#define FILE_ANY_ACCESS        0
+#define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 )
+
+typedef struct {
+       unsigned long  ReparseTag;
+       unsigned short ReparseDataLength;
+       unsigned short Reserved;
+       union {
+               struct {
+                       unsigned short SubstituteNameOffset;
+                       unsigned short SubstituteNameLength;
+                       unsigned short PrintNameOffset;
+                       unsigned short PrintNameLength;
+                       unsigned long  Flags;
+                       wchar_t        ReparseTarget[1];
+               } SymbolicLinkReparseBuffer;
+               struct {
+                       unsigned short SubstituteNameOffset;
+                       unsigned short SubstituteNameLength;
+                       unsigned short PrintNameOffset;
+                       unsigned short PrintNameLength;
+                       wchar_t        ReparseTarget[1];
+               } MountPointReparseBuffer;
+               struct {
+                       unsigned char  ReparseTarget[1];
+               } GenericReparseBuffer;
+       };
+} REPARSE_DATA_BUFFER;
+
 #define SECS_BETWEEN_EPOCHS (__int64)11644473600
 #define SECS_TO_100NS (__int64)10000000
 static inline time_t FileTimeToUnixTime(const FILETIME FileTime)
@@ -471,6 +510,12 @@ static inline void realpath_cache_add(const char *path, int path_len, const char
                }
                bucket->realpath_len = realpath_len;
                bucket->is_dir = is_dir;
+#ifdef PHP_WIN32
+               bucket->is_rvalid   = 0;
+               bucket->is_readable = 0;
+               bucket->is_wvalid   = 0;
+               bucket->is_writable = 0;
+#endif
                bucket->expires = t + CWDG(realpath_cache_ttl);
                n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
                bucket->next = CWDG(realpath_cache)[n];
@@ -503,6 +548,12 @@ static inline realpath_cache_bucket* realpath_cache_find(const char *path, int p
 }
 /* }}} */
 
+CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
+{
+    return realpath_cache_find(path, path_len, t TSRMLS_CC);
+}
+/* }}} */
+
 #undef LINK_MAX
 #define LINK_MAX 32
 
@@ -518,7 +569,8 @@ static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, i
 #endif
        realpath_cache_bucket *bucket;
        char *tmp;
-       TSRM_ALLOCA_FLAG(use_heap);
+       TSRM_ALLOCA_FLAG(use_heap)
+       TSRM_ALLOCA_FLAG(use_heap_large)
 
        while (1) {
                if (len <= start) {
@@ -606,16 +658,103 @@ static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, i
                        /* continue resolution anyway but don't save result in the cache */
                        save = 0;
                }
+               
                if (save) {
-                       directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-                       if (is_dir && !directory) {
-                               /* not a directory */
-                               FindClose(hFind);
-                               return -1;
-                       }
+                       FindClose(hFind);
                }
+
                tmp = tsrm_do_alloca(len+1, use_heap);
                memcpy(tmp, path, len+1);
+
+               if(save && (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+                       /* File is a reparse point. Get the target */
+                       HANDLE hLink = NULL;
+                       REPARSE_DATA_BUFFER * pbuffer;
+                       unsigned int retlength = 0, rname_off = 0;
+                       int bufindex = 0, rname_len = 0, isabsolute = 0;
+                       wchar_t * reparsetarget;
+
+                       if(++(*ll) > LINK_MAX) {
+                               return -1;
+                       }
+
+                       hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
+                       if(hLink == INVALID_HANDLE_VALUE) {
+                               return -1;
+                       }
+
+                       pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
+                       if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
+                               tsrm_free_alloca(pbuffer, use_heap_large);
+                               CloseHandle(hLink);
+                               return -1;
+                       }
+
+                       CloseHandle(hLink);
+
+                       if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+                               rname_len = pbuffer->SymbolicLinkReparseBuffer.PrintNameLength/2;
+                               rname_off = pbuffer->SymbolicLinkReparseBuffer.PrintNameOffset/2;
+                               reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget;
+                               isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0;
+                       }
+                       else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
+                               rname_len = pbuffer->MountPointReparseBuffer.PrintNameLength/2;
+                               rname_off = pbuffer->MountPointReparseBuffer.PrintNameOffset/2;
+                               reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget;
+                               isabsolute = 1;
+                       }
+                       else {
+                               tsrm_free_alloca(pbuffer, use_heap_large);
+                               return -1;
+                       }
+
+                       /* Convert wide string to narrow string */
+                       for(bufindex = 0; bufindex < rname_len; bufindex++) {
+                               *(path + bufindex) = (char)(reparsetarget[rname_off + bufindex]);
+                       }
+
+                       *(path + bufindex) = 0;
+                       tsrm_free_alloca(pbuffer, use_heap_large);
+                       j = bufindex;
+
+                       if(isabsolute == 1) {
+                               /* use_realpath is 0 in the call below coz path is absolute*/
+                               j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory TSRMLS_CC);
+                               if(j < 0) {
+                                       tsrm_free_alloca(tmp, use_heap);
+                                       return -1;
+                               }
+                       }
+                       else {
+                               if(i + j >= MAXPATHLEN - 1) {
+                                       tsrm_free_alloca(tmp, use_heap);
+                                       return -1;
+                               }
+
+                               memmove(path+i, path, j+1);
+                               memcpy(path, tmp, i-1);
+                               path[i-1] = DEFAULT_SLASH;
+                               j  = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
+                               if(j < 0) {
+                                       tsrm_free_alloca(tmp, use_heap);
+                                       return -1;
+                               }
+                       }
+
+                       if(link_is_dir) {
+                               *link_is_dir = directory;
+                       }
+               }
+               else {
+                       if (save) {
+                               directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+                               if (is_dir && !directory) {
+                                       /* not a directory */
+                                       return -1;
+                               }
+                       }
+
 #elif defined(NETWARE)
                save = 0;
                tmp = tsrm_do_alloca(len+1, use_heap);
@@ -687,19 +826,18 @@ static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, i
 #ifdef TSRM_WIN32
                        if (j < 0 || j + len - i >= MAXPATHLEN-1) {
                                tsrm_free_alloca(tmp, use_heap);
-                               if (save) FindClose(hFind);
                                return -1;
                        }
                        if (save) {
                                i = strlen(data.cFileName);
                                memcpy(path+j, data.cFileName, i+1);
                                j += i;
-                               FindClose(hFind);
                        } else {
                                /* use the original file or directory name as it wasn't found */
                                memcpy(path+j, tmp+i, len-i+1);
                                j += (len-i);
                        }
+               }
 #else
                        if (j < 0 || j + len - i >= MAXPATHLEN-1) {
                                tsrm_free_alloca(tmp, use_heap);
index 49602380d3cc2c55ea11f3883fb04ea1736ec487..cc7f7f61ea9f76f9aecbe16e6ba635edfd279e2c 100644 (file)
@@ -227,6 +227,12 @@ typedef struct _realpath_cache_bucket {
        int                            realpath_len;
        int                            is_dir;
        time_t                         expires;
+#ifdef PHP_WIN32
+       unsigned char                  is_rvalid;
+       unsigned char                  is_readable;
+       unsigned char                  is_wvalid;
+       unsigned char                  is_writable;
+#endif
        struct _realpath_cache_bucket *next;    
 } realpath_cache_bucket;
 
@@ -248,6 +254,7 @@ extern virtual_cwd_globals cwd_globals;
 
 CWD_API void realpath_cache_clean(TSRMLS_D);
 CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC);
+CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC);
 
 /* The actual macros to be used in programs using TSRM
  * If the program defines VIRTUAL_DIR it will use the
index ae18f39ec24bb6622747a286d9067c81c5a9d5a8..f49a711b2e2f14566516cce5cc0d67dcef01c4bc 100644 (file)
@@ -33,6 +33,7 @@
 #ifdef TSRM_WIN32
 
 #include "tsrm_win32.h"
+#include "tsrm_virtual_cwd.h"
 
 #ifdef ZTS
 static ts_rsrc_id win32_globals_id;
@@ -42,12 +43,25 @@ static tsrm_win32_globals win32_globals;
 
 static void tsrm_win32_ctor(tsrm_win32_globals *globals TSRMLS_DC)
 {
+       HANDLE process_token = NULL;
+
        globals->process = NULL;
        globals->shm     = NULL;
        globals->process_size = 0;
        globals->shm_size         = 0;
        globals->comspec = _strdup((GetVersion()<0x80000000)?"cmd.exe":"command.com");
        globals->impersonation_token = NULL;
+
+       /* Access check requires impersonation token. Create a duplicate token. */
+       if(OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &process_token)) {
+               DuplicateToken(process_token, SecurityImpersonation, &globals->impersonation_token);
+       }
+
+       /* impersonation_token will be closed when the process dies */
+       if(process_token != NULL) {
+               CloseHandle(process_token);
+               process_token = NULL;
+       }
 }
 
 static void tsrm_win32_dtor(tsrm_win32_globals *globals TSRMLS_DC)
@@ -102,6 +116,11 @@ TSRM_API int tsrm_win32_access(const char *pathname, int mode)
        BYTE * psec_desc = NULL;
        BOOL fAccess = FALSE;
        HANDLE process_token = NULL;
+
+       realpath_cache_bucket * bucket = NULL;
+       char * real_path = NULL;
+       time_t t;
+
        TSRMLS_FETCH();
 
        if (mode == 1 /*X_OK*/) {
@@ -109,18 +128,60 @@ TSRM_API int tsrm_win32_access(const char *pathname, int mode)
                return GetBinaryType(pathname, &type) ? 0 : -1;
        } else {
                if(access(pathname, mode)) {
-                               return errno;
+                       return errno;
                }
 
-               /* Do a full access check because access() will only check read-only attribute */
-               if(mode == 0 || mode > 6) {
+               if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
+                       real_path = (char *)malloc(MAX_PATH);
+                       if(tsrm_realpath(pathname, real_path TSRMLS_CC) == NULL) {
+                               goto Finished;
+                       }
+                       pathname = real_path;
+               }
+
+               if (CWDG(realpath_cache_size_limit)) {
+                       t = time(0);
+                       bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
+                       if(bucket == NULL && real_path == NULL) {
+                               /* We used the pathname directly. Call tsrm_realpath */
+                               /* so that entry is created in realpath cache */
+                               real_path = (char *)malloc(MAX_PATH);
+                               if(tsrm_realpath(pathname, real_path TSRMLS_CC) != NULL) {
+                                       pathname = real_path;
+                                       bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
+                               }
+                       }
+               }
+               /* Do a full access check because access() will only check read-only attribute */
+               if(mode == 0 || mode > 6) {
+                       if(bucket != NULL && bucket->is_rvalid) {
+                               fAccess = bucket->is_readable;
+                               goto Finished;
+                       }
+                       desired_access = FILE_GENERIC_READ;
+               } else if(mode <= 2) {
+                       if(bucket != NULL && bucket->is_wvalid) {
+                               fAccess = bucket->is_writable;
+                               goto Finished;
+                       }
+                       desired_access = FILE_GENERIC_WRITE;
+               } else if(mode <= 4) {
+                       if(bucket != NULL && bucket->is_rvalid) {
+                               fAccess = bucket->is_readable;
+                               goto Finished;
+                       }
                        desired_access = FILE_GENERIC_READ;
-               } else if(mode <= 2) {
-                               desired_access = FILE_GENERIC_WRITE;
-               } else if(mode <= 4) {
-                               desired_access = FILE_GENERIC_READ;
-               } else { // if(mode <= 6)
-                               desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+               } else { // if(mode <= 6)
+                       if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
+                               fAccess = bucket->is_readable & bucket->is_writable;
+                               goto Finished;
+                       }
+                       desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+               }
+
+               if(TWG(impersonation_token) == NULL) {
+                       goto Finished;
                }
 
                /* Get size of security buffer. Call is expected to fail */
@@ -134,35 +195,33 @@ TSRM_API int tsrm_win32_access(const char *pathname, int mode)
                        goto Finished;
                }
 
-               if(TWG(impersonation_token) == NULL) {
-
-                       if(!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &process_token)) {
-                               goto Finished;
-                       }
-
-                       /* Access check requires impersonation token. Create a duplicate token. */
-                       if(!DuplicateToken(process_token, SecurityImpersonation, &TWG(impersonation_token))) {
-                               goto Finished;
-                       }
-               }
-
                if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
                                goto Finished;
                }
 
-Finished:
-
-               /* impersonation_token will be closed when the process dies */
-               if(process_token != NULL) {
-                       CloseHandle(process_token);
-                       process_token = NULL;
+               /* Keep the result in realpath_cache */
+               if(bucket != NULL) {
+                       if(desired_access == FILE_GENERIC_READ) {
+                               bucket->is_rvalid = 1;
+                               bucket->is_readable = fAccess;
+                       }
+                       else if(desired_access == FILE_GENERIC_WRITE) {
+                               bucket->is_wvalid = 1;
+                               bucket->is_writable = fAccess;
+                       }
                }
 
+Finished:
                if(psec_desc != NULL) {
                        free(psec_desc);
                        psec_desc = NULL;
                }
 
+               if(real_path != NULL) {
+                       free(real_path);
+                       real_path = NULL;
+               }
+
                if(fAccess == FALSE) {
                        errno = EACCES;
                        return errno;
@@ -326,7 +385,7 @@ TSRM_API int pclose(FILE *stream)
        }
 
        fflush(process->stream);
-    fclose(process->stream);
+       fclose(process->stream);
 
        WaitForSingleObject(process->prochnd, INFINITE);
        GetExitCodeProcess(process->prochnd, &termstat);