From ac3ae236ecdd73c996da6b7ad3a72a441db6ce26 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sun, 7 Sep 2008 20:29:54 +0000 Subject: [PATCH] - MFB: - use php's stream in libmagic - remove duplicate code (like mime_content_type() compatibility function now use the same base as finfo_file()) - make it portable (works now on windows too, belongs other OSes) - don't close caller streams in libmagic (prevent leak and zombie stream) - string returned by magic_* are freed on magic_close, duplicate before calling magic_close (set return value) - if stat failed, don't try to call magic_* (when FILEINFO_MODE_FILE) --- ext/fileinfo/fileinfo.c | 262 +++++++++--------- ext/fileinfo/libmagic/apprentice.c | 133 ++++++--- ext/fileinfo/libmagic/compress.c | 8 + ext/fileinfo/libmagic/file.h | 13 +- ext/fileinfo/libmagic/fsmagic.c | 259 ++++++----------- ext/fileinfo/libmagic/funcs.c | 44 ++- ext/fileinfo/libmagic/magic.c | 149 +++++----- ext/fileinfo/libmagic/magic.h | 1 + ext/fileinfo/tests/finfo_file_001.phpt | 9 +- ext/fileinfo/tests/mime_content_type_001.phpt | 2 +- 10 files changed, 431 insertions(+), 449 deletions(-) diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c index c5f36d0fad..5c421e3330 100644 --- a/ext/fileinfo/fileinfo.c +++ b/ext/fileinfo/fileinfo.c @@ -38,6 +38,10 @@ #include "php_fileinfo.h" #include "fopen_wrappers.h" /* needed for is_url */ +#ifndef _S_IFDIR +# define _S_IFDIR S_IFDIR +#endif + /* {{{ macros and type definitions */ struct php_fileinfo { long options; @@ -405,87 +409,164 @@ PHP_FUNCTION(finfo_set_flags) } /* }}} */ -static void _php_finfo_get_type(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */ +#define FILEINFO_MODE_BUFFER 0 +#define FILEINFO_MODE_STREAM 1 +#define FILEINFO_MODE_FILE 2 + +static void _php_finfo_get_type(INTERNAL_FUNCTION_PARAMETERS, int mode, int mimetype_emu) /* {{{ */ { long options = 0; - char *tmp, *ret_val, *buffer = NULL; + char *ret_val = NULL, *buffer = NULL; int buffer_len; struct php_fileinfo *finfo; zval *zfinfo, *zcontext = NULL; + zval *what; + char mime_directory[] = "directory"; + + struct magic_set *magic = NULL; FILEINFO_DECLARE_INIT_OBJECT(object) - if (object) { + if (mimetype_emu) { + + /* mime_content_type(..) emulation */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &what) == FAILURE) { + return; + } + + switch (Z_TYPE_P(what)) { + case IS_UNICODE: + case IS_STRING: + if (Z_TYPE_P(what) == IS_UNICODE) { + convert_to_string_ex(&what); + } + + buffer = Z_STRVAL_P(what); + buffer_len = Z_STRLEN_P(what); + mode = FILEINFO_MODE_FILE; + break; + + case IS_RESOURCE: + mode = FILEINFO_MODE_STREAM; + break; + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only process string or stream arguments"); + RETURN_FALSE; + } + + magic = magic_open(MAGIC_MIME); + if (magic_load(magic, NULL) == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to load magic database."); + goto common; + } + } else if (object) { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lr", &buffer, &buffer_len, &options, &zcontext) == FAILURE) { RETURN_FALSE; } FILEINFO_FROM_OBJECT(finfo, object); + magic = finfo->magic; } else { if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lr", &zfinfo, &buffer, &buffer_len, &options, &zcontext) == FAILURE) { RETURN_FALSE; } ZEND_FETCH_RESOURCE(finfo, struct php_fileinfo *, &zfinfo, -1, "file_info", le_fileinfo); + magic = finfo->magic; } /* Set options for the current file/buffer. */ if (options) { - FINFO_SET_OPTION(finfo->magic, options) + FINFO_SET_OPTION(magic, options) } - if (mode) { /* file */ - /* determine if the file is a local file or remote URL */ - char *tmp2; - php_stream_wrapper *wrap = php_stream_locate_url_wrapper(buffer, &tmp2, 0 TSRMLS_CC); - if (wrap && wrap->is_url) { -#ifdef ZEND_ENGINE_2 - php_stream_context *context = php_stream_context_from_zval(zcontext, 0); -#else - php_stream_context *context = NULL; -#endif - php_stream *stream = php_stream_open_wrapper_ex(buffer, "rb", - ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context); - if (!stream) { - RETURN_FALSE; + switch (mode) { + case FILEINFO_MODE_BUFFER: + { + ret_val = (char *) magic_buffer(magic, buffer, buffer_len); + break; + } + + case FILEINFO_MODE_STREAM: + { + php_stream *stream; + off_t streampos; + + php_stream_from_zval_no_verify(stream, &what); + if (!stream) { + goto common; + } + + streampos = php_stream_tell(stream); /* remember stream position for restoration */ + php_stream_seek(stream, 0, SEEK_SET); + + ret_val = (char *) magic_stream(magic, stream); + break; + } + + case FILEINFO_MODE_FILE: + { + /* determine if the file is a local file or remote URL */ + char *tmp2; + php_stream_wrapper *wrap; + struct stat sb; + + if (buffer == NULL || !*buffer) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty filename or path"); + RETVAL_FALSE; + goto clean; } - buffer_len = php_stream_copy_to_mem(stream, &tmp, HOWMANY, 0); - php_stream_close(stream); - if (buffer_len == 0) { - RETURN_FALSE; + if (php_sys_stat(buffer, &sb) == 0) { + if (sb.st_mode & _S_IFDIR) { + ret_val = mime_directory; + goto common; + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "File or path not found '%s'", buffer); + RETVAL_FALSE; + goto clean; } - } else { /* local file */ - char resolved_path[MAXPATHLEN]; + wrap = php_stream_locate_url_wrapper(buffer, &tmp2, 0 TSRMLS_CC); + + if (wrap) { + php_stream_context *context = php_stream_context_from_zval(zcontext, 0); - if (*buffer && VCWD_REALPATH(buffer, resolved_path)) { - if (php_check_open_basedir(resolved_path TSRMLS_CC)) { - RETURN_FALSE; + php_stream *stream = php_stream_open_wrapper_ex(buffer, "rb", REPORT_ERRORS, NULL, context); + + if (!stream) { + if (mimetype_emu) { + magic_close(magic); + } + RETVAL_FALSE; + goto clean; } - ret_val = (char *) magic_file(finfo->magic, resolved_path); - } else { - RETURN_FALSE; + + ret_val = magic_stream(magic, stream); + php_stream_close(stream); } - goto common; + break; } - } else { /* buffer */ - tmp = buffer; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only process string or stream arguments"); + } + +common: + if (ret_val) { + RETVAL_STRING(ret_val, 1); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed identify data %d:%s", magic_errno(magic), magic_error(magic)); + RETVAL_FALSE; } - ret_val = (char *) magic_buffer(finfo->magic, tmp, buffer_len); - if (mode) { - efree(tmp); +clean: + if (mimetype_emu) { + magic_close(magic); } -common: + /* Restore options */ if (options) { - FINFO_SET_OPTION(finfo->magic, finfo->options) - } - - if (!ret_val) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed identify data %d:%s", - magic_errno(finfo->magic), magic_error(finfo->magic)); - RETURN_FALSE; - } else { - RETURN_STRING(ret_val, 1); + FINFO_SET_OPTION(magic, finfo->options) } + return; } /* }}} */ @@ -493,7 +574,7 @@ common: Return information about a file. */ PHP_FUNCTION(finfo_file) { - _php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + _php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, FILEINFO_MODE_FILE, 0); } /* }}} */ @@ -501,7 +582,7 @@ PHP_FUNCTION(finfo_file) Return infromation about a string buffer. */ PHP_FUNCTION(finfo_buffer) { - _php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + _php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, FILEINFO_MODE_BUFFER, 0); } /* }}} */ @@ -509,90 +590,7 @@ PHP_FUNCTION(finfo_buffer) Return content-type for file */ PHP_FUNCTION(mime_content_type) { - zval *what; - magic_t magic; - char *tmp, *ret_val; - int buffer_len; - char *tmp2; - php_stream_wrapper *wrap; - zval *zcontext = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &what) == FAILURE) { - return; - } - - RETVAL_FALSE; - - magic = magic_open(MAGIC_MIME); - if (magic_load(magic, NULL) == -1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to load magic database."); - goto cleanup; - } - - switch (Z_TYPE_P(what)) { - case IS_STRING: - wrap = php_stream_locate_url_wrapper(Z_STRVAL_P(what), &tmp2, 0 TSRMLS_CC); - /* determine if the file is a local file or remote URL */ - if (wrap && wrap->is_url) { - php_stream_context *context = php_stream_context_from_zval(zcontext, 0); - php_stream *stream = php_stream_open_wrapper_ex(Z_STRVAL_P(what), "rb", - ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context); - if (!stream) { - goto cleanup; - } - buffer_len = php_stream_copy_to_mem(stream, &tmp, 4096, 0); - php_stream_close(stream); - - if (buffer_len == 0) { - goto cleanup; - } - ret_val = (char *) magic_buffer(magic, tmp, buffer_len); - } else { /* local file */ - char resolved_path[MAXPATHLEN]; - - if (*Z_STRVAL_P(what) && VCWD_REALPATH(Z_STRVAL_P(what), resolved_path)) { - if (php_check_open_basedir(resolved_path TSRMLS_CC)) { - goto cleanup; - } - ret_val = (char *) magic_file(magic, resolved_path); - } else { - goto cleanup; - } - } - break; - case IS_RESOURCE: - { - php_stream *stream; - off_t streampos; - - php_stream_from_zval_no_verify(stream, &what); - if (!stream) { - goto cleanup; - } - streampos = php_stream_tell(stream); /* remember stream position for restoration */ - php_stream_seek(stream, 0, SEEK_SET); - - buffer_len = php_stream_copy_to_mem(stream, &tmp, 4096, 0); - php_stream_seek(stream, streampos, SEEK_SET); - - if (buffer_len == 0) { - goto cleanup; - } - ret_val = (char *) magic_buffer(magic, tmp, buffer_len); - } - break; - default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only process string or stream arguments"); - goto cleanup; - } - if (!ret_val) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed identify data %d:%s", - magic_errno(magic), magic_error(magic)); - } else { - RETVAL_STRING(ret_val, 1); - } -cleanup: - magic_close(magic); + _php_finfo_get_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1, 1); } /* }}} */ diff --git a/ext/fileinfo/libmagic/apprentice.c b/ext/fileinfo/libmagic/apprentice.c index 98f7729f06..1218bc7c88 100644 --- a/ext/fileinfo/libmagic/apprentice.c +++ b/ext/fileinfo/libmagic/apprentice.c @@ -29,21 +29,32 @@ * apprentice - make one pass through /etc/magic, learning its secrets. */ +#include "php.h" + #include "file.h" #include "magic.h" #include "patchlevel.h" #include -//#ifdef HAVE_UNISTD_H + +#ifdef PHP_WIN32 +#include "win32/unistd.h" +#define strtoull _strtoui64 +#else #include -//#endif +#endif + #include #include #include #include #include +#ifndef PHP_WIN32 #include +#endif #include +#ifndef PHP_WIN32 #include +#endif #ifndef lint FILE_RCSID("@(#)$File: apprentice.c,v 1.132 2008/03/28 18:19:30 christos Exp $") @@ -174,6 +185,10 @@ static const struct type_tbl_s { # undef XX_NULL }; +#ifndef S_ISDIR +#define S_ISDIR(mode) ((mode) & _S_IFDIR) +#endif + private int get_type(const char *l, const char **t) { @@ -528,30 +543,41 @@ private void load_1(struct magic_set *ms, int action, const char *fn, int *errs, struct magic_entry **marray, uint32_t *marraycount) { - char line[BUFSIZ]; + zstr buffer; + char *line; + size_t line_len; size_t lineno = 0; - FILE *f = fopen(ms->file = fn, "r"); - if (f == NULL) { + + php_stream *stream; + + TSRMLS_FETCH(); + +#if (PHP_MAJOR_VERSION < 6) + stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); +#else + stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS, NULL); +#endif + if (stream == NULL) { if (errno != ENOENT) file_error(ms, errno, "cannot read magic file `%s'", fn); (*errs)++; } else { - /* read and parse this file */ - for (ms->line = 1; fgets(line, sizeof(line), f) != NULL; ms->line++) { - size_t len; - len = strlen(line); - if (len == 0) /* null line, garbage, etc */ + buffer.v = emalloc(BUFSIZ+1); + for (ms->line = 1; (line = php_stream_get_line(stream, buffer, BUFSIZ, &line_len)) != NULL; ms->line++) { + if (line_len == 0 || /* null line, garbage, etc */ + line[0] == '\0' || /* empty*/ + line[0] == '#' || /* comment */ + line[0] == '\n' || line[0] == '\r') { /* New Line */ continue; - if (line[len - 1] == '\n') { + } + + if (line[line_len - 1] == '\n') { lineno++; - line[len - 1] = '\0'; /* delete newline */ + line[line_len - 1] = '\0'; /* delete newline */ } - if (line[0] == '\0') /* empty, do not parse */ - continue; - if (line[0] == '#') /* comment, do not parse */ - continue; - if (len > mime_marker_len && + + if (line_len > mime_marker_len && memcmp(line, mime_marker, mime_marker_len) == 0) { /* MIME type */ if (parse_mime(ms, marray, marraycount, @@ -562,8 +588,8 @@ load_1(struct magic_set *ms, int action, const char *fn, int *errs, if (parse(ms, marray, marraycount, line, lineno, action) != 0) (*errs)++; } - - (void)fclose(f); + efree(buffer.v); + php_stream_close(stream); } } @@ -585,7 +611,7 @@ apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */ - maxmagic = MAXMAGIS; + maxmagic = MAXMAGIS; marray = ecalloc(maxmagic, sizeof(*marray)); marraycount = 0; @@ -594,7 +620,7 @@ apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, (void)fprintf(stderr, "%s\n", usg_hdr); /* load directory or file */ - if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) { + if (php_sys_stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) { dir = opendir(fn); if (dir) { while ((d = readdir(dir))) { @@ -1816,17 +1842,20 @@ private int apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, const char *fn) { - int fd; - struct stat st; uint32_t *ptr; uint32_t version; int needsbyteswap; char *dbname = NULL; void *mm = NULL; int ret = 0; + php_stream *stream; + php_stream_statbuf st; + + + TSRMLS_FETCH(); if (fn == NULL) { - mm = &php_magic_database; + mm = (void *)&php_magic_database; ret = 3; goto internal_loaded; } @@ -1835,27 +1864,35 @@ apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, if (dbname == NULL) goto error2; - if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1) +#if (PHP_MAJOR_VERSION < 6) + stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); +#else + stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS, NULL); +#endif + + if (!stream) { goto error2; + } - if (fstat(fd, &st) == -1) { + if (php_stream_stat(stream, &st) < 0) { file_error(ms, errno, "cannot stat `%s'", dbname); goto error1; } - if (st.st_size < 8) { + + if (st.sb.st_size < 8) { file_error(ms, 0, "file `%s' is too small", dbname); goto error1; } - mm = emalloc((size_t)st.st_size); - if (read(fd, mm, (size_t)st.st_size) != (size_t)st.st_size) { + mm = emalloc((size_t)st.sb.st_size); + if (php_stream_read(stream, mm, (size_t)st.sb.st_size) != (size_t)st.sb.st_size) { file_badread(ms); goto error1; } ret = 1; - (void)close(fd); - fd = -1; + php_stream_close(stream); + stream = NULL; internal_loaded: *magicp = mm; @@ -1882,7 +1919,7 @@ internal_loaded: if (fn == NULL) { *nmagicp = (sizeof(php_magic_database) / sizeof(struct magic)); } else { - *nmagicp = (uint32_t)(st.st_size / sizeof(struct magic)); + *nmagicp = (uint32_t)(st.sb.st_size / sizeof(struct magic)); } if (*nmagicp > 0) { (*nmagicp)--; @@ -1898,8 +1935,9 @@ internal_loaded: return ret; error1: - if (fd != -1) - (void)close(fd); + if (stream) { + php_stream_close(stream); + } if (mm) { efree(mm); } else { @@ -1921,38 +1959,47 @@ private int apprentice_compile(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, const char *fn) { - int fd; char *dbname; int rv = -1; + php_stream *stream; + + TSRMLS_FETCH(); mkdbname(fn, &dbname, 1); - if (dbname == NULL) + if (dbname == NULL) { goto out; + } - if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) { +/* wb+ == O_WRONLY|O_CREAT|O_TRUNC|O_BINARY */ +#if (PHP_MAJOR_VERSION < 6) + stream = php_stream_open_wrapper((char *)fn, "wb+", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); +#else + stream = php_stream_open_wrapper((char *)fn, "wb+", REPORT_ERRORS, NULL); +#endif + + if (!stream) { file_error(ms, errno, "cannot open `%s'", dbname); goto out; } - if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) { + if (php_stream_write(stream, (char *)ar, sizeof(ar)) != (ssize_t)sizeof(ar)) { file_error(ms, errno, "error writing `%s'", dbname); goto out; } - if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET) - != sizeof(struct magic)) { + if (php_stream_seek(stream,(off_t)sizeof(struct magic), SEEK_SET) != sizeof(struct magic)) { file_error(ms, errno, "error seeking `%s'", dbname); goto out; } - if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) - != (ssize_t)(sizeof(struct magic) * *nmagicp)) { + if (php_stream_write(stream, (char *)*magicp, (sizeof(struct magic) * *nmagicp) != (ssize_t)(sizeof(struct magic) * *nmagicp))) { file_error(ms, errno, "error writing `%s'", dbname); goto out; } - (void)close(fd); + php_stream_close(stream); + rv = 0; out: efree(dbname); diff --git a/ext/fileinfo/libmagic/compress.c b/ext/fileinfo/libmagic/compress.c index f70cddeb4a..80487b4b6d 100644 --- a/ext/fileinfo/libmagic/compress.c +++ b/ext/fileinfo/libmagic/compress.c @@ -43,7 +43,9 @@ #include #include #include +#ifndef PHP_WIN32 #include +#endif #ifdef HAVE_SYS_WAIT_H #include #endif @@ -55,6 +57,7 @@ #include #endif +#undef FIONREAD #ifndef lint FILE_RCSID("@(#)$File: compress.c,v 1.56 2008/02/07 00:58:52 christos Exp $") @@ -86,6 +89,7 @@ private size_t ncompr = sizeof(compr) / sizeof(compr[0]); private ssize_t swrite(int, const void *, size_t); +#ifdef PHP_FILEINFO_UNCOMPRESS private size_t uncompressbuf(struct magic_set *, int, size_t, const unsigned char *, unsigned char **, size_t); #ifdef BUILTIN_DECOMPRESS @@ -138,6 +142,7 @@ error: ms->flags |= MAGIC_COMPRESS; return rv; } +#endif /* * `safe' write for sockets and pipes. @@ -297,6 +302,7 @@ file_pipe2file(struct magic_set *ms, int fd, const void *startbuf, return fd; } +#ifdef PHP_FILEINFO_UNCOMPRESS #ifdef BUILTIN_DECOMPRESS #define FHCRC (1 << 1) @@ -304,6 +310,7 @@ file_pipe2file(struct magic_set *ms, int fd, const void *startbuf, #define FNAME (1 << 3) #define FCOMMENT (1 << 4) + private size_t uncompressgzipped(struct magic_set *ms, const unsigned char *old, unsigned char **newch, size_t n) @@ -485,3 +492,4 @@ err: return n; } } +#endif /* if PHP_FILEINFO_UNCOMPRESS */ diff --git a/ext/fileinfo/libmagic/file.h b/ext/fileinfo/libmagic/file.h index 125a648082..8ff768a5cd 100644 --- a/ext/fileinfo/libmagic/file.h +++ b/ext/fileinfo/libmagic/file.h @@ -33,9 +33,7 @@ #ifndef __file_h__ #define __file_h__ -//#ifdef HAVE_CONFIG_H #include "config.h" -//#endif */ #include /* Include that here, to make sure __P gets defined */ #include @@ -46,6 +44,9 @@ #ifdef HAVE_INTTYPES_H #include #endif +#ifdef PHP_WIN32 +#include "win32/php_stdint.h" +#endif #include "php.h" #include "ext/standard/php_string.h" @@ -62,7 +63,7 @@ #define MAGIC "/etc/magic" #endif -#ifdef __EMX__ +#if defined(__EMX__) || defined(PHP_WIN32) #define PATHSEP ';' #else #define PATHSEP ':' @@ -323,16 +324,18 @@ typedef unsigned long unichar; struct stat; protected const char *file_fmttime(uint32_t, int); -protected int file_buffer(struct magic_set *, int, const char *, const void *, +protected int file_buffer(struct magic_set *, php_stream *, const char *, const void *, size_t); -protected int file_fsmagic(struct magic_set *, const char *, struct stat *); +protected int file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb, php_stream *stream); protected int file_pipe2file(struct magic_set *, int, const void *, size_t); protected int file_printf(struct magic_set *, const char *, ...); protected int file_reset(struct magic_set *); protected int file_tryelf(struct magic_set *, int, const unsigned char *, size_t); +#ifdef PHP_FILEINFO_UNCOMPRESS protected int file_zmagic(struct magic_set *, int, const char *, const unsigned char *, size_t); +#endif protected int file_ascmagic(struct magic_set *, const unsigned char *, size_t); protected int file_is_tar(struct magic_set *, const unsigned char *, size_t); protected int file_softmagic(struct magic_set *, const unsigned char *, size_t, int); diff --git a/ext/fileinfo/libmagic/fsmagic.c b/ext/fileinfo/libmagic/fsmagic.c index 0e51da96f0..5526fbea6e 100644 --- a/ext/fileinfo/libmagic/fsmagic.c +++ b/ext/fileinfo/libmagic/fsmagic.c @@ -60,69 +60,64 @@ FILE_RCSID("@(#)$File: fsmagic.c,v 1.50 2008/02/12 17:22:54 rrt Exp $") #endif /* lint */ -private int -bad_link(struct magic_set *ms, int err, char *buf) -{ - char *errfmt; - if (err == ELOOP) - errfmt = "symbolic link in a loop"; - else - errfmt = "broken symbolic link to `%s'"; - if (ms->flags & MAGIC_ERROR) { - file_error(ms, err, errfmt, buf); - return -1; - } - if (file_printf(ms, errfmt, buf) == -1) - return -1; - return 1; -} +#ifdef PHP_WIN32 -protected int -file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) -{ - int ret = 0; - int mime = ms->flags & MAGIC_MIME; -#ifdef S_IFLNK - char buf[BUFSIZ+4]; - int nch; - struct stat tstatbuf; +# undef S_IFIFO #endif - if (fn == NULL) - return 0; - /* - * Fstat is cheaper but fails for files you don't have read perms on. - * On 4.2BSD and similar systems, use lstat() to identify symlinks. - */ -#ifdef S_IFLNK - if ((ms->flags & MAGIC_SYMLINK) == 0) - ret = lstat(fn, sb); - else +#ifndef S_ISDIR +#define S_ISDIR(mode) ((mode) & _S_IFDIR) #endif - ret = stat(fn, sb); /* don't merge into if; see "ret =" above */ - if (ret) { - if (ms->flags & MAGIC_ERROR) { - file_error(ms, errno, "cannot stat `%s'", fn); - return -1; - } - if (file_printf(ms, "cannot open `%s' (%s)", - fn, strerror(errno)) == -1) - return -1; - return 1; +#ifndef S_ISREG +#define S_ISREG(mode) ((mode) & _S_IFREG) +#endif + +protected int +file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb, php_stream *stream) +{ + int mime = ms->flags & MAGIC_MIME; + TSRMLS_FETCH(); + + if (!fn && !stream) { + return -1; } - if (mime) { - if ((sb->st_mode & S_IFMT) != S_IFREG) { - if ((mime & MAGIC_MIME_TYPE) && - file_printf(ms, "application/x-not-regular-file") - == -1) - return -1; + if (stream) { + php_stream_statbuf ssb; + if (php_stream_stat(stream, &ssb) < 0) { + if (ms->flags & MAGIC_ERROR) { + file_error(ms, errno, "cannot stat `%s'", fn); + } + return 1; + } + memcpy(sb, &ssb.sb, sizeof(struct stat)); + } else { + if (php_sys_stat(fn, sb) != 0) { + if (ms->flags & MAGIC_ERROR) { + file_error(ms, errno, "cannot stat `%s'", fn); + } return 1; } } - else { + + if (mime) { + if (!S_ISREG(sb->st_mode)) { + if ((mime & MAGIC_MIME_TYPE) && + file_printf(ms, "application/x-not-regular-file") == -1) { + return -1; + } + return 1; + } + + if (S_ISDIR(sb->st_mode)) { + if (file_printf(ms, "directory") == -1) { + return -1; + } + return 1; + } + } else { #ifdef S_ISUID if (sb->st_mode & S_ISUID) if (file_printf(ms, "setuid ") == -1) @@ -139,65 +134,41 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) return -1; #endif } - + switch (sb->st_mode & S_IFMT) { - case S_IFDIR: - if (file_printf(ms, "directory") == -1) - return -1; - return 1; -#ifdef S_IFCHR - case S_IFCHR: - /* - * If -s has been specified, treat character special files - * like ordinary files. Otherwise, just report that they - * are block special files and go on to the next file. - */ - if ((ms->flags & MAGIC_DEVICES) != 0) - break; -#ifdef HAVE_STAT_ST_RDEV -# ifdef dv_unit - if (file_printf(ms, "character special (%d/%d/%d)", - major(sb->st_rdev), dv_unit(sb->st_rdev), - dv_subunit(sb->st_rdev)) == -1) - return -1; -# else - if (file_printf(ms, "character special (%ld/%ld)", - (long) major(sb->st_rdev), (long) minor(sb->st_rdev)) == -1) - return -1; -# endif -#else - if (file_printf(ms, "character special") == -1) - return -1; -#endif - return 1; -#endif -#ifdef S_IFBLK - case S_IFBLK: - /* - * If -s has been specified, treat block special files - * like ordinary files. Otherwise, just report that they - * are block special files and go on to the next file. - */ - if ((ms->flags & MAGIC_DEVICES) != 0) - break; -#ifdef HAVE_STAT_ST_RDEV -# ifdef dv_unit - if (file_printf(ms, "block special (%d/%d/%d)", - major(sb->st_rdev), dv_unit(sb->st_rdev), - dv_subunit(sb->st_rdev)) == -1) - return -1; -# else - if (file_printf(ms, "block special (%ld/%ld)", - (long)major(sb->st_rdev), (long)minor(sb->st_rdev)) == -1) - return -1; +#ifndef PHP_WIN32 +# ifdef S_IFCHR + case S_IFCHR: + /* + * If -s has been specified, treat character special files + * like ordinary files. Otherwise, just report that they + * are block special files and go on to the next file. + */ + if ((ms->flags & MAGIC_DEVICES) != 0) { + break; + } +# ifdef HAVE_STAT_ST_RDEV +# ifdef dv_unit + if (file_printf(ms, "character special (%d/%d/%d)", + major(sb->st_rdev), dv_unit(sb->st_rdev), + dv_subunit(sb->st_rdev)) == -1) { + return -1; + } +# else + if (file_printf(ms, "character special (%ld/%ld)", + (long) major(sb->st_rdev), (long) minor(sb->st_rdev)) == -1) { + return -1; + } +# endif +# else + if (file_printf(ms, "character special") == -1) { + return -1; + } +# endif + return 1; # endif -#else - if (file_printf(ms, "block special") == -1) - return -1; -#endif - return 1; #endif - /* TODO add code to handle V7 MUX and Blit MUX files */ + #ifdef S_IFIFO case S_IFIFO: if((ms->flags & MAGIC_DEVICES) != 0) @@ -212,67 +183,17 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) return -1; return 1; #endif + #ifdef S_IFLNK case S_IFLNK: - if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) { + /* stat is used, if it made here then the link is broken */ if (ms->flags & MAGIC_ERROR) { - file_error(ms, errno, "unreadable symlink `%s'", - fn); + file_error(ms, errno, "unreadable symlink `%s'", fn); return -1; } - if (file_printf(ms, - "unreadable symlink `%s' (%s)", fn, - strerror(errno)) == -1) - return -1; - return 1; - } - buf[nch] = '\0'; /* readlink(2) does not do this */ - - /* If broken symlink, say so and quit early. */ - if (*buf == '/') { - if (stat(buf, &tstatbuf) < 0) - return bad_link(ms, errno, buf); - } else { - char *tmp; - char buf2[BUFSIZ+BUFSIZ+4]; - - if ((tmp = strrchr(fn, '/')) == NULL) { - tmp = buf; /* in current directory anyway */ - } else { - if (tmp - fn + 1 > BUFSIZ) { - if (ms->flags & MAGIC_ERROR) { - file_error(ms, 0, - "path too long: `%s'", buf); - return -1; - } - if (file_printf(ms, - "path too long: `%s'", fn) == -1) - return -1; - return 1; - } - (void)strcpy(buf2, fn); /* take dir part */ - buf2[tmp - fn + 1] = '\0'; - (void)strcat(buf2, buf); /* plus (rel) link */ - tmp = buf2; - } - if (stat(tmp, &tstatbuf) < 0) - return bad_link(ms, errno, buf); - } - - /* Otherwise, handle it. */ - if ((ms->flags & MAGIC_SYMLINK) != 0) { - const char *p; - ms->flags &= MAGIC_SYMLINK; - p = magic_file(ms, buf); - ms->flags |= MAGIC_SYMLINK; - return p != NULL ? 1 : -1; - } else { /* just print what it points to */ - if (file_printf(ms, "symbolic link to `%s'", - buf) == -1) - return -1; - } return 1; #endif + #ifdef S_IFSOCK #ifndef __COHERENT__ case S_IFSOCK: @@ -281,12 +202,14 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) return 1; #endif #endif - case S_IFREG: - break; - default: - file_error(ms, 0, "invalid mode 0%o", sb->st_mode); - return -1; - /*NOTREACHED*/ + + case S_IFREG: + break; + + default: + file_error(ms, 0, "invalid mode 0%o", sb->st_mode); + return -1; + /*NOTREACHED*/ } /* diff --git a/ext/fileinfo/libmagic/funcs.c b/ext/fileinfo/libmagic/funcs.c index 782f8dbc2e..b4e662ebbf 100644 --- a/ext/fileinfo/libmagic/funcs.c +++ b/ext/fileinfo/libmagic/funcs.c @@ -146,7 +146,7 @@ file_badread(struct magic_set *ms) } protected int -file_buffer(struct magic_set *ms, int fd, const char *inname, const void *buf, +file_buffer(struct magic_set *ms, php_stream *stream, const char *inname, const void *buf, size_t nb) { int m; @@ -166,7 +166,7 @@ file_buffer(struct magic_set *ms, int fd, const char *inname, const void *buf, return 1; } -#ifdef __EMX__ +#if defined(__EMX__) if ((ms->flags & MAGIC_NO_CHECK_APPTYPE) == 0 && inname) { switch (file_os2_apptype(ms, inname, buf, nb)) { case -1: @@ -179,31 +179,29 @@ file_buffer(struct magic_set *ms, int fd, const char *inname, const void *buf, } #endif +#if PHP_FILEINFO_UNCOMPRESS /* try compression stuff */ if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) != 0 || - (m = file_zmagic(ms, fd, inname, buf, nb)) == 0) { - /* Check if we have a tar file */ - if ((ms->flags & MAGIC_NO_CHECK_TAR) != 0 || - (m = file_is_tar(ms, buf, nb)) == 0) { - /* try tests in /etc/magic (or surrogate magic file) */ - if ((ms->flags & MAGIC_NO_CHECK_SOFT) != 0 || - (m = file_softmagic(ms, buf, nb, BINTEST)) == 0) { - /* try known keywords, check whether it is ASCII */ - if ((ms->flags & MAGIC_NO_CHECK_ASCII) != 0 || - (m = file_ascmagic(ms, buf, nb)) == 0) { - /* abandon hope, all ye who remain here */ - if ((!mime || (mime & MAGIC_MIME_TYPE)) && - file_printf(ms, mime ? "application/octet-stream" : - "data") == -1) - return -1; - m = 1; - } - } + (m = file_zmagic(ms, stream, inname, buf, nb)) == 0) +#endif + { + /* Check if we have a tar file */ + if ((ms->flags & MAGIC_NO_CHECK_TAR) != 0 || (m = file_is_tar(ms, buf, nb)) == 0) { + /* try tests in /etc/magic (or surrogate magic file) */ + if ((ms->flags & MAGIC_NO_CHECK_SOFT) != 0 || (m = file_softmagic(ms, buf, nb, BINTEST)) == 0) { + /* try known keywords, check whether it is ASCII */ + if ((ms->flags & MAGIC_NO_CHECK_ASCII) != 0 || (m = file_ascmagic(ms, buf, nb)) == 0) { + /* abandon hope, all ye who remain here */ + if ((!mime || (mime & MAGIC_MIME_TYPE)) && file_printf(ms, mime ? "application/octet-stream" : "data") == -1) { + return -1; + } + m = 1; + } + } } } #ifdef BUILTIN_ELF - if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && m == 1 && - nb > 5 && fd != -1) { + if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && m == 1 && nb > 5 && fd != -1) { /* * We matched something in the file, so this *might* * be an ELF file, and the file is at least 5 bytes @@ -212,7 +210,7 @@ file_buffer(struct magic_set *ms, int fd, const char *inname, const void *buf, * information from the ELF headers that cannot easily * be extracted with rules in the magic file. */ - (void)file_tryelf(ms, fd, buf, nb); + (void)file_tryelf(ms, stream, buf, nb); } #endif return m; diff --git a/ext/fileinfo/libmagic/magic.c b/ext/fileinfo/libmagic/magic.c index cf695ece75..bf3ccc42d6 100644 --- a/ext/fileinfo/libmagic/magic.c +++ b/ext/fileinfo/libmagic/magic.c @@ -30,10 +30,19 @@ #include #include +#ifdef PHP_WIN32 +#include "win32/unistd.h" +#else #include +#endif #include #include -#include /* for MAXPATHLEN */ +#ifdef PHP_WIN32 +# include "config.w32.h" +#else +# include "php_config.h" +#endif + #include #include /* for PIPE_BUF */ @@ -55,7 +64,9 @@ #include #endif -#include /* for byte swapping */ +#ifndef PHP_WIN32 +# include /* for byte swapping */ +#endif #include "patchlevel.h" @@ -72,6 +83,11 @@ FILE_RCSID("@(#)$File: magic.c,v 1.50 2008/02/19 00:58:59 rrt Exp $") #endif #endif +#ifdef PHP_WIN32 +# undef S_IFLNK +# undef S_IFIFO +#endif + #ifdef __EMX__ private char *apptypeName = NULL; protected int file_os2_apptype(struct magic_set *ms, const char *fn, @@ -82,7 +98,7 @@ private void free_mlist(struct mlist *); private void close_and_restore(const struct magic_set *, const char *, int, const struct stat *); private int info_from_stat(struct magic_set *, mode_t); -private const char *file_or_fd(struct magic_set *, const char *, int); +private const char *file_or_stream(struct magic_set *, const char *, php_stream *); #ifndef STDIN_FILENO #define STDIN_FILENO 0 @@ -204,9 +220,6 @@ private void close_and_restore(const struct magic_set *ms, const char *name, int fd, const struct stat *sb) { - if (fd == STDIN_FILENO) - return; - (void) close(fd); if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) { /* @@ -240,7 +253,7 @@ close_and_restore(const struct magic_set *ms, const char *name, int fd, public const char * magic_descriptor(struct magic_set *ms, int fd) { - return file_or_fd(ms, NULL, fd); + return file_or_stream(ms, NULL, NULL); } /* @@ -249,17 +262,28 @@ magic_descriptor(struct magic_set *ms, int fd) public const char * magic_file(struct magic_set *ms, const char *inname) { - return file_or_fd(ms, inname, STDIN_FILENO); + return file_or_stream(ms, inname, NULL); +} + +public const char * +magic_stream(struct magic_set *ms, php_stream *stream) +{ + return file_or_stream(ms, NULL, stream); } private const char * -file_or_fd(struct magic_set *ms, const char *inname, int fd) +file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) { int rv = -1; unsigned char *buf; struct stat sb; ssize_t nbytes = 0; /* number of bytes read from a datafile */ - int ispipe = 0; + int no_in_stream = 0; + TSRMLS_FETCH(); + + if (!inname && !stream) { + return NULL; + } /* * one extra for terminating '\0', and @@ -268,93 +292,66 @@ file_or_fd(struct magic_set *ms, const char *inname, int fd) #define SLOP (1 + sizeof(union VALUETYPE)) buf = emalloc(HOWMANY + SLOP); - if (file_reset(ms) == -1) + if (file_reset(ms) == -1) { goto done; + } - switch (file_fsmagic(ms, inname, &sb)) { - case -1: /* error */ - goto done; - case 0: /* nothing found */ - break; - default: /* matched it and printed type */ + switch (file_fsmagic(ms, inname, &sb, stream)) { + case -1: /* error */ + goto done; + case 0: /* nothing found */ + break; + default: /* matched it and printed type */ + rv = 0; + goto done; + } + + errno = 0; + + if (!stream && inname) { + no_in_stream = 1; +#if (PHP_MAJOR_VERSION < 6) + stream = php_stream_open_wrapper(inname, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); +#else + stream = php_stream_open_wrapper(inname, "rb", REPORT_ERRORS, NULL); +#endif + } + + if (!stream) { + fprintf(stderr, "couldn't open file\n"); + if (info_from_stat(ms, sb.st_mode) == -1) + goto done; rv = 0; goto done; } - if (inname == NULL) { - if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) - ispipe = 1; - } else { - int flags = O_RDONLY|O_BINARY; - - if (stat(inname, &sb) == 0 && S_ISFIFO(sb.st_mode)) { - flags |= O_NONBLOCK; - ispipe = 1; - } - - errno = 0; - if ((fd = open(inname, flags)) < 0) { -#ifdef __CYGWIN__ - /* FIXME: Do this with EXEEXT from autotools */ - char *tmp = alloca(strlen(inname) + 5); - (void)strcat(strcpy(tmp, inname), ".exe"); - if ((fd = open(tmp, flags)) < 0) { -#endif - fprintf(stderr, "couldn't open file\n"); - if (info_from_stat(ms, sb.st_mode) == -1) - goto done; - rv = 0; - goto done; -#ifdef __CYGWIN__ - } -#endif - } #ifdef O_NONBLOCK - if ((flags = fcntl(fd, F_GETFL)) != -1) { - flags &= ~O_NONBLOCK; - (void)fcntl(fd, F_SETFL, flags); - } +/* we should be already be in non blocking mode for network socket */ #endif - } /* * try looking at the first HOWMANY bytes */ - if (ispipe) { - ssize_t r = 0; - - while ((r = sread(fd, (void *)&buf[nbytes], - (size_t)(HOWMANY - nbytes), 1)) > 0) { - nbytes += r; - if (r < PIPE_BUF) break; - } - - if (nbytes == 0) { - /* We can not read it, but we were able to stat it. */ - if (info_from_stat(ms, sb.st_mode) == -1) - goto done; - rv = 0; - goto done; - } - - } else { - if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) { - file_error(ms, errno, "cannot read `%s'", inname); - goto done; - } + if ((nbytes = php_stream_read(stream, (char *)buf, HOWMANY)) < 0) { + file_error(ms, errno, "cannot read `%s'", inname); + goto done; } (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */ - if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1) + if (file_buffer(ms, stream, inname, buf, (size_t)nbytes) == -1) goto done; rv = 0; done: efree(buf); - close_and_restore(ms, inname, fd, &sb); + + if (no_in_stream && stream) { + php_stream_close(stream); + } + + close_and_restore(ms, inname, 0, &sb); return rv == 0 ? file_getbuffer(ms) : NULL; } - public const char * magic_buffer(struct magic_set *ms, const void *buf, size_t nb) { @@ -364,7 +361,7 @@ magic_buffer(struct magic_set *ms, const void *buf, size_t nb) * The main work is done here! * We have the file name and/or the data buffer to be identified. */ - if (file_buffer(ms, -1, NULL, buf, nb) == -1) { + if (file_buffer(ms, NULL, NULL, buf, nb) == -1) { return NULL; } return file_getbuffer(ms); diff --git a/ext/fileinfo/libmagic/magic.h b/ext/fileinfo/libmagic/magic.h index ecdd53c1f9..ef9db04da2 100644 --- a/ext/fileinfo/libmagic/magic.h +++ b/ext/fileinfo/libmagic/magic.h @@ -64,6 +64,7 @@ magic_t magic_open(int); void magic_close(magic_t); const char *magic_file(magic_t, const char *); +const char *magic_stream(magic_t, php_stream *); const char *magic_descriptor(magic_t, int); const char *magic_buffer(magic_t, const void *, size_t); diff --git a/ext/fileinfo/tests/finfo_file_001.phpt b/ext/fileinfo/tests/finfo_file_001.phpt index 1cffa3b2f1..72af3dbe4b 100644 --- a/ext/fileinfo/tests/finfo_file_001.phpt +++ b/ext/fileinfo/tests/finfo_file_001.phpt @@ -11,9 +11,16 @@ var_dump(finfo_file($fp, '.')); var_dump(finfo_file($fp, '&')); ?> ---EXPECT-- +--EXPECTF-- +Warning: finfo_file(): Empty filename or path in %s on line %d bool(false) + +Warning: finfo_file(): Empty filename or path in %s on line %d bool(false) + +Warning: finfo_file(): Empty filename or path in %s on line %d bool(false) string(9) "directory" + +Warning: finfo_file(): File or path not found '&' in %s on line %d bool(false) diff --git a/ext/fileinfo/tests/mime_content_type_001.phpt b/ext/fileinfo/tests/mime_content_type_001.phpt index dd6ce07657..5ea8ab8a6b 100644 --- a/ext/fileinfo/tests/mime_content_type_001.phpt +++ b/ext/fileinfo/tests/mime_content_type_001.phpt @@ -21,7 +21,7 @@ Warning: mime_content_type(): Can only process string or stream arguments in %s Warning: mime_content_type(): Can only process string or stream arguments in %s on line %d -Warning: mime_content_type(foo/inexistent): failed to open stream: No such file or directory in %s on line %d +Warning: mime_content_type(): File or path not found 'foo/inexistent' in %s on line %d Warning: mime_content_type(): Empty filename or path in %s on line %d -- 2.40.0