Back to index

php5  5.3.10
stream.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | phar:// stream wrapper support                                       |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 2005-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt.                                 |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Gregory Beaver <cellog@php.net>                             |
00016   |          Marcus Boerger <helly@php.net>                              |
00017   +----------------------------------------------------------------------+
00018 */
00019 
00020 #define PHAR_STREAM 1
00021 #include "phar_internal.h"
00022 #include "stream.h"
00023 #include "dirstream.h"
00024 
00025 php_stream_ops phar_ops = {
00026        phar_stream_write, /* write */
00027        phar_stream_read,  /* read */
00028        phar_stream_close, /* close */
00029        phar_stream_flush, /* flush */
00030        "phar stream",
00031        phar_stream_seek,  /* seek */
00032        NULL,              /* cast */
00033        phar_stream_stat,  /* stat */
00034        NULL, /* set option */
00035 };
00036 
00037 php_stream_wrapper_ops phar_stream_wops = {
00038        phar_wrapper_open_url,
00039        NULL,                  /* phar_wrapper_close */
00040        NULL,                  /* phar_wrapper_stat, */
00041        phar_wrapper_stat,     /* stat_url */
00042        phar_wrapper_open_dir, /* opendir */
00043        "phar",
00044        phar_wrapper_unlink,   /* unlink */
00045        phar_wrapper_rename,   /* rename */
00046        phar_wrapper_mkdir,    /* create directory */
00047        phar_wrapper_rmdir,    /* remove directory */
00048 };
00049 
00050 php_stream_wrapper php_stream_phar_wrapper = {
00051        &phar_stream_wops,
00052        NULL,
00053        0 /* is_url */
00054 };
00055 
00059 php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC) /* {{{ */
00060 {
00061        php_url *resource;
00062        char *arch = NULL, *entry = NULL, *error;
00063        int arch_len, entry_len;
00064 
00065        if (strlen(filename) < 7 || strncasecmp(filename, "phar://", 7)) {
00066               return NULL;
00067        }
00068        if (mode[0] == 'a') {
00069               if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
00070                      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported");
00071               }
00072               return NULL;
00073        }
00074        if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0) TSRMLS_CC) == FAILURE) {
00075               if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
00076                      if (arch && !entry) {
00077                             php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch);
00078                             arch = NULL;
00079                      } else {
00080                             php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url or non-existent phar \"%s\"", filename);
00081                      }
00082               }
00083               return NULL;
00084        }
00085        resource = ecalloc(1, sizeof(php_url));
00086        resource->scheme = estrndup("phar", 4);
00087        resource->host = arch;
00088 
00089        resource->path = entry;
00090 #if MBO_0
00091               if (resource) {
00092                      fprintf(stderr, "Alias:     %s\n", alias);
00093                      fprintf(stderr, "Scheme:    %s\n", resource->scheme);
00094 /*                   fprintf(stderr, "User:      %s\n", resource->user);*/
00095 /*                   fprintf(stderr, "Pass:      %s\n", resource->pass ? "***" : NULL);*/
00096                      fprintf(stderr, "Host:      %s\n", resource->host);
00097 /*                   fprintf(stderr, "Port:      %d\n", resource->port);*/
00098                      fprintf(stderr, "Path:      %s\n", resource->path);
00099 /*                   fprintf(stderr, "Query:     %s\n", resource->query);*/
00100 /*                   fprintf(stderr, "Fragment:  %s\n", resource->fragment);*/
00101               }
00102 #endif
00103        if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
00104               phar_archive_data **pphar = NULL, *phar;
00105 
00106               if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets && FAILURE == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **)&pphar)) {
00107                      pphar = NULL;
00108               }
00109               if (PHAR_G(readonly) && (!pphar || !(*pphar)->is_data)) {
00110                      if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
00111                             php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by the php.ini setting phar.readonly");
00112                      }
00113                      php_url_free(resource);
00114                      return NULL;
00115               }
00116               if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, 0, options, &phar, &error TSRMLS_CC) == FAILURE)
00117               {
00118                      if (error) {
00119                             if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
00120                                    php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
00121                             }
00122                             efree(error);
00123                      }
00124                      php_url_free(resource);
00125                      return NULL;
00126               }
00127               if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
00128                      if (error) {
00129                             spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", resource->host);
00130                             if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
00131                                    php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
00132                             }
00133                             efree(error);
00134                      }
00135                      php_url_free(resource);
00136                      return NULL;
00137               }
00138        } else {
00139               if (phar_open_from_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE)
00140               {
00141                      if (error) {
00142                             if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
00143                                    php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
00144                             }
00145                             efree(error);
00146                      }
00147                      php_url_free(resource);
00148                      return NULL;
00149               }
00150        }
00151        return resource;
00152 }
00153 /* }}} */
00154 
00158 static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
00159 {
00160        phar_archive_data *phar;
00161        phar_entry_data *idata;
00162        char *internal_file;
00163        char *error;
00164        HashTable *pharcontext;
00165        php_url *resource = NULL;
00166        php_stream *fpf;
00167        zval **pzoption, *metadata;
00168        uint host_len;
00169 
00170        if ((resource = phar_parse_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
00171               return NULL;
00172        }
00173 
00174        /* we must have at the very least phar://alias.phar/internalfile.php */
00175        if (!resource->scheme || !resource->host || !resource->path) {
00176               php_url_free(resource);
00177               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", path);
00178               return NULL;
00179        }
00180 
00181        if (strcasecmp("phar", resource->scheme)) {
00182               php_url_free(resource);
00183               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", path);
00184               return NULL;
00185        }
00186 
00187        host_len = strlen(resource->host);
00188        phar_request_initialize(TSRMLS_C);
00189 
00190        /* strip leading "/" */
00191        internal_file = estrdup(resource->path + 1);
00192        if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
00193               if (NULL == (idata = phar_get_or_create_entry_data(resource->host, host_len, internal_file, strlen(internal_file), mode, 0, &error, 1 TSRMLS_CC))) {
00194                      if (error) {
00195                             php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
00196                             efree(error);
00197                      } else {
00198                             php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, resource->host);
00199                      }
00200                      efree(internal_file);
00201                      php_url_free(resource);
00202                      return NULL;
00203               }
00204               if (error) {
00205                      efree(error);
00206               }
00207               fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
00208               php_url_free(resource);
00209               efree(internal_file);
00210 #if PHP_MAJOR_VERSION >= 6
00211               if (context && context->options && phar_find_key(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption TSRMLS_CC)) {
00212 #else
00213               if (context && context->options && zend_hash_find(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption) == SUCCESS) {
00214 #endif
00215                      pharcontext = HASH_OF(*pzoption);
00216                      if (idata->internal_file->uncompressed_filesize == 0
00217                             && idata->internal_file->compressed_filesize == 0
00218 #if PHP_MAJOR_VERSION >= 6
00219                             && phar_find_key(pharcontext, "compress", sizeof("compress"), (void**)&pzoption TSRMLS_CC)
00220 #else
00221                             && zend_hash_find(pharcontext, "compress", sizeof("compress"), (void**)&pzoption) == SUCCESS
00222 #endif
00223                             && Z_TYPE_PP(pzoption) == IS_LONG
00224                             && (Z_LVAL_PP(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0
00225                      ) {
00226                             idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK;
00227                             idata->internal_file->flags |= Z_LVAL_PP(pzoption);
00228                      }
00229 #if PHP_MAJOR_VERSION >= 6
00230                      if (phar_find_key(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption TSRMLS_CC)) {
00231 #else
00232                      if (zend_hash_find(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption) == SUCCESS) {
00233 #endif
00234                             if (idata->internal_file->metadata) {
00235                                    zval_ptr_dtor(&idata->internal_file->metadata);
00236                                    idata->internal_file->metadata = NULL;
00237                             }
00238 
00239                             MAKE_STD_ZVAL(idata->internal_file->metadata);
00240                             metadata = *pzoption;
00241                             ZVAL_ZVAL(idata->internal_file->metadata, metadata, 1, 0);
00242                             idata->phar->is_modified = 1;
00243                      }
00244               }
00245               if (opened_path) {
00246                      spprintf(opened_path, MAXPATHLEN, "phar://%s/%s", idata->phar->fname, idata->internal_file->filename);
00247               }
00248               return fpf;
00249        } else {
00250               if (!*internal_file && (options & STREAM_OPEN_FOR_INCLUDE)) {
00251                      /* retrieve the stub */
00252                      if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, NULL TSRMLS_CC)) {
00253                             php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "file %s is not a valid phar archive", resource->host);
00254                             efree(internal_file);
00255                             php_url_free(resource);
00256                             return NULL;
00257                      }
00258                      if (phar->is_tar || phar->is_zip) {
00259                             if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, ".phar/stub.php", sizeof(".phar/stub.php")-1, "r", 0, &error, 0 TSRMLS_CC)) || !idata) {
00260                                    goto idata_error;
00261                             }
00262                             efree(internal_file);
00263                             if (opened_path) {
00264                                    spprintf(opened_path, MAXPATHLEN, "%s", phar->fname);
00265                             }
00266                             php_url_free(resource);
00267                             goto phar_stub;
00268                      } else {
00269                             phar_entry_info *entry;
00270 
00271                             entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
00272                             entry->is_temp_dir = 1;
00273                             entry->filename = estrndup("", 0);
00274                             entry->filename_len = 0;
00275                             entry->phar = phar;
00276                             entry->offset = entry->offset_abs = 0;
00277                             entry->compressed_filesize = entry->uncompressed_filesize = phar->halt_offset;
00278                             entry->is_crc_checked = 1;
00279 
00280                             idata = (phar_entry_data *) ecalloc(1, sizeof(phar_entry_data));
00281                             idata->fp = phar_get_pharfp(phar TSRMLS_CC);
00282                             idata->phar = phar;
00283                             idata->internal_file = entry;
00284                             if (!phar->is_persistent) {
00285                                    ++(entry->phar->refcount);
00286                             }
00287                             ++(entry->fp_refcount);
00288                             php_url_free(resource);
00289                             if (opened_path) {
00290                                    spprintf(opened_path, MAXPATHLEN, "%s", phar->fname);
00291                             }
00292                             efree(internal_file);
00293                             goto phar_stub;
00294                      }
00295               }
00296               /* read-only access is allowed to magic files in .phar directory */
00297               if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, strlen(internal_file), "r", 0, &error, 0 TSRMLS_CC)) || !idata) {
00298 idata_error:
00299                      if (error) {
00300                             php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
00301                             efree(error);
00302                      } else {
00303                             php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
00304                      }
00305                      efree(internal_file);
00306                      php_url_free(resource);
00307                      return NULL;
00308               }
00309        }
00310        php_url_free(resource);
00311 #if MBO_0
00312               fprintf(stderr, "Pharname:   %s\n", idata->phar->filename);
00313               fprintf(stderr, "Filename:   %s\n", internal_file);
00314               fprintf(stderr, "Entry:      %s\n", idata->internal_file->filename);
00315               fprintf(stderr, "Size:       %u\n", idata->internal_file->uncompressed_filesize);
00316               fprintf(stderr, "Compressed: %u\n", idata->internal_file->flags);
00317               fprintf(stderr, "Offset:     %u\n", idata->internal_file->offset_within_phar);
00318               fprintf(stderr, "Cached:     %s\n", idata->internal_file->filedata ? "yes" : "no");
00319 #endif
00320 
00321        /* check length, crc32 */
00322        if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2 TSRMLS_CC) != SUCCESS) {
00323               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
00324               efree(error);
00325               phar_entry_delref(idata TSRMLS_CC);
00326               efree(internal_file);
00327               return NULL;
00328        }
00329 
00330        if (!PHAR_G(cwd_init) && options & STREAM_OPEN_FOR_INCLUDE) {
00331               char *entry = idata->internal_file->filename, *cwd;
00332 
00333               PHAR_G(cwd_init) = 1;
00334               if ((idata->phar->is_tar || idata->phar->is_zip) && idata->internal_file->filename_len == sizeof(".phar/stub.php")-1 && !strncmp(idata->internal_file->filename, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
00335                      /* we're executing the stub, which doesn't count as a file */
00336                      PHAR_G(cwd_init) = 0;
00337               } else if ((cwd = strrchr(entry, '/'))) {
00338                      PHAR_G(cwd_len) = cwd - entry;
00339                      PHAR_G(cwd) = estrndup(entry, PHAR_G(cwd_len));
00340               } else {
00341                      /* root directory */
00342                      PHAR_G(cwd_len) = 0;
00343                      PHAR_G(cwd) = NULL;
00344               }
00345        }
00346        if (opened_path) {
00347               spprintf(opened_path, MAXPATHLEN, "phar://%s/%s", idata->phar->fname, idata->internal_file->filename);
00348        }
00349        efree(internal_file);
00350 phar_stub:
00351        fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
00352        return fpf;
00353 }
00354 /* }}} */
00355 
00359 static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
00360 {
00361        phar_entry_delref((phar_entry_data *)stream->abstract TSRMLS_CC);
00362 
00363        return 0;
00364 }
00365 /* }}} */
00366 
00370 static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
00371 {
00372        phar_entry_data *data = (phar_entry_data *)stream->abstract;
00373        size_t got;
00374        phar_entry_info *entry;
00375 
00376        if (data->internal_file->link) {
00377               entry = phar_get_link_source(data->internal_file TSRMLS_CC);
00378        } else {
00379               entry = data->internal_file;
00380        }
00381 
00382        if (entry->is_deleted) {
00383               stream->eof = 1;
00384               return 0;
00385        }
00386 
00387        /* use our proxy position */
00388        php_stream_seek(data->fp, data->position + data->zero, SEEK_SET);
00389 
00390        got = php_stream_read(data->fp, buf, MIN(count, entry->uncompressed_filesize - data->position));
00391        data->position = php_stream_tell(data->fp) - data->zero;
00392        stream->eof = (data->position == (off_t) entry->uncompressed_filesize);
00393 
00394        return got;
00395 }
00396 /* }}} */
00397 
00401 static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
00402 {
00403        phar_entry_data *data = (phar_entry_data *)stream->abstract;
00404        phar_entry_info *entry;
00405        int res;
00406        off_t temp;
00407 
00408        if (data->internal_file->link) {
00409               entry = phar_get_link_source(data->internal_file TSRMLS_CC);
00410        } else {
00411               entry = data->internal_file;
00412        }
00413 
00414        switch (whence) {
00415               case SEEK_END :
00416                      temp = data->zero + entry->uncompressed_filesize + offset;
00417                      break;
00418               case SEEK_CUR :
00419                      temp = data->zero + data->position + offset;
00420                      break;
00421               case SEEK_SET :
00422                      temp = data->zero + offset;
00423                      break;
00424               default :
00425                      temp = 0;
00426        }
00427        if (temp > data->zero + (off_t) entry->uncompressed_filesize) {
00428               *newoffset = -1;
00429               return -1;
00430        }
00431        if (temp < data->zero) {
00432               *newoffset = -1;
00433               return -1;
00434        }
00435        res = php_stream_seek(data->fp, temp, SEEK_SET);
00436        *newoffset = php_stream_tell(data->fp) - data->zero;
00437        data->position = *newoffset;
00438        return res;
00439 }
00440 /* }}} */
00441 
00445 static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
00446 {
00447        phar_entry_data *data = (phar_entry_data *) stream->abstract;
00448 
00449        php_stream_seek(data->fp, data->position, SEEK_SET);
00450        if (count != php_stream_write(data->fp, buf, count)) {
00451               php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: Could not write %d characters to \"%s\" in phar \"%s\"", (int) count, data->internal_file->filename, data->phar->fname);
00452               return -1;
00453        }
00454        data->position = php_stream_tell(data->fp);
00455        if (data->position > (off_t)data->internal_file->uncompressed_filesize) {
00456               data->internal_file->uncompressed_filesize = data->position;
00457        }
00458        data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize;
00459        data->internal_file->old_flags = data->internal_file->flags;
00460        data->internal_file->is_modified = 1;
00461        return count;
00462 }
00463 /* }}} */
00464 
00468 static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */
00469 {
00470        char *error;
00471        int ret;
00472        if (stream->mode[0] == 'w' || (stream->mode[0] == 'r' && stream->mode[1] == '+')) {
00473               ret = phar_flush(((phar_entry_data *)stream->abstract)->phar, 0, 0, 0, &error TSRMLS_CC);
00474               if (error) {
00475                      php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS TSRMLS_CC, "%s", error);
00476                      efree(error);
00477               }
00478               return ret;
00479        } else {
00480               return EOF;
00481        }
00482 }
00483 /* }}} */
00484 
00485  /* {{{ phar_dostat */
00489 void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, zend_bool is_temp_dir TSRMLS_DC)
00490 {
00491        memset(ssb, 0, sizeof(php_stream_statbuf));
00492 
00493        if (!is_temp_dir && !data->is_dir) {
00494               ssb->sb.st_size = data->uncompressed_filesize;
00495               ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
00496               ssb->sb.st_mode |= S_IFREG; /* regular file */
00497               /* timestamp is just the timestamp when this was added to the phar */
00498 #ifdef NETWARE
00499               ssb->sb.st_mtime.tv_sec = data->timestamp;
00500               ssb->sb.st_atime.tv_sec = data->timestamp;
00501               ssb->sb.st_ctime.tv_sec = data->timestamp;
00502 #else
00503               ssb->sb.st_mtime = data->timestamp;
00504               ssb->sb.st_atime = data->timestamp;
00505               ssb->sb.st_ctime = data->timestamp;
00506 #endif
00507        } else if (!is_temp_dir && data->is_dir) {
00508               ssb->sb.st_size = 0;
00509               ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
00510               ssb->sb.st_mode |= S_IFDIR; /* regular directory */
00511               /* timestamp is just the timestamp when this was added to the phar */
00512 #ifdef NETWARE
00513               ssb->sb.st_mtime.tv_sec = data->timestamp;
00514               ssb->sb.st_atime.tv_sec = data->timestamp;
00515               ssb->sb.st_ctime.tv_sec = data->timestamp;
00516 #else
00517               ssb->sb.st_mtime = data->timestamp;
00518               ssb->sb.st_atime = data->timestamp;
00519               ssb->sb.st_ctime = data->timestamp;
00520 #endif
00521        } else {
00522               ssb->sb.st_size = 0;
00523               ssb->sb.st_mode = 0777;
00524               ssb->sb.st_mode |= S_IFDIR; /* regular directory */
00525 #ifdef NETWARE
00526               ssb->sb.st_mtime.tv_sec = phar->max_timestamp;
00527               ssb->sb.st_atime.tv_sec = phar->max_timestamp;
00528               ssb->sb.st_ctime.tv_sec = phar->max_timestamp;
00529 #else
00530               ssb->sb.st_mtime = phar->max_timestamp;
00531               ssb->sb.st_atime = phar->max_timestamp;
00532               ssb->sb.st_ctime = phar->max_timestamp;
00533 #endif
00534        }
00535        if (!phar->is_writeable) {
00536               ssb->sb.st_mode = (ssb->sb.st_mode & 0555) | (ssb->sb.st_mode & ~0777);
00537        }
00538 
00539        ssb->sb.st_nlink = 1;
00540        ssb->sb.st_rdev = -1;
00541        /* this is only for APC, so use /dev/null device - no chance of conflict there! */
00542        ssb->sb.st_dev = 0xc;
00543        /* generate unique inode number for alias/filename, so no phars will conflict */
00544        if (!is_temp_dir) {
00545               ssb->sb.st_ino = data->inode;
00546        }
00547 #ifndef PHP_WIN32
00548        ssb->sb.st_blksize = -1;
00549        ssb->sb.st_blocks = -1;
00550 #endif
00551 }
00552 /* }}}*/
00553 
00557 static int phar_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
00558 {
00559        phar_entry_data *data = (phar_entry_data *)stream->abstract;
00560 
00561        /* If ssb is NULL then someone is misbehaving */
00562        if (!ssb) {
00563               return -1;
00564        }
00565 
00566        phar_dostat(data->phar, data->internal_file, ssb, 0 TSRMLS_CC);
00567        return 0;
00568 }
00569 /* }}} */
00570 
00574 static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags,
00575                               php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) /* {{{ */
00576 {
00577        php_url *resource = NULL;
00578        char *internal_file, *error;
00579        phar_archive_data *phar;
00580        phar_entry_info *entry;
00581        uint host_len;
00582        int internal_file_len;
00583 
00584        if ((resource = phar_parse_url(wrapper, url, "r", flags|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
00585               return FAILURE;
00586        }
00587 
00588        /* we must have at the very least phar://alias.phar/internalfile.php */
00589        if (!resource->scheme || !resource->host || !resource->path) {
00590               php_url_free(resource);
00591               return FAILURE;
00592        }
00593 
00594        if (strcasecmp("phar", resource->scheme)) {
00595               php_url_free(resource);
00596               return FAILURE;
00597        }
00598 
00599        host_len = strlen(resource->host);
00600        phar_request_initialize(TSRMLS_C);
00601 
00602        internal_file = resource->path + 1; /* strip leading "/" */
00603        /* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */
00604        if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
00605               php_url_free(resource);
00606               if (error) {
00607                      efree(error);
00608               }
00609               return FAILURE;
00610        }
00611        if (error) {
00612               efree(error);
00613        }
00614        if (*internal_file == '\0') {
00615               /* root directory requested */
00616               phar_dostat(phar, NULL, ssb, 1 TSRMLS_CC);
00617               php_url_free(resource);
00618               return SUCCESS;
00619        }
00620        if (!phar->manifest.arBuckets) {
00621               php_url_free(resource);
00622               return FAILURE;
00623        }
00624        internal_file_len = strlen(internal_file);
00625        /* search through the manifest of files, and if we have an exact match, it's a file */
00626        if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) {
00627               phar_dostat(phar, entry, ssb, 0 TSRMLS_CC);
00628               php_url_free(resource);
00629               return SUCCESS;
00630        }
00631        if (zend_hash_exists(&(phar->virtual_dirs), internal_file, internal_file_len)) {
00632               phar_dostat(phar, NULL, ssb, 1 TSRMLS_CC);
00633               php_url_free(resource);
00634               return SUCCESS;
00635        }
00636        /* check for mounted directories */
00637        if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) {
00638               phar_zstr key;
00639               char *str_key;
00640               ulong unused;
00641               uint keylen;
00642               HashPosition pos;
00643 
00644               zend_hash_internal_pointer_reset_ex(&phar->mounted_dirs, &pos);
00645               while (FAILURE != zend_hash_has_more_elements_ex(&phar->mounted_dirs, &pos)) {
00646                      if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &keylen, &unused, 0, &pos)) {
00647                             break;
00648                      }
00649                      PHAR_STR(key, str_key);
00650                      if ((int)keylen >= internal_file_len || strncmp(str_key, internal_file, keylen)) {
00651                             zend_hash_move_forward_ex(&phar->mounted_dirs, &pos);
00652                             PHAR_STR_FREE(str_key);
00653                             continue;
00654                      } else {
00655                             char *test;
00656                             int test_len;
00657                             php_stream_statbuf ssbi;
00658 
00659                             if (SUCCESS != zend_hash_find(&phar->manifest, str_key, keylen, (void **) &entry)) {
00660                                    PHAR_STR_FREE(str_key);
00661                                    goto free_resource;
00662                             }
00663                             PHAR_STR_FREE(str_key);
00664                             if (!entry->tmp || !entry->is_mounted) {
00665                                    goto free_resource;
00666                             }
00667                             test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, internal_file + keylen);
00668                             if (SUCCESS != php_stream_stat_path(test, &ssbi)) {
00669                                    efree(test);
00670                                    zend_hash_move_forward_ex(&phar->mounted_dirs, &pos);
00671                                    continue;
00672                             }
00673                             /* mount the file/directory just in time */
00674                             if (SUCCESS != phar_mount_entry(phar, test, test_len, internal_file, internal_file_len TSRMLS_CC)) {
00675                                    efree(test);
00676                                    goto free_resource;
00677                             }
00678                             efree(test);
00679                             if (SUCCESS != zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) {
00680                                    goto free_resource;
00681                             }
00682                             phar_dostat(phar, entry, ssb, 0 TSRMLS_CC);
00683                             php_url_free(resource);
00684                             return SUCCESS;
00685                      }
00686               }
00687        }
00688 free_resource:
00689        php_url_free(resource);
00690        return FAILURE;
00691 }
00692 /* }}} */
00693 
00697 static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
00698 {
00699        php_url *resource;
00700        char *internal_file, *error;
00701        int internal_file_len;
00702        phar_entry_data *idata;
00703        phar_archive_data **pphar;
00704        uint host_len;
00705 
00706        if ((resource = phar_parse_url(wrapper, url, "rb", options TSRMLS_CC)) == NULL) {
00707               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: unlink failed");
00708               return 0;
00709        }
00710 
00711        /* we must have at the very least phar://alias.phar/internalfile.php */
00712        if (!resource->scheme || !resource->host || !resource->path) {
00713               php_url_free(resource);
00714               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url);
00715               return 0;
00716        }
00717 
00718        if (strcasecmp("phar", resource->scheme)) {
00719               php_url_free(resource);
00720               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url);
00721               return 0;
00722        }
00723 
00724        host_len = strlen(resource->host);
00725        phar_request_initialize(TSRMLS_C);
00726 
00727        if (FAILURE == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), resource->host, host_len, (void **) &pphar)) {
00728               pphar = NULL;
00729        }
00730        if (PHAR_G(readonly) && (!pphar || !(*pphar)->is_data)) {
00731               php_url_free(resource);
00732               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by the php.ini setting phar.readonly");
00733               return 0;
00734        }
00735 
00736        /* need to copy to strip leading "/", will get touched again */
00737        internal_file = estrdup(resource->path + 1);
00738        internal_file_len = strlen(internal_file);
00739        if (FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, internal_file_len, "r", 0, &error, 1 TSRMLS_CC)) {
00740               /* constraints of fp refcount were not met */
00741               if (error) {
00742                      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "unlink of \"%s\" failed: %s", url, error);
00743                      efree(error);
00744               } else {
00745                      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "unlink of \"%s\" failed, file does not exist", url);
00746               }
00747               efree(internal_file);
00748               php_url_free(resource);
00749               return 0;
00750        }
00751        if (error) {
00752               efree(error);
00753        }
00754        if (idata->internal_file->fp_refcount > 1) {
00755               /* more than just our fp resource is open for this file */
00756               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, resource->host);
00757               efree(internal_file);
00758               php_url_free(resource);
00759               phar_entry_delref(idata TSRMLS_CC);
00760               return 0;
00761        }
00762        php_url_free(resource);
00763        efree(internal_file);
00764        phar_entry_remove(idata, &error TSRMLS_CC);
00765        if (error) {
00766               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", error);
00767               efree(error);
00768        }
00769        return 1;
00770 }
00771 /* }}} */
00772 
00773 static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
00774 {
00775        php_url *resource_from, *resource_to;
00776        char *error;
00777        phar_archive_data *phar, *pfrom, *pto;
00778        phar_entry_info *entry;
00779        uint host_len;
00780        int is_dir = 0;
00781        int is_modified = 0;
00782 
00783        error = NULL;
00784 
00785        if ((resource_from = phar_parse_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
00786               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from);
00787               return 0;
00788        }
00789        if (SUCCESS != phar_get_archive(&pfrom, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) {
00790               pfrom = NULL;
00791               if (error) {
00792                      efree(error);
00793               }
00794        }
00795        if (PHAR_G(readonly) && (!pfrom || !pfrom->is_data)) {
00796               php_url_free(resource_from);
00797               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly");
00798               return 0;
00799        }
00800 
00801        if ((resource_to = phar_parse_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
00802               php_url_free(resource_from);
00803               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_to);
00804               return 0;
00805        }
00806        if (SUCCESS != phar_get_archive(&pto, resource_to->host, strlen(resource_to->host), NULL, 0, &error TSRMLS_CC)) {
00807               if (error) {
00808                      efree(error);
00809               }
00810               pto = NULL;
00811        }
00812        if (PHAR_G(readonly) && (!pto || !pto->is_data)) {
00813               php_url_free(resource_from);
00814               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly");
00815               return 0;
00816        }
00817 
00818        if (strcmp(resource_from->host, resource_to->host)) {
00819               php_url_free(resource_from);
00820               php_url_free(resource_to);
00821               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to);
00822               return 0;
00823        }
00824 
00825        /* we must have at the very least phar://alias.phar/internalfile.php */
00826        if (!resource_from->scheme || !resource_from->host || !resource_from->path) {
00827               php_url_free(resource_from);
00828               php_url_free(resource_to);
00829               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from);
00830               return 0;
00831        }
00832 
00833        if (!resource_to->scheme || !resource_to->host || !resource_to->path) {
00834               php_url_free(resource_from);
00835               php_url_free(resource_to);
00836               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_to);
00837               return 0;
00838        }
00839 
00840        if (strcasecmp("phar", resource_from->scheme)) {
00841               php_url_free(resource_from);
00842               php_url_free(resource_to);
00843               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_from);
00844               return 0;
00845        }
00846 
00847        if (strcasecmp("phar", resource_to->scheme)) {
00848               php_url_free(resource_from);
00849               php_url_free(resource_to);
00850               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_to);
00851               return 0;
00852        }
00853 
00854        host_len = strlen(resource_from->host);
00855 
00856        if (SUCCESS != phar_get_archive(&phar, resource_from->host, host_len, NULL, 0, &error TSRMLS_CC)) {
00857               php_url_free(resource_from);
00858               php_url_free(resource_to);
00859               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
00860               efree(error);
00861               return 0;
00862        }
00863 
00864        if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
00865               php_url_free(resource_from);
00866               php_url_free(resource_to);
00867               php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", url_from, url_to);
00868               return 0;
00869        }
00870 
00871        if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) {
00872               phar_entry_info new, *source;
00873 
00874               /* perform rename magic */
00875               if (entry->is_deleted) {
00876                      php_url_free(resource_from);
00877                      php_url_free(resource_to);
00878                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to);
00879                      return 0;
00880               }
00881               /* transfer all data over to the new entry */
00882               memcpy((void *) &new, (void *) entry, sizeof(phar_entry_info));
00883               /* mark the old one for deletion */
00884               entry->is_deleted = 1;
00885               entry->fp = NULL;
00886               entry->metadata = 0;
00887               entry->link = entry->tmp = NULL;
00888               source = entry;
00889 
00890               /* add to the manifest, and then store the pointer to the new guy in entry */
00891               zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry);
00892 
00893               entry->filename = estrdup(resource_to->path+1);
00894               if (FAILURE == phar_copy_entry_fp(source, entry, &error TSRMLS_CC)) {
00895                      php_url_free(resource_from);
00896                      php_url_free(resource_to);
00897                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
00898                      efree(error);
00899                      zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename));
00900                      return 0;
00901               }
00902               is_modified = 1;
00903               entry->is_modified = 1;
00904               entry->filename_len = strlen(entry->filename);
00905               is_dir = entry->is_dir;
00906        } else {
00907               is_dir = zend_hash_exists(&(phar->virtual_dirs), resource_from->path+1, strlen(resource_from->path)-1);
00908               if (!is_dir) {
00909                      /* file does not exist */
00910                      php_url_free(resource_from);
00911                      php_url_free(resource_to);
00912                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", url_from, url_to);
00913                      return 0;
00914 
00915               }
00916        }
00917 
00918        /* Rename directory. Update all nested paths */
00919        if (is_dir) {
00920               int key_type;
00921               phar_zstr key, new_key;
00922               char *str_key, *new_str_key;
00923               uint key_len, new_key_len;
00924               ulong unused;
00925               uint from_len = strlen(resource_from->path+1);
00926               uint to_len = strlen(resource_to->path+1);
00927 
00928               for (zend_hash_internal_pointer_reset(&phar->manifest);
00929                      HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)) &&
00930                      SUCCESS == zend_hash_get_current_data(&phar->manifest, (void **) &entry);
00931                      zend_hash_move_forward(&phar->manifest)) {
00932 
00933                      PHAR_STR(key, str_key);
00934 
00935                      if (!entry->is_deleted &&
00936                             key_len > from_len &&
00937                             memcmp(str_key, resource_from->path+1, from_len) == 0 &&
00938                             IS_SLASH(str_key[from_len])) {
00939 
00940                             new_key_len = key_len + to_len - from_len;
00941                             new_str_key = emalloc(new_key_len+1);
00942                             memcpy(new_str_key, resource_to->path + 1, to_len);
00943                             memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
00944                             new_str_key[new_key_len] = 0;
00945 
00946                             is_modified = 1;
00947                             entry->is_modified = 1;
00948                             efree(entry->filename);
00949                             entry->filename = new_str_key;
00950                             entry->filename_len = new_key_len;
00951 
00952                             PHAR_ZSTR(new_str_key, new_key);
00953 #if PHP_VERSION_ID < 50300
00954                             zend_hash_update_current_key_ex(&phar->manifest, key_type, new_key, new_key_len, 0, NULL);
00955 #else
00956                             zend_hash_update_current_key_ex(&phar->manifest, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
00957 #endif
00958                      }
00959                      PHAR_STR_FREE(str_key);
00960               }
00961 
00962               for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
00963                      HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL));
00964                      zend_hash_move_forward(&phar->virtual_dirs)) {
00965 
00966                      PHAR_STR(key, str_key);
00967 
00968                      if (key_len >= from_len &&
00969                             memcmp(str_key, resource_from->path+1, from_len) == 0 &&
00970                             (key_len == from_len || IS_SLASH(str_key[from_len]))) {
00971 
00972                             new_key_len = key_len + to_len - from_len;
00973                             new_str_key = emalloc(new_key_len+1);
00974                             memcpy(new_str_key, resource_to->path + 1, to_len);
00975                             memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
00976                             new_str_key[new_key_len] = 0;
00977 
00978                             PHAR_ZSTR(new_str_key, new_key);
00979 #if PHP_VERSION_ID < 50300
00980                             zend_hash_update_current_key_ex(&phar->virtual_dirs, key_type, new_key, new_key_len, 0, NULL);
00981 #else
00982                             zend_hash_update_current_key_ex(&phar->virtual_dirs, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
00983 #endif
00984                             efree(new_str_key);
00985                      }
00986                      PHAR_STR_FREE(str_key);
00987               }
00988 
00989               for (zend_hash_internal_pointer_reset(&phar->mounted_dirs);
00990                      HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &key_len, &unused, 0, NULL)) &&
00991                      SUCCESS == zend_hash_get_current_data(&phar->mounted_dirs, (void **) &entry);
00992                      zend_hash_move_forward(&phar->mounted_dirs)) {
00993 
00994                      PHAR_STR(key, str_key);
00995 
00996                      if (key_len >= from_len &&
00997                             memcmp(str_key, resource_from->path+1, from_len) == 0 &&
00998                             (key_len == from_len || IS_SLASH(str_key[from_len]))) {
00999 
01000                             new_key_len = key_len + to_len - from_len;
01001                             new_str_key = emalloc(new_key_len+1);
01002                             memcpy(new_str_key, resource_to->path + 1, to_len);
01003                             memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
01004                             new_str_key[new_key_len] = 0;
01005 
01006                             PHAR_ZSTR(new_str_key, new_key);
01007 #if PHP_VERSION_ID < 50300
01008                             zend_hash_update_current_key_ex(&phar->mounted_dirs, key_type, new_key, new_key_len, 0, NULL);
01009 #else
01010                             zend_hash_update_current_key_ex(&phar->mounted_dirs, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
01011 #endif
01012                             efree(new_str_key);
01013                      }
01014                      PHAR_STR_FREE(str_key);
01015               }
01016        }
01017 
01018        if (is_modified) {
01019               phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
01020               if (error) {
01021                      php_url_free(resource_from);
01022                      php_url_free(resource_to);
01023                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
01024                      efree(error);
01025                      return 0;
01026               }
01027        }
01028 
01029        php_url_free(resource_from);
01030        php_url_free(resource_to);
01031 
01032        return 1;
01033 }
01034 /* }}} */
01035 
01036 /*
01037  * Local variables:
01038  * tab-width: 4
01039  * c-basic-offset: 4
01040  * End:
01041  * vim600: noet sw=4 ts=4 fdm=marker
01042  * vim<600: noet sw=4 ts=4
01043  */