diff options
| -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  | 
