diff --git a/NEWS b/NEWS index e25c7e87a939..49bde48cf755 100644 --- a/NEWS +++ b/NEWS @@ -160,6 +160,8 @@ PHP NEWS . Fixed quoted_printable_decode() truncating input at NUL bytes. (Weilin Du) . Fixed version_compare() silently truncating version strings at NUL bytes by rejecting such input. (Weilin Du) + . rename() warnings for a missing path now say whether the source or the + destination directory is missing (GH-8597). (mvanhorn) . Fixed bug #60110 (fclose(), file_put_contents(), copy() do not return false properly). (Jakub Zelenka, Ilija Tovilo) . Fixed three Windows-only proc_open() defects: an uninitialized diff --git a/UPGRADING b/UPGRADING index dbfa42b9b171..f5fe4aa30ccb 100644 --- a/UPGRADING +++ b/UPGRADING @@ -107,6 +107,8 @@ PHP 8.7 UPGRADE NOTES rebuilding PHP with --enable-fd-setsize. On Windows, selecting on more than 64 pipe or file handles no longer crashes; it fails with a warning, as that path remains bounded by WaitForMultipleObjects(). + . When rename() fails because a path does not exist, the warning now says + whether the source path is missing or the destination directory is missing. ======================================== 6. New Functions diff --git a/ext/standard/tests/file/gh8597.phpt b/ext/standard/tests/file/gh8597.phpt new file mode 100644 index 000000000000..71796c6bf787 --- /dev/null +++ b/ext/standard/tests/file/gh8597.phpt @@ -0,0 +1,43 @@ +--TEST-- +GH-8597: rename() warnings name the missing source or destination directory +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECTF-- +missing source: + +Warning: rename(): No such file or directory (source "%s/gh8597/no-such-source.txt") in %s on line %d +bool(false) +missing destination directory: + +Warning: rename(): No such file or directory (destination directory "%s/gh8597/no-such-dir") in %s on line %d +bool(false) +success: +bool(true) +bool(true) diff --git a/ext/standard/tests/file/rename_variation13.phpt b/ext/standard/tests/file/rename_variation13.phpt index e53cf86f16bb..39f878c1af5b 100644 --- a/ext/standard/tests/file/rename_variation13.phpt +++ b/ext/standard/tests/file/rename_variation13.phpt @@ -59,12 +59,12 @@ rmdir($file_path); -- testing '-1' -- bool(true) -Warning: rename('-1', '%s/renameVar13/afile.tmp'): No such file or directory in %s on line %d +Warning: rename('-1', '%s/renameVar13/afile.tmp'): No such file or directory (source "-1") in %s on line %d bool(false) -- testing '1' -- bool(true) -Warning: rename('1', '%s/renameVar13/afile.tmp'): No such file or directory in %s on line %d +Warning: rename('1', '%s/renameVar13/afile.tmp'): No such file or directory (source "1") in %s on line %d bool(false) -- testing '' -- @@ -83,19 +83,19 @@ bool(false) -- testing ' ' -- bool(true) -Warning: rename(' ', '%s/renameVar13/afile.tmp'): No such file or directory in %s on line %d +Warning: rename(' ', '%s/renameVar13/afile.tmp'): No such file or directory (source " ") in %s on line %d bool(false) -- testing '/no/such/file/dir' -- -Warning: rename('%s/renameVar13/afile.tmp', '/no/such/file/dir'): No such file or directory in %s on line %d +Warning: rename('%s/renameVar13/afile.tmp', '/no/such/file/dir'): No such file or directory (destination directory "/no/such/file") in %s on line %d bool(false) -Warning: rename('/no/such/file/dir', '%s/renameVar13/afile.tmp'): No such file or directory in %s on line %d +Warning: rename('/no/such/file/dir', '%s/renameVar13/afile.tmp'): No such file or directory (source "/no/such/file/dir") in %s on line %d bool(false) -- testing 'php/php' -- -Warning: rename('%s/renameVar13/afile.tmp', 'php/php'): %s directory in %s on line %d +Warning: rename('%s/renameVar13/afile.tmp', 'php/php'): No such file or directory (destination directory "php") in %s on line %d bool(false) -Warning: rename('php/php', '%s/renameVar13/afile.tmp'): %s directory in %s on line %d +Warning: rename('php/php', '%s/renameVar13/afile.tmp'): No such file or directory (source "php/php") in %s on line %d bool(false) diff --git a/ext/standard/tests/file/rename_variation8.phpt b/ext/standard/tests/file/rename_variation8.phpt index d2bca9ffed14..57f14cad19b1 100644 --- a/ext/standard/tests/file/rename_variation8.phpt +++ b/ext/standard/tests/file/rename_variation8.phpt @@ -46,7 +46,7 @@ rmdir(__DIR__."/rename_basic_dir1"); --EXPECTF-- *** Testing rename() on non-existing file *** -Warning: rename(): No such file or directory in %s on line %d +Warning: rename(): No such file or directory (source "%s/non_existent_file.tmp") in %s on line %d bool(false) bool(false) bool(false) @@ -58,7 +58,7 @@ bool(true) *** Testing rename() on non-existing directory *** -Warning: rename(): No such file or directory in %s on line %d +Warning: rename(): No such file or directory (source "%s/non_existent_dir") in %s on line %d bool(false) bool(false) bool(false) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 24427b41b690..de2ea9f498e7 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -19,6 +19,7 @@ #include "ext/standard/file.h" #include "ext/standard/flock_compat.h" #include "ext/standard/php_filestat.h" +#include "ext/standard/php_string.h" #include #include #ifdef HAVE_SYS_WAIT_H @@ -1397,8 +1398,9 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f if (ret == -1) { #ifndef PHP_WIN32 char errstr[256]; + int rename_errno = errno; # ifdef EXDEV - if (errno == EXDEV) { + if (rename_errno == EXDEV) { zend_stat_t sb; # if !defined(ZTS) && !defined(TSRM_WIN32) /* not sure what to do in ZTS case, umask is not thread-safe */ @@ -1458,9 +1460,30 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #ifdef PHP_WIN32 php_win32_stream_wrapper_warn_error(wrapper, context, options, PHP_STREAM_EC(RenameFailed), GetLastError()); #else - php_stream_wrapper_warn(wrapper, context, options, - RenameFailed, - "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + if (rename_errno == ENOENT) { + zend_stat_t sb; + if (VCWD_STAT(url_from, &sb) != 0) { + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "No such file or directory (source \"%s\")", url_from); + } else { + size_t to_len = strlen(url_to); + char *dir = estrndup(url_to, to_len); + php_dirname(dir, to_len); + if (dir[0] != '\0' && !(dir[0] == '.' && dir[1] == '\0') + && VCWD_STAT(dir, &sb) != 0) { + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "No such file or directory (destination directory \"%s\")", dir); + } else { + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "%s", php_socket_strerror_s(rename_errno, errstr, sizeof(errstr))); + } + efree(dir); + } + } else { + php_stream_wrapper_warn(wrapper, context, options, + RenameFailed, + "%s", php_socket_strerror_s(rename_errno, errstr, sizeof(errstr))); + } #endif return 0; }