sys_temp_dir = /var/lib/php/tmp in php.ini or pool config)/tmp via AppArmor, SELinux, or open_basedirPath::isOwner() is called, attempts is_writable('/tmp'), fails, falls through to . (current directory) or session.save_pathDENIED operation="mknod" on /tmp/<hash>Joomla\Filesystem\File::delete: Failed deleting <hash>6.0
Able to save configuration.php using the UI.
Fixed by : joomla-framework/filesystem#80
| Labels |
Added:
bug
|
||
| Labels |
Added:
No Code Attached Yet
|
||
In configuration.php:
public $tmp_path = '/home/domain.tld/public_html/tmp';In PHP-FPM:
sys_temp_dir = "/home/domain.tld/tmp"Why is it hard coded at all? Shouldn't it use the Joomla configured directory first, then the system directory, then the current directory, then the session save path? Why not test and set the system temp path to the Joomla configured path, and failing that, use the system path? I came to this thread because my /tmp mounting glitched and I found all my installations had a problem no matter what tmp directory I set, including inside the web space, and all my isWritable tests were returning true.
What about...
// In Joomla bootstrap, after configuration loads
$configuredTmp = Factory::getApplication()->get('tmp_path');
if ($configuredTmp && is_dir($configuredTmp) && is_writable($configuredTmp)) {
$probe = rtrim($configuredTmp, '/\') . DIRECTORY_SEPARATOR . '.joomla_tmp_probe_' . getmypid();
if (@file_put_contents($probe, '') !== false) {
@unlink($probe);
// Configured path is verified working — promote it to PHP level
ini_set('sys_temp_dir', $configuredTmp);
putenv('JOOMLA_TMP=' . $configuredTmp);
}
// Write failed — leave PHP's sys_temp_dir unaltered
}
// Blank or invalid path — leave PHP's sys_temp_dir unaltered
Then in POath::isOwner
/**
Method to determine if script owns the path.
@param string $path Path to check ownership.
@return boolean True if the php script owns the path passed.
@SInCE 1.0
*/
public static function isOwner($path)
{
$tmp = md5(random_bytes(16));
// Try each candidate directory, verifying actual write capability
foreach ([sys_get_temp_dir(), '.', ini_get('session.save_path')] as $candidate) {
if (!$candidate || !is_dir($candidate) || !is_writable($candidate)) {
continue;
}
$test = rtrim($candidate, '/\\') . DIRECTORY_SEPARATOR . $tmp;
// Verify actual writability — is_writable() checks only permission bits
if (@file_put_contents($test, '') === false) {
continue;
}
// Test ownership
$return = fileowner($test) === fileowner($path);
// Clean up without throwing if removal fails
@unlink($test);
return $return;
}
return false;
}
Why is it hard coded at all? Shouldn't it use the Joomla configured directory first, then the system directory, then the current directory, then the session save path? Why not test and set the system temp path to the Joomla configured path, and failing that, use the system path? I came to this thread because my /tmp mounting glitched and I found all my installations had a problem no matter what tmp directory I set, including inside the web space, and all my isWritable tests were returning true.
What about...
// In Joomla bootstrap, after configuration loads
$configuredTmp = Factory::getApplication()->get('tmp_path');
if ($configuredTmp && is_dir($configuredTmp) && is_writable($configuredTmp)) {
$probe = rtrim($configuredTmp, '/\') . DIRECTORY_SEPARATOR . '.joomla_tmp_probe_' . getmypid();
if (@file_put_contents($probe, '') !== false) {
@unlink($probe);
// Configured path is verified working — promote it to PHP level
ini_set('sys_temp_dir', $configuredTmp);
putenv('JOOMLA_TMP=' . $configuredTmp);
}
// Write failed — leave PHP's sys_temp_dir unaltered
}
// Blank or invalid path — leave PHP's sys_temp_dir unaltered
Then in POath::isOwner
/**
Method to determine if script owns the path.
@param string $path Path to check ownership.
@return boolean True if the php script owns the path passed.
@SInCE 1.0
*/
public static function isOwner($path)
{
$tmp = md5(random_bytes(16));
// Try each candidate directory, verifying actual write capability
foreach ([sys_get_temp_dir(), '.', ini_get('session.save_path')] as $candidate) {
if (!$candidate || !is_dir($candidate) || !is_writable($candidate)) {
continue;
}
$test = rtrim($candidate, '/\\') . DIRECTORY_SEPARATOR . $tmp;
// Verify actual writability — is_writable() checks only permission bits
if (@file_put_contents($test, '') === false) {
continue;
}
// Test ownership
$return = fileowner($test) === fileowner($path);
// Clean up without throwing if removal fails
@unlink($test);
return $return;
}
return false;
}
Sorry. I see a couple of typo's in the comments. I think the code is correct. Happy to test it if this is thought to be a good course.
Sorry. I see a couple of typo's in the comments. I think the code is correct. Happy to test it if this is thought to be a good course.
In your global configuration what is the setting for the tmp path