You are here

private function TarArchiveReader::extractAllToDirectory in Backup and Migrate 8.4

Parameters

$directory: The directory to extract the files to.

Return value

bool

Throws

\BackupMigrate\Core\Exception\BackupMigrateException

1 call to TarArchiveReader::extractAllToDirectory()
TarArchiveReader::extractTo in lib/backup_migrate_core/src/Service/TarArchiveReader.php
Extract all files to the given directory.

File

lib/backup_migrate_core/src/Service/TarArchiveReader.php, line 61

Class

TarArchiveReader
Class TarArchiveReader.

Namespace

BackupMigrate\Core\Service

Code

private function extractAllToDirectory($directory) {
  clearstatcache();

  // Read a header block.
  while (strlen($block = $this->archive
    ->readBytes(512)) != 0) {
    $header = $this
      ->readHeader($block);
    if (!$header) {
      return FALSE;
    }
    if ($header['filename'] == '') {
      continue;
    }

    // Check for potentially malicious files (containing '..' etc.).
    if ($this
      ->maliciousFilename($header['filename'])) {
      throw new BackupMigrateException('Malicious .tar detected, file %filename. Will not install in desired directory tree', [
        '%filename' => $header['filename'],
      ]);
    }

    // ignore extended / pax headers.
    if ($header['typeflag'] == 'x' || $header['typeflag'] == 'g') {
      $this->archive
        ->seekBytes(ceil($header['size'] / 512));
      continue;
    }

    // Add the destination directory to the path.
    if (substr($header['filename'], 0, 1) == '/') {
      $header['filename'] = $directory . $header['filename'];
    }
    else {
      $header['filename'] = $directory . '/' . $header['filename'];
    }

    // If the file already exists, make sure we can overwrite it.
    if (file_exists($header['filename'])) {

      // Cannot overwrite a directory with a file.
      if (@is_dir($header['filename']) && $header['typeflag'] == '') {
        throw new BackupMigrateException('File %filename already exists as a directory', [
          '%filename' => $header['filename'],
        ]);
      }

      // Cannot overwrite a file with a directory.
      if (@is_file($header['filename']) && !@is_link($header['filename']) && $header['typeflag'] == "5") {
        throw new BackupMigrateException('Directory %filename already exists as file', [
          '%filename' => $header['filename'],
        ]);
      }

      // Cannot overwrite a read-only file.
      if (!is_writeable($header['filename'])) {
        throw new BackupMigrateException('File %filename already exists and is write protected', [
          '%filename' => $header['filename'],
        ]);
      }
    }

    // Extract a directory.
    if ($header['typeflag'] == "5") {
      if (!$this
        ->createDir($header['filename'])) {
        throw new BackupMigrateException('Unable to create directory %filename', [
          '%filename' => $header['filename'],
        ]);
      }
    }
    else {
      if (!$this
        ->createDir(dirname($header['filename']))) {
        throw new BackupMigrateException('Unable to create directory for %filename', [
          '%filename' => $header['filename'],
        ]);
      }

      // Symlink.
      if ($header['typeflag'] == "2") {
        if (@file_exists($header['filename'])) {
          @unlink($header['filename']);
        }
        if (!@symlink($header['link'], $header['filename'])) {
          throw new BackupMigrateException('Unable to extract symbolic link: %filename', [
            '%filename' => $header['filename'],
          ]);
        }
      }
      else {

        // Open the file for writing.
        if (($dest_file = @fopen($header['filename'], "wb")) == 0) {
          throw new BackupMigrateException('Error while opening %filename in write binary mode', [
            '%filename' => $header['filename'],
          ]);
        }

        // Write the file.
        $n = floor($header['size'] / 512);
        for ($i = 0; $i < $n; $i++) {
          $content = $this->archive
            ->readBytes(512);
          fwrite($dest_file, $content, 512);
        }
        if ($header['size'] % 512 != 0) {
          $content = $this->archive
            ->readBytes(512);
          fwrite($dest_file, $content, $header['size'] % 512);
        }
        @fclose($dest_file);

        // Change the file mode, mtime.
        @touch($header['filename'], $header['mtime']);
        if ($header['mode'] & 0111) {

          // make file executable, obey umask.
          $mode = fileperms($header['filename']) | ~umask() & 0111;
          @chmod($header['filename'], $mode);
        }
        clearstatcache();

        // Check if the file exists.
        if (!is_file($header['filename'])) {
          throw new BackupMigrateException('Extracted file %filename does not exist. Archive may be corrupted.', [
            '%filename' => $header['filename'],
          ]);
        }

        // Check the file size.
        $file_size = filesize($header['filename']);
        if ($file_size != $header['size']) {
          throw new BackupMigrateException('Extracted file %filename does not have the correct file size. File is %actual bytes (%expected bytes expected). Archive may be corrupted', [
            '%filename' => $header['filename'],
            '%expected' => (int) $header['size'],
            (int) '%actual' => $file_size,
          ]);
        }
      }
    }
  }
  return TRUE;
}