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-19 Instrumented lines: 178
Code covered: 79.8 % Executed lines: 142
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 290174 2009-11-03 17:16:45Z 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             290 : {
      71                 :         size_t len;
      72                 :         const char *p;
      73                 :         char c;
      74             290 :         int ret = 1;
      75                 : 
      76            8406 :         for (p = key; (c = *p); p++) {
      77                 :                 /* valid characters are a..z,A..Z,0..9 */
      78            8116 :                 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             290 :         len = p - key;
      89                 : 
      90             290 :         if (len == 0) {
      91               0 :                 ret = 0;
      92                 :         }
      93                 : 
      94             290 :         return ret;
      95                 : }
      96                 : 
      97                 : static char *ps_files_path_create(char *buf, size_t buflen, ps_files *data, const char *key)
      98             522 : {
      99                 :         size_t key_len;
     100                 :         const char *p;
     101                 :         int i;
     102                 :         int n;
     103                 : 
     104             522 :         key_len = strlen(key);
     105             522 :         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             519 :         p = key;
     111             519 :         memcpy(buf, data->basedir, data->basedir_len);
     112             519 :         n = data->basedir_len;
     113             519 :         buf[n++] = PHP_DIR_SEPARATOR;
     114             519 :         for (i = 0; i < (int)data->dirdepth; i++) {
     115               0 :                 buf[n++] = *p++;
     116               0 :                 buf[n++] = PHP_DIR_SEPARATOR;
     117                 :         }
     118             519 :         memcpy(buf + n, FILE_PREFIX, sizeof(FILE_PREFIX) - 1);
     119             519 :         n += sizeof(FILE_PREFIX) - 1;
     120             519 :         memcpy(buf + n, key, key_len);
     121             519 :         n += key_len;
     122             519 :         buf[n] = '\0';
     123                 : 
     124             519 :         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             808 : {
     133             808 :         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             286 :                 close(data->fd);
     140             286 :                 data->fd = -1;
     141                 :         }
     142             808 : }
     143                 : 
     144                 : static void ps_files_open(ps_files *data, const char *key TSRMLS_DC)
     145             346 : {
     146                 :         char buf[MAXPATHLEN];
     147                 : 
     148             346 :         if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) {
     149             290 :                 if (data->lastkey) {
     150               0 :                         efree(data->lastkey);
     151               0 :                         data->lastkey = NULL;
     152                 :                 }
     153                 : 
     154             290 :                 ps_files_close(data);
     155                 : 
     156             290 :                 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             290 :                 if (!ps_files_path_create(buf, sizeof(buf), data, key)) {
     162               2 :                         return;
     163                 :                 }
     164                 : 
     165             288 :                 data->lastkey = estrdup(key);
     166                 : 
     167             288 :                 data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, data->filemode);
     168                 : 
     169             288 :                 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             286 :                         if (PG(safe_mode) || PG(open_basedir)) {
     173                 :                                 struct stat sbuf;
     174                 : 
     175             286 :                                 if (fstat(data->fd, &sbuf)) {
     176               0 :                                         close(data->fd);
     177               0 :                                         return;
     178                 :                                 }
     179             286 :                                 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             286 :                         flock(data->fd, LOCK_EX);
     192                 : 
     193                 : #ifdef F_SETFD
     194                 : # ifndef FD_CLOEXEC
     195                 : #  define FD_CLOEXEC 1
     196                 : # endif
     197             286 :                         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               4 : {
     209                 :         DIR *dir;
     210                 :         char dentry[sizeof(struct dirent) + MAXPATHLEN];
     211               4 :         struct dirent *entry = (struct dirent *) &dentry;
     212                 :         struct stat sbuf;
     213                 :         char buf[MAXPATHLEN];
     214                 :         time_t now;
     215               4 :         int nrdels = 0;
     216                 :         size_t dirname_len;
     217                 : 
     218               4 :         dir = opendir(dirname);
     219               4 :         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               4 :         time(&now);
     225                 : 
     226               4 :         dirname_len = strlen(dirname);
     227                 : 
     228                 :         /* Prepare buffer (dirname never changes) */
     229               4 :         memcpy(buf, dirname, dirname_len);
     230               4 :         buf[dirname_len] = PHP_DIR_SEPARATOR;
     231                 : 
     232            1144 :         while (php_readdir_r(dir, (struct dirent *) dentry, &entry) == 0 && entry) {
     233                 :                 /* does the file start with our prefix? */
     234            1136 :                 if (!strncmp(entry->d_name, FILE_PREFIX, sizeof(FILE_PREFIX) - 1)) {
     235              38 :                         size_t entry_len = strlen(entry->d_name);
     236                 : 
     237                 :                         /* does it fit into our buffer? */
     238              38 :                         if (entry_len + dirname_len + 2 < MAXPATHLEN) {
     239                 :                                 /* create the full path.. */
     240              38 :                                 memcpy(buf + dirname_len + 1, entry->d_name, entry_len);
     241                 : 
     242                 :                                 /* NUL terminate it and */
     243              38 :                                 buf[dirname_len + entry_len + 1] = '\0';
     244                 : 
     245                 :                                 /* check whether its last access was more than maxlifet ago */
     246              38 :                                 if (VCWD_STAT(buf, &sbuf) == 0 &&
     247                 :                                                 (now - sbuf.st_mtime) > maxlifetime) {
     248              20 :                                         VCWD_UNLINK(buf);
     249              20 :                                         nrdels++;
     250                 :                                 }
     251                 :                         }
     252                 :                 }
     253                 :         }
     254                 : 
     255               4 :         closedir(dir);
     256                 : 
     257               4 :         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 (strcmp(save_path, "/tmp")) {
     276               0 :                         if (PG(safe_mode) && (!php_checkuid(save_path, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
     277               0 :                                 return FAILURE;
     278                 :                         }
     279               0 :                         if (php_check_open_basedir(save_path TSRMLS_CC)) {
     280               0 :                                 return FAILURE;
     281                 :                         }
     282                 :                 }
     283                 :         }
     284                 : 
     285                 :         /* split up input parameter */
     286             290 :         last = save_path;
     287             290 :         p = strchr(save_path, ';');
     288             582 :         while (p) {
     289               4 :                 argv[argc++] = last;
     290               4 :                 last = ++p;
     291               4 :                 p = strchr(p, ';');
     292               4 :                 if (argc > 1) break;
     293                 :         }
     294             290 :         argv[argc++] = last;
     295                 : 
     296             290 :         if (argc > 1) {
     297               2 :                 errno = 0;
     298               2 :                 dirdepth = (size_t) strtol(argv[0], NULL, 10);
     299               2 :                 if (errno == ERANGE) {
     300               0 :                         php_error(E_WARNING, "The first parameter in session.save_path is invalid");
     301               0 :                         return FAILURE;
     302                 :                 }
     303                 :         }
     304                 : 
     305             290 :         if (argc > 2) {
     306               2 :                 errno = 0;
     307               2 :                 filemode = strtol(argv[1], NULL, 8);
     308               2 :                 if (errno == ERANGE || filemode < 0 || filemode > 07777) {
     309               0 :                         php_error(E_WARNING, "The second parameter in session.save_path is invalid");
     310               0 :                         return FAILURE;
     311                 :                 }
     312                 :         }
     313             290 :         save_path = argv[argc - 1];
     314                 : 
     315             290 :         data = ecalloc(1, sizeof(*data));
     316                 : 
     317             290 :         data->fd = -1;
     318             290 :         data->dirdepth = dirdepth;
     319             290 :         data->filemode = filemode;
     320             290 :         data->basedir_len = strlen(save_path);
     321             290 :         data->basedir = estrndup(save_path, data->basedir_len);
     322                 : 
     323             290 :         PS_SET_MOD_DATA(data);
     324                 : 
     325             290 :         return SUCCESS;
     326                 : }
     327                 : 
     328                 : PS_CLOSE_FUNC(files)
     329             290 : {
     330             290 :         PS_FILES_DATA;
     331                 : 
     332             290 :         ps_files_close(data);
     333                 : 
     334             290 :         if (data->lastkey) {
     335             288 :                 efree(data->lastkey);
     336                 :         }
     337                 : 
     338             290 :         efree(data->basedir);
     339             290 :         efree(data);
     340             290 :         *mod_data = NULL;
     341                 : 
     342             290 :         return SUCCESS;
     343                 : }
     344                 : 
     345                 : PS_READ_FUNC(files)
     346             289 : {
     347                 :         long n;
     348                 :         struct stat sbuf;
     349             289 :         PS_FILES_DATA;
     350                 : 
     351             289 :         ps_files_open(data, key TSRMLS_CC);
     352             289 :         if (data->fd < 0) {
     353               3 :                 return FAILURE;
     354                 :         }
     355                 : 
     356             286 :         if (fstat(data->fd, &sbuf)) {
     357               0 :                 return FAILURE;
     358                 :         }
     359                 : 
     360             286 :         data->st_size = *vallen = sbuf.st_size;
     361                 : 
     362             286 :         if (sbuf.st_size == 0) {
     363             272 :                 *val = STR_EMPTY_ALLOC();
     364             272 :                 return SUCCESS;
     365                 :         }
     366                 : 
     367              14 :         *val = emalloc(sbuf.st_size);
     368                 : 
     369                 : #if defined(HAVE_PREAD)
     370              14 :         n = pread(data->fd, *val, sbuf.st_size, 0);
     371                 : #else
     372                 :         lseek(data->fd, 0, SEEK_SET);
     373                 :         n = read(data->fd, *val, sbuf.st_size);
     374                 : #endif
     375                 : 
     376              14 :         if (n != sbuf.st_size) {
     377               0 :                 if (n == -1) {
     378               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "read failed: %s (%d)", strerror(errno), errno);
     379                 :                 } else {
     380               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "read returned less bytes than requested");
     381                 :                 }
     382               0 :                 efree(*val);
     383               0 :                 return FAILURE;
     384                 :         }
     385                 : 
     386              14 :         return SUCCESS;
     387                 : }
     388                 : 
     389                 : PS_WRITE_FUNC(files)
     390              57 : {
     391                 :         long n;
     392              57 :         PS_FILES_DATA;
     393                 : 
     394              57 :         ps_files_open(data, key TSRMLS_CC);
     395              57 :         if (data->fd < 0) {
     396               1 :                 return FAILURE;
     397                 :         }
     398                 : 
     399                 :         /* Truncate file if the amount of new data is smaller than the existing data set. */
     400                 : 
     401              56 :         if (vallen < (int)data->st_size) {
     402               2 :                 ftruncate(data->fd, 0);
     403                 :         }
     404                 : 
     405                 : #if defined(HAVE_PWRITE)
     406              56 :         n = pwrite(data->fd, val, vallen, 0);
     407                 : #else
     408                 :         lseek(data->fd, 0, SEEK_SET);
     409                 :         n = write(data->fd, val, vallen);
     410                 : #endif
     411                 : 
     412              56 :         if (n != vallen) {
     413               0 :                 if (n == -1) {
     414               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
     415                 :                 } else {
     416               0 :                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "write wrote less bytes than requested");
     417                 :                 }
     418               0 :                 return FAILURE;
     419                 :         }
     420                 : 
     421              56 :         return SUCCESS;
     422                 : }
     423                 : 
     424                 : PS_DESTROY_FUNC(files)
     425             232 : {
     426                 :         char buf[MAXPATHLEN];
     427             232 :         PS_FILES_DATA;
     428                 : 
     429             232 :         if (!ps_files_path_create(buf, sizeof(buf), data, key)) {
     430               1 :                 return FAILURE;
     431                 :         }
     432                 : 
     433             231 :         if (data->fd != -1) {
     434             228 :                 ps_files_close(data);
     435                 : 
     436             228 :                 if (VCWD_UNLINK(buf) == -1) {
     437                 :                         /* This is a little safety check for instances when we are dealing with a regenerated session
     438                 :                          * that was not yet written to disk. */
     439               8 :                         if (!VCWD_ACCESS(buf, F_OK)) {
     440               0 :                                 return FAILURE;
     441                 :                         }
     442                 :                 }
     443                 :         }
     444                 : 
     445             231 :         return SUCCESS;
     446                 : }
     447                 : 
     448                 : PS_GC_FUNC(files)
     449               4 : {
     450               4 :         PS_FILES_DATA;
     451                 : 
     452                 :         /* we don't perform any cleanup, if dirdepth is larger than 0.
     453                 :            we return SUCCESS, since all cleanup should be handled by
     454                 :            an external entity (i.e. find -ctime x | xargs rm) */
     455                 : 
     456               4 :         if (data->dirdepth == 0) {
     457               4 :                 *nrdels = ps_files_cleanup_dir(data->basedir, maxlifetime TSRMLS_CC);
     458                 :         }
     459                 : 
     460               4 :         return SUCCESS;
     461                 : }
     462                 : 
     463                 : /*
     464                 :  * Local variables:
     465                 :  * tab-width: 4
     466                 :  * c-basic-offset: 4
     467                 :  * End:
     468                 :  * vim600: sw=4 ts=4 fdm=marker
     469                 :  * vim<600: sw=4 ts=4
     470                 :  */

Generated by: LTP GCOV extension version 1.5

Generated at Thu, 19 Nov 2009 08:20:18 +0000 (5 days ago)

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