16
16
+----------------------------------------------------------------------+
17
17
*/
18
18
19
- /* $Id$ */
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 vulunerabilty 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 gurantees 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 defitions.
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
+ *************************************************************************/
20
54
21
55
#include "php.h"
22
56
@@ -67,7 +101,8 @@ typedef struct {
67
101
} ps_files ;
68
102
69
103
ps_module ps_mod_files = {
70
- PS_MOD_SID (files )
104
+ /* New save handlers MUST use PS_MOD_UPDATE_TIMESTAMP macro */
105
+ PS_MOD_UPDATE_TIMESTAMP (files )
71
106
};
72
107
73
108
@@ -182,6 +217,43 @@ static void ps_files_open(ps_files *data, const char *key)
182
217
}
183
218
}
184
219
220
+ static int ps_files_write (ps_files * data , zend_string * key , zend_string * val )
221
+ {
222
+ zend_long n ;
223
+ zend_stat_t sbuf ;
224
+
225
+ /* PS(id) may be changed by calling session_regenerate_id().
226
+ Re-initialization should be tried here. ps_files_open() checks
227
+ data->lastkey and reopen when it is needed. */
228
+ ps_files_open (data , key -> val );
229
+ if (data -> fd < 0 ) {
230
+ return FAILURE ;
231
+ }
232
+
233
+ /* Truncate file if the amount of new data is smaller than the existing data set. */
234
+ if (val -> len < (int )data -> st_size ) {
235
+ php_ignore_value (ftruncate (data -> fd , 0 ));
236
+ }
237
+
238
+ #if defined(HAVE_PWRITE )
239
+ n = pwrite (data -> fd , val -> val , val -> len , 0 );
240
+ #else
241
+ lseek (data -> fd , 0 , SEEK_SET );
242
+ n = write (data -> fd , val -> val , val -> len );
243
+ #endif
244
+
245
+ if (n != val -> len ) {
246
+ if (n == -1 ) {
247
+ php_error_docref (NULL , E_WARNING , "write failed: %s (%d)" , strerror (errno ), errno );
248
+ } else {
249
+ php_error_docref (NULL , E_WARNING , "write wrote less bytes than requested" );
250
+ }
251
+ return FAILURE ;
252
+ }
253
+
254
+ return SUCCESS ;
255
+ }
256
+
185
257
static int ps_files_cleanup_dir (const char * dirname , int maxlifetime )
186
258
{
187
259
DIR * dir ;
@@ -252,6 +324,18 @@ static int ps_files_key_exists(ps_files *data, const char *key)
252
324
253
325
#define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA()
254
326
327
+
328
+ /*
329
+ * Open save handler. Setup resources that are needed by the handler.
330
+ * PARAMETERS: PS_OPEN_ARGS in php_session.h
331
+ * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL valid module data
332
+ * (void **mod_data) with SUCCESS, NULL(default) for FAILUREs.
333
+ *
334
+ * Files save handler checks/create save_path directory and setup ps_files data.
335
+ * Note that files save handler supports splitting session data into multiple
336
+ * directories.
337
+ * *mod_data, *save_path, *session_name are guranteed to have non-NULL values.
338
+ */
255
339
PS_OPEN_FUNC (files )
256
340
{
257
341
ps_files * data ;
@@ -316,6 +400,17 @@ PS_OPEN_FUNC(files)
316
400
return SUCCESS ;
317
401
}
318
402
403
+
404
+ /*
405
+ * Clean up opened resources.
406
+ * PARAMETERS: PS_CLOSE_ARGS in php_session.h
407
+ * RETURN VALUE: SUCCESS. Must set PS module data(void **mod_data) to NULL.
408
+ *
409
+ * Files save handler closes open files and it's memory.
410
+ * *mod_data is guranteed to have non-NULL value.
411
+ * PS_CLOSE_FUNC() must set *mod_data to NULL. PS_CLOSE_FUNC() should not
412
+ * fail.
413
+ */
319
414
PS_CLOSE_FUNC (files )
320
415
{
321
416
PS_FILES_DATA ;
@@ -333,32 +428,24 @@ PS_CLOSE_FUNC(files)
333
428
return SUCCESS ;
334
429
}
335
430
431
+
432
+ /*
433
+ * Read session data from opened resource.
434
+ * PARAMETERS: PS_READ_ARGS in php_session.h
435
+ * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL session data to (zend_string **val)
436
+ * for SUCCESS. NULL(default) for FAILUREs.
437
+ *
438
+ * Files save handler supports splitting session data into multiple
439
+ * directories.
440
+ * *mod_data, *key are guranteed to have non-NULL values.
441
+ */
336
442
PS_READ_FUNC (files )
337
443
{
338
444
zend_long n ;
339
445
zend_stat_t sbuf ;
340
446
PS_FILES_DATA ;
341
447
342
- /* If strict mode, check session id existence */
343
- if (PS (use_strict_mode ) &&
344
- ps_files_key_exists (data , key ? key -> val : NULL ) == FAILURE ) {
345
- /* key points to PS(id), but cannot change here. */
346
- if (key ) {
347
- zend_string_release (PS (id ));
348
- PS (id ) = NULL ;
349
- }
350
- PS (id ) = PS (mod )-> s_create_sid ((void * * )& data );
351
- if (!PS (id )) {
352
- return FAILURE ;
353
- }
354
- if (PS (use_cookies )) {
355
- PS (send_cookie ) = 1 ;
356
- }
357
- php_session_reset_id ();
358
- PS (session_status ) = php_session_active ;
359
- }
360
-
361
- ps_files_open (data , PS (id )-> val );
448
+ ps_files_open (data , key -> val );
362
449
if (data -> fd < 0 ) {
363
450
return FAILURE ;
364
451
}
@@ -390,47 +477,82 @@ PS_READ_FUNC(files)
390
477
php_error_docref (NULL , E_WARNING , "read returned less bytes than requested" );
391
478
}
392
479
zend_string_release (* val );
480
+ * val = STR_EMPTY_ALLOC ();
393
481
return FAILURE ;
394
482
}
395
483
396
484
return SUCCESS ;
397
485
}
398
486
487
+
488
+ /*
489
+ * Write session data.
490
+ * PARAMETERS: PS_WRITE_ARGS in php_session.h
491
+ * RETURN VALUE: SUCCESS or FAILURE.
492
+ *
493
+ * PS_WRITE_FUNC() must write session data(zend_string *val) unconditionally.
494
+ * *mod_data, *key, *val are guranteed to have non-NULL values.
495
+ */
399
496
PS_WRITE_FUNC (files )
400
497
{
401
- zend_long n ;
402
498
PS_FILES_DATA ;
403
499
404
- ps_files_open (data , key -> val );
405
- if (data -> fd < 0 ) {
406
- return FAILURE ;
407
- }
500
+ return ps_files_write (data , key , val );
501
+ }
408
502
409
- /* Truncate file if the amount of new data is smaller than the existing data set. */
410
503
411
- if (val -> len < (int )data -> st_size ) {
412
- php_ignore_value (ftruncate (data -> fd , 0 ));
504
+ /*
505
+ * Update session data modification/access time stamp.
506
+ * PARAMETERS: PS_UPDATE_TIMESTAMP_ARGS in php_session.h
507
+ * RETURN VALUE: SUCCESS or FAILURE.
508
+ *
509
+ * PS_UPDATE_TIMESTAMP_FUNC() updates time stamp(mtime) so that active session
510
+ * data files will not be purged by GC. If session data storage does not need to
511
+ * update timestamp, it should return SUCCESS simply. (e.g. Memcache)
512
+ * *mod_data, *key, *val are guranteed to have non-NULL values.
513
+ *
514
+ * NOTE: Updating access timestamp at PS_READ_FUNC() may extend life of obsolete
515
+ * session data. Use of PS_UPDATE_TIMESTAMP_FUNC() is prefered whenenver it is
516
+ * possible.
517
+ */
518
+ PS_UPDATE_TIMESTAMP_FUNC (files )
519
+ {
520
+ char buf [MAXPATHLEN ];
521
+ struct utimbuf newtimebuf ;
522
+ struct utimbuf * newtime = & newtimebuf ;
523
+ int ret ;
524
+ PS_FILES_DATA ;
525
+
526
+ if (!ps_files_path_create (buf , sizeof (buf ), data , key -> val )) {
527
+ return FAILURE ;
413
528
}
414
529
415
- #if defined(HAVE_PWRITE )
416
- n = pwrite (data -> fd , val -> val , val -> len , 0 );
530
+ /* Update mtime */
531
+ #ifdef HAVE_UTIME_NULL
532
+ newtime = NULL ;
417
533
#else
418
- lseek (data -> fd , 0 , SEEK_SET );
419
- n = write (data -> fd , val -> val , val -> len );
534
+ newtime -> modtime = newtime -> actime = time (NULL );
420
535
#endif
421
-
422
- if (n != val -> len ) {
423
- if (n == -1 ) {
424
- php_error_docref (NULL , E_WARNING , "write failed: %s (%d)" , strerror (errno ), errno );
425
- } else {
426
- php_error_docref (NULL , E_WARNING , "write wrote less bytes than requested" );
427
- }
428
- return FAILURE ;
536
+ ret = VCWD_UTIME (buf , newtime );
537
+ if (ret == -1 ) {
538
+ /* New session ID, create data file */
539
+ return ps_files_write (data , key , val );
429
540
}
430
541
431
542
return SUCCESS ;
432
543
}
433
544
545
+
546
+ /*
547
+ * Delete session data.
548
+ * PARAMETERS: PS_DESTROY_ARGS in php_session.h
549
+ * RETURN VALUE: SUCCESS or FAILURE.
550
+ *
551
+ * PS_DESTROY_FUNC() must remove the session data specified by *key from
552
+ * session data storage unconditionally. It must not return FAILURE for
553
+ * non-existent session data.
554
+ * *mod_data, *key are guranteed to have non-NULL values.
555
+ */
434
556
PS_DESTROY_FUNC (files )
435
557
{
436
558
char buf [MAXPATHLEN ];
@@ -455,11 +577,23 @@ PS_DESTROY_FUNC(files)
455
577
return SUCCESS ;
456
578
}
457
579
580
+
581
+ /*
582
+ * Cleanup expired session data.
583
+ * PARAMETERS: PS_GC_ARGS in php_session.h
584
+ * RETURN VALUE: SUCCESS or FAILURE. Number of deleted records(int *nrdels(default=-1)).
585
+ *
586
+ * PS_GC_FUNC() must remove session data that are not accessed
587
+ * 'session.maxlifetime'(seconds). If storage does not need manual GC, it
588
+ * may return SUCCESS simply. (e.g. Memcache) It must set number of records
589
+ * deleted(nrdels).
590
+ * *mod_data is guranteed to have non-NULL value.
591
+ */
458
592
PS_GC_FUNC (files )
459
593
{
460
594
PS_FILES_DATA ;
461
595
462
- /* we don't perform any cleanup, if dirdepth is larger than 0.
596
+ /* We don't perform any cleanup, if dirdepth is larger than 0.
463
597
we return SUCCESS, since all cleanup should be handled by
464
598
an external entity (i.e. find -ctime x | xargs rm) */
465
599
@@ -470,6 +604,20 @@ PS_GC_FUNC(files)
470
604
return SUCCESS ;
471
605
}
472
606
607
+
608
+ /*
609
+ * Create session ID.
610
+ * PARAMETERS: PS_CREATE_SID_ARGS in php_session.h
611
+ * RETURN VALUE: Valid session ID(zend_string *) or NULL for FAILURE.
612
+ *
613
+ * PS_CREATE_SID_FUNC() must check collision. i.e. Check session data if
614
+ * new sid exists already.
615
+ * *mod_data is guranteed to have non-NULL value.
616
+ * NOTE: Default php_session_create_id() does not check collision. If
617
+ * NULL is returned, session module create new ID by using php_session_create_id().
618
+ * If php_session_create_id() fails due to invalid configuration, it raises E_ERROR.
619
+ * NULL return value checks from php_session_create_id() is not required generally.
620
+ */
473
621
PS_CREATE_SID_FUNC (files )
474
622
{
475
623
zend_string * sid ;
@@ -478,13 +626,21 @@ PS_CREATE_SID_FUNC(files)
478
626
479
627
do {
480
628
sid = php_session_create_id ((void * * )& data );
629
+ if (!sid ) {
630
+ if (-- maxfail < 0 ) {
631
+ return NULL ;
632
+ } else {
633
+ continue ;
634
+ }
635
+ }
481
636
/* Check collision */
482
- if (data && ps_files_key_exists (data , sid ? sid -> val : NULL ) == SUCCESS ) {
637
+ /* FIXME: mod_data(data) should not be NULL (User handler could be NULL) */
638
+ if (data && ps_files_key_exists (data , sid -> val ) == SUCCESS ) {
483
639
if (sid ) {
484
640
zend_string_release (sid );
485
641
sid = NULL ;
486
642
}
487
- if (!( maxfail -- ) ) {
643
+ if (-- maxfail < 0 ) {
488
644
return NULL ;
489
645
}
490
646
}
@@ -494,6 +650,22 @@ PS_CREATE_SID_FUNC(files)
494
650
}
495
651
496
652
653
+ /*
654
+ * Check session ID existence for use_strict_mode support.
655
+ * PARAMETERS: PS_VALIDATE_SID_ARGS in php_session.h
656
+ * RETURN VALUE: SUCCESS or FAILURE.
657
+ *
658
+ * Return SUCCESS for valid key(already exsting session).
659
+ * Return FAILURE for invalid key(non-existing session).
660
+ * *mod_data, *key are guranteed to have non-NULL values.
661
+ */
662
+ PS_VALIDATE_SID_FUNC (files )
663
+ {
664
+ PS_FILES_DATA ;
665
+
666
+ return ps_files_key_exists (data , key -> val );
667
+ }
668
+
497
669
/*
498
670
* Local variables:
499
671
* tab-width: 4
0 commit comments