0

I'm using the following code to create and append data to a tar-archive in PHP. The problem is that phar does not use an exclusive lock on the tar-file causing problem when I have atomic writes to the same file.

function phar_put_contents($fname, $archive, $data) { 
  $i=0;
  do { 
    $fp = @fopen($archive.'.lock', 'w');
    if(!$fp) { 
      usleep(25);
      continue;
    } 
    if(flock($fp, LOCK_EX)) { 
      try{
        $myPhar = new PharData($archive.'.tar',0);
        $myPhar[$fname] = $data;
        $myPhar->stopBuffering();
        flock($fp, LOCK_UN) && @fclose($fp);
        @unlink($archive.'.lock');
        return true;
      } catch (Exception $e) { 
        error_log($e->getMessage()." in ".$e->getFile().":".$e->getLine(),0);
        unset($e);
        @flock($fp, LOCK_UN) && @fclose($fp);
      } 
    } 
  } while ($i++<8);
  return false;
}

Using a look file seems to be a "good" solution but it's not optimal since my archives gets currupted quite frequently.

Fredrik Erlandsson
  • 1,279
  • 1
  • 13
  • 22

1 Answers1

0

Ok, it seems like the Phar and PharData classes in PHP is somewhat unfinished, they neither have lock() nor close() making my approach for external locking nonworking..

The following code is what I used to try to have a function that appends data to a tar archive.

function phar_put_contents($fname, $archive, $data) { 
  $i=0;
  do { 
    $fp = @fopen($archive.'.lock', 'w');
    if(!$fp) { 
      usleep(25);
      continue;
    } 
    if(flock($fp, LOCK_EX)) { 
      try{
        file_put_contents('/tmp/'.$fname, $data);
        $tarCmd = "tar ". (file_exists($archive.".tar") ? "-rf ":"-cf ") .$archive.".tar  -C /tmp ".$fname;
        exec($tarCmd, $result, $status);
        if($status!=0)
          throw new Exception($tarCmd . implode($result, "\n"));
        @unlink('/tmp/'.$fname);
        flock($fp, LOCK_UN) && @fclose($fp);
        @unlink($archive.'.lock');
        return true;
      } catch (Exception $e) { 
        error_log($e->getMessage()." in ".$e->getFile().":".$e->getLine(),0);
        unset($e);
        @flock($fp, LOCK_UN) && @fclose($fp);
      } 
    } 
  } while ($i++<8);
  return false;
}

Note that I'm using exec() and calls the external version of tar. This were a necessity since Phar does very unreliable flushes to the archive, making the tar-file broken since two instances of the code can modify the same file at the same time.

Fredrik Erlandsson
  • 1,279
  • 1
  • 13
  • 22