Skip to content

Commit 7e15a07

Browse files
authored
fileinfo: Remove php_fileinfo struct (#18398)
* fileinfo: Remove `php_fileinfo` struct This is just a needless layer of indirection and requires an additional allocation. * fileinfo: Remove options field from `finfo_object` This was only required to restore the original options when options are given for `finfo_file()` or `finfo_buffer()`. This can more reliably be achieved using `magic_getflags()` and is therefore redundant. * fileinfo: Preserve error for uninitialized `finfo` objects
1 parent acd4b81 commit 7e15a07

File tree

2 files changed

+91
-55
lines changed

2 files changed

+91
-55
lines changed

ext/fileinfo/fileinfo.c

+38-55
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,11 @@
3636
#include "fopen_wrappers.h" /* needed for is_url */
3737
#include "Zend/zend_exceptions.h"
3838

39-
/* {{{ macros and type definitions */
40-
typedef struct _php_fileinfo {
41-
zend_long options;
42-
struct magic_set *magic;
43-
} php_fileinfo;
44-
4539
static zend_object_handlers finfo_object_handlers;
4640
zend_class_entry *finfo_class_entry;
4741

4842
typedef struct _finfo_object {
49-
php_fileinfo *ptr;
43+
struct magic_set *magic;
5044
zend_object zo;
5145
} finfo_object;
5246

@@ -56,26 +50,12 @@ static inline finfo_object *php_finfo_fetch_object(zend_object *obj) {
5650

5751
#define Z_FINFO_P(zv) php_finfo_fetch_object(Z_OBJ_P((zv)))
5852

59-
#define FILEINFO_FROM_OBJECT(finfo, object) \
60-
{ \
61-
finfo_object *obj = Z_FINFO_P(object); \
62-
finfo = obj->ptr; \
63-
if (!finfo) { \
64-
zend_throw_error(NULL, "Invalid finfo object"); \
65-
RETURN_THROWS(); \
66-
} \
67-
}
68-
6953
/* {{{ finfo_objects_free */
7054
static void finfo_objects_free(zend_object *object)
7155
{
7256
finfo_object *intern = php_finfo_fetch_object(object);
7357

74-
if (intern->ptr) {
75-
magic_close(intern->ptr->magic);
76-
efree(intern->ptr);
77-
}
78-
58+
magic_close(intern->magic);
7959
zend_object_std_dtor(&intern->zo);
8060
}
8161
/* }}} */
@@ -153,7 +133,6 @@ PHP_FUNCTION(finfo_open)
153133
zend_long options = MAGIC_NONE;
154134
char *file = NULL;
155135
size_t file_len = 0;
156-
php_fileinfo *finfo;
157136
zval *object = getThis();
158137
char resolved_path[MAXPATHLEN];
159138
zend_error_handling zeh;
@@ -163,15 +142,10 @@ PHP_FUNCTION(finfo_open)
163142
}
164143

165144
if (object) {
166-
finfo_object *finfo_obj = Z_FINFO_P(object);
167-
168145
zend_replace_error_handling(EH_THROW, NULL, &zeh);
169146

170-
if (finfo_obj->ptr) {
171-
magic_close(finfo_obj->ptr->magic);
172-
efree(finfo_obj->ptr);
173-
finfo_obj->ptr = NULL;
174-
}
147+
magic_close(Z_FINFO_P(object)->magic);
148+
Z_FINFO_P(object)->magic = NULL;
175149
}
176150

177151
if (file_len == 0) {
@@ -199,13 +173,9 @@ PHP_FUNCTION(finfo_open)
199173
file = resolved_path;
200174
}
201175

202-
finfo = emalloc(sizeof(php_fileinfo));
176+
struct magic_set *magic = magic_open(options);
203177

204-
finfo->options = options;
205-
finfo->magic = magic_open(options);
206-
207-
if (finfo->magic == NULL) {
208-
efree(finfo);
178+
if (magic == NULL) {
209179
php_error_docref(NULL, E_WARNING, "Invalid mode '" ZEND_LONG_FMT "'.", options);
210180
if (object) {
211181
zend_restore_error_handling(&zeh);
@@ -216,10 +186,9 @@ PHP_FUNCTION(finfo_open)
216186
RETURN_FALSE;
217187
}
218188

219-
if (magic_load(finfo->magic, file) == -1) {
189+
if (magic_load(magic, file) == -1) {
220190
php_error_docref(NULL, E_WARNING, "Failed to load magic database at \"%s\"", file);
221-
magic_close(finfo->magic);
222-
efree(finfo);
191+
magic_close(magic);
223192
if (object) {
224193
zend_restore_error_handling(&zeh);
225194
if (!EG(exception)) {
@@ -230,14 +199,13 @@ PHP_FUNCTION(finfo_open)
230199
}
231200

232201
if (object) {
233-
finfo_object *obj;
234202
zend_restore_error_handling(&zeh);
235-
obj = Z_FINFO_P(object);
236-
obj->ptr = finfo;
203+
finfo_object *obj = Z_FINFO_P(object);
204+
obj->magic = magic;
237205
} else {
238206
zend_object *zobj = finfo_objects_new(finfo_class_entry);
239207
finfo_object *obj = php_finfo_fetch_object(zobj);
240-
obj->ptr = finfo;
208+
obj->magic = magic;
241209
RETURN_OBJ(zobj);
242210
}
243211
}
@@ -260,18 +228,20 @@ PHP_FUNCTION(finfo_close)
260228
PHP_FUNCTION(finfo_set_flags)
261229
{
262230
zend_long options;
263-
php_fileinfo *finfo;
264231
zval *self;
265232

266233
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &self, finfo_class_entry, &options) == FAILURE) {
267234
RETURN_THROWS();
268235
}
269-
FILEINFO_FROM_OBJECT(finfo, self);
236+
237+
if (!Z_FINFO_P(self)->magic) {
238+
zend_throw_error(NULL, "Invalid finfo object");
239+
RETURN_THROWS();
240+
}
270241

271242
/* We do not check the return value as it can only ever fail if options contains MAGIC_PRESERVE_ATIME
272243
* and the system neither has utime(3) nor utimes(2). Something incredibly unlikely. */
273-
magic_setflags(finfo->magic, options);
274-
finfo->options = options;
244+
magic_setflags(Z_FINFO_P(self)->magic, options);
275245

276246
RETURN_TRUE;
277247
}
@@ -331,13 +301,17 @@ PHP_FUNCTION(finfo_file)
331301
zend_string *path = NULL;
332302
zend_long options = 0;
333303
zval *zcontext = NULL;
334-
php_fileinfo *finfo = NULL;
335304

336305
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OP|lr!", &self, finfo_class_entry, &path, &options, &zcontext) == FAILURE) {
337306
RETURN_THROWS();
338307
}
339-
FILEINFO_FROM_OBJECT(finfo, self);
340-
struct magic_set *magic = finfo->magic;
308+
309+
if (!Z_FINFO_P(self)->magic) {
310+
zend_throw_error(NULL, "Invalid finfo object");
311+
RETURN_THROWS();
312+
}
313+
314+
struct magic_set *magic = Z_FINFO_P(self)->magic;
341315

342316
if (UNEXPECTED(ZSTR_LEN(path) == 0)) {
343317
zend_argument_must_not_be_empty_error(2);
@@ -349,16 +323,18 @@ PHP_FUNCTION(finfo_file)
349323
}
350324

351325
/* Set options for the current file/buffer. */
326+
int old_options = magic_getflags(magic);
352327
if (options) {
353328
/* We do not check the return value as it can only ever fail if options contains MAGIC_PRESERVE_ATIME
354329
* and the system neither has utime(3) nor utimes(2). Something incredibly unlikely. */
355330
magic_setflags(magic, options);
356331
}
357332

358333
const char *ret_val = php_fileinfo_from_path(magic, path, context);
334+
359335
/* Restore options */
360336
if (options) {
361-
magic_setflags(magic, finfo->options);
337+
magic_setflags(magic, old_options);
362338
}
363339

364340
if (UNEXPECTED(ret_val == NULL)) {
@@ -375,24 +351,31 @@ PHP_FUNCTION(finfo_buffer)
375351
zend_string *buffer = NULL;
376352
zend_long options = 0;
377353
zval *dummy_context = NULL;
378-
php_fileinfo *finfo = NULL;
379354

380355
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OS|lr!", &self, finfo_class_entry, &buffer, &options, &dummy_context) == FAILURE) {
381356
RETURN_THROWS();
382357
}
383-
FILEINFO_FROM_OBJECT(finfo, self);
384-
struct magic_set *magic = finfo->magic;
358+
359+
if (!Z_FINFO_P(self)->magic) {
360+
zend_throw_error(NULL, "Invalid finfo object");
361+
RETURN_THROWS();
362+
}
363+
364+
struct magic_set *magic = Z_FINFO_P(self)->magic;
385365

386366
/* Set options for the current file/buffer. */
367+
int old_options = magic_getflags(magic);
387368
if (options) {
369+
/* We do not check the return value as it can only ever fail if options contains MAGIC_PRESERVE_ATIME
370+
* and the system neither has utime(3) nor utimes(2). Something incredibly unlikely. */
388371
magic_setflags(magic, options);
389372
}
390373

391374
const char *ret_val = magic_buffer(magic, ZSTR_VAL(buffer), ZSTR_LEN(buffer));
392375

393376
/* Restore options */
394377
if (options) {
395-
magic_setflags(magic, finfo->options);
378+
magic_setflags(magic, old_options);
396379
}
397380

398381
if (UNEXPECTED(ret_val == NULL)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
--TEST--
2+
Fileinfo uninitialized object
3+
--EXTENSIONS--
4+
fileinfo
5+
--FILE--
6+
<?php
7+
8+
$finfo = (new ReflectionClass('finfo'))->newInstanceWithoutConstructor();
9+
10+
try {
11+
var_dump(finfo_set_flags($finfo, FILEINFO_NONE));
12+
} catch (Error $e) {
13+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
14+
}
15+
16+
try {
17+
var_dump($finfo->set_flags(FILEINFO_NONE));
18+
} catch (Error $e) {
19+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
20+
}
21+
22+
try {
23+
var_dump(finfo_file($finfo, __FILE__));
24+
} catch (Error $e) {
25+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
26+
}
27+
28+
try {
29+
var_dump($finfo->file(__FILE__));
30+
} catch (Error $e) {
31+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
32+
}
33+
34+
try {
35+
var_dump(finfo_buffer($finfo, file_get_contents(__FILE__)));
36+
} catch (Error $e) {
37+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
38+
}
39+
40+
try {
41+
var_dump($finfo->file(file_get_contents(__FILE__)));
42+
} catch (Error $e) {
43+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
44+
}
45+
46+
?>
47+
--EXPECT--
48+
Error: Invalid finfo object
49+
Error: Invalid finfo object
50+
Error: Invalid finfo object
51+
Error: Invalid finfo object
52+
Error: Invalid finfo object
53+
Error: Invalid finfo object

0 commit comments

Comments
 (0)