@@ -276,7 +276,10 @@ static zend_result ps_files_write(ps_files *data, zend_string *key, zend_string
276276 return SUCCESS ;
277277}
278278
279- static int ps_files_cleanup_dir (const zend_string * dirname , zend_long maxlifetime )
279+ /* Recursively remove expired session files. When dirdepth > 0 the
280+ * cleanup descends into subdirectories up to that many levels before
281+ * inspecting individual session files. */
282+ static int ps_files_cleanup_dir (const zend_string * dirname , zend_long maxlifetime , size_t remaining_depth )
280283{
281284 DIR * dir ;
282285 struct dirent * entry ;
@@ -291,8 +294,6 @@ static int ps_files_cleanup_dir(const zend_string *dirname, zend_long maxlifetim
291294 return -1 ;
292295 }
293296
294- time (& now );
295-
296297 if (ZSTR_LEN (dirname ) >= MAXPATHLEN ) {
297298 php_error_docref (NULL , E_NOTICE , "ps_files_cleanup_dir: dirname(%s) is too long" , ZSTR_VAL (dirname ));
298299 closedir (dir );
@@ -304,31 +305,52 @@ static int ps_files_cleanup_dir(const zend_string *dirname, zend_long maxlifetim
304305 buf [ZSTR_LEN (dirname )] = PHP_DIR_SEPARATOR ;
305306
306307 while ((entry = readdir (dir ))) {
307- /* does the file start with our prefix? */
308- if (!strncmp (entry -> d_name , FILE_PREFIX , sizeof (FILE_PREFIX ) - 1 )) {
309- size_t entry_len = strlen (entry -> d_name );
310-
311- /* does it fit into our buffer? */
312- if (entry_len + ZSTR_LEN (dirname ) + 2 < MAXPATHLEN ) {
313- /* create the full path.. */
314- memcpy (buf + ZSTR_LEN (dirname ) + 1 , entry -> d_name , entry_len );
315-
316- /* NUL terminate it and */
317- buf [ZSTR_LEN (dirname ) + entry_len + 1 ] = '\0' ;
318-
319- /* check whether its last access was more than maxlifetime ago */
320- if (VCWD_STAT (buf , & sbuf ) == 0 &&
321- (now - sbuf .st_mtime ) > maxlifetime ) {
322- VCWD_UNLINK (buf );
323- nrdels ++ ;
308+ /* skip . and .. */
309+ if (entry -> d_name [0 ] == '.' &&
310+ (entry -> d_name [1 ] == '\0' ||
311+ (entry -> d_name [1 ] == '.' && entry -> d_name [2 ] == '\0' ))) {
312+ continue ;
313+ }
314+ size_t entry_len = strlen (entry -> d_name );
315+ /* does it fit into our buffer? */
316+ if (ZSTR_LEN (dirname ) + 1 + entry_len >= MAXPATHLEN ) {
317+ continue ;
318+ }
319+ /* create the full path and NUL-terminate it */
320+ memcpy (buf + ZSTR_LEN (dirname ) + 1 , entry -> d_name , entry_len );
321+ buf [ZSTR_LEN (dirname ) + 1 + entry_len ] = '\0' ;
322+
323+ if (remaining_depth == 0 ) {
324+ /* target depth: delete expired session files */
325+ if (strncmp (entry -> d_name , FILE_PREFIX , sizeof (FILE_PREFIX ) - 1 ) != 0 ) {
326+ continue ;
327+ }
328+ if (VCWD_STAT (buf , & sbuf ) != 0 ) {
329+ continue ;
330+ }
331+ time (& now );
332+ if ((now - sbuf .st_mtime ) > maxlifetime ) {
333+ VCWD_UNLINK (buf );
334+ nrdels ++ ;
335+ }
336+ } else {
337+ /* intermediate depth: recurse into subdirectories */
338+ if (VCWD_STAT (buf , & sbuf ) != 0 ) {
339+ continue ;
340+ }
341+ if (S_ISDIR (sbuf .st_mode )) {
342+ zend_string * subdir = zend_string_init (buf , ZSTR_LEN (dirname ) + 1 + entry_len , 0 );
343+ int n = ps_files_cleanup_dir (subdir , maxlifetime , remaining_depth - 1 );
344+ zend_string_release (subdir );
345+ if (n >= 0 ) {
346+ nrdels += n ;
324347 }
325348 }
326349 }
327350 }
328351
329352 closedir (dir );
330-
331- return (nrdels );
353+ return nrdels ;
332354}
333355
334356static zend_result ps_files_key_exists (ps_files * data , const zend_string * key )
@@ -624,15 +646,7 @@ PS_GC_FUNC(files)
624646{
625647 PS_FILES_DATA ;
626648
627- /* We don't perform any cleanup, if dirdepth is larger than 0.
628- we return SUCCESS, since all cleanup should be handled by
629- an external entity (i.e. find -ctime x | xargs rm) */
630-
631- if (data -> dirdepth == 0 ) {
632- * nrdels = ps_files_cleanup_dir (data -> basedir , maxlifetime );
633- } else {
634- * nrdels = -1 ; // Cannot process multiple depth save dir
635- }
649+ * nrdels = ps_files_cleanup_dir (data -> basedir , maxlifetime , data -> dirdepth );
636650
637651 return * nrdels ;
638652}
0 commit comments