php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #53076 file_put_contents doesn't release the LOCK_EX
Submitted: 2010-10-15 17:12 UTC Modified: 2010-11-25 20:39 UTC
From: admin at saltwaterc dot net Assigned:
Status: Not a bug Package: Scripting Engine problem
PHP Version: 5.3.3 OS: Irrelevant
Private report: No CVE-ID: None
 [2010-10-15 17:12 UTC] admin at saltwaterc dot net
Description:
------------
The name is pretty self explanatory. When the file_put_content function is used along with the LOCK_EX flag, the file descriptor / file handle doesn't get unlocked. Most of the time this won't be an issue, but depending on the underlying file system, it could lead to severe application crash. Such example is the GlusterFS version packaged for the Ubuntu 10.04 which would block the PHP process in uninterruptible sleep. I know that most of the time this situation won't affect real life scenarios, but it already took down a virtual host from our production cluster, based onto the above setup.

Test script:
---------------
<?php
file_put_contents('foo', 'bar', LOCK_EX);
$fh = fopen($file, 'rb');
flock($fh, LOCK_SH);
$file_contents = stream_get_contents($fh);
flock($fh, LOCK_UN);
fclose($fh);
echo $file_contents.PHP_EOL;

Expected result:
----------------
output: bar
strace:
getcwd("/media/glusterfs01/gluster-bug", 4096) = 31
lstat("/media/glusterfs01/gluster-bug/foo", {st_mode=S_IFREG|0644, st_size=3, ...}) = 0
open("/media/glusterfs01/gluster-bug/foo", O_WRONLY|O_CREAT, 0666) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=3, ...}) = 0
lseek(3, 0, SEEK_CUR)                   = 0
flock(3, LOCK_EX)                       = 0
ftruncate(3, 0)                         = 0
write(3, "bar", 3)                      = 3
flock(3, LOCK_UN)                       = 0
close(3)                                = 0
getcwd("/media/glusterfs01/gluster-bug", 4096) = 31
open("/media/glusterfs01/gluster-bug/foo", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=3, ...}) = 0
lseek(3, 0, SEEK_CUR)                   = 0
flock(3, LOCK_SH)                       = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=3, ...}) = 0
read(3, "bar", 8192)                    = 3
read(3, "", 8192)                       = 0
read(3, "", 8192)                       = 0
flock(3, LOCK_UN)                       = 0
close(3)                                = 0



Actual result:
--------------
output: none
strace:
getcwd("/media/glusterfs01/gluster-bug", 4096) = 31
lstat("/media/glusterfs01/gluster-bug/foo", {st_mode=S_IFREG|0644, st_size=3, ...}) = 0
open("/media/glusterfs01/gluster-bug/foo", O_WRONLY|O_CREAT, 0666) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=3, ...}) = 0
lseek(3, 0, SEEK_CUR)                   = 0
flock(3, LOCK_EX)                       = 0
ftruncate(3, 0)                         = 0
write(3, "bar", 3)                      = 3
flock(3, LOCK_UN)                       = 0
close(3)                                = 0
getcwd("/media/glusterfs01/gluster-bug", 4096) = 31
open("/media/glusterfs01/gluster-bug/foo", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=3, ...}) = 0
lseek(3, 0, SEEK_CUR)                   = 0
flock(3, LOCK_SH

And then it blocks here ...


Patches

file_put_contents.patch (last revision 2010-10-15 15:14 UTC by admin at saltwaterc dot net)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-10-15 17:16 UTC] admin at saltwaterc dot net
Test script:
---------------
<?php
file_put_contents('foo', 'bar', LOCK_EX);
$fh = fopen('foo', 'rb'); // there, I messed up the first time
flock($fh, LOCK_SH);
$file_contents = stream_get_contents($fh);
flock($fh, LOCK_UN);
fclose($fh);
echo $file_contents.PHP_EOL;
 [2010-10-16 02:05 UTC] cataphract@php.net
-Status: Open +Status: Feedback
 [2010-10-16 02:05 UTC] cataphract@php.net
I can't reproduce this. I get the output "bar".

File locks are released when the file are closed. Additionally, your 'actual result' section shows an unlock:

...
write(3, "bar", 3)                      = 3
flock(3, LOCK_UN)                       = 0 <<<<<---

Am I missing something?
 [2010-10-16 02:12 UTC] rasmus@php.net
Note the example code does an explicit unlock.  Are you sure that isn't what you 
are seeing there?

If you try: strace php -r 'file_put_contents('foo', 'bar', LOCK_EX);'
Do you see an unlock still?

I get:
open("/home/rasmus/foo", O_WRONLY|O_CREAT, 0666) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lseek(3, 0, SEEK_CUR)                   = 0
flock(3, LOCK_EX)                       = 0
ftruncate(3, 0)                         = 0
write(3, "bar", 3)                      = 3
close(3)                                = 0
 [2010-10-16 02:20 UTC] crrodriguez at opensuse dot org
Patch ACK'ed , looks correct.

BUT, you should also call php_stream_flush before closing the stream.. to make it 
slightly more reliable though there is still no warranty that the file is stored 
on the filesystem...

article worth reading http://thunk.org/tytso/blog/2009/03/12/delayed-allocation-
and-the-zero-length-file-problem/ ;)
 [2010-10-16 09:52 UTC] cataphract@php.net
I don't get an explicit LOCK_UN either, but it should be unnecessary, as locks are released when the file is closed.

@crrodriguez

There's no need to call php_stream_flush as that is already done when the stream is closed. See:
http://lxr.php.net/xref/PHP_TRUNK/main/streams/streams.c#_php_stream_free
 [2010-10-16 14:56 UTC] admin at saltwaterc dot net
My bad. I did the bug report in a little bit hurry.
The 'actual result' is broken since I use my patch
for my PHP build, while keeping an eye on my old 
strace outputs, with actual production setup: 
Invision Power Board + Minify + PHP 5.3.3 (PHP-FPM)
+ Ubuntu Server 10.04. Therefore, it's kinda 'emulated'.
But it does happen with the standard PHP of doing things.
However, the lock release when the stream is closed
depends onto the filesystem implementation.

See this changelog: 
http://ftp.zresearch.com/pub/gluster/glusterfs/3.0/3.0.3/GlusterFS_3.0.3_Release
_Notes.pdf

I know that usually this happens on buggy filesystems
that have issues with advisory locking, but in my 
humble opinion this doesn't mean that the file_put_contents
should not include those 3 lines of code that make things
to be a little bit stable from now on such as avoiding deadlocks
when things go unplanned. As I said, this specific conditions 
took down one of our production virtual hosts.
 [2010-10-16 17:20 UTC] cataphract@php.net
-Status: Feedback +Status: Open
 [2010-11-25 20:39 UTC] iliaa@php.net
-Status: Open +Status: Bogus
 [2010-11-25 20:39 UTC] iliaa@php.net
Sorry, but your problem does not imply a bug in PHP itself.  For a
list of more appropriate places to ask for help using PHP, please
visit http://www.php.net/support.php as this bug system is not the
appropriate forum for asking support questions.  Due to the volume
of reports we can not explain in detail here why your report is not
a bug.  The support channels will be able to provide an explanation
for you.

Thank you for your interest in PHP.

Closing the file will both release the locks and flush the data to disk. If you 
have an interrupt happen it between, the same could be true before flush or 
unlock.
 [2010-11-26 08:35 UTC] admin at saltwaterc dot net
Bogus? Seriously? That's a three line patch. A simple if. Now look, I
am not a wannabe researcher trying to find theoretical problems with
your scripting engine. I'm an actual system administrator, doing
actual live PHP deployments and that condition actually broke our
application (specifically an IPB product, forum + support module) with
our actual network filesystem. Real life situation, not Hollywood
junk.

I may not have your operating system, specifically *nix, experience,
but I can recognize an issue when it is present. I already told you
that not having that explicit unlock may cause deadlocks, depending
onto the *filesystem implementation*. I know, it's a very specific
situation, would (mostly?) happen on buggy (network?) filesystems, but
this attitude: "it's their junk, let them fix it" won't help anybody
when in production scenarios the applications start to crash.

I know that probably this would be ignored, and sometimes ignorance is
bliss, but ignorance doesn't fix software. Just don't tell me that I
am the one that doesn't understand the situation since you keep
quoting: "closing the file will both release the locks and flush the
data to disk" - this is simply not universally true. I rest my case,
do whatever you want.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat May 04 05:01:30 2024 UTC