PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LTP GCOV extension - code coverage report
Current view: directory - session - mod_files.c
Test: PHP Code Coverage
Date: 2009-11-23 Instrumented lines: 187
Code covered: 80.2 % Executed lines: 150
Legend: not executed executed

       1                 : /*
       2                 :    +----------------------------------------------------------------------+
       3                 :    | PHP Version 6                                                        |
       4                 :    +----------------------------------------------------------------------+
       5                 :    | Copyright (c) 1997-2009 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 290190 2009-11-03 21:21:34Z guenter $ */
      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             219 : {
      71                 :         size_t len;
      72                 :         const char *p;
      73                 :         char c;
      74             219 :         int ret = 1;
      75                 : 
      76            6612 :         for (p = key; (c = *p); p++) {
      77                 :                 /* valid characters are a..z,A..Z,0..9 */
      78            6394 :                 if (!((c >= 'a' && c <= 'z')
      79                 :                                 || (c >= 'A' && c <= 'Z')
      80                 :                                 || (c >= '0' && c <= '9')
      81                 :                                 || c == ','
      82                 :                                 || c == '-')) {
      83               1 :                         ret = 0;
      84               1 :                         break;
      85                 :                 }
      86                 :         }
      87                 : 
      88             219 :         len = p - key;
      89                 : 
      90             219 :         if (len == 0) {
      91               1 :                 ret = 0;
      92                 :         }
      93                 : 
      94             219 :         return ret;
      95                 : }
      96                 : 
      97                 : static char *ps_files_path_create(char *buf, size_t buflen, ps_files *data, const char *key TSRMLS_DC)
      98             388 : {
      99                 :         size_t key_len;
     100                 :         const char *p;
     101                 :         int i;
     102                 :         int n;
     103                 : 
     104             388 :         key_len = strlen(key);
     105             388 :         if (key_len <= data->dirdepth ||
     106                 :                 buflen < (strlen(data->basedir) + 2 * data->dirdepth + key_len + 5 + sizeof(FILE_PREFIX))) {
     107               2 :                 return NULL;
     108                 :         }
     109                 : 
     110             386 :         p = key;
     111             386 :         memcpy(buf, data->basedir, data->basedir_len);
     112             386 :         n = data->basedir_len;
     113             386 :         buf[n++] = PHP_DIR_SEPARATOR;
     114             386 :         for (i = 0; i < (int)data->dirdepth; i++) {
     115               0 :                 buf[n++] = *p++;
     116               0 :                 buf[n++] = PHP_DIR_SEPARATOR;
     117                 :         }
     118             386 :         memcpy(buf + n, FILE_PREFIX, sizeof(FILE_PREFIX) - 1);
     119             386 :         n += sizeof(FILE_PREFIX) - 1;
     120             386 :         memcpy(buf + n, key, key_len);
     121             386 :         n += key_len;
     122             386 :         buf[n] = '\0';
     123                 : 
     124             386 :         if (UG(filesystem_encoding_conv) &&
     125                 :                 ucnv_getType(UG(filesystem_encoding_conv)) != UCNV_UTF8) {
     126               0 :                 char *newbuf = NULL;
     127                 :                 int newlen;
     128               0 :                 UErrorCode status = U_ZERO_ERROR;
     129                 : 
     130               0 :                 zend_convert_encodings(ZEND_U_CONVERTER(UG(filesystem_encoding_conv)), UG(utf8_conv), &newbuf, &newlen, buf, n, &status);
     131                 : 
     132               0 :                 if (status != U_ZERO_ERROR) {
     133               0 :                         php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Failure converting savepath to local filesystem encoding, attempting to use utf8");
     134                 :                 } else {
     135               0 :                         if ((unsigned int)newlen >= buflen) {
     136               0 :                                 newlen = buflen - 1;
     137               0 :                                 newbuf[newlen] = 0;
     138                 :                         }
     139               0 :                         memcpy(buf, newbuf, newlen + 1);
     140                 :                 }
     141               0 :                 if (newbuf) {
     142               0 :                         efree(newbuf);
     143                 :                 }
     144                 :         }
     145                 : 
     146             386 :         return buf;
     147                 : }
     148                 : 
     149                 : #ifndef O_BINARY
     150                 : # define O_BINARY 0
     151                 : #endif
     152                 : 
     153                 : static void ps_files_close(ps_files *data)
     154             602 : {
     155             602 :         if (data->fd != -1) {
     156                 : #ifdef PHP_WIN32
     157                 :                 /* On Win32 locked files that are closed without being explicitly unlocked
     158                 :                    will be unlocked only when "system resources become available". */
     159                 :                 flock(data->fd, LOCK_UN);
     160                 : #endif
     161             214 :                 close(data->fd);
     162             214 :                 data->fd = -1;
     163                 :         }
     164             602 : }
     165                 : 
     166                 : static void ps_files_open(ps_files *data, const char *key TSRMLS_DC)
     167             265 : {
     168                 :         char buf[MAXPATHLEN];
     169                 : 
     170             265 :         if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) {
     171             219 :                 if (data->lastkey) {
     172               1 :                         efree(data->lastkey);
     173               1 :                         data->lastkey = NULL;
     174                 :                 }
     175                 : 
     176             219 :                 ps_files_close(data);
     177                 : 
     178             219 :                 if (!ps_files_valid_key(key)) {
     179               1 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,'");
     180               1 :                         PS(invalid_session_id) = 1;
     181               1 :                         return;
     182                 :                 }
     183             218 :                 if (!ps_files_path_create(buf, sizeof(buf), data, key TSRMLS_CC)) {
     184               2 :                         return;
     185                 :                 }
     186                 : 
     187             216 :                 data->lastkey = estrdup(key);
     188                 : 
     189             216 :                 data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, data->filemode);
     190                 : 
     191             216 :                 if (data->fd != -1) {
     192                 : #ifndef PHP_WIN32
     193                 :                         /* check to make sure that the opened file is not a symlink, linking to data outside of allowable dirs */
     194             214 :                         if (PG(open_basedir)) {
     195                 :                                 struct stat sbuf;
     196                 : 
     197             214 :                                 if (fstat(data->fd, &sbuf)) {
     198               0 :                                         close(data->fd);
     199               0 :                                         return;
     200                 :                                 }
     201             214 :                                 if (
     202                 :                                         S_ISLNK(sbuf.st_mode) &&
     203                 :                                         php_check_open_basedir(buf TSRMLS_CC)
     204                 :                                 ) {
     205               0 :                                         close(data->fd);
     206               0 :                                         return;
     207                 :                                 }
     208                 :                         }
     209                 : #endif
     210             214 :                         flock(data->fd, LOCK_EX);
     211                 : 
     212                 : #ifdef F_SETFD
     213                 : # ifndef FD_CLOEXEC
     214                 : #  define FD_CLOEXEC 1
     215                 : # endif
     216             214 :                         if (fcntl(data->fd, F_SETFD, FD_CLOEXEC)) {
     217               0 :                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "fcntl(%d, F_SETFD, FD_CLOEXEC) failed: %s (%d)", data->fd, strerror(errno), errno);
     218                 :                         }
     219                 : #endif
     220                 :                 } else {
     221               2 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "open(%s, O_RDWR) failed: %s (%d)", buf, strerror(errno), errno);
     222                 :                 }
     223                 :         }
     224                 : }
     225                 : 
     226                 : static int ps_files_cleanup_dir(const char *dirname, int maxlifetime TSRMLS_DC)
     227               4 : {
     228                 :         DIR *dir;
     229                 :         char dentry[sizeof(struct dirent) + MAXPATHLEN];
     230               4 :         struct dirent *entry = (struct dirent *) &dentry;
     231                 :         struct stat sbuf;
     232                 :         char buf[MAXPATHLEN];
     233                 :         time_t now;
     234               4 :         int nrdels = 0;
     235                 :         size_t dirname_len;
     236                 : 
     237               4 :         dir = opendir(dirname);
     238               4 :         if (!dir) {
     239               0 :                 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ps_files_cleanup_dir: opendir(%s) failed: %s (%d)", dirname, strerror(errno), errno);
     240               0 :                 return (0);
     241                 :         }
     242                 : 
     243               4 :         time(&now);
     244                 : 
     245               4 :         dirname_len = strlen(dirname);
     246                 : 
     247                 :         /* Prepare buffer (dirname never changes) */
     248               4 :         memcpy(buf, dirname, dirname_len);
     249               4 :         buf[dirname_len] = PHP_DIR_SEPARATOR;
     250                 : 
     251            1266 :         while (php_readdir_r(dir, (struct dirent *) dentry, &entry) == 0 && entry) {
     252                 :                 /* does the file start with our prefix? */
     253            1258 :                 if (!strncmp(entry->d_name, FILE_PREFIX, sizeof(FILE_PREFIX) - 1)) {
     254              42 :                         size_t entry_len = strlen(entry->d_name);
     255                 : 
     256                 :                         /* does it fit into our buffer? */
     257              42 :                         if (entry_len + dirname_len + 2 < MAXPATHLEN) {
     258                 :                                 /* create the full path.. */
     259              42 :                                 memcpy(buf + dirname_len + 1, entry->d_name, entry_len);
     260                 : 
     261                 :                                 /* NUL terminate it and */
     262              42 :                                 buf[dirname_len + entry_len + 1] = '\0';
     263                 : 
     264                 :                                 /* check whether its last access was more than maxlifet ago */
     265              42 :                                 if (VCWD_STAT(buf, &sbuf) == 0 &&
     266                 :                                                 (now - sbuf.st_mtime) > maxlifetime) {
     267              29 :                                         VCWD_UNLINK(buf);
     268              29 :                                         nrdels++;
     269                 :                                 }
     270                 :                         }
     271                 :                 }
     272                 :         }
     273                 : 
     274               4 :         closedir(dir);
     275                 : 
     276               4 :         return (nrdels);
     277                 : }
     278                 : 
     279                 : #define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA()
     280                 : 
     281                 : PS_OPEN_FUNC(files)
     282             216 : {
     283                 :         ps_files *data;
     284                 :         const char *p, *last;
     285                 :         const char *argv[3];
     286             216 :         int argc = 0;
     287             216 :         size_t dirdepth = 0;
     288             216 :         int filemode = 0600;
     289                 : 
     290             216 :         if (*save_path == '\0') {
     291                 :                 /* if save path is an empty string, determine the temporary dir */
     292             205 :                 save_path = php_get_temporary_directory();
     293                 : 
     294             205 :                 if (php_check_open_basedir(save_path TSRMLS_CC)) {
     295               0 :                         return FAILURE;
     296                 :                 }
     297                 :         }
     298                 : 
     299                 :         /* split up input parameter */
     300             216 :         last = save_path;
     301             216 :         p = strchr(save_path, ';');
     302             434 :         while (p) {
     303               4 :                 argv[argc++] = last;
     304               4 :                 last = ++p;
     305               4 :                 p = strchr(p, ';');
     306               4 :                 if (argc > 1) break;
     307                 :         }
     308             216 :         argv[argc++] = last;
     309                 : 
     310             216 :         if (argc > 1) {
     311               2 :                 errno = 0;
     312               2 :                 dirdepth = (size_t) strtol(argv[0], NULL, 10);
     313               2 :                 if (errno == ERANGE) {
     314               0 :                         php_error(E_WARNING, "The first parameter in session.save_path is invalid");
     315               0 :                         return FAILURE;
     316                 :                 }
     317                 :         }
     318                 : 
     319             216 :         if (argc > 2) {
     320               2 :                 errno = 0;
     321               2 :                 filemode = strtol(argv[1], NULL, 8);
     322               2 :                 if (errno == ERANGE || filemode < 0 || filemode > 07777) {
     323               0 :                         php_error(E_WARNING, "The second parameter in session.save_path is invalid");
     324               0 :                         return FAILURE;
     325                 :                 }
     326                 :         }
     327             216 :         save_path = argv[argc - 1];
     328                 : 
     329             216 :         data = ecalloc(1, sizeof(*data));
     330                 : 
     331             216 :         data->fd = -1;
     332             216 :         data->dirdepth = dirdepth;
     333             216 :         data->filemode = filemode;
     334             216 :         data->basedir_len = strlen(save_path);
     335             216 :         data->basedir = estrndup(save_path, data->basedir_len);
     336                 : 
     337             216 :         PS_SET_MOD_DATA(data);
     338                 : 
     339             216 :         return SUCCESS;
     340                 : }
     341                 : 
     342                 : PS_CLOSE_FUNC(files)
     343             216 : {
     344             216 :         PS_FILES_DATA;
     345                 : 
     346             216 :         ps_files_close(data);
     347                 : 
     348             216 :         if (data->lastkey) {
     349             215 :                 efree(data->lastkey);
     350                 :         }
     351                 : 
     352             216 :         efree(data->basedir);
     353             216 :         efree(data);
     354             216 :         *mod_data = NULL;
     355                 : 
     356             216 :         return SUCCESS;
     357                 : }
     358                 : 
     359                 : PS_READ_FUNC(files)
     360             217 : {
     361                 :         long n;
     362                 :         struct stat sbuf;
     363             217 :         PS_FILES_DATA;
     364                 : 
     365             217 :         ps_files_open(data, key TSRMLS_CC);
     366             217 :         if (data->fd < 0) {
     367               4 :                 return FAILURE;
     368                 :         }
     369                 : 
     370             213 :         if (fstat(data->fd, &sbuf)) {
     371               0 :                 return FAILURE;
     372                 :         }
     373                 : 
     374             213 :         data->st_size = *vallen = sbuf.st_size;
     375                 : 
     376             213 :         if (sbuf.st_size == 0) {
     377             207 :                 *val = STR_EMPTY_ALLOC();
     378             207 :                 return SUCCESS;
     379                 :         }
     380                 : 
     381               6 :         *val = emalloc(sbuf.st_size);
     382                 : 
     383                 : #if defined(HAVE_PREAD)
     384               6 :         n = pread(data->fd, *val, sbuf.st_size, 0);
     385                 : #else
     386                 :         lseek(data->fd, 0, SEEK_SET);
     387                 :         n = read(data->fd, *val, sbuf.st_size);
     388                 : #endif
     389                 : 
     390               6 :         if (n != sbuf.st_size) {
     391               0 :                 if (n == -1) {
     392               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "read failed: %s (%d)", strerror(errno), errno);
     393                 :                 } else {
     394               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "read returned less bytes than requested");
     395                 :                 }
     396               0 :                 efree(*val);
     397               0 :                 return FAILURE;
     398                 :         }
     399                 : 
     400               6 :         return SUCCESS;
     401                 : }
     402                 : 
     403                 : PS_WRITE_FUNC(files)
     404              48 : {
     405                 :         long n;
     406              48 :         PS_FILES_DATA;
     407                 : 
     408              48 :         ps_files_open(data, key TSRMLS_CC);
     409              48 :         if (data->fd < 0) {
     410               1 :                 return FAILURE;
     411                 :         }
     412                 : 
     413                 :         /* Truncate file if the amount of new data is smaller than the existing data set. */
     414                 : 
     415              47 :         if (vallen < (int)data->st_size) {
     416               1 :                 ftruncate(data->fd, 0);
     417                 :         }
     418                 : 
     419                 : #if defined(HAVE_PWRITE)
     420              47 :         n = pwrite(data->fd, val, vallen, 0);
     421                 : #else
     422                 :         lseek(data->fd, 0, SEEK_SET);
     423                 :         n = write(data->fd, val, vallen);
     424                 : #endif
     425                 : 
     426              47 :         if (n != vallen) {
     427               0 :                 if (n == -1) {
     428               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
     429                 :                 } else {
     430               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "write wrote less bytes than requested");
     431                 :                 }
     432               0 :                 return FAILURE;
     433                 :         }
     434                 : 
     435              47 :         return SUCCESS;
     436                 : }
     437                 : 
     438                 : PS_DESTROY_FUNC(files)
     439             170 : {
     440                 :         char buf[MAXPATHLEN];
     441             170 :         PS_FILES_DATA;
     442                 : 
     443             170 :         if (!ps_files_path_create(buf, sizeof(buf), data, key TSRMLS_CC)) {
     444               0 :                 return FAILURE;
     445                 :         }
     446                 : 
     447             170 :         if (data->fd != -1) {
     448             167 :                 ps_files_close(data);
     449                 : 
     450             167 :                 if (VCWD_UNLINK(buf) == -1) {
     451                 :                         /* This is a little safety check for instances when we are dealing with a regenerated session
     452                 :                          * that was not yet written to disk. */
     453               6 :                         if (!VCWD_ACCESS(buf, F_OK)) {
     454               0 :                                 return FAILURE;
     455                 :                         }
     456                 :                 }
     457                 :         }
     458                 : 
     459             170 :         return SUCCESS;
     460                 : }
     461                 : 
     462                 : PS_GC_FUNC(files)
     463               4 : {
     464               4 :         PS_FILES_DATA;
     465                 : 
     466                 :         /* we don't perform any cleanup, if dirdepth is larger than 0.
     467                 :            we return SUCCESS, since all cleanup should be handled by
     468                 :            an external entity (i.e. find -ctime x | xargs rm) */
     469                 : 
     470               4 :         if (data->dirdepth == 0) {
     471               4 :                 *nrdels = ps_files_cleanup_dir(data->basedir, maxlifetime TSRMLS_CC);
     472                 :         }
     473                 : 
     474               4 :         return SUCCESS;
     475                 : }
     476                 : 
     477                 : /*
     478                 :  * Local variables:
     479                 :  * tab-width: 4
     480                 :  * c-basic-offset: 4
     481                 :  * End:
     482                 :  * vim600: sw=4 ts=4 fdm=marker
     483                 :  * vim<600: sw=4 ts=4
     484                 :  */

Generated by: LTP GCOV extension version 1.5

Generated at Mon, 23 Nov 2009 17:39:37 +0000 (34 hours ago)

Copyright © 2005-2009 The PHP Group
All rights reserved.