10

This is the file test1.php:

 <?php    
    set_time_limit(0);
    for($i= 1;$i<5000 ;$i++){

        $comm_sjis = "echo 'test'";
        $result = exec($comm_sjis);

    }
    unset($result);
    echo 'ok';

This is file test2.php:

<?php

set_time_limit(0);

function write_txt($str)
{
    $filepath = 'E:/temp/test.xml';
    if (($fp = @fopen($filepath, "a")) === false) {
        return;
    }

    if (!flock($fp, LOCK_EX)) {
        @fclose($fp);
        return;
    }

    if (fwrite($fp, $str . "\n") === false) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }

    if (!fflush($fp)) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }

    if (!flock($fp, LOCK_UN)) {
        @fclose($fp);
        return;
    }

    if (!fclose($fp)) {
        return;
    }
}

for($i= 1;$i<100 ;$i++){

    write_txt('test');  
    unlink('E:/temp/test.xml');
}
echo 'ok';

If I run file test2.php while test1.php is running, an error will occur:

Warning: unlink(E:/temp/test.xml): Permission denied in C:\xampp\htdocs\test2.php on line 45

When I only run test2.php, without test1.php, this error does not occur. Why does unlink give a Permission denied error when I execute the function?

I'm using XAMPP 3.2 vs php 5.6 with Windows 7.

D T
  • 3,522
  • 7
  • 45
  • 89
  • It's because your XML file or folder needs to permission for unlink that file `test.xml` – Nawin Nov 28 '17 at 04:43
  • test1 and test2 don't appear to match each other. What's test1 for and where do you create test.xml? – Forbs Nov 28 '17 at 04:44
  • @Nawin:if not run file test1.php, only run file test2.php, it is ok. – D T Nov 28 '17 at 04:47
  • @Forbs : write_txt('test'); to create test.xml – D T Nov 28 '17 at 04:48
  • yes, it is correct. you can try my code. it create file xml ok. – D T Nov 28 '17 at 05:01
  • Hello, I've been testing and here is what I found: Testing your files I get the same error. But if I comment in test2.php all instructions about using lock, the permission denied does not appear anymore. So, it's not a problem just for use unlink while exec, if you don't lock the file you don't get the error. However, wow, it's a strange behaviour. – José Carlos PHP Dec 03 '17 at 21:26
  • Here you can find my testing files: https://programadorphpfreelance.com/repositorio/test_unlink-exec.zip I'm sorry I said lock the file is a requirement to get the error, but no, is not. However, as you can see trying 3.php, unlink is not enough to get the error. – José Carlos PHP Dec 06 '17 at 10:46
  • have you tried this with all anti-virus and similar tools disabled? – mnagel Dec 06 '17 at 12:56
  • 2
    Ok, first, depending on command, you would concat them and then, call exec command. Say, every 5 iterations you would call exec. Second, there are better ways to avoid errors and / or warnings without using @, which is very discouraged. In your case, you could use http://php.net/manual/pt_BR/function.is-resource.php to check if the resource is valid instead of just silencing it. – Marco Dec 06 '17 at 16:59
  • @Nawin just some explanation about why that is correct an `if` does not test `==` or `===` the if test `true` or `false` the contents of the brackets are run first and return `true` or `false` setting a variable the contents of that var are returned so if that `$fp = false` the if is sent false the same as doing `if($fp)` after you have set `$fp` this is the most basic of if statment usage in php you should really go read some documentation and not just go by the basic code you have seen. – Barkermn01 Dec 07 '17 at 15:06
  • Installed XAMPP on Win10 and tried to replicate with OP's test1.php and test2.php only change was writing to my own C:\temp\test.xml tried to replicate by running test1.php in one console, and test2.php in the other and it all worked fine.. maybe Win10 has resolved the typically crappy Windows resource management. – DDeMartini Dec 07 '17 at 16:53
  • i test on win 10 , but it still occur error: Warning: unlink(E:/temp/test.xml): Permission denied – D T Dec 19 '17 at 02:33

4 Answers4

3

You're silencing errors in fopen which means that if, at any point, the file fails to open (perhaps because of a memory limit being reached in XAMPP, for example), you'd have no way of knowing in your script (you can view it in your logs).

From the PHP Manual:

bool unlink ( string $filename [, resource $context ] )

Deletes filename. Similar to the Unix C unlink() function. An E_WARNING level error will be generated on failure.

unlink deletes a file. That means that if your file fails to open using fopen, and you haven't created it already, it may well not exist. Trying to unlink a file that does not exist will result in an error.

An easy solution would be to silence errors on unlink as well.

@unlink('E:/temp/test.xml');

That way it will fail gracefully if your function fails to write a file. Another option is to check if the file exists before trying to unlink.

$file = 'E:/temp/test.xml';
if (file_exists($file)) {
    error_log(‘could not write file’);
    unlink($file);
}

My favored option in this case might be to use Exceptions. When you fail to open or fail to unlock a file, throw an Exception. You can catch it, log the issue and break the loop before you try to unlink.

That should help you debug what is going on.

Example: namespace Test;

class FileException extends \Exception { }
class UnlockFailedException extends FileException { }

function write_txt($str)
{
    $filepath = 'E:/temp/test.xml';
    if (($fp = @fopen($filepath, "a")) === false) {
        throw new FileException('Could not open file');
    }

    if (!flock($fp, LOCK_EX)) {
        @fclose($fp);
        return;
    }

    if (fwrite($fp, $str . "\n") === false) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }

    if (!fflush($fp)) {
        @flock($fp, LOCK_UN);
        @fclose($fp);
        return;
    }

    if (!flock($fp, LOCK_UN)) {
        @fclose($fp);
        throw new UnlockFailedException('Unable to unlock file.');
    }
    /** 
     * This doesn't do anything
     * if (!fclose($fp)) {
     *     return;
     * }
     */
    fclose($fp);
}

Then:

$msg = 'ok';
for($i= 1;$i<100 ;$i++){
    try {
        write_txt('test');  
        unlink('E:/temp/test.xml');
    } catch (FileException $e) {
        error_log($e->getMessage());
        $msg = 'errors detected';
    }
}
echo $msg;

By adding exception handling, you can debug this kind of behavior for yourself and find the root cause of the issue.

A couple final notes:

I run Linux, so it’s not easy for me to test this behavior on a Windows 7 XAMPP machine. However, I suspect it is because the system is locking due to limited I/O resources. Note that, according to the manual, Windows places a madatory lock on the file (whereas in Linux the lock is advisory). Running flock in a massive loop and echo in a massive loop on a limited resources system can lead to resources failing. If you intend to run something like this in production you won’t run into it as often but you will still need to account for it. Exception handling and error logging can ensure that when the file doesn't work you will have enough data to debug.

Assuming unlink is the problem is not a safe assumption at all.

As stated, silencing unlink or checking to see if the file exists will silence this error.

smcjones
  • 5,490
  • 1
  • 23
  • 39
  • I tried script without silence errors, and even returning true or false inside write_txt() function. There there is no problem. This is a strange behaviour this guy has discovered. The error is concerned to lock the file, if all instructions about lock are removered, the error doesn't appear. Please try before asume something that is only a guess. – José Carlos PHP Dec 04 '17 at 10:43
  • Are you using XAMPP? It kinda feels like you're trolling a little. Why would returning true or false make a difference? I don't need to test this thoroughly to understand how `unlink` works. I am trying to edit the question to make it more clear. – smcjones Dec 04 '17 at 22:30
  • I think, the problem is function "exec" and "unlink" call at a time, it will occur error. – D T Dec 05 '17 at 04:31
  • Please test yourself. First time I read this question it seems simple to me but I decide to test before answer and I found the strange behavior this guy has discovered. However, his test code is not good enough. I provide mine so you can try: https://programadorphpfreelance.com/repositorio/test_unlink-exec.zip It contents 1.php and 2.php files as original, but improved to not let a way for other errors. And also contents 2b.php and 3.php, they both are alternatives to 2.php, they content a header comment inside. Yes, I'm using XAMPP. No, I'm not trolling. – José Carlos PHP Dec 06 '17 at 10:42
  • I'm sorry I said lock the file is a requirement to get the error, but no, is not. However, as you can see trying 3.php, unlink is not enough to get the error. – José Carlos PHP Dec 06 '17 at 10:50
  • Can you convert that to a github gist or repo? I am reluctant to open zip files from the internet. – smcjones Dec 06 '17 at 14:03
0

Permission is denied, because the file is being used by another process.
Try fclose on file, before running to unlink.

Ali Farhoudi
  • 5,350
  • 7
  • 26
  • 44
-1

I solved this problem by upgrade PHP version to 7.1.12

D T
  • 3,522
  • 7
  • 45
  • 89
-4

Try this

 unlink ('E://temp//test.xml');

You might notice that you original code uses // to create but only / to delete.

Forbs
  • 1,256
  • 1
  • 7
  • 9