Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(13655)

Delta Between Two Patch Sets: Lib/test/test_tarfile.py

Issue 23228: Crashes when tarfile contains a symlink and unpack directory contain it too
Left Patch Set: Created 4 years, 8 months ago
Right Patch Set: Created 3 years, 4 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« Lib/tarfile.py ('K') | « Lib/tarfile.py ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 import sys 1 import sys
2 import os 2 import os
3 import io 3 import io
4 from hashlib import md5 4 from hashlib import md5
5 from contextlib import contextmanager
5 6
6 import unittest 7 import unittest
8 import unittest.mock
7 import tarfile 9 import tarfile
8 import tempfile 10
9 11 from test import support
10 from test import support, script_helper 12 from test.support import script_helper
11 13
12 # Check for our compression modules. 14 # Check for our compression modules.
13 try: 15 try:
14 import gzip 16 import gzip
15 except ImportError: 17 except ImportError:
16 gzip = None 18 gzip = None
17 try: 19 try:
18 import bz2 20 import bz2
19 except ImportError: 21 except ImportError:
20 bz2 = None 22 bz2 = None
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 fobj.write(tarfile.TarInfo("foo").tobuf()) 357 fobj.write(tarfile.TarInfo("foo").tobuf())
356 358
357 tar = tarfile.open(tmpname, mode="r", ignore_zeros=True) 359 tar = tarfile.open(tmpname, mode="r", ignore_zeros=True)
358 try: 360 try:
359 self.assertListEqual(tar.getnames(), ["foo"], 361 self.assertListEqual(tar.getnames(), ["foo"],
360 "ignore_zeros=True should have skipped the %r-blocks" % 362 "ignore_zeros=True should have skipped the %r-blocks" %
361 char) 363 char)
362 finally: 364 finally:
363 tar.close() 365 tar.close()
364 366
367 def test_premature_end_of_archive(self):
368 for size in (512, 600, 1024, 1200):
369 with tarfile.open(tmpname, "w:") as tar:
370 t = tarfile.TarInfo("foo")
371 t.size = 1024
372 tar.addfile(t, io.BytesIO(b"a" * 1024))
373
374 with open(tmpname, "r+b") as fobj:
375 fobj.truncate(size)
376
377 with tarfile.open(tmpname) as tar:
378 with self.assertRaisesRegex(tarfile.ReadError, "unexpected end o f data"):
379 for t in tar:
380 pass
381
382 with tarfile.open(tmpname) as tar:
383 t = tar.next()
384
385 with self.assertRaisesRegex(tarfile.ReadError, "unexpected end o f data"):
386 tar.extract(t, TEMPDIR)
387
388 with self.assertRaisesRegex(tarfile.ReadError, "unexpected end o f data"):
389 tar.extractfile(t).read()
365 390
366 class MiscReadTestBase(CommonReadTest): 391 class MiscReadTestBase(CommonReadTest):
367 def requires_name_attribute(self): 392 def requires_name_attribute(self):
368 pass 393 pass
369 394
370 def test_no_name_argument(self): 395 def test_no_name_argument(self):
371 self.requires_name_attribute() 396 self.requires_name_attribute()
372 with open(self.tarname, "rb") as fobj: 397 with open(self.tarname, "rb") as fobj:
373 self.assertIsInstance(fobj.name, str) 398 self.assertIsInstance(fobj.name, str)
374 with tarfile.open(fileobj=fobj, mode=self.mode) as tar: 399 with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar: 518 with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar:
494 tar.extract("ustar/regtype", TEMPDIR) 519 tar.extract("ustar/regtype", TEMPDIR)
495 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/regtype ")) 520 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/regtype "))
496 521
497 tar.extract("ustar/lnktype", TEMPDIR) 522 tar.extract("ustar/lnktype", TEMPDIR)
498 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/lnktype ")) 523 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/lnktype "))
499 with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f: 524 with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f:
500 data = f.read() 525 data = f.read()
501 self.assertEqual(md5sum(data), md5_regtype) 526 self.assertEqual(md5sum(data), md5_regtype)
502 527
528 @support.skip_unless_symlink
529 def test_extract_symlink(self):
530 # Test symlink extraction (e.g. bug #23228).
531 with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar:
532 tar.extract("ustar/regtype", TEMPDIR)
533 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/regtype "))
534
503 tar.extract("ustar/symtype", TEMPDIR) 535 tar.extract("ustar/symtype", TEMPDIR)
504 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/symtype ")) 536 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/symtype "))
505 with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f: 537 with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f:
506 data = f.read() 538 data = f.read()
507 self.assertEqual(md5sum(data), md5_regtype) 539 self.assertEqual(md5sum(data), md5_regtype)
540
541 self.assertRaises(FileExistsError, tar.extract, "ustar/symtype", TEM PDIR)
508 542
509 def test_extractall(self): 543 def test_extractall(self):
510 # Test if extractall() correctly restores directory permissions 544 # Test if extractall() correctly restores directory permissions
511 # and times (see issue1735). 545 # and times (see issue1735).
512 tar = tarfile.open(tarname, encoding="iso8859-1") 546 tar = tarfile.open(tarname, encoding="iso8859-1")
513 DIR = os.path.join(TEMPDIR, "extractall") 547 DIR = os.path.join(TEMPDIR, "extractall")
514 os.mkdir(DIR) 548 os.mkdir(DIR)
515 try: 549 try:
516 directories = [t for t in tar if t.isdir()] 550 directories = [t for t in tar if t.isdir()]
517 tar.extractall(DIR, directories) 551 tar.extractall(DIR, directories)
(...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after
973 tar.addfile(tarfile.TarInfo("foo")) 1007 tar.addfile(tarfile.TarInfo("foo"))
974 tar.close() 1008 tar.close()
975 self.assertFalse(fobj.closed, "external fileobjs must never closed") 1009 self.assertFalse(fobj.closed, "external fileobjs must never closed")
976 # Issue #20238: Incomplete gzip output with mode="w:gz" 1010 # Issue #20238: Incomplete gzip output with mode="w:gz"
977 data = fobj.getvalue() 1011 data = fobj.getvalue()
978 del tar 1012 del tar
979 support.gc_collect() 1013 support.gc_collect()
980 self.assertFalse(fobj.closed) 1014 self.assertFalse(fobj.closed)
981 self.assertEqual(data, fobj.getvalue()) 1015 self.assertEqual(data, fobj.getvalue())
982 1016
1017 def test_eof_marker(self):
1018 # Make sure an end of archive marker is written (two zero blocks).
1019 # tarfile insists on aligning archives to a 20 * 512 byte recordsize.
1020 # So, we create an archive that has exactly 10240 bytes without the
1021 # marker, and has 20480 bytes once the marker is written.
1022 with tarfile.open(tmpname, self.mode) as tar:
1023 t = tarfile.TarInfo("foo")
1024 t.size = tarfile.RECORDSIZE - tarfile.BLOCKSIZE
1025 tar.addfile(t, io.BytesIO(b"a" * t.size))
1026
1027 with self.open(tmpname, "rb") as fobj:
1028 self.assertEqual(len(fobj.read()), tarfile.RECORDSIZE * 2)
1029
983 1030
984 class WriteTest(WriteTestBase, unittest.TestCase): 1031 class WriteTest(WriteTestBase, unittest.TestCase):
985 1032
986 prefix = "w:" 1033 prefix = "w:"
987 1034
988 def test_100_char_name(self): 1035 def test_100_char_name(self):
989 # The name field in a tar header stores strings of at most 100 chars. 1036 # The name field in a tar header stores strings of at most 100 chars.
990 # If a string is shorter than 100 chars it has to be padded with '\0', 1037 # If a string is shorter than 100 chars it has to be padded with '\0',
991 # which implies that a string of exactly 100 chars is stored without 1038 # which implies that a string of exactly 100 chars is stored without
992 # a trailing '\0'. 1039 # a trailing '\0'.
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
1087 # Test for #1257255. 1134 # Test for #1257255.
1088 dstname = os.path.abspath(tmpname) 1135 dstname = os.path.abspath(tmpname)
1089 tar = tarfile.open(tmpname, self.mode) 1136 tar = tarfile.open(tmpname, self.mode)
1090 try: 1137 try:
1091 self.assertEqual(tar.name, dstname, 1138 self.assertEqual(tar.name, dstname,
1092 "archive name must be absolute") 1139 "archive name must be absolute")
1093 tar.add(dstname) 1140 tar.add(dstname)
1094 self.assertEqual(tar.getnames(), [], 1141 self.assertEqual(tar.getnames(), [],
1095 "added the archive to itself") 1142 "added the archive to itself")
1096 1143
1097 cwd = os.getcwd() 1144 with support.change_cwd(TEMPDIR):
1098 os.chdir(TEMPDIR) 1145 tar.add(dstname)
1099 tar.add(dstname)
1100 os.chdir(cwd)
1101 self.assertEqual(tar.getnames(), [], 1146 self.assertEqual(tar.getnames(), [],
1102 "added the archive to itself") 1147 "added the archive to itself")
1103 finally: 1148 finally:
1104 tar.close() 1149 tar.close()
1105 1150
1106 def test_exclude(self): 1151 def test_exclude(self):
1107 tempdir = os.path.join(TEMPDIR, "exclude") 1152 tempdir = os.path.join(TEMPDIR, "exclude")
1108 os.mkdir(tempdir) 1153 os.mkdir(tempdir)
1109 try: 1154 try:
1110 for name in ("foo", "bar", "baz"): 1155 for name in ("foo", "bar", "baz"):
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
1247 1292
1248 def test_abs_pathnames(self): 1293 def test_abs_pathnames(self):
1249 if sys.platform == "win32": 1294 if sys.platform == "win32":
1250 self._test_pathname("C:\\foo", "foo") 1295 self._test_pathname("C:\\foo", "foo")
1251 else: 1296 else:
1252 self._test_pathname("/foo", "foo") 1297 self._test_pathname("/foo", "foo")
1253 self._test_pathname("///foo", "foo") 1298 self._test_pathname("///foo", "foo")
1254 1299
1255 def test_cwd(self): 1300 def test_cwd(self):
1256 # Test adding the current working directory. 1301 # Test adding the current working directory.
1257 cwd = os.getcwd() 1302 with support.change_cwd(TEMPDIR):
1258 os.chdir(TEMPDIR)
1259 try:
1260 tar = tarfile.open(tmpname, self.mode) 1303 tar = tarfile.open(tmpname, self.mode)
1261 try: 1304 try:
1262 tar.add(".") 1305 tar.add(".")
1263 finally: 1306 finally:
1264 tar.close() 1307 tar.close()
1265 1308
1266 tar = tarfile.open(tmpname, "r") 1309 tar = tarfile.open(tmpname, "r")
1267 try: 1310 try:
1268 for t in tar: 1311 for t in tar:
1269 if t.name != ".": 1312 if t.name != ".":
1270 self.assertTrue(t.name.startswith("./"), t.name) 1313 self.assertTrue(t.name.startswith("./"), t.name)
1271 finally: 1314 finally:
1272 tar.close() 1315 tar.close()
1273 finally:
1274 os.chdir(cwd)
1275 1316
1276 def test_open_nonwritable_fileobj(self): 1317 def test_open_nonwritable_fileobj(self):
1277 for exctype in OSError, EOFError, RuntimeError: 1318 for exctype in OSError, EOFError, RuntimeError:
1278 class BadFile(io.BytesIO): 1319 class BadFile(io.BytesIO):
1279 first = True 1320 first = True
1280 def write(self, data): 1321 def write(self, data):
1281 if self.first: 1322 if self.first:
1282 self.first = False 1323 self.first = False
1283 raise exctype 1324 raise exctype
1284 1325
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
1422 1463
1423 def test_longnamelink_1024(self): 1464 def test_longnamelink_1024(self):
1424 self._test(("longnam/" * 127) + "longname", 1465 self._test(("longnam/" * 127) + "longname",
1425 ("longlnk/" * 127) + "longlink") 1466 ("longlnk/" * 127) + "longlink")
1426 1467
1427 def test_longnamelink_1025(self): 1468 def test_longnamelink_1025(self):
1428 self._test(("longnam/" * 127) + "longname_", 1469 self._test(("longnam/" * 127) + "longname_",
1429 ("longlnk/" * 127) + "longlink_") 1470 ("longlnk/" * 127) + "longlink_")
1430 1471
1431 1472
1473 class CreateTest(WriteTestBase, unittest.TestCase):
1474
1475 prefix = "x:"
1476
1477 file_path = os.path.join(TEMPDIR, "spameggs42")
1478
1479 def setUp(self):
1480 support.unlink(tmpname)
1481
1482 @classmethod
1483 def setUpClass(cls):
1484 with open(cls.file_path, "wb") as fobj:
1485 fobj.write(b"aaa")
1486
1487 @classmethod
1488 def tearDownClass(cls):
1489 support.unlink(cls.file_path)
1490
1491 def test_create(self):
1492 with tarfile.open(tmpname, self.mode) as tobj:
1493 tobj.add(self.file_path)
1494
1495 with self.taropen(tmpname) as tobj:
1496 names = tobj.getnames()
1497 self.assertEqual(len(names), 1)
1498 self.assertIn('spameggs42', names[0])
1499
1500 def test_create_existing(self):
1501 with tarfile.open(tmpname, self.mode) as tobj:
1502 tobj.add(self.file_path)
1503
1504 with self.assertRaises(FileExistsError):
1505 tobj = tarfile.open(tmpname, self.mode)
1506
1507 with self.taropen(tmpname) as tobj:
1508 names = tobj.getnames()
1509 self.assertEqual(len(names), 1)
1510 self.assertIn('spameggs42', names[0])
1511
1512 def test_create_taropen(self):
1513 with self.taropen(tmpname, "x") as tobj:
1514 tobj.add(self.file_path)
1515
1516 with self.taropen(tmpname) as tobj:
1517 names = tobj.getnames()
1518 self.assertEqual(len(names), 1)
1519 self.assertIn('spameggs42', names[0])
1520
1521 def test_create_existing_taropen(self):
1522 with self.taropen(tmpname, "x") as tobj:
1523 tobj.add(self.file_path)
1524
1525 with self.assertRaises(FileExistsError):
1526 with self.taropen(tmpname, "x"):
1527 pass
1528
1529 with self.taropen(tmpname) as tobj:
1530 names = tobj.getnames()
1531 self.assertEqual(len(names), 1)
1532 self.assertIn("spameggs42", names[0])
1533
1534
1535 class GzipCreateTest(GzipTest, CreateTest):
1536 pass
1537
1538
1539 class Bz2CreateTest(Bz2Test, CreateTest):
1540 pass
1541
1542
1543 class LzmaCreateTest(LzmaTest, CreateTest):
1544 pass
1545
1546
1547 class CreateWithXModeTest(CreateTest):
1548
1549 prefix = "x"
1550
1551 test_create_taropen = None
1552 test_create_existing_taropen = None
1553
1554
1432 @unittest.skipUnless(hasattr(os, "link"), "Missing hardlink implementation") 1555 @unittest.skipUnless(hasattr(os, "link"), "Missing hardlink implementation")
1433 class HardlinkTest(unittest.TestCase): 1556 class HardlinkTest(unittest.TestCase):
1434 # Test the creation of LNKTYPE (hardlink) members in an archive. 1557 # Test the creation of LNKTYPE (hardlink) members in an archive.
1435 1558
1436 def setUp(self): 1559 def setUp(self):
1437 self.foo = os.path.join(TEMPDIR, "foo") 1560 self.foo = os.path.join(TEMPDIR, "foo")
1438 self.bar = os.path.join(TEMPDIR, "bar") 1561 self.bar = os.path.join(TEMPDIR, "bar")
1439 1562
1440 with open(self.foo, "wb") as fobj: 1563 with open(self.foo, "wb") as fobj:
1441 fobj.write(b"foo") 1564 fobj.write(b"foo")
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
1546 tar = tarfile.open(tmpname, encoding="iso8859-1") 1669 tar = tarfile.open(tmpname, encoding="iso8859-1")
1547 try: 1670 try:
1548 t = tar.getmembers()[0] 1671 t = tar.getmembers()[0]
1549 self.assertEqual(t.pax_headers, pax_headers) 1672 self.assertEqual(t.pax_headers, pax_headers)
1550 self.assertEqual(t.name, "foo") 1673 self.assertEqual(t.name, "foo")
1551 self.assertEqual(t.uid, 123) 1674 self.assertEqual(t.uid, 123)
1552 finally: 1675 finally:
1553 tar.close() 1676 tar.close()
1554 1677
1555 1678
1556 class UstarUnicodeTest(unittest.TestCase): 1679 class UnicodeTest:
1557
1558 format = tarfile.USTAR_FORMAT
1559 1680
1560 def test_iso8859_1_filename(self): 1681 def test_iso8859_1_filename(self):
1561 self._test_unicode_filename("iso8859-1") 1682 self._test_unicode_filename("iso8859-1")
1562 1683
1563 def test_utf7_filename(self): 1684 def test_utf7_filename(self):
1564 self._test_unicode_filename("utf7") 1685 self._test_unicode_filename("utf7")
1565 1686
1566 def test_utf8_filename(self): 1687 def test_utf8_filename(self):
1567 self._test_unicode_filename("utf-8") 1688 self._test_unicode_filename("utf-8")
1568 1689
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1629 if self.format != tarfile.PAX_FORMAT: 1750 if self.format != tarfile.PAX_FORMAT:
1630 tar.close() 1751 tar.close()
1631 tar = tarfile.open(tmpname, encoding="ascii") 1752 tar = tarfile.open(tmpname, encoding="ascii")
1632 t = tar.getmember("foo") 1753 t = tar.getmember("foo")
1633 self.assertEqual(t.uname, "\udce4\udcf6\udcfc") 1754 self.assertEqual(t.uname, "\udce4\udcf6\udcfc")
1634 self.assertEqual(t.gname, "\udce4\udcf6\udcfc") 1755 self.assertEqual(t.gname, "\udce4\udcf6\udcfc")
1635 finally: 1756 finally:
1636 tar.close() 1757 tar.close()
1637 1758
1638 1759
1639 class GNUUnicodeTest(UstarUnicodeTest): 1760 class UstarUnicodeTest(UnicodeTest, unittest.TestCase):
1761
1762 format = tarfile.USTAR_FORMAT
1763
1764 # Test whether the utf-8 encoded version of a filename exceeds the 100
1765 # bytes name field limit (every occurrence of '\xff' will be expanded to 2
1766 # bytes).
1767 def test_unicode_name1(self):
1768 self._test_ustar_name("0123456789" * 10)
1769 self._test_ustar_name("0123456789" * 10 + "0", ValueError)
1770 self._test_ustar_name("0123456789" * 9 + "01234567\xff")
1771 self._test_ustar_name("0123456789" * 9 + "012345678\xff", ValueError)
1772
1773 def test_unicode_name2(self):
1774 self._test_ustar_name("0123456789" * 9 + "012345\xff\xff")
1775 self._test_ustar_name("0123456789" * 9 + "0123456\xff\xff", ValueError)
1776
1777 # Test whether the utf-8 encoded version of a filename exceeds the 155
1778 # bytes prefix + '/' + 100 bytes name limit.
1779 def test_unicode_longname1(self):
1780 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 10)
1781 self._test_ustar_name("0123456789" * 15 + "0123/4" + "0123456789" * 10, ValueError)
1782 self._test_ustar_name("0123456789" * 15 + "012\xff/" + "0123456789" * 10 )
1783 self._test_ustar_name("0123456789" * 15 + "0123\xff/" + "0123456789" * 1 0, ValueError)
1784
1785 def test_unicode_longname2(self):
1786 self._test_ustar_name("0123456789" * 15 + "01\xff/2" + "0123456789" * 10 , ValueError)
1787 self._test_ustar_name("0123456789" * 15 + "01\xff\xff/" + "0123456789" * 10, ValueError)
1788
1789 def test_unicode_longname3(self):
1790 self._test_ustar_name("0123456789" * 15 + "01\xff\xff/2" + "0123456789" * 10, ValueError)
1791 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 9 + "01234567\xff")
1792 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 9 + "012345678\xff", ValueError)
1793
1794 def test_unicode_longname4(self):
1795 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 9 + "012345\xff\xff")
1796 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 9 + "0123456\xff\xff", ValueError)
1797
1798 def _test_ustar_name(self, name, exc=None):
1799 with tarfile.open(tmpname, "w", format=self.format, encoding="utf-8") as tar:
1800 t = tarfile.TarInfo(name)
1801 if exc is None:
1802 tar.addfile(t)
1803 else:
1804 self.assertRaises(exc, tar.addfile, t)
1805
1806 if exc is None:
1807 with tarfile.open(tmpname, "r", encoding="utf-8") as tar:
1808 for t in tar:
1809 self.assertEqual(name, t.name)
1810 break
1811
1812 # Test the same as above for the 100 bytes link field.
1813 def test_unicode_link1(self):
1814 self._test_ustar_link("0123456789" * 10)
1815 self._test_ustar_link("0123456789" * 10 + "0", ValueError)
1816 self._test_ustar_link("0123456789" * 9 + "01234567\xff")
1817 self._test_ustar_link("0123456789" * 9 + "012345678\xff", ValueError)
1818
1819 def test_unicode_link2(self):
1820 self._test_ustar_link("0123456789" * 9 + "012345\xff\xff")
1821 self._test_ustar_link("0123456789" * 9 + "0123456\xff\xff", ValueError)
1822
1823 def _test_ustar_link(self, name, exc=None):
1824 with tarfile.open(tmpname, "w", format=self.format, encoding="utf-8") as tar:
1825 t = tarfile.TarInfo("foo")
1826 t.linkname = name
1827 if exc is None:
1828 tar.addfile(t)
1829 else:
1830 self.assertRaises(exc, tar.addfile, t)
1831
1832 if exc is None:
1833 with tarfile.open(tmpname, "r", encoding="utf-8") as tar:
1834 for t in tar:
1835 self.assertEqual(name, t.linkname)
1836 break
1837
1838
1839 class GNUUnicodeTest(UnicodeTest, unittest.TestCase):
1640 1840
1641 format = tarfile.GNU_FORMAT 1841 format = tarfile.GNU_FORMAT
1642 1842
1643 def test_bad_pax_header(self): 1843 def test_bad_pax_header(self):
1644 # Test for issue #8633. GNU tar <= 1.23 creates raw binary fields 1844 # Test for issue #8633. GNU tar <= 1.23 creates raw binary fields
1645 # without a hdrcharset=BINARY header. 1845 # without a hdrcharset=BINARY header.
1646 for encoding, name in ( 1846 for encoding, name in (
1647 ("utf-8", "pax/bad-pax-\udce4\udcf6\udcfc"), 1847 ("utf-8", "pax/bad-pax-\udce4\udcf6\udcfc"),
1648 ("iso8859-1", "pax/bad-pax-\xe4\xf6\xfc"),): 1848 ("iso8859-1", "pax/bad-pax-\xe4\xf6\xfc"),):
1649 with tarfile.open(tarname, encoding=encoding, 1849 with tarfile.open(tarname, encoding=encoding,
1650 errors="surrogateescape") as tar: 1850 errors="surrogateescape") as tar:
1651 try: 1851 try:
1652 t = tar.getmember(name) 1852 t = tar.getmember(name)
1653 except KeyError: 1853 except KeyError:
1654 self.fail("unable to read bad GNU tar pax header") 1854 self.fail("unable to read bad GNU tar pax header")
1655 1855
1656 1856
1657 class PAXUnicodeTest(UstarUnicodeTest): 1857 class PAXUnicodeTest(UnicodeTest, unittest.TestCase):
1658 1858
1659 format = tarfile.PAX_FORMAT 1859 format = tarfile.PAX_FORMAT
1660 1860
1661 # PAX_FORMAT ignores encoding in write mode. 1861 # PAX_FORMAT ignores encoding in write mode.
1662 test_unicode_filename_error = None 1862 test_unicode_filename_error = None
1663 1863
1664 def test_binary_header(self): 1864 def test_binary_header(self):
1665 # Test a POSIX.1-2008 compatible header with a hdrcharset=BINARY field. 1865 # Test a POSIX.1-2008 compatible header with a hdrcharset=BINARY field.
1666 for encoding, name in ( 1866 for encoding, name in (
1667 ("utf-8", "pax/hdrcharset-\udce4\udcf6\udcfc"), 1867 ("utf-8", "pax/hdrcharset-\udce4\udcf6\udcfc"),
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after
1848 0o10000000) 2048 0o10000000)
1849 self.assertEqual(tarfile.nti(b"\x80\x00\x00\x00\xff\xff\xff\xff"), 2049 self.assertEqual(tarfile.nti(b"\x80\x00\x00\x00\xff\xff\xff\xff"),
1850 0xffffffff) 2050 0xffffffff)
1851 self.assertEqual(tarfile.nti(b"\xff\xff\xff\xff\xff\xff\xff\xff"), 2051 self.assertEqual(tarfile.nti(b"\xff\xff\xff\xff\xff\xff\xff\xff"),
1852 -1) 2052 -1)
1853 self.assertEqual(tarfile.nti(b"\xff\xff\xff\xff\xff\xff\xff\x9c"), 2053 self.assertEqual(tarfile.nti(b"\xff\xff\xff\xff\xff\xff\xff\x9c"),
1854 -100) 2054 -100)
1855 self.assertEqual(tarfile.nti(b"\xff\x00\x00\x00\x00\x00\x00\x00"), 2055 self.assertEqual(tarfile.nti(b"\xff\x00\x00\x00\x00\x00\x00\x00"),
1856 -0x100000000000000) 2056 -0x100000000000000)
1857 2057
2058 # Issue 24514: Test if empty number fields are converted to zero.
2059 self.assertEqual(tarfile.nti(b"\0"), 0)
2060 self.assertEqual(tarfile.nti(b" \0"), 0)
2061
1858 def test_write_number_fields(self): 2062 def test_write_number_fields(self):
1859 self.assertEqual(tarfile.itn(1), b"0000001\x00") 2063 self.assertEqual(tarfile.itn(1), b"0000001\x00")
1860 self.assertEqual(tarfile.itn(0o7777777), b"7777777\x00") 2064 self.assertEqual(tarfile.itn(0o7777777), b"7777777\x00")
1861 self.assertEqual(tarfile.itn(0o10000000), 2065 self.assertEqual(tarfile.itn(0o10000000),
1862 b"\x80\x00\x00\x00\x00\x20\x00\x00") 2066 b"\x80\x00\x00\x00\x00\x20\x00\x00")
1863 self.assertEqual(tarfile.itn(0xffffffff), 2067 self.assertEqual(tarfile.itn(0xffffffff),
1864 b"\x80\x00\x00\x00\xff\xff\xff\xff") 2068 b"\x80\x00\x00\x00\xff\xff\xff\xff")
1865 self.assertEqual(tarfile.itn(-1), 2069 self.assertEqual(tarfile.itn(-1),
1866 b"\xff\xff\xff\xff\xff\xff\xff\xff") 2070 b"\xff\xff\xff\xff\xff\xff\xff\xff")
1867 self.assertEqual(tarfile.itn(-100), 2071 self.assertEqual(tarfile.itn(-100),
1868 b"\xff\xff\xff\xff\xff\xff\xff\x9c") 2072 b"\xff\xff\xff\xff\xff\xff\xff\x9c")
1869 self.assertEqual(tarfile.itn(-0x100000000000000), 2073 self.assertEqual(tarfile.itn(-0x100000000000000),
1870 b"\xff\x00\x00\x00\x00\x00\x00\x00") 2074 b"\xff\x00\x00\x00\x00\x00\x00\x00")
1871 2075
1872 def test_number_field_limits(self): 2076 def test_number_field_limits(self):
1873 with self.assertRaises(ValueError): 2077 with self.assertRaises(ValueError):
1874 tarfile.itn(-1, 8, tarfile.USTAR_FORMAT) 2078 tarfile.itn(-1, 8, tarfile.USTAR_FORMAT)
1875 with self.assertRaises(ValueError): 2079 with self.assertRaises(ValueError):
1876 tarfile.itn(0o10000000, 8, tarfile.USTAR_FORMAT) 2080 tarfile.itn(0o10000000, 8, tarfile.USTAR_FORMAT)
1877 with self.assertRaises(ValueError): 2081 with self.assertRaises(ValueError):
1878 tarfile.itn(-0x10000000001, 6, tarfile.GNU_FORMAT) 2082 tarfile.itn(-0x10000000001, 6, tarfile.GNU_FORMAT)
1879 with self.assertRaises(ValueError): 2083 with self.assertRaises(ValueError):
1880 tarfile.itn(0x10000000000, 6, tarfile.GNU_FORMAT) 2084 tarfile.itn(0x10000000000, 6, tarfile.GNU_FORMAT)
1881 2085
2086 def test__all__(self):
2087 blacklist = {'version', 'grp', 'pwd', 'symlink_exception',
2088 'NUL', 'BLOCKSIZE', 'RECORDSIZE', 'GNU_MAGIC',
2089 'POSIX_MAGIC', 'LENGTH_NAME', 'LENGTH_LINK',
2090 'LENGTH_PREFIX', 'REGTYPE', 'AREGTYPE', 'LNKTYPE',
2091 'SYMTYPE', 'CHRTYPE', 'BLKTYPE', 'DIRTYPE', 'FIFOTYPE',
2092 'CONTTYPE', 'GNUTYPE_LONGNAME', 'GNUTYPE_LONGLINK',
2093 'GNUTYPE_SPARSE', 'XHDTYPE', 'XGLTYPE', 'SOLARIS_XHDTYPE',
2094 'SUPPORTED_TYPES', 'REGULAR_TYPES', 'GNU_TYPES',
2095 'PAX_FIELDS', 'PAX_NAME_FIELDS', 'PAX_NUMBER_FIELDS',
2096 'stn', 'nts', 'nti', 'itn', 'calc_chksums', 'copyfileobj',
2097 'filemode',
2098 'EmptyHeaderError', 'TruncatedHeaderError',
2099 'EOFHeaderError', 'InvalidHeaderError',
2100 'SubsequentHeaderError', 'ExFileObject',
2101 'main'}
2102 support.check__all__(self, tarfile, blacklist=blacklist)
2103
1882 2104
1883 class CommandLineTest(unittest.TestCase): 2105 class CommandLineTest(unittest.TestCase):
1884 2106
1885 def tarfilecmd(self, *args, **kwargs): 2107 def tarfilecmd(self, *args, **kwargs):
1886 rc, out, err = script_helper.assert_python_ok('-m', 'tarfile', *args, 2108 rc, out, err = script_helper.assert_python_ok('-m', 'tarfile', *args,
1887 **kwargs) 2109 **kwargs)
1888 return out.replace(os.linesep.encode(), b'\n') 2110 return out.replace(os.linesep.encode(), b'\n')
1889 2111
1890 def tarfilecmd_failure(self, *args): 2112 def tarfilecmd_failure(self, *args):
1891 return script_helper.assert_python_failure('-m', 'tarfile', *args) 2113 return script_helper.assert_python_failure('-m', 'tarfile', *args)
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
2000 tar_name = os.path.join(TEMPDIR, ".testtar") 2222 tar_name = os.path.join(TEMPDIR, ".testtar")
2001 files = [support.findfile('tokenize_tests.txt')] 2223 files = [support.findfile('tokenize_tests.txt')]
2002 try: 2224 try:
2003 out = self.tarfilecmd('-c', tar_name, *files) 2225 out = self.tarfilecmd('-c', tar_name, *files)
2004 self.assertEqual(out, b'') 2226 self.assertEqual(out, b'')
2005 with tarfile.open(tar_name) as tar: 2227 with tarfile.open(tar_name) as tar:
2006 tar.getmembers() 2228 tar.getmembers()
2007 finally: 2229 finally:
2008 support.unlink(tar_name) 2230 support.unlink(tar_name)
2009 2231
2232 def test_create_command_compressed(self):
2233 files = [support.findfile('tokenize_tests.txt'),
2234 support.findfile('tokenize_tests-no-coding-cookie-'
2235 'and-utf8-bom-sig-only.txt')]
2236 for filetype in (GzipTest, Bz2Test, LzmaTest):
2237 if not filetype.open:
2238 continue
2239 try:
2240 tar_name = tmpname + '.' + filetype.suffix
2241 out = self.tarfilecmd('-c', tar_name, *files)
2242 with filetype.taropen(tar_name) as tar:
2243 tar.getmembers()
2244 finally:
2245 support.unlink(tar_name)
2246
2010 def test_extract_command(self): 2247 def test_extract_command(self):
2011 self.make_simple_tarfile(tmpname) 2248 self.make_simple_tarfile(tmpname)
2012 for opt in '-e', '--extract': 2249 for opt in '-e', '--extract':
2013 try: 2250 try:
2014 with support.temp_cwd(tarextdir): 2251 with support.temp_cwd(tarextdir):
2015 out = self.tarfilecmd(opt, tmpname) 2252 out = self.tarfilecmd(opt, tmpname)
2016 self.assertEqual(out, b'') 2253 self.assertEqual(out, b'')
2017 finally: 2254 finally:
2018 support.rmtree(tarextdir) 2255 support.rmtree(tarextdir)
2019 2256
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
2161 except tarfile.ReadError: 2398 except tarfile.ReadError:
2162 pass # we have no interest in ReadErrors 2399 pass # we have no interest in ReadErrors
2163 2400
2164 def test_partial_input(self): 2401 def test_partial_input(self):
2165 self._test_partial_input("r") 2402 self._test_partial_input("r")
2166 2403
2167 def test_partial_input_bz2(self): 2404 def test_partial_input_bz2(self):
2168 self._test_partial_input("r:bz2") 2405 self._test_partial_input("r:bz2")
2169 2406
2170 2407
2171 @unittest.skipIf(hasattr(os, "link"), "requires os.link to be missing") 2408 def root_is_uid_gid_0():
2172 class TestTarfileLinkExtract(unittest.TestCase): 2409 try:
2173 """Regression test for #23228""" 2410 import pwd, grp
2174 2411 except ImportError:
2175 def setUp(self): 2412 return False
2176 self.tempdir = tempfile.mkdtemp() 2413 if pwd.getpwuid(0)[0] != 'root':
2177 self.cwd = os.getcwd() 2414 return False
barry 2015/01/13 17:38:20 I'd probably move the chdir into a try/finally ins
2178 os.chdir(self.tempdir) 2415 if grp.getgrgid(0)[0] != 'root':
2179 2416 return False
2180 def tearDown(self): 2417 return True
2181 os.chdir(self.cwd) 2418
2182 support.rmtree(self.tempdir) 2419
2183 2420 @unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown")
2184 def test_tarfile_crash(self): 2421 @unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid")
2185 os.mkdir("run") 2422 class NumericOwnerTest(unittest.TestCase):
2186 os.mkdir("var") 2423 # mock the following:
2187 os.symlink("run", "var/run") 2424 # os.chown: so we can test what's being called
2188 tarname = "lala.tar" 2425 # os.chmod: so the modes are not actually changed. if they are, we can't
2189 with tarfile.open(tarname, "w") as w: 2426 # delete the files/directories
2190 w.add("run") 2427 # os.geteuid: so we can lie and say we're root (uid = 0)
2191 w.add("var") 2428
2192 with tarfile.open(tarname, "r") as r: 2429 @staticmethod
2193 r.extractall(".") 2430 def _make_test_archive(filename_1, dirname_1, filename_2):
2194 2431 # the file contents to write
2432 fobj = io.BytesIO(b"content")
2433
2434 # create a tar file with a file, a directory, and a file within that
2435 # directory. Assign various .uid/.gid values to them
2436 items = [(filename_1, 99, 98, tarfile.REGTYPE, fobj),
2437 (dirname_1, 77, 76, tarfile.DIRTYPE, None),
2438 (filename_2, 88, 87, tarfile.REGTYPE, fobj),
2439 ]
2440 with tarfile.open(tmpname, 'w') as tarfl:
2441 for name, uid, gid, typ, contents in items:
2442 t = tarfile.TarInfo(name)
2443 t.uid = uid
2444 t.gid = gid
2445 t.uname = 'root'
2446 t.gname = 'root'
2447 t.type = typ
2448 tarfl.addfile(t, contents)
2449
2450 # return the full pathname to the tar file
2451 return tmpname
2452
2453 @staticmethod
2454 @contextmanager
2455 def _setup_test(mock_geteuid):
2456 mock_geteuid.return_value = 0 # lie and say we're root
2457 fname = 'numeric-owner-testfile'
2458 dirname = 'dir'
2459
2460 # the names we want stored in the tarfile
2461 filename_1 = fname
2462 dirname_1 = dirname
2463 filename_2 = os.path.join(dirname, fname)
2464
2465 # create the tarfile with the contents we're after
2466 tar_filename = NumericOwnerTest._make_test_archive(filename_1,
2467 dirname_1,
2468 filename_2)
2469
2470 # open the tarfile for reading. yield it and the names of the items
2471 # we stored into the file
2472 with tarfile.open(tar_filename) as tarfl:
2473 yield tarfl, filename_1, dirname_1, filename_2
2474
2475 @unittest.mock.patch('os.chown')
2476 @unittest.mock.patch('os.chmod')
2477 @unittest.mock.patch('os.geteuid')
2478 def test_extract_with_numeric_owner(self, mock_geteuid, mock_chmod,
2479 mock_chown):
2480 with self._setup_test(mock_geteuid) as (tarfl, filename_1, _,
2481 filename_2):
2482 tarfl.extract(filename_1, TEMPDIR, numeric_owner=True)
2483 tarfl.extract(filename_2 , TEMPDIR, numeric_owner=True)
2484
2485 # convert to filesystem paths
2486 f_filename_1 = os.path.join(TEMPDIR, filename_1)
2487 f_filename_2 = os.path.join(TEMPDIR, filename_2)
2488
2489 mock_chown.assert_has_calls([unittest.mock.call(f_filename_1, 99, 98),
2490 unittest.mock.call(f_filename_2, 88, 87),
2491 ],
2492 any_order=True)
2493
2494 @unittest.mock.patch('os.chown')
2495 @unittest.mock.patch('os.chmod')
2496 @unittest.mock.patch('os.geteuid')
2497 def test_extractall_with_numeric_owner(self, mock_geteuid, mock_chmod,
2498 mock_chown):
2499 with self._setup_test(mock_geteuid) as (tarfl, filename_1, dirname_1,
2500 filename_2):
2501 tarfl.extractall(TEMPDIR, numeric_owner=True)
2502
2503 # convert to filesystem paths
2504 f_filename_1 = os.path.join(TEMPDIR, filename_1)
2505 f_dirname_1 = os.path.join(TEMPDIR, dirname_1)
2506 f_filename_2 = os.path.join(TEMPDIR, filename_2)
2507
2508 mock_chown.assert_has_calls([unittest.mock.call(f_filename_1, 99, 98),
2509 unittest.mock.call(f_dirname_1, 77, 76),
2510 unittest.mock.call(f_filename_2, 88, 87),
2511 ],
2512 any_order=True)
2513
2514 # this test requires that uid=0 and gid=0 really be named 'root'. that's
2515 # because the uname and gname in the test file are 'root', and extract()
2516 # will look them up using pwd and grp to find their uid and gid, which we
2517 # test here to be 0.
2518 @unittest.skipUnless(root_is_uid_gid_0(),
2519 'uid=0,gid=0 must be named "root"')
2520 @unittest.mock.patch('os.chown')
2521 @unittest.mock.patch('os.chmod')
2522 @unittest.mock.patch('os.geteuid')
2523 def test_extract_without_numeric_owner(self, mock_geteuid, mock_chmod,
2524 mock_chown):
2525 with self._setup_test(mock_geteuid) as (tarfl, filename_1, _, _):
2526 tarfl.extract(filename_1, TEMPDIR, numeric_owner=False)
2527
2528 # convert to filesystem paths
2529 f_filename_1 = os.path.join(TEMPDIR, filename_1)
2530
2531 mock_chown.assert_called_with(f_filename_1, 0, 0)
2532
2533 @unittest.mock.patch('os.geteuid')
2534 def test_keyword_only(self, mock_geteuid):
2535 with self._setup_test(mock_geteuid) as (tarfl, filename_1, _, _):
2536 self.assertRaises(TypeError,
2537 tarfl.extract, filename_1, TEMPDIR, False, True)
2538
2195 2539
2196 def setUpModule(): 2540 def setUpModule():
2197 support.unlink(TEMPDIR) 2541 support.unlink(TEMPDIR)
2198 os.makedirs(TEMPDIR) 2542 os.makedirs(TEMPDIR)
2199 2543
2200 global testtarnames 2544 global testtarnames
2201 testtarnames = [tarname] 2545 testtarnames = [tarname]
2202 with open(tarname, "rb") as fobj: 2546 with open(tarname, "rb") as fobj:
2203 data = fobj.read() 2547 data = fobj.read()
2204 2548
2205 # Create compressed tarfiles. 2549 # Create compressed tarfiles.
2206 for c in GzipTest, Bz2Test, LzmaTest: 2550 for c in GzipTest, Bz2Test, LzmaTest:
2207 if c.open: 2551 if c.open:
2208 support.unlink(c.tarname) 2552 support.unlink(c.tarname)
2209 testtarnames.append(c.tarname) 2553 testtarnames.append(c.tarname)
2210 with c.open(c.tarname, "wb") as tar: 2554 with c.open(c.tarname, "wb") as tar:
2211 tar.write(data) 2555 tar.write(data)
2212 2556
2213 def tearDownModule(): 2557 def tearDownModule():
2214 if os.path.exists(TEMPDIR): 2558 if os.path.exists(TEMPDIR):
2215 support.rmtree(TEMPDIR) 2559 support.rmtree(TEMPDIR)
2216 2560
2217 if __name__ == "__main__": 2561 if __name__ == "__main__":
2218 unittest.main() 2562 unittest.main()
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+