jeudi 9 juillet 2020

flock() between PHP and C edge case

I have a PHP script which receives and saves invoices as files in Linux. Later, a C++ infinite loop based program reads each and does some processing. I want the latter to read each file safely (only after fully written).

PHP side code simplification:

file_put_contents("sampleDir/invoice.xml", "contents", LOCK_EX)

On the C++ side (with C filesystem API), I must first note that I want to preserve a code which deletes the files in the designated invoices folder which are empty, just as a means to properly deal with the edge case of an empty file being created from other sources (not the PHP script).

Now, here's a C++ side code simplification, too:

FILE* pInvoiceFile = fopen("sampleDir/invoice.xml", "r");

if (pInvoiceFile != NULL)
{
    if (flock(pInvoiceFile->_fileno, LOCK_SH) == 0)
    {
        struct stat fileStat;
        fstat(pInvoiceFile->_fileno, &fileStat);
        string invoice;
        invoice.resize(fileStat.st_size);

        if (fread((char*)invoice.data(), 1, fileStat.st_size, pInvoiceFile) < 1)
        {
            remove("sampleDir/invoice.xml"); // Edge case resolution
        }

        flock(pInvoiceFile->_fileno, LOCK_UN);
    }
}

fclose(pInvoiceFile);

As you can see, the summarizing key concept is the cooperation of LOCK_EX and LOCK_SH flags.

My problem is that, while this integration has been working fine, yesterday I noticed the edge case executed for an invoice which should not be empty, and thus it got deleted by the C++ program.

PHP manual on file_put_contents mentions the following for the LOCK_EX flag:

Acquire an exclusive lock on the file while proceeding to the writing. In other words, a flock() call happens between the fopen() call and the fwrite() call. This is not identical to an fopen() call with mode "x".

  • Could the problem be caused as a race condition by the FLOCK_EX not being established right before file_put_contents calls fopen? If so, what could be done to solve this while keeping the edge case removal code?
  • Otherwise, may I be doing anything wrong overall?

Aucun commentaire:

Enregistrer un commentaire