Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
- Apply realpath() cache patch. We don't use it if we're in safe_mode…
… and

- friends (which are quite slow anyway).
- If it proves to be stable I'll remove the #ifdef's in a few weeks.
  • Loading branch information
Andi Gutmans committed Oct 5, 2004
1 parent 01fda44 commit 216853c
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 22 deletions.
156 changes: 139 additions & 17 deletions TSRM/tsrm_virtual_cwd.c
Expand Up @@ -60,9 +60,9 @@ MUTEX_T cwd_mutex;
#endif

#ifdef ZTS
static ts_rsrc_id cwd_globals_id;
ts_rsrc_id cwd_globals_id;
#else
static virtual_cwd_globals cwd_globals;
virtual_cwd_globals cwd_globals;
#endif

cwd_state main_cwd_state; /* True global */
Expand Down Expand Up @@ -175,11 +175,31 @@ static int php_is_file_ok(const cwd_state *state)
static void cwd_globals_ctor(virtual_cwd_globals *cwd_globals TSRMLS_DC)
{
CWD_STATE_COPY(&cwd_globals->cwd, &main_cwd_state);
#ifdef REALPATH_CACHE
cwd_globals->realpath_cache_size = 0;
cwd_globals->realpath_cache_size_limit = REALPATH_CACHE_SIZE;
cwd_globals->realpath_cache_ttl = REALPATH_CACHE_TTL;
memset(cwd_globals->realpath_cache, 0, sizeof(cwd_globals->realpath_cache));
#endif
}

static void cwd_globals_dtor(virtual_cwd_globals *cwd_globals TSRMLS_DC)
{
CWD_STATE_FREE(&cwd_globals->cwd);
#ifdef REALPATH_CACHE
{
int i;

for (i = 0; i < sizeof(cwd_globals->realpath_cache)/sizeof(cwd_globals->realpath_cache[0]); i++) {
realpath_cache_bucket *p = cwd_globals->realpath_cache[i];
while (p != NULL) {
realpath_cache_bucket *r = p;
p = p->next;
free(r);
}
}
}
#endif
}

static char *tsrm_strndup(const char *s, size_t length)
Expand Down Expand Up @@ -287,6 +307,65 @@ CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC)
return buf;
}

#ifdef REALPATH_CACHE
static inline unsigned long realpath_cache_key(const char *path, int path_len)
{
register unsigned long h;

const char *e = path + path_len;
for (h = 2166136261U; path < e; ) {
h *= 16777619;
h ^= *path++;
}
return h;
}

static inline void realpath_cache_add(const char *path, int path_len, const char *realpath, int realpath_len, time_t t TSRMLS_DC)
{
long size = sizeof(realpath_cache_bucket) + path_len + 1 + realpath_len + 1;
if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) {
realpath_cache_bucket *bucket = malloc(size);
unsigned long n;

bucket->key = realpath_cache_key(path, path_len);
bucket->path = (char*)bucket + sizeof(realpath_cache_bucket);
memcpy(bucket->path, path, path_len+1);
bucket->path_len = path_len;
bucket->realpath = bucket->path + (path_len + 1);
memcpy(bucket->realpath, realpath, realpath_len+1);
bucket->realpath_len = realpath_len;
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];
CWDG(realpath_cache)[n] = bucket;
CWDG(realpath_cache_size) += size;
}
}

static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC)
{
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 (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) {
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);
} else if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
memcmp(path, (*bucket)->path, path_len) == 0) {
return *bucket;
} else {
*bucket = (*bucket)->next;
}
}
return NULL;
}
#endif


/* 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)
Expand All @@ -295,7 +374,7 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func
char *ptr, *path_copy;
char *tok = NULL;
int ptr_length;
cwd_state *old_state;
cwd_state old_state;
int ret = 0;
int copy_amount = -1;
char *free_path;
Expand All @@ -305,10 +384,50 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func
#else
char *new_path;
#endif
#ifdef REALPATH_CACHE
char orig_path[MAXPATHLEN];
int orig_path_len;
realpath_cache_bucket *bucket;
time_t t;
TSRMLS_FETCH();
#endif

if (path_length == 0)
return (0);

#ifdef REALPATH_CACHE
if (use_realpath && CWDG(realpath_cache_size_limit)) {
if (IS_ABSOLUTE_PATH(path, path_length) || (state->cwd_length < 1)) {
memcpy(orig_path, path, path_length+1);
orig_path_len = path_length;
} else {
orig_path_len = path_length + state->cwd_length + 1;
if (orig_path_len+1 > MAXPATHLEN) {
return 1;
}
memcpy(orig_path, state->cwd, state->cwd_length);
orig_path[state->cwd_length] = DEFAULT_SLASH;
memcpy(orig_path + state->cwd_length + 1, path, path_length + 1);
}
t = CWDG(realpath_cache_ttl)?time(NULL):0;
if ((bucket = realpath_cache_find(orig_path, orig_path_len, t TSRMLS_CC)) != NULL) {
int len = bucket->realpath_len;

CWD_STATE_COPY(&old_state, state);
state->cwd = (char *) realloc(state->cwd, len+1);
memcpy(state->cwd, bucket->realpath, len+1);
state->cwd_length = len;
if (verify_path && verify_path(state)) {
CWD_STATE_FREE(state);
*state = old_state;
return 1;
} else {
CWD_STATE_FREE(&old_state);
return 0;
}
}
}
#endif
#if !defined(TSRM_WIN32) && !defined(NETWARE)
/* cwd_length can be 0 when getcwd() fails.
* This can happen under solaris when a dir does not have read permissions
Expand Down Expand Up @@ -364,8 +483,7 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func
#endif
free_path = path_copy = tsrm_strndup(path, path_length);

old_state = (cwd_state *) malloc(sizeof(cwd_state));
CWD_STATE_COPY(old_state, state);
CWD_STATE_COPY(&old_state, state);
#if VIRTUAL_CWD_DEBUG
fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path);
#endif
Expand All @@ -385,7 +503,7 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func
memcpy(state->cwd, path_copy, copy_amount);
path_copy += copy_amount;
} else {
memcpy(state->cwd, old_state->cwd, copy_amount);
memcpy(state->cwd, old_state.cwd, copy_amount);
}
}
state->cwd[copy_amount] = '\0';
Expand Down Expand Up @@ -453,24 +571,28 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func
state->cwd_length = path_length;
}

if (verify_path && verify_path(state)) {
CWD_STATE_FREE(state);
#ifdef TSRM_WIN32
if (new_path) {
free(new_path);
}
#endif
free(free_path);

*state = *old_state;
#ifdef REALPATH_CACHE
if (ret == 0 && use_realpath && CWDG(realpath_cache_size_limit)) {
realpath_cache_add(orig_path, orig_path_len, state->cwd, state->cwd_length, t TSRMLS_CC);
}
#endif

if (verify_path && verify_path(state)) {
CWD_STATE_FREE(state);
*state = old_state;
ret = 1;
} else {
CWD_STATE_FREE(old_state);
CWD_STATE_FREE(&old_state);
ret = 0;
}

free(old_state);
#ifdef TSRM_WIN32
if (new_path) {
free(new_path);
}
#endif
free(free_path);
#if VIRTUAL_CWD_DEBUG
fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd);
#endif
Expand Down
24 changes: 24 additions & 0 deletions TSRM/tsrm_virtual_cwd.h
Expand Up @@ -200,13 +200,37 @@ CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group TSRMLS_

CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath);

#define REALPATH_CACHE
#define REALPATH_CACHE_TTL (2*60) /* 2 minutes */
#define REALPATH_CACHE_SIZE 0 /* disabled while php.ini isn't loaded */

#ifdef REALPATH_CACHE
typedef struct _realpath_cache_bucket {
unsigned long key;
char *path;
int path_len;
char *realpath;
int realpath_len;
time_t expires;
struct _realpath_cache_bucket *next;
} realpath_cache_bucket;
#endif

typedef struct _virtual_cwd_globals {
cwd_state cwd;
#ifdef REALPATH_CACHE
long realpath_cache_size;
long realpath_cache_size_limit;
long realpath_cache_ttl;
realpath_cache_bucket *realpath_cache[1024];
#endif
} virtual_cwd_globals;

#ifdef ZTS
extern ts_rsrc_id cwd_globals_id;
# define CWDG(v) TSRMG(cwd_globals_id, virtual_cwd_globals *, v)
#else
extern virtual_cwd_globals cwd_globals;
# define CWDG(v) (cwd_globals.v)
#endif

Expand Down
5 changes: 1 addition & 4 deletions main/SAPI.c
Expand Up @@ -79,9 +79,7 @@ SAPI_API void sapi_startup(sapi_module_struct *sf)
sapi_globals_ctor(&sapi_globals TSRMLS_CC);
#endif

#ifdef VIRTUAL_DIR
virtual_cwd_startup(); /* Could use shutdown to free the main cwd but it would just slow it down for CGI */
#endif

#ifdef PHP_WIN32
tsrm_win32_startup();
Expand All @@ -93,9 +91,8 @@ SAPI_API void sapi_startup(sapi_module_struct *sf)
SAPI_API void sapi_shutdown(void)
{
reentrancy_shutdown();
#ifdef VIRTUAL_DIR

virtual_cwd_shutdown();
#endif

#ifdef PHP_WIN32
tsrm_win32_shutdown();
Expand Down
10 changes: 9 additions & 1 deletion main/main.c
Expand Up @@ -322,7 +322,10 @@ PHP_INI_BEGIN()

STD_PHP_INI_BOOLEAN("allow_url_fopen", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_url_fopen, php_core_globals, core_globals)
STD_PHP_INI_BOOLEAN("always_populate_raw_post_data", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, always_populate_raw_post_data, php_core_globals, core_globals)

#ifdef REALPATH_CACHE
STD_PHP_INI_ENTRY("realpath_cache_size", "16K", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_size_limit, virtual_cwd_globals, cwd_globals)
STD_PHP_INI_ENTRY("realpath_cache_ttl", "120", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_ttl, virtual_cwd_globals, cwd_globals)
#endif
PHP_INI_END()
/* }}} */

Expand Down Expand Up @@ -1386,6 +1389,11 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod
REGISTER_INI_ENTRIES();
zend_register_standard_ini_entries(TSRMLS_C);

/* Disable realpath cache if safe_mode or open_basedir are set */
if (PG(safe_mode) || (PG(open_basedir) && *PG(open_basedir))) {
CWDG(realpath_cache_size_limit) = 0;
}

/* initialize stream wrappers registry
* (this uses configuration parameters from php.ini)
*/
Expand Down

0 comments on commit 216853c

Please sign in to comment.