summaryrefslogtreecommitdiff
path: root/ext/session/mod_files.c
diff options
context:
space:
mode:
authorYasuo Ohgaki <yohgaki@php.net>2015-01-21 19:13:59 +0900
committerYasuo Ohgaki <yohgaki@php.net>2015-01-22 13:34:58 +0900
commite6c8640a2a242b4c620923dcbe5f93c8366585e7 (patch)
tree4eff7b05ff1c9a44eed52f5c1ae86dcf071cca7a /ext/session/mod_files.c
parentebb60ac7dd179a3bea540d50a7d595010a82a656 (diff)
downloadphp-git-e6c8640a2a242b4c620923dcbe5f93c8366585e7.tar.gz
WIP - test passes
Diffstat (limited to 'ext/session/mod_files.c')
-rw-r--r--ext/session/mod_files.c262
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