Jump to content

Forum File Locking Problem


tHud

Recommended Posts

Hello :)

 

I have been using a certain forum software for many years now on an old php4 server.

I recently trialed the software on a php5 server in preparation for upgrading.

 

My forums appeared as plain white pages as soon as I altered any admin feature.

I tracked the casue down to a function that locks and writes to the admin file.

Whenever this function is called, the admin file gets wiped to zero bytes.

 

 

 

// Lock a file, and write if lock was successful

function lock_and_write($file, $body, $append = 0) {

ignore_user_abort(1);

$mode = ($append == 1) ? "a" : "w";

if ($fp = fopen($file, $mode)) {

if (flock($fp, LOCK_EX)) {

fwrite($fp, $body);

flock($fp, LOCK_UN);

fclose($fp);

ignore_user_abort(0);

return "";

} else {

fclose($fp);

ignore_user_abort(0);

return "no_lock";

}

} else {

ignore_user_abort(0);

return "no_write";

}

}

 

 

Can anyone suggest a better way of writing this function?

 

This forum is quite well known and in use on many different platforms, other users are not reporting problems - so I guess I'm an oddity.

I will seriously be considering moving to new software whether or not I can resolve this issue.

(The new company owner doesn't appear the least bit interested in the product.)

Edited by tHud
Link to comment
Share on other sites

I'm hosted on a shared server - so I have created this htaccess file.

php_flag display_errors on

 

php_value error_reporting E_ALL

 

Hopefully that's ok?

 

However, no errors are being reported.

When I update some admin setting the config file is being erased and all I see is a blank (white) screen.

Link to comment
Share on other sites

It is possible that the software is changing the setting in one of the configuration scripts. Try adding the following, as the first lines inside of that function, just long enough to test.

 

error_reporting(E_ALL);
ini_set('display_errors', 1);

Link to comment
Share on other sites

php_value error_reporting -1

 

You can't use the named constants like E_ALL in .htaccess, you have to use a numerical value.

 

Have you verified that $body contains the proper data? I'd speculate that there is a problem elsewhere causing $body to be blank. Also what OS is this being run on? Some form of linux I am guessing.

Link to comment
Share on other sites

As a side note, once the permission issue (or whatever, is resolved); I would consider rewriting that to use file_get_contents and file_put_contents. Which, unless I am mistaken, are atomic and would not require the LOCKing strategy that is being used.

 

Something like:

if ($append == 1) {
 $body = file_get_contents($file) . $body);
}
file_put_contents($file, $body);

 

Hmmm, well, file_put_contents() has a flag for APPEND and a flag for LOCK_EX. So, depending on the PHP version you are using, it could possibly be done without the file_get_contents() call.

 

Having said all that; keep in mind, once you start making changes to third party software, upgrading to a future release can become problematic.

Link to comment
Share on other sites

Hi,

 

.htaccess

 

php_flag display_errors on

 

php_value error_reporting 8191

 

 

 

From PHPinfo

PHP Version 5.3.3-1ubuntu9.7

 

System Linux svr59.XXXXXX.com 2.6.35-22-server #35-Ubuntu SMP Sat Oct 16 22:02:33 UTC 2010 x86_64

 

 

I added these lines

// Lock a file, and write if lock was successful

function lock_and_write($file, $body, $append = 0) {

 

error_reporting(E_ALL);

ini_set('display_errors', 1);

 

echo $body;

exit;

 

ignore_user_abort(1);

 

$mode = ($append == 1) ? "a" : "w";

 

if ($fp = fopen($file, $mode)) {

if (flock($fp, LOCK_EX)) {

fwrite($fp, $body);

flock($fp, LOCK_UN);

fclose($fp);

ignore_user_abort(0);

return "";

} else {

fclose($fp);

ignore_user_abort(0);

return "no_lock";

}

} else {

ignore_user_abort(0);

return "no_write";

}

}

...and I see that $body contains all the expected data of a config file.

 

Then I removed the echo /exit commands and the script predictably blanked the file again (unfortunately - no error reported).

 

 

I did try using file_put_contents (just to check - and it worked). By that I mean that wherever another script calls that function - I replaced it with f_p_c - but there are so many calls to that function, I felt it better to ask here first.

 

My inexperience with php meant that I was unable to re-write the original function using file_put_contents ..and of course, as you say - upgrades could be a serious issue ongoing.

Edited by tHud
Link to comment
Share on other sites

...

My forums appeared as plain white pages ...

 

When you get a blank white page, use the View -> Source feature of the browser to see if there is any "hidden" content. A malformed HTML tag could cause a problem. If there is anything there (in the view->source) show it to us.

 

The function is returning a string indicating success or failure, I wonder what is happening to that string, more specifically, I wonder what string it is returning. Can you show us the code that calls that function in the script you are having trouble with?

 

fopen with the "w" flag will truncate the file, so the indication is that one of the following functions is failing (flock or fwrite). There are no tests to see if the write or subsequent unlock is failing.

 

Just for debugging purposes, try adding some error triggers in the code. See the lines below that are marked with "##":

 

// Lock a file, and write if lock was successful
function lock_and_write($file, $body, $append = 0) {
 error_reporting(E_ALL);##
 ini_set('display_errors', 1);##

 ignore_user_abort(1);
 $mode = ($append == 1) ? "a" : "w";
 if ($fp = fopen($file, $mode)) {
   if (flock($fp, LOCK_EX)) {
    ##fwrite($fp, $body);
    if (! fwrite($fp, $body)) trigger_error('fwrite failed', E_USER_WARNING);##
    ##flock($fp, LOCK_UN);
    if (! flock($fp, LOCK_UN)) trigger_error('flock failed (unlock)', E_USER_WARNING);##
    fclose($fp);
    ignore_user_abort(0);
    return "";
   } else {
    trigger_error('flock failed (lock)', E_USER_WARNING);##
    fclose($fp);
    ignore_user_abort(0);
    return "no_lock";
   }
 } else {
   trigger_error('fopen failed', E_USER_WARNING);##
   ignore_user_abort(0);
   return "no_write";
 }
}

 

This should print one or more error (warning) messages.

Link to comment
Share on other sites

... but there are so many calls to that function, I felt it better to ask here first.

 

You don't have to replace all the areas that call the lock_and_write function, you would just replace that functions body.

function lock_and_write($file, $body, $append = 0){
  $flags = LOCK_EX;
  if ($append){
    $flags = $flags|FILE_APPEND;
  }

  file_put_contents($file, $body, $flags);
}

 

Link to comment
Share on other sites

I tried replacing the function with the one you posted - and it's the same thing.

No errors are reported and a white screen. (Source displays nothing except the #1 which I believe Firefox adds.)

 

Here are some examples of the calls to the function...

 

// Update community intro if necessary

$oldintro = @file_get_contents("{$config['FULL_PATH']}/includes/community_intro.php");

 

$intro_text = get_input("intro_body","post");

if ($intro_text != $oldintro) {

$check = lock_and_write("{$config['FULL_PATH']}/includes/community_intro.php",$intro_text);

if ($check == "no_write") {

$admin->error($ubbt_lang['NO_WRITE_INTRO']);

} // end if

} // end if

 

// Update the header file if necessary

$oldfile = "";

$fd = file("{$config['FULL_PATH']}/includes/header.php");

while (list($linenum,$line) = each($fd)) {

$oldfile .= "$line";

}

$headerfile = get_input("headerfile","post");

if ($headerfile!= $oldfile) {

 

$check = lock_and_write("{$config['FULL_PATH']}/includes/header.php",$headerfile);

if ($check == "no_write") {

$admin->error($ubbt_lang['NO_WRITE_HEADER']);

} // end if

} // end if

 

// Update the footer file if necessary

$oldfile = "";

$fd = file("{$config['FULL_PATH']}/includes/footer.php");

while (list($linenum,$line) = each($fd)) {

$oldfile .= "$line";

}

$footerfile = get_input("footerfile","post");

if ($footerfile!= $oldfile) {

$check = lock_and_write("{$config['FULL_PATH']}/includes/footer.php",$footerfile);

if ($check == "no_write") {

$admin->error($ubbt_lang['NO_WRITE_FOOTER']);

} // end if

} // end if

 

// Update the insert file if necessary

$oldfile = "";

$fd = file("{$config['FULL_PATH']}/includes/header-insert.php");

while (list($linenum,$line) = each($fd)) {

$oldfile .= "$line";

}

$insertfile = get_input("insertfile","post");

if ($insertfile!= $oldfile) {

$check = lock_and_write("{$config['FULL_PATH']}/includes/header-insert.php",$insertfile);

if ($check == "no_write") {

$admin->error($ubbt_lang['NO_WRITE_INSERT']);

} // end if

} // end if

Link to comment
Share on other sites

Just for completeness, I should point out the the database connection variables are stored in the blanked file - which clearly will account for the white pages in the admin area.

When trying to access the script as a user - there is an error message along the lines of ---- dbase error - contact admin.

Link to comment
Share on other sites

If you did not even get the WARNINGs I put in that last code, my guess is that they have a custom error handler which is trapping and hopefully logging the errors somewhere. You might try to find a log file from the app and see if it contains any recent messages.

 

The code you just posted is checking for the "no write" result, but not for the "no lock" result. So, it would appear that the flock call is failing. However, the file_put_contents() version should not have been affected by that.

 

Of course, we are assuming that you were correct when you stated that this function is the one causing the problems. I can't tell you how many times I've narrowed the problem down to a particular function, only to discover later that I was wrong about where the problem was.

 

Try this version of the function. I changed the trigger_error() calls to print() and added a print of the filesize on entry and exit. See what we can see.

 

// Lock a file, and write if lock was successful
function lock_and_write($file, $body, $append = 0) {
 error_reporting(E_ALL);##
 ini_set('display_errors', 1);##

print('Enter lock_and_write. Filesize = ' . filesize($file) . '<BR>');##
 ignore_user_abort(1);
 $mode = ($append == 1) ? "a" : "w";
 if ($fp = fopen($file, $mode)) {
       if (flock($fp, LOCK_EX)) {
        ##fwrite($fp, $body);
        if (! fwrite($fp, $body)) print('fwrite failed!<BR>');##
        ##flock($fp, LOCK_UN);
        if (! flock($fp, LOCK_UN)) print('flock failed (unlock)!<BR>');##
        fclose($fp);
        ignore_user_abort(0);
print('Exit lock_and_write (OK). Filesize = ' . filesize($file) . '<BR>');##
        return "";
       } else {
        print('flock failed (lock)!<BR>');##
        fclose($fp);
        ignore_user_abort(0);
print('Exit lock_and_write (no lock). Filesize = ' . filesize($file) . '<BR>');##
        return "no_lock";
       }
 } else {
       print('fopen failed!<BR>');##
       ignore_user_abort(0);
print('Exit lock_and_write (no write). Filesize = ' . filesize($file) . '<BR>');##
       return "no_write";
 }
}

Link to comment
Share on other sites

Of course, we are assuming that you were correct when you stated that this function is the one causing the problems. I can't tell you how many times I've narrowed the problem down to a particular function, only to discover later that I was wrong about where the problem was.

 

ha ha yes! In younger days I was an electronics engineer and I certainly know that feeling :)

 

Well, the screen flashed briefly before going blank but eventually I managed to copy these messages.

 

 

Enter lock_and_write. Filesize = 7316

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 7316

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

Enter lock_and_write. Filesize = 0

flock failed (lock)!

Exit lock_and_write (no lock). Filesize = 0

 

 

 

I have to tell you that I really appreciate every-bodies help. It's already more than the new owner of the software is prepared to do.

 

Thank you!

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.