OK sure
OK sure

Reputation: 2656

Docker, TravisCI, PHP file uploads and intermittent permissions error on /tmp

I have an intermittent file upload error, only occurring in Travis:

Warning: file_put_contents(/tmp/customerDownload_1502892540/image.png): failed to open stream: Permission denied (500 Internal Server Error)

This is a Symfony 2.8 project running on a PHP 7.1 Docker container. There's a Behat scenario to test file uploading by administrators and downloading by users. My method for creating the file is as follows:

/**
 * @param string $fileContents
 * @param Media $file
 * @return File
 */
private function createLocalTemporaryFile(string $fileContents, Media $file): File
{
    $tmpDir = '/tmp/customerDownload_' . time();
    if (!file_exists($tmpDir)) {
        mkdir($tmpDir);
    }
    $tmpFilePath = $tmpDir . '/' . $file->getName();
    file_put_contents($tmpFilePath, base64_decode($fileContents));
    $tmpFile = new File(realpath($tmpFilePath));

    return $tmpFile;
}

It fails about 20% of the time which is a significant amount. But never locally or in production. I've tried setting permissions on /tmp to include the www-data user:group but it has no effect. I'm confused as to why it wouldn't be able to put contents to a file in a directory it had created in the first place.

Can anyone suggest why this might be happening, or how to ensure it doesn't?

Upvotes: 1

Views: 241

Answers (1)

OK sure
OK sure

Reputation: 2656

I believe this to be some sort of race condition and file naming issue. I'm using the same file for tests and perhaps the tests are running fast enough that it's trying to write over a file and and cannot. Updated the method to the following and so far in 12 tests, hasn't failed.

/**
 * @param string $fileContents
 * @param Media $file
 * @return File
 */
private function createLocalTemporaryFile(string $fileContents, Media $file): File
{
    $tmpDir = '/tmp/' . uniqid('customerDownload_', true);
    mkdir($tmpDir);
    $tmpFilePath = $tmpDir . '/' . $file->getName();
    file_put_contents($tmpFilePath, base64_decode($fileContents));
    $tmpFile = new File(realpath($tmpFilePath));

    return $tmpFile;
}

Upvotes: 1

Related Questions