Korvo
Korvo

Reputation: 9714

PHP Error deleting file

Error deleting file if there are multiple connections to multiple page. Error:Warning: unlink(folder/1.txt.txt) [function.unlink]: Permission denied in C:\htdocs\fopen.php on line 7

Note: If only one connection to access everything normally occurs (no error occurs).

PHP code fopen.php:

<?php
function fastWrite($a){
    echo 'Coping file: "',$a,'" to "',$a,'.txt"<br>';
    copy($a,$a.'.txt');

    echo 'Delete file: "',$a,'.txt"<br>';
    unlink($a.'.txt');
}

for($i=0;$i<10;$i++){
    fastWrite('folder/1.txt');
    echo '<hr>';
}
?>

html/javascript code (to simulate multiple connections):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
<title>my test</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript">
function myTest(z){
    $.ajax("fopen.php?time="+(new Date().getTime()),{"success":function(data){
        $("<div></div>").addClass("sty").html(data).appendTo("body");
    },"error":function(a,b,c){
        $("<div></div>").addClass("sty").html([a,b,c]).appendTo("body");
    }});
}
</script>
<style>
.sty{
border:1px #000 solid;
overflow:auto;
margin:5px 0 0 5px;
}
</style>
</head>
<body>
<p><a href="teste.html">New test</a></p>
<script type="text/javascript">
var dd = "";
for(var i=0;i<10;i++){
    dd += "myTest(\"#a"+(i+1)+"\");\n";
}
eval(dd);
</script>
</body>
</html>

What did I do wrong?

Thanks.


Solution: clearstatcache

Upvotes: 1

Views: 1315

Answers (2)

Salman Arshad
Salman Arshad

Reputation: 272026

The problem is that you have two or more scripts that write to and delete 1.txt.txt. This is called a race condition. Script1.php has no direct way of knowing if Script2.php is using a file, you need to implement this mechanism yourself.

A simple solution is to create a lock file before using the shared file and delete the lock file once you are done with it.

There is a new problem then: how do you ensure that the two scripts do not create the lock file at once? Script1.php might find that lock file isn't there but before it actually creates the file, the processor switches to Script2.php which also finds the lock file missing. What then?

PHP provides a useful flock function. I am not aware of the gory details but I believe it should solve your problem, to some extent, on some platforms at least.

<?php
function fastWrite($a)
{
    # //// LOCK \\\\
    $fp = fopen("fastwrite.lock", "w");
    if (flock($fp, LOCK_EX) === false) { # PHP will stop at this line until the lock is acquired
        die("flock failed");
    }
    # \\\\ LOCK ////

    echo 'Coping file: "', $a, '" to "', $a, '.txt"<br>';
    copy($a, $a . '.txt');

    echo 'Delete file: "', $a, '.txt"<br>';
    unlink($a . '.txt');

    # //// UNLOCK \\\\
    if (flock($fp, LOCK_UN) === false) {
        die("flock failed");
    }
    fclose($fp);
    # \\\\ UNLOCK ////

}
for ($i = 0; $i < 10; $i++) {
    fastWrite('1.txt');
    echo '<hr>';
}

PS: I was not able to reproduce the race condition on my system.

Upvotes: 1

slashingweapon
slashingweapon

Reputation: 11307

You're having a problem because two processes are trying to copy and delete the same file at the same time. Because they are separate processes, you can't easily control the order in which they do things.

Imagine two processes, running fastWrite() at the same time:

  • t1 copies 'a' to 'a.txt'
  • t2 copies 'a' to 'a.txt'
  • t2 deletes 'a.txt'
  • t1 tries to delete 'a.txt', but fails because it does not exist

This is called a "race condition".

If you don't mind that the unlink call will sometimes fail, you can ignore the error by using the '@' symbol in front of the command:

@unlink("$a.txt");

I'm pretty sure that saving user-generated data into the same file over and over again isn't your ultimate goal. You obviously encountered this problem in the pursuit of something larger. Maybe you should start a new question more focused on that problem.

If you just need a temporary file to work with during the connection, don't always name the file the same. Instead, you could:

function doStuffToFile($fname) {
    $tempName = $fname . "." . getmypid() . "." . rand();
    copy($fname, $tempName);
    // do stuff to your temporary file
    unlink($tempName);
}

Upvotes: 3

Related Questions