diff options
author | Yasuo Ohgaki <yohgaki@php.net> | 2015-01-21 19:13:59 +0900 |
---|---|---|
committer | Yasuo Ohgaki <yohgaki@php.net> | 2015-01-22 13:34:58 +0900 |
commit | e6c8640a2a242b4c620923dcbe5f93c8366585e7 (patch) | |
tree | 4eff7b05ff1c9a44eed52f5c1ae86dcf071cca7a /ext/session/mod_files.c | |
parent | ebb60ac7dd179a3bea540d50a7d595010a82a656 (diff) | |
download | php-git-e6c8640a2a242b4c620923dcbe5f93c8366585e7.tar.gz |
WIP - test passes
Diffstat (limited to 'ext/session/mod_files.c')
-rw-r--r-- | ext/session/mod_files.c | 262 |
1 files changed, 217 insertions, 45 deletions
diff --git a/ext/session/mod_files.c b/ext/session/mod_files.c index 1d6686a5d9..36c9a3354e 100644 --- a/ext/session/mod_files.c +++ b/ext/session/mod_files.c @@ -16,7 +16,41 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ +/************************************************************************** + * Files save handler should be used as reference implementations of session + * save handlers. PS_* functions are called as follows with standard usage. + * + * PS_OPEN_FUNC() - Create module data that manages save handler. + * PS_CREATE_SID() and/or PS_VALIDATE_SID() + * - PS_CREATE_ID() is called if session ID(key) is not + * provided or invalid. PS_VALIDATE_SID() is called to + * verify session ID already exists or not to mitigate + * session adoption vulunerabilty risk. + * PS_READ_FUNC() - Read data from storage. + * PS_GC_FUNC() - Perform GC. Called by probability + * (session.gc_probability/session.gc_divisor). + * PS_WRITE_FUNC() or PS_UPDATE_TIMESTAMP() + * - Write session data or update session data timestamp. + * It depends on session data change. + * PS_CLOSE_FUNC() - Clean up module data created by PS_OPEN_FUNC(). + * + * Session module gurantees PS_OPEN_FUNC() is called before calling other + * PS_*_FUNC() functions. Other than this, session module may call any + * PS_*_FUNC() at any time. You may assume non null *mod_data created by + * PS_OPEN_FUNC() is passed to PS_*_FUNC(). + * + * NOTE: + * - Save handlers _MUST_NOT_ change/refer PS() values. + * i.e. PS(id), PS(session_status), PS(mod) and any other PS() values. + * Use only function parameters passed from session module. + * - Save handler _MUST_ use PS_GET_MOD_DATA()/PS_SET_MOD_DATA() macro to + * set/get save handler module data(mod_data). mod_data contains + * data required by PS modules. It will not be NULL except PS_OPEN_FUNC(). + * - Refer to PS_* macros in php_session.h for function/parameter defitions. + * - Returning FAILURE state from PS_* function results in raising errors. + * Avoid failure state as much as possible. + * - Use static ps_[module name]_[function name] functions for internal use. + *************************************************************************/ #include "php.h" @@ -67,7 +101,8 @@ typedef struct { } ps_files; ps_module ps_mod_files = { - PS_MOD_SID(files) + /* New save handlers MUST use PS_MOD_UPDATE_TIMESTAMP macro */ + PS_MOD_UPDATE_TIMESTAMP(files) }; @@ -182,6 +217,43 @@ static void ps_files_open(ps_files *data, const char *key) } } +static int ps_files_write(ps_files *data, zend_string *key, zend_string *val) +{ + zend_long n; + zend_stat_t sbuf; + + /* PS(id) may be changed by calling session_regenerate_id(). + Re-initialization should be tried here. ps_files_open() checks + data->lastkey and reopen when it is needed. */ + ps_files_open(data, key->val); + if (data->fd < 0) { + return FAILURE; + } + + /* Truncate file if the amount of new data is smaller than the existing data set. */ + if (val->len < (int)data->st_size) { + php_ignore_value(ftruncate(data->fd, 0)); + } + +#if defined(HAVE_PWRITE) + n = pwrite(data->fd, val->val, val->len, 0); +#else + lseek(data->fd, 0, SEEK_SET); + n = write(data->fd, val->val, val->len); +#endif + + if (n != val->len) { + if (n == -1) { + php_error_docref(NULL, E_WARNING, "write failed: %s (%d)", strerror(errno), errno); + } else { + php_error_docref(NULL, E_WARNING, "write wrote less bytes than requested"); + } + return FAILURE; + } + + return SUCCESS; +} + static int ps_files_cleanup_dir(const char *dirname, int maxlifetime) { DIR *dir; @@ -252,6 +324,18 @@ static int ps_files_key_exists(ps_files *data, const char *key) #define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA() + +/* + * Open save handler. Setup resources that are needed by the handler. + * PARAMETERS: PS_OPEN_ARGS in php_session.h + * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL valid module data + * (void **mod_data) with SUCCESS, NULL(default) for FAILUREs. + * + * Files save handler checks/create save_path directory and setup ps_files data. + * Note that files save handler supports splitting session data into multiple + * directories. + * *mod_data, *save_path, *session_name are guranteed to have non-NULL values. + */ PS_OPEN_FUNC(files) { ps_files *data; @@ -316,6 +400,17 @@ PS_OPEN_FUNC(files) return SUCCESS; } + +/* + * Clean up opened resources. + * PARAMETERS: PS_CLOSE_ARGS in php_session.h + * RETURN VALUE: SUCCESS. Must set PS module data(void **mod_data) to NULL. + * + * Files save handler closes open files and it's memory. + * *mod_data is guranteed to have non-NULL value. + * PS_CLOSE_FUNC() must set *mod_data to NULL. PS_CLOSE_FUNC() should not + * fail. + */ PS_CLOSE_FUNC(files) { PS_FILES_DATA; @@ -333,32 +428,24 @@ PS_CLOSE_FUNC(files) return SUCCESS; } + +/* + * Read session data from opened resource. + * PARAMETERS: PS_READ_ARGS in php_session.h + * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL session data to (zend_string **val) + * for SUCCESS. NULL(default) for FAILUREs. + * + * Files save handler supports splitting session data into multiple + * directories. + * *mod_data, *key are guranteed to have non-NULL values. + */ PS_READ_FUNC(files) { zend_long n; zend_stat_t sbuf; PS_FILES_DATA; - /* If strict mode, check session id existence */ - if (PS(use_strict_mode) && - ps_files_key_exists(data, key? key->val : NULL) == FAILURE) { - /* key points to PS(id), but cannot change here. */ - if (key) { - zend_string_release(PS(id)); - PS(id) = NULL; - } - PS(id) = PS(mod)->s_create_sid((void **)&data); - if (!PS(id)) { - return FAILURE; - } - if (PS(use_cookies)) { - PS(send_cookie) = 1; - } - php_session_reset_id(); - PS(session_status) = php_session_active; - } - - ps_files_open(data, PS(id)->val); + ps_files_open(data, key->val); if (data->fd < 0) { return FAILURE; } @@ -390,47 +477,82 @@ PS_READ_FUNC(files) php_error_docref(NULL, E_WARNING, "read returned less bytes than requested"); } zend_string_release(*val); + *val = STR_EMPTY_ALLOC(); return FAILURE; } return SUCCESS; } + +/* + * Write session data. + * PARAMETERS: PS_WRITE_ARGS in php_session.h + * RETURN VALUE: SUCCESS or FAILURE. + * + * PS_WRITE_FUNC() must write session data(zend_string *val) unconditionally. + * *mod_data, *key, *val are guranteed to have non-NULL values. + */ PS_WRITE_FUNC(files) { - zend_long n; PS_FILES_DATA; - ps_files_open(data, key->val); - if (data->fd < 0) { - return FAILURE; - } + return ps_files_write(data, key, val); +} - /* Truncate file if the amount of new data is smaller than the existing data set. */ - if (val->len < (int)data->st_size) { - php_ignore_value(ftruncate(data->fd, 0)); +/* + * Update session data modification/access time stamp. + * PARAMETERS: PS_UPDATE_TIMESTAMP_ARGS in php_session.h + * RETURN VALUE: SUCCESS or FAILURE. + * + * PS_UPDATE_TIMESTAMP_FUNC() updates time stamp(mtime) so that active session + * data files will not be purged by GC. If session data storage does not need to + * update timestamp, it should return SUCCESS simply. (e.g. Memcache) + * *mod_data, *key, *val are guranteed to have non-NULL values. + * + * NOTE: Updating access timestamp at PS_READ_FUNC() may extend life of obsolete + * session data. Use of PS_UPDATE_TIMESTAMP_FUNC() is prefered whenenver it is + * possible. + */ +PS_UPDATE_TIMESTAMP_FUNC(files) +{ + char buf[MAXPATHLEN]; + struct utimbuf newtimebuf; + struct utimbuf *newtime = &newtimebuf; + int ret; + PS_FILES_DATA; + + if (!ps_files_path_create(buf, sizeof(buf), data, key->val)) { + return FAILURE; } -#if defined(HAVE_PWRITE) - n = pwrite(data->fd, val->val, val->len, 0); + /* Update mtime */ +#ifdef HAVE_UTIME_NULL + newtime = NULL; #else - lseek(data->fd, 0, SEEK_SET); - n = write(data->fd, val->val, val->len); + newtime->modtime = newtime->actime = time(NULL); #endif - - if (n != val->len) { - if (n == -1) { - php_error_docref(NULL, E_WARNING, "write failed: %s (%d)", strerror(errno), errno); - } else { - php_error_docref(NULL, E_WARNING, "write wrote less bytes than requested"); - } - return FAILURE; + ret = VCWD_UTIME(buf, newtime); + if (ret == -1) { + /* New session ID, create data file */ + return ps_files_write(data, key, val); } return SUCCESS; } + +/* + * Delete session data. + * PARAMETERS: PS_DESTROY_ARGS in php_session.h + * RETURN VALUE: SUCCESS or FAILURE. + * + * PS_DESTROY_FUNC() must remove the session data specified by *key from + * session data storage unconditionally. It must not return FAILURE for + * non-existent session data. + * *mod_data, *key are guranteed to have non-NULL values. + */ PS_DESTROY_FUNC(files) { char buf[MAXPATHLEN]; @@ -455,11 +577,23 @@ PS_DESTROY_FUNC(files) return SUCCESS; } + +/* + * Cleanup expired session data. + * PARAMETERS: PS_GC_ARGS in php_session.h + * RETURN VALUE: SUCCESS or FAILURE. Number of deleted records(int *nrdels(default=-1)). + * + * PS_GC_FUNC() must remove session data that are not accessed + * 'session.maxlifetime'(seconds). If storage does not need manual GC, it + * may return SUCCESS simply. (e.g. Memcache) It must set number of records + * deleted(nrdels). + * *mod_data is guranteed to have non-NULL value. + */ PS_GC_FUNC(files) { PS_FILES_DATA; - /* we don't perform any cleanup, if dirdepth is larger than 0. + /* We don't perform any cleanup, if dirdepth is larger than 0. we return SUCCESS, since all cleanup should be handled by an external entity (i.e. find -ctime x | xargs rm) */ @@ -470,6 +604,20 @@ PS_GC_FUNC(files) return SUCCESS; } + +/* + * Create session ID. + * PARAMETERS: PS_CREATE_SID_ARGS in php_session.h + * RETURN VALUE: Valid session ID(zend_string *) or NULL for FAILURE. + * + * PS_CREATE_SID_FUNC() must check collision. i.e. Check session data if + * new sid exists already. + * *mod_data is guranteed to have non-NULL value. + * NOTE: Default php_session_create_id() does not check collision. If + * NULL is returned, session module create new ID by using php_session_create_id(). + * If php_session_create_id() fails due to invalid configuration, it raises E_ERROR. + * NULL return value checks from php_session_create_id() is not required generally. + */ PS_CREATE_SID_FUNC(files) { zend_string *sid; @@ -478,13 +626,21 @@ PS_CREATE_SID_FUNC(files) do { sid = php_session_create_id((void**)&data); + if (!sid) { + if (--maxfail < 0) { + return NULL; + } else { + continue; + } + } /* Check collision */ - if (data && ps_files_key_exists(data, sid? sid->val : NULL) == SUCCESS) { + /* FIXME: mod_data(data) should not be NULL (User handler could be NULL) */ + if (data && ps_files_key_exists(data, sid->val) == SUCCESS) { if (sid) { zend_string_release(sid); sid = NULL; } - if (!(maxfail--)) { + if (--maxfail < 0) { return NULL; } } @@ -495,6 +651,22 @@ PS_CREATE_SID_FUNC(files) /* + * Check session ID existence for use_strict_mode support. + * PARAMETERS: PS_VALIDATE_SID_ARGS in php_session.h + * RETURN VALUE: SUCCESS or FAILURE. + * + * Return SUCCESS for valid key(already exsting session). + * Return FAILURE for invalid key(non-existing session). + * *mod_data, *key are guranteed to have non-NULL values. + */ +PS_VALIDATE_SID_FUNC(files) +{ + PS_FILES_DATA; + + return ps_files_key_exists(data, key->val); +} + +/* * Local variables: * tab-width: 4 * c-basic-offset: 4 |