comparison core/lib/Drupal/Core/Archiver/ArchiveTar.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents 4c8ae668cc8c
children af1871eacc83
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
40 * @link http://pear.php.net/package/Archive_Tar 40 * @link http://pear.php.net/package/Archive_Tar
41 */ 41 */
42 42
43 /** 43 /**
44 * Note on Drupal 8 porting. 44 * Note on Drupal 8 porting.
45 * This file origin is Tar.php, release 1.4.0 (stable) with some code 45 * This file origin is Tar.php, release 1.4.5 (stable) with some code
46 * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net. 46 * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net.
47 * To simplify future porting from pear of this file, you should not 47 * To simplify future porting from pear of this file, you should not
48 * do cosmetic or other non significant changes to this file. 48 * do cosmetic or other non significant changes to this file.
49 * The following changes have been done: 49 * The following changes have been done:
50 * Added namespace Drupal\Core\Archiver. 50 * Added namespace Drupal\Core\Archiver.
51 * Removed require_once 'PEAR.php'. 51 * Removed require_once 'PEAR.php'.
52 * Added defintion of OS_WINDOWS taken from PEAR.php. 52 * Added definition of OS_WINDOWS taken from PEAR.php.
53 * Renamed class to ArchiveTar. 53 * Renamed class to ArchiveTar.
54 * Removed extends PEAR from class. 54 * Removed extends PEAR from class.
55 * Removed call parent:: __construct(). 55 * Removed call parent:: __construct().
56 * Changed PEAR::loadExtension($extname) to this->loadExtension($extname). 56 * Changed PEAR::loadExtension($extname) to this->loadExtension($extname).
57 * Added function loadExtension() taken from PEAR.php. 57 * Added function loadExtension() taken from PEAR.php.
150 * @var object PEAR_Error object 150 * @var object PEAR_Error object
151 */ 151 */
152 public $error_object = null; 152 public $error_object = null;
153 153
154 /** 154 /**
155 * Format for data extraction
156 *
157 * @var string
158 */
159 public $_fmt ='';
160 /**
155 * Archive_Tar Class constructor. This flavour of the constructor only 161 * Archive_Tar Class constructor. This flavour of the constructor only
156 * declare a new Archive_Tar object, identifying it by the name of the 162 * declare a new Archive_Tar object, identifying it by the name of the
157 * tar file. 163 * tar file.
158 * If the compress argument is set the tar will be read or created as a 164 * If the compress argument is set the tar will be read or created as a
159 * gzip or bz2 compressed TAR file. 165 * gzip or bz2 compressed TAR file.
179 $data = fread($fp, 2); 185 $data = fread($fp, 2);
180 fclose($fp); 186 fclose($fp);
181 if ($data == "\37\213") { 187 if ($data == "\37\213") {
182 $this->_compress = true; 188 $this->_compress = true;
183 $this->_compress_type = 'gz'; 189 $this->_compress_type = 'gz';
184 // No sure it's enought for a magic code .... 190 // Not sure it's enough for a magic code ....
185 } elseif ($data == "BZ") { 191 } elseif ($data == "BZ") {
186 $this->_compress = true; 192 $this->_compress = true;
187 $this->_compress_type = 'bz2'; 193 $this->_compress_type = 'bz2';
188 } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') { 194 } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') {
189 $this->_compress = true; 195 $this->_compress = true;
255 "with '$extname' support.\n" 261 "with '$extname' support.\n"
256 ); 262 );
257 return false; 263 return false;
258 } 264 }
259 } 265 }
266
267
268 if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
269 $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
270 "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
271 "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
272 } else {
273 $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
274 "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
275 "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
276 }
277
278
260 } 279 }
261 280
262 public function __destruct() 281 public function __destruct()
263 { 282 {
264 $this->_close(); 283 $this->_close();
575 /** 594 /**
576 * This method extract all the content of the archive in the directory 595 * This method extract all the content of the archive in the directory
577 * indicated by $p_path. When relevant the memorized path of the 596 * indicated by $p_path. When relevant the memorized path of the
578 * files/dir can be modified by removing the $p_remove_path path at the 597 * files/dir can be modified by removing the $p_remove_path path at the
579 * beginning of the file/dir path. 598 * beginning of the file/dir path.
580 * While extracting a file, if the directory path does not exists it is 599 * While extracting a file, if the directory path does not exist it is
581 * created. 600 * created.
582 * While extracting a file, if the file already exists it is replaced 601 * While extracting a file, if the file already exists it is replaced
583 * without looking for last modification date. 602 * without looking for last modification date.
584 * While extracting a file, if the file already exists and is write 603 * While extracting a file, if the file already exists and is write
585 * protected, the extraction is aborted. 604 * protected, the extraction is aborted.
710 if (($v_size = func_num_args()) == 0) { 729 if (($v_size = func_num_args()) == 0) {
711 return true; 730 return true;
712 } 731 }
713 732
714 // ----- Get the arguments 733 // ----- Get the arguments
715 $v_att_list = & func_get_args(); 734 $v_att_list = func_get_args();
716 735
717 // ----- Read the attributes 736 // ----- Read the attributes
718 $i = 0; 737 $i = 0;
719 while ($i < $v_size) { 738 while ($i < $v_size) {
720 739
1390 public function _writeHeader($p_filename, $p_stored_filename) 1409 public function _writeHeader($p_filename, $p_stored_filename)
1391 { 1410 {
1392 if ($p_stored_filename == '') { 1411 if ($p_stored_filename == '') {
1393 $p_stored_filename = $p_filename; 1412 $p_stored_filename = $p_filename;
1394 } 1413 }
1395 $v_reduce_filename = $this->_pathReduction($p_stored_filename); 1414
1396 1415 $v_reduced_filename = $this->_pathReduction($p_stored_filename);
1397 if (strlen($v_reduce_filename) > 99) { 1416
1398 if (!$this->_writeLongHeader($v_reduce_filename)) { 1417 if (strlen($v_reduced_filename) > 99) {
1418 if (!$this->_writeLongHeader($v_reduced_filename, false)) {
1419 return false;
1420 }
1421 }
1422
1423 $v_linkname = '';
1424 if (@is_link($p_filename)) {
1425 $v_linkname = readlink($p_filename);
1426 }
1427
1428 if (strlen($v_linkname) > 99) {
1429 if (!$this->_writeLongHeader($v_linkname, true)) {
1399 return false; 1430 return false;
1400 } 1431 }
1401 } 1432 }
1402 1433
1403 $v_info = lstat($p_filename); 1434 $v_info = lstat($p_filename);
1404 $v_uid = sprintf("%07s", DecOct($v_info[4])); 1435 $v_uid = sprintf("%07s", DecOct($v_info[4]));
1405 $v_gid = sprintf("%07s", DecOct($v_info[5])); 1436 $v_gid = sprintf("%07s", DecOct($v_info[5]));
1406 $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777)); 1437 $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
1407
1408 $v_mtime = sprintf("%011s", DecOct($v_info['mtime'])); 1438 $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
1409
1410 $v_linkname = '';
1411 1439
1412 if (@is_link($p_filename)) { 1440 if (@is_link($p_filename)) {
1413 $v_typeflag = '2'; 1441 $v_typeflag = '2';
1414 $v_linkname = readlink($p_filename);
1415 $v_size = sprintf("%011s", DecOct(0)); 1442 $v_size = sprintf("%011s", DecOct(0));
1416 } elseif (@is_dir($p_filename)) { 1443 } elseif (@is_dir($p_filename)) {
1417 $v_typeflag = "5"; 1444 $v_typeflag = "5";
1418 $v_size = sprintf("%011s", DecOct(0)); 1445 $v_size = sprintf("%011s", DecOct(0));
1419 } else { 1446 } else {
1421 clearstatcache(); 1448 clearstatcache();
1422 $v_size = sprintf("%011s", DecOct($v_info['size'])); 1449 $v_size = sprintf("%011s", DecOct($v_info['size']));
1423 } 1450 }
1424 1451
1425 $v_magic = 'ustar '; 1452 $v_magic = 'ustar ';
1426
1427 $v_version = ' '; 1453 $v_version = ' ';
1428 1454
1429 if (function_exists('posix_getpwuid')) { 1455 if (function_exists('posix_getpwuid')) {
1430 $userinfo = posix_getpwuid($v_info[4]); 1456 $userinfo = posix_getpwuid($v_info[4]);
1431 $groupinfo = posix_getgrgid($v_info[5]); 1457 $groupinfo = posix_getgrgid($v_info[5]);
1436 $v_uname = ''; 1462 $v_uname = '';
1437 $v_gname = ''; 1463 $v_gname = '';
1438 } 1464 }
1439 1465
1440 $v_devmajor = ''; 1466 $v_devmajor = '';
1441
1442 $v_devminor = ''; 1467 $v_devminor = '';
1443
1444 $v_prefix = ''; 1468 $v_prefix = '';
1445 1469
1446 $v_binary_data_first = pack( 1470 $v_binary_data_first = pack(
1447 "a100a8a8a8a12a12", 1471 "a100a8a8a8a12a12",
1448 $v_reduce_filename, 1472 $v_reduced_filename,
1449 $v_perms, 1473 $v_perms,
1450 $v_uid, 1474 $v_uid,
1451 $v_gid, 1475 $v_gid,
1452 $v_size, 1476 $v_size,
1453 $v_mtime 1477 $v_mtime
1483 1507
1484 // ----- Write the first 148 bytes of the header in the archive 1508 // ----- Write the first 148 bytes of the header in the archive
1485 $this->_writeBlock($v_binary_data_first, 148); 1509 $this->_writeBlock($v_binary_data_first, 148);
1486 1510
1487 // ----- Write the calculated checksum 1511 // ----- Write the calculated checksum
1488 $v_checksum = sprintf("%06s ", DecOct($v_checksum)); 1512 $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
1489 $v_binary_data = pack("a8", $v_checksum); 1513 $v_binary_data = pack("a8", $v_checksum);
1490 $this->_writeBlock($v_binary_data, 8); 1514 $this->_writeBlock($v_binary_data, 8);
1491 1515
1492 // ----- Write the last 356 bytes of the header in the archive 1516 // ----- Write the last 356 bytes of the header in the archive
1493 $this->_writeBlock($v_binary_data_last, 356); 1517 $this->_writeBlock($v_binary_data_last, 356);
1515 $p_gid = 0 1539 $p_gid = 0
1516 ) { 1540 ) {
1517 $p_filename = $this->_pathReduction($p_filename); 1541 $p_filename = $this->_pathReduction($p_filename);
1518 1542
1519 if (strlen($p_filename) > 99) { 1543 if (strlen($p_filename) > 99) {
1520 if (!$this->_writeLongHeader($p_filename)) { 1544 if (!$this->_writeLongHeader($p_filename, false)) {
1521 return false; 1545 return false;
1522 } 1546 }
1523 } 1547 }
1524 1548
1525 if ($p_type == "5") { 1549 if ($p_type == "5") {
1611 1635
1612 /** 1636 /**
1613 * @param string $p_filename 1637 * @param string $p_filename
1614 * @return bool 1638 * @return bool
1615 */ 1639 */
1616 public function _writeLongHeader($p_filename) 1640 public function _writeLongHeader($p_filename, $is_link = false)
1617 { 1641 {
1618 $v_size = sprintf("%11s ", DecOct(strlen($p_filename))); 1642 $v_uid = sprintf("%07s", 0);
1619 1643 $v_gid = sprintf("%07s", 0);
1620 $v_typeflag = 'L'; 1644 $v_perms = sprintf("%07s", 0);
1621 1645 $v_size = sprintf("%'011s", DecOct(strlen($p_filename)));
1646 $v_mtime = sprintf("%011s", 0);
1647 $v_typeflag = ($is_link ? 'K' : 'L');
1622 $v_linkname = ''; 1648 $v_linkname = '';
1623 1649 $v_magic = 'ustar ';
1624 $v_magic = ''; 1650 $v_version = ' ';
1625
1626 $v_version = '';
1627
1628 $v_uname = ''; 1651 $v_uname = '';
1629
1630 $v_gname = ''; 1652 $v_gname = '';
1631
1632 $v_devmajor = ''; 1653 $v_devmajor = '';
1633
1634 $v_devminor = ''; 1654 $v_devminor = '';
1635
1636 $v_prefix = ''; 1655 $v_prefix = '';
1637 1656
1638 $v_binary_data_first = pack( 1657 $v_binary_data_first = pack(
1639 "a100a8a8a8a12a12", 1658 "a100a8a8a8a12a12",
1640 '././@LongLink', 1659 '././@LongLink',
1641 0, 1660 $v_perms,
1642 0, 1661 $v_uid,
1643 0, 1662 $v_gid,
1644 $v_size, 1663 $v_size,
1645 0 1664 $v_mtime
1646 ); 1665 );
1647 $v_binary_data_last = pack( 1666 $v_binary_data_last = pack(
1648 "a1a100a6a2a32a32a8a8a155a12", 1667 "a1a100a6a2a32a32a8a8a155a12",
1649 $v_typeflag, 1668 $v_typeflag,
1650 $v_linkname, 1669 $v_linkname,
1675 1694
1676 // ----- Write the first 148 bytes of the header in the archive 1695 // ----- Write the first 148 bytes of the header in the archive
1677 $this->_writeBlock($v_binary_data_first, 148); 1696 $this->_writeBlock($v_binary_data_first, 148);
1678 1697
1679 // ----- Write the calculated checksum 1698 // ----- Write the calculated checksum
1680 $v_checksum = sprintf("%06s ", DecOct($v_checksum)); 1699 $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
1681 $v_binary_data = pack("a8", $v_checksum); 1700 $v_binary_data = pack("a8", $v_checksum);
1682 $this->_writeBlock($v_binary_data, 8); 1701 $this->_writeBlock($v_binary_data, 8);
1683 1702
1684 // ----- Write the last 356 bytes of the header in the archive 1703 // ----- Write the last 356 bytes of the header in the archive
1685 $this->_writeBlock($v_binary_data_last, 356); 1704 $this->_writeBlock($v_binary_data_last, 356);
1716 $v_header = array(); 1735 $v_header = array();
1717 } 1736 }
1718 // ----- Calculate the checksum 1737 // ----- Calculate the checksum
1719 $v_checksum = 0; 1738 $v_checksum = 0;
1720 // ..... First part of the header 1739 // ..... First part of the header
1721 for ($i = 0; $i < 148; $i++) { 1740 $v_binary_split = str_split($v_binary_data);
1722 $v_checksum += ord(substr($v_binary_data, $i, 1)); 1741 $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148)));
1723 } 1742 $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',)));
1724 // ..... Ignore the checksum value and replace it by ' ' (space) 1743 $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512)));
1725 for ($i = 148; $i < 156; $i++) { 1744
1726 $v_checksum += ord(' '); 1745
1727 } 1746 $v_data = unpack($this->_fmt, $v_binary_data);
1728 // ..... Last part of the header
1729 for ($i = 156; $i < 512; $i++) {
1730 $v_checksum += ord(substr($v_binary_data, $i, 1));
1731 }
1732
1733 if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
1734 $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
1735 "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
1736 "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
1737 } else {
1738 $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
1739 "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
1740 "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
1741 }
1742 $v_data = unpack($fmt, $v_binary_data);
1743 1747
1744 if (strlen($v_data["prefix"]) > 0) { 1748 if (strlen($v_data["prefix"]) > 0) {
1745 $v_data["filename"] = "$v_data[prefix]/$v_data[filename]"; 1749 $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
1746 } 1750 }
1747 1751
1773 return false; 1777 return false;
1774 } 1778 }
1775 $v_header['mode'] = OctDec(trim($v_data['mode'])); 1779 $v_header['mode'] = OctDec(trim($v_data['mode']));
1776 $v_header['uid'] = OctDec(trim($v_data['uid'])); 1780 $v_header['uid'] = OctDec(trim($v_data['uid']));
1777 $v_header['gid'] = OctDec(trim($v_data['gid'])); 1781 $v_header['gid'] = OctDec(trim($v_data['gid']));
1778 $v_header['size'] = OctDec(trim($v_data['size'])); 1782 $v_header['size'] = $this->_tarRecToSize($v_data['size']);
1779 $v_header['mtime'] = OctDec(trim($v_data['mtime'])); 1783 $v_header['mtime'] = OctDec(trim($v_data['mtime']));
1780 if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { 1784 if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
1781 $v_header['size'] = 0; 1785 $v_header['size'] = 0;
1782 } 1786 }
1783 $v_header['link'] = trim($v_data['link']); 1787 $v_header['link'] = trim($v_data['link']);
1793 1797
1794 return true; 1798 return true;
1795 } 1799 }
1796 1800
1797 /** 1801 /**
1802 * Convert Tar record size to actual size
1803 *
1804 * @param string $tar_size
1805 * @return size of tar record in bytes
1806 */
1807 private function _tarRecToSize($tar_size)
1808 {
1809 /*
1810 * First byte of size has a special meaning if bit 7 is set.
1811 *
1812 * Bit 7 indicates base-256 encoding if set.
1813 * Bit 6 is the sign bit.
1814 * Bits 5:0 are most significant value bits.
1815 */
1816 $ch = ord($tar_size[0]);
1817 if ($ch & 0x80) {
1818 // Full 12-bytes record is required.
1819 $rec_str = $tar_size . "\x00";
1820
1821 $size = ($ch & 0x40) ? -1 : 0;
1822 $size = ($size << 6) | ($ch & 0x3f);
1823
1824 for ($num_ch = 1; $num_ch < 12; ++$num_ch) {
1825 $size = ($size * 256) + ord($rec_str[$num_ch]);
1826 }
1827
1828 return $size;
1829
1830 } else {
1831 return OctDec(trim($tar_size));
1832 }
1833 }
1834
1835 /**
1798 * Detect and report a malicious file name 1836 * Detect and report a malicious file name
1799 * 1837 *
1800 * @param string $file 1838 * @param string $file
1801 * 1839 *
1802 * @return bool 1840 * @return bool
1803 */ 1841 */
1804 private function _maliciousFilename($file) 1842 private function _maliciousFilename($file)
1805 { 1843 {
1806 if (strpos($file, '/../') !== false) { 1844 if (strpos($file, 'phar://') === 0) {
1807 return true; 1845 return true;
1808 } 1846 }
1809 if (strpos($file, '../') === 0) { 1847 if (strpos($file, DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) !== false) {
1848 return true;
1849 }
1850 if (strpos($file, '..' . DIRECTORY_SEPARATOR) === 0) {
1810 return true; 1851 return true;
1811 } 1852 }
1812 return false; 1853 return false;
1813 } 1854 }
1814 1855
1869 1910
1870 if ($v_header['filename'] == '') { 1911 if ($v_header['filename'] == '') {
1871 continue; 1912 continue;
1872 } 1913 }
1873 1914
1874 // ----- Look for long filename 1915 switch ($v_header['typeflag']) {
1875 if ($v_header['typeflag'] == 'L') { 1916 case 'L': {
1876 if (!$this->_readLongHeader($v_header)) { 1917 if (!$this->_readLongHeader($v_header)) {
1877 return null; 1918 return null;
1878 } 1919 }
1920 } break;
1921
1922 case 'K': {
1923 $v_link_header = $v_header;
1924 if (!$this->_readLongHeader($v_link_header)) {
1925 return null;
1926 }
1927 $v_header['link'] = $v_link_header['filename'];
1928 } break;
1879 } 1929 }
1880 1930
1881 if ($v_header['filename'] == $p_filename) { 1931 if ($v_header['filename'] == $p_filename) {
1882 if ($v_header['typeflag'] == "5") { 1932 if ($v_header['typeflag'] == "5") {
1883 $this->_error( 1933 $this->_error(
1974 2024
1975 if ($v_header['filename'] == '') { 2025 if ($v_header['filename'] == '') {
1976 continue; 2026 continue;
1977 } 2027 }
1978 2028
1979 // ----- Look for long filename 2029 switch ($v_header['typeflag']) {
1980 if ($v_header['typeflag'] == 'L') { 2030 case 'L': {
1981 if (!$this->_readLongHeader($v_header)) { 2031 if (!$this->_readLongHeader($v_header)) {
1982 return false; 2032 return null;
1983 } 2033 }
2034 } break;
2035
2036 case 'K': {
2037 $v_link_header = $v_header;
2038 if (!$this->_readLongHeader($v_link_header)) {
2039 return null;
2040 }
2041 $v_header['link'] = $v_link_header['filename'];
2042 } break;
1984 } 2043 }
1985 2044
1986 // ignore extended / pax headers 2045 // ignore extended / pax headers
1987 if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') { 2046 if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') {
1988 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 2047 $this->_jumpBlock(ceil(($v_header['size'] / 512)));
2383 return true; 2442 return true;
2384 } 2443 }
2385 2444
2386 /** 2445 /**
2387 * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", 2446 * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
2388 * rand emove double slashes. 2447 * and remove double slashes.
2389 * 2448 *
2390 * @param string $p_dir path to reduce 2449 * @param string $p_dir path to reduce
2391 * 2450 *
2392 * @return string reduced path 2451 * @return string reduced path
2393 */ 2452 */