Improve SPL directory and stat() cache using zend_srting* instead of char*

This commit is contained in:
Dmitry Stogov 2021-02-26 02:28:46 +03:00
parent 5e8ae15c3d
commit 13e4ce386b
13 changed files with 276 additions and 288 deletions

View file

@ -1443,7 +1443,6 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
case IS_OBJECT: case IS_OBJECT:
if (instanceof_function(Z_OBJCE_P(value), spl_ce_SplFileInfo)) { if (instanceof_function(Z_OBJCE_P(value), spl_ce_SplFileInfo)) {
char *test = NULL; char *test = NULL;
zval dummy;
spl_filesystem_object *intern = (spl_filesystem_object*)((char*)Z_OBJ_P(value) - Z_OBJ_P(value)->handlers->offset); spl_filesystem_object *intern = (spl_filesystem_object*)((char*)Z_OBJ_P(value) - Z_OBJ_P(value)->handlers->offset);
if (!base_len) { if (!base_len) {
@ -1455,9 +1454,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
case SPL_FS_DIR: case SPL_FS_DIR:
test = spl_filesystem_object_get_path(intern, NULL); test = spl_filesystem_object_get_path(intern, NULL);
fname_len = spprintf(&fname, 0, "%s%c%s", test, DEFAULT_SLASH, intern->u.dir.entry.d_name); fname_len = spprintf(&fname, 0, "%s%c%s", test, DEFAULT_SLASH, intern->u.dir.entry.d_name);
php_stat(fname, fname_len, FS_IS_DIR, &dummy); if (php_stream_stat_path(fname, &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) {
if (Z_TYPE(dummy) == IS_TRUE) {
/* ignore directories */ /* ignore directories */
efree(fname); efree(fname);
return ZEND_HASH_APPLY_KEEP; return ZEND_HASH_APPLY_KEEP;
@ -1478,7 +1475,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
goto phar_spl_fileinfo; goto phar_spl_fileinfo;
case SPL_FS_INFO: case SPL_FS_INFO:
case SPL_FS_FILE: case SPL_FS_FILE:
fname = expand_filepath(intern->file_name, NULL); fname = expand_filepath(ZSTR_VAL(intern->file_name), NULL);
if (!fname) { if (!fname) {
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path"); zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path");
return ZEND_HASH_APPLY_STOP; return ZEND_HASH_APPLY_STOP;
@ -4644,11 +4641,11 @@ PHP_METHOD(PharFileInfo, chmod)
/* hackish cache in php_stat needs to be cleared */ /* hackish cache in php_stat needs to be cleared */
/* if this code fails to work, check main/streams/streams.c, _php_stream_stat_path */ /* if this code fails to work, check main/streams/streams.c, _php_stream_stat_path */
if (BG(CurrentLStatFile)) { if (BG(CurrentLStatFile)) {
efree(BG(CurrentLStatFile)); zend_string_release(BG(CurrentLStatFile));
} }
if (BG(CurrentStatFile)) { if (BG(CurrentStatFile)) {
efree(BG(CurrentStatFile)); zend_string_release(BG(CurrentStatFile));
} }
BG(CurrentLStatFile) = NULL; BG(CurrentLStatFile) = NULL;

View file

@ -114,18 +114,18 @@ static void spl_filesystem_object_free_storage(zend_object *object) /* {{{ */
zend_object_std_dtor(&intern->std); zend_object_std_dtor(&intern->std);
if (intern->_path) { if (intern->path) {
efree(intern->_path); zend_string_release(intern->path);
} }
if (intern->file_name) { if (intern->file_name) {
efree(intern->file_name); zend_string_release(intern->file_name);
} }
switch(intern->type) { switch(intern->type) {
case SPL_FS_INFO: case SPL_FS_INFO:
break; break;
case SPL_FS_DIR: case SPL_FS_DIR:
if (intern->u.dir.sub_path) { if (intern->u.dir.sub_path) {
efree(intern->u.dir.sub_path); zend_string_release(intern->u.dir.sub_path);
} }
break; break;
case SPL_FS_FILE: case SPL_FS_FILE:
@ -133,7 +133,7 @@ static void spl_filesystem_object_free_storage(zend_object *object) /* {{{ */
efree(intern->u.file.open_mode); efree(intern->u.file.open_mode);
} }
if (intern->orig_path) { if (intern->orig_path) {
efree(intern->orig_path); zend_string_release(intern->orig_path);
} }
spl_filesystem_file_free_line(intern); spl_filesystem_file_free_line(intern);
break; break;
@ -195,9 +195,9 @@ PHPAPI char* spl_filesystem_object_get_path(spl_filesystem_object *intern, size_
} }
#endif #endif
if (len) { if (len) {
*len = intern->_path_len; *len = intern->path ? ZSTR_LEN(intern->path) : 0;
} }
return intern->_path; return intern->path ? ZSTR_VAL(intern->path) : NULL;
} /* }}} */ } /* }}} */
static inline int spl_filesystem_object_get_file_name(spl_filesystem_object *intern) /* {{{ */ static inline int spl_filesystem_object_get_file_name(spl_filesystem_object *intern) /* {{{ */
@ -217,15 +217,15 @@ static inline int spl_filesystem_object_get_file_name(spl_filesystem_object *int
size_t path_len = 0; size_t path_len = 0;
char *path = spl_filesystem_object_get_path(intern, &path_len); char *path = spl_filesystem_object_get_path(intern, &path_len);
if (intern->file_name) { if (intern->file_name) {
efree(intern->file_name); zend_string_release(intern->file_name);
} }
/* if there is parent path, amend it, otherwise just use the given path as is */ /* if there is parent path, amend it, otherwise just use the given path as is */
if (path_len == 0) { if (path_len == 0) {
intern->file_name_len = spprintf( intern->file_name = zend_strpprintf(
&intern->file_name, 0, "%s", intern->u.dir.entry.d_name); 0, "%s", intern->u.dir.entry.d_name);
} else { } else {
intern->file_name_len = spprintf( intern->file_name = zend_strpprintf(
&intern->file_name, 0, "%s%c%s", path, slash, intern->u.dir.entry.d_name); 0, "%s%c%s", path, slash, intern->u.dir.entry.d_name);
} }
} }
break; break;
@ -254,18 +254,17 @@ static inline int spl_filesystem_is_dot(const char * d_name) /* {{{ */
/* {{{ spl_filesystem_dir_open */ /* {{{ spl_filesystem_dir_open */
/* open a directory resource */ /* open a directory resource */
static void spl_filesystem_dir_open(spl_filesystem_object* intern, char *path) static void spl_filesystem_dir_open(spl_filesystem_object* intern, zend_string *path)
{ {
int skip_dots = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_SKIPDOTS); int skip_dots = SPL_HAS_FLAG(intern->flags, SPL_FILE_DIR_SKIPDOTS);
intern->type = SPL_FS_DIR; intern->type = SPL_FS_DIR;
intern->_path_len = strlen(path); intern->u.dir.dirp = php_stream_opendir(ZSTR_VAL(path), REPORT_ERRORS, FG(default_context));
intern->u.dir.dirp = php_stream_opendir(path, REPORT_ERRORS, FG(default_context));
if (intern->_path_len > 1 && IS_SLASH_AT(path, intern->_path_len-1)) { if (ZSTR_LEN(path) > 1 && IS_SLASH_AT(ZSTR_VAL(path), ZSTR_LEN(path)-1)) {
intern->_path = estrndup(path, --intern->_path_len); intern->path = zend_string_init(ZSTR_VAL(path), ZSTR_LEN(path)-1, 0);
} else { } else {
intern->_path = estrndup(path, intern->_path_len); intern->path = zend_string_copy(path);
} }
intern->u.dir.index = 0; intern->u.dir.index = 0;
@ -274,7 +273,7 @@ static void spl_filesystem_dir_open(spl_filesystem_object* intern, char *path)
if (!EG(exception)) { if (!EG(exception)) {
/* open failed w/out notice (turned to exception due to EH_THROW) */ /* open failed w/out notice (turned to exception due to EH_THROW) */
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
"Failed to open directory \"%s\"", path); "Failed to open directory \"%s\"", ZSTR_VAL(path));
} }
} else { } else {
do { do {
@ -289,8 +288,7 @@ static int spl_filesystem_file_open(spl_filesystem_object *intern, int use_inclu
zval tmp; zval tmp;
intern->type = SPL_FS_FILE; intern->type = SPL_FS_FILE;
php_stat(intern->file_name, FS_IS_DIR, &tmp);
php_stat(intern->file_name, intern->file_name_len, FS_IS_DIR, &tmp);
if (Z_TYPE(tmp) == IS_TRUE) { if (Z_TYPE(tmp) == IS_TRUE) {
intern->u.file.open_mode = NULL; intern->u.file.open_mode = NULL;
intern->file_name = NULL; intern->file_name = NULL;
@ -299,11 +297,11 @@ static int spl_filesystem_file_open(spl_filesystem_object *intern, int use_inclu
} }
intern->u.file.context = php_stream_context_from_zval(intern->u.file.zcontext, 0); intern->u.file.context = php_stream_context_from_zval(intern->u.file.zcontext, 0);
intern->u.file.stream = php_stream_open_wrapper_ex(intern->file_name, intern->u.file.open_mode, (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, intern->u.file.context); intern->u.file.stream = php_stream_open_wrapper_ex(ZSTR_VAL(intern->file_name), intern->u.file.open_mode, (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, intern->u.file.context);
if (!intern->file_name_len || !intern->u.file.stream) { if (!ZSTR_LEN(intern->file_name) || !intern->u.file.stream) {
if (!EG(exception)) { if (!EG(exception)) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot open file '%s'", intern->file_name_len ? intern->file_name : ""); zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot open file '%s'", ZSTR_VAL(intern->file_name));
} }
intern->file_name = NULL; /* until here it is not a copy */ intern->file_name = NULL; /* until here it is not a copy */
intern->u.file.open_mode = NULL; intern->u.file.open_mode = NULL;
@ -317,13 +315,14 @@ static int spl_filesystem_file_open(spl_filesystem_object *intern, int use_inclu
} }
*/ */
if (intern->file_name_len > 1 && IS_SLASH_AT(intern->file_name, intern->file_name_len-1)) { if (ZSTR_LEN(intern->file_name) > 1 && IS_SLASH_AT(ZSTR_VAL(intern->file_name), ZSTR_LEN(intern->file_name)-1)) {
intern->file_name_len--; intern->file_name = zend_string_init(ZSTR_VAL(intern->file_name), ZSTR_LEN(intern->file_name)-1, 0);
} else {
intern->file_name = zend_string_copy(intern->file_name);
} }
intern->orig_path = estrndup(intern->u.file.stream->orig_path, strlen(intern->u.file.stream->orig_path)); intern->orig_path = zend_string_init(intern->u.file.stream->orig_path, strlen(intern->u.file.stream->orig_path), 0);
intern->file_name = estrndup(intern->file_name, intern->file_name_len);
intern->u.file.open_mode = estrndup(intern->u.file.open_mode, intern->u.file.open_mode_len); intern->u.file.open_mode = estrndup(intern->u.file.open_mode, intern->u.file.open_mode_len);
/* avoid reference counting in debug mode, thus do it manually */ /* avoid reference counting in debug mode, thus do it manually */
@ -363,13 +362,11 @@ static zend_object *spl_filesystem_object_clone(zend_object *old_object)
switch (source->type) { switch (source->type) {
case SPL_FS_INFO: case SPL_FS_INFO:
intern->_path_len = source->_path_len; intern->path = zend_string_copy(source->path);
intern->_path = estrndup(source->_path, source->_path_len); intern->file_name = zend_string_copy(source->file_name);
intern->file_name_len = source->file_name_len;
intern->file_name = estrndup(source->file_name, intern->file_name_len);
break; break;
case SPL_FS_DIR: case SPL_FS_DIR:
spl_filesystem_dir_open(intern, source->_path); spl_filesystem_dir_open(intern, source->path);
/* read until we hit the position in which we were before */ /* read until we hit the position in which we were before */
skip_dots = SPL_HAS_FLAG(source->flags, SPL_FILE_DIR_SKIPDOTS); skip_dots = SPL_HAS_FLAG(source->flags, SPL_FILE_DIR_SKIPDOTS);
for(index = 0; index < source->u.dir.index; ++index) { for(index = 0; index < source->u.dir.index; ++index) {
@ -398,58 +395,43 @@ static zend_object *spl_filesystem_object_clone(zend_object *old_object)
} }
/* }}} */ /* }}} */
void spl_filesystem_info_set_filename(spl_filesystem_object *intern, char *path, size_t len, size_t use_copy) /* {{{ */ static void spl_filesystem_info_set_filename(spl_filesystem_object *intern, zend_string *path) /* {{{ */
{ {
char *p1, *p2; size_t path_len;
if (intern->file_name) { if (intern->file_name) {
efree(intern->file_name); zend_string_release(intern->file_name);
} }
intern->file_name = use_copy ? estrndup(path, len) : path; path_len = ZSTR_LEN(path);
intern->file_name_len = len; if (path_len > 1 && IS_SLASH_AT(ZSTR_VAL(path), path_len-1)) {
path_len--;
while (intern->file_name_len > 1 && IS_SLASH_AT(intern->file_name, intern->file_name_len-1)) { intern->file_name = zend_string_init(ZSTR_VAL(path), path_len, 0);
intern->file_name[intern->file_name_len-1] = 0;
intern->file_name_len--;
}
p1 = strrchr(intern->file_name, '/');
#ifdef PHP_WIN32
p2 = strrchr(intern->file_name, '\\');
#else
p2 = 0;
#endif
if (p1 || p2) {
intern->_path_len = ((p1 > p2 ? p1 : p2) - intern->file_name);
} else { } else {
intern->_path_len = 0; intern->file_name = zend_string_copy(path);
}
while (path_len > 1 && !IS_SLASH_AT(ZSTR_VAL(path), path_len-1)) {
path_len--;
}
if (path_len) {
path_len--;
} }
if (intern->_path) { if (intern->path) {
efree(intern->_path); zend_string_release(intern->path);
} }
intern->_path = estrndup(path, intern->_path_len); intern->path = zend_string_init(ZSTR_VAL(path), path_len, 0);
} /* }}} */ } /* }}} */
static spl_filesystem_object *spl_filesystem_object_create_info(spl_filesystem_object *source, char *file_path, size_t file_path_len, int use_copy, zend_class_entry *ce, zval *return_value) /* {{{ */ static spl_filesystem_object *spl_filesystem_object_create_info(spl_filesystem_object *source, zend_string *file_path, zend_class_entry *ce, zval *return_value) /* {{{ */
{ {
spl_filesystem_object *intern; spl_filesystem_object *intern;
zval arg1; zval arg1;
zend_error_handling error_handling; zend_error_handling error_handling;
if (!file_path || !file_path_len) { if (!file_path || !ZSTR_LEN(file_path)) {
#ifdef PHP_WIN32 #ifdef PHP_WIN32
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot create SplFileInfo for empty path"); zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot create SplFileInfo for empty path");
if (file_path && !use_copy) {
efree(file_path);
}
#else
if (file_path && !use_copy) {
efree(file_path);
}
file_path_len = 1;
file_path = "/";
#endif #endif
return NULL; return NULL;
} }
@ -462,11 +444,11 @@ static spl_filesystem_object *spl_filesystem_object_create_info(spl_filesystem_o
RETVAL_OBJ(&intern->std); RETVAL_OBJ(&intern->std);
if (ce->constructor->common.scope != spl_ce_SplFileInfo) { if (ce->constructor->common.scope != spl_ce_SplFileInfo) {
ZVAL_STRINGL(&arg1, file_path, file_path_len); ZVAL_STR_COPY(&arg1, file_path);
zend_call_method_with_1_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1); zend_call_method_with_1_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1);
zval_ptr_dtor(&arg1); zval_ptr_dtor(&arg1);
} else { } else {
spl_filesystem_info_set_filename(intern, file_path, file_path_len, use_copy); spl_filesystem_info_set_filename(intern, file_path);
} }
zend_restore_error_handling(&error_handling); zend_restore_error_handling(&error_handling);
@ -503,14 +485,20 @@ static spl_filesystem_object *spl_filesystem_object_create_type(int num_args, sp
} }
if (ce->constructor->common.scope != spl_ce_SplFileInfo) { if (ce->constructor->common.scope != spl_ce_SplFileInfo) {
ZVAL_STRINGL(&arg1, source->file_name, source->file_name_len); ZVAL_STR_COPY(&arg1, source->file_name);
zend_call_method_with_1_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1); zend_call_method_with_1_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1);
zval_ptr_dtor(&arg1); zval_ptr_dtor(&arg1);
} else { } else {
intern->file_name = estrndup(source->file_name, source->file_name_len); char *path;
intern->file_name_len = source->file_name_len; size_t path_len;
intern->_path = spl_filesystem_object_get_path(source, &intern->_path_len);
intern->_path = estrndup(intern->_path, intern->_path_len); intern->file_name = zend_string_copy(source->file_name);
path = spl_filesystem_object_get_path(source, &path_len);
if (source->path && ZSTR_VAL(source->path) == path) {
intern->path = zend_string_copy(source->path);
} else {
intern->path = zend_string_init(path, path_len, 0);
}
} }
break; break;
case SPL_FS_FILE: case SPL_FS_FILE:
@ -535,16 +523,22 @@ static spl_filesystem_object *spl_filesystem_object_create_type(int num_args, sp
} }
if (ce->constructor->common.scope != spl_ce_SplFileObject) { if (ce->constructor->common.scope != spl_ce_SplFileObject) {
ZVAL_STRINGL(&arg1, source->file_name, source->file_name_len); ZVAL_STR_COPY(&arg1, source->file_name);
ZVAL_STRINGL(&arg2, open_mode, open_mode_len); ZVAL_STRINGL(&arg2, open_mode, open_mode_len);
zend_call_method_with_2_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1, &arg2); zend_call_method_with_2_params(Z_OBJ_P(return_value), ce, &ce->constructor, "__construct", NULL, &arg1, &arg2);
zval_ptr_dtor(&arg1); zval_ptr_dtor(&arg1);
zval_ptr_dtor(&arg2); zval_ptr_dtor(&arg2);
} else { } else {
char *path;
size_t path_len;
intern->file_name = source->file_name; intern->file_name = source->file_name;
intern->file_name_len = source->file_name_len; path = spl_filesystem_object_get_path(source, &path_len);
intern->_path = spl_filesystem_object_get_path(source, &intern->_path_len); if (source->path && ZSTR_VAL(source->path) == path) {
intern->_path = estrndup(intern->_path, intern->_path_len); intern->path = zend_string_copy(source->path);
} else {
intern->path = zend_string_init(path, path_len, 0);
}
intern->u.file.open_mode = open_mode; intern->u.file.open_mode = open_mode;
intern->u.file.open_mode_len = open_mode_len; intern->u.file.open_mode_len = open_mode_len;
@ -574,20 +568,17 @@ static int spl_filesystem_is_invalid_or_dot(const char * d_name) /* {{{ */
} }
/* }}} */ /* }}} */
static char *spl_filesystem_object_get_pathname(spl_filesystem_object *intern, size_t *len) { /* {{{ */ static zend_string *spl_filesystem_object_get_pathname(spl_filesystem_object *intern) { /* {{{ */
switch (intern->type) { switch (intern->type) {
case SPL_FS_INFO: case SPL_FS_INFO:
case SPL_FS_FILE: case SPL_FS_FILE:
*len = intern->file_name_len;
return intern->file_name; return intern->file_name;
case SPL_FS_DIR: case SPL_FS_DIR:
if (intern->u.dir.entry.d_name[0]) { if (intern->u.dir.entry.d_name[0]) {
spl_filesystem_object_get_file_name(intern); spl_filesystem_object_get_file_name(intern);
*len = intern->file_name_len;
return intern->file_name; return intern->file_name;
} }
} }
*len = 0;
return NULL; return NULL;
} }
/* }}} */ /* }}} */
@ -598,8 +589,7 @@ static inline HashTable *spl_filesystem_object_get_debug_info(zend_object *objec
zval tmp; zval tmp;
HashTable *rv; HashTable *rv;
zend_string *pnstr; zend_string *pnstr;
char *path; zend_string *path;
size_t path_len;
char stmp[2]; char stmp[2];
if (!intern->std.properties) { if (!intern->std.properties) {
@ -609,19 +599,25 @@ static inline HashTable *spl_filesystem_object_get_debug_info(zend_object *objec
rv = zend_array_dup(intern->std.properties); rv = zend_array_dup(intern->std.properties);
pnstr = spl_gen_private_prop_name(spl_ce_SplFileInfo, "pathName", sizeof("pathName")-1); pnstr = spl_gen_private_prop_name(spl_ce_SplFileInfo, "pathName", sizeof("pathName")-1);
path = spl_filesystem_object_get_pathname(intern, &path_len); path = spl_filesystem_object_get_pathname(intern);
ZVAL_STRINGL(&tmp, path ? path : "", path_len); if (path) {
ZVAL_STR_COPY(&tmp, path);
} else {
ZVAL_EMPTY_STRING(&tmp);
}
zend_symtable_update(rv, pnstr, &tmp); zend_symtable_update(rv, pnstr, &tmp);
zend_string_release_ex(pnstr, 0); zend_string_release_ex(pnstr, 0);
if (intern->file_name) { if (intern->file_name) {
size_t path_len;
pnstr = spl_gen_private_prop_name(spl_ce_SplFileInfo, "fileName", sizeof("fileName")-1); pnstr = spl_gen_private_prop_name(spl_ce_SplFileInfo, "fileName", sizeof("fileName")-1);
spl_filesystem_object_get_path(intern, &path_len); spl_filesystem_object_get_path(intern, &path_len);
if (path_len && path_len < intern->file_name_len) { if (path_len && path_len < ZSTR_LEN(intern->file_name)) {
ZVAL_STRINGL(&tmp, intern->file_name + path_len + 1, intern->file_name_len - (path_len + 1)); ZVAL_STRINGL(&tmp, ZSTR_VAL(intern->file_name) + path_len + 1, ZSTR_LEN(intern->file_name) - (path_len + 1));
} else { } else {
ZVAL_STRINGL(&tmp, intern->file_name, intern->file_name_len); ZVAL_STR_COPY(&tmp, intern->file_name);
} }
zend_symtable_update(rv, pnstr, &tmp); zend_symtable_update(rv, pnstr, &tmp);
zend_string_release_ex(pnstr, 0); zend_string_release_ex(pnstr, 0);
@ -630,7 +626,7 @@ static inline HashTable *spl_filesystem_object_get_debug_info(zend_object *objec
#ifdef HAVE_GLOB #ifdef HAVE_GLOB
pnstr = spl_gen_private_prop_name(spl_ce_DirectoryIterator, "glob", sizeof("glob")-1); pnstr = spl_gen_private_prop_name(spl_ce_DirectoryIterator, "glob", sizeof("glob")-1);
if (php_stream_is(intern->u.dir.dirp ,&php_glob_stream_ops)) { if (php_stream_is(intern->u.dir.dirp ,&php_glob_stream_ops)) {
ZVAL_STRINGL(&tmp, intern->_path, intern->_path_len); ZVAL_STR_COPY(&tmp, intern->path);
} else { } else {
ZVAL_FALSE(&tmp); ZVAL_FALSE(&tmp);
} }
@ -639,7 +635,7 @@ static inline HashTable *spl_filesystem_object_get_debug_info(zend_object *objec
#endif #endif
pnstr = spl_gen_private_prop_name(spl_ce_RecursiveDirectoryIterator, "subPathName", sizeof("subPathName")-1); pnstr = spl_gen_private_prop_name(spl_ce_RecursiveDirectoryIterator, "subPathName", sizeof("subPathName")-1);
if (intern->u.dir.sub_path) { if (intern->u.dir.sub_path) {
ZVAL_STRINGL(&tmp, intern->u.dir.sub_path, intern->u.dir.sub_path_len); ZVAL_STR_COPY(&tmp, intern->u.dir.sub_path);
} else { } else {
ZVAL_EMPTY_STRING(&tmp); ZVAL_EMPTY_STRING(&tmp);
} }
@ -690,18 +686,17 @@ zend_function *spl_filesystem_object_get_method_check(zend_object **object, zend
void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_long ctor_flags) /* {{{ */ void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_long ctor_flags) /* {{{ */
{ {
spl_filesystem_object *intern; spl_filesystem_object *intern;
char *path; zend_string *path;
int parsed; int parsed;
size_t len;
zend_long flags; zend_long flags;
zend_error_handling error_handling; zend_error_handling error_handling;
if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_FLAGS)) { if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_FLAGS)) {
flags = SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_FILEINFO; flags = SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_FILEINFO;
parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &path, &len, &flags); parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "P|l", &path, &flags);
} else { } else {
flags = SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_SELF; flags = SPL_FILE_DIR_KEY_AS_PATHNAME|SPL_FILE_DIR_CURRENT_AS_SELF;
parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "p", &path, &len); parsed = zend_parse_parameters(ZEND_NUM_ARGS(), "P", &path);
} }
if (SPL_HAS_FLAG(ctor_flags, SPL_FILE_DIR_SKIPDOTS)) { if (SPL_HAS_FLAG(ctor_flags, SPL_FILE_DIR_SKIPDOTS)) {
flags |= SPL_FILE_DIR_SKIPDOTS; flags |= SPL_FILE_DIR_SKIPDOTS;
@ -713,13 +708,13 @@ void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_long cto
RETURN_THROWS(); RETURN_THROWS();
} }
if (len == 0) { if (ZSTR_LEN(path) == 0) {
zend_argument_value_error(1, "cannot be empty"); zend_argument_value_error(1, "cannot be empty");
RETURN_THROWS(); RETURN_THROWS();
} }
intern = Z_SPLFILESYSTEM_P(ZEND_THIS); intern = Z_SPLFILESYSTEM_P(ZEND_THIS);
if (intern->_path) { if (intern->path) {
/* object is already initialized */ /* object is already initialized */
zend_throw_error(NULL, "Directory object is already initialized"); zend_throw_error(NULL, "Directory object is already initialized");
RETURN_THROWS(); RETURN_THROWS();
@ -728,10 +723,10 @@ void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_long cto
zend_replace_error_handling(EH_THROW, spl_ce_UnexpectedValueException, &error_handling); zend_replace_error_handling(EH_THROW, spl_ce_UnexpectedValueException, &error_handling);
#ifdef HAVE_GLOB #ifdef HAVE_GLOB
if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_GLOB) && strstr(path, "glob://") != path) { if (SPL_HAS_FLAG(ctor_flags, DIT_CTOR_GLOB) && memcmp(ZSTR_VAL(path), "glob://", sizeof("glob://")-1) != 0) {
spprintf(&path, 0, "glob://%s", path); path = zend_strpprintf(0, "glob://%s", ZSTR_VAL(path));
spl_filesystem_dir_open(intern, path); spl_filesystem_dir_open(intern, path);
efree(path); zend_string_release(path);
} else } else
#endif #endif
{ {
@ -811,7 +806,7 @@ PHP_METHOD(DirectoryIterator, next)
spl_filesystem_dir_read(intern); spl_filesystem_dir_read(intern);
} while (skip_dots && spl_filesystem_is_dot(intern->u.dir.entry.d_name)); } while (skip_dots && spl_filesystem_is_dot(intern->u.dir.entry.d_name));
if (intern->file_name) { if (intern->file_name) {
efree(intern->file_name); zend_string_release(intern->file_name);
intern->file_name = NULL; intern->file_name = NULL;
} }
} }
@ -896,10 +891,10 @@ PHP_METHOD(SplFileInfo, getFilename)
spl_filesystem_object_get_path(intern, &path_len); spl_filesystem_object_get_path(intern, &path_len);
if (path_len && path_len < intern->file_name_len) { if (path_len && path_len < ZSTR_LEN(intern->file_name)) {
RETURN_STRINGL(intern->file_name + path_len + 1, intern->file_name_len - (path_len + 1)); RETURN_STRINGL(ZSTR_VAL(intern->file_name) + path_len + 1, ZSTR_LEN(intern->file_name) - (path_len + 1));
} else { } else {
RETURN_STRINGL(intern->file_name, intern->file_name_len); RETURN_STR_COPY(intern->file_name);
} }
} }
/* }}} */ /* }}} */
@ -939,12 +934,12 @@ PHP_METHOD(SplFileInfo, getExtension)
spl_filesystem_object_get_path(intern, &path_len); spl_filesystem_object_get_path(intern, &path_len);
if (path_len && path_len < intern->file_name_len) { if (path_len && path_len < ZSTR_LEN(intern->file_name)) {
fname = intern->file_name + path_len + 1; fname = ZSTR_VAL(intern->file_name) + path_len + 1;
flen = intern->file_name_len - (path_len + 1); flen = ZSTR_LEN(intern->file_name) - (path_len + 1);
} else { } else {
fname = intern->file_name; fname = ZSTR_VAL(intern->file_name);
flen = intern->file_name_len; flen = ZSTR_LEN(intern->file_name);
} }
ret = php_basename(fname, flen, NULL, 0); ret = php_basename(fname, flen, NULL, 0);
@ -1007,12 +1002,12 @@ PHP_METHOD(SplFileInfo, getBasename)
spl_filesystem_object_get_path(intern, &path_len); spl_filesystem_object_get_path(intern, &path_len);
if (path_len && path_len < intern->file_name_len) { if (path_len && path_len < ZSTR_LEN(intern->file_name)) {
fname = intern->file_name + path_len + 1; fname = ZSTR_VAL(intern->file_name) + path_len + 1;
flen = intern->file_name_len - (path_len + 1); flen = ZSTR_LEN(intern->file_name) - (path_len + 1);
} else { } else {
fname = intern->file_name; fname = ZSTR_VAL(intern->file_name);
flen = intern->file_name_len; flen = ZSTR_LEN(intern->file_name);
} }
RETURN_STR(php_basename(fname, flen, suffix, slen)); RETURN_STR(php_basename(fname, flen, suffix, slen));
@ -1041,15 +1036,14 @@ PHP_METHOD(DirectoryIterator, getBasename)
PHP_METHOD(SplFileInfo, getPathname) PHP_METHOD(SplFileInfo, getPathname)
{ {
spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS); spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS);
char *path; zend_string *path;
size_t path_len;
if (zend_parse_parameters_none() == FAILURE) { if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS(); RETURN_THROWS();
} }
path = spl_filesystem_object_get_pathname(intern, &path_len); path = spl_filesystem_object_get_pathname(intern);
if (path != NULL) { if (path) {
RETURN_STRINGL(path, path_len); RETURN_STR_COPY(path);
} else { } else {
RETURN_EMPTY_STRING(); RETURN_EMPTY_STRING();
} }
@ -1071,7 +1065,7 @@ PHP_METHOD(FilesystemIterator, key)
if (spl_filesystem_object_get_file_name(intern) != SUCCESS) { if (spl_filesystem_object_get_file_name(intern) != SUCCESS) {
RETURN_THROWS(); RETURN_THROWS();
} }
RETURN_STRINGL(intern->file_name, intern->file_name_len); RETURN_STR_COPY(intern->file_name);
} }
} }
/* }}} */ /* }}} */
@ -1089,7 +1083,7 @@ PHP_METHOD(FilesystemIterator, current)
if (spl_filesystem_object_get_file_name(intern) != SUCCESS) { if (spl_filesystem_object_get_file_name(intern) != SUCCESS) {
RETURN_THROWS(); RETURN_THROWS();
} }
RETURN_STRINGL(intern->file_name, intern->file_name_len); RETURN_STR_COPY(intern->file_name);
} else if (SPL_FILE_DIR_CURRENT(intern, SPL_FILE_DIR_CURRENT_AS_FILEINFO)) { } else if (SPL_FILE_DIR_CURRENT(intern, SPL_FILE_DIR_CURRENT_AS_FILEINFO)) {
if (spl_filesystem_object_get_file_name(intern) != SUCCESS) { if (spl_filesystem_object_get_file_name(intern) != SUCCESS) {
RETURN_THROWS(); RETURN_THROWS();
@ -1121,16 +1115,15 @@ PHP_METHOD(DirectoryIterator, isDot)
PHP_METHOD(SplFileInfo, __construct) PHP_METHOD(SplFileInfo, __construct)
{ {
spl_filesystem_object *intern; spl_filesystem_object *intern;
char *path; zend_string *path;
size_t len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &path, &len) == FAILURE) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &path) == FAILURE) {
RETURN_THROWS(); RETURN_THROWS();
} }
intern = Z_SPLFILESYSTEM_P(ZEND_THIS); intern = Z_SPLFILESYSTEM_P(ZEND_THIS);
spl_filesystem_info_set_filename(intern, path, len, 1); spl_filesystem_info_set_filename(intern, path);
/* intern->type = SPL_FS_INFO; already set */ /* intern->type = SPL_FS_INFO; already set */
} }
@ -1149,7 +1142,7 @@ PHP_METHOD(SplFileInfo, func_name) \
RETURN_THROWS(); \ RETURN_THROWS(); \
} \ } \
zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling);\ zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling);\
php_stat(intern->file_name, intern->file_name_len, func_num, return_value); \ php_stat(intern->file_name, func_num, return_value); \
zend_restore_error_handling(&error_handling); \ zend_restore_error_handling(&error_handling); \
} }
/* }}} */ /* }}} */
@ -1239,23 +1232,23 @@ PHP_METHOD(SplFileInfo, getLinkTarget)
zend_value_error("Filename cannot be empty"); zend_value_error("Filename cannot be empty");
RETURN_THROWS(); RETURN_THROWS();
} }
if (!IS_ABSOLUTE_PATH(intern->file_name, intern->file_name_len)) { if (!IS_ABSOLUTE_PATH(ZSTR_VAL(intern->file_name), ZSTR_LEN(intern->file_name))) {
char expanded_path[MAXPATHLEN]; char expanded_path[MAXPATHLEN];
if (!expand_filepath_with_mode(intern->file_name, expanded_path, NULL, 0, CWD_EXPAND )) { if (!expand_filepath_with_mode(ZSTR_VAL(intern->file_name), expanded_path, NULL, 0, CWD_EXPAND )) {
zend_restore_error_handling(&error_handling); zend_restore_error_handling(&error_handling);
php_error_docref(NULL, E_WARNING, "No such file or directory"); php_error_docref(NULL, E_WARNING, "No such file or directory");
RETURN_FALSE; RETURN_FALSE;
} }
ret = php_sys_readlink(expanded_path, buff, MAXPATHLEN - 1); ret = php_sys_readlink(expanded_path, buff, MAXPATHLEN - 1);
} else { } else {
ret = php_sys_readlink(intern->file_name, buff, MAXPATHLEN-1); ret = php_sys_readlink(ZSTR_VAL(intern->file_name), buff, MAXPATHLEN-1);
} }
#else #else
ret = -1; /* always fail if not implemented */ ret = -1; /* always fail if not implemented */
#endif #endif
if (ret == -1) { if (ret == -1) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Unable to read link %s, error: %s", intern->file_name, strerror(errno)); zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Unable to read link %s, error: %s", ZSTR_VAL(intern->file_name), strerror(errno));
RETVAL_FALSE; RETVAL_FALSE;
} else { } else {
/* Append NULL to the end of the string */ /* Append NULL to the end of the string */
@ -1290,9 +1283,9 @@ PHP_METHOD(SplFileInfo, getRealPath)
} }
if (intern->orig_path) { if (intern->orig_path) {
filename = intern->orig_path; filename = ZSTR_VAL(intern->orig_path);
} else { } else {
filename = intern->file_name; filename = intern->file_name ? ZSTR_VAL(intern->file_name) : NULL;
} }
@ -1367,19 +1360,18 @@ PHP_METHOD(SplFileInfo, getPathInfo)
{ {
spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS); spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS);
zend_class_entry *ce = intern->info_class; zend_class_entry *ce = intern->info_class;
size_t path_len; zend_string *path;
char *path;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|C!", &ce) == FAILURE) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "|C!", &ce) == FAILURE) {
RETURN_THROWS(); RETURN_THROWS();
} }
path = spl_filesystem_object_get_pathname(intern, &path_len); path = spl_filesystem_object_get_pathname(intern);
if (path) { if (path && ZSTR_LEN(path)) {
char *dpath = estrndup(path, path_len); zend_string *dpath = zend_string_init(ZSTR_VAL(path), ZSTR_LEN(path), 0);
path_len = php_dirname(dpath, path_len); ZSTR_LEN(dpath) = php_dirname(ZSTR_VAL(dpath), ZSTR_LEN(path));
spl_filesystem_object_create_info(intern, dpath, path_len, 1, ce, return_value); spl_filesystem_object_create_info(intern, dpath, ce, return_value);
efree(dpath); zend_string_release(dpath);
} }
} }
/* }}} */ /* }}} */
@ -1470,12 +1462,12 @@ PHP_METHOD(RecursiveDirectoryIterator, hasChildren)
RETURN_THROWS(); RETURN_THROWS();
} }
if (!allow_links && !(intern->flags & SPL_FILE_DIR_FOLLOW_SYMLINKS)) { if (!allow_links && !(intern->flags & SPL_FILE_DIR_FOLLOW_SYMLINKS)) {
php_stat(intern->file_name, intern->file_name_len, FS_IS_LINK, return_value); php_stat(intern->file_name, FS_IS_LINK, return_value);
if (zend_is_true(return_value)) { if (zend_is_true(return_value)) {
RETURN_FALSE; RETURN_FALSE;
} }
} }
php_stat(intern->file_name, intern->file_name_len, FS_IS_DIR, return_value); php_stat(intern->file_name, FS_IS_DIR, return_value);
} }
} }
/* }}} */ /* }}} */
@ -1497,17 +1489,16 @@ PHP_METHOD(RecursiveDirectoryIterator, getChildren)
} }
ZVAL_LONG(&zflags, intern->flags); ZVAL_LONG(&zflags, intern->flags);
ZVAL_STRINGL(&zpath, intern->file_name, intern->file_name_len); ZVAL_STR_COPY(&zpath, intern->file_name);
spl_instantiate_arg_ex2(Z_OBJCE_P(ZEND_THIS), return_value, &zpath, &zflags); spl_instantiate_arg_ex2(Z_OBJCE_P(ZEND_THIS), return_value, &zpath, &zflags);
zval_ptr_dtor(&zpath); zval_ptr_dtor(&zpath);
subdir = Z_SPLFILESYSTEM_P(return_value); subdir = Z_SPLFILESYSTEM_P(return_value);
if (subdir) { if (subdir) {
if (intern->u.dir.sub_path && intern->u.dir.sub_path[0]) { if (intern->u.dir.sub_path && ZSTR_LEN(intern->u.dir.sub_path)) {
subdir->u.dir.sub_path_len = spprintf(&subdir->u.dir.sub_path, 0, "%s%c%s", intern->u.dir.sub_path, slash, intern->u.dir.entry.d_name); subdir->u.dir.sub_path = zend_strpprintf(0, "%s%c%s", ZSTR_VAL(intern->u.dir.sub_path), slash, intern->u.dir.entry.d_name);
} else { } else {
subdir->u.dir.sub_path_len = strlen(intern->u.dir.entry.d_name); subdir->u.dir.sub_path = zend_string_init(intern->u.dir.entry.d_name, strlen(intern->u.dir.entry.d_name), 0);
subdir->u.dir.sub_path = estrndup(intern->u.dir.entry.d_name, subdir->u.dir.sub_path_len);
} }
subdir->info_class = intern->info_class; subdir->info_class = intern->info_class;
subdir->file_class = intern->file_class; subdir->file_class = intern->file_class;
@ -1526,7 +1517,7 @@ PHP_METHOD(RecursiveDirectoryIterator, getSubPath)
} }
if (intern->u.dir.sub_path) { if (intern->u.dir.sub_path) {
RETURN_STRINGL(intern->u.dir.sub_path, intern->u.dir.sub_path_len); RETURN_STR_COPY(intern->u.dir.sub_path);
} else { } else {
RETURN_EMPTY_STRING(); RETURN_EMPTY_STRING();
} }
@ -1544,7 +1535,7 @@ PHP_METHOD(RecursiveDirectoryIterator, getSubPathname)
} }
if (intern->u.dir.sub_path) { if (intern->u.dir.sub_path) {
RETURN_NEW_STR(strpprintf(0, "%s%c%s", intern->u.dir.sub_path, slash, intern->u.dir.entry.d_name)); RETURN_NEW_STR(strpprintf(0, "%s%c%s", ZSTR_VAL(intern->u.dir.sub_path), slash, intern->u.dir.entry.d_name));
} else { } else {
RETURN_STRING(intern->u.dir.entry.d_name); RETURN_STRING(intern->u.dir.entry.d_name);
} }
@ -1672,7 +1663,7 @@ static void spl_filesystem_dir_it_move_forward(zend_object_iterator *iter)
object->u.dir.index++; object->u.dir.index++;
spl_filesystem_dir_read(object); spl_filesystem_dir_read(object);
if (object->file_name) { if (object->file_name) {
efree(object->file_name); zend_string_release(object->file_name);
object->file_name = NULL; object->file_name = NULL;
} }
} }
@ -1711,7 +1702,7 @@ static zval *spl_filesystem_tree_it_current_data(zend_object_iterator *iter)
if (spl_filesystem_object_get_file_name(object) != SUCCESS) { if (spl_filesystem_object_get_file_name(object) != SUCCESS) {
return NULL; return NULL;
} }
ZVAL_STRINGL(&iterator->current, object->file_name, object->file_name_len); ZVAL_STR_COPY(&iterator->current, object->file_name);
} }
return &iterator->current; return &iterator->current;
} else if (SPL_FILE_DIR_CURRENT(object, SPL_FILE_DIR_CURRENT_AS_FILEINFO)) { } else if (SPL_FILE_DIR_CURRENT(object, SPL_FILE_DIR_CURRENT_AS_FILEINFO)) {
@ -1739,7 +1730,7 @@ static void spl_filesystem_tree_it_current_key(zend_object_iterator *iter, zval
if (spl_filesystem_object_get_file_name(object) != SUCCESS) { if (spl_filesystem_object_get_file_name(object) != SUCCESS) {
return; return;
} }
ZVAL_STRINGL(key, object->file_name, object->file_name_len); ZVAL_STR_COPY(key, object->file_name);
} }
} }
/* }}} */ /* }}} */
@ -1755,7 +1746,7 @@ static void spl_filesystem_tree_it_move_forward(zend_object_iterator *iter)
spl_filesystem_dir_read(object); spl_filesystem_dir_read(object);
} while (spl_filesystem_is_dot(object->u.dir.entry.d_name)); } while (spl_filesystem_is_dot(object->u.dir.entry.d_name));
if (object->file_name) { if (object->file_name) {
efree(object->file_name); zend_string_release(object->file_name);
object->file_name = NULL; object->file_name = NULL;
} }
if (!Z_ISUNDEF(iterator->current)) { if (!Z_ISUNDEF(iterator->current)) {
@ -1831,7 +1822,7 @@ static int spl_filesystem_object_cast(zend_object *readobj, zval *writeobj, int
switch (intern->type) { switch (intern->type) {
case SPL_FS_INFO: case SPL_FS_INFO:
case SPL_FS_FILE: case SPL_FS_FILE:
ZVAL_STRINGL(writeobj, intern->file_name, intern->file_name_len); ZVAL_STR_COPY(writeobj, intern->file_name);
return SUCCESS; return SUCCESS;
case SPL_FS_DIR: case SPL_FS_DIR:
ZVAL_STRING(writeobj, intern->u.dir.entry.d_name); ZVAL_STRING(writeobj, intern->u.dir.entry.d_name);
@ -1856,7 +1847,7 @@ static int spl_filesystem_file_read(spl_filesystem_object *intern, int silent) /
if (php_stream_eof(intern->u.file.stream)) { if (php_stream_eof(intern->u.file.stream)) {
if (!silent) { if (!silent) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", intern->file_name); zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", ZSTR_VAL(intern->file_name));
} }
return FAILURE; return FAILURE;
} }
@ -1926,7 +1917,7 @@ static int spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesystem_obje
if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV) || intern->u.file.func_getCurr->common.scope != spl_ce_SplFileObject) { if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV) || intern->u.file.func_getCurr->common.scope != spl_ce_SplFileObject) {
if (php_stream_eof(intern->u.file.stream)) { if (php_stream_eof(intern->u.file.stream)) {
if (!silent) { if (!silent) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", intern->file_name); zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", ZSTR_VAL(intern->file_name));
} }
return FAILURE; return FAILURE;
} }
@ -2011,7 +2002,7 @@ static void spl_filesystem_file_rewind(zval * this_ptr, spl_filesystem_object *i
return; return;
} }
if (-1 == php_stream_rewind(intern->u.file.stream)) { if (-1 == php_stream_rewind(intern->u.file.stream)) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot rewind file %s", intern->file_name); zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot rewind file %s", ZSTR_VAL(intern->file_name));
} else { } else {
spl_filesystem_file_free_line(intern); spl_filesystem_file_free_line(intern);
intern->u.file.current_line_num = 0; intern->u.file.current_line_num = 0;
@ -2026,16 +2017,14 @@ PHP_METHOD(SplFileObject, __construct)
{ {
spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS); spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS);
bool use_include_path = 0; bool use_include_path = 0;
char *p1, *p2; size_t path_len;
char *tmp_path;
size_t tmp_path_len;
zend_error_handling error_handling; zend_error_handling error_handling;
intern->u.file.open_mode = NULL; intern->u.file.open_mode = NULL;
intern->u.file.open_mode_len = 0; intern->u.file.open_mode_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|sbr!", if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|sbr!",
&intern->file_name, &intern->file_name_len, &intern->file_name,
&intern->u.file.open_mode, &intern->u.file.open_mode_len, &intern->u.file.open_mode, &intern->u.file.open_mode_len,
&use_include_path, &intern->u.file.zcontext) == FAILURE) { &use_include_path, &intern->u.file.zcontext) == FAILURE) {
intern->u.file.open_mode = NULL; intern->u.file.open_mode = NULL;
@ -2051,29 +2040,21 @@ PHP_METHOD(SplFileObject, __construct)
zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling); zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling);
if (spl_filesystem_file_open(intern, use_include_path, 0) == SUCCESS) { if (spl_filesystem_file_open(intern, use_include_path, 0) == SUCCESS) {
tmp_path_len = strlen(intern->u.file.stream->orig_path); path_len = strlen(intern->u.file.stream->orig_path);
if (tmp_path_len > 1 && IS_SLASH_AT(intern->u.file.stream->orig_path, tmp_path_len-1)) { if (path_len > 1 && IS_SLASH_AT(intern->u.file.stream->orig_path, path_len-1)) {
tmp_path_len--; path_len--;
} }
tmp_path = estrndup(intern->u.file.stream->orig_path, tmp_path_len); while (path_len > 1 && !IS_SLASH_AT(intern->u.file.stream->orig_path, path_len-1)) {
path_len--;
p1 = strrchr(tmp_path, '/');
#ifdef PHP_WIN32
p2 = strrchr(tmp_path, '\\');
#else
p2 = 0;
#endif
if (p1 || p2) {
intern->_path_len = ((p1 > p2 ? p1 : p2) - tmp_path);
} else {
intern->_path_len = 0;
} }
efree(tmp_path); if (path_len) {
path_len--;
}
intern->_path = estrndup(intern->u.file.stream->orig_path, intern->_path_len); intern->path = zend_string_init(intern->u.file.stream->orig_path, path_len, 0);
} }
zend_restore_error_handling(&error_handling); zend_restore_error_handling(&error_handling);
@ -2083,8 +2064,8 @@ PHP_METHOD(SplFileObject, __construct)
/* {{{ Construct a new temp file object */ /* {{{ Construct a new temp file object */
PHP_METHOD(SplTempFileObject, __construct) PHP_METHOD(SplTempFileObject, __construct)
{ {
zend_string *file_name;
zend_long max_memory = PHP_STREAM_MAX_MEM; zend_long max_memory = PHP_STREAM_MAX_MEM;
char tmp_fname[48];
spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS); spl_filesystem_object *intern = Z_SPLFILESYSTEM_P(ZEND_THIS);
zend_error_handling error_handling; zend_error_handling error_handling;
@ -2093,23 +2074,21 @@ PHP_METHOD(SplTempFileObject, __construct)
} }
if (max_memory < 0) { if (max_memory < 0) {
intern->file_name = "php://memory"; file_name = zend_string_init("php://memory", sizeof("php://memory")-1, 0);
intern->file_name_len = 12;
} else if (ZEND_NUM_ARGS()) { } else if (ZEND_NUM_ARGS()) {
intern->file_name_len = slprintf(tmp_fname, sizeof(tmp_fname), "php://temp/maxmemory:" ZEND_LONG_FMT, max_memory); file_name = zend_strpprintf(0, "php://temp/maxmemory:" ZEND_LONG_FMT, max_memory);
intern->file_name = tmp_fname;
} else { } else {
intern->file_name = "php://temp"; file_name = zend_string_init("php://temp", sizeof("php://temp")-1, 0);
intern->file_name_len = 10;
} }
intern->file_name = file_name;
intern->u.file.open_mode = "wb"; intern->u.file.open_mode = "wb";
intern->u.file.open_mode_len = 1; intern->u.file.open_mode_len = 1;
zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling); zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling);
if (spl_filesystem_file_open(intern, 0, 0) == SUCCESS) { if (spl_filesystem_file_open(intern, 0, 0) == SUCCESS) {
intern->_path_len = 0; intern->path = ZSTR_EMPTY_ALLOC();
intern->_path = estrndup("", 0);
} }
zend_string_release(file_name);
zend_restore_error_handling(&error_handling); zend_restore_error_handling(&error_handling);
} /* }}} */ } /* }}} */
@ -2690,7 +2669,7 @@ PHP_METHOD(SplFileObject, ftruncate)
CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
if (!php_stream_truncate_supported(intern->u.file.stream)) { if (!php_stream_truncate_supported(intern->u.file.stream)) {
zend_throw_exception_ex(spl_ce_LogicException, 0, "Can't truncate file %s", intern->file_name); zend_throw_exception_ex(spl_ce_LogicException, 0, "Can't truncate file %s", ZSTR_VAL(intern->file_name));
RETURN_THROWS(); RETURN_THROWS();
} }

View file

@ -58,11 +58,9 @@ typedef struct {
struct _spl_filesystem_object { struct _spl_filesystem_object {
void *oth; void *oth;
const spl_other_handler *oth_handler; const spl_other_handler *oth_handler;
char *_path; zend_string *path;
size_t _path_len; zend_string *orig_path;
char *orig_path; zend_string *file_name;
char *file_name;
size_t file_name_len;
SPL_FS_OBJ_TYPE type; SPL_FS_OBJ_TYPE type;
zend_long flags; zend_long flags;
zend_class_entry *file_class; zend_class_entry *file_class;
@ -71,8 +69,7 @@ struct _spl_filesystem_object {
struct { struct {
php_stream *dirp; php_stream *dirp;
php_stream_dirent entry; php_stream_dirent entry;
char *sub_path; zend_string *sub_path;
size_t sub_path_len;
int index; int index;
int is_recursive; int is_recursive;
zend_function *func_rewind; zend_function *func_rewind;

View file

@ -79,7 +79,7 @@ typedef struct _php_basic_globals {
time_t page_mtime; time_t page_mtime;
/* filestat.c && main/streams/streams.c */ /* filestat.c && main/streams/streams.c */
char *CurrentStatFile, *CurrentLStatFile; zend_string *CurrentStatFile, *CurrentLStatFile;
php_stream_statbuf ssb, lssb; php_stream_statbuf ssb, lssb;
/* mt_rand.c */ /* mt_rand.c */

View file

@ -317,12 +317,12 @@ PHP_FUNCTION(chdir)
RETURN_FALSE; RETURN_FALSE;
} }
if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentStatFile), strlen(BG(CurrentStatFile)))) { if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(ZSTR_VAL(BG(CurrentStatFile)), ZSTR_LEN(BG(CurrentStatFile)))) {
efree(BG(CurrentStatFile)); zend_string_release(BG(CurrentStatFile));
BG(CurrentStatFile) = NULL; BG(CurrentStatFile) = NULL;
} }
if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentLStatFile), strlen(BG(CurrentLStatFile)))) { if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(ZSTR_VAL(BG(CurrentLStatFile)), ZSTR_LEN(BG(CurrentLStatFile)))) {
efree(BG(CurrentLStatFile)); zend_string_release(BG(CurrentLStatFile));
BG(CurrentLStatFile) = NULL; BG(CurrentLStatFile) = NULL;
} }

View file

@ -1643,7 +1643,7 @@ PHPAPI int php_copy_file_ctx(const char *src, const char *dest, int src_flg, php
return FAILURE; return FAILURE;
} }
switch (php_stream_stat_path_ex(dest, PHP_STREAM_URL_STAT_QUIET | PHP_STREAM_URL_STAT_NOCACHE, &dest_s, ctx)) { switch (php_stream_stat_path_ex(dest, PHP_STREAM_URL_STAT_QUIET, &dest_s, ctx)) {
case -1: case -1:
/* non-statable stream */ /* non-statable stream */
goto safe_to_copy; goto safe_to_copy;

View file

@ -88,11 +88,11 @@ PHP_RINIT_FUNCTION(filestat) /* {{{ */
PHP_RSHUTDOWN_FUNCTION(filestat) /* {{{ */ PHP_RSHUTDOWN_FUNCTION(filestat) /* {{{ */
{ {
if (BG(CurrentStatFile)) { if (BG(CurrentStatFile)) {
efree (BG(CurrentStatFile)); zend_string_release(BG(CurrentStatFile));
BG(CurrentStatFile) = NULL; BG(CurrentStatFile) = NULL;
} }
if (BG(CurrentLStatFile)) { if (BG(CurrentLStatFile)) {
efree (BG(CurrentLStatFile)); zend_string_release(BG(CurrentLStatFile));
BG(CurrentLStatFile) = NULL; BG(CurrentLStatFile) = NULL;
} }
return SUCCESS; return SUCCESS;
@ -680,11 +680,11 @@ PHPAPI void php_clear_stat_cache(bool clear_realpath_cache, const char *filename
* as it may contain outdated data (e.g. "nlink" for a directory when deleting a file * as it may contain outdated data (e.g. "nlink" for a directory when deleting a file
* in this directory, as shown by lstat_stat_variation9.phpt) */ * in this directory, as shown by lstat_stat_variation9.phpt) */
if (BG(CurrentStatFile)) { if (BG(CurrentStatFile)) {
efree(BG(CurrentStatFile)); zend_string_release(BG(CurrentStatFile));
BG(CurrentStatFile) = NULL; BG(CurrentStatFile) = NULL;
} }
if (BG(CurrentLStatFile)) { if (BG(CurrentLStatFile)) {
efree(BG(CurrentLStatFile)); zend_string_release(BG(CurrentLStatFile));
BG(CurrentLStatFile) = NULL; BG(CurrentLStatFile) = NULL;
} }
if (clear_realpath_cache) { if (clear_realpath_cache) {
@ -720,26 +720,27 @@ PHP_FUNCTION(clearstatcache)
#define IS_ACCESS_CHECK(__t) (IS_ABLE_CHECK(type) || (__t) == FS_EXISTS) #define IS_ACCESS_CHECK(__t) (IS_ABLE_CHECK(type) || (__t) == FS_EXISTS)
/* {{{ php_stat */ /* {{{ php_stat */
PHPAPI void php_stat(const char *filename, size_t filename_length, int type, zval *return_value) PHPAPI void php_stat(zend_string *filename, int type, zval *return_value)
{ {
zend_stat_t *stat_sb; zend_stat_t *stat_sb;
php_stream_statbuf ssb; php_stream_statbuf ssb;
int flags = 0, rmask=S_IROTH, wmask=S_IWOTH, xmask=S_IXOTH; /* access rights defaults to other */ int flags = 0, rmask=S_IROTH, wmask=S_IWOTH, xmask=S_IXOTH; /* access rights defaults to other */
const char *local; const char *local = NULL;
php_stream_wrapper *wrapper; php_stream_wrapper *wrapper = NULL;
if (!filename_length || CHECK_NULL_PATH(filename, filename_length)) {
if (filename_length && !IS_EXISTS_CHECK(type)) {
php_error_docref(NULL, E_WARNING, "Filename contains null byte");
}
RETURN_FALSE;
}
if ((wrapper = php_stream_locate_url_wrapper(filename, &local, 0)) == &php_plain_files_wrapper && php_check_open_basedir(local)) {
RETURN_FALSE;
}
if (IS_ACCESS_CHECK(type)) { if (IS_ACCESS_CHECK(type)) {
if (!ZSTR_LEN(filename) || CHECK_NULL_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename))) {
if (ZSTR_LEN(filename) && !IS_EXISTS_CHECK(type)) {
php_error_docref(NULL, E_WARNING, "Filename contains null byte");
}
RETURN_FALSE;
}
if ((wrapper = php_stream_locate_url_wrapper(ZSTR_VAL(filename), &local, 0)) == &php_plain_files_wrapper
&& php_check_open_basedir(local)) {
RETURN_FALSE;
}
if (wrapper == &php_plain_files_wrapper) { if (wrapper == &php_plain_files_wrapper) {
switch (type) { switch (type) {
@ -774,13 +775,63 @@ PHPAPI void php_stat(const char *filename, size_t filename_length, int type, zva
flags |= PHP_STREAM_URL_STAT_QUIET; flags |= PHP_STREAM_URL_STAT_QUIET;
} }
if (php_stream_stat_path_ex((char *)filename, flags, &ssb, NULL)) { do {
/* Error Occurred */ /* Try to hit the cache first */
if (!IS_EXISTS_CHECK(type)) { if (flags & PHP_STREAM_URL_STAT_LINK) {
php_error_docref(NULL, E_WARNING, "%sstat failed for %s", IS_LINK_OPERATION(type) ? "L" : "", filename); if (filename == BG(CurrentLStatFile)
|| (BG(CurrentLStatFile)
&& zend_string_equal_content(filename, BG(CurrentLStatFile)))) {
memcpy(&ssb, &BG(lssb), sizeof(php_stream_statbuf));
break;
}
} else {
if (filename == BG(CurrentStatFile)
|| (BG(CurrentStatFile)
&& zend_string_equal_content(filename, BG(CurrentStatFile)))) {
memcpy(&ssb, &BG(ssb), sizeof(php_stream_statbuf));
break;
}
} }
RETURN_FALSE;
} if (!wrapper) {
if (!ZSTR_LEN(filename) || CHECK_NULL_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename))) {
if (ZSTR_LEN(filename) && !IS_EXISTS_CHECK(type)) {
php_error_docref(NULL, E_WARNING, "Filename contains null byte");
}
RETURN_FALSE;
}
if ((wrapper = php_stream_locate_url_wrapper(ZSTR_VAL(filename), &local, 0)) == &php_plain_files_wrapper
&& php_check_open_basedir(local)) {
RETURN_FALSE;
}
}
if (!wrapper
|| !wrapper->wops->url_stat
|| wrapper->wops->url_stat(wrapper, local, flags | PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR, &ssb, NULL)) {
/* Error Occurred */
if (!IS_EXISTS_CHECK(type)) {
php_error_docref(NULL, E_WARNING, "%sstat failed for %s", IS_LINK_OPERATION(type) ? "L" : "", ZSTR_VAL(filename));
}
RETURN_FALSE;
}
/* Drop into cache */
if (flags & PHP_STREAM_URL_STAT_LINK) {
if (BG(CurrentLStatFile)) {
zend_string_release(BG(CurrentLStatFile));
}
BG(CurrentLStatFile) = zend_string_copy(filename);
memcpy(&BG(lssb), &ssb, sizeof(php_stream_statbuf));
} else {
if (BG(CurrentStatFile)) {
zend_string_release(BG(CurrentStatFile));
}
BG(CurrentStatFile) = zend_string_copy(filename);
memcpy(&BG(ssb), &ssb, sizeof(php_stream_statbuf));
}
} while (0);
stat_sb = &ssb.sb; stat_sb = &ssb.sb;
@ -936,14 +987,13 @@ PHPAPI void php_stat(const char *filename, size_t filename_length, int type, zva
/* {{{ FileFunction(name, funcnum) */ /* {{{ FileFunction(name, funcnum) */
#define FileFunction(name, funcnum) \ #define FileFunction(name, funcnum) \
ZEND_NAMED_FUNCTION(name) { \ ZEND_NAMED_FUNCTION(name) { \
char *filename; \ zend_string *filename; \
size_t filename_len; \
\ \
ZEND_PARSE_PARAMETERS_START(1, 1) \ ZEND_PARSE_PARAMETERS_START(1, 1) \
Z_PARAM_STRING(filename, filename_len) \ Z_PARAM_STR(filename) \
ZEND_PARSE_PARAMETERS_END(); \ ZEND_PARSE_PARAMETERS_END(); \
\ \
php_stat(filename, filename_len, funcnum, return_value); \ php_stat(filename, funcnum, return_value); \
} }
/* }}} */ /* }}} */

View file

@ -41,7 +41,7 @@ PHP_RSHUTDOWN_FUNCTION(filestat);
typedef size_t php_stat_len; typedef size_t php_stat_len;
PHPAPI void php_clear_stat_cache(bool clear_realpath_cache, const char *filename, size_t filename_len); PHPAPI void php_clear_stat_cache(bool clear_realpath_cache, const char *filename, size_t filename_len);
PHPAPI void php_stat(const char *filename, size_t filename_length, int type, zval *return_value); PHPAPI void php_stat(zend_string *filename, int type, zval *return_value);
/* Switches for various filestat functions: */ /* Switches for various filestat functions: */
#define FS_PERMS 0 #define FS_PERMS 0

View file

@ -280,8 +280,7 @@ static int php_zip_add_file(ze_zip_object *obj, const char *filename, size_t fil
{ {
struct zip_source *zs; struct zip_source *zs;
char resolved_path[MAXPATHLEN]; char resolved_path[MAXPATHLEN];
zval exists_flag; php_stream_statbuf ssb;
if (ZIP_OPENBASEDIR_CHECKPATH(filename)) { if (ZIP_OPENBASEDIR_CHECKPATH(filename)) {
return -1; return -1;
@ -292,8 +291,7 @@ static int php_zip_add_file(ze_zip_object *obj, const char *filename, size_t fil
return -1; return -1;
} }
php_stat(resolved_path, strlen(resolved_path), FS_EXISTS, &exists_flag); if (php_stream_stat_path_ex(resolved_path, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL)) {
if (Z_TYPE(exists_flag) == IS_FALSE) {
php_error_docref(NULL, E_WARNING, "No such file or directory"); php_error_docref(NULL, E_WARNING, "No such file or directory");
return -1; return -1;
} }

View file

@ -391,7 +391,7 @@ END_EXTERN_C()
/* Flags for url_stat method in wrapper ops */ /* Flags for url_stat method in wrapper ops */
#define PHP_STREAM_URL_STAT_LINK 1 #define PHP_STREAM_URL_STAT_LINK 1
#define PHP_STREAM_URL_STAT_QUIET 2 #define PHP_STREAM_URL_STAT_QUIET 2
#define PHP_STREAM_URL_STAT_NOCACHE 4 #define PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR 4
/* change the blocking mode of stream: value == 1 => blocking, value == 0 => non-blocking. */ /* change the blocking mode of stream: value == 1 => blocking, value == 0 => non-blocking. */
#define PHP_STREAM_OPTION_BLOCKING 1 #define PHP_STREAM_OPTION_BLOCKING 1

View file

@ -1157,12 +1157,14 @@ static php_stream *php_plain_files_stream_opener(php_stream_wrapper *wrapper, co
static int php_plain_files_url_stater(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context) static int php_plain_files_url_stater(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context)
{ {
if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) { if (!(flags & PHP_STREAM_URL_STAT_IGNORE_OPEN_BASEDIR)) {
url += sizeof("file://") - 1; if (strncasecmp(url, "file://", sizeof("file://") - 1) == 0) {
} url += sizeof("file://") - 1;
}
if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) { if (php_check_open_basedir_ex(url, (flags & PHP_STREAM_URL_STAT_QUIET) ? 0 : 1)) {
return -1; return -1;
}
} }
#ifdef PHP_WIN32 #ifdef PHP_WIN32

View file

@ -1975,47 +1975,12 @@ PHPAPI int _php_stream_stat_path(const char *path, int flags, php_stream_statbuf
{ {
php_stream_wrapper *wrapper = NULL; php_stream_wrapper *wrapper = NULL;
const char *path_to_open = path; const char *path_to_open = path;
int ret;
memset(ssb, 0, sizeof(*ssb)); memset(ssb, 0, sizeof(*ssb));
if (!(flags & PHP_STREAM_URL_STAT_NOCACHE)) {
/* Try to hit the cache first */
if (flags & PHP_STREAM_URL_STAT_LINK) {
if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) {
memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf));
return 0;
}
} else {
if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) {
memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf));
return 0;
}
}
}
wrapper = php_stream_locate_url_wrapper(path, &path_to_open, 0); wrapper = php_stream_locate_url_wrapper(path, &path_to_open, 0);
if (wrapper && wrapper->wops->url_stat) { if (wrapper && wrapper->wops->url_stat) {
ret = wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context); return wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context);
if (ret == 0) {
if (!(flags & PHP_STREAM_URL_STAT_NOCACHE)) {
/* Drop into cache */
if (flags & PHP_STREAM_URL_STAT_LINK) {
if (BG(CurrentLStatFile)) {
efree(BG(CurrentLStatFile));
}
BG(CurrentLStatFile) = estrdup(path);
memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf));
} else {
if (BG(CurrentStatFile)) {
efree(BG(CurrentStatFile));
}
BG(CurrentStatFile) = estrdup(path);
memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf));
}
}
}
return ret;
} }
return -1; return -1;
} }

View file

@ -175,12 +175,12 @@ void phpdbg_webdata_decompress(char *msg, int len) {
if ((zvp = zend_hash_str_find(ht, ZEND_STRL("cwd"))) && Z_TYPE_P(zvp) == IS_STRING) { if ((zvp = zend_hash_str_find(ht, ZEND_STRL("cwd"))) && Z_TYPE_P(zvp) == IS_STRING) {
if (VCWD_CHDIR(Z_STRVAL_P(zvp)) == SUCCESS) { if (VCWD_CHDIR(Z_STRVAL_P(zvp)) == SUCCESS) {
if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentStatFile), strlen(BG(CurrentStatFile)))) { if (BG(CurrentStatFile) && !IS_ABSOLUTE_PATH(ZSTR_VAL(BG(CurrentStatFile)), ZSTR_LEN(BG(CurrentStatFile)))) {
efree(BG(CurrentStatFile)); zend_string_release(BG(CurrentStatFile));
BG(CurrentStatFile) = NULL; BG(CurrentStatFile) = NULL;
} }
if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(BG(CurrentLStatFile), strlen(BG(CurrentLStatFile)))) { if (BG(CurrentLStatFile) && !IS_ABSOLUTE_PATH(ZSTR_VAL(BG(CurrentLStatFile)), ZSTR_LEN(BG(CurrentLStatFile)))) {
efree(BG(CurrentLStatFile)); zend_string_release(BG(CurrentLStatFile));
BG(CurrentLStatFile) = NULL; BG(CurrentLStatFile) = NULL;
} }
} }