xref: /PHP-7.4/ext/session/mod_files.c (revision 2b28f718)
19ece649fSJani Taskinen /*
2a1b42e3fSSascha Schumann    +----------------------------------------------------------------------+
3d0cb7153SJohannes Schlüter    | PHP Version 7                                                        |
4a1b42e3fSSascha Schumann    +----------------------------------------------------------------------+
50cf7de1cSZeev Suraski    | Copyright (c) The PHP Group                                          |
6a1b42e3fSSascha Schumann    +----------------------------------------------------------------------+
75bd93221Sfoobar    | This source file is subject to version 3.01 of the PHP license,      |
8c5724cbdSZeev Suraski    | that is bundled with this package in the file LICENSE, and is        |
9f68c7ff2SJames Cox    | available through the world-wide-web at the following url:           |
105bd93221Sfoobar    | http://www.php.net/license/3_01.txt                                  |
11c5724cbdSZeev Suraski    | If you did not receive a copy of the PHP license and are unable to   |
12c5724cbdSZeev Suraski    | obtain it through the world-wide-web, please send a note to          |
13c5724cbdSZeev Suraski    | license@php.net so we can mail you a copy immediately.               |
14a1b42e3fSSascha Schumann    +----------------------------------------------------------------------+
1590613d22SSebastian Bergmann    | Author: Sascha Schumann <sascha@schumann.cx>                         |
16a1b42e3fSSascha Schumann    +----------------------------------------------------------------------+
17a1b42e3fSSascha Schumann  */
18a1b42e3fSSascha Schumann 
19e6c8640aSYasuo Ohgaki /**************************************************************************
20e6c8640aSYasuo Ohgaki  * Files save handler should be used as reference implementations of session
21e6c8640aSYasuo Ohgaki  * save handlers. PS_* functions are called as follows with standard usage.
22e6c8640aSYasuo Ohgaki  *
23e6c8640aSYasuo Ohgaki  *    PS_OPEN_FUNC()  - Create module data that manages save handler.
24e6c8640aSYasuo Ohgaki  *    PS_CREATE_SID() and/or PS_VALIDATE_SID()
25e6c8640aSYasuo Ohgaki  *                    - PS_CREATE_ID() is called if session ID(key) is not
26e6c8640aSYasuo Ohgaki  *                      provided or invalid. PS_VALIDATE_SID() is called to
27e6c8640aSYasuo Ohgaki  *                      verify session ID already exists or not to mitigate
28dbb1298fSYasuo Ohgaki  *                      session adoption vulnerability risk.
29e6c8640aSYasuo Ohgaki  *    PS_READ_FUNC()  - Read data from storage.
30e6c8640aSYasuo Ohgaki  *    PS_GC_FUNC()    - Perform GC. Called by probability
31e6c8640aSYasuo Ohgaki  *                                (session.gc_probability/session.gc_divisor).
3264002648SGabriel Caruso  *    PS_WRITE_FUNC() or PS_UPDATE_TIMESTAMP()
33e6c8640aSYasuo Ohgaki  *                    - Write session data or update session data timestamp.
34e6c8640aSYasuo Ohgaki  *                      It depends on session data change.
35e6c8640aSYasuo Ohgaki  *    PS_CLOSE_FUNC() - Clean up module data created by PS_OPEN_FUNC().
36e6c8640aSYasuo Ohgaki  *
37dbb1298fSYasuo Ohgaki  * Session module guarantees PS_OPEN_FUNC() is called before calling other
38e6c8640aSYasuo Ohgaki  * PS_*_FUNC() functions. Other than this, session module may call any
39e6c8640aSYasuo Ohgaki  * PS_*_FUNC() at any time. You may assume non null *mod_data created by
40e6c8640aSYasuo Ohgaki  * PS_OPEN_FUNC() is passed to PS_*_FUNC().
41e6c8640aSYasuo Ohgaki  *
42e6c8640aSYasuo Ohgaki  * NOTE:
43e6c8640aSYasuo Ohgaki  *  - Save handlers _MUST_NOT_ change/refer PS() values.
44e6c8640aSYasuo Ohgaki  *    i.e. PS(id), PS(session_status), PS(mod) and any other PS() values.
45e6c8640aSYasuo Ohgaki  *    Use only function parameters passed from session module.
46e6c8640aSYasuo Ohgaki  *  - Save handler _MUST_ use PS_GET_MOD_DATA()/PS_SET_MOD_DATA() macro to
47e6c8640aSYasuo Ohgaki  *    set/get save handler module data(mod_data). mod_data contains
48e6c8640aSYasuo Ohgaki  *    data required by PS modules. It will not be NULL except PS_OPEN_FUNC().
49dbb1298fSYasuo Ohgaki  *  - Refer to PS_* macros in php_session.h for function/parameter definitions.
50e6c8640aSYasuo Ohgaki  *  - Returning FAILURE state from PS_* function results in raising errors.
51e6c8640aSYasuo Ohgaki  *    Avoid failure state as much as possible.
52e6c8640aSYasuo Ohgaki  *  - Use static ps_[module name]_[function name] functions for internal use.
53e6c8640aSYasuo Ohgaki  *************************************************************************/
549c558821SRasmus Lerdorf 
5530c131c8SSascha Schumann #include "php.h"
5630c131c8SSascha Schumann 
57a1b42e3fSSascha Schumann #include <sys/stat.h>
58a1b42e3fSSascha Schumann #include <sys/types.h>
59dbb24c2eSZeev Suraski 
60dbb24c2eSZeev Suraski #if HAVE_SYS_FILE_H
61c5611602SSascha Schumann #include <sys/file.h>
62dbb24c2eSZeev Suraski #endif
63dbb24c2eSZeev Suraski 
647e50dda9SSascha Schumann #if HAVE_DIRENT_H
657e50dda9SSascha Schumann #include <dirent.h>
667e50dda9SSascha Schumann #endif
677e50dda9SSascha Schumann 
6880bdd19eSZeev Suraski #ifdef PHP_WIN32
6914d9a59fSAndi Gutmans #include "win32/readdir.h"
7014d9a59fSAndi Gutmans #endif
71533ef398SSascha Schumann #include <time.h>
7214d9a59fSAndi Gutmans 
73a1b42e3fSSascha Schumann #include <fcntl.h>
7499df46faSAndrei Zmievski #include <errno.h>
75a1b42e3fSSascha Schumann 
767bcc97c8SWez Furlong #if HAVE_UNISTD_H
777bcc97c8SWez Furlong #include <unistd.h>
787bcc97c8SWez Furlong #endif
797bcc97c8SWez Furlong 
80a1b42e3fSSascha Schumann #include "php_session.h"
81d8a9548cSSascha Schumann #include "mod_files.h"
82d8a9548cSSascha Schumann #include "ext/standard/flock_compat.h"
8332be6f26SWez Furlong #include "php_open_temporary_file.h"
84a1b42e3fSSascha Schumann 
857e50dda9SSascha Schumann #define FILE_PREFIX "sess_"
867e50dda9SSascha Schumann 
8724b72e7aSAnatol Belski #ifdef PHP_WIN32
8824b72e7aSAnatol Belski # ifndef O_NOFOLLOW
8924b72e7aSAnatol Belski #  define O_NOFOLLOW 0
9024b72e7aSAnatol Belski # endif
9124b72e7aSAnatol Belski #endif
9224b72e7aSAnatol Belski 
93a1b42e3fSSascha Schumann typedef struct {
94a1b42e3fSSascha Schumann 	char *lastkey;
95a1b42e3fSSascha Schumann 	char *basedir;
9686cf74a1SSascha Schumann 	size_t basedir_len;
9790c29791SSascha Schumann 	size_t dirdepth;
98814fa2c7SSascha Schumann 	size_t st_size;
99a10f0830SSascha Schumann 	int filemode;
10049e78dd0SAnatol Belski 	int fd;
101a1b42e3fSSascha Schumann } ps_files;
102a1b42e3fSSascha Schumann 
10383e495e0SDmitry Stogov const ps_module ps_mod_files = {
104e6c8640aSYasuo Ohgaki 	/* New save handlers MUST use PS_MOD_UPDATE_TIMESTAMP macro */
105e6c8640aSYasuo Ohgaki 	PS_MOD_UPDATE_TIMESTAMP(files)
106a1b42e3fSSascha Schumann };
107a1b42e3fSSascha Schumann 
1084703eb40SSascha Schumann 
ps_files_path_create(char * buf,size_t buflen,ps_files * data,const char * key)1095bbddee8SSascha Schumann static char *ps_files_path_create(char *buf, size_t buflen, ps_files *data, const char *key)
110a1b42e3fSSascha Schumann {
11186cf74a1SSascha Schumann 	size_t key_len;
1124b023c15SSascha Schumann 	const char *p;
1134b023c15SSascha Schumann 	int i;
11418e0393bSAnatol Belski 	size_t n;
1159ece649fSJani Taskinen 
11686cf74a1SSascha Schumann 	key_len = strlen(key);
11766826730SChristoph M. Becker 	if (!data || key_len <= data->dirdepth ||
118de85bf40SJani Taskinen 		buflen < (strlen(data->basedir) + 2 * data->dirdepth + key_len + 5 + sizeof(FILE_PREFIX))) {
1197e70c1adSSascha Schumann 		return NULL;
120de85bf40SJani Taskinen 	}
121de85bf40SJani Taskinen 
1227e70c1adSSascha Schumann 	p = key;
12386cf74a1SSascha Schumann 	memcpy(buf, data->basedir, data->basedir_len);
12486cf74a1SSascha Schumann 	n = data->basedir_len;
12586cf74a1SSascha Schumann 	buf[n++] = PHP_DIR_SEPARATOR;
12622790c96SHarald Radi 	for (i = 0; i < (int)data->dirdepth; i++) {
1277e70c1adSSascha Schumann 		buf[n++] = *p++;
12830ccec36SZeev Suraski 		buf[n++] = PHP_DIR_SEPARATOR;
1297e70c1adSSascha Schumann 	}
13086cf74a1SSascha Schumann 	memcpy(buf + n, FILE_PREFIX, sizeof(FILE_PREFIX) - 1);
13186cf74a1SSascha Schumann 	n += sizeof(FILE_PREFIX) - 1;
13286cf74a1SSascha Schumann 	memcpy(buf + n, key, key_len);
13386cf74a1SSascha Schumann 	n += key_len;
13475db7e91SSascha Schumann 	buf[n] = '\0';
1359ece649fSJani Taskinen 
1367e70c1adSSascha Schumann 	return buf;
1377e70c1adSSascha Schumann }
1387e70c1adSSascha Schumann 
139682c58c2SStanislav Malyshev #ifndef O_BINARY
1409ece649fSJani Taskinen # define O_BINARY 0
1419ece649fSJani Taskinen #endif
142682c58c2SStanislav Malyshev 
ps_files_close(ps_files * data)1434086197dSDaniel Beulshausen static void ps_files_close(ps_files *data)
1444086197dSDaniel Beulshausen {
1454086197dSDaniel Beulshausen 	if (data->fd != -1) {
1469ece649fSJani Taskinen #ifdef PHP_WIN32
14772de75c9Sfoobar 		/* On Win32 locked files that are closed without being explicitly unlocked
14872de75c9Sfoobar 		   will be unlocked only when "system resources become available". */
149569bd005SIlia Alshanetsky 		flock(data->fd, LOCK_UN);
15072de75c9Sfoobar #endif
1514086197dSDaniel Beulshausen 		close(data->fd);
1524086197dSDaniel Beulshausen 		data->fd = -1;
1534086197dSDaniel Beulshausen 	}
1544086197dSDaniel Beulshausen }
1554086197dSDaniel Beulshausen 
ps_files_open(ps_files * data,const char * key)156bdeb220fSAnatol Belski static void ps_files_open(ps_files *data, const char *key)
1577e70c1adSSascha Schumann {
1587e70c1adSSascha Schumann 	char buf[MAXPATHLEN];
159f2d1ace1SXinchen Hui #if !defined(O_NOFOLLOW) || !defined(PHP_WIN32)
160a9c5dab1SAnatol Belski     struct stat sbuf;
1610ba3aec5SPierre Joye #endif
162b41a6c6fSYasuo Ohgaki 	int ret;
1634b023c15SSascha Schumann 
16412d3e3d7SSascha Schumann 	if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) {
16512d3e3d7SSascha Schumann 		if (data->lastkey) {
1661dd31c38SSascha Schumann 			efree(data->lastkey);
1671dd31c38SSascha Schumann 			data->lastkey = NULL;
1681dd31c38SSascha Schumann 		}
1694086197dSDaniel Beulshausen 
1707c0ba958SSascha Schumann 		ps_files_close(data);
171de85bf40SJani Taskinen 
17225e8fcc8SYasuo Ohgaki 		if (php_session_valid_key(key) == FAILURE) {
173bdeb220fSAnatol Belski 			php_error_docref(NULL, E_WARNING, "The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,'");
17439f16dbcSMarkus Fischer 			return;
17539f16dbcSMarkus Fischer 		}
17625e8fcc8SYasuo Ohgaki 
177de85bf40SJani Taskinen 		if (!ps_files_path_create(buf, sizeof(buf), data, key)) {
1785e9b4c26SAnatol Belski 			php_error_docref(NULL, E_WARNING, "Failed to create session data file path. Too short session ID, invalid save_path or path lentgth exceeds MAXPATHLEN(%d)", MAXPATHLEN);
179bd32363eSYasuo Ohgaki 			return;
180de85bf40SJani Taskinen 		}
181de85bf40SJani Taskinen 
182a1b42e3fSSascha Schumann 		data->lastkey = estrdup(key);
183de85bf40SJani Taskinen 
18456f97273SStanislav Malyshev 		/* O_NOFOLLOW to prevent us from following evil symlinks */
1857f43aeb1SStanislav Malyshev #ifdef O_NOFOLLOW
18656f97273SStanislav Malyshev 		data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY | O_NOFOLLOW, data->filemode);
1877f43aeb1SStanislav Malyshev #else
188b7a7b1a6SStanislav Malyshev 		/* Check to make sure that the opened file is not outside of allowable dirs.
18956f97273SStanislav Malyshev 		   This is not 100% safe but it's hard to do something better without O_NOFOLLOW */
190bdeb220fSAnatol Belski 		if(PG(open_basedir) && lstat(buf, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) && php_check_open_basedir(buf)) {
19156f97273SStanislav Malyshev 			return;
19256f97273SStanislav Malyshev 		}
193de85bf40SJani Taskinen 		data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, data->filemode);
1947f43aeb1SStanislav Malyshev #endif
195de85bf40SJani Taskinen 
1965a83ad6dSSascha Schumann 		if (data->fd != -1) {
19789c0ba16SIlia Alshanetsky #ifndef PHP_WIN32
19856f97273SStanislav Malyshev 			/* check that this session file was created by us or root – we
199a2c461d5SEvgeniy Spinov 			   don't want to end up accepting the sessions of another webapp
200a2c461d5SEvgeniy Spinov 
201a2c461d5SEvgeniy Spinov 			   If the process is ran by root, we ignore session file ownership
202a2c461d5SEvgeniy Spinov 			   Use case: session is initiated by Apache under non-root and then
203a2c461d5SEvgeniy Spinov 			   accessed by backend with root permissions to execute some system tasks.
204a2c461d5SEvgeniy Spinov 
205a2c461d5SEvgeniy Spinov 			   */
206650e073fSEvgeniy Spinov 			if (zend_fstat(data->fd, &sbuf) || (sbuf.st_uid != 0 && sbuf.st_uid != getuid() && sbuf.st_uid != geteuid() && getuid() != 0)) {
2077f43aeb1SStanislav Malyshev 				close(data->fd);
2087f43aeb1SStanislav Malyshev 				data->fd = -1;
2095e9b4c26SAnatol Belski 				php_error_docref(NULL, E_WARNING, "Session data file is not created by your uid");
2107f43aeb1SStanislav Malyshev 				return;
21189c0ba16SIlia Alshanetsky 			}
21289c0ba16SIlia Alshanetsky #endif
213b41a6c6fSYasuo Ohgaki 			do {
214b41a6c6fSYasuo Ohgaki 				ret = flock(data->fd, LOCK_EX);
215b41a6c6fSYasuo Ohgaki 			} while (ret == -1 && errno == EINTR);
2161d22766fSSascha Schumann 
2171d22766fSSascha Schumann #ifdef F_SETFD
218de85bf40SJani Taskinen # ifndef FD_CLOEXEC
219de85bf40SJani Taskinen #  define FD_CLOEXEC 1
220de85bf40SJani Taskinen # endif
22147e4f575SAnantha Kesari H Y 			if (fcntl(data->fd, F_SETFD, FD_CLOEXEC)) {
222bdeb220fSAnatol Belski 				php_error_docref(NULL, E_WARNING, "fcntl(%d, F_SETFD, FD_CLOEXEC) failed: %s (%d)", data->fd, strerror(errno), errno);
2235a83ad6dSSascha Schumann 			}
2241d22766fSSascha Schumann #endif
2255a83ad6dSSascha Schumann 		} else {
226bdeb220fSAnatol Belski 			php_error_docref(NULL, E_WARNING, "open(%s, O_RDWR) failed: %s (%d)", buf, strerror(errno), errno);
2275a83ad6dSSascha Schumann 		}
228a1b42e3fSSascha Schumann 	}
229a1b42e3fSSascha Schumann }
230a1b42e3fSSascha Schumann 
ps_files_write(ps_files * data,zend_string * key,zend_string * val)231e6c8640aSYasuo Ohgaki static int ps_files_write(ps_files *data, zend_string *key, zend_string *val)
232e6c8640aSYasuo Ohgaki {
233323b2733SDmitry Stogov 	size_t n = 0;
234e6c8640aSYasuo Ohgaki 
235e6c8640aSYasuo Ohgaki 	/* PS(id) may be changed by calling session_regenerate_id().
236e6c8640aSYasuo Ohgaki 	   Re-initialization should be tried here. ps_files_open() checks
237e6c8640aSYasuo Ohgaki        data->lastkey and reopen when it is needed. */
2384a2e40bbSDmitry Stogov 	ps_files_open(data, ZSTR_VAL(key));
239e6c8640aSYasuo Ohgaki 	if (data->fd < 0) {
240e6c8640aSYasuo Ohgaki 		return FAILURE;
241e6c8640aSYasuo Ohgaki 	}
242e6c8640aSYasuo Ohgaki 
243e6c8640aSYasuo Ohgaki 	/* Truncate file if the amount of new data is smaller than the existing data set. */
2444a2e40bbSDmitry Stogov 	if (ZSTR_LEN(val) < data->st_size) {
245e6c8640aSYasuo Ohgaki 		php_ignore_value(ftruncate(data->fd, 0));
246e6c8640aSYasuo Ohgaki 	}
247e6c8640aSYasuo Ohgaki 
248e6c8640aSYasuo Ohgaki #if defined(HAVE_PWRITE)
2494a2e40bbSDmitry Stogov 	n = pwrite(data->fd, ZSTR_VAL(val), ZSTR_LEN(val), 0);
250e6c8640aSYasuo Ohgaki #else
251e6c8640aSYasuo Ohgaki 	lseek(data->fd, 0, SEEK_SET);
25218e0393bSAnatol Belski #ifdef PHP_WIN32
25318e0393bSAnatol Belski 	{
2544a2e40bbSDmitry Stogov 		unsigned int to_write = ZSTR_LEN(val) > UINT_MAX ? UINT_MAX : (unsigned int)ZSTR_LEN(val);
2554a2e40bbSDmitry Stogov 		char *buf = ZSTR_VAL(val);
25618e0393bSAnatol Belski 		int wrote;
25718e0393bSAnatol Belski 
25818e0393bSAnatol Belski 		do {
25918e0393bSAnatol Belski 			wrote = _write(data->fd, buf, to_write);
26018e0393bSAnatol Belski 
26118e0393bSAnatol Belski 			n += wrote;
26218e0393bSAnatol Belski 			buf = wrote > -1 ? buf + wrote : 0;
2634a2e40bbSDmitry Stogov 			to_write = wrote > -1 ? (ZSTR_LEN(val) - n > UINT_MAX ? UINT_MAX : (unsigned int)(ZSTR_LEN(val) - n)): 0;
26418e0393bSAnatol Belski 
26518e0393bSAnatol Belski 		} while(wrote > 0);
26618e0393bSAnatol Belski 	}
26718e0393bSAnatol Belski #else
2684a2e40bbSDmitry Stogov 	n = write(data->fd, ZSTR_VAL(val), ZSTR_LEN(val));
26918e0393bSAnatol Belski #endif
270e6c8640aSYasuo Ohgaki #endif
271e6c8640aSYasuo Ohgaki 
2724a2e40bbSDmitry Stogov 	if (n != ZSTR_LEN(val)) {
273323b2733SDmitry Stogov 		if (n == (size_t)-1) {
274e6c8640aSYasuo Ohgaki 			php_error_docref(NULL, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
275e6c8640aSYasuo Ohgaki 		} else {
276e6c8640aSYasuo Ohgaki 			php_error_docref(NULL, E_WARNING, "write wrote less bytes than requested");
277e6c8640aSYasuo Ohgaki 		}
278e6c8640aSYasuo Ohgaki 		return FAILURE;
279e6c8640aSYasuo Ohgaki 	}
280e6c8640aSYasuo Ohgaki 
281e6c8640aSYasuo Ohgaki 	return SUCCESS;
282e6c8640aSYasuo Ohgaki }
283e6c8640aSYasuo Ohgaki 
ps_files_cleanup_dir(const char * dirname,zend_long maxlifetime)28418e0393bSAnatol Belski static int ps_files_cleanup_dir(const char *dirname, zend_long maxlifetime)
2857e70c1adSSascha Schumann {
2867e70c1adSSascha Schumann 	DIR *dir;
287*2b28f718SNikita Popov 	struct dirent *entry;
2884d997f63SAnatol Belski 	zend_stat_t sbuf;
2897e70c1adSSascha Schumann 	char buf[MAXPATHLEN];
2907e70c1adSSascha Schumann 	time_t now;
291af9a852bSSascha Schumann 	int nrdels = 0;
29286cf74a1SSascha Schumann 	size_t dirname_len;
2937e70c1adSSascha Schumann 
2947e70c1adSSascha Schumann 	dir = opendir(dirname);
295af9a852bSSascha Schumann 	if (!dir) {
296bdeb220fSAnatol Belski 		php_error_docref(NULL, E_NOTICE, "ps_files_cleanup_dir: opendir(%s) failed: %s (%d)", dirname, strerror(errno), errno);
297af9a852bSSascha Schumann 		return (0);
298af9a852bSSascha Schumann 	}
2997e70c1adSSascha Schumann 
3007e70c1adSSascha Schumann 	time(&now);
3017e70c1adSSascha Schumann 
30286cf74a1SSascha Schumann 	dirname_len = strlen(dirname);
30386cf74a1SSascha Schumann 
30467447375SXinchen Hui 	if (dirname_len >= MAXPATHLEN) {
30567447375SXinchen Hui 		php_error_docref(NULL, E_NOTICE, "ps_files_cleanup_dir: dirname(%s) is too long", dirname);
30619350b6bSDavid Carlier 		closedir(dir);
30767447375SXinchen Hui 		return (0);
30867447375SXinchen Hui 	}
30967447375SXinchen Hui 
31086cf74a1SSascha Schumann 	/* Prepare buffer (dirname never changes) */
31186cf74a1SSascha Schumann 	memcpy(buf, dirname, dirname_len);
31286cf74a1SSascha Schumann 	buf[dirname_len] = PHP_DIR_SEPARATOR;
3139ece649fSJani Taskinen 
314*2b28f718SNikita Popov 	while ((entry = readdir(dir))) {
3157e70c1adSSascha Schumann 		/* does the file start with our prefix? */
31686cf74a1SSascha Schumann 		if (!strncmp(entry->d_name, FILE_PREFIX, sizeof(FILE_PREFIX) - 1)) {
317de85bf40SJani Taskinen 			size_t entry_len = strlen(entry->d_name);
31886cf74a1SSascha Schumann 
31986cf74a1SSascha Schumann 			/* does it fit into our buffer? */
32086cf74a1SSascha Schumann 			if (entry_len + dirname_len + 2 < MAXPATHLEN) {
32186cf74a1SSascha Schumann 				/* create the full path.. */
32286cf74a1SSascha Schumann 				memcpy(buf + dirname_len + 1, entry->d_name, entry_len);
323de85bf40SJani Taskinen 
32486cf74a1SSascha Schumann 				/* NUL terminate it and */
32586cf74a1SSascha Schumann 				buf[dirname_len + entry_len + 1] = '\0';
326de85bf40SJani Taskinen 
327c8a12508SYasuo Ohgaki 				/* check whether its last access was more than maxlifetime ago */
3289ece649fSJani Taskinen 				if (VCWD_STAT(buf, &sbuf) == 0 &&
3293b646f0eSZeev Suraski 						(now - sbuf.st_mtime) > maxlifetime) {
3304c823e8aSAndi Gutmans 					VCWD_UNLINK(buf);
33186cf74a1SSascha Schumann 					nrdels++;
33286cf74a1SSascha Schumann 				}
33386cf74a1SSascha Schumann 			}
3347e70c1adSSascha Schumann 		}
3357e70c1adSSascha Schumann 	}
3367e70c1adSSascha Schumann 
3377e70c1adSSascha Schumann 	closedir(dir);
338af9a852bSSascha Schumann 
339af9a852bSSascha Schumann 	return (nrdels);
3407e70c1adSSascha Schumann }
3417e70c1adSSascha Schumann 
ps_files_key_exists(ps_files * data,const char * key)342bdeb220fSAnatol Belski static int ps_files_key_exists(ps_files *data, const char *key)
34325e8fcc8SYasuo Ohgaki {
34425e8fcc8SYasuo Ohgaki 	char buf[MAXPATHLEN];
3454d997f63SAnatol Belski 	zend_stat_t sbuf;
34625e8fcc8SYasuo Ohgaki 
34725e8fcc8SYasuo Ohgaki 	if (!key || !ps_files_path_create(buf, sizeof(buf), data, key)) {
34825e8fcc8SYasuo Ohgaki 		return FAILURE;
34925e8fcc8SYasuo Ohgaki 	}
35025e8fcc8SYasuo Ohgaki 	if (VCWD_STAT(buf, &sbuf)) {
35125e8fcc8SYasuo Ohgaki 		return FAILURE;
35225e8fcc8SYasuo Ohgaki 	}
35325e8fcc8SYasuo Ohgaki 	return SUCCESS;
35425e8fcc8SYasuo Ohgaki }
35525e8fcc8SYasuo Ohgaki 
35625e8fcc8SYasuo Ohgaki 
3577e70c1adSSascha Schumann #define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA()
3587e70c1adSSascha Schumann 
359e6c8640aSYasuo Ohgaki 
360e6c8640aSYasuo Ohgaki /*
361e6c8640aSYasuo Ohgaki  * Open save handler. Setup resources that are needed by the handler.
362e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_OPEN_ARGS in php_session.h
363e6c8640aSYasuo Ohgaki  * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL valid module data
364e6c8640aSYasuo Ohgaki  * (void **mod_data) with SUCCESS, NULL(default) for FAILUREs.
365e6c8640aSYasuo Ohgaki  *
366e6c8640aSYasuo Ohgaki  * Files save handler checks/create save_path directory and setup ps_files data.
367e6c8640aSYasuo Ohgaki  * Note that files save handler supports splitting session data into multiple
368e6c8640aSYasuo Ohgaki  * directories.
369dbb1298fSYasuo Ohgaki  * *mod_data, *save_path, *session_name are guaranteed to have non-NULL values.
370e6c8640aSYasuo Ohgaki  */
PS_OPEN_FUNC(files)3717e70c1adSSascha Schumann PS_OPEN_FUNC(files)
3727e70c1adSSascha Schumann {
3737e70c1adSSascha Schumann 	ps_files *data;
374a10f0830SSascha Schumann 	const char *p, *last;
375a10f0830SSascha Schumann 	const char *argv[3];
376a10f0830SSascha Schumann 	int argc = 0;
377a10f0830SSascha Schumann 	size_t dirdepth = 0;
378a10f0830SSascha Schumann 	int filemode = 0600;
379a10f0830SSascha Schumann 
38032be6f26SWez Furlong 	if (*save_path == '\0') {
38132be6f26SWez Furlong 		/* if save path is an empty string, determine the temporary dir */
382bdeb220fSAnatol Belski 		save_path = php_get_temporary_directory();
3837ba84b88SIlia Alshanetsky 
384bdeb220fSAnatol Belski 		if (php_check_open_basedir(save_path)) {
385c9fab635SAlexey Zakhlestin 			return FAILURE;
3867ba84b88SIlia Alshanetsky 		}
38732be6f26SWez Furlong 	}
3889ece649fSJani Taskinen 
389a10f0830SSascha Schumann 	/* split up input parameter */
390a10f0830SSascha Schumann 	last = save_path;
391a10f0830SSascha Schumann 	p = strchr(save_path, ';');
392a10f0830SSascha Schumann 	while (p) {
393a10f0830SSascha Schumann 		argv[argc++] = last;
394a10f0830SSascha Schumann 		last = ++p;
395a10f0830SSascha Schumann 		p = strchr(p, ';');
396b8bc0f24SHannes Magnusson 		if (argc > 1) break;
397a10f0830SSascha Schumann 	}
398a10f0830SSascha Schumann 	argv[argc++] = last;
3997e70c1adSSascha Schumann 
400a10f0830SSascha Schumann 	if (argc > 1) {
4010b8401bfSSascha Schumann 		errno = 0;
4024d997f63SAnatol Belski 		dirdepth = (size_t) ZEND_STRTOL(argv[0], NULL, 10);
4030b8401bfSSascha Schumann 		if (errno == ERANGE) {
404de85bf40SJani Taskinen 			php_error(E_WARNING, "The first parameter in session.save_path is invalid");
405a10f0830SSascha Schumann 			return FAILURE;
406a10f0830SSascha Schumann 		}
407a10f0830SSascha Schumann 	}
4089ece649fSJani Taskinen 
409a10f0830SSascha Schumann 	if (argc > 2) {
410a10f0830SSascha Schumann 		errno = 0;
411512429ffSAnatol Belski 		filemode = (int)ZEND_STRTOL(argv[1], NULL, 8);
412a10f0830SSascha Schumann 		if (errno == ERANGE || filemode < 0 || filemode > 07777) {
413de85bf40SJani Taskinen 			php_error(E_WARNING, "The second parameter in session.save_path is invalid");
4140b8401bfSSascha Schumann 			return FAILURE;
4150b8401bfSSascha Schumann 		}
4167e70c1adSSascha Schumann 	}
417a10f0830SSascha Schumann 	save_path = argv[argc - 1];
418a10f0830SSascha Schumann 
4199ece649fSJani Taskinen 	data = ecalloc(1, sizeof(*data));
4209ece649fSJani Taskinen 
421a10f0830SSascha Schumann 	data->fd = -1;
422a10f0830SSascha Schumann 	data->dirdepth = dirdepth;
423a10f0830SSascha Schumann 	data->filemode = filemode;
42486cf74a1SSascha Schumann 	data->basedir_len = strlen(save_path);
42586cf74a1SSascha Schumann 	data->basedir = estrndup(save_path, data->basedir_len);
4269ece649fSJani Taskinen 
427a99f146eSFelipe Pena 	if (PS_GET_MOD_DATA()) {
428bdeb220fSAnatol Belski 		ps_close_files(mod_data);
429a99f146eSFelipe Pena 	}
430a10f0830SSascha Schumann 	PS_SET_MOD_DATA(data);
4319ece649fSJani Taskinen 
4327e70c1adSSascha Schumann 	return SUCCESS;
4337e70c1adSSascha Schumann }
4347e70c1adSSascha Schumann 
435e6c8640aSYasuo Ohgaki 
436e6c8640aSYasuo Ohgaki /*
437e6c8640aSYasuo Ohgaki  * Clean up opened resources.
438e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_CLOSE_ARGS in php_session.h
439e6c8640aSYasuo Ohgaki  * RETURN VALUE: SUCCESS. Must set PS module data(void **mod_data) to NULL.
440e6c8640aSYasuo Ohgaki  *
441e6c8640aSYasuo Ohgaki  * Files save handler closes open files and it's memory.
442dbb1298fSYasuo Ohgaki  * *mod_data is guaranteed to have non-NULL value.
443e6c8640aSYasuo Ohgaki  * PS_CLOSE_FUNC() must set *mod_data to NULL. PS_CLOSE_FUNC() should not
444e6c8640aSYasuo Ohgaki  * fail.
445e6c8640aSYasuo Ohgaki  */
PS_CLOSE_FUNC(files)4467e70c1adSSascha Schumann PS_CLOSE_FUNC(files)
4477e70c1adSSascha Schumann {
4487e70c1adSSascha Schumann 	PS_FILES_DATA;
4497e70c1adSSascha Schumann 
4504086197dSDaniel Beulshausen 	ps_files_close(data);
4514086197dSDaniel Beulshausen 
452de85bf40SJani Taskinen 	if (data->lastkey) {
45312d3e3d7SSascha Schumann 		efree(data->lastkey);
4544c711200SYasuo Ohgaki 		data->lastkey = NULL;
455de85bf40SJani Taskinen 	}
456de85bf40SJani Taskinen 
4577e70c1adSSascha Schumann 	efree(data->basedir);
4587e70c1adSSascha Schumann 	efree(data);
459de8bfb32SXinchen Hui 	PS_SET_MOD_DATA(NULL);
4607e70c1adSSascha Schumann 
4617e70c1adSSascha Schumann 	return SUCCESS;
4627e70c1adSSascha Schumann }
4637e70c1adSSascha Schumann 
464e6c8640aSYasuo Ohgaki 
465e6c8640aSYasuo Ohgaki /*
466e6c8640aSYasuo Ohgaki  * Read session data from opened resource.
467e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_READ_ARGS in php_session.h
468e6c8640aSYasuo Ohgaki  * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL session data to (zend_string **val)
469e6c8640aSYasuo Ohgaki  * for SUCCESS. NULL(default) for FAILUREs.
470e6c8640aSYasuo Ohgaki  *
471e6c8640aSYasuo Ohgaki  * Files save handler supports splitting session data into multiple
472e6c8640aSYasuo Ohgaki  * directories.
473dbb1298fSYasuo Ohgaki  * *mod_data, *key are guaranteed to have non-NULL values.
474e6c8640aSYasuo Ohgaki  */
PS_READ_FUNC(files)475a1b42e3fSSascha Schumann PS_READ_FUNC(files)
476a1b42e3fSSascha Schumann {
47718e0393bSAnatol Belski 	zend_long n = 0;
47845185642SAnatol Belski 	zend_stat_t sbuf;
479a1b42e3fSSascha Schumann 	PS_FILES_DATA;
480a1b42e3fSSascha Schumann 
4814a2e40bbSDmitry Stogov 	ps_files_open(data, ZSTR_VAL(key));
482de85bf40SJani Taskinen 	if (data->fd < 0) {
483a1b42e3fSSascha Schumann 		return FAILURE;
484de85bf40SJani Taskinen 	}
485de85bf40SJani Taskinen 
486f9b05124SAnatol Belski 	if (zend_fstat(data->fd, &sbuf)) {
487a1b42e3fSSascha Schumann 		return FAILURE;
488de85bf40SJani Taskinen 	}
489de85bf40SJani Taskinen 
4903647fc6fSXinchen Hui 	data->st_size = sbuf.st_size;
491de85bf40SJani Taskinen 
49226cb5355SSascha Schumann 	if (sbuf.st_size == 0) {
4934bd22cf1SDmitry Stogov 		*val = ZSTR_EMPTY_ALLOC();
49426cb5355SSascha Schumann 		return SUCCESS;
49526cb5355SSascha Schumann 	}
496de85bf40SJani Taskinen 
497c3e3c98eSAnatol Belski 	*val = zend_string_alloc(sbuf.st_size, 0);
498a1b42e3fSSascha Schumann 
499e1dd35bdSSascha Schumann #if defined(HAVE_PREAD)
5004a2e40bbSDmitry Stogov 	n = pread(data->fd, ZSTR_VAL(*val), ZSTR_LEN(*val), 0);
501702d7afcSSascha Schumann #else
502814fa2c7SSascha Schumann 	lseek(data->fd, 0, SEEK_SET);
50318e0393bSAnatol Belski #ifdef PHP_WIN32
50418e0393bSAnatol Belski 	{
5054a2e40bbSDmitry Stogov 		unsigned int to_read = ZSTR_LEN(*val) > UINT_MAX ? UINT_MAX : (unsigned int)ZSTR_LEN(*val);
5064a2e40bbSDmitry Stogov 		char *buf = ZSTR_VAL(*val);
50718e0393bSAnatol Belski 		int read_in;
50818e0393bSAnatol Belski 
50918e0393bSAnatol Belski 		do {
51018e0393bSAnatol Belski 			read_in = _read(data->fd, buf, to_read);
51118e0393bSAnatol Belski 
51218e0393bSAnatol Belski 			n += read_in;
51318e0393bSAnatol Belski 			buf = read_in > -1 ? buf + read_in : 0;
5144a2e40bbSDmitry Stogov 			to_read = read_in > -1 ? (ZSTR_LEN(*val) - n > UINT_MAX ? UINT_MAX : (unsigned int)(ZSTR_LEN(*val) - n)): 0;
51518e0393bSAnatol Belski 
51618e0393bSAnatol Belski 		} while(read_in > 0);
51718e0393bSAnatol Belski 
51818e0393bSAnatol Belski 	}
51918e0393bSAnatol Belski #else
5204a2e40bbSDmitry Stogov 	n = read(data->fd, ZSTR_VAL(*val), ZSTR_LEN(*val));
52118e0393bSAnatol Belski #endif
522702d7afcSSascha Schumann #endif
523702d7afcSSascha Schumann 
52418e0393bSAnatol Belski 	if (n != (zend_long)sbuf.st_size) {
525de85bf40SJani Taskinen 		if (n == -1) {
526bdeb220fSAnatol Belski 			php_error_docref(NULL, E_WARNING, "read failed: %s (%d)", strerror(errno), errno);
527de85bf40SJani Taskinen 		} else {
528bdeb220fSAnatol Belski 			php_error_docref(NULL, E_WARNING, "read returned less bytes than requested");
529de85bf40SJani Taskinen 		}
5305eb1f92fSDmitry Stogov 		zend_string_release_ex(*val, 0);
5314bd22cf1SDmitry Stogov 		*val =  ZSTR_EMPTY_ALLOC();
532a1b42e3fSSascha Schumann 		return FAILURE;
533a1b42e3fSSascha Schumann 	}
5349ece649fSJani Taskinen 
5352f7cc862SYasuo Ohgaki 	ZSTR_VAL(*val)[ZSTR_LEN(*val)] = '\0';
536a1b42e3fSSascha Schumann 	return SUCCESS;
537a1b42e3fSSascha Schumann }
538a1b42e3fSSascha Schumann 
539e6c8640aSYasuo Ohgaki 
540e6c8640aSYasuo Ohgaki /*
541e6c8640aSYasuo Ohgaki  * Write session data.
542e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_WRITE_ARGS in php_session.h
543e6c8640aSYasuo Ohgaki  * RETURN VALUE: SUCCESS or FAILURE.
544e6c8640aSYasuo Ohgaki  *
545e6c8640aSYasuo Ohgaki  * PS_WRITE_FUNC() must write session data(zend_string *val) unconditionally.
546dbb1298fSYasuo Ohgaki  * *mod_data, *key, *val are guaranteed to have non-NULL values.
547e6c8640aSYasuo Ohgaki  */
PS_WRITE_FUNC(files)548a1b42e3fSSascha Schumann PS_WRITE_FUNC(files)
549a1b42e3fSSascha Schumann {
550a1b42e3fSSascha Schumann 	PS_FILES_DATA;
551a1b42e3fSSascha Schumann 
552e6c8640aSYasuo Ohgaki 	return ps_files_write(data, key, val);
553e6c8640aSYasuo Ohgaki }
554a1b42e3fSSascha Schumann 
5559ece649fSJani Taskinen 
556e6c8640aSYasuo Ohgaki /*
557e6c8640aSYasuo Ohgaki  * Update session data modification/access time stamp.
558e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_UPDATE_TIMESTAMP_ARGS in php_session.h
559e6c8640aSYasuo Ohgaki  * RETURN VALUE: SUCCESS or FAILURE.
560e6c8640aSYasuo Ohgaki  *
561e6c8640aSYasuo Ohgaki  * PS_UPDATE_TIMESTAMP_FUNC() updates time stamp(mtime) so that active session
562e6c8640aSYasuo Ohgaki  * data files will not be purged by GC. If session data storage does not need to
563e6c8640aSYasuo Ohgaki  * update timestamp, it should return SUCCESS simply. (e.g. Memcache)
564dbb1298fSYasuo Ohgaki  * *mod_data, *key, *val are guaranteed to have non-NULL values.
565e6c8640aSYasuo Ohgaki  *
566e6c8640aSYasuo Ohgaki  * NOTE: Updating access timestamp at PS_READ_FUNC() may extend life of obsolete
567dbb1298fSYasuo Ohgaki  * session data. Use of PS_UPDATE_TIMESTAMP_FUNC() is preferred whenever it is
568e6c8640aSYasuo Ohgaki  * possible.
569e6c8640aSYasuo Ohgaki  */
571e6c8640aSYasuo Ohgaki {
572e6c8640aSYasuo Ohgaki 	char buf[MAXPATHLEN];
573e6c8640aSYasuo Ohgaki 	int ret;
574e6c8640aSYasuo Ohgaki 	PS_FILES_DATA;
575e6c8640aSYasuo Ohgaki 
5764a2e40bbSDmitry Stogov 	if (!ps_files_path_create(buf, sizeof(buf), data, ZSTR_VAL(key))) {
577e6c8640aSYasuo Ohgaki 		return FAILURE;
578de85bf40SJani Taskinen 	}
579814fa2c7SSascha Schumann 
580e6c8640aSYasuo Ohgaki 	/* Update mtime */
58129dc0470SPeter Kokot 	ret = VCWD_UTIME(buf, NULL);
582e6c8640aSYasuo Ohgaki 	if (ret == -1) {
583e6c8640aSYasuo Ohgaki 		/* New session ID, create data file */
584e6c8640aSYasuo Ohgaki 		return ps_files_write(data, key, val);
5856baa5bc2SSascha Schumann 	}
586a1b42e3fSSascha Schumann 
587a1b42e3fSSascha Schumann 	return SUCCESS;
588a1b42e3fSSascha Schumann }
589a1b42e3fSSascha Schumann 
590e6c8640aSYasuo Ohgaki 
591e6c8640aSYasuo Ohgaki /*
592e6c8640aSYasuo Ohgaki  * Delete session data.
593e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_DESTROY_ARGS in php_session.h
594e6c8640aSYasuo Ohgaki  * RETURN VALUE: SUCCESS or FAILURE.
595e6c8640aSYasuo Ohgaki  *
596e6c8640aSYasuo Ohgaki  * PS_DESTROY_FUNC() must remove the session data specified by *key from
597e6c8640aSYasuo Ohgaki  * session data storage unconditionally. It must not return FAILURE for
598e6c8640aSYasuo Ohgaki  * non-existent session data.
599dbb1298fSYasuo Ohgaki  * *mod_data, *key are guaranteed to have non-NULL values.
600e6c8640aSYasuo Ohgaki  */
PS_DESTROY_FUNC(files)6011798a018SZeev Suraski PS_DESTROY_FUNC(files)
602a1b42e3fSSascha Schumann {
60334b3dc9bSZeev Suraski 	char buf[MAXPATHLEN];
604a1b42e3fSSascha Schumann 	PS_FILES_DATA;
605a1b42e3fSSascha Schumann 
6064a2e40bbSDmitry Stogov 	if (!ps_files_path_create(buf, sizeof(buf), data, ZSTR_VAL(key))) {
6077e70c1adSSascha Schumann 		return FAILURE;
608de85bf40SJani Taskinen 	}
609de85bf40SJani Taskinen 
610101d925bSIlia Alshanetsky 	if (data->fd != -1) {
611101d925bSIlia Alshanetsky 		ps_files_close(data);
6129ece649fSJani Taskinen 
613101d925bSIlia Alshanetsky 		if (VCWD_UNLINK(buf) == -1) {
614d58b3869SIlia Alshanetsky 			/* This is a little safety check for instances when we are dealing with a regenerated session
6159ece649fSJani Taskinen 			 * that was not yet written to disk. */
616d58b3869SIlia Alshanetsky 			if (!VCWD_ACCESS(buf, F_OK)) {
617d58b3869SIlia Alshanetsky 				return FAILURE;
618d58b3869SIlia Alshanetsky 			}
619101d925bSIlia Alshanetsky 		}
620277b0e15SSascha Schumann 	}
621a1b42e3fSSascha Schumann 
622a1b42e3fSSascha Schumann 	return SUCCESS;
623a1b42e3fSSascha Schumann }
624a1b42e3fSSascha Schumann 
625e6c8640aSYasuo Ohgaki 
626e6c8640aSYasuo Ohgaki /*
627e6c8640aSYasuo Ohgaki  * Cleanup expired session data.
628e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_GC_ARGS in php_session.h
629e6c8640aSYasuo Ohgaki  * RETURN VALUE: SUCCESS or FAILURE. Number of deleted records(int *nrdels(default=-1)).
630e6c8640aSYasuo Ohgaki  *
631e6c8640aSYasuo Ohgaki  * PS_GC_FUNC() must remove session data that are not accessed
632e6c8640aSYasuo Ohgaki  * 'session.maxlifetime'(seconds). If storage does not need manual GC, it
633e6c8640aSYasuo Ohgaki  * may return SUCCESS simply. (e.g. Memcache) It must set number of records
634e6c8640aSYasuo Ohgaki  * deleted(nrdels).
635dbb1298fSYasuo Ohgaki  * *mod_data is guaranteed to have non-NULL value.
636e6c8640aSYasuo Ohgaki  */
PS_GC_FUNC(files)6379ece649fSJani Taskinen PS_GC_FUNC(files)
638a1b42e3fSSascha Schumann {
6397e50dda9SSascha Schumann 	PS_FILES_DATA;
6409ece649fSJani Taskinen 
641e6c8640aSYasuo Ohgaki 	/* We don't perform any cleanup, if dirdepth is larger than 0.
6427e50dda9SSascha Schumann 	   we return SUCCESS, since all cleanup should be handled by
6437e50dda9SSascha Schumann 	   an external entity (i.e. find -ctime x | xargs rm) */
6449ece649fSJani Taskinen 
645de85bf40SJani Taskinen 	if (data->dirdepth == 0) {
646bdeb220fSAnatol Belski 		*nrdels = ps_files_cleanup_dir(data->basedir, maxlifetime);
647a4a2f66eSYasuo Ohgaki 	} else {
648a4a2f66eSYasuo Ohgaki 		*nrdels = -1; // Cannot process multiple depth save dir
649de85bf40SJani Taskinen 	}
650de85bf40SJani Taskinen 
651a4a2f66eSYasuo Ohgaki 	return *nrdels;
652a1b42e3fSSascha Schumann }
6539c558821SRasmus Lerdorf 
654e6c8640aSYasuo Ohgaki 
655e6c8640aSYasuo Ohgaki /*
656e6c8640aSYasuo Ohgaki  * Create session ID.
657e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_CREATE_SID_ARGS in php_session.h
658e6c8640aSYasuo Ohgaki  * RETURN VALUE: Valid session ID(zend_string *) or NULL for FAILURE.
659e6c8640aSYasuo Ohgaki  *
660e6c8640aSYasuo Ohgaki  * PS_CREATE_SID_FUNC() must check collision. i.e. Check session data if
661e6c8640aSYasuo Ohgaki  * new sid exists already.
662dbb1298fSYasuo Ohgaki  * *mod_data is guaranteed to have non-NULL value.
663e6c8640aSYasuo Ohgaki  * NOTE: Default php_session_create_id() does not check collision. If
664e6c8640aSYasuo Ohgaki  * NULL is returned, session module create new ID by using php_session_create_id().
665e6c8640aSYasuo Ohgaki  * If php_session_create_id() fails due to invalid configuration, it raises E_ERROR.
666e6c8640aSYasuo Ohgaki  * NULL return value checks from php_session_create_id() is not required generally.
667e6c8640aSYasuo Ohgaki  */
PS_CREATE_SID_FUNC(files)66825e8fcc8SYasuo Ohgaki PS_CREATE_SID_FUNC(files)
66925e8fcc8SYasuo Ohgaki {
6703647fc6fSXinchen Hui 	zend_string *sid;
67182b0e8beSYasuo Ohgaki 	int maxfail = 3;
67225e8fcc8SYasuo Ohgaki 	PS_FILES_DATA;
67325e8fcc8SYasuo Ohgaki 
67482b0e8beSYasuo Ohgaki 	do {
675bdeb220fSAnatol Belski 		sid = php_session_create_id((void**)&data);
676e6c8640aSYasuo Ohgaki 		if (!sid) {
677e6c8640aSYasuo Ohgaki 			if (--maxfail < 0) {
678e6c8640aSYasuo Ohgaki 				return NULL;
679e6c8640aSYasuo Ohgaki 			} else {
680e6c8640aSYasuo Ohgaki 				continue;
681e6c8640aSYasuo Ohgaki 			}
682e6c8640aSYasuo Ohgaki 		}
68382b0e8beSYasuo Ohgaki 		/* Check collision */
684e6c8640aSYasuo Ohgaki 		/* FIXME: mod_data(data) should not be NULL (User handler could be NULL) */
6854a2e40bbSDmitry Stogov 		if (data && ps_files_key_exists(data, ZSTR_VAL(sid)) == SUCCESS) {
68682b0e8beSYasuo Ohgaki 			if (sid) {
6875eb1f92fSDmitry Stogov 				zend_string_release_ex(sid, 0);
68882b0e8beSYasuo Ohgaki 				sid = NULL;
68982b0e8beSYasuo Ohgaki 			}
690e6c8640aSYasuo Ohgaki 			if (--maxfail < 0) {
69182b0e8beSYasuo Ohgaki 				return NULL;
69282b0e8beSYasuo Ohgaki 			}
69382b0e8beSYasuo Ohgaki 		}
69482b0e8beSYasuo Ohgaki 	} while(!sid);
69525e8fcc8SYasuo Ohgaki 
69625e8fcc8SYasuo Ohgaki 	return sid;
69725e8fcc8SYasuo Ohgaki }
69825e8fcc8SYasuo Ohgaki 
69925e8fcc8SYasuo Ohgaki 
700e6c8640aSYasuo Ohgaki /*
701e6c8640aSYasuo Ohgaki  * Check session ID existence for use_strict_mode support.
702e6c8640aSYasuo Ohgaki  * PARAMETERS: PS_VALIDATE_SID_ARGS in php_session.h
703e6c8640aSYasuo Ohgaki  * RETURN VALUE: SUCCESS or FAILURE.
704e6c8640aSYasuo Ohgaki  *
705dbb1298fSYasuo Ohgaki  * Return SUCCESS for valid key(already existing session).
706e6c8640aSYasuo Ohgaki  * Return FAILURE for invalid key(non-existing session).
707dbb1298fSYasuo Ohgaki  * *mod_data, *key are guaranteed to have non-NULL values.
708e6c8640aSYasuo Ohgaki  */
PS_VALIDATE_SID_FUNC(files)709e6c8640aSYasuo Ohgaki PS_VALIDATE_SID_FUNC(files)
710e6c8640aSYasuo Ohgaki {
711e6c8640aSYasuo Ohgaki 	PS_FILES_DATA;
712e6c8640aSYasuo Ohgaki 
7134a2e40bbSDmitry Stogov 	return ps_files_key_exists(data, ZSTR_VAL(key));
714e6c8640aSYasuo Ohgaki }