ID: 39367 User updated by: j at pureftpd dot org Reported By: j at pureftpd dot org Status: Open Bug Type: Filesystem function related Operating System: Any PHP Version: 5.2.0 New Comment:
Another fix is to have clearstatcache() also clear the realpath cache. Patch follows. Also available from ftp://ftp.c9x.org/misc/ php_clearstatcache_should_clear_realpath_cache.diff --- ext/standard/filestat.c.orig Sat Nov 4 13:14:10 2006 +++ ext/standard/filestat.c Sat Nov 4 13:14:40 2006 @@ -633,6 +633,7 @@ efree(BG(CurrentLStatFile)); BG(CurrentLStatFile) = NULL; } + realpath_cache_empty(); } /* }}} */ --- TSRM/tsrm_virtual_cwd.c.orig Sat Nov 4 00:56:05 2006 +++ TSRM/tsrm_virtual_cwd.c Sat Nov 4 13:28:29 2006 @@ -360,6 +360,24 @@ return NULL; } +CWD_API int realpath_cache_empty(void) +{ + int i; + + for (i = 0; i < sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]); i++) { + realpath_cache_bucket *bucket = CWDG (realpath_cache)[i]; + + while (bucket != NULL) { + realpath_cache_bucket *r = bucket; + bucket = bucket->next; + free(r); + } + CWDG(realpath_cache)[i] = NULL; + } + CWDG(realpath_cache_size) = 0; + + return 0; +} /* Resolve path relatively to state and put the real path into state */ /* returns 0 for ok, 1 for error */ --- TSRM/tsrm_virtual_cwd.h.orig Sat Nov 4 02:39:04 2006 +++ TSRM/tsrm_virtual_cwd.h Sat Nov 4 13:12:50 2006 @@ -156,6 +156,7 @@ CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC); CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC); CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC); +CWD_API int realpath_cache_empty(void); #if defined(TSRM_WIN32) /* these are not defined in win32 headers */ #ifndef W_OK Previous Comments: ------------------------------------------------------------------------ [2006-11-04 02:00:22] j at pureftpd dot org I think the real bug is that unlink(), rename() and rmdir() don't refresh the related realpath cache entries. Here's a patch that fixes this, also available from ftp:// ftp.c9x.org/misc/ php_sync_realpath_cache_with_unlink_and_rename.diff --- TSRM/tsrm_virtual_cwd.c.orig Sat Nov 4 00:56:05 2006 +++ TSRM/tsrm_virtual_cwd.c Sat Nov 4 02:53:13 2006 @@ -361,6 +361,23 @@ } +CWD_API int realpath_cache_delete(const char *path, int path_len) { + unsigned long key = realpath_cache_key(path, path_len); + unsigned long n = key % (sizeof(CWDG (realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + realpath_cache_bucket **bucket = &CWDG (realpath_cache)[n]; + + while (*bucket != NULL) { + if (key == (*bucket)->key && path_len == (*bucket)->path_len && + memcmp(path, (*bucket)->path, path_len) == 0) { + realpath_cache_bucket *r = *bucket; + *bucket = (*bucket)->next; + CWDG(realpath_cache_size) -= sizeof (realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1; + free(r); + } + } + return 0; +} + /* Resolve path relatively to state and put the real path into state */ /* returns 0 for ok, 1 for error */ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) --- TSRM/tsrm_virtual_cwd.h.orig Sat Nov 4 02:39:04 2006 +++ TSRM/tsrm_virtual_cwd.h Sat Nov 4 02:48:04 2006 @@ -232,14 +232,14 @@ #define VCWD_CHDIR_FILE(path) virtual_chdir_file(path, virtual_chdir TSRMLS_CC) #define VCWD_GETWD(buf) #define VCWD_REALPATH(path, real_path) virtual_realpath (path, real_path TSRMLS_CC) -#define VCWD_RENAME(oldname, newname) virtual_rename (oldname, newname TSRMLS_CC) +#define VCWD_RENAME(oldname, newname) (virtual_rename (oldname, newname TSRMLS_CC) + realpath_cache_delete (oldname, strlen(oldname)) + realpath_cache_delete(newname, strlen(newname))) #define VCWD_STAT(path, buff) virtual_stat(path, buff TSRMLS_CC) #if !defined(TSRM_WIN32) #define VCWD_LSTAT(path, buff) virtual_lstat(path, buff TSRMLS_CC) #endif -#define VCWD_UNLINK(path) virtual_unlink(path TSRMLS_CC) +#define VCWD_UNLINK(path) (virtual_unlink(path TSRMLS_CC) + realpath_cache_delete(path, strlen(path))) #define VCWD_MKDIR(pathname, mode) virtual_mkdir(pathname, mode TSRMLS_CC) -#define VCWD_RMDIR(pathname) virtual_rmdir(pathname TSRMLS_CC) +#define VCWD_RMDIR(pathname) (virtual_rmdir(pathname TSRMLS_CC) + realpath_cache_delete(pathname, strlen (pathname))) #define VCWD_OPENDIR(pathname) virtual_opendir(pathname TSRMLS_CC) #define VCWD_POPEN(command, type) virtual_popen(command, type TSRMLS_CC) #define VCWD_ACCESS(pathname, mode) virtual_access (pathname, mode TSRMLS_CC) @@ -261,15 +261,15 @@ #define VCWD_OPEN(path, flags) open(path, flags) #define VCWD_OPEN_MODE(path, flags, mode) open(path, flags, mode) #define VCWD_CREAT(path, mode) creat(path, mode) -#define VCWD_RENAME(oldname, newname) rename(oldname, newname) +#define VCWD_RENAME(oldname, newname) (rename(oldname, newname) + realpath_cache_delete(oldname, strlen(oldname)) + realpath_cache_delete(newname, strlen(newname))) #define VCWD_CHDIR(path) chdir(path) #define VCWD_CHDIR_FILE(path) virtual_chdir_file(path, chdir) #define VCWD_GETWD(buf) getwd(buf) #define VCWD_STAT(path, buff) stat(path, buff) #define VCWD_LSTAT(path, buff) lstat(path, buff) -#define VCWD_UNLINK(path) unlink(path) +#define VCWD_UNLINK(path) (unlink(path) + realpath_cache_delete(path, strlen(path))) #define VCWD_MKDIR(pathname, mode) mkdir(pathname, mode) -#define VCWD_RMDIR(pathname) rmdir(pathname) +#define VCWD_RMDIR(pathname) (rmdir(pathname) + realpath_cache_delete(pathname, strlen(pathname))) #define VCWD_OPENDIR(pathname) opendir(pathname) #define VCWD_POPEN(command, type) popen(command, type) #if defined(TSRM_WIN32) ------------------------------------------------------------------------ [2006-11-04 00:09:00] j at pureftpd dot org Description: ------------ realpath() on a symlink returns the final file the link points to. Caching symbolic links through the realpath cache can cause unexpected behaviors. Here's a fix against PHP 5.2.0 --- TSRM/tsrm_virtual_cwd.c.orig Sat Nov 4 00:56:05 2006 +++ TSRM/tsrm_virtual_cwd.c Sat Nov 4 00:58:03 2006 @@ -562,7 +562,11 @@ } if (use_realpath && CWDG(realpath_cache_size_limit)) { - realpath_cache_add(path, path_length, state- >cwd, state->cwd_length, t TSRMLS_CC); + struct stat buf; + + if (lstat(path, &buf) == 0 && !S_ISLNK (buf.st_mode)) { + realpath_cache_add(path, path_length, state->cwd, state- >cwd_length, t TSRMLS_CC); + } } if (verify_path && verify_path(state)) { Reproduce code: --------------- See bug #36555, or that code : <?php @unlink('/tmp/1link'); @unlink('/tmp/1tmp'); @unlink('/tmp/testfile1'); file_put_contents('/tmp/testfile1', '42'); symlink('/tmp/testfile1', '/tmp/1tmp'); rename('/tmp/1tmp', '/tmp/1link'); $a = file_get_contents('/tmp/1link'); var_dump($a); unlink('/tmp/1link'); clearstatcache(); $a = file_get_contents('/tmp/1link'); var_dump($a); ?> Expected result: ---------------- The second file_get_contents() should fail because /tmp/1link has been unlinked. But it doesn't. Actual result: -------------- 42 42 ------------------------------------------------------------------------ -- Edit this bug report at http://bugs.php.net/?id=39367&edit=1