2005-12-05 04:35:38 +08:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2005-12-05 06:50:54 +08:00
| phar php single - file executable PHP extension |
2005-12-05 04:35:38 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2005-12-05 06:50:54 +08:00
| Copyright ( c ) 2005 The PHP Group |
2005-12-05 04:35:38 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2005-12-05 06:50:54 +08:00
| This source file is subject to version 3.01 of the PHP license , |
2005-12-05 04:35:38 +08:00
| that is bundled with this package in the file LICENSE , and is |
| available through the world - wide - web at the following url : |
2005-12-05 06:50:54 +08:00
| http : //www.php.net/license/3_01.txt. |
2005-12-05 04:35:38 +08:00
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world - wide - web , please send a note to |
| license @ php . net so we can mail you a copy immediately . |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2005-12-05 06:50:54 +08:00
| Author : Gregory Beaver < cellog @ php . net > |
2005-12-05 04:35:38 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
/* $Id$ */
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <time.h>
# include "php.h"
# include "php_ini.h"
# include "ext/standard/info.h"
# include "ext/standard/url.h"
# include "ext/standard/crc32.h"
# include "zend_execute.h"
2006-01-02 04:16:09 +08:00
# include "zend_qsort.h"
2005-12-05 04:35:38 +08:00
# include "zend_constants.h"
2006-01-02 04:16:09 +08:00
# include "zend_operators.h"
2005-12-05 04:35:38 +08:00
# include "php_phar.h"
2005-12-05 12:47:29 +08:00
# include "main/php_streams.h"
2005-12-05 04:35:38 +08:00
# ifndef TRUE
# define TRUE 1
# define FALSE 0
# endif
ZEND_DECLARE_MODULE_GLOBALS ( phar )
typedef struct _internal_phar_stream_data {
phar_file_data * data ;
2005-12-08 15:38:44 +08:00
long pointer ; /* relative position within file data */
2005-12-05 04:35:38 +08:00
char * file ;
phar_manifest_entry * internal_file ;
} phar_internal_file_data ;
/* True global resources - no need for thread safety here */
/* borrowed from ext/standard/pack.c */
static int machine_little_endian ;
static int little_endian_long_map [ 4 ] ;
/* end borrowing */
static zend_class_entry * php_archive_entry_ptr ;
static void destroy_phar_data ( void * pDest )
{
phar_file_data * data = ( phar_file_data * ) pDest ;
2005-12-10 07:37:03 +08:00
efree ( data - > alias ) ;
2005-12-05 04:35:38 +08:00
zend_hash_destroy ( data - > manifest ) ;
2005-12-05 12:21:36 +08:00
FREE_HASHTABLE ( data - > manifest ) ;
2005-12-05 04:35:38 +08:00
}
static void destroy_phar_manifest ( void * pDest )
{
phar_manifest_entry * data = ( phar_manifest_entry * ) pDest ;
efree ( data - > filename ) ;
}
2005-12-07 09:18:54 +08:00
static phar_internal_file_data * phar_get_filedata ( char * alias , char * path TSRMLS_DC )
2005-12-05 04:35:38 +08:00
{
phar_file_data * data ;
phar_internal_file_data * ret ;
phar_manifest_entry * file_data ;
ret = NULL ;
2005-12-05 16:31:05 +08:00
if ( SUCCESS = = zend_hash_find ( & ( PHAR_GLOBALS - > phar_data ) , alias , strlen ( alias ) , ( void * * ) & data ) ) {
2005-12-05 04:35:38 +08:00
if ( SUCCESS = = zend_hash_find ( data - > manifest , path , strlen ( path ) , ( void * * ) & file_data ) ) {
ret = ( phar_internal_file_data * ) emalloc ( sizeof ( phar_internal_file_data ) ) ;
ret - > data = data ;
ret - > internal_file = file_data ;
ret - > pointer = 0 ;
}
}
return ret ;
}
/* {{{ phar_functions[]
*
* Every user visible function must have an entry in phar_functions [ ] .
*/
function_entry phar_functions [ ] = {
{ NULL , NULL , NULL } /* Must be the last line in phar_functions[] */
} ;
/* }}} */
2005-12-07 14:39:03 +08:00
PHP_METHOD ( PHP_Archive , canCompress )
{
# ifdef HAVE_PHAR_ZLIB
RETURN_TRUE ;
# else
RETURN_FALSE ;
# endif
}
2005-12-05 04:35:38 +08:00
/* {{{ php_archive_methods
*/
PHP_METHOD ( PHP_Archive , mapPhar )
{
2005-12-10 07:55:52 +08:00
char * fname , * alias , * buffer , * endbuffer , * unpack_var , * savebuf ;
2005-12-05 12:21:36 +08:00
phar_file_data mydata ;
2005-12-05 04:35:38 +08:00
zend_bool compressed ;
2005-12-05 12:21:36 +08:00
phar_manifest_entry entry ;
2005-12-05 04:35:38 +08:00
HashTable * manifest ;
int alias_len , i ;
long halt_offset ;
php_uint32 manifest_len , manifest_count , manifest_index ;
zval * halt_constant , * * unused1 , * * unused2 ;
2005-12-05 12:47:29 +08:00
php_stream * fp ;
2005-12-05 04:35:38 +08:00
2005-12-14 11:42:43 +08:00
if ( zend_parse_parameters ( ZEND_NUM_ARGS ( ) TSRMLS_CC , " sb|zz " , & alias , & alias_len , & compressed , & unused1 , & unused2 ) = = FAILURE ) {
2005-12-05 04:35:38 +08:00
return ;
}
2005-12-05 09:07:02 +08:00
# ifndef HAVE_PHAR_ZLIB
2005-12-05 04:35:38 +08:00
if ( compressed ) {
2005-12-07 14:39:03 +08:00
php_error_docref ( NULL TSRMLS_CC , E_ERROR , " zlib extension is required for compressed .phar files " ) ;
return ;
2005-12-05 04:35:38 +08:00
}
# endif
fname = zend_get_executed_filename ( TSRMLS_C ) ;
if ( ! strcmp ( fname , " [no active file] " ) ) {
2005-12-07 14:39:03 +08:00
php_error_docref ( NULL TSRMLS_CC , E_ERROR , " cannot initialize a phar outside of PHP execution " ) ;
2005-12-05 04:35:38 +08:00
return ;
}
MAKE_STD_ZVAL ( halt_constant ) ;
2005-12-08 14:38:26 +08:00
if ( 0 = = zend_get_constant ( " __COMPILER_HALT_OFFSET__ " , 24 , halt_constant TSRMLS_CC ) ) {
zval_dtor ( halt_constant ) ;
FREE_ZVAL ( halt_constant ) ;
php_error_docref ( NULL TSRMLS_CC , E_ERROR , " __HALT_COMPILER(); must be declared in a phar " ) ;
return ;
}
2005-12-05 04:35:38 +08:00
halt_offset = Z_LVAL ( * halt_constant ) ;
zval_dtor ( halt_constant ) ;
2005-12-05 12:21:36 +08:00
FREE_ZVAL ( halt_constant ) ;
2005-12-05 04:35:38 +08:00
2005-12-05 12:47:29 +08:00
if ( PG ( safe_mode ) & & ( ! php_checkuid ( fname , NULL , CHECKUID_ALLOW_ONLY_FILE ) ) ) {
return ;
}
2005-12-05 04:35:38 +08:00
2005-12-05 12:47:29 +08:00
if ( php_check_open_basedir ( fname TSRMLS_CC ) ) {
return ;
2005-12-05 04:35:38 +08:00
}
2005-12-05 12:47:29 +08:00
fp = php_stream_open_wrapper ( fname , " rb " , IGNORE_URL | STREAM_MUST_SEEK | REPORT_ERRORS , NULL ) ;
2005-12-05 04:35:38 +08:00
if ( ! fp ) {
2005-12-07 14:39:03 +08:00
php_error_docref ( NULL TSRMLS_CC , E_ERROR , " unable to open phar for reading \" %s \" " , fname ) ;
2005-12-05 04:35:38 +08:00
return ;
}
2005-12-07 14:39:03 +08:00
# define MAPPHAR_ALLOC_FAIL(msg) php_stream_close(fp);\
php_stream_close ( fp ) ; \
php_error_docref ( NULL TSRMLS_CC , E_ERROR , msg , fname ) ; \
2005-12-05 04:35:38 +08:00
return ;
2005-12-08 15:34:16 +08:00
# define MAPPHAR_FAIL(msg) efree(savebuf);\
2005-12-07 14:39:03 +08:00
MAPPHAR_ALLOC_FAIL ( msg )
2005-12-08 14:46:02 +08:00
2005-12-08 15:38:44 +08:00
/* check for ?>\n and increment accordingly */
2005-12-08 15:08:49 +08:00
if ( - 1 = = php_stream_seek ( fp , halt_offset , SEEK_SET ) ) {
2005-12-08 15:34:16 +08:00
MAPPHAR_ALLOC_FAIL ( " cannot seek to __HALT_COMPILER(); location in phar \" %s \" " )
2005-12-08 14:46:02 +08:00
}
2005-12-07 14:39:03 +08:00
if ( FALSE = = ( buffer = ( char * ) emalloc ( 4 ) ) ) {
MAPPHAR_ALLOC_FAIL ( " memory allocation failed in phar \" %s \" " )
}
2005-12-08 15:34:16 +08:00
savebuf = buffer ;
2005-12-07 14:39:03 +08:00
if ( 3 ! = php_stream_read ( fp , buffer , 3 ) ) {
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (truncated manifest) " )
2005-12-05 04:35:38 +08:00
}
if ( * buffer = = ' ' & & * ( buffer + 1 ) = = ' ? ' & & * ( buffer + 2 ) = = ' > ' ) {
int nextchar ;
2005-12-07 09:18:54 +08:00
halt_offset + = 3 ;
2005-12-07 14:39:03 +08:00
if ( EOF = = ( nextchar = php_stream_getc ( fp ) ) ) {
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (truncated manifest) " )
}
2005-12-05 04:35:38 +08:00
if ( ( char ) nextchar = = ' \r ' ) {
2005-12-07 14:39:03 +08:00
if ( EOF = = ( nextchar = php_stream_getc ( fp ) ) ) {
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (truncated manifest) " )
}
2005-12-05 04:35:38 +08:00
halt_offset + + ;
}
if ( ( char ) nextchar = = ' \n ' ) {
halt_offset + + ;
}
}
2005-12-08 15:38:44 +08:00
/* make sure we are at the right location to read the manifest */
2005-12-08 15:08:49 +08:00
if ( - 1 = = php_stream_seek ( fp , halt_offset , SEEK_SET ) ) {
MAPPHAR_FAIL ( " cannot seek to __HALT_COMPILER(); location in phar \" %s \" " )
}
2005-12-05 04:35:38 +08:00
2005-12-08 15:38:44 +08:00
/* read in manifest */
2005-12-05 04:35:38 +08:00
i = 0 ;
# define PHAR_GET_VAL(var) \
2005-12-08 15:34:16 +08:00
if ( buffer > endbuffer ) { \
2005-12-08 15:59:18 +08:00
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (buffer overrun) " ) \
2005-12-08 15:34:16 +08:00
} \
2005-12-05 04:35:38 +08:00
unpack_var = ( char * ) & var ; \
var = 0 ; \
for ( i = 0 ; i < 4 ; i + + ) { \
unpack_var [ little_endian_long_map [ i ] ] = * buffer + + ; \
2005-12-08 15:59:18 +08:00
if ( buffer > endbuffer ) { \
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (buffer overrun) " ) \
} \
2005-12-05 04:35:38 +08:00
}
2005-12-07 14:39:03 +08:00
if ( 4 ! = php_stream_read ( fp , buffer , 4 ) ) {
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (truncated manifest) " )
}
2005-12-08 15:59:18 +08:00
endbuffer = buffer + 5 ;
2005-12-05 04:35:38 +08:00
PHAR_GET_VAL ( manifest_len )
buffer - = 4 ;
2005-12-08 15:34:16 +08:00
if ( manifest_len > 1048576 ) {
/* prevent serious memory issues by limiting manifest to at most 1 MB in length */
MAPPHAR_FAIL ( " manifest cannot be larger than 1 MB in phar \" %s \" " )
}
2005-12-07 14:39:03 +08:00
if ( FALSE = = ( buffer = ( char * ) erealloc ( buffer , manifest_len ) ) ) {
2005-12-08 15:34:16 +08:00
MAPPHAR_FAIL ( " memory allocation failed in phar \" %s \" " )
2005-12-07 14:39:03 +08:00
}
2005-12-05 04:35:38 +08:00
savebuf = buffer ;
2005-12-08 15:38:44 +08:00
/* set the test pointer */
2005-12-05 04:35:38 +08:00
endbuffer = buffer + manifest_len ;
2005-12-08 15:38:44 +08:00
/* retrieve manifest */
2005-12-07 14:39:03 +08:00
if ( manifest_len ! = php_stream_read ( fp , buffer , manifest_len ) ) {
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (truncated manifest) " )
}
2005-12-08 15:38:44 +08:00
/* extract the number of entries */
2005-12-05 04:35:38 +08:00
PHAR_GET_VAL ( manifest_count )
2005-12-10 07:37:03 +08:00
/* we have 5 32-bit items at least */
if ( manifest_count > ( manifest_len / ( 4 * 5 ) ) ) {
2005-12-10 03:55:29 +08:00
/* prevent serious memory issues */
MAPPHAR_FAIL ( " too many manifest entries for size of manifest in phar \" %s \" " )
}
2005-12-08 15:38:44 +08:00
/* set up our manifest */
2005-12-05 12:21:36 +08:00
ALLOC_HASHTABLE ( manifest ) ;
2005-12-05 04:35:38 +08:00
zend_hash_init ( manifest , sizeof ( phar_manifest_entry ) ,
zend_get_hash_value , destroy_phar_manifest , 0 ) ;
for ( manifest_index = 0 ; manifest_index < manifest_count ; manifest_index + + ) {
2005-12-07 14:39:03 +08:00
if ( buffer > endbuffer ) {
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (truncated manifest) " )
}
2005-12-05 12:21:36 +08:00
PHAR_GET_VAL ( entry . filename_len )
entry . filename = ( char * ) emalloc ( entry . filename_len + 1 ) ;
memcpy ( entry . filename , buffer , entry . filename_len ) ;
entry . filename [ entry . filename_len ] = ' \0 ' ;
buffer + = entry . filename_len ;
PHAR_GET_VAL ( entry . uncompressed_filesize )
PHAR_GET_VAL ( entry . timestamp )
PHAR_GET_VAL ( entry . offset_within_phar )
PHAR_GET_VAL ( entry . compressed_filesize )
2005-12-10 08:00:00 +08:00
entry . crc_checked = 0 ;
2005-12-10 07:55:52 +08:00
if ( entry . compressed_filesize < 9 ) {
MAPPHAR_FAIL ( " internal corruption of phar \" %s \" (file size in phar is not large enough) " )
}
2005-12-05 12:21:36 +08:00
zend_hash_add ( manifest , entry . filename , entry . filename_len , & entry ,
2005-12-05 04:35:38 +08:00
sizeof ( phar_manifest_entry ) , NULL ) ;
}
# undef PHAR_GET_VAL
2005-12-05 12:21:36 +08:00
mydata . file = fname ;
2005-12-10 07:37:03 +08:00
/* alias is auto-efreed after returning, so we must dupe it */
mydata . alias = estrndup ( alias , alias_len ) ;
2005-12-05 12:21:36 +08:00
mydata . alias_len = alias_len ;
mydata . internal_file_start = manifest_len + halt_offset + 4 ;
mydata . is_compressed = compressed ;
mydata . manifest = manifest ;
2005-12-05 16:31:05 +08:00
zend_hash_add ( & ( PHAR_GLOBALS - > phar_data ) , alias , alias_len , & mydata ,
2005-12-05 04:35:38 +08:00
sizeof ( phar_file_data ) , NULL ) ;
efree ( savebuf ) ;
2005-12-05 12:47:29 +08:00
php_stream_close ( fp ) ;
2005-12-05 04:35:38 +08:00
}
2005-12-14 11:42:43 +08:00
PHP_METHOD ( PHP_Archive , apiVersion ) ;
2005-12-05 04:35:38 +08:00
zend_function_entry php_archive_methods [ ] = {
PHP_ME ( PHP_Archive , mapPhar , NULL , ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_FINAL )
PHP_ME ( PHP_Archive , apiVersion , NULL , ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_FINAL )
2005-12-07 14:39:03 +08:00
PHP_ME ( PHP_Archive , canCompress , NULL , ZEND_ACC_PUBLIC | ZEND_ACC_STATIC | ZEND_ACC_FINAL )
2005-12-05 04:35:38 +08:00
{ NULL , NULL , NULL }
} ;
/* }}} */
/* {{{ phar_module_entry
*/
zend_module_entry phar_module_entry = {
# if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER ,
# endif
" phar " ,
phar_functions ,
PHP_MINIT ( phar ) ,
PHP_MSHUTDOWN ( phar ) ,
PHP_RINIT ( phar ) ,
PHP_RSHUTDOWN ( phar ) ,
PHP_MINFO ( phar ) ,
# if ZEND_MODULE_API_NO >= 20010901
" 0.1.0 " , /* Replace with version number for your extension */
# endif
STANDARD_MODULE_PROPERTIES
} ;
/* }}} */
# ifdef COMPILE_DL_PHAR
ZEND_GET_MODULE ( phar )
# endif
/* {{{ php_phar_init_globals
*/
static void php_phar_init_globals_module ( zend_phar_globals * phar_globals )
{
memset ( phar_globals , 0 , sizeof ( zend_phar_globals ) ) ;
}
/* }}} */
PHP_PHAR_API php_stream * php_stream_phar_url_wrapper ( php_stream_wrapper * wrapper , char * path , char * mode , int options , char * * opened_path , php_stream_context * context STREAMS_DC TSRMLS_DC ) ;
PHP_PHAR_API int phar_close ( php_stream * stream , int close_handle TSRMLS_DC ) ;
2005-12-05 16:31:05 +08:00
PHP_PHAR_API int phar_closedir ( php_stream * stream , int close_handle TSRMLS_DC ) ;
2005-12-05 04:35:38 +08:00
PHP_PHAR_API size_t phar_read ( php_stream * stream , char * buf , size_t count TSRMLS_DC ) ;
PHP_PHAR_API size_t phar_readdir ( php_stream * stream , char * buf , size_t count TSRMLS_DC ) ;
PHP_PHAR_API int phar_seek ( php_stream * stream , off_t offset , int whence , off_t * newoffset TSRMLS_DC ) ;
PHP_PHAR_API size_t phar_write ( php_stream * stream , const char * buf , size_t count TSRMLS_DC ) ;
PHP_PHAR_API int phar_flush ( php_stream * stream TSRMLS_DC ) ;
PHP_PHAR_API int phar_stat ( php_stream * stream , php_stream_statbuf * ssb TSRMLS_DC ) ;
PHP_PHAR_API int phar_stream_stat ( php_stream_wrapper * wrapper , char * url , int flags , php_stream_statbuf * ssb , php_stream_context * context TSRMLS_DC ) ;
PHP_PHAR_API php_stream * phar_opendir ( php_stream_wrapper * wrapper , char * filename , char * mode ,
int options , char * * opened_path , php_stream_context * context STREAMS_DC TSRMLS_DC ) ;
static php_stream_ops phar_ops = {
2005-12-08 15:38:44 +08:00
phar_write , /* write (does nothing) */
phar_read , /* read */
phar_close , /* close */
phar_flush , /* flush (does nothing) */
2005-12-05 04:35:38 +08:00
" phar stream " ,
2005-12-12 14:17:16 +08:00
phar_seek , /* seek */
2005-12-08 15:38:44 +08:00
NULL , /* cast */
phar_stat , /* stat */
NULL , /* set option */
2005-12-05 04:35:38 +08:00
} ;
static php_stream_ops phar_dir_ops = {
2005-12-08 15:38:44 +08:00
phar_write , /* write (does nothing) */
phar_readdir , /* read */
phar_closedir , /* close */
phar_flush , /* flush (does nothing) */
2005-12-05 04:35:38 +08:00
" phar stream " ,
2005-12-08 15:38:44 +08:00
NULL , /* seek */
NULL , /* cast */
2005-12-11 15:25:30 +08:00
phar_stat , /* stat */
2005-12-08 15:38:44 +08:00
NULL , /* set option */
2005-12-05 04:35:38 +08:00
} ;
static php_stream_wrapper_ops phar_stream_wops = {
php_stream_phar_url_wrapper ,
NULL , /* stream_close */
2005-12-08 15:38:44 +08:00
NULL , /* php_stream_phar_stat, */
2005-12-05 04:35:38 +08:00
phar_stream_stat , /* stat_url */
phar_opendir , /* opendir */
" phar " ,
NULL , /* unlink */
NULL , /* rename */
NULL , /* create directory */
NULL /* remove directory */
} ;
php_stream_wrapper php_stream_phar_wrapper = {
& phar_stream_wops ,
NULL ,
0 /* is_url */
} ;
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION ( phar )
{
zend_class_entry php_archive_entry ;
int machine_endian_check = 1 ;
2005-12-07 09:18:54 +08:00
ZEND_INIT_MODULE_GLOBALS ( phar , php_phar_init_globals_module , NULL ) ;
2005-12-05 04:35:38 +08:00
machine_little_endian = ( ( char * ) & machine_endian_check ) [ 0 ] ;
if ( machine_little_endian ) {
little_endian_long_map [ 0 ] = 0 ;
little_endian_long_map [ 1 ] = 1 ;
little_endian_long_map [ 2 ] = 2 ;
little_endian_long_map [ 3 ] = 3 ;
}
else {
zval val ;
int size = sizeof ( Z_LVAL ( val ) ) ;
Z_LVAL ( val ) = 0 ; /*silence a warning*/
little_endian_long_map [ 0 ] = size - 1 ;
little_endian_long_map [ 1 ] = size - 2 ;
little_endian_long_map [ 2 ] = size - 3 ;
little_endian_long_map [ 3 ] = size - 4 ;
}
INIT_CLASS_ENTRY ( php_archive_entry , " PHP_Archive " , php_archive_methods ) ;
php_archive_entry_ptr = zend_register_internal_class ( & php_archive_entry TSRMLS_CC ) ;
return php_register_url_stream_wrapper ( " phar " , & php_stream_phar_wrapper TSRMLS_CC ) ;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION ( phar )
{
return php_unregister_url_stream_wrapper ( " phar " TSRMLS_CC ) ;
}
/* }}} */
/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION ( phar )
{
zend_hash_init ( & ( PHAR_GLOBALS - > phar_data ) , sizeof ( phar_file_data ) ,
zend_get_hash_value , destroy_phar_data , 0 ) ;
return SUCCESS ;
}
/* }}} */
/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION ( phar )
{
zend_hash_destroy ( & ( PHAR_GLOBALS - > phar_data ) ) ;
return SUCCESS ;
}
/* }}} */
2005-12-14 11:42:43 +08:00
PHP_METHOD ( PHP_Archive , apiVersion )
{
RETURN_STRING ( " 0.7.1 " , 3 ) ;
}
2005-12-05 04:35:38 +08:00
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION ( phar )
{
php_info_print_table_start ( ) ;
php_info_print_table_header ( 2 , " phar PHP Archive support " , " enabled " ) ;
2005-12-14 11:42:43 +08:00
php_info_print_table_row ( 2 , " phar API version " , " 0.7.1 " ) ;
2006-01-02 06:19:51 +08:00
php_info_print_table_row ( 2 , " CVS revision " , " $Revision$ " ) ;
php_info_print_table_row ( 2 , " compressed phar support " ,
2005-12-14 11:42:43 +08:00
# ifdef HAVE_PHAR_ZLIB
2006-01-02 06:19:51 +08:00
" enabled " ) ;
2005-12-14 11:42:43 +08:00
# else
2006-01-02 06:19:51 +08:00
" disabled " ) ;
2005-12-14 11:42:43 +08:00
# endif
2005-12-05 04:35:38 +08:00
php_info_print_table_end ( ) ;
}
/* }}} */
/*
*/
static int phar_postprocess_file ( char * contents , php_uint32 nr , unsigned long crc32 , zend_bool read )
{
unsigned int crc = ~ 0 ;
php_uint32 i , actual_length ;
char * unpack_var ;
int len = 0 ;
if ( read ) {
# define PHAR_GET_VAL(var) \
unpack_var = ( char * ) & var ; \
var = 0 ; \
for ( i = 0 ; i < 4 ; i + + ) { \
unpack_var [ little_endian_long_map [ i ] ] = * contents + + ; \
}
PHAR_GET_VAL ( crc32 )
PHAR_GET_VAL ( actual_length )
if ( actual_length ! = nr ) {
2005-12-10 07:55:52 +08:00
return - 2 ;
2005-12-05 04:35:38 +08:00
}
}
for ( len + = nr ; nr - - ; + + contents ) {
CRC32 ( crc , * contents ) ;
}
if ( ~ crc = = crc32 ) {
return 0 ;
} else {
return - 1 ;
}
# undef PHAR_GET_VAL
}
/* {{{ php_stream_phar_url_wrapper
*/
PHP_PHAR_API php_stream * php_stream_phar_url_wrapper ( php_stream_wrapper * wrapper , char * path , char * mode , int options , char * * opened_path , php_stream_context * context STREAMS_DC TSRMLS_DC )
{
phar_internal_file_data * idata ;
php_stream * stream = NULL ;
char * internal_file ;
char * buffer ;
php_url * resource = NULL ;
2005-12-05 12:47:29 +08:00
php_stream * fp ;
2005-12-10 07:55:52 +08:00
int status ;
2005-12-05 09:07:02 +08:00
# ifdef HAVE_PHAR_ZLIB
2005-12-10 07:55:52 +08:00
unsigned long crc32 ;
php_uint32 actual_length , i ;
char * unpack_var , * savebuf ;
2005-12-05 04:35:38 +08:00
/* borrowed from zlib.c gzinflate() function */
2005-12-10 07:37:03 +08:00
php_uint32 offset ;
2005-12-05 04:35:38 +08:00
unsigned long length ;
char * s1 = NULL ;
z_stream zstream ;
# endif
2005-12-07 14:39:03 +08:00
resource = php_url_parse ( path ) ;
2006-01-02 04:40:43 +08:00
/* we must have at the very least phar://alias.phar/internalfile.php */
2005-12-05 04:35:38 +08:00
if ( ! resource | | ! resource - > scheme | | ! resource - > host | | ! resource - > path ) {
2005-12-07 14:39:03 +08:00
if ( resource ) {
php_url_free ( resource ) ;
}
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: invalid url \" %s \" " , path ) ;
2005-12-07 14:39:03 +08:00
return NULL ;
2005-12-05 04:35:38 +08:00
}
if ( strcasecmp ( " phar " , resource - > scheme ) ) {
2005-12-07 14:39:03 +08:00
if ( resource ) {
php_url_free ( resource ) ;
}
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: not a phar stream url \" %s \" " , path ) ;
2005-12-07 14:39:03 +08:00
return NULL ;
2005-12-05 04:35:38 +08:00
}
2005-12-10 07:37:03 +08:00
/* strip leading "/" */
internal_file = estrndup ( resource - > path + 1 , strlen ( resource - > path ) - 1 ) ;
2005-12-07 09:18:54 +08:00
if ( NULL = = ( idata = phar_get_filedata ( resource - > host , internal_file TSRMLS_CC ) ) ) {
2006-01-01 15:40:36 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: \" %s \" is not a file in phar \" %s \" " , internal_file , resource - > host ) ;
2005-12-10 07:37:03 +08:00
efree ( internal_file ) ;
2005-12-05 04:35:38 +08:00
return NULL ;
}
php_url_free ( resource ) ;
2005-12-05 12:47:29 +08:00
if ( PG ( safe_mode ) & & ( ! php_checkuid ( idata - > data - > file , NULL , CHECKUID_ALLOW_ONLY_FILE ) ) ) {
2005-12-10 07:37:03 +08:00
efree ( internal_file ) ;
2005-12-05 12:47:29 +08:00
return NULL ;
}
2005-12-05 04:35:38 +08:00
2005-12-05 12:47:29 +08:00
if ( php_check_open_basedir ( idata - > data - > file TSRMLS_CC ) ) {
2005-12-10 07:37:03 +08:00
efree ( internal_file ) ;
2005-12-05 12:47:29 +08:00
return NULL ;
2005-12-05 04:35:38 +08:00
}
2005-12-05 12:47:29 +08:00
fp = php_stream_open_wrapper ( idata - > data - > file , " rb " , IGNORE_URL | STREAM_MUST_SEEK | REPORT_ERRORS , NULL ) ;
2005-12-05 04:35:38 +08:00
if ( ! fp ) {
2005-12-10 07:37:03 +08:00
buffer = idata - > data - > file ;
2005-12-05 04:35:38 +08:00
efree ( idata ) ;
2005-12-10 07:37:03 +08:00
efree ( internal_file ) ;
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: cannot open phar \" %s \" " , buffer ) ;
2005-12-05 04:35:38 +08:00
return NULL ;
}
/* seek to start of internal file and read it */
2005-12-10 07:37:03 +08:00
if ( - 1 = = php_stream_seek ( fp , idata - > data - > internal_file_start + idata - > internal_file - > offset_within_phar , SEEK_SET ) ) {
php_stream_close ( fp ) ;
buffer = idata - > data - > file ;
offset = idata - > data - > internal_file_start + idata - > internal_file - > offset_within_phar ;
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (cannot seek to start of file \" %s \" at offset \" %d \" ) " ,
2005-12-10 07:37:03 +08:00
buffer , internal_file ,
offset ) ;
2005-12-11 08:00:31 +08:00
efree ( internal_file ) ;
2005-12-10 07:37:03 +08:00
return NULL ;
}
2005-12-05 04:35:38 +08:00
if ( idata - > data - > is_compressed ) {
2005-12-05 09:07:02 +08:00
# ifdef HAVE_PHAR_ZLIB
2005-12-05 04:35:38 +08:00
buffer = ( char * ) emalloc ( idata - > internal_file - > compressed_filesize ) ;
if ( idata - > internal_file - > compressed_filesize ! =
2005-12-05 12:47:29 +08:00
php_stream_read ( fp , buffer , idata - > internal_file - > compressed_filesize ) ) {
2006-01-02 06:11:03 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (filesize mismatch on file \" %s \" ) " , idata - > data - > file , internal_file ) ;
2005-12-05 12:47:29 +08:00
php_stream_close ( fp ) ;
2005-12-05 04:35:38 +08:00
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
efree ( internal_file ) ;
2005-12-05 04:35:38 +08:00
return NULL ;
}
2005-12-05 12:47:29 +08:00
php_stream_close ( fp ) ;
2005-12-05 04:35:38 +08:00
savebuf = buffer ;
# define PHAR_GET_VAL(var) \
unpack_var = ( char * ) & var ; \
var = 0 ; \
for ( i = 0 ; i < 4 ; i + + ) { \
unpack_var [ little_endian_long_map [ i ] ] = * buffer + + ; \
}
PHAR_GET_VAL ( crc32 )
PHAR_GET_VAL ( actual_length )
# undef PHAR_GET_VAL
/* borrowed from zlib.c gzinflate() function */
zstream . zalloc = ( alloc_func ) Z_NULL ;
zstream . zfree = ( free_func ) Z_NULL ;
length = idata - > internal_file - > uncompressed_filesize ;
do {
idata - > file = ( char * ) erealloc ( s1 , length ) ;
if ( ! idata - > file & & s1 ) {
efree ( s1 ) ;
efree ( savebuf ) ;
2005-12-10 07:37:03 +08:00
buffer = idata - > data - > file ;
2005-12-05 04:35:38 +08:00
efree ( idata - > file ) ;
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (corrupted zlib compression of file \" %s \" ) " , buffer , internal_file ) ;
efree ( internal_file ) ;
2005-12-05 04:35:38 +08:00
return NULL ;
}
zstream . next_in = ( Bytef * ) buffer ;
zstream . avail_in = ( uInt ) idata - > internal_file - > compressed_filesize ;
zstream . next_out = idata - > file ;
zstream . avail_out = ( uInt ) length ;
/* init with -MAX_WBITS disables the zlib internal headers */
status = inflateInit2 ( & zstream , - MAX_WBITS ) ;
if ( status = = Z_OK ) {
status = inflate ( & zstream , Z_FINISH ) ;
if ( status ! = Z_STREAM_END ) {
inflateEnd ( & zstream ) ;
if ( status = = Z_OK ) {
status = Z_BUF_ERROR ;
}
} else {
status = inflateEnd ( & zstream ) ;
}
}
s1 = idata - > file ;
} while ( status = = Z_BUF_ERROR ) ;
if ( status ! = Z_OK ) {
efree ( savebuf ) ;
efree ( idata - > file ) ;
efree ( idata ) ;
2005-12-11 07:19:32 +08:00
efree ( internal_file ) ;
2005-12-12 05:44:44 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: decompression failed " ) ;
2005-12-05 04:35:38 +08:00
return NULL ;
}
2005-12-07 14:39:03 +08:00
2005-12-05 12:21:36 +08:00
efree ( savebuf ) ;
2005-12-08 15:38:44 +08:00
/* check length */
2005-12-05 04:35:38 +08:00
if ( actual_length ! = idata - > internal_file - > uncompressed_filesize ) {
2006-01-02 06:19:51 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (filesize mismatch on file \" %s \" ) " , idata - > file , internal_file ) ;
2005-12-11 08:00:31 +08:00
efree ( internal_file ) ;
2006-01-02 06:11:03 +08:00
efree ( idata - > file ) ;
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
return NULL ;
2005-12-05 04:35:38 +08:00
}
2005-12-10 07:55:52 +08:00
/* check crc32/filesize */
2005-12-10 08:00:00 +08:00
if ( ! idata - > internal_file - > crc_checked ) {
status = phar_postprocess_file ( idata - > file , idata - > internal_file - > uncompressed_filesize , crc32 , 0 ) ;
if ( - 1 = = status ) {
2006-01-02 06:19:51 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (crc32 mismatch on file \" %s \" ) " , idata - > file , internal_file ) ;
2006-01-02 06:11:03 +08:00
efree ( idata - > file ) ;
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
efree ( internal_file ) ;
2005-12-10 08:00:00 +08:00
return NULL ;
}
if ( - 2 = = status ) {
2006-01-02 06:19:51 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (filesize mismatch on file \" %s \" ) " , idata - > file , internal_file ) ;
2006-01-02 06:11:03 +08:00
efree ( idata - > file ) ;
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
efree ( internal_file ) ;
2005-12-10 08:00:00 +08:00
return NULL ;
}
idata - > internal_file - > crc_checked = 1 ;
2005-12-05 04:35:38 +08:00
}
# else
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " zlib extension must be enabled for compressed .phar files " ) ;
efree ( internal_file ) ;
2005-12-07 14:39:03 +08:00
return NULL ;
2005-12-05 04:35:38 +08:00
# endif
2006-01-02 06:11:03 +08:00
} else { /* from here is for non-compressed */
2005-12-05 04:35:38 +08:00
idata - > file = ( char * ) emalloc ( idata - > internal_file - > compressed_filesize ) ;
if ( idata - > internal_file - > compressed_filesize ! =
2005-12-05 12:47:29 +08:00
php_stream_read ( fp , idata - > file , idata - > internal_file - > compressed_filesize ) ) {
php_stream_close ( fp ) ;
2006-01-02 06:19:51 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (actual filesize mismatch on file \" %s \" ) " , idata - > file , internal_file ) ;
2005-12-05 04:35:38 +08:00
efree ( idata - > file ) ;
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
efree ( internal_file ) ;
2005-12-05 04:35:38 +08:00
return NULL ;
}
2005-12-05 12:47:29 +08:00
php_stream_close ( fp ) ;
2005-12-08 15:38:44 +08:00
/* check length, crc32 */
2005-12-10 08:00:00 +08:00
if ( ! idata - > internal_file - > crc_checked ) {
status = phar_postprocess_file ( idata - > file , idata - > internal_file - > uncompressed_filesize , 0 , 1 ) ;
if ( - 1 = = status ) {
2006-01-02 06:19:51 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (crc32 mismatch on file \" %s \" ) " , idata - > file , internal_file ) ;
2005-12-10 08:00:00 +08:00
efree ( idata - > file ) ;
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
efree ( internal_file ) ;
2005-12-10 08:00:00 +08:00
return NULL ;
}
if ( - 2 = = status ) {
2006-01-02 06:19:51 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: internal corruption of phar \" %s \" (filesize mismatch on file \" %s \" ) " , idata - > file , internal_file ) ;
2005-12-10 08:00:00 +08:00
efree ( idata - > file ) ;
efree ( idata ) ;
2005-12-11 08:00:31 +08:00
efree ( internal_file ) ;
2005-12-10 08:00:00 +08:00
return NULL ;
}
idata - > internal_file - > crc_checked = 1 ;
2005-12-10 07:55:52 +08:00
}
2005-12-05 04:35:38 +08:00
memmove ( idata - > file , idata - > file + 8 , idata - > internal_file - > uncompressed_filesize ) ;
}
stream = php_stream_alloc ( & phar_ops , idata , NULL , mode ) ;
2005-12-10 07:37:03 +08:00
efree ( internal_file ) ;
2005-12-05 04:35:38 +08:00
return stream ;
}
/* }}} */
PHP_PHAR_API int phar_close ( php_stream * stream , int close_handle TSRMLS_DC )
{
phar_internal_file_data * data = ( phar_internal_file_data * ) stream - > abstract ;
2006-01-02 06:19:51 +08:00
if ( data - > file ) {
efree ( data - > file ) ;
}
2005-12-05 04:35:38 +08:00
efree ( data ) ;
return 0 ;
}
2005-12-05 16:31:05 +08:00
PHP_PHAR_API int phar_closedir ( php_stream * stream , int close_handle TSRMLS_DC )
{
HashTable * data = ( HashTable * ) stream - > abstract ;
2006-01-02 06:19:51 +08:00
if ( data )
{
zend_hash_destroy ( data ) ;
FREE_HASHTABLE ( data ) ;
stream - > abstract = NULL ;
}
2005-12-05 16:31:05 +08:00
return 0 ;
}
2005-12-05 04:35:38 +08:00
PHP_PHAR_API size_t phar_read ( php_stream * stream , char * buf , size_t count TSRMLS_DC )
{
size_t to_read ;
phar_internal_file_data * data = ( phar_internal_file_data * ) stream - > abstract ;
to_read = MIN ( data - > internal_file - > uncompressed_filesize - data - > pointer , count ) ;
if ( to_read = = 0 ) {
2006-01-01 15:40:36 +08:00
if ( count ! = 0 ) {
stream - > eof = 1 ;
}
2005-12-05 04:35:38 +08:00
return 0 ;
}
memcpy ( buf , data - > file + data - > pointer , to_read ) ;
data - > pointer + = to_read ;
return to_read ;
}
PHP_PHAR_API size_t phar_readdir ( php_stream * stream , char * buf , size_t count TSRMLS_DC )
{
size_t to_read ;
2005-12-05 16:31:05 +08:00
HashTable * data = ( HashTable * ) stream - > abstract ;
char * key ;
uint keylen ;
ulong unused ;
2005-12-05 04:35:38 +08:00
2005-12-05 16:31:05 +08:00
if ( FAILURE = = zend_hash_has_more_elements ( data ) ) {
return 0 ;
}
if ( HASH_KEY_NON_EXISTANT = = zend_hash_get_current_key_ex ( data , & key , & keylen , & unused , 0 , NULL ) ) {
2005-12-05 04:35:38 +08:00
return 0 ;
}
2005-12-05 16:31:05 +08:00
zend_hash_move_forward ( data ) ;
to_read = MIN ( keylen , count ) ;
if ( to_read = = 0 | | count < keylen ) {
return 0 ;
}
memset ( buf , 0 , sizeof ( php_stream_dirent ) ) ;
memcpy ( ( ( php_stream_dirent * ) buf ) - > d_name , key , to_read ) ;
( ( php_stream_dirent * ) buf ) - > d_name [ to_read + 1 ] = ' \0 ' ;
2005-12-05 04:35:38 +08:00
2005-12-05 16:31:05 +08:00
return sizeof ( php_stream_dirent ) ;
2005-12-05 04:35:38 +08:00
}
PHP_PHAR_API int phar_seek ( php_stream * stream , off_t offset , int whence , off_t * newoffset TSRMLS_DC )
{
phar_internal_file_data * data = ( phar_internal_file_data * ) stream - > abstract ;
switch ( whence ) {
case SEEK_SET :
if ( offset < 0 | | offset > data - > internal_file - > uncompressed_filesize ) {
* newoffset = ( off_t ) - 1 ;
return - 1 ;
}
data - > pointer = offset ;
* newoffset = offset ;
return 0 ;
case SEEK_CUR :
if ( data - > pointer + offset < 0 | | data - > pointer + offset
> data - > internal_file - > uncompressed_filesize ) {
* newoffset = ( off_t ) - 1 ;
return - 1 ;
}
data - > pointer + = offset ;
* newoffset = data - > pointer ;
return 0 ;
case SEEK_END :
if ( offset > 0 | | - 1 * offset > data - > internal_file - > uncompressed_filesize ) {
* newoffset = ( off_t ) - 1 ;
return - 1 ;
}
data - > pointer = data - > internal_file - > uncompressed_filesize + offset ;
* newoffset = data - > pointer ;
return 0 ;
default :
* newoffset = ( off_t ) - 1 ;
return - 1 ;
}
}
PHP_PHAR_API size_t phar_write ( php_stream * stream , const char * buf , size_t count TSRMLS_DC )
{
return 0 ;
}
PHP_PHAR_API int phar_flush ( php_stream * stream TSRMLS_DC )
{
return EOF ;
}
2006-01-01 15:40:36 +08:00
static void phar_dostat ( phar_manifest_entry * data , php_stream_statbuf * ssb , zend_bool is_dir , char * alias ,
int alias_len TSRMLS_DC ) ;
2005-12-05 04:35:38 +08:00
PHP_PHAR_API int phar_stat ( php_stream * stream , php_stream_statbuf * ssb TSRMLS_DC )
{
2005-12-07 09:18:54 +08:00
phar_internal_file_data * data ;
2005-12-05 04:35:38 +08:00
/* If ssb is NULL then someone is misbehaving */
if ( ! ssb ) return - 1 ;
2005-12-07 09:18:54 +08:00
data = ( phar_internal_file_data * ) stream - > abstract ;
2006-01-01 15:40:36 +08:00
phar_dostat ( data - > internal_file , ssb , 0 , data - > data - > alias , data - > data - > alias_len TSRMLS_CC ) ;
2005-12-05 04:35:38 +08:00
return 0 ;
}
2006-01-01 15:40:36 +08:00
static void phar_dostat ( phar_manifest_entry * data , php_stream_statbuf * ssb , zend_bool is_dir , char * alias ,
int alias_len TSRMLS_DC )
2005-12-05 04:35:38 +08:00
{
2006-01-01 15:40:36 +08:00
char * tmp ;
int tmp_len ;
2005-12-05 04:35:38 +08:00
memset ( ssb , 0 , sizeof ( php_stream_statbuf ) ) ;
2005-12-11 15:25:30 +08:00
/* read-only across the board */
2005-12-05 04:35:38 +08:00
ssb - > sb . st_mode = 0444 ;
if ( ! is_dir ) {
ssb - > sb . st_size = data - > uncompressed_filesize ;
2005-12-11 15:25:30 +08:00
ssb - > sb . st_mode | = S_IFREG ; /* regular file */
/* timestamp is just the timestamp when this was added to the phar */
2005-12-05 04:35:38 +08:00
# ifdef NETWARE
ssb - > sb . st_mtime . tv_sec = data - > timestamp ;
ssb - > sb . st_atime . tv_sec = data - > timestamp ;
ssb - > sb . st_ctime . tv_sec = data - > timestamp ;
# else
ssb - > sb . st_mtime = data - > timestamp ;
ssb - > sb . st_atime = data - > timestamp ;
ssb - > sb . st_ctime = data - > timestamp ;
# endif
} else {
ssb - > sb . st_size = 0 ;
2005-12-11 15:25:30 +08:00
ssb - > sb . st_mode | = S_IFDIR ; /* regular directory */
2005-12-05 04:35:38 +08:00
# ifdef NETWARE
ssb - > sb . st_mtime . tv_sec = 0 ;
ssb - > sb . st_atime . tv_sec = 0 ;
ssb - > sb . st_ctime . tv_sec = 0 ;
# else
ssb - > sb . st_mtime = 0 ;
ssb - > sb . st_atime = 0 ;
ssb - > sb . st_ctime = 0 ;
# endif
}
ssb - > sb . st_nlink = 1 ;
ssb - > sb . st_rdev = - 1 ;
2006-01-01 15:40:36 +08:00
if ( data ) {
tmp_len = data - > filename_len + alias_len ;
} else {
tmp_len = alias_len + 1 ;
}
tmp = ( char * ) emalloc ( tmp_len ) ;
memcpy ( tmp , alias , alias_len ) ;
if ( data ) {
memcpy ( tmp + alias_len , data - > filename , data - > filename_len ) ;
} else {
* ( tmp + alias_len ) = ' / ' ;
}
/* this is only for APC, so use /dev/null device - no chance of conflict there! */
ssb - > sb . st_dev = 0xc ;
/* generate unique inode number for alias/filename, so no phars will conflict */
ssb - > sb . st_ino = zend_get_hash_value ( tmp , tmp_len ) ;
efree ( tmp ) ;
2005-12-07 09:18:54 +08:00
# ifndef PHP_WIN32
2005-12-05 04:35:38 +08:00
ssb - > sb . st_blksize = - 1 ;
ssb - > sb . st_blocks = - 1 ;
2005-12-07 09:18:54 +08:00
# endif
2005-12-05 04:35:38 +08:00
}
PHP_PHAR_API int phar_stream_stat ( php_stream_wrapper * wrapper , char * url , int flags ,
php_stream_statbuf * ssb , php_stream_context * context TSRMLS_DC )
{
php_url * resource = NULL ;
char * internal_file , * key ;
uint keylen ;
ulong unused ;
phar_file_data * data ;
phar_manifest_entry * file_data ;
resource = php_url_parse ( url ) ;
2006-01-02 04:40:43 +08:00
/* we must have at the very least phar://alias.phar/internalfile.php */
2005-12-05 04:35:38 +08:00
if ( ! resource | | ! resource - > scheme | | ! resource - > host | | ! resource - > path ) {
2005-12-07 14:39:03 +08:00
php_url_free ( resource ) ;
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , flags TSRMLS_CC , " phar error: invalid url \" %s \" " , url ) ;
2005-12-05 04:35:38 +08:00
php_url_free ( resource ) ;
return - 1 ;
}
if ( strcasecmp ( " phar " , resource - > scheme ) ) {
2005-12-07 14:39:03 +08:00
php_url_free ( resource ) ;
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , flags TSRMLS_CC , " phar error: not a phar url \" %s \" " , url ) ;
2005-12-07 14:39:03 +08:00
return - 1 ;
2005-12-05 04:35:38 +08:00
}
internal_file = resource - > path + 1 ; /* strip leading "/" */
2006-01-02 04:40:43 +08:00
/* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */
2005-12-05 16:31:05 +08:00
if ( SUCCESS = = zend_hash_find ( & ( PHAR_GLOBALS - > phar_data ) , resource - > host , strlen ( resource - > host ) , ( void * * ) & data ) ) {
2005-12-05 04:35:38 +08:00
if ( * internal_file = = ' \0 ' ) {
2005-12-08 15:38:44 +08:00
/* root directory requested */
2006-01-01 15:40:36 +08:00
phar_dostat ( NULL , ssb , 1 , data - > alias , data - > alias_len TSRMLS_CC ) ;
2005-12-05 04:35:38 +08:00
php_url_free ( resource ) ;
return 0 ;
}
2005-12-11 15:25:30 +08:00
/* search through the manifest of files, and if we have an exact match, it's a file */
2005-12-05 04:35:38 +08:00
if ( SUCCESS = = zend_hash_find ( data - > manifest , internal_file , strlen ( internal_file ) , ( void * * ) & file_data ) ) {
2006-01-01 15:40:36 +08:00
phar_dostat ( file_data , ssb , 0 , data - > alias , data - > alias_len TSRMLS_CC ) ;
2005-12-05 04:35:38 +08:00
} else {
2005-12-11 15:25:30 +08:00
/* search for directory (partial match of a file) */
2005-12-05 04:35:38 +08:00
zend_hash_internal_pointer_reset ( data - > manifest ) ;
2005-12-11 15:25:30 +08:00
while ( HASH_KEY_NON_EXISTANT ! = zend_hash_has_more_elements ( data - > manifest ) ) {
2005-12-05 16:31:05 +08:00
if ( HASH_KEY_NON_EXISTANT ! =
zend_hash_get_current_key_ex (
data - > manifest , & key , & keylen , & unused , 0 , NULL ) ) {
2005-12-11 15:25:30 +08:00
if ( 0 = = memcmp ( internal_file , key , strlen ( internal_file ) ) ) {
/* directory found, all dirs have the same stat */
2005-12-11 16:14:44 +08:00
if ( key [ strlen ( internal_file ) ] = = ' / ' ) {
2006-01-01 15:40:36 +08:00
phar_dostat ( NULL , ssb , 1 , data - > alias , data - > alias_len TSRMLS_CC ) ;
2005-12-11 16:14:44 +08:00
break ;
}
2005-12-05 04:35:38 +08:00
}
}
2005-12-11 15:25:30 +08:00
if ( SUCCESS ! = zend_hash_move_forward ( data - > manifest ) ) {
break ;
}
2005-12-05 04:35:38 +08:00
}
}
}
php_url_free ( resource ) ;
return 0 ;
}
2005-12-11 15:25:30 +08:00
/* add an empty element with a char * key to a hash table, avoiding duplicates */
2005-12-05 16:31:05 +08:00
static int phar_add_empty ( HashTable * ht , char * arKey , uint nKeyLength )
{
void * dummy = ( void * ) 1 ;
return zend_hash_update ( ht , arKey , nKeyLength , & dummy , sizeof ( void * ) , NULL ) ;
}
2006-01-02 04:16:09 +08:00
static int compare_dir_name ( const void * a , const void * b TSRMLS_DC )
{
Bucket * f ;
Bucket * s ;
int result ;
f = * ( ( Bucket * * ) a ) ;
s = * ( ( Bucket * * ) b ) ;
2006-01-02 06:20:26 +08:00
result = zend_binary_strcmp ( f - > arKey , f - > nKeyLength ,
s - > arKey , s - > nKeyLength ) ;
2006-01-02 04:16:09 +08:00
if ( result = = 0 ) {
return 0 ;
}
if ( result < 0 ) {
return - 1 ;
} else if ( result > 0 ) {
return 1 ;
} else {
return 0 ;
}
return 0 ;
}
2005-12-07 09:18:54 +08:00
static php_stream * phar_make_dirstream ( char * dir , HashTable * manifest TSRMLS_DC )
2005-12-05 04:35:38 +08:00
{
2005-12-05 16:31:05 +08:00
HashTable * data ;
2005-12-05 04:35:38 +08:00
int dirlen = strlen ( dir ) ;
char * save , * found , * key ;
uint keylen ;
ulong unused ;
2005-12-05 16:31:05 +08:00
char * entry ;
ALLOC_HASHTABLE ( data ) ;
zend_hash_init ( data , 64 , zend_get_hash_value , NULL , 0 ) ;
2005-12-05 04:35:38 +08:00
zend_hash_internal_pointer_reset ( manifest ) ;
2005-12-11 16:14:44 +08:00
while ( HASH_KEY_NON_EXISTANT ! = zend_hash_has_more_elements ( manifest ) ) {
2005-12-05 16:31:05 +08:00
if ( HASH_KEY_NON_EXISTANT = = zend_hash_get_current_key_ex ( manifest , & key , & keylen , & unused , 0 , NULL ) ) {
2005-12-11 16:14:44 +08:00
break ;
2005-12-05 04:35:38 +08:00
}
if ( * dir = = ' / ' ) {
2005-12-11 16:14:44 +08:00
/* root directory */
2005-12-05 16:31:05 +08:00
if ( NULL ! = ( found = ( char * ) memchr ( key , ' / ' , keylen ) ) ) {
2005-12-08 15:38:44 +08:00
/* the entry has a path separator and is a subdirectory */
2005-12-11 16:14:44 +08:00
entry = ( char * ) emalloc ( found - key + 1 ) ;
memcpy ( entry , key , found - key ) ;
keylen = found - key ;
entry [ keylen ] = ' \0 ' ;
} else {
entry = ( char * ) emalloc ( keylen + 1 ) ;
memcpy ( entry , key , keylen ) ;
entry [ keylen ] = ' \0 ' ;
2005-12-05 04:35:38 +08:00
}
2005-12-11 16:14:44 +08:00
goto PHAR_ADD_ENTRY ;
2005-12-05 04:35:38 +08:00
} else {
if ( 0 ! = memcmp ( key , dir , dirlen ) ) {
2005-12-08 15:38:44 +08:00
/* entry in directory not found */
2005-12-11 16:14:44 +08:00
if ( SUCCESS ! = zend_hash_move_forward ( manifest ) ) {
break ;
}
2005-12-05 04:35:38 +08:00
continue ;
2005-12-11 16:14:44 +08:00
} else {
if ( key [ dirlen ] ! = ' / ' ) {
if ( SUCCESS ! = zend_hash_move_forward ( manifest ) ) {
break ;
}
continue ;
}
2005-12-05 04:35:38 +08:00
}
}
save = key ;
2005-12-08 15:38:44 +08:00
save + = dirlen + 1 ; /* seek to just past the path separator */
2005-12-05 04:35:38 +08:00
if ( NULL ! = ( found = ( char * ) memchr ( save , ' / ' , keylen - dirlen - 1 ) ) ) {
2005-12-08 15:38:44 +08:00
/* is subdirectory */
2005-12-05 16:31:05 +08:00
save - = dirlen + 1 ;
2005-12-11 16:14:44 +08:00
entry = ( char * ) emalloc ( found - save + dirlen + 1 ) ;
memcpy ( entry , save + dirlen + 1 , found - save - dirlen - 1 ) ;
keylen = found - save - dirlen - 1 ;
entry [ keylen ] = ' \0 ' ;
2005-12-05 04:35:38 +08:00
} else {
2005-12-08 15:38:44 +08:00
/* is file */
2005-12-05 16:31:05 +08:00
save - = dirlen + 1 ;
entry = ( char * ) emalloc ( keylen - dirlen + 1 ) ;
2005-12-11 16:14:44 +08:00
memcpy ( entry , save + dirlen + 1 , keylen - dirlen - 1 ) ;
entry [ keylen - dirlen - 1 ] = ' \0 ' ;
keylen = keylen - dirlen - 1 ;
2005-12-05 04:35:38 +08:00
}
2005-12-11 16:14:44 +08:00
PHAR_ADD_ENTRY :
2005-12-05 16:31:05 +08:00
phar_add_empty ( data , entry , keylen ) ;
efree ( entry ) ;
2005-12-11 16:14:44 +08:00
if ( SUCCESS ! = zend_hash_move_forward ( manifest ) ) {
break ;
}
}
if ( HASH_KEY_NON_EXISTANT ! = zend_hash_has_more_elements ( data ) ) {
efree ( dir ) ;
2006-01-02 04:16:09 +08:00
if ( zend_hash_sort ( data , zend_qsort , compare_dir_name , 0 TSRMLS_CC ) = = FAILURE ) {
FREE_HASHTABLE ( data ) ;
return NULL ;
}
2005-12-11 16:14:44 +08:00
return php_stream_alloc ( & phar_dir_ops , data , NULL , " r " ) ;
} else {
efree ( dir ) ;
FREE_HASHTABLE ( data ) ;
return NULL ;
2005-12-05 04:35:38 +08:00
}
}
PHP_PHAR_API php_stream * phar_opendir ( php_stream_wrapper * wrapper , char * filename , char * mode ,
int options , char * * opened_path , php_stream_context * context STREAMS_DC TSRMLS_DC )
{
php_url * resource = NULL ;
php_stream * ret ;
char * internal_file , * key ;
uint keylen ;
ulong unused ;
phar_file_data * data ;
phar_manifest_entry * file_data ;
resource = php_url_parse ( filename ) ;
2006-01-02 04:40:43 +08:00
/* we must have at the very least phar://alias.phar/ */
2005-12-05 04:35:38 +08:00
if ( ! resource | | ! resource - > scheme | | ! resource - > host | | ! resource - > path ) {
2005-12-11 15:25:30 +08:00
if ( resource - > host & & ! resource - > path ) {
2006-01-02 04:40:43 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: no directory in \" %s \" , must have at least phar://%s/ for root directory " , filename , resource - > host ) ;
2005-12-11 15:25:30 +08:00
php_url_free ( resource ) ;
return NULL ;
}
2005-12-07 14:39:03 +08:00
php_url_free ( resource ) ;
2006-01-02 04:40:43 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: invalid url \" %s \" , must have at least phar://%s/ " , filename , filename ) ;
2005-12-07 14:39:03 +08:00
return NULL ;
2005-12-05 04:35:38 +08:00
}
if ( strcasecmp ( " phar " , resource - > scheme ) ) {
2005-12-07 14:39:03 +08:00
php_url_free ( resource ) ;
2005-12-11 08:00:31 +08:00
php_stream_wrapper_log_error ( wrapper , options TSRMLS_CC , " phar error: not a phar url \" %s \" " , filename ) ;
2005-12-07 14:39:03 +08:00
return NULL ;
2005-12-05 04:35:38 +08:00
}
internal_file = resource - > path + 1 ; /* strip leading "/" */
2005-12-05 16:31:05 +08:00
if ( SUCCESS = = zend_hash_find ( & ( PHAR_GLOBALS - > phar_data ) , resource - > host , strlen ( resource - > host ) , ( void * * ) & data ) ) {
2005-12-05 04:35:38 +08:00
if ( * internal_file = = ' \0 ' ) {
2005-12-08 15:38:44 +08:00
/* root directory requested */
2005-12-11 16:14:44 +08:00
internal_file = estrndup ( internal_file - 1 , 1 ) ;
ret = phar_make_dirstream ( internal_file , data - > manifest TSRMLS_CC ) ;
2005-12-05 04:35:38 +08:00
php_url_free ( resource ) ;
return ret ;
}
if ( SUCCESS = = zend_hash_find ( data - > manifest , internal_file , strlen ( internal_file ) , ( void * * ) & file_data ) ) {
php_url_free ( resource ) ;
return NULL ;
} else {
2005-12-08 15:38:44 +08:00
/* search for directory */
2005-12-05 04:35:38 +08:00
zend_hash_internal_pointer_reset ( data - > manifest ) ;
2005-12-11 16:14:44 +08:00
while ( HASH_KEY_NON_EXISTANT ! = zend_hash_has_more_elements ( data - > manifest ) ) {
2005-12-05 16:31:05 +08:00
if ( HASH_KEY_NON_EXISTANT ! =
zend_hash_get_current_key_ex (
data - > manifest , & key , & keylen , & unused , 0 , NULL ) ) {
2005-12-11 16:14:44 +08:00
if ( 0 = = memcmp ( key , internal_file , strlen ( internal_file ) ) ) {
2005-12-08 15:38:44 +08:00
/* directory found */
2005-12-11 16:14:44 +08:00
internal_file = estrndup ( internal_file ,
strlen ( internal_file ) ) ;
2005-12-05 04:35:38 +08:00
php_url_free ( resource ) ;
2005-12-07 09:18:54 +08:00
return phar_make_dirstream ( internal_file , data - > manifest TSRMLS_CC ) ;
2005-12-05 04:35:38 +08:00
}
}
2005-12-11 16:14:44 +08:00
if ( SUCCESS ! = zend_hash_move_forward ( data - > manifest ) ) {
break ;
}
2005-12-05 04:35:38 +08:00
}
}
}
php_url_free ( resource ) ;
return NULL ;
}
/*
* Local variables :
* tab - width : 4
* c - basic - offset : 4
* End :
* vim600 : noet sw = 4 ts = 4 fdm = marker
* vim < 600 : noet sw = 4 ts = 4
*/