RETURN_FALSE;
}
-#ifndef ZTS
- ret = symlink(Z_STRVAL_PP(topath), Z_STRVAL_PP(frompath));
-#else
- ret = symlink(dest_p, source_p);
-#endif
+ /* For the source, an expanded path must be used (in ZTS an other thread could have changed the CWD).
+ * For the target the exact string given by the user must be used, relative or not, existing or not.
+ * The target is relative to the link itself, not to the CWD. */
+ ret = symlink(Z_STRVAL_PP(topath), source_p);
+
if (ret == -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
--- /dev/null
+--TEST--
+symlink() using a relative path, and symlink() to a symlink
+--FILE--
+<?php
+$prefix = __FILE__;
+
+touch($prefix . "_file");
+
+// symlink to a regular file using a relative dest
+symlink(basename($prefix . "_file"), $prefix . "_link1");
+
+// symlink to a symlink using a relative path
+symlink(basename($prefix . "_link1"), $prefix . "_link2");
+
+// symlink to a non-existent path
+@unlink($prefix . "_nonexistant");
+symlink(basename($prefix . "_nonexistant"), $prefix . "_link3");
+
+// symlink to a regular file using an absolute path
+symlink($prefix . "_file", $prefix . "_link4");
+
+// symlink to a symlink using an absolute path
+symlink($prefix . "_link4", $prefix . "_link5");
+
+var_dump(readlink($prefix . "_link1"));
+var_dump(readlink($prefix . "_link2"));
+var_dump(readlink($prefix . "_link3"));
+var_dump(readlink($prefix . "_link4"));
+var_dump(readlink($prefix . "_link5"));
+
+unlink($prefix . "_link5");
+unlink($prefix . "_link4");
+unlink($prefix . "_link3");
+unlink($prefix . "_link2");
+unlink($prefix . "_link1");
+unlink($prefix . "_file");
+
+?>
+--EXPECTF--
+string(%d) "symlink_to_symlink.php_file"
+string(%d) "symlink_to_symlink.php_link1"
+string(%d) "symlink_to_symlink.php_nonexistant"
+string(%d) "%s/symlink_to_symlink.php_file"
+string(%d) "%s/symlink_to_symlink.php_link4"