Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'unlink', Permission denied error when executing function [exec]

Tags:

php

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.

like image 339
D T Avatar asked Nov 28 '17 04:11

D T


1 Answers

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.

like image 173
smcjones Avatar answered Nov 11 '22 03:11

smcjones