diff options
author | Remi Collet <remi@remirepo.net> | 2023-10-12 12:48:34 +0200 |
---|---|---|
committer | Remi Collet <remi@php.net> | 2023-10-12 12:48:34 +0200 |
commit | 9104d986ab93577575eab6cc7c750b1adb84b46a (patch) | |
tree | 9fba62d46982147576c44989ee802d60431f34e2 | |
parent | 858524052cd950ffe79d40464df8a83cedf19776 (diff) |
implement rpm stream wrapper
-rw-r--r-- | package.xml | 9 | ||||
-rw-r--r-- | php_rpminfo.h | 2 | ||||
-rw-r--r-- | rpminfo.c | 179 | ||||
-rw-r--r-- | tests/014-stream.phpt | 34 |
4 files changed, 218 insertions, 6 deletions
diff --git a/package.xml b/package.xml index 20ec125..18e098f 100644 --- a/package.xml +++ b/package.xml @@ -13,10 +13,10 @@ Documentation: https://www.php.net/rpminfo <email>remi@php.net</email> <active>yes</active> </lead> - <date>2023-09-26</date> + <date>2023-10-12</date> <version> - <release>0.7.1dev</release> - <api>0.7.0</api> + <release>0.8.0dev</release> + <api>0.0.0</api> </version> <stability> <release>stable</release> @@ -24,7 +24,7 @@ Documentation: https://www.php.net/rpminfo </stability> <license uri="https://www.php.net/license/3_01.txt" filesource="LICENSE">PHP-3.01</license> <notes> -- +- implement rpm stream wrapper </notes> <contents> <dir name="/"> @@ -56,6 +56,7 @@ Documentation: https://www.php.net/rpminfo <file name="011-rpmvercmp_error8.phpt" role="test"/> <file name="012-rpmaddtag.phpt" role="test"/> <file name="013-rpmdbsearch-error.phpt" role="test"/> + <file name="014-stream.phpt" role="test"/> <file name="bidon.rpm" role="test"/> <file name="bidon-src.rpm" role="test"/> </dir> diff --git a/php_rpminfo.h b/php_rpminfo.h index 2ba4727..7df010e 100644 --- a/php_rpminfo.h +++ b/php_rpminfo.h @@ -22,7 +22,7 @@ extern zend_module_entry rpminfo_module_entry; #define phpext_rpminfo_ptr &rpminfo_module_entry -#define PHP_RPMINFO_VERSION "0.7.1-dev" +#define PHP_RPMINFO_VERSION "0.8.0-dev" #ifdef PHP_WIN32 # define PHP_RPMINFO_API __declspec(dllexport) @@ -23,6 +23,7 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/php_string.h" #include <fcntl.h> #include <rpm/rpmdb.h> @@ -34,6 +35,17 @@ #include "rpminfo_arginfo.h" +struct php_rpm_stream_data_t { + FD_t gzdi; + Header h; + rpmfiles files; + rpmfi fi; + php_stream *stream; +}; + +#define STREAM_DATA_FROM_STREAM() \ + struct php_rpm_stream_data_t *self = (struct php_rpm_stream_data_t *) stream->abstract; + ZEND_DECLARE_MODULE_GLOBALS(rpminfo) static rpmts rpminfo_getts(void) { @@ -302,7 +314,7 @@ static unsigned char nibble(char c) { if (c >= 'A' && c <= 'F') { return (c - 'A') + 10; } - return 0; + return 0; } static int hex2bin(const char *hex, char *bin, int len) { @@ -561,6 +573,169 @@ PHP_FUNCTION(rpmaddtag) } /* }}} */ +static ssize_t php_rpm_ops_read(php_stream *stream, char *buf, size_t count) +{ + ssize_t n = -1; + STREAM_DATA_FROM_STREAM(); + + if (self) { + n = rpmfiArchiveRead(self->fi, buf, count); + if (n == 0 || n < (ssize_t)count) { + stream->eof = 1; + } + } + return n; +} + +static int php_rpm_ops_close(php_stream *stream, int close_handle) +{ + STREAM_DATA_FROM_STREAM(); + + if (self) { + if (close_handle) { + Fclose(self->gzdi); + rpmfilesFree(self->files); + rpmfiFree(self->fi); + headerFree(self->h); + } + efree(self); + } + stream->abstract = NULL; + + return EOF; +} + + +const php_stream_ops php_stream_rpmio_ops = { + NULL, /* write */ + php_rpm_ops_read, + php_rpm_ops_close, + NULL, /* flush */ + "rpm", + NULL, /* seek */ + NULL, /* cast */ + NULL, /* stat */ + NULL /* set_option */ +}; + +php_stream *php_stream_rpm_opener(php_stream_wrapper *wrapper, + const char *path, + const char *mode, + int options, + zend_string **opened_path, + php_stream_context *context STREAMS_DC) +{ + size_t path_len; + zend_string *file_basename; + char file_dirname[MAXPATHLEN]; + char *fragment; + size_t fragment_len; + php_stream *stream = NULL; + struct php_rpm_stream_data_t *self; + FD_t fdi; + FD_t gzdi; + int rc; + Header h; + char rpmio_flags[80]; + const char *compr; + rpmfiles files; + rpmfi fi; + rpmts ts = rpminfo_getts(); + + fragment = strchr(path, '#'); + if (!fragment) { + return NULL; + } + if (strncasecmp("rpm://", path, 6) == 0) { + path += 6; + } + fragment_len = strlen(fragment); + if (fragment_len < 1) { + return NULL; + } + path_len = strlen(path); + if (path_len >= MAXPATHLEN || mode[0] != 'r') { + return NULL; + } + memcpy(file_dirname, path, path_len - fragment_len); + file_dirname[path_len - fragment_len] = '\0'; + file_basename = php_basename(path, path_len - fragment_len, NULL, 0); + fragment++; + if (php_check_open_basedir(file_dirname)) { + zend_string_release_ex(file_basename, 0); + return NULL; + } + fdi = Fopen(file_dirname, "r.ufdio"); + if (Ferror(fdi)) { + zend_string_release_ex(file_basename, 0); + return NULL; + } + rc = rpmReadPackageFile(ts, fdi, "rpm2cpio", &h); + if (rc != RPMRC_OK && rc != RPMRC_NOKEY && rc != RPMRC_NOTTRUSTED) { + zend_string_release_ex(file_basename, 0); + return NULL; + } + + compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); + snprintf(rpmio_flags, sizeof(rpmio_flags), "r.%s", compr ? compr : "gzip"); + gzdi = Fdopen(fdi, rpmio_flags); + if (gzdi == NULL) { + zend_string_release_ex(file_basename, 0); + return NULL; + } + + files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER); + fi = rpmfiNewArchiveReader(gzdi, files, RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST); + + while((rc = rpmfiNext(fi)) >=0) { + const char *fn = rpmfiFN(fi); + /* + printf("Name=%s, Size=%d, N=%d, mode=%d, reg=%d, content=%d\n", fn, + (int)rpmfiFSize(fi), (int)rpmfiFNlink(fi), (int)rpmfiFMode(fi), + (int)S_ISREG(rpmfiFMode(fi)), (int)rpmfiArchiveHasContent(fi)); + */ + if (!strcmp(fn, fragment)) { + break; + } + } + if (rc == RPMERR_ITER_END || !S_ISREG(rpmfiFMode(fi)) || !rpmfiArchiveHasContent(fi)) { + Fclose(gzdi); + rpmfilesFree(files); + rpmfiFree(fi); + headerFree(h); + } else { + self = emalloc(sizeof(*self)); + self->gzdi = gzdi; + self->files = files; + self->fi = fi; + self->h = h; + + stream = php_stream_alloc(&php_stream_rpmio_ops, self, NULL, mode); + } + zend_string_release_ex(file_basename, 0); + + return stream; +} + +static const php_stream_wrapper_ops rpm_stream_wops = { + php_stream_rpm_opener, + NULL, /* close */ + NULL, /* fstat */ + NULL, /* stat */ + NULL, /* opendir */ + "RPM wrapper", + NULL, /* unlink */ + NULL, /* rename */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL /* metadata */ +}; + +const php_stream_wrapper php_stream_rpm_wrapper = { + &rpm_stream_wops, + NULL, + 0 /* is_url */ +}; /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(rpminfo) @@ -606,6 +781,8 @@ PHP_MINIT_FUNCTION(rpminfo) } rpmtdFree(names); + php_register_url_stream_wrapper("rpm", &php_stream_rpm_wrapper); + return SUCCESS; } /* }}} */ diff --git a/tests/014-stream.phpt b/tests/014-stream.phpt new file mode 100644 index 0000000..5a4fefa --- /dev/null +++ b/tests/014-stream.phpt @@ -0,0 +1,34 @@ +--TEST-- +Check for stream +--SKIPIF-- +<?php if (!extension_loaded("rpminfo")) print "skip"; ?> +--FILE-- +<?php +$n = "rpm://" . __DIR__ . "/bidon.rpm#/usr/share/doc/bidon/README"; + +var_dump(in_array('rpm', stream_get_wrappers())); + +var_dump($f = fopen($n, "r")); +var_dump(trim(fread($f, 10))); +var_dump(feof($f)); +var_dump(trim(fread($f, 100))); +var_dump(feof($f)); +fclose($f); + +var_dump(trim(file_get_contents($n))); + +var_dump(file_get_contents(str_replace('README', 'TODO', $n))); +?> +Done +--EXPECTF-- +bool(true) +resource(%d) of type (stream) +string(10) "Mon Feb 12" +bool(false) +string(17) "13:27:47 CET 2018" +bool(true) +string(28) "Mon Feb 12 13:27:47 CET 2018" + +Warning: file_get_contents(%s/bidon.rpm#/usr/share/doc/bidon/TODO): Failed to open stream: operation failed in %s on line %d +bool(false) +Done |