LTP GCOV extension - code coverage report
Current view: directory - ext/session - mod_files.c
Test: PHP Code Coverage
Date: 2007-04-10 Instrumented lines: 172
Code covered: 50.0 % Executed lines: 86
Legend: not executed executed

       1                 : /* 
       2                 :    +----------------------------------------------------------------------+
       3                 :    | PHP Version 5                                                        |
       4                 :    +----------------------------------------------------------------------+
       5                 :    | Copyright (c) 1997-2007 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                 : /* $Id: mod_files.c,v 1.100.2.3.2.5 2007/03/03 15:07:31 iliaa Exp $ */
      20                 : 
      21                 : #include "php.h"
      22                 : 
      23                 : #include <sys/stat.h>
      24                 : #include <sys/types.h>
      25                 : 
      26                 : #if HAVE_SYS_FILE_H
      27                 : #include <sys/file.h>
      28                 : #endif
      29                 : 
      30                 : #if HAVE_DIRENT_H
      31                 : #include <dirent.h>
      32                 : #endif
      33                 : 
      34                 : #ifdef PHP_WIN32
      35                 : #include "win32/readdir.h"
      36                 : #endif
      37                 : #include <time.h>
      38                 : 
      39                 : #include <fcntl.h>
      40                 : #include <errno.h>
      41                 : 
      42                 : #if HAVE_UNISTD_H
      43                 : #include <unistd.h>
      44                 : #endif
      45                 : 
      46                 : #include "php_session.h"
      47                 : #include "mod_files.h"
      48                 : #include "ext/standard/flock_compat.h"
      49                 : #include "php_open_temporary_file.h"
      50                 : 
      51                 : #define FILE_PREFIX "sess_"
      52                 : 
      53                 : typedef struct {
      54                 :         int fd;
      55                 :         char *lastkey;
      56                 :         char *basedir;
      57                 :         size_t basedir_len;
      58                 :         size_t dirdepth;
      59                 :         size_t st_size;
      60                 :         int filemode;
      61                 : } ps_files;
      62                 : 
      63                 : ps_module ps_mod_files = {
      64                 :         PS_MOD(files)
      65                 : };
      66                 : 
      67                 : /* If you change the logic here, please also update the error message in
      68                 :  * ps_files_open() appropriately */
      69                 : static int ps_files_valid_key(const char *key)
      70               1 : {
      71                 :         size_t len;
      72                 :         const char *p;
      73                 :         char c;
      74               1 :         int ret = 1;
      75                 : 
      76              33 :         for (p = key; (c = *p); p++) {
      77                 :                 /* valid characters are a..z,A..Z,0..9 */
      78              32 :                 if (!((c >= 'a' && c <= 'z')
      79                 :                                 || (c >= 'A' && c <= 'Z')
      80                 :                                 || (c >= '0' && c <= '9')
      81                 :                                 || c == ','
      82                 :                                 || c == '-')) {
      83               0 :                         ret = 0;
      84               0 :                         break;
      85                 :                 }
      86                 :         }
      87                 : 
      88               1 :         len = p - key;
      89                 :         
      90               1 :         if (len == 0)
      91               0 :                 ret = 0;
      92                 :         
      93               1 :         return ret;
      94                 : }
      95                 : 
      96                 : static char *ps_files_path_create(char *buf, size_t buflen, ps_files *data, const char *key)
      97               1 : {
      98                 :         size_t key_len;
      99                 :         const char *p;
     100                 :         int i;
     101                 :         int n;
     102                 :         
     103               1 :         key_len = strlen(key);
     104               1 :         if (key_len <= data->dirdepth || buflen < 
     105                 :                         (strlen(data->basedir) + 2 * data->dirdepth + key_len + 5 + sizeof(FILE_PREFIX))) 
     106               0 :                 return NULL;
     107               1 :         p = key;
     108               1 :         memcpy(buf, data->basedir, data->basedir_len);
     109               1 :         n = data->basedir_len;
     110               1 :         buf[n++] = PHP_DIR_SEPARATOR;
     111               1 :         for (i = 0; i < (int)data->dirdepth; i++) {
     112               0 :                 buf[n++] = *p++;
     113               0 :                 buf[n++] = PHP_DIR_SEPARATOR;
     114                 :         }
     115               1 :         memcpy(buf + n, FILE_PREFIX, sizeof(FILE_PREFIX) - 1);
     116               1 :         n += sizeof(FILE_PREFIX) - 1;
     117               1 :         memcpy(buf + n, key, key_len);
     118               1 :         n += key_len;
     119               1 :         buf[n] = '\0';
     120                 :         
     121               1 :         return buf;
     122                 : }
     123                 : 
     124                 : #ifndef O_BINARY
     125                 : #define O_BINARY 0
     126                 : #endif 
     127                 : 
     128                 : static void ps_files_close(ps_files *data)
     129               2 : {
     130               2 :         if (data->fd != -1) {
     131                 : #ifdef PHP_WIN32 
     132                 :                 /* On Win32 locked files that are closed without being explicitly unlocked
     133                 :                    will be unlocked only when "system resources become available". */
     134                 :                 flock(data->fd, LOCK_UN);
     135                 : #endif
     136               1 :                 close(data->fd);
     137               1 :                 data->fd = -1;
     138                 :         }
     139               2 : }
     140                 : 
     141                 : static void ps_files_open(ps_files *data, const char *key TSRMLS_DC)
     142               2 : {
     143                 :         char buf[MAXPATHLEN];
     144                 : 
     145               2 :         if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) {
     146               1 :                 if (data->lastkey) {
     147               0 :                         efree(data->lastkey);
     148               0 :                         data->lastkey = NULL;
     149                 :                 }
     150                 : 
     151               1 :                 ps_files_close(data);
     152                 :                 
     153               1 :                 if (!ps_files_valid_key(key)) {
     154               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,'");
     155               0 :                         PS(invalid_session_id) = 1;
     156               0 :                         return;
     157                 :                 }
     158               1 :                 if (!ps_files_path_create(buf, sizeof(buf), data, key))
     159               0 :                         return;
     160                 :                 
     161               1 :                 data->lastkey = estrdup(key);
     162                 :                 
     163               1 :                 data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, 
     164                 :                                 data->filemode);
     165                 :                 
     166               1 :                 if (data->fd != -1) {
     167               1 :                         flock(data->fd, LOCK_EX);
     168                 : 
     169                 : #ifdef F_SETFD
     170                 : #ifndef FD_CLOEXEC
     171                 : #define FD_CLOEXEC 1
     172                 : #endif
     173               1 :                         if (fcntl(data->fd, F_SETFD, FD_CLOEXEC)) {
     174               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "fcntl(%d, F_SETFD, FD_CLOEXEC) failed: %s (%d)", data->fd, strerror(errno), errno);
     175                 :                         }
     176                 : #endif
     177                 :                 } else {
     178               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "open(%s, O_RDWR) failed: %s (%d)", buf, 
     179                 :                                         strerror(errno), errno);
     180                 :                 }
     181                 :         }
     182                 : }
     183                 : 
     184                 : static int ps_files_cleanup_dir(const char *dirname, int maxlifetime TSRMLS_DC)
     185               0 : {
     186                 :         DIR *dir;
     187                 :         char dentry[sizeof(struct dirent) + MAXPATHLEN];
     188               0 :         struct dirent *entry = (struct dirent *) &dentry;
     189                 :         struct stat sbuf;
     190                 :         char buf[MAXPATHLEN];
     191                 :         time_t now;
     192               0 :         int nrdels = 0;
     193                 :         size_t dirname_len;
     194                 : 
     195               0 :         dir = opendir(dirname);
     196               0 :         if (!dir) {
     197               0 :                 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ps_files_cleanup_dir: opendir(%s) failed: %s (%d)", dirname, strerror(errno), errno);
     198               0 :                 return (0);
     199                 :         }
     200                 : 
     201               0 :         time(&now);
     202                 : 
     203               0 :         dirname_len = strlen(dirname);
     204                 : 
     205                 :         /* Prepare buffer (dirname never changes) */
     206               0 :         memcpy(buf, dirname, dirname_len);
     207               0 :         buf[dirname_len] = PHP_DIR_SEPARATOR;
     208                 :         
     209               0 :         while (php_readdir_r(dir, (struct dirent *) dentry, &entry) == 0 && entry) {
     210                 :                 /* does the file start with our prefix? */
     211               0 :                 if (!strncmp(entry->d_name, FILE_PREFIX, sizeof(FILE_PREFIX) - 1)) {
     212                 :                         size_t entry_len;
     213                 : 
     214               0 :                         entry_len = strlen(entry->d_name);
     215                 :                         /* does it fit into our buffer? */
     216               0 :                         if (entry_len + dirname_len + 2 < MAXPATHLEN) {
     217                 :                                 /* create the full path.. */
     218               0 :                                 memcpy(buf + dirname_len + 1, entry->d_name, entry_len);
     219                 :                                 /* NUL terminate it and */
     220               0 :                                 buf[dirname_len + entry_len + 1] = '\0';
     221                 :                                 /* check whether its last access was more than maxlifet ago */
     222               0 :                                 if (VCWD_STAT(buf, &sbuf) == 0 && 
     223                 : #ifdef NETWARE
     224                 :                                                 (now - sbuf.st_mtime.tv_sec) > maxlifetime) {
     225                 : #else
     226                 :                                                 (now - sbuf.st_mtime) > maxlifetime) {
     227                 : #endif
     228               0 :                                         VCWD_UNLINK(buf);
     229               0 :                                         nrdels++;
     230                 :                                 }
     231                 :                         }
     232                 :                 }
     233                 :         }
     234                 : 
     235               0 :         closedir(dir);
     236                 : 
     237               0 :         return (nrdels);
     238                 : }
     239                 : 
     240                 : #define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA()
     241                 : 
     242                 : PS_OPEN_FUNC(files)
     243               1 : {
     244                 :         ps_files *data;
     245                 :         const char *p, *last;
     246                 :         const char *argv[3];
     247               1 :         int argc = 0;
     248               1 :         size_t dirdepth = 0;
     249               1 :         int filemode = 0600;
     250                 : 
     251               1 :         if (*save_path == '\0') {
     252                 :                 /* if save path is an empty string, determine the temporary dir */
     253               1 :                 save_path = php_get_temporary_directory();
     254                 : 
     255               1 :                 if (strcmp(save_path, "/tmp")) {
     256               0 :                         if (PG(safe_mode) && (!php_checkuid(save_path, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
     257               0 :                                 return FAILURE;
     258                 :                         }
     259               0 :                         if (php_check_open_basedir(save_path TSRMLS_CC)) {
     260               0 :                                 return FAILURE;
     261                 :                         }
     262                 :                 }
     263                 :         }
     264                 :         
     265                 :         /* split up input parameter */
     266               1 :         last = save_path;
     267               1 :         p = strchr(save_path, ';');
     268               2 :         while (p) {
     269               0 :                 argv[argc++] = last;
     270               0 :                 last = ++p;
     271               0 :                 p = strchr(p, ';');
     272               0 :                 if (argc > 1) break;
     273                 :         }
     274               1 :         argv[argc++] = last;
     275                 : 
     276               1 :         if (argc > 1) {
     277               0 :                 errno = 0;
     278               0 :                 dirdepth = (size_t) strtol(argv[0], NULL, 10);
     279               0 :                 if (errno == ERANGE) {
     280               0 :                         php_error(E_WARNING, 
     281                 :                                         "The first parameter in session.save_path is invalid");
     282               0 :                         return FAILURE;
     283                 :                 }
     284                 :         }
     285                 :         
     286               1 :         if (argc > 2) {
     287               0 :                 errno = 0;
     288               0 :                 filemode = strtol(argv[1], NULL, 8);
     289               0 :                 if (errno == ERANGE || filemode < 0 || filemode > 07777) {
     290               0 :                         php_error(E_WARNING, 
     291                 :                                         "The second parameter in session.save_path is invalid");
     292               0 :                         return FAILURE;
     293                 :                 }
     294                 :         }
     295               1 :         save_path = argv[argc - 1];
     296                 : 
     297               1 :         data = emalloc(sizeof(*data));
     298               1 :         memset(data, 0, sizeof(*data));
     299                 :         
     300               1 :         data->fd = -1;
     301               1 :         data->dirdepth = dirdepth;
     302               1 :         data->filemode = filemode;
     303               1 :         data->basedir_len = strlen(save_path);
     304               1 :         data->basedir = estrndup(save_path, data->basedir_len);
     305                 :         
     306               1 :         PS_SET_MOD_DATA(data);
     307                 :         
     308               1 :         return SUCCESS;
     309                 : }
     310                 : 
     311                 : PS_CLOSE_FUNC(files)
     312               1 : {
     313               1 :         PS_FILES_DATA;
     314                 : 
     315               1 :         ps_files_close(data);
     316                 : 
     317               1 :         if (data->lastkey) 
     318               1 :                 efree(data->lastkey);
     319               1 :         efree(data->basedir);
     320               1 :         efree(data);
     321               1 :         *mod_data = NULL;
     322                 : 
     323               1 :         return SUCCESS;
     324                 : }
     325                 : 
     326                 : PS_READ_FUNC(files)
     327               1 : {
     328                 :         long n;
     329                 :         struct stat sbuf;
     330               1 :         PS_FILES_DATA;
     331                 : 
     332               1 :         ps_files_open(data, key TSRMLS_CC);
     333               1 :         if (data->fd < 0)
     334               0 :                 return FAILURE;
     335                 :         
     336               1 :         if (fstat(data->fd, &sbuf))
     337               0 :                 return FAILURE;
     338                 :         
     339               1 :         data->st_size = *vallen = sbuf.st_size;
     340                 :         
     341               1 :         if (sbuf.st_size == 0) {
     342               1 :                 *val = STR_EMPTY_ALLOC();
     343               1 :                 return SUCCESS;
     344                 :         }
     345                 :         
     346               0 :         *val = emalloc(sbuf.st_size);
     347                 : 
     348                 : #if defined(HAVE_PREAD)
     349               0 :         n = pread(data->fd, *val, sbuf.st_size, 0);
     350                 : #else
     351                 :         lseek(data->fd, 0, SEEK_SET);
     352                 :         n = read(data->fd, *val, sbuf.st_size);
     353                 : #endif
     354                 : 
     355               0 :         if (n != sbuf.st_size) {
     356               0 :                 if (n == -1)
     357               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "read failed: %s (%d)", strerror(errno), errno);
     358                 :                 else
     359               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "read returned less bytes than requested");
     360               0 :                 efree(*val);
     361               0 :                 return FAILURE;
     362                 :         }
     363                 :         
     364               0 :         return SUCCESS;
     365                 : }
     366                 : 
     367                 : PS_WRITE_FUNC(files)
     368               1 : {
     369                 :         long n;
     370               1 :         PS_FILES_DATA;
     371                 : 
     372               1 :         ps_files_open(data, key TSRMLS_CC);
     373               1 :         if (data->fd < 0)
     374               0 :                 return FAILURE;
     375                 : 
     376                 :         /* 
     377                 :          * truncate file, if the amount of new data is smaller than
     378                 :          * the existing data set.
     379                 :          */
     380                 :         
     381               1 :         if (vallen < (int)data->st_size)
     382               0 :                 ftruncate(data->fd, 0);
     383                 : 
     384                 : #if defined(HAVE_PWRITE)
     385               1 :         n = pwrite(data->fd, val, vallen, 0);
     386                 : #else
     387                 :         lseek(data->fd, 0, SEEK_SET);
     388                 :         n = write(data->fd, val, vallen);
     389                 : #endif
     390                 : 
     391               1 :         if (n != vallen) {
     392               0 :                 if (n == -1)
     393               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
     394                 :                 else
     395               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "write wrote less bytes than requested");
     396               0 :                 return FAILURE;
     397                 :         }
     398                 : 
     399               1 :         return SUCCESS;
     400                 : }
     401                 : 
     402                 : PS_DESTROY_FUNC(files)
     403               0 : {
     404                 :         char buf[MAXPATHLEN];
     405               0 :         PS_FILES_DATA;
     406                 : 
     407               0 :         if (!ps_files_path_create(buf, sizeof(buf), data, key))
     408               0 :                 return FAILURE;
     409                 :         
     410               0 :         if (data->fd != -1) {
     411               0 :                 ps_files_close(data);
     412                 :         
     413               0 :                 if (VCWD_UNLINK(buf) == -1) {
     414                 :                         /* This is a little safety check for instances when we are dealing with a regenerated session
     415                 :                          * that was not yet written to disk
     416                 :                          */
     417               0 :                         if (!VCWD_ACCESS(buf, F_OK)) {
     418               0 :                                 return FAILURE;
     419                 :                         }
     420                 :                 }
     421                 :         }
     422                 : 
     423               0 :         return SUCCESS;
     424                 : }
     425                 : 
     426                 : PS_GC_FUNC(files) 
     427               0 : {
     428               0 :         PS_FILES_DATA;
     429                 :         
     430                 :         /* we don't perform any cleanup, if dirdepth is larger than 0.
     431                 :            we return SUCCESS, since all cleanup should be handled by
     432                 :            an external entity (i.e. find -ctime x | xargs rm) */
     433                 :            
     434               0 :         if (data->dirdepth == 0)
     435               0 :                 *nrdels = ps_files_cleanup_dir(data->basedir, maxlifetime TSRMLS_CC);
     436                 :         
     437               0 :         return SUCCESS;
     438                 : }
     439                 : 
     440                 : /*
     441                 :  * Local variables:
     442                 :  * tab-width: 4
     443                 :  * c-basic-offset: 4
     444                 :  * End:
     445                 :  * vim600: sw=4 ts=4 fdm=marker
     446                 :  * vim<600: sw=4 ts=4
     447                 :  */

Generated by: LTP GCOV extension version 1.5