Skip to content

Commit

Permalink
Tar: Added extraction support for long file names
Browse files Browse the repository at this point in the history
Supports POSIX ustar prefixes and GNU longlink entries
  • Loading branch information
splitbrain committed Nov 4, 2012
1 parent fba11f6 commit 421a270
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 16 deletions.
17 changes: 16 additions & 1 deletion _test/tests/inc/tar.test.php
Expand Up @@ -218,7 +218,6 @@ public function test_excludeextract(){
}
}


/**
* Check the extension to compression guesser
*/
Expand All @@ -234,4 +233,20 @@ public function test_filetype(){
$this->assertEquals(Tar::COMPRESS_BZIP, $tar->filetype('foo.tar.BZ2'));
$this->assertEquals(Tar::COMPRESS_BZIP, $tar->filetype('foo.tar.bz2'));
}

public function test_longpathextract(){
$dir = dirname(__FILE__).'/tar';
$out = sys_get_temp_dir().'/dwtartest'.md5(time());

foreach(array('ustar','gnu') as $format){
$tar = new Tar();
$tar->open("$dir/longpath-$format.tgz");
$tar->extract($out);

$this->assertFileExists($out.'/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/1234567890/test.txt');

TestUtils::rdelete($out);
}
}

}
Binary file added _test/tests/inc/tar/longpath-gnu.tgz
Binary file not shown.
Binary file added _test/tests/inc/tar/longpath-ustar.tgz
Binary file not shown.
43 changes: 28 additions & 15 deletions inc/Tar.class.php
Expand Up @@ -204,7 +204,7 @@ function extract($outdir, $strip='', $exclude='', $include='') {

// is this a file?
if(!$header['typeflag']){
$fp = @fopen($output, "wb");
$fp = fopen($output, "wb");
if(!$fp) throw(new TarIOException('Could not open file for writing: '.$output));

$size = floor($header['size'] / 512);
Expand Down Expand Up @@ -508,7 +508,6 @@ protected function writeFileHeader($name, $uid, $gid, $perm, $size, $mtime, $isd
/**
* Decode the given tar file header
*
* @todo how to handle filenames >100 chars?
* @param string $block a 512 byte block containign the header data
* @return array|bool
*/
Expand All @@ -521,22 +520,36 @@ protected function parseHeader($block) {
for($i = 156, $chks += 256; $i < 512; $i++)
$chks += ord($block[$i]);

$headers = @unpack("a100filename/a8perm/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $block);
if(!$headers) return false;
$header = @unpack("a100filename/a8perm/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix", $block);
if(!$header) return false;

$return['checksum'] = OctDec(trim($headers['checksum']));
$return['checksum'] = OctDec(trim($header['checksum']));
if($return['checksum'] != $chks) return false;

$return['filename'] = trim($headers['filename']);
$return['perm'] = OctDec(trim($headers['perm']));
$return['uid'] = OctDec(trim($headers['uid']));
$return['gid'] = OctDec(trim($headers['gid']));
$return['size'] = OctDec(trim($headers['size']));
$return['mtime'] = OctDec(trim($headers['mtime']));
$return['typeflag'] = $headers['typeflag'];
$return['link'] = trim($headers['link']);
$return['uname'] = trim($headers['uname']);
$return['gname'] = trim($headers['gname']);
$return['filename'] = trim($header['filename']);
$return['perm'] = OctDec(trim($header['perm']));
$return['uid'] = OctDec(trim($header['uid']));
$return['gid'] = OctDec(trim($header['gid']));
$return['size'] = OctDec(trim($header['size']));
$return['mtime'] = OctDec(trim($header['mtime']));
$return['typeflag'] = $header['typeflag'];
$return['link'] = trim($header['link']);
$return['uname'] = trim($header['uname']);
$return['gname'] = trim($header['gname']);

// Handle ustar Posix compliant path prefixes
if(trim($header['prefix'])) $return['filename'] = trim($header['prefix']).'/'.$return['filename'];

// Handle Long-Link entries from GNU Tar
if($return['typeflag'] == 'L'){
// following data block(s) is the filename
$filename = trim($this->readbytes(ceil($header['size'] / 512) * 512));
// next block is the real header
$block = $this->readbytes(512);
$return = $this->parseHeader($block);
// overwrite the filename
$return['filename'] = $filename;
}

return $return;
}
Expand Down

0 comments on commit 421a270

Please sign in to comment.