file_put_contents(temporary://...): Failes to open stream: "Drupal\Core\StreamWrapper\TemporaryStream::stream_open" call failed

Created on 13 December 2024, about 1 month ago

i've moved D11 temp folder to `/tmp/drupal/mysite`.

in 'settings.php',

$settings = [
  ...
  'file_public_path' => 'sites/default/files',
  'file_private_path' => '/srv/drupal/private',
  'file_temp_path' => '/tmp/drupal/mysite',
  ...

confirming,

drush status | grep -E "^Drupal version|^Files"
	Drupal version   : 11.0.9
	Files, Public    : sites/default/files
	Files, Private   : /srv/drupal/private
	Files, Temp      : /tmp/drupal/mysite

perms are

ls -ald \
 /srv/drupal \
 /srv/drupal/private \
 /tmp/drupal \
 /tmp/drupal/mysite

	drwxrwx---  7 wwwrun www 4.0K Dec 13 10:05 /srv/drupal/
	drwxrwx---  2 wwwrun www 4.0K Dec 13 10:13 /srv/drupal/private/
	drwxrwx---  3 wwwrun www   60 Dec 12 22:57 /tmp/drupal/
	drwxrwx---  2 wwwrun www   80 Dec 13 11:12 /tmp/drupal/mysite/

it's writeable

sudo -u wwwrun touch /tmp/drupal/mysite/test.txt
ls -al  /tmp/drupal/mysite/test.txt
	-rw-r--r-- 1 wwwrun www 0 Dec 13 11:12 /tmp/drupal/mysite/test.txt

on site access, https://example.com/, i see

Notice: tempnam(): file created in the system's temporary directory in Drupal\Core\File\FileSystem->tempnam() (line 274 of core/lib/Drupal/Core/File/FileSystem.php).

	Drupal\Core\File\FileSystem->tempnam() (Line: 484)
	Drupal\Core\File\FileSystem->saveData() (Line: 56)
	Drupal\Core\Asset\AssetDumper->dumpToUri() (Line: 205)
	Drupal\system\Controller\AssetControllerBase->deliver()
	call_user_func_array() (Line: 123)
	Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->{closure:Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::wrapControllerExecutionInRenderContext():121}() (Line: 593)
	Drupal\Core\Render\Renderer->executeInRenderContext() (Line: 121)
	Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext() (Line: 97)
	Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->{closure:Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::onController():96}() (Line: 183)
	Symfony\Component\HttpKernel\HttpKernel->handleRaw() (Line: 76)
	Symfony\Component\HttpKernel\HttpKernel->handle() (Line: 53)
	Drupal\Core\StackMiddleware\Session->handle() (Line: 48)
	Drupal\Core\StackMiddleware\KernelPreHandle->handle() (Line: 28)
	Drupal\Core\StackMiddleware\ContentLength->handle() (Line: 32)
	Drupal\big_pipe\StackMiddleware\ContentLength->handle() (Line: 106)
	Drupal\page_cache\StackMiddleware\PageCache->pass() (Line: 85)
	Drupal\page_cache\StackMiddleware\PageCache->handle() (Line: 48)
	Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() (Line: 51)
	Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() (Line: 36)
	Drupal\Core\StackMiddleware\AjaxPageState->handle() (Line: 51)
	Drupal\Core\StackMiddleware\StackedHttpKernel->handle() (Line: 709)
	Drupal\Core\DrupalKernel->handle() (Line: 19)


Warning: file_put_contents(temporary://file11v996hs0u5ceWbEZpg): Failed to open stream: "Drupal\Core\StreamWrapper\TemporaryStream::stream_open" call failed in Drupal\Core\File\FileSystem->saveData() (line 485 of core/lib/Drupal/Core/File/FileSystem.php).

	Drupal\Core\File\FileSystem->saveData() (Line: 56)
	Drupal\Core\Asset\AssetDumper->dumpToUri() (Line: 205)
	Drupal\system\Controller\AssetControllerBase->deliver()
	call_user_func_array() (Line: 123)
	Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->{closure:Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::wrapControllerExecutionInRenderContext():121}() (Line: 593)
	Drupal\Core\Render\Renderer->executeInRenderContext() (Line: 121)
	Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext() (Line: 97)
	Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->{closure:Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::onController():96}() (Line: 183)
	Symfony\Component\HttpKernel\HttpKernel->handleRaw() (Line: 76)
	Symfony\Component\HttpKernel\HttpKernel->handle() (Line: 53)
	Drupal\Core\StackMiddleware\Session->handle() (Line: 48)
	Drupal\Core\StackMiddleware\KernelPreHandle->handle() (Line: 28)
	Drupal\Core\StackMiddleware\ContentLength->handle() (Line: 32)
	Drupal\big_pipe\StackMiddleware\ContentLength->handle() (Line: 106)
	Drupal\page_cache\StackMiddleware\PageCache->pass() (Line: 85)
	Drupal\page_cache\StackMiddleware\PageCache->handle() (Line: 48)
	Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() (Line: 51)
	Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() (Line: 36)
	Drupal\Core\StackMiddleware\AjaxPageState->handle() (Line: 51)
	Drupal\Core\StackMiddleware\StackedHttpKernel->handle() (Line: 709)
	Drupal\Core\DrupalKernel->handle() (Line: 19)
🐛 Bug report
Status

Active

Version

11.0 🔥

Component

base system

Created by

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @pgndrupal
  • It looks like a permissions issue or that the directory is totally unreachable by the web server process. You are in the best position to solve this one. It isn't a bug in Drupal.

    Is there selinux or other advanced ACL or other file access security system running?

  • no se linux. no app armor.

    it's not perms.
    no combination of dir perms (777, 775, 770, etc) of any/all of the path dirs in `/tmp/drupal/mysite` makes any difference -- same error.
    sticky bit on the dir makes no diff, either.

    checking, any other app running as same user/group (wwwrun:www) under same webserver (nginx) -- namely wordpress, symfony/sulu, joomla, own site, etc -- that needs/uses /tmp space, can be pointed at that same dir, `/tmp/drupal/mysysite`, with no issues.

    D11, using that `/tmp/**/*` path, consistenly reports the error as reported.

    BUT ...

    moving /tmp space to any *other* top-dir location -- e.g., /var/lib/wwwrun/tmp ``` works just fine.
    no error reported, atm.

    what's "different" and common here about `/tmp`, is that it's a shared memory mount:

    mount | grep "/tmp"
      tmpfs on /tmp type tmpfs (rw,nosuid,nodev,nr_inodes=1048576,inode64)
    

    it appears that tmpfs makes it unhappy.

    now as to why, dunno yet. afaik, `saveData()` _shouldn't_ have any issues with tmpfs or not.

    looking online for any relevant or related issues/bugs hasn't borne fruit. yet.

  • @cilefen

    > It looks like a permissions issue or that the directory is totally unreachable by the web server process.

    it doesn't look like a permissions issue to me.
    and, the directory is demonstrably reachable -- for read & write -- by the web server process.

    just, atm, not by D11.

  • this is consistently reproducible here across multiple instances -- different hardware, Drupal installs, persons doing the work.

    for us, it's only an issue -- temp space usags on tmpfs -- with Drupal installs.

    we have a working 'fix' -- don't use tmpfs with Drupal.

    if that's considered a 'support request', and functions "as intended", n/p & noted; pls feel free to close this.

  • 🇺🇸United States nyroofing

    To resolve this issue, ensure that the temporary directory is correctly configured in Configuration > Media > File System and that it has the necessary write permissions. Additionally, verify that the server's PHP configuration allows writing to the specified directory.

  • Maybe it is a bug. Add the steps to reproduce it with a common linux distribution to the issue summary and someone will probably have a look.

    Here is the call point with the warning: https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Co...

    It's a little difficult to remove Drupal to test this, with, say, a plain PHP script, because setting up the temporary:// stream wrapper involves a few classes. Otherwise, I would suggest trying that.

  • Actually it would be interesting to know if you were to make a PHP file in the web root called test.php with these contents:

    <?php
    if (file_put_contents('/tmp/drupal/mysite/foobar', 'test') === FALSE) {
          throw new \Exception("Temporary file could not be created.");
    }
    

    Then browse to https://example.com/test.php. This would be a basic test that doesn't use a stream wrapper. What happens?

  • 🇮🇳India ramprassad

    This does not seem to be a drupal issue as file_put_contents with the direct path as mentioned in #19 also doesn't work.

  • @cliefen

    > Then browse to https://example.com/test.php. This would be a basic test that doesn't use a stream wrapper. What happens?

    rm -rf /tmp/drupal
    mkdir -p /tmp/drupal/mysite
    find /tmp/drupal -type d -exec chmod 750 {} \; -exec chown wwwrun:www {} \;
    
    cat /srv/my.app/web/test.php
    	<?php
    
    	if (file_put_contents('/tmp/drupal/mysite/foobar.txt', 'test') === FALSE) {
    		throw new \Exception("Temporary file could not be created.");
    	} else {
    		echo "Temp file created";
    	}
    
    sudo -u wwwrun php /srv/my.app/web/test.php
    	Temp file created
    
    tree -Csup /tmp/drupal/
    	[drwxr-x--- wwwrun            60]  /tmp/drupal/
    	└── [drwxr-x--- wwwrun            60]  mysite
    		└── [-rw-r--r-- wwwrun             4]  foobar.txt
    
    cat /tmp/drupal/mysite/foobar.txt
    	test
    
    rm -f /tmp/drupal/mysite/foobar.txt
    
    nav to: https://example.com/test.php
    
    ==> /var/log/nginx/php-fpm-www.log <==
    	[20-Dec-2024 21:43:44 America/New_York] PHP Warning:  file_put_contents(/tmp/drupal/mysite/foobar.txt): Failed to open stream: No such file or directory in /srv/my.app/web/test.php on line 3
    	[20-Dec-2024 21:43:44 America/New_York] PHP Fatal error:  Uncaught Exception: Temporary file could not be created. in /srv/my.app/web/test.php:4
    	Stack trace:
    	#0 {main}
    	thrown in /srv/my.app/web/test.php on line 4
    
  • 🇺🇸United States cmlara

    @pgndrupal Can you check if your deployment is using SystemD PrivateTmp=true?
    https://stackoverflow.com/questions/53700985/why-cant-write-info-into-tm...

    Note for anyone using #8 you MUST mkdir /tmp/drupal/mysite/ (do not do so in the PHP script as that will contaminate the test) first otherwise file_put_contents will always fail.

    Another test for @pgndrupal would be in settings.php to put @mkdir('/tmp/drupal/mysite/');. If the issue goes away that also implies that PHP is running with a private /tmp space.

  • @cmlara

    indeed!

    in `/etc/systemd/system/php-fpm.service`

    i've

    	[Service]
    	PrivateTmp=true
    

    if

    -	PrivateTmp=true
    +	#PrivateTmp=true
    

    then Drupal's happy with the tempfile location in `/tmp/drupal/**/*`!

    `php-fpm.service` does NOT have a specified `User=/Group=`

    but DOES launch as

    ExecStart=/usr/sbin/php-fpm \
     --nodaemonize \
     --pid /run/nginx/php-fpm.pid \
     --fpm-config /usr/local/etc/php8/php-fpm.conf \
     -c /usr/local/etc/php8/php.ini
    

    where

    cat /usr/local/etc/php8/php-fpm.conf
    	...
    	[www]
    	user         = wwwrun
    	group        = www
    	listen       = /run/nginx/php-fpm.sock
    	listen.owner = wwwrun
    	listen.group = www
    	listen.mode  = 0660
    	...
    

    so

    ps aux | grep php-fpm
    	root     35046  0.0  0.0 697120 43896 ?        Ss   22:11   0:00 php-fpm: master process (/usr/local/etc/php8//php-fpm.conf)
    	wwwrun   35054  0.0  0.0 697120 19416 ?        S    22:11   0:00 php-fpm: pool www
    	wwwrun   35055  0.0  0.0 697120 15040 ?        S    22:11   0:00 php-fpm: pool www
    	wwwrun   35056  0.0  0.0 697120 15168 ?        S    22:11   0:00 php-fpm: pool www
    	wwwrun   35057  0.0  0.0 697120 15168 ?        S    22:11   0:00 php-fpm: pool www
    	wwwrun   35058  0.0  0.0 697120 15168 ?        S    22:11   0:00 php-fpm: pool www
    

    if i

    -	#PrivateTmp=true
    +	PrivateTmp=true
    

    and

    eval echo "~wwwrun"
    	/var/lib/wwwrun
    
    edit settings.php`
    	...
    	$settings = [
    		...$settings,
    	-	'file_temp_path' => '/tmp/drupal/mysite',
    	+	'file_temp_path' => '/var/lib/wwwrun/tmp/drupal/mysite',
    		],
    	...
    

    then it also works, despite the 'correct' ownership of `/tmp/drupal/mysite`.

    which sure seems like it's a namespace issue, as a result of that `PrivateTmp=true`.

    reading,

    https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.htm...

    i'm unclear whether i can 'fix' the issue for Drupal, leaving the (some?) `PrivateTmp=` protection in place.

    i need to take a closer look @ `JoinsNamespaceOf=` ...

    i've not had an issue with php-fpm service's `PrivateTmp=true` with any other service using php-fpm.
    i don't know if this is, or isn't, intended behavior in Drupal.

  • 🇺🇸United States cmlara

    i've not had an issue with php-fpm service's `PrivateTmp=true` with any other service using php-fpm.
    i don't know if this is, or isn't, intended behavior in Drupal.

    I believe this is intended behavior, that any path configured for $settings['file_temp_path'] is expected to be present and accessible to Drupal at the time we are bootstrapped. In other words, I believe it is intentional that we will not call mkdir($settings['file_temp_path]).

    Perhaps other systems 'tried to help you' by recursively creating these folders however they would have masked the underlying 'fault' that you are not directly using /tmp and instead going through a systemd provided abstraction layer.

    Since we don't mkdir() the path, and since PHP-FPM gets a 'virtual' /tmp that does not already have this directory fopen() fails as "path does not exist" which is where the errors above come from. In theory the mkdir() suggestion from #12 could exist indefinitely as a solution for you.

  • not clear what the `mkdir` point you're making is ... (pre)creating the dir doesn't appear to help.
    this fails/failed even IF the dir exists -- separately/previously created -- as above in #11

    the solution is to have Drupal write it's "tempfile" to OTHER than non-allowed `/tmp`.
    either to a different mount path, or _somehow_ by pointing Drupal at the systemd-generated namespaced dynamic /tmp path.

    or, of course, turning OFF `PrivateTmp=true`

Production build 0.71.5 2024