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-21 Instrumented lines: 177
Code covered: 81.9 % Executed lines: 145
Legend: not executed executed

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

Generated by: LTP GCOV extension version 1.5

Generated at Sat, 21 Nov 2009 12:27:07 +0000 (3 days ago)

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