No Code Attached Yet bug
avatar manusfreedom
manusfreedom
23 Jun 2026

What happened?

  1. Configure php-fpm with a custom temporary directory (e.g. sys_temp_dir = /var/lib/php/tmp in php.ini or pool config)
  2. Restrict access to /tmp via AppArmor, SELinux, or open_basedir
  3. Save Global Configuration in Joomla administrator
  4. Path::isOwner() is called, attempts is_writable('/tmp'), fails, falls through to . (current directory) or session.save_path
  5. AppArmor logs DENIED operation="mknod" on /tmp/<hash>
  6. The UI displays: Joomla\Filesystem\File::delete: Failed deleting <hash>

Version

6.0

Expected result

Able to save configuration.php using the UI.

Actual result

Image

System Information

  • Ubuntu 24.04
  • PHP 8.*
  • /tmp protected (AppArmor)
  • Joomla 5 & 6

Additional Comments

Fixed by : joomla-framework/filesystem#80

avatar manusfreedom manusfreedom - open - 23 Jun 2026
avatar manusfreedom manusfreedom - change - 23 Jun 2026
Labels Added: bug
avatar manusfreedom manusfreedom - labeled - 23 Jun 2026
avatar joomla-cms-bot joomla-cms-bot - change - 23 Jun 2026
Labels Added: No Code Attached Yet
avatar joomla-cms-bot joomla-cms-bot - labeled - 23 Jun 2026
avatar brianteeman
brianteeman - comment - 24 Jun 2026

In your global configuration what is the setting for the tmp path

avatar manusfreedom
manusfreedom - comment - 24 Jun 2026

In configuration.php:

        public $tmp_path = '/home/domain.tld/public_html/tmp';

In PHP-FPM:

sys_temp_dir                             = "/home/domain.tld/tmp"
avatar neo314
neo314 - comment - 17 Jul 2026

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;
    }


    This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/48010.

avatar neo314
neo314 - comment - 17 Jul 2026

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;
    }


    This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/48010.

avatar neo314
neo314 - comment - 17 Jul 2026

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.


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/48010.

avatar neo314
neo314 - comment - 17 Jul 2026

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.


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/48010.

Add a Comment

Login with GitHub to post a comment