mod_files.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2018 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Author: Sascha Schumann <sascha@schumann.cx> |
  16. +----------------------------------------------------------------------+
  17. */
  18. /**************************************************************************
  19. * Files save handler should be used as reference implementations of session
  20. * save handlers. PS_* functions are called as follows with standard usage.
  21. *
  22. * PS_OPEN_FUNC() - Create module data that manages save handler.
  23. * PS_CREATE_SID() and/or PS_VALIDATE_SID()
  24. * - PS_CREATE_ID() is called if session ID(key) is not
  25. * provided or invalid. PS_VALIDATE_SID() is called to
  26. * verify session ID already exists or not to mitigate
  27. * session adoption vulnerability risk.
  28. * PS_READ_FUNC() - Read data from storage.
  29. * PS_GC_FUNC() - Perform GC. Called by probability
  30. * (session.gc_probability/session.gc_divisor).
  31. * PS_WRITE_FUNC() or PS_UPDATE_TIMESTAMP()
  32. * - Write session data or update session data timestamp.
  33. * It depends on session data change.
  34. * PS_CLOSE_FUNC() - Clean up module data created by PS_OPEN_FUNC().
  35. *
  36. * Session module guarantees PS_OPEN_FUNC() is called before calling other
  37. * PS_*_FUNC() functions. Other than this, session module may call any
  38. * PS_*_FUNC() at any time. You may assume non null *mod_data created by
  39. * PS_OPEN_FUNC() is passed to PS_*_FUNC().
  40. *
  41. * NOTE:
  42. * - Save handlers _MUST_NOT_ change/refer PS() values.
  43. * i.e. PS(id), PS(session_status), PS(mod) and any other PS() values.
  44. * Use only function parameters passed from session module.
  45. * - Save handler _MUST_ use PS_GET_MOD_DATA()/PS_SET_MOD_DATA() macro to
  46. * set/get save handler module data(mod_data). mod_data contains
  47. * data required by PS modules. It will not be NULL except PS_OPEN_FUNC().
  48. * - Refer to PS_* macros in php_session.h for function/parameter definitions.
  49. * - Returning FAILURE state from PS_* function results in raising errors.
  50. * Avoid failure state as much as possible.
  51. * - Use static ps_[module name]_[function name] functions for internal use.
  52. *************************************************************************/
  53. #include "php.h"
  54. #include <sys/stat.h>
  55. #include <sys/types.h>
  56. #if HAVE_SYS_FILE_H
  57. #include <sys/file.h>
  58. #endif
  59. #if HAVE_DIRENT_H
  60. #include <dirent.h>
  61. #endif
  62. #ifdef PHP_WIN32
  63. #include "win32/readdir.h"
  64. #endif
  65. #include <time.h>
  66. #include <fcntl.h>
  67. #include <errno.h>
  68. #if HAVE_UNISTD_H
  69. #include <unistd.h>
  70. #endif
  71. #include "php_session.h"
  72. #include "mod_files.h"
  73. #include "ext/standard/flock_compat.h"
  74. #include "php_open_temporary_file.h"
  75. #define FILE_PREFIX "sess_"
  76. #ifdef PHP_WIN32
  77. # ifndef O_NOFOLLOW
  78. # define O_NOFOLLOW 0
  79. # endif
  80. #endif
  81. typedef struct {
  82. char *lastkey;
  83. char *basedir;
  84. size_t basedir_len;
  85. size_t dirdepth;
  86. size_t st_size;
  87. int filemode;
  88. int fd;
  89. } ps_files;
  90. const ps_module ps_mod_files = {
  91. /* New save handlers MUST use PS_MOD_UPDATE_TIMESTAMP macro */
  92. PS_MOD_UPDATE_TIMESTAMP(files)
  93. };
  94. static char *ps_files_path_create(char *buf, size_t buflen, ps_files *data, const char *key)
  95. {
  96. size_t key_len;
  97. const char *p;
  98. int i;
  99. size_t n;
  100. key_len = strlen(key);
  101. if (!data || key_len <= data->dirdepth ||
  102. buflen < (strlen(data->basedir) + 2 * data->dirdepth + key_len + 5 + sizeof(FILE_PREFIX))) {
  103. return NULL;
  104. }
  105. p = key;
  106. memcpy(buf, data->basedir, data->basedir_len);
  107. n = data->basedir_len;
  108. buf[n++] = PHP_DIR_SEPARATOR;
  109. for (i = 0; i < (int)data->dirdepth; i++) {
  110. buf[n++] = *p++;
  111. buf[n++] = PHP_DIR_SEPARATOR;
  112. }
  113. memcpy(buf + n, FILE_PREFIX, sizeof(FILE_PREFIX) - 1);
  114. n += sizeof(FILE_PREFIX) - 1;
  115. memcpy(buf + n, key, key_len);
  116. n += key_len;
  117. buf[n] = '\0';
  118. return buf;
  119. }
  120. #ifndef O_BINARY
  121. # define O_BINARY 0
  122. #endif
  123. static void ps_files_close(ps_files *data)
  124. {
  125. if (data->fd != -1) {
  126. #ifdef PHP_WIN32
  127. /* On Win32 locked files that are closed without being explicitly unlocked
  128. will be unlocked only when "system resources become available". */
  129. flock(data->fd, LOCK_UN);
  130. #endif
  131. close(data->fd);
  132. data->fd = -1;
  133. }
  134. }
  135. static void ps_files_open(ps_files *data, const char *key)
  136. {
  137. char buf[MAXPATHLEN];
  138. #if !defined(O_NOFOLLOW) || !defined(PHP_WIN32)
  139. struct stat sbuf;
  140. #endif
  141. int ret;
  142. if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) {
  143. if (data->lastkey) {
  144. efree(data->lastkey);
  145. data->lastkey = NULL;
  146. }
  147. ps_files_close(data);
  148. if (php_session_valid_key(key) == FAILURE) {
  149. 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 '-,'");
  150. return;
  151. }
  152. if (!ps_files_path_create(buf, sizeof(buf), data, key)) {
  153. 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);
  154. return;
  155. }
  156. data->lastkey = estrdup(key);
  157. /* O_NOFOLLOW to prevent us from following evil symlinks */
  158. #ifdef O_NOFOLLOW
  159. data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY | O_NOFOLLOW, data->filemode);
  160. #else
  161. /* Check to make sure that the opened file is not outside of allowable dirs.
  162. This is not 100% safe but it's hard to do something better without O_NOFOLLOW */
  163. if(PG(open_basedir) && lstat(buf, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) && php_check_open_basedir(buf)) {
  164. return;
  165. }
  166. data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, data->filemode);
  167. #endif
  168. if (data->fd != -1) {
  169. #ifndef PHP_WIN32
  170. /* check that this session file was created by us or root – we
  171. don't want to end up accepting the sessions of another webapp
  172. If the process is ran by root, we ignore session file ownership
  173. Use case: session is initiated by Apache under non-root and then
  174. accessed by backend with root permissions to execute some system tasks.
  175. */
  176. if (zend_fstat(data->fd, &sbuf) || (sbuf.st_uid != 0 && sbuf.st_uid != getuid() && sbuf.st_uid != geteuid() && getuid() != 0)) {
  177. close(data->fd);
  178. data->fd = -1;
  179. php_error_docref(NULL, E_WARNING, "Session data file is not created by your uid");
  180. return;
  181. }
  182. #endif
  183. do {
  184. ret = flock(data->fd, LOCK_EX);
  185. } while (ret == -1 && errno == EINTR);
  186. #ifdef F_SETFD
  187. # ifndef FD_CLOEXEC
  188. # define FD_CLOEXEC 1
  189. # endif
  190. if (fcntl(data->fd, F_SETFD, FD_CLOEXEC)) {
  191. php_error_docref(NULL, E_WARNING, "fcntl(%d, F_SETFD, FD_CLOEXEC) failed: %s (%d)", data->fd, strerror(errno), errno);
  192. }
  193. #endif
  194. } else {
  195. php_error_docref(NULL, E_WARNING, "open(%s, O_RDWR) failed: %s (%d)", buf, strerror(errno), errno);
  196. }
  197. }
  198. }
  199. static int ps_files_write(ps_files *data, zend_string *key, zend_string *val)
  200. {
  201. size_t n = 0;
  202. /* PS(id) may be changed by calling session_regenerate_id().
  203. Re-initialization should be tried here. ps_files_open() checks
  204. data->lastkey and reopen when it is needed. */
  205. ps_files_open(data, ZSTR_VAL(key));
  206. if (data->fd < 0) {
  207. return FAILURE;
  208. }
  209. /* Truncate file if the amount of new data is smaller than the existing data set. */
  210. if (ZSTR_LEN(val) < data->st_size) {
  211. php_ignore_value(ftruncate(data->fd, 0));
  212. }
  213. #if defined(HAVE_PWRITE)
  214. n = pwrite(data->fd, ZSTR_VAL(val), ZSTR_LEN(val), 0);
  215. #else
  216. lseek(data->fd, 0, SEEK_SET);
  217. #ifdef PHP_WIN32
  218. {
  219. unsigned int to_write = ZSTR_LEN(val) > UINT_MAX ? UINT_MAX : (unsigned int)ZSTR_LEN(val);
  220. char *buf = ZSTR_VAL(val);
  221. int wrote;
  222. do {
  223. wrote = _write(data->fd, buf, to_write);
  224. n += wrote;
  225. buf = wrote > -1 ? buf + wrote : 0;
  226. to_write = wrote > -1 ? (ZSTR_LEN(val) - n > UINT_MAX ? UINT_MAX : (unsigned int)(ZSTR_LEN(val) - n)): 0;
  227. } while(wrote > 0);
  228. }
  229. #else
  230. n = write(data->fd, ZSTR_VAL(val), ZSTR_LEN(val));
  231. #endif
  232. #endif
  233. if (n != ZSTR_LEN(val)) {
  234. if (n == (size_t)-1) {
  235. php_error_docref(NULL, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
  236. } else {
  237. php_error_docref(NULL, E_WARNING, "write wrote less bytes than requested");
  238. }
  239. return FAILURE;
  240. }
  241. return SUCCESS;
  242. }
  243. static int ps_files_cleanup_dir(const char *dirname, zend_long maxlifetime)
  244. {
  245. DIR *dir;
  246. char dentry[sizeof(struct dirent) + MAXPATHLEN];
  247. struct dirent *entry = (struct dirent *) &dentry;
  248. zend_stat_t sbuf;
  249. char buf[MAXPATHLEN];
  250. time_t now;
  251. int nrdels = 0;
  252. size_t dirname_len;
  253. dir = opendir(dirname);
  254. if (!dir) {
  255. php_error_docref(NULL, E_NOTICE, "ps_files_cleanup_dir: opendir(%s) failed: %s (%d)", dirname, strerror(errno), errno);
  256. return (0);
  257. }
  258. time(&now);
  259. dirname_len = strlen(dirname);
  260. if (dirname_len >= MAXPATHLEN) {
  261. php_error_docref(NULL, E_NOTICE, "ps_files_cleanup_dir: dirname(%s) is too long", dirname);
  262. closedir(dir);
  263. return (0);
  264. }
  265. /* Prepare buffer (dirname never changes) */
  266. memcpy(buf, dirname, dirname_len);
  267. buf[dirname_len] = PHP_DIR_SEPARATOR;
  268. while (php_readdir_r(dir, (struct dirent *) dentry, &entry) == 0 && entry) {
  269. /* does the file start with our prefix? */
  270. if (!strncmp(entry->d_name, FILE_PREFIX, sizeof(FILE_PREFIX) - 1)) {
  271. size_t entry_len = strlen(entry->d_name);
  272. /* does it fit into our buffer? */
  273. if (entry_len + dirname_len + 2 < MAXPATHLEN) {
  274. /* create the full path.. */
  275. memcpy(buf + dirname_len + 1, entry->d_name, entry_len);
  276. /* NUL terminate it and */
  277. buf[dirname_len + entry_len + 1] = '\0';
  278. /* check whether its last access was more than maxlifetime ago */
  279. if (VCWD_STAT(buf, &sbuf) == 0 &&
  280. (now - sbuf.st_mtime) > maxlifetime) {
  281. VCWD_UNLINK(buf);
  282. nrdels++;
  283. }
  284. }
  285. }
  286. }
  287. closedir(dir);
  288. return (nrdels);
  289. }
  290. static int ps_files_key_exists(ps_files *data, const char *key)
  291. {
  292. char buf[MAXPATHLEN];
  293. zend_stat_t sbuf;
  294. if (!key || !ps_files_path_create(buf, sizeof(buf), data, key)) {
  295. return FAILURE;
  296. }
  297. if (VCWD_STAT(buf, &sbuf)) {
  298. return FAILURE;
  299. }
  300. return SUCCESS;
  301. }
  302. #define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA()
  303. /*
  304. * Open save handler. Setup resources that are needed by the handler.
  305. * PARAMETERS: PS_OPEN_ARGS in php_session.h
  306. * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL valid module data
  307. * (void **mod_data) with SUCCESS, NULL(default) for FAILUREs.
  308. *
  309. * Files save handler checks/create save_path directory and setup ps_files data.
  310. * Note that files save handler supports splitting session data into multiple
  311. * directories.
  312. * *mod_data, *save_path, *session_name are guaranteed to have non-NULL values.
  313. */
  314. PS_OPEN_FUNC(files)
  315. {
  316. ps_files *data;
  317. const char *p, *last;
  318. const char *argv[3];
  319. int argc = 0;
  320. size_t dirdepth = 0;
  321. int filemode = 0600;
  322. if (*save_path == '\0') {
  323. /* if save path is an empty string, determine the temporary dir */
  324. save_path = php_get_temporary_directory();
  325. if (php_check_open_basedir(save_path)) {
  326. return FAILURE;
  327. }
  328. }
  329. /* split up input parameter */
  330. last = save_path;
  331. p = strchr(save_path, ';');
  332. while (p) {
  333. argv[argc++] = last;
  334. last = ++p;
  335. p = strchr(p, ';');
  336. if (argc > 1) break;
  337. }
  338. argv[argc++] = last;
  339. if (argc > 1) {
  340. errno = 0;
  341. dirdepth = (size_t) ZEND_STRTOL(argv[0], NULL, 10);
  342. if (errno == ERANGE) {
  343. php_error(E_WARNING, "The first parameter in session.save_path is invalid");
  344. return FAILURE;
  345. }
  346. }
  347. if (argc > 2) {
  348. errno = 0;
  349. filemode = (int)ZEND_STRTOL(argv[1], NULL, 8);
  350. if (errno == ERANGE || filemode < 0 || filemode > 07777) {
  351. php_error(E_WARNING, "The second parameter in session.save_path is invalid");
  352. return FAILURE;
  353. }
  354. }
  355. save_path = argv[argc - 1];
  356. data = ecalloc(1, sizeof(*data));
  357. data->fd = -1;
  358. data->dirdepth = dirdepth;
  359. data->filemode = filemode;
  360. data->basedir_len = strlen(save_path);
  361. data->basedir = estrndup(save_path, data->basedir_len);
  362. if (PS_GET_MOD_DATA()) {
  363. ps_close_files(mod_data);
  364. }
  365. PS_SET_MOD_DATA(data);
  366. return SUCCESS;
  367. }
  368. /*
  369. * Clean up opened resources.
  370. * PARAMETERS: PS_CLOSE_ARGS in php_session.h
  371. * RETURN VALUE: SUCCESS. Must set PS module data(void **mod_data) to NULL.
  372. *
  373. * Files save handler closes open files and it's memory.
  374. * *mod_data is guaranteed to have non-NULL value.
  375. * PS_CLOSE_FUNC() must set *mod_data to NULL. PS_CLOSE_FUNC() should not
  376. * fail.
  377. */
  378. PS_CLOSE_FUNC(files)
  379. {
  380. PS_FILES_DATA;
  381. ps_files_close(data);
  382. if (data->lastkey) {
  383. efree(data->lastkey);
  384. data->lastkey = NULL;
  385. }
  386. efree(data->basedir);
  387. efree(data);
  388. PS_SET_MOD_DATA(NULL);
  389. return SUCCESS;
  390. }
  391. /*
  392. * Read session data from opened resource.
  393. * PARAMETERS: PS_READ_ARGS in php_session.h
  394. * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL session data to (zend_string **val)
  395. * for SUCCESS. NULL(default) for FAILUREs.
  396. *
  397. * Files save handler supports splitting session data into multiple
  398. * directories.
  399. * *mod_data, *key are guaranteed to have non-NULL values.
  400. */
  401. PS_READ_FUNC(files)
  402. {
  403. zend_long n = 0;
  404. zend_stat_t sbuf;
  405. PS_FILES_DATA;
  406. ps_files_open(data, ZSTR_VAL(key));
  407. if (data->fd < 0) {
  408. return FAILURE;
  409. }
  410. if (zend_fstat(data->fd, &sbuf)) {
  411. return FAILURE;
  412. }
  413. data->st_size = sbuf.st_size;
  414. if (sbuf.st_size == 0) {
  415. *val = ZSTR_EMPTY_ALLOC();
  416. return SUCCESS;
  417. }
  418. *val = zend_string_alloc(sbuf.st_size, 0);
  419. #if defined(HAVE_PREAD)
  420. n = pread(data->fd, ZSTR_VAL(*val), ZSTR_LEN(*val), 0);
  421. #else
  422. lseek(data->fd, 0, SEEK_SET);
  423. #ifdef PHP_WIN32
  424. {
  425. unsigned int to_read = ZSTR_LEN(*val) > UINT_MAX ? UINT_MAX : (unsigned int)ZSTR_LEN(*val);
  426. char *buf = ZSTR_VAL(*val);
  427. int read_in;
  428. do {
  429. read_in = _read(data->fd, buf, to_read);
  430. n += read_in;
  431. buf = read_in > -1 ? buf + read_in : 0;
  432. to_read = read_in > -1 ? (ZSTR_LEN(*val) - n > UINT_MAX ? UINT_MAX : (unsigned int)(ZSTR_LEN(*val) - n)): 0;
  433. } while(read_in > 0);
  434. }
  435. #else
  436. n = read(data->fd, ZSTR_VAL(*val), ZSTR_LEN(*val));
  437. #endif
  438. #endif
  439. if (n != (zend_long)sbuf.st_size) {
  440. if (n == -1) {
  441. php_error_docref(NULL, E_WARNING, "read failed: %s (%d)", strerror(errno), errno);
  442. } else {
  443. php_error_docref(NULL, E_WARNING, "read returned less bytes than requested");
  444. }
  445. zend_string_release_ex(*val, 0);
  446. *val = ZSTR_EMPTY_ALLOC();
  447. return FAILURE;
  448. }
  449. ZSTR_VAL(*val)[ZSTR_LEN(*val)] = '\0';
  450. return SUCCESS;
  451. }
  452. /*
  453. * Write session data.
  454. * PARAMETERS: PS_WRITE_ARGS in php_session.h
  455. * RETURN VALUE: SUCCESS or FAILURE.
  456. *
  457. * PS_WRITE_FUNC() must write session data(zend_string *val) unconditionally.
  458. * *mod_data, *key, *val are guaranteed to have non-NULL values.
  459. */
  460. PS_WRITE_FUNC(files)
  461. {
  462. PS_FILES_DATA;
  463. return ps_files_write(data, key, val);
  464. }
  465. /*
  466. * Update session data modification/access time stamp.
  467. * PARAMETERS: PS_UPDATE_TIMESTAMP_ARGS in php_session.h
  468. * RETURN VALUE: SUCCESS or FAILURE.
  469. *
  470. * PS_UPDATE_TIMESTAMP_FUNC() updates time stamp(mtime) so that active session
  471. * data files will not be purged by GC. If session data storage does not need to
  472. * update timestamp, it should return SUCCESS simply. (e.g. Memcache)
  473. * *mod_data, *key, *val are guaranteed to have non-NULL values.
  474. *
  475. * NOTE: Updating access timestamp at PS_READ_FUNC() may extend life of obsolete
  476. * session data. Use of PS_UPDATE_TIMESTAMP_FUNC() is preferred whenever it is
  477. * possible.
  478. */
  479. PS_UPDATE_TIMESTAMP_FUNC(files)
  480. {
  481. char buf[MAXPATHLEN];
  482. struct utimbuf newtimebuf;
  483. struct utimbuf *newtime = &newtimebuf;
  484. int ret;
  485. PS_FILES_DATA;
  486. if (!ps_files_path_create(buf, sizeof(buf), data, ZSTR_VAL(key))) {
  487. return FAILURE;
  488. }
  489. /* Update mtime */
  490. #ifdef HAVE_UTIME_NULL
  491. newtime = NULL;
  492. #else
  493. newtime->modtime = newtime->actime = time(NULL);
  494. #endif
  495. ret = VCWD_UTIME(buf, newtime);
  496. if (ret == -1) {
  497. /* New session ID, create data file */
  498. return ps_files_write(data, key, val);
  499. }
  500. return SUCCESS;
  501. }
  502. /*
  503. * Delete session data.
  504. * PARAMETERS: PS_DESTROY_ARGS in php_session.h
  505. * RETURN VALUE: SUCCESS or FAILURE.
  506. *
  507. * PS_DESTROY_FUNC() must remove the session data specified by *key from
  508. * session data storage unconditionally. It must not return FAILURE for
  509. * non-existent session data.
  510. * *mod_data, *key are guaranteed to have non-NULL values.
  511. */
  512. PS_DESTROY_FUNC(files)
  513. {
  514. char buf[MAXPATHLEN];
  515. PS_FILES_DATA;
  516. if (!ps_files_path_create(buf, sizeof(buf), data, ZSTR_VAL(key))) {
  517. return FAILURE;
  518. }
  519. if (data->fd != -1) {
  520. ps_files_close(data);
  521. if (VCWD_UNLINK(buf) == -1) {
  522. /* This is a little safety check for instances when we are dealing with a regenerated session
  523. * that was not yet written to disk. */
  524. if (!VCWD_ACCESS(buf, F_OK)) {
  525. return FAILURE;
  526. }
  527. }
  528. }
  529. return SUCCESS;
  530. }
  531. /*
  532. * Cleanup expired session data.
  533. * PARAMETERS: PS_GC_ARGS in php_session.h
  534. * RETURN VALUE: SUCCESS or FAILURE. Number of deleted records(int *nrdels(default=-1)).
  535. *
  536. * PS_GC_FUNC() must remove session data that are not accessed
  537. * 'session.maxlifetime'(seconds). If storage does not need manual GC, it
  538. * may return SUCCESS simply. (e.g. Memcache) It must set number of records
  539. * deleted(nrdels).
  540. * *mod_data is guaranteed to have non-NULL value.
  541. */
  542. PS_GC_FUNC(files)
  543. {
  544. PS_FILES_DATA;
  545. /* We don't perform any cleanup, if dirdepth is larger than 0.
  546. we return SUCCESS, since all cleanup should be handled by
  547. an external entity (i.e. find -ctime x | xargs rm) */
  548. if (data->dirdepth == 0) {
  549. *nrdels = ps_files_cleanup_dir(data->basedir, maxlifetime);
  550. } else {
  551. *nrdels = -1; // Cannot process multiple depth save dir
  552. }
  553. return *nrdels;
  554. }
  555. /*
  556. * Create session ID.
  557. * PARAMETERS: PS_CREATE_SID_ARGS in php_session.h
  558. * RETURN VALUE: Valid session ID(zend_string *) or NULL for FAILURE.
  559. *
  560. * PS_CREATE_SID_FUNC() must check collision. i.e. Check session data if
  561. * new sid exists already.
  562. * *mod_data is guaranteed to have non-NULL value.
  563. * NOTE: Default php_session_create_id() does not check collision. If
  564. * NULL is returned, session module create new ID by using php_session_create_id().
  565. * If php_session_create_id() fails due to invalid configuration, it raises E_ERROR.
  566. * NULL return value checks from php_session_create_id() is not required generally.
  567. */
  568. PS_CREATE_SID_FUNC(files)
  569. {
  570. zend_string *sid;
  571. int maxfail = 3;
  572. PS_FILES_DATA;
  573. do {
  574. sid = php_session_create_id((void**)&data);
  575. if (!sid) {
  576. if (--maxfail < 0) {
  577. return NULL;
  578. } else {
  579. continue;
  580. }
  581. }
  582. /* Check collision */
  583. /* FIXME: mod_data(data) should not be NULL (User handler could be NULL) */
  584. if (data && ps_files_key_exists(data, ZSTR_VAL(sid)) == SUCCESS) {
  585. if (sid) {
  586. zend_string_release_ex(sid, 0);
  587. sid = NULL;
  588. }
  589. if (--maxfail < 0) {
  590. return NULL;
  591. }
  592. }
  593. } while(!sid);
  594. return sid;
  595. }
  596. /*
  597. * Check session ID existence for use_strict_mode support.
  598. * PARAMETERS: PS_VALIDATE_SID_ARGS in php_session.h
  599. * RETURN VALUE: SUCCESS or FAILURE.
  600. *
  601. * Return SUCCESS for valid key(already existing session).
  602. * Return FAILURE for invalid key(non-existing session).
  603. * *mod_data, *key are guaranteed to have non-NULL values.
  604. */
  605. PS_VALIDATE_SID_FUNC(files)
  606. {
  607. PS_FILES_DATA;
  608. return ps_files_key_exists(data, ZSTR_VAL(key));
  609. }
  610. /*
  611. * Local variables:
  612. * tab-width: 4
  613. * c-basic-offset: 4
  614. * End:
  615. * vim600: sw=4 ts=4 fdm=marker
  616. * vim<600: sw=4 ts=4
  617. */