php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #21087 flock($fp, LOCK_SH) causes fwrite() to fail
Submitted: 2002-12-18 18:58 UTC Modified: 2002-12-22 10:16 UTC
Votes:1
Avg. Score:4.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:0 (0.0%)
From: gfraley5 at earthlink dot net Assigned:
Status: Not a bug Package: Filesystem function related
PHP Version: 4.3.0RC3 OS: XP
Private report: No CVE-ID: None
 [2002-12-18 18:58 UTC] gfraley5 at earthlink dot net
This is just a heads up.  I can reproduce it but can't figure out why.  I have a guestbook script that I wrote (KISGB) and it works perfectly under all prior to 4.3.x versions.  However, 4.3.0RC3 (don't know about previous RC's) will not recreate the file.  Basically the script is trying to rewrite a configuration file.  It fails every time (writing out a file of zero length) but I can't find anything on the logs.  As soon as I restore a previous non 4.2.3x release, it works flawlessly.  I will continue to try to produce a smaller script to replicate it, but it will fail 100% of the time under 4.3.0.  I will also add that the script is a phpNUKE module, so it is operating within that framework.  It is a flat file, not a MySQL table.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2002-12-19 06:29 UTC] mira-junk at cekit dot cz
I've read similar bug report from several of our users (we have free hosting server with aprox. 60,000 users, currently running latest RC of php-4.3.0 with apache-2.0.43), but I've not been able to reproduce it. Can you please paste here part of your code ?
 [2002-12-19 07:16 UTC] gfraley5 at earthlink dot net
I'm running Apache 1.3.27.  I haven't tested it under *nix.  As far as a code snippet, I will assume that it is in this code, somewhere:

if ($admin_from_web==true) {
	$row = 0;
	$insert_msg = '';
	$fp = fopen("$path_to_gb",'r') or die("$unable_to_access_file_msg $path_to_gb");
	set_file_buffer($fp,$buffersize);
	flock($fp,1);
	$num = 16;
	for ($i=0;$i<$num;$i++) {
		$j = $i+1;
		$head_array[$i] = ${"col$j"};
	}
	while ($data = fgetcsv($fp,$csv_buffer_size,",")) {
		$row++;
		if ($row<2) continue;
		$new_msg = "";
		if ($data[0]==$msgid) {
			if (!empty($update)) {
				reset ($HTTP_POST_VARS);
				while (list ($key, $val) = each ($HTTP_POST_VARS)) {			
					$found=false;
					for ($i=0;$i<count($head_array);$i++) {
						if ($key==$head_array[$i]) {
							$rec[$i] = $val;
							$found=true;
						}
						if ($found) break;
					}
				}
				$msg = $label_name."$data[4] (ip = ".getenv(REMOTE_ADDR).") - $head_array[0]:$msgid ".$on." ".date($date_format)." $at ".date($time_format)."\n";
				for ($c=0;$c<$num;$c++) {
					if ($c==0) $comma=""; 
					else $comma=",";
					if ($c==13&&$row>1) {
						$rec[$c] = htmlspecialchars($rec[$c],ENT_QUOTES);
						$rec[$c] = str_replace("&amp;#","&#",$rec[$c]);						
						$new_msg .= $comma."\"$rec[$c]\"";						
					}
					elseif ($c<4) $new_msg .= $comma."$rec[$c]";
					else $new_msg .= $comma."\"$rec[$c]\"";
					if (chop($data[$c])!=chop($rec[$c])) {
							$msg .= "$head_array[$c]: [$data[$c]]\n\n$head_array[$c]: [$rec[$c]]\n\n\n";
					}
				}
				$new_msg .= "\n";
				if ($admin_email_advice_on_gb_edit && !empty($admin_email_address)) {
					$to = "$admin_email_address,$admin_email_address_addl";
					$subject = "$admin_email_subject_on_gb_edit";
					@mail($to, $subject, "$msg","From: $admin_email_address") ;
				}
			}
		}
		else {
			for ($c=0;$c<$num;$c++) {
				$val = $data[$c];
				if ($c==0) $comma=""; 
				else $comma=",";
				if ($c==13&&$row>1) $new_msg .= $comma."\"$val\"";
				elseif ($c<4&&$row>1) $new_msg .= $comma."$val";
				else $new_msg .= $comma."\"$val\"";
			}
			$new_msg .= "\n";						
		}
		if (!empty($new_msg)) $newgb[] = $new_msg;
	}
	flock($fp,3);
	fclose($fp);
	unset($delete);
	unset($update);
	$buffersize = round(filesize($path_to_gb)*1.5);	
	$fp = fopen("$path_to_gb",'w') or die("$unable_to_access_file_msg $path_to_gb");
	set_file_buffer($fp,$buffersize);
	flock($fp,1);	
	for ($i=0;$i<count($newgb);$i++) {
		if ($i==0) $prefx = "<? Header(\"HTTP/1.0 403 Forbidden\");exit; ?>\n";
		else $prefx = "";
		fwrite($fp,$prefx.$newgb[$i]);
	}
	flock($fp,3);
	fclose($fp);
}
 [2002-12-19 07:43 UTC] wez@php.net
Please try and cut down the script to a complete self-contained example that I can run here.
I'm not going to spend hours debugging a long, incomplete code fragment.
What I'm looking for it something like this:

$fp = fopen($filename, "w");
$len = fwrite($fp, $data);
fclose($fp);
echo "Wrote $len bytes";

If that works, try adding in the flock calls.
If that works, try adding in those set_file_buffer calls
(NB: there does not seem to be a need to play with file
buffering in this case).

Please help me to help you.
 [2002-12-19 11:17 UTC] gfraley5 at earthlink dot net
Sorry for the misunderstanding.  First of all I did not know that you were a developer working the problem. Nothing in your comment told me that.  Secondly, I have already stated that I will try to produce a smaller script (see my original post).  I just haven't had time to work the problem.  As to the buffering, without it, the full text is not written out.  It gets truncated.
 [2002-12-19 11:27 UTC] wez@php.net
Keep this at feedback.
(and note that I am not mira-junk@cekit.cz and that my previous comment on this was my first).
 [2002-12-19 21:21 UTC] gfraley5 at earthlink dot net
It appears to be the flock() call.  This fails.  However, if you comment out the flock() calls it works.

$filename = "c:/phptest.txt";
$data = "This is test text";
$fp = fopen("$filename",'w');
flock($fp,1);
$len = fwrite($fp,$data);
flock($fp,3);
fclose($fp);
echo "Wrote $len bytes";
 [2002-12-19 22:27 UTC] iliaa@php.net
Small note, the 2nd flock (flock($fp,3);) is entirely optional. The problem is occures if prior to write the LOCK_SH is set on the file, the problem does not happen if LOCK_EX is used.
 [2002-12-21 18:41 UTC] gfraley5 at earthlink dot net
Just a further note.  It also/still errors on RC4.  Also, if you use flock($fp,2) instead of flock($fp,1) it works.
 [2002-12-22 08:05 UTC] wez@php.net
Update summary.
LOCK_EX works fine.
Additionally, writing to the file prior to obtaining the lock also works fine, but defeats the purpose of the lock.
 [2002-12-22 08:51 UTC] wez@php.net
Your script is broken :)
It's taken a few hours for us to realize this and it's pretty easy to overlook.

You are locking the file for shared access and then writing to it.  This doesn't make sense, because you should really be using an exclusive lock (LOCK_EX) to do this.

Under unix systems, flock is an advisory lock, meaning that processes can ignore the lock status if they wish.

Under windows, the locking is enforced by the OS which forbids the write access, hence the zero-size file.

Additionally, the win32 docs state that closing a file that was locked (but not unlocked) leads to undefined behaviour, so that flock($fp, LOCK_UN) call *is* required for win32.
 [2002-12-22 09:09 UTC] gfraley5 at earthlink dot net
I am so sorry, but why does it [appear to] work under previous releases and only breaks under 4.3.0RC's?  have you tightened up the code?
 [2002-12-22 10:16 UTC] wez@php.net
I can't explain the sudden change in behaviour either.
The entire filesystem IO layer was rewritten since 4.2,
but AFAICT, the same functions are called in 4.2 as are
being called in 4.3, in the same order.
The only difference is that 4.3 prefers to use fd's rather
than FILE*'s, but in my tests here, using the FILE* gave
the same error.

*shrug*

 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Apr 24 10:01:31 2024 UTC