root/ext/session/mod_files.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ps_files_path_create
  2. ps_files_close
  3. ps_files_open
  4. ps_files_write
  5. ps_files_cleanup_dir
  6. ps_files_key_exists
  7. PS_OPEN_FUNC
  8. PS_CLOSE_FUNC
  9. PS_READ_FUNC
  10. PS_WRITE_FUNC
  11. PS_UPDATE_TIMESTAMP_FUNC
  12. PS_DESTROY_FUNC
  13. PS_GC_FUNC
  14. PS_CREATE_SID_FUNC
  15. PS_VALIDATE_SID_FUNC

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

/* [<][>][^][v][top][bottom][index][help] */