/** * @param $directory * The directory to extract the files to. * @return bool * @throws \BackupMigrate\Core\Exception\BackupMigrateException */ 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; }