From abfc228f36db9a98df6795ba5eeeef143006c828 Mon Sep 17 00:00:00 2001 From: Greg Beaver Date: Wed, 9 Apr 2008 19:23:31 +0000 Subject: [PATCH] add addFile/addFromString/addEmptyDir. API is identical to ext/zip [DOC] --- ext/phar/dirstream.c | 16 ++- ext/phar/phar_object.c | 193 +++++++++++++++++++++++++++++------ ext/phar/tests/addfuncs.phpt | 36 +++++++ ext/phar/tests/dir.phpt | 2 +- ext/phar/tests/tar/033.phpt | 2 +- ext/phar/tests/tar/033a.phpt | 2 +- ext/phar/tests/tar/dir.phpt | 2 +- ext/phar/tests/zip/033.phpt | 2 +- ext/phar/tests/zip/033a.phpt | 2 +- ext/phar/tests/zip/dir.phpt | 2 +- ext/phar/util.c | 12 ++- 11 files changed, 223 insertions(+), 48 deletions(-) create mode 100644 ext/phar/tests/addfuncs.phpt diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 14f13f9c6a..ce398d0bc8 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -471,7 +471,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in return FAILURE; } - if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 1, &error TSRMLS_CC))) { + if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 2, &error TSRMLS_CC))) { /* directory exists, or is a subdirectory of an existing file */ efree(e->filename); efree(e); @@ -485,6 +485,18 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in php_url_free(resource); return FAILURE; } + if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 0, &error TSRMLS_CC))) { + /* entry exists as a file */ + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", resource->path+1, resource->host); + php_url_free(resource); + return FAILURE; + } + if (error) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error); + efree(error); + php_url_free(resource); + return FAILURE; + } memset((void *) &entry, 0, sizeof(phar_entry_info)); @@ -582,7 +594,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_ return FAILURE; } - if (!(entry = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 1, &error TSRMLS_CC))) { + if (!(entry = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 2, &error TSRMLS_CC))) { if (error) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error); efree(error); diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index bc7f7ec065..a1f7e2c270 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -2827,7 +2827,7 @@ PHP_METHOD(Phar, offsetGet) return; } - if (!phar_get_entry_info_dir(phar_obj->arc.archive, fname, fname_len, 2, &error TSRMLS_CC)) { + if (!phar_get_entry_info_dir(phar_obj->arc.archive, fname, fname_len, 1, &error TSRMLS_CC)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist%s%s", fname, error?", ":"", error?error:""); } else { fname_len = spprintf(&fname, 0, "phar://%s/%s", phar_obj->arc.archive->fname, fname); @@ -2840,44 +2840,21 @@ PHP_METHOD(Phar, offsetGet) } /* }}} */ -/* {{{ proto int Phar::offsetSet(string entry, string value) - * set the contents of an internal file to those of an external file +/* {{{ add a file within the phar archive from a string or resource */ -PHP_METHOD(Phar, offsetSet) +static void phar_add_file(phar_archive_data *phar, char *filename, int filename_len, char *cont_str, int cont_len, zval *zresource TSRMLS_DC) { - char *fname, *cont_str = NULL, *error; - int fname_len, cont_len; - zval *zresource; + char *error; long contents_len; phar_entry_data *data; php_stream *contents_file; - PHAR_ARCHIVE_OBJECT(); - - if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by INI setting"); - return; - } - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sr", &fname, &fname_len, &zresource) == FAILURE - && zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &fname, &fname_len, &cont_str, &cont_len) == FAILURE) { - return; - } - if ((phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) && fname_len == sizeof(".phar/stub.php")-1 && !memcmp(fname, ".phar/stub.php", sizeof(".phar/stub.php")-1)) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot set stub \".phar/stub.php\" directly in phar \"%s\", use setStub", phar_obj->arc.archive->fname); - return; - } - - if ((phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) && fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot set alias \".phar/alias.txt\" directly in phar \"%s\", use setAlias", phar_obj->arc.archive->fname); - return; - } - if (!(data = phar_get_or_create_entry_data(phar_obj->arc.archive->fname, phar_obj->arc.archive->fname_len, fname, fname_len, "w+b", 2, &error TSRMLS_CC))) { + if (!(data = phar_get_or_create_entry_data(phar->fname, phar->fname_len, filename, filename_len, "w+b", 0, &error TSRMLS_CC))) { if (error) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist and cannot be created: %s", fname, error); + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist and cannot be created: %s", filename, error); efree(error); } else { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist and cannot be created", fname); + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s does not exist and cannot be created", filename); } return; } else { @@ -2888,12 +2865,12 @@ PHP_METHOD(Phar, offsetSet) if (cont_str) { contents_len = php_stream_write(data->fp, cont_str, cont_len); if (contents_len != cont_len) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s could not be written to", fname); + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s could not be written to", filename); return; } } else { if (!(php_stream_from_zval_no_verify(contents_file, &zresource))) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s could not be written to", fname); + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Entry %s could not be written to", filename); return; } contents_len = php_stream_copy_to_stream(contents_file, data->fp, PHP_STREAM_COPY_ALL); @@ -2901,7 +2878,7 @@ PHP_METHOD(Phar, offsetSet) data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize = contents_len; } phar_entry_delref(data TSRMLS_CC); - phar_flush(phar_obj->arc.archive, 0, 0, 0, &error TSRMLS_CC); + phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); if (error) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); efree(error); @@ -2910,6 +2887,69 @@ PHP_METHOD(Phar, offsetSet) } /* }}} */ +/* {{{ create a directory within the phar archive + */ +static void phar_mkdir(phar_archive_data *phar, char *dirname, int dirname_len TSRMLS_DC) +{ + char *error; + phar_entry_data *data; + + if (!(data = phar_get_or_create_entry_data(phar->fname, phar->fname_len, dirname, dirname_len, "w+b", 2, &error TSRMLS_CC))) { + if (error) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Directory %s does not exist and cannot be created: %s", dirname, error); + efree(error); + } else { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Directory %s does not exist and cannot be created", dirname); + } + return; + } else { + if (error) { + efree(error); + } + phar_entry_delref(data TSRMLS_CC); + phar_flush(phar, 0, 0, 0, &error TSRMLS_CC); + if (error) { + zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error); + efree(error); + } + } +} +/* }}} */ + + +/* {{{ proto int Phar::offsetSet(string entry, string value) + * set the contents of an internal file to those of an external file + */ +PHP_METHOD(Phar, offsetSet) +{ + char *fname, *cont_str = NULL; + int fname_len, cont_len; + zval *zresource; + PHAR_ARCHIVE_OBJECT(); + + if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by INI setting"); + return; + } + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sr", &fname, &fname_len, &zresource) == FAILURE + && zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &fname, &fname_len, &cont_str, &cont_len) == FAILURE) { + return; + } + + if ((phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) && fname_len == sizeof(".phar/stub.php")-1 && !memcmp(fname, ".phar/stub.php", sizeof(".phar/stub.php")-1)) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot set stub \".phar/stub.php\" directly in phar \"%s\", use setStub", phar_obj->arc.archive->fname); + return; + } + + if ((phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) && fname_len == sizeof(".phar/alias.txt")-1 && !memcmp(fname, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Cannot set alias \".phar/alias.txt\" directly in phar \"%s\", use setAlias", phar_obj->arc.archive->fname); + return; + } + phar_add_file(phar_obj->arc.archive, fname, fname_len, cont_str, cont_len, zresource); +} +/* }}} */ + /* {{{ proto int Phar::offsetUnset(string entry) * remove a file from a phar */ @@ -2924,7 +2964,7 @@ PHP_METHOD(Phar, offsetUnset) zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by INI setting"); return; } - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { return; } @@ -2951,6 +2991,71 @@ PHP_METHOD(Phar, offsetUnset) } /* }}} */ +/* {{{ proto string Phar::addEmptyDir(string dirname) + * Adds an empty directory to the phar archive + */ +PHP_METHOD(Phar, addEmptyDir) +{ + char *dirname; + int dirname_len; + PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &dirname, &dirname_len) == FAILURE) { + return; + } + + phar_mkdir(phar_obj->arc.archive, dirname, dirname_len); +} +/* }}} */ + +/* {{{ proto string Phar::addFile(string filename[, string localname]) + * Adds a file to the archive using the filename, or the second parameter as the name within the archive + */ +PHP_METHOD(Phar, addFile) +{ + char *fname, *localname = NULL; + int fname_len, localname_len; + php_stream *resource; + zval *zresource; + PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &fname, &fname_len, &localname, &localname_len) == FAILURE) { + return; + } + + if (!(resource = php_stream_open_wrapper(fname, "rb", 0, NULL))) { + zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "phar error: unable to open file \"%s\" to add to phar archive", fname); + return; + } + if (localname) { + fname = localname; + fname_len = localname_len; + } + + MAKE_STD_ZVAL(zresource); + php_stream_to_zval(resource, zresource); + phar_add_file(phar_obj->arc.archive, fname, fname_len, NULL, 0, zresource); + efree(zresource); +} +/* }}} */ + +/* {{{ proto string Phar::addFromString(string localname, string contents) + * Adds a file to the archive using its contents as a string + */ +PHP_METHOD(Phar, addFromString) +{ + char *localname, *cont_str; + int localname_len, cont_len; + PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &localname, &localname_len, &cont_str, &cont_len) == FAILURE) { + return; + } + + phar_add_file(phar_obj->arc.archive, localname, localname_len, cont_str, cont_len, NULL); +} +/* }}} */ + /* {{{ proto string Phar::getStub() * Returns the stub at the head of a phar archive as a string. */ @@ -3772,6 +3877,23 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_running, 0, 0, 1) ZEND_ARG_INFO(0, retphar) ZEND_END_ARG_INFO(); +static +ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_emptydir, 0, 0, 0) + ZEND_ARG_INFO(0, dirname) +ZEND_END_ARG_INFO(); + +static +ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_addfile, 0, 0, 1) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, localname) +ZEND_END_ARG_INFO(); + +static +ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_fromstring, 0, 0, 1) + ZEND_ARG_INFO(0, localname) + ZEND_ARG_INFO(0, contents) +ZEND_END_ARG_INFO(); + #endif /* HAVE_SPL */ zend_function_entry php_archive_methods[] = { @@ -3779,6 +3901,9 @@ zend_function_entry php_archive_methods[] = { PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PRIVATE) #else PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PUBLIC) + PHP_ME(Phar, addEmptyDir, arginfo_phar_emptydir, ZEND_ACC_PUBLIC) + PHP_ME(Phar, addFile, arginfo_phar_addfile, ZEND_ACC_PUBLIC) + PHP_ME(Phar, addFromString, arginfo_phar_fromstring, ZEND_ACC_PUBLIC) PHP_ME(Phar, buildFromIterator, arginfo_phar_build, ZEND_ACC_PUBLIC) PHP_ME(Phar, compress, arginfo_phar_comp, ZEND_ACC_PUBLIC) PHP_ME(Phar, compressAllFilesBZIP2, NULL, ZEND_ACC_PUBLIC) diff --git a/ext/phar/tests/addfuncs.phpt b/ext/phar/tests/addfuncs.phpt new file mode 100644 index 0000000000..9c320b55db --- /dev/null +++ b/ext/phar/tests/addfuncs.phpt @@ -0,0 +1,36 @@ +--TEST-- +Phar: addFile/addFromString +--SKIPIF-- + +--INI-- +phar.readonly=0 +--FILE-- +addFromString('a', 'hi'); +echo file_get_contents($pname . '/a') . "\n"; +$phar->addFile($pname . '/a', 'b'); +echo file_get_contents($pname . '/b') . "\n"; +try { +$phar->addFile($pname . '/a'); +} catch (Exception $e) { +echo $e->getMessage() . "\n"; +} +try { +$phar->addFile($pname . '/a', 'a'); +} catch (Exception $e) { +echo $e->getMessage() . "\n"; +} + +?> +===DONE=== +--CLEAN-- + +--EXPECTF-- +hi +hi +Entry phar://%saddfuncs.phar.php/a does not exist and cannot be created: phar error: invalid path "phar://%saddfuncs.phar.php/a" contains double slash +Entry a does not exist and cannot be created: phar error: file "a" in phar "%saddfuncs.phar.php" cannot be opened for writing, readable file pointers are open +===DONE=== \ No newline at end of file diff --git a/ext/phar/tests/dir.phpt b/ext/phar/tests/dir.phpt index 633f8d81fe..7071fcb5d8 100644 --- a/ext/phar/tests/dir.phpt +++ b/ext/phar/tests/dir.phpt @@ -16,7 +16,7 @@ $pname3 = 'phar://' . $fname3; $phar = new Phar($fname); var_dump($phar->isPhar()); -$phar['test/'] = ''; +$phar->addEmptyDir('test'); var_dump($phar['test']->isDir()); var_dump($phar['test/']->isDir()); copy($fname, $fname2); diff --git a/ext/phar/tests/tar/033.phpt b/ext/phar/tests/tar/033.phpt index 2f4e4b2a4f..29223e49e3 100644 --- a/ext/phar/tests/tar/033.phpt +++ b/ext/phar/tests/tar/033.phpt @@ -14,7 +14,7 @@ $alias = 'phar://hio'; $phar = new Phar($fname); $phar['a.php'] = ''; $phar->setAlias('hio'); -$phar['test/'] = ''; +$phar->addEmptyDir('test'); $phar->stopBuffering(); try { diff --git a/ext/phar/tests/tar/033a.phpt b/ext/phar/tests/tar/033a.phpt index 92c089faa9..09b5446aec 100644 --- a/ext/phar/tests/tar/033a.phpt +++ b/ext/phar/tests/tar/033a.phpt @@ -14,7 +14,7 @@ $alias = 'phar://hio'; $phar = new Phar($fname); $phar['a.php'] = ''; $phar->setAlias('hio'); -$phar['test/'] = ''; +$phar->addEmptyDir('test'); $phar->stopBuffering(); ini_set('phar.readonly', 1); diff --git a/ext/phar/tests/tar/dir.phpt b/ext/phar/tests/tar/dir.phpt index 554953d900..f04e5461f1 100644 --- a/ext/phar/tests/tar/dir.phpt +++ b/ext/phar/tests/tar/dir.phpt @@ -16,7 +16,7 @@ $pname3 = 'phar://' . $fname3; $phar = new Phar($fname); var_dump($phar->isTar()); -$phar['test/'] = ''; +$phar->addEmptyDir('test'); var_dump($phar['test']->isDir()); var_dump($phar['test/']->isDir()); copy($fname, $fname2); diff --git a/ext/phar/tests/zip/033.phpt b/ext/phar/tests/zip/033.phpt index 084afaa944..6cb2e8afd2 100644 --- a/ext/phar/tests/zip/033.phpt +++ b/ext/phar/tests/zip/033.phpt @@ -14,7 +14,7 @@ $alias = 'phar://hio'; $phar = new Phar($fname); $phar['a.php'] = ''; $phar->setAlias('hio'); -$phar['test/'] = ''; +$phar->addEmptyDir('test'); $phar->stopBuffering(); try { diff --git a/ext/phar/tests/zip/033a.phpt b/ext/phar/tests/zip/033a.phpt index af4b56a487..429d3bdd98 100644 --- a/ext/phar/tests/zip/033a.phpt +++ b/ext/phar/tests/zip/033a.phpt @@ -14,7 +14,7 @@ $alias = 'phar://hio'; $phar = new Phar($fname); $phar['a.php'] = ''; $phar->setAlias('hio'); -$phar['test/'] = ''; +$phar->addEmptyDir('test'); $phar->stopBuffering(); ini_set('phar.readonly', 1); diff --git a/ext/phar/tests/zip/dir.phpt b/ext/phar/tests/zip/dir.phpt index 889dfff656..210b9b816e 100644 --- a/ext/phar/tests/zip/dir.phpt +++ b/ext/phar/tests/zip/dir.phpt @@ -16,7 +16,7 @@ $pname3 = 'phar://' . $fname3; $phar = new Phar($fname); var_dump($phar->isZip()); -$phar['test/'] = ''; +$phar->addEmptyDir('test'); var_dump($phar['test']->isDir()); var_dump($phar['test/']->isDir()); copy($fname, $fname2); diff --git a/ext/phar/util.c b/ext/phar/util.c index 6c496650a8..0791bb0ebb 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -436,7 +436,7 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char return FAILURE; } if (allow_dir) { - if ((entry = phar_get_entry_info_dir(phar, path, path_len, 2, for_create && !PHAR_G(readonly) && !phar->is_data ? NULL : error TSRMLS_CC)) == NULL) { + if ((entry = phar_get_entry_info_dir(phar, path, path_len, allow_dir, for_create && !PHAR_G(readonly) && !phar->is_data ? NULL : error TSRMLS_CC)) == NULL) { if (for_create && (!PHAR_G(readonly) || phar->is_data)) { return SUCCESS; } @@ -564,11 +564,13 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char } etemp.fp_refcount = 1; - if (is_dir) { + if (allow_dir == 2) { etemp.is_dir = 1; etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_DIR; - etemp.filename_len--; /* strip trailing / */ - path_len--; + if (is_dir) { + etemp.filename_len--; /* strip trailing / */ + path_len--; + } } else { etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_FILE; } @@ -1015,7 +1017,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in } return NULL; } - if (!entry->is_dir && is_dir) { + if (!entry->is_dir && dir == 2) { /* user requested a directory, we must return one */ if (error) { spprintf(error, 4096, "phar error: path \"%s\" exists and is a not a directory", path); -- 2.40.0