Commit 91ac8252783 for php.net
commit 91ac82527839cda5f05a67e7d91b579903840104
Author: Jakub Zelenka <bukka@php.net>
Date: Sat Jan 3 14:41:57 2026 +0100
Fix bug #74357: lchown fails to change ownership of symlink with ZTS
diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c
index 366e13ce9b6..e4d88338bc9 100644
--- a/Zend/zend_virtual_cwd.c
+++ b/Zend/zend_virtual_cwd.c
@@ -1417,7 +1417,7 @@ CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int li
int ret;
CWD_STATE_COPY(&new_state, &CWDG(cwd));
- if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
+ if (virtual_file_ex(&new_state, filename, NULL, link ? CWD_EXPAND : CWD_REALPATH)) {
CWD_STATE_FREE_ERR(&new_state);
return -1;
}
diff --git a/ext/standard/tests/file/bug74357.phpt b/ext/standard/tests/file/bug74357.phpt
new file mode 100644
index 00000000000..ebef650bb64
--- /dev/null
+++ b/ext/standard/tests/file/bug74357.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Bug #74357 (lchown fails to change ownership of symlink with ZTS)
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) == 'WIN') die('skip no windows support');
+if (!function_exists("posix_getpwuid")) die('skip no posix_getpwuid()');
+require __DIR__ . '/../skipif_no_root.inc';
+if (posix_getpwuid(1000) === false) die('skip no user with uid 1000')
+?>
+--FILE--
+<?php
+$link = __DIR__ . "/bug74357link";
+$dir = __DIR__ . "bug74357dir";
+
+if (is_link($link)) {
+ unlink($link);
+}
+if (!is_dir($dir)) {
+ mkdir($dir);
+}
+symlink($dir, $link);
+lchown($link, 1000);
+var_dump(lstat($link)['uid']);
+
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . "/bug74357link");
+@rmdir(__DIR__ . "bug74357dir");
+?>
+--EXPECT--
+int(1000)
diff --git a/ext/standard/tests/skipif_no_root.inc b/ext/standard/tests/skipif_no_root.inc
new file mode 100644
index 00000000000..ef60c14745b
--- /dev/null
+++ b/ext/standard/tests/skipif_no_root.inc
@@ -0,0 +1,16 @@
+<?php
+
+// Skip if being run by root (files are always readable, writeable and executable)
+$filename = @tempnam(__DIR__, 'root_check_');
+if (!file_exists($filename)) {
+ die('WARN Unable to create the "root check" file');
+}
+
+$isRoot = fileowner($filename) == 0;
+
+unlink($filename);
+
+if (!$isRoot) {
+ die('SKIP Cannot be run as non root');
+}
+