Skip to content

Commit fabee4e

Browse files
committed
ext/fileinfo: Separate implementations of functions
Instead of relying on a "god" function
1 parent 3d41cb0 commit fabee4e

File tree

4 files changed

+161
-142
lines changed

4 files changed

+161
-142
lines changed

UPGRADING

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ PHP 8.5 UPGRADE NOTES
4545
change, but should closer match user expectations, demonstrated by GH-15753
4646
and GH-16198.
4747

48+
- FileInfo:
49+
. finfo_file() and finfo::file() now throws a ValueError instead of a
50+
TypeError when $filename contains nul bytes.
51+
This aligns the type of Error thrown to be consistent with the rest of
52+
the language.
53+
4854
- Intl:
4955
. The extension now requires at least ICU 57.1.
5056

ext/fileinfo/fileinfo.c

Lines changed: 153 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -277,57 +277,73 @@ PHP_FUNCTION(finfo_set_flags)
277277
}
278278
/* }}} */
279279

280-
#define FILEINFO_MODE_BUFFER 0
281-
#define FILEINFO_MODE_STREAM 1
282-
#define FILEINFO_MODE_FILE 2
283-
284-
static void _php_finfo_get_type(INTERNAL_FUNCTION_PARAMETERS, int mode, int mimetype_emu) /* {{{ */
280+
static const char* php_fileinfo_from_path(struct magic_set *magic, const zend_string *path, php_stream_context *context)
285281
{
286-
zend_long options = 0;
287-
char *ret_val = NULL, *buffer = NULL;
288-
size_t buffer_len;
289-
php_fileinfo *finfo = NULL;
290-
zval *zcontext = NULL;
291-
zval *what;
292-
char mime_directory[] = "directory";
293-
struct magic_set *magic = NULL;
282+
ZEND_ASSERT(magic != NULL);
283+
ZEND_ASSERT(path);
284+
ZEND_ASSERT(ZSTR_LEN(path) != 0);
285+
ZEND_ASSERT(!zend_str_has_nul_byte(path));
286+
ZEND_ASSERT(context != NULL);
287+
288+
/* determine if the file is a local file or remote URL */
289+
const char *dummy;
290+
php_stream_statbuf ssb;
291+
292+
const php_stream_wrapper *wrap = php_stream_locate_url_wrapper(ZSTR_VAL(path), &dummy, 0);
293+
if (UNEXPECTED(wrap == NULL)) {
294+
return NULL;
295+
}
294296

295-
if (mimetype_emu) {
297+
#ifdef PHP_WIN32
298+
if (php_stream_stat_path_ex(ZSTR_VAL(path), 0, &ssb, context) == SUCCESS) {
299+
if (ssb.sb.st_mode & S_IFDIR) {
300+
return "directory";
301+
}
302+
}
303+
#endif
296304

297-
/* mime_content_type(..) emulation */
298-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &what) == FAILURE) {
299-
RETURN_THROWS();
305+
php_stream *stream = php_stream_open_wrapper_ex(ZSTR_VAL(path), "rb", REPORT_ERRORS, NULL, context);
306+
if (!stream) {
307+
return NULL;
308+
}
309+
310+
const char *ret_val = NULL;
311+
if (php_stream_stat(stream, &ssb) == SUCCESS) {
312+
if (ssb.sb.st_mode & S_IFDIR) {
313+
ret_val = "directory";
314+
} else {
315+
ret_val = magic_stream(magic, stream);
316+
if (UNEXPECTED(ret_val == NULL)) {
317+
php_error_docref(NULL, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic));
318+
}
300319
}
320+
}
301321

302-
switch (Z_TYPE_P(what)) {
303-
case IS_STRING:
304-
buffer = Z_STRVAL_P(what);
305-
buffer_len = Z_STRLEN_P(what);
306-
mode = FILEINFO_MODE_FILE;
307-
break;
322+
php_stream_close(stream);
308323

309-
case IS_RESOURCE:
310-
mode = FILEINFO_MODE_STREAM;
311-
break;
324+
return ret_val;
325+
}
312326

313-
default:
314-
zend_argument_type_error(1, "must be of type resource|string, %s given", zend_zval_value_name(what));
315-
RETURN_THROWS();
316-
}
327+
/* Return information about a file. */
328+
PHP_FUNCTION(finfo_file)
329+
{
330+
zval *self;
331+
zend_string *path = NULL;
332+
zend_long options = 0;
333+
zval *zcontext = NULL;
334+
php_fileinfo *finfo = NULL;
317335

318-
magic = magic_open(MAGIC_MIME_TYPE);
319-
if (magic_load(magic, NULL) == -1) {
320-
php_error_docref(NULL, E_WARNING, "Failed to load magic database");
321-
goto common;
322-
}
323-
} else {
324-
zval *self;
325-
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|lr!", &self, finfo_class_entry, &buffer, &buffer_len, &options, &zcontext) == FAILURE) {
326-
RETURN_THROWS();
327-
}
328-
FILEINFO_FROM_OBJECT(finfo, self);
329-
magic = finfo->magic;
336+
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OP|lr!", &self, finfo_class_entry, &path, &options, &zcontext) == FAILURE) {
337+
RETURN_THROWS();
338+
}
339+
FILEINFO_FROM_OBJECT(finfo, self);
340+
struct magic_set *magic = finfo->magic;
341+
342+
if (UNEXPECTED(ZSTR_LEN(path) == 0)) {
343+
zend_argument_must_not_be_empty_error(2);
344+
RETURN_THROWS();
330345
}
346+
php_stream_context *context = php_stream_context_from_zval(zcontext, false);
331347

332348
/* Set options for the current file/buffer. */
333349
if (options) {
@@ -336,124 +352,121 @@ static void _php_finfo_get_type(INTERNAL_FUNCTION_PARAMETERS, int mode, int mime
336352
magic_setflags(magic, options);
337353
}
338354

339-
switch (mode) {
340-
case FILEINFO_MODE_BUFFER:
341-
{
342-
ret_val = (char *) magic_buffer(magic, buffer, buffer_len);
343-
break;
344-
}
345-
346-
case FILEINFO_MODE_STREAM:
347-
{
348-
php_stream *stream;
349-
zend_off_t streampos;
350-
351-
php_stream_from_zval_no_verify(stream, what);
352-
if (!stream) {
353-
goto common;
354-
}
355-
356-
streampos = php_stream_tell(stream); /* remember stream position for restoration */
357-
php_stream_seek(stream, 0, SEEK_SET);
355+
const char *ret_val = php_fileinfo_from_path(magic, path, context);
356+
/* Restore options */
357+
if (options) {
358+
magic_setflags(magic, finfo->options);
359+
}
358360

359-
ret_val = (char *) magic_stream(magic, stream);
361+
if (UNEXPECTED(ret_val == NULL)) {
362+
RETURN_FALSE;
363+
} else {
364+
RETURN_STRING(ret_val);
365+
}
366+
}
360367

361-
php_stream_seek(stream, streampos, SEEK_SET);
362-
break;
363-
}
368+
/* Return information about a string buffer. */
369+
PHP_FUNCTION(finfo_buffer)
370+
{
371+
zval *self;
372+
zend_string *buffer = NULL;
373+
zend_long options = 0;
374+
zval *dummy_context = NULL;
375+
php_fileinfo *finfo = NULL;
364376

365-
case FILEINFO_MODE_FILE:
366-
{
367-
/* determine if the file is a local file or remote URL */
368-
const char *tmp2;
369-
php_stream_wrapper *wrap;
370-
php_stream_statbuf ssb;
371-
372-
// Implementation is used for both finfo_file() and mimetype_emu()
373-
int buffer_param_num = (mimetype_emu ? 1 : 2);
374-
if (buffer == NULL || buffer_len == 0) {
375-
zend_argument_must_not_be_empty_error(buffer_param_num);
376-
goto clean;
377-
}
378-
if (CHECK_NULL_PATH(buffer, buffer_len)) {
379-
zend_argument_type_error(buffer_param_num, "must not contain any null bytes");
380-
goto clean;
381-
}
377+
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS|lr!", &self, finfo_class_entry, &buffer, &options, &dummy_context) == FAILURE) {
378+
RETURN_THROWS();
379+
}
380+
FILEINFO_FROM_OBJECT(finfo, self);
381+
struct magic_set *magic = finfo->magic;
382382

383-
wrap = php_stream_locate_url_wrapper(buffer, &tmp2, 0);
383+
/* Set options for the current file/buffer. */
384+
if (options) {
385+
magic_setflags(magic, options);
386+
}
384387

385-
if (wrap) {
386-
php_stream *stream;
387-
php_stream_context *context = php_stream_context_from_zval(zcontext, 0);
388+
const char *ret_val = magic_buffer(magic, ZSTR_VAL(buffer), ZSTR_LEN(buffer));
388389

389-
#ifdef PHP_WIN32
390-
if (php_stream_stat_path_ex(buffer, 0, &ssb, context) == SUCCESS) {
391-
if (ssb.sb.st_mode & S_IFDIR) {
392-
ret_val = mime_directory;
393-
goto common;
394-
}
395-
}
396-
#endif
390+
/* Restore options */
391+
if (options) {
392+
magic_setflags(magic, finfo->options);
393+
}
397394

398-
stream = php_stream_open_wrapper_ex(buffer, "rb", REPORT_ERRORS, NULL, context);
395+
if (UNEXPECTED(ret_val == NULL)) {
396+
php_error_docref(NULL, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic));
397+
RETURN_FALSE;
398+
} else {
399+
RETURN_STRING(ret_val);
400+
}
401+
}
399402

400-
if (!stream) {
401-
RETVAL_FALSE;
402-
goto clean;
403-
}
403+
/* Return content-type for file */
404+
PHP_FUNCTION(mime_content_type)
405+
{
406+
zval *path_or_stream;
407+
const zend_string *path = NULL;
408+
php_stream *stream = NULL;
409+
struct magic_set *magic = NULL;
404410

405-
if (php_stream_stat(stream, &ssb) == SUCCESS) {
406-
if (ssb.sb.st_mode & S_IFDIR) {
407-
ret_val = mime_directory;
408-
} else {
409-
ret_val = (char *)magic_stream(magic, stream);
410-
}
411-
}
411+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &path_or_stream) == FAILURE) {
412+
RETURN_THROWS();
413+
}
412414

413-
php_stream_close(stream);
415+
switch (Z_TYPE_P(path_or_stream)) {
416+
case IS_STRING:
417+
path = Z_STR_P(path_or_stream);
418+
if (UNEXPECTED(ZSTR_LEN(path) == 0)) {
419+
zend_argument_must_not_be_empty_error(1);
420+
RETURN_THROWS();
421+
}
422+
if (UNEXPECTED(zend_str_has_nul_byte(path))) {
423+
zend_argument_type_error(1, "must not contain any null bytes");
424+
RETURN_THROWS();
414425
}
415426
break;
416-
}
417-
EMPTY_SWITCH_DEFAULT_CASE()
427+
428+
case IS_RESOURCE:
429+
php_stream_from_zval(stream, path_or_stream);
430+
break;
431+
432+
default:
433+
zend_argument_type_error(1, "must be of type resource|string, %s given", zend_zval_value_name(path_or_stream));
434+
RETURN_THROWS();
418435
}
419436

420-
common:
421-
if (ret_val) {
422-
RETVAL_STRING(ret_val);
423-
} else {
424-
php_error_docref(NULL, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic));
425-
RETVAL_FALSE;
437+
magic = magic_open(MAGIC_MIME_TYPE);
438+
if (UNEXPECTED(magic == NULL)) {
439+
php_error_docref(NULL, E_WARNING, "Failed to load magic database");
440+
RETURN_FALSE;
426441
}
427442

428-
clean:
429-
if (mimetype_emu) {
443+
if (UNEXPECTED(magic_load(magic, NULL) == -1)) {
444+
php_error_docref(NULL, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic));
430445
magic_close(magic);
446+
RETURN_FALSE;
431447
}
432448

433-
/* Restore options */
434-
if (options) {
435-
magic_setflags(magic, finfo->options);
436-
}
437-
}
438-
/* }}} */
449+
const char *ret_val;
450+
if (path) {
451+
php_stream_context *context = php_stream_context_get_default(false);
452+
ret_val = php_fileinfo_from_path(magic, path, context);
453+
} else {
454+
/* remember stream position for restoration */
455+
zend_off_t current_stream_pos = php_stream_tell(stream);
456+
php_stream_seek(stream, 0, SEEK_SET);
439457

440-
/* {{{ Return information about a file. */
441-
PHP_FUNCTION(finfo_file)
442-
{
443-
_php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, FILEINFO_MODE_FILE, 0);
444-
}
445-
/* }}} */
458+
ret_val = magic_stream(magic, stream);
459+
if (UNEXPECTED(ret_val == NULL)) {
460+
php_error_docref(NULL, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic));
461+
}
446462

447-
/* {{{ Return information about a string buffer. */
448-
PHP_FUNCTION(finfo_buffer)
449-
{
450-
_php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, FILEINFO_MODE_BUFFER, 0);
451-
}
452-
/* }}} */
463+
php_stream_seek(stream, current_stream_pos, SEEK_SET);
464+
}
453465

454-
/* {{{ Return content-type for file */
455-
PHP_FUNCTION(mime_content_type)
456-
{
457-
_php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1, 1);
466+
if (UNEXPECTED(ret_val == NULL)) {
467+
RETVAL_FALSE;
468+
} else {
469+
RETVAL_STRING(ret_val);
470+
}
471+
magic_close(magic);
458472
}
459-
/* }}} */

ext/fileinfo/tests/finfo_file_001.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ fileinfo
88
$fp = finfo_open();
99
try {
1010
var_dump(finfo_file($fp, "\0"));
11-
} catch (\TypeError $e) {
11+
} catch (\ValueError $e) {
1212
echo $e->getMessage() . \PHP_EOL;
1313
}
1414
try {

ext/fileinfo/tests/finfo_file_basic.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var_dump( finfo_file( $finfo, __FILE__, FILEINFO_CONTINUE ) );
1515
var_dump( finfo_file( $finfo, $magicFile ) );
1616
try {
1717
var_dump( finfo_file( $finfo, $magicFile.chr(0).$magicFile) );
18-
} catch (\TypeError $e) {
18+
} catch (\ValueError $e) {
1919
echo $e->getMessage() . \PHP_EOL;
2020
}
2121

0 commit comments

Comments
 (0)