blob: 7e32cbccd6c56d63d97b47f7e17c9001fe2b620d [file] [log] [blame]
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00001import sys
2import os
Lars Gustäbelb506dc32007-08-07 18:36:16 +00003import io
Guido van Rossuma8add0e2007-05-14 22:03:55 +00004from hashlib import md5
Eric V. Smith7a803892015-04-15 10:27:58 -04005from contextlib import contextmanager
Serhiy Storchakaa89d22a2016-10-30 20:52:29 +02006from random import Random
Serhiy Storchakac45cd162017-03-08 10:32:44 +02007import pathlib
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00008
9import unittest
Eric V. Smith7a803892015-04-15 10:27:58 -040010import unittest.mock
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000011import tarfile
12
Berker Peksagce643912015-05-06 06:33:17 +030013from test import support
14from test.support import script_helper
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000015
16# Check for our compression modules.
17try:
18 import gzip
Brett Cannon260fbe82013-07-04 18:16:15 -040019except ImportError:
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000020 gzip = None
21try:
22 import bz2
Brett Cannon260fbe82013-07-04 18:16:15 -040023except ImportError:
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000024 bz2 = None
Lars Gustäbel0a9dd2f2011-12-10 20:38:14 +010025try:
26 import lzma
Brett Cannon260fbe82013-07-04 18:16:15 -040027except ImportError:
Lars Gustäbel0a9dd2f2011-12-10 20:38:14 +010028 lzma = None
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000029
Guido van Rossumd8faa362007-04-27 19:54:29 +000030def md5sum(data):
Guido van Rossuma8add0e2007-05-14 22:03:55 +000031 return md5(data).hexdigest()
Guido van Rossumd8faa362007-04-27 19:54:29 +000032
Antoine Pitrouab58b5f2010-09-23 19:39:35 +000033TEMPDIR = os.path.abspath(support.TESTFN) + "-tardir"
Serhiy Storchakad27b4552013-11-24 01:53:29 +020034tarextdir = TEMPDIR + '-extract-test'
Antoine Pitrou941ee882009-11-11 20:59:38 +000035tarname = support.findfile("testtar.tar")
Guido van Rossumd8faa362007-04-27 19:54:29 +000036gzipname = os.path.join(TEMPDIR, "testtar.tar.gz")
37bz2name = os.path.join(TEMPDIR, "testtar.tar.bz2")
Lars Gustäbel0a9dd2f2011-12-10 20:38:14 +010038xzname = os.path.join(TEMPDIR, "testtar.tar.xz")
Guido van Rossumd8faa362007-04-27 19:54:29 +000039tmpname = os.path.join(TEMPDIR, "tmp.tar")
Serhiy Storchakad27b4552013-11-24 01:53:29 +020040dotlessname = os.path.join(TEMPDIR, "testtar")
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000041
Guido van Rossumd8faa362007-04-27 19:54:29 +000042md5_regtype = "65f477c818ad9e15f7feab0c6d37742f"
43md5_sparse = "a54fbc4ca4f4399a90e1b27164012fc6"
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000044
45
Serhiy Storchaka8b562922013-06-17 15:38:50 +030046class TarTest:
Guido van Rossumd8faa362007-04-27 19:54:29 +000047 tarname = tarname
Serhiy Storchaka8b562922013-06-17 15:38:50 +030048 suffix = ''
49 open = io.FileIO
Serhiy Storchaka53ad0cd2014-01-18 15:35:37 +020050 taropen = tarfile.TarFile.taropen
Serhiy Storchaka8b562922013-06-17 15:38:50 +030051
52 @property
53 def mode(self):
54 return self.prefix + self.suffix
55
56@support.requires_gzip
57class GzipTest:
58 tarname = gzipname
59 suffix = 'gz'
60 open = gzip.GzipFile if gzip else None
Serhiy Storchaka53ad0cd2014-01-18 15:35:37 +020061 taropen = tarfile.TarFile.gzopen
Serhiy Storchaka8b562922013-06-17 15:38:50 +030062
63@support.requires_bz2
64class Bz2Test:
65 tarname = bz2name
66 suffix = 'bz2'
67 open = bz2.BZ2File if bz2 else None
Serhiy Storchaka53ad0cd2014-01-18 15:35:37 +020068 taropen = tarfile.TarFile.bz2open
Serhiy Storchaka8b562922013-06-17 15:38:50 +030069
70@support.requires_lzma
71class LzmaTest:
72 tarname = xzname
73 suffix = 'xz'
74 open = lzma.LZMAFile if lzma else None
Serhiy Storchaka53ad0cd2014-01-18 15:35:37 +020075 taropen = tarfile.TarFile.xzopen
Serhiy Storchaka8b562922013-06-17 15:38:50 +030076
77
78class ReadTest(TarTest):
79
80 prefix = "r:"
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000081
82 def setUp(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +030083 self.tar = tarfile.open(self.tarname, mode=self.mode,
84 encoding="iso8859-1")
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000085
86 def tearDown(self):
87 self.tar.close()
88
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000089
Serhiy Storchaka8b562922013-06-17 15:38:50 +030090class UstarReadTest(ReadTest, unittest.TestCase):
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +000091
Guido van Rossumd8faa362007-04-27 19:54:29 +000092 def test_fileobj_regular_file(self):
93 tarinfo = self.tar.getmember("ustar/regtype")
Lars Gustäbel7a919e92012-05-05 18:15:03 +020094 with self.tar.extractfile(tarinfo) as fobj:
Antoine Pitroue1eca4e2010-10-29 23:49:49 +000095 data = fobj.read()
Serhiy Storchaka8b562922013-06-17 15:38:50 +030096 self.assertEqual(len(data), tarinfo.size,
97 "regular file extraction failed")
98 self.assertEqual(md5sum(data), md5_regtype,
Antoine Pitroue1eca4e2010-10-29 23:49:49 +000099 "regular file extraction failed")
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000100
Guido van Rossumd8faa362007-04-27 19:54:29 +0000101 def test_fileobj_readlines(self):
102 self.tar.extract("ustar/regtype", TEMPDIR)
103 tarinfo = self.tar.getmember("ustar/regtype")
Antoine Pitrou95f55602010-09-23 18:36:46 +0000104 with open(os.path.join(TEMPDIR, "ustar/regtype"), "r") as fobj1:
105 lines1 = fobj1.readlines()
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000106
Lars Gustäbel7a919e92012-05-05 18:15:03 +0200107 with self.tar.extractfile(tarinfo) as fobj:
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000108 fobj2 = io.TextIOWrapper(fobj)
109 lines2 = fobj2.readlines()
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300110 self.assertEqual(lines1, lines2,
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000111 "fileobj.readlines() failed")
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300112 self.assertEqual(len(lines2), 114,
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000113 "fileobj.readlines() failed")
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300114 self.assertEqual(lines2[83],
115 "I will gladly admit that Python is not the fastest "
116 "running scripting language.\n",
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000117 "fileobj.readlines() failed")
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000118
Guido van Rossumd8faa362007-04-27 19:54:29 +0000119 def test_fileobj_iter(self):
120 self.tar.extract("ustar/regtype", TEMPDIR)
121 tarinfo = self.tar.getmember("ustar/regtype")
Victor Stinner4e86d5b2011-05-04 13:55:36 +0200122 with open(os.path.join(TEMPDIR, "ustar/regtype"), "r") as fobj1:
Antoine Pitrou95f55602010-09-23 18:36:46 +0000123 lines1 = fobj1.readlines()
Lars Gustäbel7a919e92012-05-05 18:15:03 +0200124 with self.tar.extractfile(tarinfo) as fobj2:
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000125 lines2 = list(io.TextIOWrapper(fobj2))
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300126 self.assertEqual(lines1, lines2,
127 "fileobj.__iter__() failed")
Martin v. Löwisdf241532005-03-03 08:17:42 +0000128
Guido van Rossumd8faa362007-04-27 19:54:29 +0000129 def test_fileobj_seek(self):
130 self.tar.extract("ustar/regtype", TEMPDIR)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000131 with open(os.path.join(TEMPDIR, "ustar/regtype"), "rb") as fobj:
132 data = fobj.read()
Neal Norwitzf3396542005-10-28 05:52:22 +0000133
Guido van Rossumd8faa362007-04-27 19:54:29 +0000134 tarinfo = self.tar.getmember("ustar/regtype")
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200135 with self.tar.extractfile(tarinfo) as fobj:
136 text = fobj.read()
137 fobj.seek(0)
138 self.assertEqual(0, fobj.tell(),
139 "seek() to file's start failed")
140 fobj.seek(2048, 0)
141 self.assertEqual(2048, fobj.tell(),
142 "seek() to absolute position failed")
143 fobj.seek(-1024, 1)
144 self.assertEqual(1024, fobj.tell(),
145 "seek() to negative relative position failed")
146 fobj.seek(1024, 1)
147 self.assertEqual(2048, fobj.tell(),
148 "seek() to positive relative position failed")
149 s = fobj.read(10)
150 self.assertEqual(s, data[2048:2058],
151 "read() after seek failed")
152 fobj.seek(0, 2)
153 self.assertEqual(tarinfo.size, fobj.tell(),
154 "seek() to file's end failed")
155 self.assertEqual(fobj.read(), b"",
156 "read() at file's end did not return empty string")
157 fobj.seek(-tarinfo.size, 2)
158 self.assertEqual(0, fobj.tell(),
159 "relative seek() to file's end failed")
160 fobj.seek(512)
161 s1 = fobj.readlines()
162 fobj.seek(512)
163 s2 = fobj.readlines()
164 self.assertEqual(s1, s2,
165 "readlines() after seek failed")
166 fobj.seek(0)
167 self.assertEqual(len(fobj.readline()), fobj.tell(),
168 "tell() after readline() failed")
169 fobj.seek(512)
170 self.assertEqual(len(fobj.readline()) + 512, fobj.tell(),
171 "tell() after seek() and readline() failed")
172 fobj.seek(0)
173 line = fobj.readline()
174 self.assertEqual(fobj.read(), data[len(line):],
175 "read() after readline() failed")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000176
Lars Gustäbel7a919e92012-05-05 18:15:03 +0200177 def test_fileobj_text(self):
178 with self.tar.extractfile("ustar/regtype") as fobj:
179 fobj = io.TextIOWrapper(fobj)
180 data = fobj.read().encode("iso8859-1")
181 self.assertEqual(md5sum(data), md5_regtype)
182 try:
183 fobj.seek(100)
184 except AttributeError:
185 # Issue #13815: seek() complained about a missing
186 # flush() method.
187 self.fail("seeking failed in text mode")
188
Lars Gustäbel1b512722010-06-03 12:45:16 +0000189 # Test if symbolic and hard links are resolved by extractfile(). The
190 # test link members each point to a regular member whose data is
191 # supposed to be exported.
192 def _test_fileobj_link(self, lnktype, regtype):
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300193 with self.tar.extractfile(lnktype) as a, \
194 self.tar.extractfile(regtype) as b:
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000195 self.assertEqual(a.name, b.name)
Lars Gustäbel1b512722010-06-03 12:45:16 +0000196
197 def test_fileobj_link1(self):
198 self._test_fileobj_link("ustar/lnktype", "ustar/regtype")
199
200 def test_fileobj_link2(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300201 self._test_fileobj_link("./ustar/linktest2/lnktype",
202 "ustar/linktest1/regtype")
Lars Gustäbel1b512722010-06-03 12:45:16 +0000203
204 def test_fileobj_symlink1(self):
205 self._test_fileobj_link("ustar/symtype", "ustar/regtype")
206
207 def test_fileobj_symlink2(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300208 self._test_fileobj_link("./ustar/linktest2/symtype",
209 "ustar/linktest1/regtype")
Lars Gustäbel1b512722010-06-03 12:45:16 +0000210
Lars Gustäbel1ef9eda2012-04-24 21:04:40 +0200211 def test_issue14160(self):
212 self._test_fileobj_link("symtype2", "ustar/regtype")
213
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300214class GzipUstarReadTest(GzipTest, UstarReadTest):
215 pass
216
217class Bz2UstarReadTest(Bz2Test, UstarReadTest):
218 pass
219
220class LzmaUstarReadTest(LzmaTest, UstarReadTest):
221 pass
222
Guido van Rossumd8faa362007-04-27 19:54:29 +0000223
Serhiy Storchaka3b4f1592014-02-05 20:53:36 +0200224class ListTest(ReadTest, unittest.TestCase):
225
226 # Override setUp to use default encoding (UTF-8)
227 def setUp(self):
228 self.tar = tarfile.open(self.tarname, mode=self.mode)
229
230 def test_list(self):
231 tio = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n')
232 with support.swap_attr(sys, 'stdout', tio):
233 self.tar.list(verbose=False)
234 out = tio.detach().getvalue()
235 self.assertIn(b'ustar/conttype', out)
236 self.assertIn(b'ustar/regtype', out)
237 self.assertIn(b'ustar/lnktype', out)
238 self.assertIn(b'ustar' + (b'/12345' * 40) + b'67/longname', out)
239 self.assertIn(b'./ustar/linktest2/symtype', out)
240 self.assertIn(b'./ustar/linktest2/lnktype', out)
241 # Make sure it puts trailing slash for directory
242 self.assertIn(b'ustar/dirtype/', out)
243 self.assertIn(b'ustar/dirtype-with-size/', out)
244 # Make sure it is able to print unencodable characters
Serhiy Storchaka162c4772014-02-19 18:44:12 +0200245 def conv(b):
246 s = b.decode(self.tar.encoding, 'surrogateescape')
247 return s.encode('ascii', 'backslashreplace')
248 self.assertIn(conv(b'ustar/umlauts-\xc4\xd6\xdc\xe4\xf6\xfc\xdf'), out)
249 self.assertIn(conv(b'misc/regtype-hpux-signed-chksum-'
250 b'\xc4\xd6\xdc\xe4\xf6\xfc\xdf'), out)
251 self.assertIn(conv(b'misc/regtype-old-v7-signed-chksum-'
252 b'\xc4\xd6\xdc\xe4\xf6\xfc\xdf'), out)
253 self.assertIn(conv(b'pax/bad-pax-\xe4\xf6\xfc'), out)
254 self.assertIn(conv(b'pax/hdrcharset-\xe4\xf6\xfc'), out)
Serhiy Storchaka3b4f1592014-02-05 20:53:36 +0200255 # Make sure it prints files separated by one newline without any
256 # 'ls -l'-like accessories if verbose flag is not being used
257 # ...
258 # ustar/conttype
259 # ustar/regtype
260 # ...
261 self.assertRegex(out, br'ustar/conttype ?\r?\n'
262 br'ustar/regtype ?\r?\n')
263 # Make sure it does not print the source of link without verbose flag
264 self.assertNotIn(b'link to', out)
265 self.assertNotIn(b'->', out)
266
267 def test_list_verbose(self):
268 tio = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n')
269 with support.swap_attr(sys, 'stdout', tio):
270 self.tar.list(verbose=True)
271 out = tio.detach().getvalue()
272 # Make sure it prints files separated by one newline with 'ls -l'-like
273 # accessories if verbose flag is being used
274 # ...
275 # ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/conttype
276 # ?rw-r--r-- tarfile/tarfile 7011 2003-01-06 07:19:43 ustar/regtype
277 # ...
Serhiy Storchaka255493c2014-02-05 20:54:43 +0200278 self.assertRegex(out, (br'\?rw-r--r-- tarfile/tarfile\s+7011 '
Serhiy Storchaka3b4f1592014-02-05 20:53:36 +0200279 br'\d{4}-\d\d-\d\d\s+\d\d:\d\d:\d\d '
280 br'ustar/\w+type ?\r?\n') * 2)
281 # Make sure it prints the source of link with verbose flag
282 self.assertIn(b'ustar/symtype -> regtype', out)
283 self.assertIn(b'./ustar/linktest2/symtype -> ../linktest1/regtype', out)
284 self.assertIn(b'./ustar/linktest2/lnktype link to '
285 b'./ustar/linktest1/regtype', out)
286 self.assertIn(b'gnu' + (b'/123' * 125) + b'/longlink link to gnu' +
287 (b'/123' * 125) + b'/longname', out)
288 self.assertIn(b'pax' + (b'/123' * 125) + b'/longlink link to pax' +
289 (b'/123' * 125) + b'/longname', out)
290
Serhiy Storchakaa7eb7462014-08-21 10:01:16 +0300291 def test_list_members(self):
292 tio = io.TextIOWrapper(io.BytesIO(), 'ascii', newline='\n')
293 def members(tar):
294 for tarinfo in tar.getmembers():
295 if 'reg' in tarinfo.name:
296 yield tarinfo
297 with support.swap_attr(sys, 'stdout', tio):
298 self.tar.list(verbose=False, members=members(self.tar))
299 out = tio.detach().getvalue()
300 self.assertIn(b'ustar/regtype', out)
301 self.assertNotIn(b'ustar/conttype', out)
302
Serhiy Storchaka3b4f1592014-02-05 20:53:36 +0200303
304class GzipListTest(GzipTest, ListTest):
305 pass
306
307
308class Bz2ListTest(Bz2Test, ListTest):
309 pass
310
311
312class LzmaListTest(LzmaTest, ListTest):
313 pass
314
315
Lars Gustäbel9520a432009-11-22 18:48:49 +0000316class CommonReadTest(ReadTest):
317
318 def test_empty_tarfile(self):
319 # Test for issue6123: Allow opening empty archives.
320 # This test checks if tarfile.open() is able to open an empty tar
321 # archive successfully. Note that an empty tar archive is not the
322 # same as an empty file!
Antoine Pitrou95f55602010-09-23 18:36:46 +0000323 with tarfile.open(tmpname, self.mode.replace("r", "w")):
324 pass
Lars Gustäbel9520a432009-11-22 18:48:49 +0000325 try:
326 tar = tarfile.open(tmpname, self.mode)
327 tar.getnames()
328 except tarfile.ReadError:
329 self.fail("tarfile.open() failed on empty archive")
Antoine Pitrou95f55602010-09-23 18:36:46 +0000330 else:
331 self.assertListEqual(tar.getmembers(), [])
332 finally:
333 tar.close()
Lars Gustäbel9520a432009-11-22 18:48:49 +0000334
Serhiy Storchakaf22fe0f2014-01-13 19:08:00 +0200335 def test_non_existent_tarfile(self):
336 # Test for issue11513: prevent non-existent gzipped tarfiles raising
337 # multiple exceptions.
338 with self.assertRaisesRegex(FileNotFoundError, "xxx"):
339 tarfile.open("xxx", self.mode)
340
Lars Gustäbel9520a432009-11-22 18:48:49 +0000341 def test_null_tarfile(self):
342 # Test for issue6123: Allow opening empty archives.
343 # This test guarantees that tarfile.open() does not treat an empty
344 # file as an empty tar archive.
Antoine Pitrou95f55602010-09-23 18:36:46 +0000345 with open(tmpname, "wb"):
346 pass
Lars Gustäbel9520a432009-11-22 18:48:49 +0000347 self.assertRaises(tarfile.ReadError, tarfile.open, tmpname, self.mode)
348 self.assertRaises(tarfile.ReadError, tarfile.open, tmpname)
349
350 def test_ignore_zeros(self):
351 # Test TarFile's ignore_zeros option.
Serhiy Storchakaa89d22a2016-10-30 20:52:29 +0200352 # generate 512 pseudorandom bytes
353 data = Random(0).getrandbits(512*8).to_bytes(512, 'big')
Lars Gustäbel9520a432009-11-22 18:48:49 +0000354 for char in (b'\0', b'a'):
355 # Test if EOFHeaderError ('\0') and InvalidHeaderError ('a')
356 # are ignored correctly.
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300357 with self.open(tmpname, "w") as fobj:
Antoine Pitrou95f55602010-09-23 18:36:46 +0000358 fobj.write(char * 1024)
Serhiy Storchakaa89d22a2016-10-30 20:52:29 +0200359 tarinfo = tarfile.TarInfo("foo")
360 tarinfo.size = len(data)
361 fobj.write(tarinfo.tobuf())
362 fobj.write(data)
Lars Gustäbel9520a432009-11-22 18:48:49 +0000363
364 tar = tarfile.open(tmpname, mode="r", ignore_zeros=True)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000365 try:
366 self.assertListEqual(tar.getnames(), ["foo"],
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300367 "ignore_zeros=True should have skipped the %r-blocks" %
368 char)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000369 finally:
370 tar.close()
Lars Gustäbel9520a432009-11-22 18:48:49 +0000371
Lars Gustäbel03572682015-07-06 09:27:24 +0200372 def test_premature_end_of_archive(self):
373 for size in (512, 600, 1024, 1200):
374 with tarfile.open(tmpname, "w:") as tar:
375 t = tarfile.TarInfo("foo")
376 t.size = 1024
377 tar.addfile(t, io.BytesIO(b"a" * 1024))
378
379 with open(tmpname, "r+b") as fobj:
380 fobj.truncate(size)
381
382 with tarfile.open(tmpname) as tar:
383 with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
384 for t in tar:
385 pass
386
387 with tarfile.open(tmpname) as tar:
388 t = tar.next()
389
390 with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
391 tar.extract(t, TEMPDIR)
392
393 with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"):
394 tar.extractfile(t).read()
Lars Gustäbel9520a432009-11-22 18:48:49 +0000395
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300396class MiscReadTestBase(CommonReadTest):
Serhiy Storchaka2c6a3ae2014-07-16 23:58:58 +0300397 def requires_name_attribute(self):
398 pass
399
Thomas Woutersed03b412007-08-28 21:37:11 +0000400 def test_no_name_argument(self):
Serhiy Storchaka2c6a3ae2014-07-16 23:58:58 +0300401 self.requires_name_attribute()
Antoine Pitrou95f55602010-09-23 18:36:46 +0000402 with open(self.tarname, "rb") as fobj:
Serhiy Storchaka2c6a3ae2014-07-16 23:58:58 +0300403 self.assertIsInstance(fobj.name, str)
404 with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
405 self.assertIsInstance(tar.name, str)
406 self.assertEqual(tar.name, os.path.abspath(fobj.name))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000407
Thomas Woutersed03b412007-08-28 21:37:11 +0000408 def test_no_name_attribute(self):
Antoine Pitrou95f55602010-09-23 18:36:46 +0000409 with open(self.tarname, "rb") as fobj:
410 data = fobj.read()
Thomas Woutersed03b412007-08-28 21:37:11 +0000411 fobj = io.BytesIO(data)
412 self.assertRaises(AttributeError, getattr, fobj, "name")
413 tar = tarfile.open(fileobj=fobj, mode=self.mode)
Serhiy Storchaka2c6a3ae2014-07-16 23:58:58 +0300414 self.assertIsNone(tar.name)
Thomas Woutersed03b412007-08-28 21:37:11 +0000415
416 def test_empty_name_attribute(self):
Antoine Pitrou95f55602010-09-23 18:36:46 +0000417 with open(self.tarname, "rb") as fobj:
418 data = fobj.read()
Thomas Woutersed03b412007-08-28 21:37:11 +0000419 fobj = io.BytesIO(data)
420 fobj.name = ""
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000421 with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
Serhiy Storchaka2c6a3ae2014-07-16 23:58:58 +0300422 self.assertIsNone(tar.name)
423
424 def test_int_name_attribute(self):
425 # Issue 21044: tarfile.open() should handle fileobj with an integer
426 # 'name' attribute.
427 fd = os.open(self.tarname, os.O_RDONLY)
428 with open(fd, 'rb') as fobj:
429 self.assertIsInstance(fobj.name, int)
430 with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
431 self.assertIsNone(tar.name)
432
433 def test_bytes_name_attribute(self):
434 self.requires_name_attribute()
435 tarname = os.fsencode(self.tarname)
436 with open(tarname, 'rb') as fobj:
437 self.assertIsInstance(fobj.name, bytes)
438 with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
439 self.assertIsInstance(tar.name, bytes)
440 self.assertEqual(tar.name, os.path.abspath(fobj.name))
Thomas Woutersed03b412007-08-28 21:37:11 +0000441
Serhiy Storchakac45cd162017-03-08 10:32:44 +0200442 def test_pathlike_name(self):
443 tarname = pathlib.Path(self.tarname)
444 with tarfile.open(tarname, mode=self.mode) as tar:
445 self.assertIsInstance(tar.name, str)
446 self.assertEqual(tar.name, os.path.abspath(os.fspath(tarname)))
447 with self.taropen(tarname) as tar:
448 self.assertIsInstance(tar.name, str)
449 self.assertEqual(tar.name, os.path.abspath(os.fspath(tarname)))
450 with tarfile.TarFile.open(tarname, mode=self.mode) as tar:
451 self.assertIsInstance(tar.name, str)
452 self.assertEqual(tar.name, os.path.abspath(os.fspath(tarname)))
453 if self.suffix == '':
454 with tarfile.TarFile(tarname, mode='r') as tar:
455 self.assertIsInstance(tar.name, str)
456 self.assertEqual(tar.name, os.path.abspath(os.fspath(tarname)))
457
Serhiy Storchaka53ad0cd2014-01-18 15:35:37 +0200458 def test_illegal_mode_arg(self):
459 with open(tmpname, 'wb'):
460 pass
461 with self.assertRaisesRegex(ValueError, 'mode must be '):
462 tar = self.taropen(tmpname, 'q')
463 with self.assertRaisesRegex(ValueError, 'mode must be '):
464 tar = self.taropen(tmpname, 'rw')
465 with self.assertRaisesRegex(ValueError, 'mode must be '):
466 tar = self.taropen(tmpname, '')
467
Christian Heimesd8654cf2007-12-02 15:22:16 +0000468 def test_fileobj_with_offset(self):
469 # Skip the first member and store values from the second member
470 # of the testtar.
471 tar = tarfile.open(self.tarname, mode=self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000472 try:
473 tar.next()
474 t = tar.next()
475 name = t.name
476 offset = t.offset
Lars Gustäbel7a919e92012-05-05 18:15:03 +0200477 with tar.extractfile(t) as f:
478 data = f.read()
Antoine Pitrou95f55602010-09-23 18:36:46 +0000479 finally:
480 tar.close()
Christian Heimesd8654cf2007-12-02 15:22:16 +0000481
482 # Open the testtar and seek to the offset of the second member.
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300483 with self.open(self.tarname) as fobj:
Antoine Pitrou95f55602010-09-23 18:36:46 +0000484 fobj.seek(offset)
Christian Heimesd8654cf2007-12-02 15:22:16 +0000485
Antoine Pitrou95f55602010-09-23 18:36:46 +0000486 # Test if the tarfile starts with the second member.
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200487 with tar.open(self.tarname, mode="r:", fileobj=fobj) as tar:
488 t = tar.next()
489 self.assertEqual(t.name, name)
490 # Read to the end of fileobj and test if seeking back to the
491 # beginning works.
492 tar.getmembers()
493 self.assertEqual(tar.extractfile(t).read(), data,
494 "seek back did not work")
Christian Heimesd8654cf2007-12-02 15:22:16 +0000495
Guido van Rossumd8faa362007-04-27 19:54:29 +0000496 def test_fail_comp(self):
497 # For Gzip and Bz2 Tests: fail with a ReadError on an uncompressed file.
Guido van Rossumd8faa362007-04-27 19:54:29 +0000498 self.assertRaises(tarfile.ReadError, tarfile.open, tarname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000499 with open(tarname, "rb") as fobj:
500 self.assertRaises(tarfile.ReadError, tarfile.open,
501 fileobj=fobj, mode=self.mode)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000502
503 def test_v7_dirtype(self):
504 # Test old style dirtype member (bug #1336623):
505 # Old V7 tars create directory members using an AREGTYPE
506 # header with a "/" appended to the filename field.
507 tarinfo = self.tar.getmember("misc/dirtype-old-v7")
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300508 self.assertEqual(tarinfo.type, tarfile.DIRTYPE,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000509 "v7 dirtype failed")
510
Christian Heimes126d29a2008-02-11 22:57:17 +0000511 def test_xstar_type(self):
512 # The xstar format stores extra atime and ctime fields inside the
513 # space reserved for the prefix field. The prefix field must be
514 # ignored in this case, otherwise it will mess up the name.
515 try:
516 self.tar.getmember("misc/regtype-xstar")
517 except KeyError:
518 self.fail("failed to find misc/regtype-xstar (mangled prefix?)")
519
Guido van Rossumd8faa362007-04-27 19:54:29 +0000520 def test_check_members(self):
521 for tarinfo in self.tar:
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300522 self.assertEqual(int(tarinfo.mtime), 0o7606136617,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000523 "wrong mtime for %s" % tarinfo.name)
524 if not tarinfo.name.startswith("ustar/"):
525 continue
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300526 self.assertEqual(tarinfo.uname, "tarfile",
Guido van Rossumd8faa362007-04-27 19:54:29 +0000527 "wrong uname for %s" % tarinfo.name)
528
529 def test_find_members(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300530 self.assertEqual(self.tar.getmembers()[-1].name, "misc/eof",
Guido van Rossumd8faa362007-04-27 19:54:29 +0000531 "could not find all members")
532
Brian Curtin74e45612010-07-09 15:58:59 +0000533 @unittest.skipUnless(hasattr(os, "link"),
534 "Missing hardlink implementation")
Brian Curtin3b4499c2010-12-28 14:31:47 +0000535 @support.skip_unless_symlink
Guido van Rossumd8faa362007-04-27 19:54:29 +0000536 def test_extract_hardlink(self):
537 # Test hardlink extraction (e.g. bug #857297).
Serhiy Storchaka88339c42012-12-30 20:16:30 +0200538 with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar:
Antoine Pitrou95f55602010-09-23 18:36:46 +0000539 tar.extract("ustar/regtype", TEMPDIR)
Tim Goldene0bd2c52014-05-06 13:24:26 +0100540 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/regtype"))
Neal Norwitzf3396542005-10-28 05:52:22 +0000541
Serhiy Storchaka88339c42012-12-30 20:16:30 +0200542 tar.extract("ustar/lnktype", TEMPDIR)
Tim Goldene0bd2c52014-05-06 13:24:26 +0100543 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/lnktype"))
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000544 with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f:
545 data = f.read()
Antoine Pitrou95f55602010-09-23 18:36:46 +0000546 self.assertEqual(md5sum(data), md5_regtype)
Neal Norwitzf3396542005-10-28 05:52:22 +0000547
Serhiy Storchaka88339c42012-12-30 20:16:30 +0200548 tar.extract("ustar/symtype", TEMPDIR)
Tim Goldene0bd2c52014-05-06 13:24:26 +0100549 self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/symtype"))
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000550 with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f:
551 data = f.read()
Antoine Pitrou95f55602010-09-23 18:36:46 +0000552 self.assertEqual(md5sum(data), md5_regtype)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000553
Christian Heimesfaf2f632008-01-06 16:59:19 +0000554 def test_extractall(self):
555 # Test if extractall() correctly restores directory permissions
556 # and times (see issue1735).
Christian Heimesfaf2f632008-01-06 16:59:19 +0000557 tar = tarfile.open(tarname, encoding="iso8859-1")
Martin v. Löwisbe647e22010-11-01 22:08:46 +0000558 DIR = os.path.join(TEMPDIR, "extractall")
559 os.mkdir(DIR)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000560 try:
561 directories = [t for t in tar if t.isdir()]
Martin v. Löwisbe647e22010-11-01 22:08:46 +0000562 tar.extractall(DIR, directories)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000563 for tarinfo in directories:
Martin v. Löwisbe647e22010-11-01 22:08:46 +0000564 path = os.path.join(DIR, tarinfo.name)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000565 if sys.platform != "win32":
566 # Win32 has no support for fine grained permissions.
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300567 self.assertEqual(tarinfo.mode & 0o777,
568 os.stat(path).st_mode & 0o777)
Victor Stinner26bfb5a2010-10-29 10:59:08 +0000569 def format_mtime(mtime):
570 if isinstance(mtime, float):
571 return "{} ({})".format(mtime, mtime.hex())
572 else:
573 return "{!r} (int)".format(mtime)
Victor Stinner14d8fe72010-10-29 11:02:06 +0000574 file_mtime = os.path.getmtime(path)
Victor Stinner26bfb5a2010-10-29 10:59:08 +0000575 errmsg = "tar mtime {0} != file time {1} of path {2!a}".format(
576 format_mtime(tarinfo.mtime),
577 format_mtime(file_mtime),
578 path)
579 self.assertEqual(tarinfo.mtime, file_mtime, errmsg)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000580 finally:
581 tar.close()
Tim Goldene0bd2c52014-05-06 13:24:26 +0100582 support.rmtree(DIR)
Christian Heimesfaf2f632008-01-06 16:59:19 +0000583
Martin v. Löwis16f344d2010-11-01 21:39:13 +0000584 def test_extract_directory(self):
585 dirtype = "ustar/dirtype"
Martin v. Löwisbe647e22010-11-01 22:08:46 +0000586 DIR = os.path.join(TEMPDIR, "extractdir")
587 os.mkdir(DIR)
588 try:
589 with tarfile.open(tarname, encoding="iso8859-1") as tar:
590 tarinfo = tar.getmember(dirtype)
591 tar.extract(tarinfo, path=DIR)
592 extracted = os.path.join(DIR, dirtype)
593 self.assertEqual(os.path.getmtime(extracted), tarinfo.mtime)
594 if sys.platform != "win32":
595 self.assertEqual(os.stat(extracted).st_mode & 0o777, 0o755)
596 finally:
Tim Goldene0bd2c52014-05-06 13:24:26 +0100597 support.rmtree(DIR)
Martin v. Löwis16f344d2010-11-01 21:39:13 +0000598
Serhiy Storchakac45cd162017-03-08 10:32:44 +0200599 def test_extractall_pathlike_name(self):
600 DIR = pathlib.Path(TEMPDIR) / "extractall"
601 with support.temp_dir(DIR), \
602 tarfile.open(tarname, encoding="iso8859-1") as tar:
603 directories = [t for t in tar if t.isdir()]
604 tar.extractall(DIR, directories)
605 for tarinfo in directories:
606 path = DIR / tarinfo.name
607 self.assertEqual(os.path.getmtime(path), tarinfo.mtime)
608
609 def test_extract_pathlike_name(self):
610 dirtype = "ustar/dirtype"
611 DIR = pathlib.Path(TEMPDIR) / "extractall"
612 with support.temp_dir(DIR), \
613 tarfile.open(tarname, encoding="iso8859-1") as tar:
614 tarinfo = tar.getmember(dirtype)
615 tar.extract(tarinfo, path=DIR)
616 extracted = DIR / dirtype
617 self.assertEqual(os.path.getmtime(extracted), tarinfo.mtime)
618
Lars Gustäbelb7f09232009-11-23 15:48:33 +0000619 def test_init_close_fobj(self):
620 # Issue #7341: Close the internal file object in the TarFile
621 # constructor in case of an error. For the test we rely on
622 # the fact that opening an empty file raises a ReadError.
623 empty = os.path.join(TEMPDIR, "empty")
Antoine Pitrou95f55602010-09-23 18:36:46 +0000624 with open(empty, "wb") as fobj:
625 fobj.write(b"")
Lars Gustäbelb7f09232009-11-23 15:48:33 +0000626
627 try:
628 tar = object.__new__(tarfile.TarFile)
629 try:
630 tar.__init__(empty)
631 except tarfile.ReadError:
632 self.assertTrue(tar.fileobj.closed)
633 else:
634 self.fail("ReadError not raised")
635 finally:
Antoine Pitrou95f55602010-09-23 18:36:46 +0000636 support.unlink(empty)
Lars Gustäbelb7f09232009-11-23 15:48:33 +0000637
Serhiy Storchaka263fab92013-05-09 14:22:26 +0300638 def test_parallel_iteration(self):
639 # Issue #16601: Restarting iteration over tarfile continued
640 # from where it left off.
641 with tarfile.open(self.tarname) as tar:
642 for m1, m2 in zip(tar, tar):
643 self.assertEqual(m1.offset, m2.offset)
644 self.assertEqual(m1.get_info(), m2.get_info())
645
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300646class MiscReadTest(MiscReadTestBase, unittest.TestCase):
647 test_fail_comp = None
Guido van Rossumd8faa362007-04-27 19:54:29 +0000648
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300649class GzipMiscReadTest(GzipTest, MiscReadTestBase, unittest.TestCase):
Serhiy Storchakaf22fe0f2014-01-13 19:08:00 +0200650 pass
Guido van Rossumd8faa362007-04-27 19:54:29 +0000651
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300652class Bz2MiscReadTest(Bz2Test, MiscReadTestBase, unittest.TestCase):
Serhiy Storchaka2c6a3ae2014-07-16 23:58:58 +0300653 def requires_name_attribute(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300654 self.skipTest("BZ2File have no name attribute")
655
656class LzmaMiscReadTest(LzmaTest, MiscReadTestBase, unittest.TestCase):
Serhiy Storchaka2c6a3ae2014-07-16 23:58:58 +0300657 def requires_name_attribute(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300658 self.skipTest("LZMAFile have no name attribute")
659
660
661class StreamReadTest(CommonReadTest, unittest.TestCase):
662
663 prefix="r|"
Guido van Rossumd8faa362007-04-27 19:54:29 +0000664
Lars Gustäbeldd071042011-02-23 11:42:22 +0000665 def test_read_through(self):
666 # Issue #11224: A poorly designed _FileInFile.read() method
667 # caused seeking errors with stream tar files.
668 for tarinfo in self.tar:
669 if not tarinfo.isreg():
670 continue
Lars Gustäbel7a919e92012-05-05 18:15:03 +0200671 with self.tar.extractfile(tarinfo) as fobj:
672 while True:
673 try:
674 buf = fobj.read(512)
675 except tarfile.StreamError:
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300676 self.fail("simple read-through using "
677 "TarFile.extractfile() failed")
Lars Gustäbel7a919e92012-05-05 18:15:03 +0200678 if not buf:
679 break
Lars Gustäbeldd071042011-02-23 11:42:22 +0000680
Guido van Rossumd8faa362007-04-27 19:54:29 +0000681 def test_fileobj_regular_file(self):
682 tarinfo = self.tar.next() # get "regtype" (can't use getmember)
Lars Gustäbel7a919e92012-05-05 18:15:03 +0200683 with self.tar.extractfile(tarinfo) as fobj:
684 data = fobj.read()
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300685 self.assertEqual(len(data), tarinfo.size,
686 "regular file extraction failed")
687 self.assertEqual(md5sum(data), md5_regtype,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000688 "regular file extraction failed")
689
690 def test_provoke_stream_error(self):
691 tarinfos = self.tar.getmembers()
Lars Gustäbel7a919e92012-05-05 18:15:03 +0200692 with self.tar.extractfile(tarinfos[0]) as f: # read the first member
693 self.assertRaises(tarfile.StreamError, f.read)
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000694
Guido van Rossumd8faa362007-04-27 19:54:29 +0000695 def test_compare_members(self):
696 tar1 = tarfile.open(tarname, encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +0000697 try:
698 tar2 = self.tar
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000699
Antoine Pitrou95f55602010-09-23 18:36:46 +0000700 while True:
701 t1 = tar1.next()
702 t2 = tar2.next()
703 if t1 is None:
704 break
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300705 self.assertIsNotNone(t2, "stream.next() failed.")
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +0000706
Antoine Pitrou95f55602010-09-23 18:36:46 +0000707 if t2.islnk() or t2.issym():
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300708 with self.assertRaises(tarfile.StreamError):
709 tar2.extractfile(t2)
Antoine Pitrou95f55602010-09-23 18:36:46 +0000710 continue
Guido van Rossumd8faa362007-04-27 19:54:29 +0000711
Antoine Pitrou95f55602010-09-23 18:36:46 +0000712 v1 = tar1.extractfile(t1)
713 v2 = tar2.extractfile(t2)
714 if v1 is None:
715 continue
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300716 self.assertIsNotNone(v2, "stream.extractfile() failed")
717 self.assertEqual(v1.read(), v2.read(),
718 "stream extraction failed")
Antoine Pitrou95f55602010-09-23 18:36:46 +0000719 finally:
720 tar1.close()
Thomas Wouters902d6eb2007-01-09 23:18:33 +0000721
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300722class GzipStreamReadTest(GzipTest, StreamReadTest):
723 pass
Thomas Wouters89f507f2006-12-13 04:49:30 +0000724
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300725class Bz2StreamReadTest(Bz2Test, StreamReadTest):
726 pass
Thomas Wouterscf297e42007-02-23 15:07:44 +0000727
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300728class LzmaStreamReadTest(LzmaTest, StreamReadTest):
729 pass
730
731
732class DetectReadTest(TarTest, unittest.TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000733 def _testfunc_file(self, name, mode):
734 try:
Antoine Pitrou95f55602010-09-23 18:36:46 +0000735 tar = tarfile.open(name, mode)
Lars Gustäbelb506dc32007-08-07 18:36:16 +0000736 except tarfile.ReadError as e:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000737 self.fail()
Antoine Pitrou95f55602010-09-23 18:36:46 +0000738 else:
739 tar.close()
Thomas Wouterscf297e42007-02-23 15:07:44 +0000740
Guido van Rossumd8faa362007-04-27 19:54:29 +0000741 def _testfunc_fileobj(self, name, mode):
742 try:
Antoine Pitrou605c2932010-09-23 20:15:14 +0000743 with open(name, "rb") as f:
744 tar = tarfile.open(name, mode, fileobj=f)
Lars Gustäbelb506dc32007-08-07 18:36:16 +0000745 except tarfile.ReadError as e:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000746 self.fail()
Antoine Pitrou95f55602010-09-23 18:36:46 +0000747 else:
748 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000749
750 def _test_modes(self, testfunc):
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300751 if self.suffix:
752 with self.assertRaises(tarfile.ReadError):
753 tarfile.open(tarname, mode="r:" + self.suffix)
754 with self.assertRaises(tarfile.ReadError):
755 tarfile.open(tarname, mode="r|" + self.suffix)
756 with self.assertRaises(tarfile.ReadError):
757 tarfile.open(self.tarname, mode="r:")
758 with self.assertRaises(tarfile.ReadError):
759 tarfile.open(self.tarname, mode="r|")
760 testfunc(self.tarname, "r")
761 testfunc(self.tarname, "r:" + self.suffix)
762 testfunc(self.tarname, "r:*")
763 testfunc(self.tarname, "r|" + self.suffix)
764 testfunc(self.tarname, "r|*")
Lars Gustäbel0a9dd2f2011-12-10 20:38:14 +0100765
Guido van Rossumd8faa362007-04-27 19:54:29 +0000766 def test_detect_file(self):
767 self._test_modes(self._testfunc_file)
768
769 def test_detect_fileobj(self):
770 self._test_modes(self._testfunc_fileobj)
771
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300772class GzipDetectReadTest(GzipTest, DetectReadTest):
773 pass
774
775class Bz2DetectReadTest(Bz2Test, DetectReadTest):
Lars Gustäbeled1ac582011-12-06 12:56:38 +0100776 def test_detect_stream_bz2(self):
777 # Originally, tarfile's stream detection looked for the string
778 # "BZh91" at the start of the file. This is incorrect because
Victor Stinner8c663fd2017-11-08 14:44:44 -0800779 # the '9' represents the blocksize (900,000 bytes). If the file was
Lars Gustäbeled1ac582011-12-06 12:56:38 +0100780 # compressed using another blocksize autodetection fails.
Lars Gustäbeled1ac582011-12-06 12:56:38 +0100781 with open(tarname, "rb") as fobj:
782 data = fobj.read()
783
Victor Stinner8c663fd2017-11-08 14:44:44 -0800784 # Compress with blocksize 100,000 bytes, the file starts with "BZh11".
Lars Gustäbeled1ac582011-12-06 12:56:38 +0100785 with bz2.BZ2File(tmpname, "wb", compresslevel=1) as fobj:
786 fobj.write(data)
787
788 self._testfunc_file(tmpname, "r|*")
789
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300790class LzmaDetectReadTest(LzmaTest, DetectReadTest):
791 pass
Guido van Rossumd8faa362007-04-27 19:54:29 +0000792
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300793
794class MemberReadTest(ReadTest, unittest.TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000795
796 def _test_member(self, tarinfo, chksum=None, **kwargs):
797 if chksum is not None:
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300798 with self.tar.extractfile(tarinfo) as f:
799 self.assertEqual(md5sum(f.read()), chksum,
800 "wrong md5sum for %s" % tarinfo.name)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000801
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000802 kwargs["mtime"] = 0o7606136617
Guido van Rossumd8faa362007-04-27 19:54:29 +0000803 kwargs["uid"] = 1000
804 kwargs["gid"] = 100
805 if "old-v7" not in tarinfo.name:
806 # V7 tar can't handle alphabetic owners.
807 kwargs["uname"] = "tarfile"
808 kwargs["gname"] = "tarfile"
809 for k, v in kwargs.items():
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300810 self.assertEqual(getattr(tarinfo, k), v,
Guido van Rossumd8faa362007-04-27 19:54:29 +0000811 "wrong value in %s field of %s" % (k, tarinfo.name))
812
813 def test_find_regtype(self):
814 tarinfo = self.tar.getmember("ustar/regtype")
815 self._test_member(tarinfo, size=7011, chksum=md5_regtype)
816
817 def test_find_conttype(self):
818 tarinfo = self.tar.getmember("ustar/conttype")
819 self._test_member(tarinfo, size=7011, chksum=md5_regtype)
820
821 def test_find_dirtype(self):
822 tarinfo = self.tar.getmember("ustar/dirtype")
823 self._test_member(tarinfo, size=0)
824
825 def test_find_dirtype_with_size(self):
826 tarinfo = self.tar.getmember("ustar/dirtype-with-size")
827 self._test_member(tarinfo, size=255)
828
829 def test_find_lnktype(self):
830 tarinfo = self.tar.getmember("ustar/lnktype")
831 self._test_member(tarinfo, size=0, linkname="ustar/regtype")
832
833 def test_find_symtype(self):
834 tarinfo = self.tar.getmember("ustar/symtype")
835 self._test_member(tarinfo, size=0, linkname="regtype")
836
837 def test_find_blktype(self):
838 tarinfo = self.tar.getmember("ustar/blktype")
839 self._test_member(tarinfo, size=0, devmajor=3, devminor=0)
840
841 def test_find_chrtype(self):
842 tarinfo = self.tar.getmember("ustar/chrtype")
843 self._test_member(tarinfo, size=0, devmajor=1, devminor=3)
844
845 def test_find_fifotype(self):
846 tarinfo = self.tar.getmember("ustar/fifotype")
847 self._test_member(tarinfo, size=0)
848
849 def test_find_sparse(self):
850 tarinfo = self.tar.getmember("ustar/sparse")
851 self._test_member(tarinfo, size=86016, chksum=md5_sparse)
852
Lars Gustäbel9cbdd752010-10-29 09:08:19 +0000853 def test_find_gnusparse(self):
854 tarinfo = self.tar.getmember("gnu/sparse")
855 self._test_member(tarinfo, size=86016, chksum=md5_sparse)
856
857 def test_find_gnusparse_00(self):
858 tarinfo = self.tar.getmember("gnu/sparse-0.0")
859 self._test_member(tarinfo, size=86016, chksum=md5_sparse)
860
861 def test_find_gnusparse_01(self):
862 tarinfo = self.tar.getmember("gnu/sparse-0.1")
863 self._test_member(tarinfo, size=86016, chksum=md5_sparse)
864
865 def test_find_gnusparse_10(self):
866 tarinfo = self.tar.getmember("gnu/sparse-1.0")
867 self._test_member(tarinfo, size=86016, chksum=md5_sparse)
868
Guido van Rossumd8faa362007-04-27 19:54:29 +0000869 def test_find_umlauts(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300870 tarinfo = self.tar.getmember("ustar/umlauts-"
871 "\xc4\xd6\xdc\xe4\xf6\xfc\xdf")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000872 self._test_member(tarinfo, size=7011, chksum=md5_regtype)
873
874 def test_find_ustar_longname(self):
875 name = "ustar/" + "12345/" * 39 + "1234567/longname"
Benjamin Peterson577473f2010-01-19 00:09:57 +0000876 self.assertIn(name, self.tar.getnames())
Guido van Rossumd8faa362007-04-27 19:54:29 +0000877
878 def test_find_regtype_oldv7(self):
879 tarinfo = self.tar.getmember("misc/regtype-old-v7")
880 self._test_member(tarinfo, size=7011, chksum=md5_regtype)
881
882 def test_find_pax_umlauts(self):
Antoine Pitrouab58b5f2010-09-23 19:39:35 +0000883 self.tar.close()
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300884 self.tar = tarfile.open(self.tarname, mode=self.mode,
885 encoding="iso8859-1")
886 tarinfo = self.tar.getmember("pax/umlauts-"
887 "\xc4\xd6\xdc\xe4\xf6\xfc\xdf")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000888 self._test_member(tarinfo, size=7011, chksum=md5_regtype)
889
890
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300891class LongnameTest:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000892
893 def test_read_longname(self):
894 # Test reading of longname (bug #1471427).
Guido van Rossume7ba4952007-06-06 23:52:48 +0000895 longname = self.subdir + "/" + "123/" * 125 + "longname"
Guido van Rossumd8faa362007-04-27 19:54:29 +0000896 try:
Guido van Rossume7ba4952007-06-06 23:52:48 +0000897 tarinfo = self.tar.getmember(longname)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000898 except KeyError:
899 self.fail("longname not found")
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300900 self.assertNotEqual(tarinfo.type, tarfile.DIRTYPE,
901 "read longname as dirtype")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000902
903 def test_read_longlink(self):
904 longname = self.subdir + "/" + "123/" * 125 + "longname"
905 longlink = self.subdir + "/" + "123/" * 125 + "longlink"
906 try:
907 tarinfo = self.tar.getmember(longlink)
908 except KeyError:
909 self.fail("longlink not found")
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300910 self.assertEqual(tarinfo.linkname, longname, "linkname wrong")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000911
912 def test_truncated_longname(self):
913 longname = self.subdir + "/" + "123/" * 125 + "longname"
914 tarinfo = self.tar.getmember(longname)
915 offset = tarinfo.offset
916 self.tar.fileobj.seek(offset)
Lars Gustäbelb506dc32007-08-07 18:36:16 +0000917 fobj = io.BytesIO(self.tar.fileobj.read(3 * 512))
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300918 with self.assertRaises(tarfile.ReadError):
919 tarfile.open(name="foo.tar", fileobj=fobj)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000920
Guido van Rossume7ba4952007-06-06 23:52:48 +0000921 def test_header_offset(self):
922 # Test if the start offset of the TarInfo object includes
923 # the preceding extended header.
924 longname = self.subdir + "/" + "123/" * 125 + "longname"
925 offset = self.tar.getmember(longname).offset
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000926 with open(tarname, "rb") as fobj:
927 fobj.seek(offset)
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300928 tarinfo = tarfile.TarInfo.frombuf(fobj.read(512),
929 "iso8859-1", "strict")
Antoine Pitroue1eca4e2010-10-29 23:49:49 +0000930 self.assertEqual(tarinfo.type, self.longnametype)
Guido van Rossume7ba4952007-06-06 23:52:48 +0000931
Guido van Rossumd8faa362007-04-27 19:54:29 +0000932
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300933class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000934
935 subdir = "gnu"
Guido van Rossume7ba4952007-06-06 23:52:48 +0000936 longnametype = tarfile.GNUTYPE_LONGNAME
Guido van Rossumd8faa362007-04-27 19:54:29 +0000937
Lars Gustäbel9cbdd752010-10-29 09:08:19 +0000938 # Since 3.2 tarfile is supposed to accurately restore sparse members and
939 # produce files with holes. This is what we actually want to test here.
940 # Unfortunately, not all platforms/filesystems support sparse files, and
941 # even on platforms that do it is non-trivial to make reliable assertions
942 # about holes in files. Therefore, we first do one basic test which works
943 # an all platforms, and after that a test that will work only on
944 # platforms/filesystems that prove to support sparse files.
945 def _test_sparse_file(self, name):
946 self.tar.extract(name, TEMPDIR)
947 filename = os.path.join(TEMPDIR, name)
948 with open(filename, "rb") as fobj:
949 data = fobj.read()
950 self.assertEqual(md5sum(data), md5_sparse,
951 "wrong md5sum for %s" % name)
952
953 if self._fs_supports_holes():
954 s = os.stat(filename)
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300955 self.assertLess(s.st_blocks * 512, s.st_size)
Lars Gustäbel9cbdd752010-10-29 09:08:19 +0000956
957 def test_sparse_file_old(self):
958 self._test_sparse_file("gnu/sparse")
959
960 def test_sparse_file_00(self):
961 self._test_sparse_file("gnu/sparse-0.0")
962
963 def test_sparse_file_01(self):
964 self._test_sparse_file("gnu/sparse-0.1")
965
966 def test_sparse_file_10(self):
967 self._test_sparse_file("gnu/sparse-1.0")
968
969 @staticmethod
970 def _fs_supports_holes():
971 # Return True if the platform knows the st_blocks stat attribute and
972 # uses st_blocks units of 512 bytes, and if the filesystem is able to
Victor Stinnerb2385452019-01-21 10:24:12 +0100973 # store holes of 4 KiB in files.
974 #
975 # The function returns False if page size is larger than 4 KiB.
976 # For example, ppc64 uses pages of 64 KiB.
Victor Stinner9c3de4a2011-08-17 20:49:41 +0200977 if sys.platform.startswith("linux"):
Lars Gustäbel9cbdd752010-10-29 09:08:19 +0000978 # Linux evidentially has 512 byte st_blocks units.
979 name = os.path.join(TEMPDIR, "sparse-test")
980 with open(name, "wb") as fobj:
Victor Stinnerb2385452019-01-21 10:24:12 +0100981 # Seek to "punch a hole" of 4 KiB
Lars Gustäbel9cbdd752010-10-29 09:08:19 +0000982 fobj.seek(4096)
Victor Stinnerb2385452019-01-21 10:24:12 +0100983 fobj.write(b'x' * 4096)
Lars Gustäbel9cbdd752010-10-29 09:08:19 +0000984 fobj.truncate()
985 s = os.stat(name)
Tim Goldene0bd2c52014-05-06 13:24:26 +0100986 support.unlink(name)
Victor Stinnerb2385452019-01-21 10:24:12 +0100987 return (s.st_blocks * 512 < s.st_size)
Lars Gustäbel9cbdd752010-10-29 09:08:19 +0000988 else:
989 return False
Guido van Rossumd8faa362007-04-27 19:54:29 +0000990
991
Serhiy Storchaka8b562922013-06-17 15:38:50 +0300992class PaxReadTest(LongnameTest, ReadTest, unittest.TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000993
994 subdir = "pax"
Guido van Rossume7ba4952007-06-06 23:52:48 +0000995 longnametype = tarfile.XHDTYPE
Guido van Rossumd8faa362007-04-27 19:54:29 +0000996
Guido van Rossume7ba4952007-06-06 23:52:48 +0000997 def test_pax_global_headers(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000998 tar = tarfile.open(tarname, encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +0000999 try:
1000 tarinfo = tar.getmember("pax/regtype1")
1001 self.assertEqual(tarinfo.uname, "foo")
1002 self.assertEqual(tarinfo.gname, "bar")
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001003 self.assertEqual(tarinfo.pax_headers.get("VENDOR.umlauts"),
1004 "\xc4\xd6\xdc\xe4\xf6\xfc\xdf")
Guido van Rossume7ba4952007-06-06 23:52:48 +00001005
Antoine Pitrou95f55602010-09-23 18:36:46 +00001006 tarinfo = tar.getmember("pax/regtype2")
1007 self.assertEqual(tarinfo.uname, "")
1008 self.assertEqual(tarinfo.gname, "bar")
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001009 self.assertEqual(tarinfo.pax_headers.get("VENDOR.umlauts"),
1010 "\xc4\xd6\xdc\xe4\xf6\xfc\xdf")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001011
Antoine Pitrou95f55602010-09-23 18:36:46 +00001012 tarinfo = tar.getmember("pax/regtype3")
1013 self.assertEqual(tarinfo.uname, "tarfile")
1014 self.assertEqual(tarinfo.gname, "tarfile")
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001015 self.assertEqual(tarinfo.pax_headers.get("VENDOR.umlauts"),
1016 "\xc4\xd6\xdc\xe4\xf6\xfc\xdf")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001017 finally:
1018 tar.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001019
1020 def test_pax_number_fields(self):
1021 # All following number fields are read from the pax header.
1022 tar = tarfile.open(tarname, encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001023 try:
1024 tarinfo = tar.getmember("pax/regtype4")
1025 self.assertEqual(tarinfo.size, 7011)
1026 self.assertEqual(tarinfo.uid, 123)
1027 self.assertEqual(tarinfo.gid, 123)
1028 self.assertEqual(tarinfo.mtime, 1041808783.0)
1029 self.assertEqual(type(tarinfo.mtime), float)
1030 self.assertEqual(float(tarinfo.pax_headers["atime"]), 1041808783.0)
1031 self.assertEqual(float(tarinfo.pax_headers["ctime"]), 1041808783.0)
1032 finally:
1033 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001034
1035
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001036class WriteTestBase(TarTest):
Georg Brandlf08a9dd2008-06-10 16:57:31 +00001037 # Put all write tests in here that are supposed to be tested
1038 # in all possible mode combinations.
1039
1040 def test_fileobj_no_close(self):
1041 fobj = io.BytesIO()
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +02001042 with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
1043 tar.addfile(tarfile.TarInfo("foo"))
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001044 self.assertFalse(fobj.closed, "external fileobjs must never closed")
Serhiy Storchaka9fbec7a2014-01-18 15:53:05 +02001045 # Issue #20238: Incomplete gzip output with mode="w:gz"
1046 data = fobj.getvalue()
1047 del tar
1048 support.gc_collect()
1049 self.assertFalse(fobj.closed)
1050 self.assertEqual(data, fobj.getvalue())
Georg Brandlf08a9dd2008-06-10 16:57:31 +00001051
Lars Gustäbel20703c62015-05-27 12:53:44 +02001052 def test_eof_marker(self):
1053 # Make sure an end of archive marker is written (two zero blocks).
1054 # tarfile insists on aligning archives to a 20 * 512 byte recordsize.
1055 # So, we create an archive that has exactly 10240 bytes without the
1056 # marker, and has 20480 bytes once the marker is written.
1057 with tarfile.open(tmpname, self.mode) as tar:
1058 t = tarfile.TarInfo("foo")
1059 t.size = tarfile.RECORDSIZE - tarfile.BLOCKSIZE
1060 tar.addfile(t, io.BytesIO(b"a" * t.size))
1061
1062 with self.open(tmpname, "rb") as fobj:
1063 self.assertEqual(len(fobj.read()), tarfile.RECORDSIZE * 2)
1064
Georg Brandlf08a9dd2008-06-10 16:57:31 +00001065
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001066class WriteTest(WriteTestBase, unittest.TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001067
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001068 prefix = "w:"
Guido van Rossumd8faa362007-04-27 19:54:29 +00001069
1070 def test_100_char_name(self):
1071 # The name field in a tar header stores strings of at most 100 chars.
1072 # If a string is shorter than 100 chars it has to be padded with '\0',
1073 # which implies that a string of exactly 100 chars is stored without
1074 # a trailing '\0'.
1075 name = "0123456789" * 10
1076 tar = tarfile.open(tmpname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001077 try:
1078 t = tarfile.TarInfo(name)
1079 tar.addfile(t)
1080 finally:
1081 tar.close()
Thomas Wouterscf297e42007-02-23 15:07:44 +00001082
Guido van Rossumd8faa362007-04-27 19:54:29 +00001083 tar = tarfile.open(tmpname)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001084 try:
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001085 self.assertEqual(tar.getnames()[0], name,
Antoine Pitrou95f55602010-09-23 18:36:46 +00001086 "failed to store 100 char filename")
1087 finally:
1088 tar.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +00001089
Guido van Rossumd8faa362007-04-27 19:54:29 +00001090 def test_tar_size(self):
1091 # Test for bug #1013882.
1092 tar = tarfile.open(tmpname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001093 try:
1094 path = os.path.join(TEMPDIR, "file")
1095 with open(path, "wb") as fobj:
1096 fobj.write(b"aaa")
1097 tar.add(path)
1098 finally:
1099 tar.close()
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001100 self.assertGreater(os.path.getsize(tmpname), 0,
Guido van Rossumd8faa362007-04-27 19:54:29 +00001101 "tarfile is empty")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001102
Guido van Rossumd8faa362007-04-27 19:54:29 +00001103 # The test_*_size tests test for bug #1167128.
1104 def test_file_size(self):
1105 tar = tarfile.open(tmpname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001106 try:
1107 path = os.path.join(TEMPDIR, "file")
1108 with open(path, "wb"):
1109 pass
1110 tarinfo = tar.gettarinfo(path)
1111 self.assertEqual(tarinfo.size, 0)
Martin v. Löwis5dbdc592005-08-27 10:07:56 +00001112
Antoine Pitrou95f55602010-09-23 18:36:46 +00001113 with open(path, "wb") as fobj:
1114 fobj.write(b"aaa")
1115 tarinfo = tar.gettarinfo(path)
1116 self.assertEqual(tarinfo.size, 3)
1117 finally:
1118 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001119
1120 def test_directory_size(self):
1121 path = os.path.join(TEMPDIR, "directory")
Martin v. Löwis5dbdc592005-08-27 10:07:56 +00001122 os.mkdir(path)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001123 try:
1124 tar = tarfile.open(tmpname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001125 try:
1126 tarinfo = tar.gettarinfo(path)
1127 self.assertEqual(tarinfo.size, 0)
1128 finally:
1129 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001130 finally:
Tim Goldene0bd2c52014-05-06 13:24:26 +01001131 support.rmdir(path)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001132
Bernhard M. Wiedemann84521042018-01-31 11:17:10 +01001133 # mock the following:
1134 # os.listdir: so we know that files are in the wrong order
Bernhard M. Wiedemann4ad703b2018-02-06 19:08:53 +01001135 def test_ordered_recursion(self):
Bernhard M. Wiedemann84521042018-01-31 11:17:10 +01001136 path = os.path.join(TEMPDIR, "directory")
1137 os.mkdir(path)
1138 open(os.path.join(path, "1"), "a").close()
1139 open(os.path.join(path, "2"), "a").close()
Bernhard M. Wiedemann84521042018-01-31 11:17:10 +01001140 try:
1141 tar = tarfile.open(tmpname, self.mode)
1142 try:
Bernhard M. Wiedemann4ad703b2018-02-06 19:08:53 +01001143 with unittest.mock.patch('os.listdir') as mock_listdir:
1144 mock_listdir.return_value = ["2", "1"]
1145 tar.add(path)
Bernhard M. Wiedemann84521042018-01-31 11:17:10 +01001146 paths = []
1147 for m in tar.getmembers():
1148 paths.append(os.path.split(m.name)[-1])
1149 self.assertEqual(paths, ["directory", "1", "2"]);
1150 finally:
1151 tar.close()
1152 finally:
1153 support.unlink(os.path.join(path, "1"))
1154 support.unlink(os.path.join(path, "2"))
1155 support.rmdir(path)
1156
Serhiy Storchakac45cd162017-03-08 10:32:44 +02001157 def test_gettarinfo_pathlike_name(self):
1158 with tarfile.open(tmpname, self.mode) as tar:
1159 path = pathlib.Path(TEMPDIR) / "file"
1160 with open(path, "wb") as fobj:
1161 fobj.write(b"aaa")
1162 tarinfo = tar.gettarinfo(path)
1163 tarinfo2 = tar.gettarinfo(os.fspath(path))
1164 self.assertIsInstance(tarinfo.name, str)
1165 self.assertEqual(tarinfo.name, tarinfo2.name)
1166 self.assertEqual(tarinfo.size, 3)
1167
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001168 @unittest.skipUnless(hasattr(os, "link"),
1169 "Missing hardlink implementation")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001170 def test_link_size(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001171 link = os.path.join(TEMPDIR, "link")
1172 target = os.path.join(TEMPDIR, "link_target")
1173 with open(target, "wb") as fobj:
1174 fobj.write(b"aaa")
xdegayed7d4fea2017-11-12 18:02:06 +01001175 try:
1176 os.link(target, link)
1177 except PermissionError as e:
1178 self.skipTest('os.link(): %s' % e)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001179 try:
1180 tar = tarfile.open(tmpname, self.mode)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001181 try:
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001182 # Record the link target in the inodes list.
1183 tar.gettarinfo(target)
1184 tarinfo = tar.gettarinfo(link)
1185 self.assertEqual(tarinfo.size, 0)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001186 finally:
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001187 tar.close()
1188 finally:
Tim Goldene0bd2c52014-05-06 13:24:26 +01001189 support.unlink(target)
1190 support.unlink(link)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001191
Brian Curtin3b4499c2010-12-28 14:31:47 +00001192 @support.skip_unless_symlink
Guido van Rossumd8faa362007-04-27 19:54:29 +00001193 def test_symlink_size(self):
Brian Curtind40e6f72010-07-08 21:39:08 +00001194 path = os.path.join(TEMPDIR, "symlink")
1195 os.symlink("link_target", path)
1196 try:
1197 tar = tarfile.open(tmpname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001198 try:
1199 tarinfo = tar.gettarinfo(path)
1200 self.assertEqual(tarinfo.size, 0)
1201 finally:
1202 tar.close()
Brian Curtind40e6f72010-07-08 21:39:08 +00001203 finally:
Tim Goldene0bd2c52014-05-06 13:24:26 +01001204 support.unlink(path)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001205
1206 def test_add_self(self):
1207 # Test for #1257255.
1208 dstname = os.path.abspath(tmpname)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001209 tar = tarfile.open(tmpname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001210 try:
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001211 self.assertEqual(tar.name, dstname,
1212 "archive name must be absolute")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001213 tar.add(dstname)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001214 self.assertEqual(tar.getnames(), [],
1215 "added the archive to itself")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001216
Serhiy Storchaka2a23adf2015-09-06 14:13:25 +03001217 with support.change_cwd(TEMPDIR):
1218 tar.add(dstname)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001219 self.assertEqual(tar.getnames(), [],
1220 "added the archive to itself")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001221 finally:
1222 tar.close()
Martin v. Löwis5dbdc592005-08-27 10:07:56 +00001223
Lars Gustäbel049d2aa2009-09-12 10:44:00 +00001224 def test_filter(self):
1225 tempdir = os.path.join(TEMPDIR, "filter")
1226 os.mkdir(tempdir)
1227 try:
1228 for name in ("foo", "bar", "baz"):
1229 name = os.path.join(tempdir, name)
Victor Stinnerbf816222011-06-30 23:25:47 +02001230 support.create_empty_file(name)
Lars Gustäbel049d2aa2009-09-12 10:44:00 +00001231
1232 def filter(tarinfo):
1233 if os.path.basename(tarinfo.name) == "bar":
1234 return
1235 tarinfo.uid = 123
1236 tarinfo.uname = "foo"
1237 return tarinfo
1238
1239 tar = tarfile.open(tmpname, self.mode, encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001240 try:
1241 tar.add(tempdir, arcname="empty_dir", filter=filter)
1242 finally:
1243 tar.close()
Lars Gustäbel049d2aa2009-09-12 10:44:00 +00001244
Raymond Hettingera63a3122011-01-26 20:34:14 +00001245 # Verify that filter is a keyword-only argument
1246 with self.assertRaises(TypeError):
1247 tar.add(tempdir, "empty_dir", True, None, filter)
1248
Lars Gustäbel049d2aa2009-09-12 10:44:00 +00001249 tar = tarfile.open(tmpname, "r")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001250 try:
1251 for tarinfo in tar:
1252 self.assertEqual(tarinfo.uid, 123)
1253 self.assertEqual(tarinfo.uname, "foo")
1254 self.assertEqual(len(tar.getmembers()), 3)
1255 finally:
1256 tar.close()
Lars Gustäbel049d2aa2009-09-12 10:44:00 +00001257 finally:
Tim Goldene0bd2c52014-05-06 13:24:26 +01001258 support.rmtree(tempdir)
Lars Gustäbel049d2aa2009-09-12 10:44:00 +00001259
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001260 # Guarantee that stored pathnames are not modified. Don't
1261 # remove ./ or ../ or double slashes. Still make absolute
1262 # pathnames relative.
1263 # For details see bug #6054.
1264 def _test_pathname(self, path, cmp_path=None, dir=False):
1265 # Create a tarfile with an empty member named path
1266 # and compare the stored name with the original.
1267 foo = os.path.join(TEMPDIR, "foo")
1268 if not dir:
Victor Stinnerbf816222011-06-30 23:25:47 +02001269 support.create_empty_file(foo)
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001270 else:
1271 os.mkdir(foo)
1272
1273 tar = tarfile.open(tmpname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001274 try:
1275 tar.add(foo, arcname=path)
1276 finally:
1277 tar.close()
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001278
1279 tar = tarfile.open(tmpname, "r")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001280 try:
1281 t = tar.next()
1282 finally:
1283 tar.close()
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001284
1285 if not dir:
Tim Goldene0bd2c52014-05-06 13:24:26 +01001286 support.unlink(foo)
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001287 else:
Tim Goldene0bd2c52014-05-06 13:24:26 +01001288 support.rmdir(foo)
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001289
1290 self.assertEqual(t.name, cmp_path or path.replace(os.sep, "/"))
1291
Senthil Kumaranbe5dbeb2011-04-30 06:09:51 +08001292
1293 @support.skip_unless_symlink
Senthil Kumaran123932f2011-04-28 15:38:12 +08001294 def test_extractall_symlinks(self):
1295 # Test if extractall works properly when tarfile contains symlinks
1296 tempdir = os.path.join(TEMPDIR, "testsymlinks")
1297 temparchive = os.path.join(TEMPDIR, "testsymlinks.tar")
1298 os.mkdir(tempdir)
1299 try:
1300 source_file = os.path.join(tempdir,'source')
1301 target_file = os.path.join(tempdir,'symlink')
1302 with open(source_file,'w') as f:
1303 f.write('something\n')
1304 os.symlink(source_file, target_file)
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +02001305 with tarfile.open(temparchive, 'w') as tar:
1306 tar.add(source_file)
1307 tar.add(target_file)
Senthil Kumaran123932f2011-04-28 15:38:12 +08001308 # Let's extract it to the location which contains the symlink
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +02001309 with tarfile.open(temparchive) as tar:
1310 # this should not raise OSError: [Errno 17] File exists
1311 try:
1312 tar.extractall(path=tempdir)
1313 except OSError:
1314 self.fail("extractall failed with symlinked files")
Senthil Kumaran123932f2011-04-28 15:38:12 +08001315 finally:
Tim Goldene0bd2c52014-05-06 13:24:26 +01001316 support.unlink(temparchive)
1317 support.rmtree(tempdir)
Martin v. Löwis5dbdc592005-08-27 10:07:56 +00001318
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001319 def test_pathnames(self):
1320 self._test_pathname("foo")
1321 self._test_pathname(os.path.join("foo", ".", "bar"))
1322 self._test_pathname(os.path.join("foo", "..", "bar"))
1323 self._test_pathname(os.path.join(".", "foo"))
1324 self._test_pathname(os.path.join(".", "foo", "."))
1325 self._test_pathname(os.path.join(".", "foo", ".", "bar"))
1326 self._test_pathname(os.path.join(".", "foo", "..", "bar"))
1327 self._test_pathname(os.path.join(".", "foo", "..", "bar"))
1328 self._test_pathname(os.path.join("..", "foo"))
1329 self._test_pathname(os.path.join("..", "foo", ".."))
1330 self._test_pathname(os.path.join("..", "foo", ".", "bar"))
1331 self._test_pathname(os.path.join("..", "foo", "..", "bar"))
1332
1333 self._test_pathname("foo" + os.sep + os.sep + "bar")
1334 self._test_pathname("foo" + os.sep + os.sep, "foo", dir=True)
1335
1336 def test_abs_pathnames(self):
1337 if sys.platform == "win32":
1338 self._test_pathname("C:\\foo", "foo")
1339 else:
1340 self._test_pathname("/foo", "foo")
1341 self._test_pathname("///foo", "foo")
1342
1343 def test_cwd(self):
1344 # Test adding the current working directory.
Serhiy Storchaka2a23adf2015-09-06 14:13:25 +03001345 with support.change_cwd(TEMPDIR):
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001346 tar = tarfile.open(tmpname, self.mode)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001347 try:
1348 tar.add(".")
1349 finally:
1350 tar.close()
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001351
1352 tar = tarfile.open(tmpname, "r")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001353 try:
1354 for t in tar:
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001355 if t.name != ".":
1356 self.assertTrue(t.name.startswith("./"), t.name)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001357 finally:
1358 tar.close()
Lars Gustäbelbfdfdda2009-08-28 19:59:59 +00001359
Serhiy Storchakac2d01422014-01-18 16:14:10 +02001360 def test_open_nonwritable_fileobj(self):
1361 for exctype in OSError, EOFError, RuntimeError:
1362 class BadFile(io.BytesIO):
1363 first = True
1364 def write(self, data):
1365 if self.first:
1366 self.first = False
1367 raise exctype
1368
1369 f = BadFile()
1370 with self.assertRaises(exctype):
1371 tar = tarfile.open(tmpname, self.mode, fileobj=f,
1372 format=tarfile.PAX_FORMAT,
1373 pax_headers={'non': 'empty'})
1374 self.assertFalse(f.closed)
1375
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001376class GzipWriteTest(GzipTest, WriteTest):
1377 pass
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00001378
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001379class Bz2WriteTest(Bz2Test, WriteTest):
1380 pass
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00001381
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001382class LzmaWriteTest(LzmaTest, WriteTest):
1383 pass
1384
1385
1386class StreamWriteTest(WriteTestBase, unittest.TestCase):
1387
1388 prefix = "w|"
1389 decompressor = None
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001390
Guido van Rossumd8faa362007-04-27 19:54:29 +00001391 def test_stream_padding(self):
1392 # Test for bug #1543303.
1393 tar = tarfile.open(tmpname, self.mode)
1394 tar.close()
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001395 if self.decompressor:
1396 dec = self.decompressor()
Antoine Pitrou95f55602010-09-23 18:36:46 +00001397 with open(tmpname, "rb") as fobj:
1398 data = fobj.read()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001399 data = dec.decompress(data)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001400 self.assertFalse(dec.unused_data, "found trailing data")
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001401 else:
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001402 with self.open(tmpname) as fobj:
Antoine Pitrou95f55602010-09-23 18:36:46 +00001403 data = fobj.read()
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001404 self.assertEqual(data.count(b"\0"), tarfile.RECORDSIZE,
1405 "incorrect zero padding")
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001406
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001407 @unittest.skipUnless(sys.platform != "win32" and hasattr(os, "umask"),
1408 "Missing umask implementation")
Lars Gustäbeld6eb70b2010-04-29 15:37:02 +00001409 def test_file_mode(self):
1410 # Test for issue #8464: Create files with correct
1411 # permissions.
Lars Gustäbeld6eb70b2010-04-29 15:37:02 +00001412 if os.path.exists(tmpname):
Tim Goldene0bd2c52014-05-06 13:24:26 +01001413 support.unlink(tmpname)
Lars Gustäbeld6eb70b2010-04-29 15:37:02 +00001414
1415 original_umask = os.umask(0o022)
1416 try:
1417 tar = tarfile.open(tmpname, self.mode)
1418 tar.close()
1419 mode = os.stat(tmpname).st_mode & 0o777
1420 self.assertEqual(mode, 0o644, "wrong file permissions")
1421 finally:
1422 os.umask(original_umask)
1423
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001424class GzipStreamWriteTest(GzipTest, StreamWriteTest):
1425 pass
1426
1427class Bz2StreamWriteTest(Bz2Test, StreamWriteTest):
1428 decompressor = bz2.BZ2Decompressor if bz2 else None
1429
1430class LzmaStreamWriteTest(LzmaTest, StreamWriteTest):
1431 decompressor = lzma.LZMADecompressor if lzma else None
1432
Thomas Wouters00ee7ba2006-08-21 19:07:27 +00001433
Guido van Rossumd8faa362007-04-27 19:54:29 +00001434class GNUWriteTest(unittest.TestCase):
1435 # This testcase checks for correct creation of GNU Longname
1436 # and Longlink extended headers (cp. bug #812325).
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001437
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001438 def _length(self, s):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001439 blocks = len(s) // 512 + 1
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001440 return blocks * 512
1441
1442 def _calc_size(self, name, link=None):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001443 # Initial tar header
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001444 count = 512
1445
1446 if len(name) > tarfile.LENGTH_NAME:
Guido van Rossumd8faa362007-04-27 19:54:29 +00001447 # GNU longname extended header + longname
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001448 count += 512
1449 count += self._length(name)
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001450 if link is not None and len(link) > tarfile.LENGTH_LINK:
Guido van Rossumd8faa362007-04-27 19:54:29 +00001451 # GNU longlink extended header + longlink
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001452 count += 512
1453 count += self._length(link)
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001454 return count
1455
1456 def _test(self, name, link=None):
1457 tarinfo = tarfile.TarInfo(name)
1458 if link:
1459 tarinfo.linkname = link
1460 tarinfo.type = tarfile.LNKTYPE
1461
Guido van Rossumd8faa362007-04-27 19:54:29 +00001462 tar = tarfile.open(tmpname, "w")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001463 try:
1464 tar.format = tarfile.GNU_FORMAT
1465 tar.addfile(tarinfo)
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001466
Antoine Pitrou95f55602010-09-23 18:36:46 +00001467 v1 = self._calc_size(name, link)
1468 v2 = tar.offset
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001469 self.assertEqual(v1, v2, "GNU longname/longlink creation failed")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001470 finally:
1471 tar.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +00001472
Guido van Rossumd8faa362007-04-27 19:54:29 +00001473 tar = tarfile.open(tmpname)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001474 try:
1475 member = tar.next()
1476 self.assertIsNotNone(member,
1477 "unable to read longname member")
1478 self.assertEqual(tarinfo.name, member.name,
1479 "unable to read longname member")
1480 self.assertEqual(tarinfo.linkname, member.linkname,
1481 "unable to read longname member")
1482 finally:
1483 tar.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +00001484
Neal Norwitz0662f8a2004-07-20 21:54:18 +00001485 def test_longname_1023(self):
1486 self._test(("longnam/" * 127) + "longnam")
1487
1488 def test_longname_1024(self):
1489 self._test(("longnam/" * 127) + "longname")
1490
1491 def test_longname_1025(self):
1492 self._test(("longnam/" * 127) + "longname_")
1493
1494 def test_longlink_1023(self):
1495 self._test("name", ("longlnk/" * 127) + "longlnk")
1496
1497 def test_longlink_1024(self):
1498 self._test("name", ("longlnk/" * 127) + "longlink")
1499
1500 def test_longlink_1025(self):
1501 self._test("name", ("longlnk/" * 127) + "longlink_")
1502
1503 def test_longnamelink_1023(self):
1504 self._test(("longnam/" * 127) + "longnam",
1505 ("longlnk/" * 127) + "longlnk")
1506
1507 def test_longnamelink_1024(self):
1508 self._test(("longnam/" * 127) + "longname",
1509 ("longlnk/" * 127) + "longlink")
1510
1511 def test_longnamelink_1025(self):
1512 self._test(("longnam/" * 127) + "longname_",
1513 ("longlnk/" * 127) + "longlink_")
1514
Guido van Rossumd8faa362007-04-27 19:54:29 +00001515
Lars Gustäbel20703c62015-05-27 12:53:44 +02001516class CreateTest(WriteTestBase, unittest.TestCase):
Berker Peksag0fe63252015-02-13 21:02:12 +02001517
1518 prefix = "x:"
1519
1520 file_path = os.path.join(TEMPDIR, "spameggs42")
1521
1522 def setUp(self):
1523 support.unlink(tmpname)
1524
1525 @classmethod
1526 def setUpClass(cls):
1527 with open(cls.file_path, "wb") as fobj:
1528 fobj.write(b"aaa")
1529
1530 @classmethod
1531 def tearDownClass(cls):
1532 support.unlink(cls.file_path)
1533
1534 def test_create(self):
1535 with tarfile.open(tmpname, self.mode) as tobj:
1536 tobj.add(self.file_path)
1537
1538 with self.taropen(tmpname) as tobj:
1539 names = tobj.getnames()
1540 self.assertEqual(len(names), 1)
1541 self.assertIn('spameggs42', names[0])
1542
1543 def test_create_existing(self):
1544 with tarfile.open(tmpname, self.mode) as tobj:
1545 tobj.add(self.file_path)
1546
1547 with self.assertRaises(FileExistsError):
1548 tobj = tarfile.open(tmpname, self.mode)
1549
1550 with self.taropen(tmpname) as tobj:
1551 names = tobj.getnames()
1552 self.assertEqual(len(names), 1)
1553 self.assertIn('spameggs42', names[0])
1554
1555 def test_create_taropen(self):
1556 with self.taropen(tmpname, "x") as tobj:
1557 tobj.add(self.file_path)
1558
1559 with self.taropen(tmpname) as tobj:
1560 names = tobj.getnames()
1561 self.assertEqual(len(names), 1)
1562 self.assertIn('spameggs42', names[0])
1563
1564 def test_create_existing_taropen(self):
1565 with self.taropen(tmpname, "x") as tobj:
1566 tobj.add(self.file_path)
1567
1568 with self.assertRaises(FileExistsError):
1569 with self.taropen(tmpname, "x"):
1570 pass
1571
1572 with self.taropen(tmpname) as tobj:
1573 names = tobj.getnames()
1574 self.assertEqual(len(names), 1)
1575 self.assertIn("spameggs42", names[0])
1576
Serhiy Storchakac45cd162017-03-08 10:32:44 +02001577 def test_create_pathlike_name(self):
1578 with tarfile.open(pathlib.Path(tmpname), self.mode) as tobj:
1579 self.assertIsInstance(tobj.name, str)
1580 self.assertEqual(tobj.name, os.path.abspath(tmpname))
1581 tobj.add(pathlib.Path(self.file_path))
1582 names = tobj.getnames()
1583 self.assertEqual(len(names), 1)
1584 self.assertIn('spameggs42', names[0])
1585
1586 with self.taropen(tmpname) as tobj:
1587 names = tobj.getnames()
1588 self.assertEqual(len(names), 1)
1589 self.assertIn('spameggs42', names[0])
1590
1591 def test_create_taropen_pathlike_name(self):
1592 with self.taropen(pathlib.Path(tmpname), "x") as tobj:
1593 self.assertIsInstance(tobj.name, str)
1594 self.assertEqual(tobj.name, os.path.abspath(tmpname))
1595 tobj.add(pathlib.Path(self.file_path))
1596 names = tobj.getnames()
1597 self.assertEqual(len(names), 1)
1598 self.assertIn('spameggs42', names[0])
1599
1600 with self.taropen(tmpname) as tobj:
1601 names = tobj.getnames()
1602 self.assertEqual(len(names), 1)
1603 self.assertIn('spameggs42', names[0])
1604
Berker Peksag0fe63252015-02-13 21:02:12 +02001605
1606class GzipCreateTest(GzipTest, CreateTest):
1607 pass
1608
1609
1610class Bz2CreateTest(Bz2Test, CreateTest):
1611 pass
1612
1613
1614class LzmaCreateTest(LzmaTest, CreateTest):
1615 pass
1616
1617
1618class CreateWithXModeTest(CreateTest):
1619
1620 prefix = "x"
1621
1622 test_create_taropen = None
1623 test_create_existing_taropen = None
1624
1625
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001626@unittest.skipUnless(hasattr(os, "link"), "Missing hardlink implementation")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001627class HardlinkTest(unittest.TestCase):
1628 # Test the creation of LNKTYPE (hardlink) members in an archive.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001629
1630 def setUp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001631 self.foo = os.path.join(TEMPDIR, "foo")
1632 self.bar = os.path.join(TEMPDIR, "bar")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001633
Antoine Pitrou95f55602010-09-23 18:36:46 +00001634 with open(self.foo, "wb") as fobj:
1635 fobj.write(b"foo")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001636
xdegayed7d4fea2017-11-12 18:02:06 +01001637 try:
1638 os.link(self.foo, self.bar)
1639 except PermissionError as e:
1640 self.skipTest('os.link(): %s' % e)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001641
Guido van Rossumd8faa362007-04-27 19:54:29 +00001642 self.tar = tarfile.open(tmpname, "w")
Neal Norwitzb0e32e22005-10-20 04:50:13 +00001643 self.tar.add(self.foo)
1644
Guido van Rossumd8faa362007-04-27 19:54:29 +00001645 def tearDown(self):
Hirokazu Yamamotoaf079d42008-09-21 11:50:03 +00001646 self.tar.close()
Antoine Pitrou95f55602010-09-23 18:36:46 +00001647 support.unlink(self.foo)
1648 support.unlink(self.bar)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001649
Neal Norwitzb0e32e22005-10-20 04:50:13 +00001650 def test_add_twice(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001651 # The same name will be added as a REGTYPE every
1652 # time regardless of st_nlink.
Neal Norwitzb0e32e22005-10-20 04:50:13 +00001653 tarinfo = self.tar.gettarinfo(self.foo)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001654 self.assertEqual(tarinfo.type, tarfile.REGTYPE,
Neal Norwitzb0e32e22005-10-20 04:50:13 +00001655 "add file as regular failed")
1656
1657 def test_add_hardlink(self):
Neal Norwitzb0e32e22005-10-20 04:50:13 +00001658 tarinfo = self.tar.gettarinfo(self.bar)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001659 self.assertEqual(tarinfo.type, tarfile.LNKTYPE,
Neal Norwitzb0e32e22005-10-20 04:50:13 +00001660 "add file as hardlink failed")
1661
1662 def test_dereference_hardlink(self):
1663 self.tar.dereference = True
Neal Norwitzb0e32e22005-10-20 04:50:13 +00001664 tarinfo = self.tar.gettarinfo(self.bar)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001665 self.assertEqual(tarinfo.type, tarfile.REGTYPE,
Neal Norwitzb0e32e22005-10-20 04:50:13 +00001666 "dereferencing hardlink failed")
1667
Neal Norwitza4f651a2004-07-20 22:07:44 +00001668
Guido van Rossumd8faa362007-04-27 19:54:29 +00001669class PaxWriteTest(GNUWriteTest):
Martin v. Löwis78be7df2005-03-05 12:47:42 +00001670
Guido van Rossumd8faa362007-04-27 19:54:29 +00001671 def _test(self, name, link=None):
1672 # See GNUWriteTest.
1673 tarinfo = tarfile.TarInfo(name)
1674 if link:
1675 tarinfo.linkname = link
1676 tarinfo.type = tarfile.LNKTYPE
Andrew M. Kuchlingd4f25522004-10-20 11:47:01 +00001677
Guido van Rossumd8faa362007-04-27 19:54:29 +00001678 tar = tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001679 try:
1680 tar.addfile(tarinfo)
1681 finally:
1682 tar.close()
Andrew M. Kuchlingd4f25522004-10-20 11:47:01 +00001683
Guido van Rossumd8faa362007-04-27 19:54:29 +00001684 tar = tarfile.open(tmpname)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001685 try:
1686 if link:
1687 l = tar.getmembers()[0].linkname
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001688 self.assertEqual(link, l, "PAX longlink creation failed")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001689 else:
1690 n = tar.getmembers()[0].name
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001691 self.assertEqual(name, n, "PAX longname creation failed")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001692 finally:
1693 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001694
Guido van Rossume7ba4952007-06-06 23:52:48 +00001695 def test_pax_global_header(self):
1696 pax_headers = {
Guido van Rossum9cbfffd2007-06-07 00:54:15 +00001697 "foo": "bar",
1698 "uid": "0",
1699 "mtime": "1.23",
Guido van Rossuma0557702007-08-07 23:19:53 +00001700 "test": "\xe4\xf6\xfc",
1701 "\xe4\xf6\xfc": "test"}
Guido van Rossume7ba4952007-06-06 23:52:48 +00001702
Benjamin Peterson886af962010-03-21 23:13:07 +00001703 tar = tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT,
Guido van Rossume7ba4952007-06-06 23:52:48 +00001704 pax_headers=pax_headers)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001705 try:
1706 tar.addfile(tarfile.TarInfo("test"))
1707 finally:
1708 tar.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001709
1710 # Test if the global header was written correctly.
1711 tar = tarfile.open(tmpname, encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001712 try:
1713 self.assertEqual(tar.pax_headers, pax_headers)
1714 self.assertEqual(tar.getmembers()[0].pax_headers, pax_headers)
1715 # Test if all the fields are strings.
1716 for key, val in tar.pax_headers.items():
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001717 self.assertIsNot(type(key), bytes)
1718 self.assertIsNot(type(val), bytes)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001719 if key in tarfile.PAX_NUMBER_FIELDS:
1720 try:
1721 tarfile.PAX_NUMBER_FIELDS[key](val)
1722 except (TypeError, ValueError):
1723 self.fail("unable to convert pax header field")
1724 finally:
1725 tar.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001726
1727 def test_pax_extended_header(self):
1728 # The fields from the pax header have priority over the
1729 # TarInfo.
Guido van Rossum9cbfffd2007-06-07 00:54:15 +00001730 pax_headers = {"path": "foo", "uid": "123"}
Guido van Rossume7ba4952007-06-06 23:52:48 +00001731
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001732 tar = tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT,
1733 encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001734 try:
1735 t = tarfile.TarInfo()
1736 t.name = "\xe4\xf6\xfc" # non-ASCII
1737 t.uid = 8**8 # too large
1738 t.pax_headers = pax_headers
1739 tar.addfile(t)
1740 finally:
1741 tar.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001742
1743 tar = tarfile.open(tmpname, encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001744 try:
1745 t = tar.getmembers()[0]
1746 self.assertEqual(t.pax_headers, pax_headers)
1747 self.assertEqual(t.name, "foo")
1748 self.assertEqual(t.uid, 123)
1749 finally:
1750 tar.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001751
1752
Lars Gustäbel0f450ab2016-04-19 08:43:17 +02001753class UnicodeTest:
Guido van Rossume7ba4952007-06-06 23:52:48 +00001754
1755 def test_iso8859_1_filename(self):
1756 self._test_unicode_filename("iso8859-1")
1757
1758 def test_utf7_filename(self):
1759 self._test_unicode_filename("utf7")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001760
1761 def test_utf8_filename(self):
Marc-André Lemburg8f36af72011-02-25 15:42:01 +00001762 self._test_unicode_filename("utf-8")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001763
Guido van Rossumd8faa362007-04-27 19:54:29 +00001764 def _test_unicode_filename(self, encoding):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001765 tar = tarfile.open(tmpname, "w", format=self.format,
1766 encoding=encoding, errors="strict")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001767 try:
1768 name = "\xe4\xf6\xfc"
1769 tar.addfile(tarfile.TarInfo(name))
1770 finally:
1771 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001772
1773 tar = tarfile.open(tmpname, encoding=encoding)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001774 try:
1775 self.assertEqual(tar.getmembers()[0].name, name)
1776 finally:
1777 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001778
1779 def test_unicode_filename_error(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001780 tar = tarfile.open(tmpname, "w", format=self.format,
1781 encoding="ascii", errors="strict")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001782 try:
1783 tarinfo = tarfile.TarInfo()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001784
Antoine Pitrou95f55602010-09-23 18:36:46 +00001785 tarinfo.name = "\xe4\xf6\xfc"
1786 self.assertRaises(UnicodeError, tar.addfile, tarinfo)
Guido van Rossume7ba4952007-06-06 23:52:48 +00001787
Antoine Pitrou95f55602010-09-23 18:36:46 +00001788 tarinfo.name = "foo"
1789 tarinfo.uname = "\xe4\xf6\xfc"
1790 self.assertRaises(UnicodeError, tar.addfile, tarinfo)
1791 finally:
1792 tar.close()
Guido van Rossume7ba4952007-06-06 23:52:48 +00001793
1794 def test_unicode_argument(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001795 tar = tarfile.open(tarname, "r",
1796 encoding="iso8859-1", errors="strict")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001797 try:
1798 for t in tar:
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001799 self.assertIs(type(t.name), str)
1800 self.assertIs(type(t.linkname), str)
1801 self.assertIs(type(t.uname), str)
1802 self.assertIs(type(t.gname), str)
Antoine Pitrou95f55602010-09-23 18:36:46 +00001803 finally:
1804 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001805
Guido van Rossume7ba4952007-06-06 23:52:48 +00001806 def test_uname_unicode(self):
Lars Gustäbel3741eff2007-08-21 12:17:05 +00001807 t = tarfile.TarInfo("foo")
1808 t.uname = "\xe4\xf6\xfc"
1809 t.gname = "\xe4\xf6\xfc"
Guido van Rossumd8faa362007-04-27 19:54:29 +00001810
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001811 tar = tarfile.open(tmpname, mode="w", format=self.format,
1812 encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001813 try:
1814 tar.addfile(t)
1815 finally:
1816 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001817
Lars Gustäbel3741eff2007-08-21 12:17:05 +00001818 tar = tarfile.open(tmpname, encoding="iso8859-1")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001819 try:
Guido van Rossume7ba4952007-06-06 23:52:48 +00001820 t = tar.getmember("foo")
Antoine Pitrou95f55602010-09-23 18:36:46 +00001821 self.assertEqual(t.uname, "\xe4\xf6\xfc")
1822 self.assertEqual(t.gname, "\xe4\xf6\xfc")
1823
1824 if self.format != tarfile.PAX_FORMAT:
Antoine Pitrouab58b5f2010-09-23 19:39:35 +00001825 tar.close()
Antoine Pitrou95f55602010-09-23 18:36:46 +00001826 tar = tarfile.open(tmpname, encoding="ascii")
1827 t = tar.getmember("foo")
1828 self.assertEqual(t.uname, "\udce4\udcf6\udcfc")
1829 self.assertEqual(t.gname, "\udce4\udcf6\udcfc")
1830 finally:
1831 tar.close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001832
Lars Gustäbelb506dc32007-08-07 18:36:16 +00001833
Lars Gustäbel0f450ab2016-04-19 08:43:17 +02001834class UstarUnicodeTest(UnicodeTest, unittest.TestCase):
1835
1836 format = tarfile.USTAR_FORMAT
1837
1838 # Test whether the utf-8 encoded version of a filename exceeds the 100
1839 # bytes name field limit (every occurrence of '\xff' will be expanded to 2
1840 # bytes).
1841 def test_unicode_name1(self):
1842 self._test_ustar_name("0123456789" * 10)
1843 self._test_ustar_name("0123456789" * 10 + "0", ValueError)
1844 self._test_ustar_name("0123456789" * 9 + "01234567\xff")
1845 self._test_ustar_name("0123456789" * 9 + "012345678\xff", ValueError)
1846
1847 def test_unicode_name2(self):
1848 self._test_ustar_name("0123456789" * 9 + "012345\xff\xff")
1849 self._test_ustar_name("0123456789" * 9 + "0123456\xff\xff", ValueError)
1850
1851 # Test whether the utf-8 encoded version of a filename exceeds the 155
1852 # bytes prefix + '/' + 100 bytes name limit.
1853 def test_unicode_longname1(self):
1854 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 10)
1855 self._test_ustar_name("0123456789" * 15 + "0123/4" + "0123456789" * 10, ValueError)
1856 self._test_ustar_name("0123456789" * 15 + "012\xff/" + "0123456789" * 10)
1857 self._test_ustar_name("0123456789" * 15 + "0123\xff/" + "0123456789" * 10, ValueError)
1858
1859 def test_unicode_longname2(self):
1860 self._test_ustar_name("0123456789" * 15 + "01\xff/2" + "0123456789" * 10, ValueError)
1861 self._test_ustar_name("0123456789" * 15 + "01\xff\xff/" + "0123456789" * 10, ValueError)
1862
1863 def test_unicode_longname3(self):
1864 self._test_ustar_name("0123456789" * 15 + "01\xff\xff/2" + "0123456789" * 10, ValueError)
1865 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 9 + "01234567\xff")
1866 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 9 + "012345678\xff", ValueError)
1867
1868 def test_unicode_longname4(self):
1869 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 9 + "012345\xff\xff")
1870 self._test_ustar_name("0123456789" * 15 + "01234/" + "0123456789" * 9 + "0123456\xff\xff", ValueError)
1871
1872 def _test_ustar_name(self, name, exc=None):
1873 with tarfile.open(tmpname, "w", format=self.format, encoding="utf-8") as tar:
1874 t = tarfile.TarInfo(name)
1875 if exc is None:
1876 tar.addfile(t)
1877 else:
1878 self.assertRaises(exc, tar.addfile, t)
1879
1880 if exc is None:
Lars Gustäbelddd99172016-04-19 11:58:41 +02001881 with tarfile.open(tmpname, "r", encoding="utf-8") as tar:
Lars Gustäbel0f450ab2016-04-19 08:43:17 +02001882 for t in tar:
1883 self.assertEqual(name, t.name)
1884 break
1885
1886 # Test the same as above for the 100 bytes link field.
1887 def test_unicode_link1(self):
1888 self._test_ustar_link("0123456789" * 10)
1889 self._test_ustar_link("0123456789" * 10 + "0", ValueError)
1890 self._test_ustar_link("0123456789" * 9 + "01234567\xff")
1891 self._test_ustar_link("0123456789" * 9 + "012345678\xff", ValueError)
1892
1893 def test_unicode_link2(self):
1894 self._test_ustar_link("0123456789" * 9 + "012345\xff\xff")
1895 self._test_ustar_link("0123456789" * 9 + "0123456\xff\xff", ValueError)
1896
1897 def _test_ustar_link(self, name, exc=None):
1898 with tarfile.open(tmpname, "w", format=self.format, encoding="utf-8") as tar:
1899 t = tarfile.TarInfo("foo")
1900 t.linkname = name
1901 if exc is None:
1902 tar.addfile(t)
1903 else:
1904 self.assertRaises(exc, tar.addfile, t)
1905
1906 if exc is None:
Lars Gustäbelddd99172016-04-19 11:58:41 +02001907 with tarfile.open(tmpname, "r", encoding="utf-8") as tar:
Lars Gustäbel0f450ab2016-04-19 08:43:17 +02001908 for t in tar:
1909 self.assertEqual(name, t.linkname)
1910 break
1911
1912
1913class GNUUnicodeTest(UnicodeTest, unittest.TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001914
Guido van Rossume7ba4952007-06-06 23:52:48 +00001915 format = tarfile.GNU_FORMAT
Guido van Rossumd8faa362007-04-27 19:54:29 +00001916
Lars Gustäbel1465cc22010-05-17 18:02:50 +00001917 def test_bad_pax_header(self):
1918 # Test for issue #8633. GNU tar <= 1.23 creates raw binary fields
1919 # without a hdrcharset=BINARY header.
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001920 for encoding, name in (
1921 ("utf-8", "pax/bad-pax-\udce4\udcf6\udcfc"),
Lars Gustäbel1465cc22010-05-17 18:02:50 +00001922 ("iso8859-1", "pax/bad-pax-\xe4\xf6\xfc"),):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001923 with tarfile.open(tarname, encoding=encoding,
1924 errors="surrogateescape") as tar:
Lars Gustäbel1465cc22010-05-17 18:02:50 +00001925 try:
1926 t = tar.getmember(name)
1927 except KeyError:
1928 self.fail("unable to read bad GNU tar pax header")
1929
Guido van Rossumd8faa362007-04-27 19:54:29 +00001930
Lars Gustäbel0f450ab2016-04-19 08:43:17 +02001931class PAXUnicodeTest(UnicodeTest, unittest.TestCase):
Lars Gustäbel3741eff2007-08-21 12:17:05 +00001932
1933 format = tarfile.PAX_FORMAT
1934
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001935 # PAX_FORMAT ignores encoding in write mode.
1936 test_unicode_filename_error = None
1937
Lars Gustäbel1465cc22010-05-17 18:02:50 +00001938 def test_binary_header(self):
1939 # Test a POSIX.1-2008 compatible header with a hdrcharset=BINARY field.
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001940 for encoding, name in (
1941 ("utf-8", "pax/hdrcharset-\udce4\udcf6\udcfc"),
Lars Gustäbel1465cc22010-05-17 18:02:50 +00001942 ("iso8859-1", "pax/hdrcharset-\xe4\xf6\xfc"),):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001943 with tarfile.open(tarname, encoding=encoding,
1944 errors="surrogateescape") as tar:
Lars Gustäbel1465cc22010-05-17 18:02:50 +00001945 try:
1946 t = tar.getmember(name)
1947 except KeyError:
1948 self.fail("unable to read POSIX.1-2008 binary header")
1949
Lars Gustäbel3741eff2007-08-21 12:17:05 +00001950
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001951class AppendTestBase:
Guido van Rossumd8faa362007-04-27 19:54:29 +00001952 # Test append mode (cp. patch #1652681).
Thomas Wouters902d6eb2007-01-09 23:18:33 +00001953
Guido van Rossumd8faa362007-04-27 19:54:29 +00001954 def setUp(self):
1955 self.tarname = tmpname
1956 if os.path.exists(self.tarname):
Tim Goldene0bd2c52014-05-06 13:24:26 +01001957 support.unlink(self.tarname)
Thomas Wouters902d6eb2007-01-09 23:18:33 +00001958
Guido van Rossumd8faa362007-04-27 19:54:29 +00001959 def _create_testtar(self, mode="w:"):
Antoine Pitrou95f55602010-09-23 18:36:46 +00001960 with tarfile.open(tarname, encoding="iso8859-1") as src:
1961 t = src.getmember("ustar/regtype")
1962 t.name = "foo"
Lars Gustäbel7a919e92012-05-05 18:15:03 +02001963 with src.extractfile(t) as f:
Antoine Pitroue1eca4e2010-10-29 23:49:49 +00001964 with tarfile.open(self.tarname, mode) as tar:
1965 tar.addfile(t, f)
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00001966
Serhiy Storchaka8b562922013-06-17 15:38:50 +03001967 def test_append_compressed(self):
1968 self._create_testtar("w:" + self.suffix)
1969 self.assertRaises(tarfile.ReadError, tarfile.open, tmpname, "a")
1970
1971class AppendTest(AppendTestBase, unittest.TestCase):
1972 test_append_compressed = None
1973
1974 def _add_testfile(self, fileobj=None):
1975 with tarfile.open(self.tarname, "a", fileobj=fileobj) as tar:
1976 tar.addfile(tarfile.TarInfo("bar"))
1977
Guido van Rossumd8faa362007-04-27 19:54:29 +00001978 def _test(self, names=["bar"], fileobj=None):
Antoine Pitrou95f55602010-09-23 18:36:46 +00001979 with tarfile.open(self.tarname, fileobj=fileobj) as tar:
1980 self.assertEqual(tar.getnames(), names)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001981
1982 def test_non_existing(self):
1983 self._add_testfile()
1984 self._test()
1985
1986 def test_empty(self):
Lars Gustäbel9520a432009-11-22 18:48:49 +00001987 tarfile.open(self.tarname, "w:").close()
Guido van Rossumd8faa362007-04-27 19:54:29 +00001988 self._add_testfile()
1989 self._test()
1990
1991 def test_empty_fileobj(self):
Lars Gustäbel9520a432009-11-22 18:48:49 +00001992 fobj = io.BytesIO(b"\0" * 1024)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001993 self._add_testfile(fobj)
1994 fobj.seek(0)
1995 self._test(fileobj=fobj)
1996
1997 def test_fileobj(self):
1998 self._create_testtar()
Antoine Pitrou95f55602010-09-23 18:36:46 +00001999 with open(self.tarname, "rb") as fobj:
2000 data = fobj.read()
Guido van Rossum34d19282007-08-09 01:03:29 +00002001 fobj = io.BytesIO(data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002002 self._add_testfile(fobj)
2003 fobj.seek(0)
2004 self._test(names=["foo", "bar"], fileobj=fobj)
2005
2006 def test_existing(self):
2007 self._create_testtar()
2008 self._add_testfile()
2009 self._test(names=["foo", "bar"])
2010
Lars Gustäbel9520a432009-11-22 18:48:49 +00002011 # Append mode is supposed to fail if the tarfile to append to
2012 # does not end with a zero block.
2013 def _test_error(self, data):
Antoine Pitrou95f55602010-09-23 18:36:46 +00002014 with open(self.tarname, "wb") as fobj:
2015 fobj.write(data)
Lars Gustäbel9520a432009-11-22 18:48:49 +00002016 self.assertRaises(tarfile.ReadError, self._add_testfile)
2017
2018 def test_null(self):
2019 self._test_error(b"")
2020
2021 def test_incomplete(self):
2022 self._test_error(b"\0" * 13)
2023
2024 def test_premature_eof(self):
2025 data = tarfile.TarInfo("foo").tobuf()
2026 self._test_error(data)
2027
2028 def test_trailing_garbage(self):
2029 data = tarfile.TarInfo("foo").tobuf()
2030 self._test_error(data + b"\0" * 13)
2031
2032 def test_invalid(self):
2033 self._test_error(b"a" * 512)
2034
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002035class GzipAppendTest(GzipTest, AppendTestBase, unittest.TestCase):
2036 pass
2037
2038class Bz2AppendTest(Bz2Test, AppendTestBase, unittest.TestCase):
2039 pass
2040
2041class LzmaAppendTest(LzmaTest, AppendTestBase, unittest.TestCase):
2042 pass
2043
Guido van Rossumd8faa362007-04-27 19:54:29 +00002044
2045class LimitsTest(unittest.TestCase):
2046
2047 def test_ustar_limits(self):
2048 # 100 char name
2049 tarinfo = tarfile.TarInfo("0123456789" * 10)
Guido van Rossume7ba4952007-06-06 23:52:48 +00002050 tarinfo.tobuf(tarfile.USTAR_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002051
2052 # 101 char name that cannot be stored
2053 tarinfo = tarfile.TarInfo("0123456789" * 10 + "0")
Guido van Rossume7ba4952007-06-06 23:52:48 +00002054 self.assertRaises(ValueError, tarinfo.tobuf, tarfile.USTAR_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002055
2056 # 256 char name with a slash at pos 156
2057 tarinfo = tarfile.TarInfo("123/" * 62 + "longname")
Guido van Rossume7ba4952007-06-06 23:52:48 +00002058 tarinfo.tobuf(tarfile.USTAR_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002059
2060 # 256 char name that cannot be stored
2061 tarinfo = tarfile.TarInfo("1234567/" * 31 + "longname")
Guido van Rossume7ba4952007-06-06 23:52:48 +00002062 self.assertRaises(ValueError, tarinfo.tobuf, tarfile.USTAR_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002063
2064 # 512 char name
2065 tarinfo = tarfile.TarInfo("123/" * 126 + "longname")
Guido van Rossume7ba4952007-06-06 23:52:48 +00002066 self.assertRaises(ValueError, tarinfo.tobuf, tarfile.USTAR_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002067
2068 # 512 char linkname
2069 tarinfo = tarfile.TarInfo("longlink")
2070 tarinfo.linkname = "123/" * 126 + "longname"
Guido van Rossume7ba4952007-06-06 23:52:48 +00002071 self.assertRaises(ValueError, tarinfo.tobuf, tarfile.USTAR_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002072
2073 # uid > 8 digits
2074 tarinfo = tarfile.TarInfo("name")
Guido van Rossumcd16bf62007-06-13 18:07:49 +00002075 tarinfo.uid = 0o10000000
Guido van Rossume7ba4952007-06-06 23:52:48 +00002076 self.assertRaises(ValueError, tarinfo.tobuf, tarfile.USTAR_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002077
2078 def test_gnu_limits(self):
2079 tarinfo = tarfile.TarInfo("123/" * 126 + "longname")
Guido van Rossume7ba4952007-06-06 23:52:48 +00002080 tarinfo.tobuf(tarfile.GNU_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002081
2082 tarinfo = tarfile.TarInfo("longlink")
2083 tarinfo.linkname = "123/" * 126 + "longname"
Guido van Rossume7ba4952007-06-06 23:52:48 +00002084 tarinfo.tobuf(tarfile.GNU_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002085
2086 # uid >= 256 ** 7
2087 tarinfo = tarfile.TarInfo("name")
Guido van Rossumcd16bf62007-06-13 18:07:49 +00002088 tarinfo.uid = 0o4000000000000000000
Guido van Rossume7ba4952007-06-06 23:52:48 +00002089 self.assertRaises(ValueError, tarinfo.tobuf, tarfile.GNU_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002090
2091 def test_pax_limits(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002092 tarinfo = tarfile.TarInfo("123/" * 126 + "longname")
Guido van Rossume7ba4952007-06-06 23:52:48 +00002093 tarinfo.tobuf(tarfile.PAX_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002094
2095 tarinfo = tarfile.TarInfo("longlink")
2096 tarinfo.linkname = "123/" * 126 + "longname"
Guido van Rossume7ba4952007-06-06 23:52:48 +00002097 tarinfo.tobuf(tarfile.PAX_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002098
2099 tarinfo = tarfile.TarInfo("name")
Guido van Rossumcd16bf62007-06-13 18:07:49 +00002100 tarinfo.uid = 0o4000000000000000000
Guido van Rossume7ba4952007-06-06 23:52:48 +00002101 tarinfo.tobuf(tarfile.PAX_FORMAT)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002102
2103
Lars Gustäbelb506dc32007-08-07 18:36:16 +00002104class MiscTest(unittest.TestCase):
2105
2106 def test_char_fields(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002107 self.assertEqual(tarfile.stn("foo", 8, "ascii", "strict"),
2108 b"foo\0\0\0\0\0")
2109 self.assertEqual(tarfile.stn("foobar", 3, "ascii", "strict"),
2110 b"foo")
2111 self.assertEqual(tarfile.nts(b"foo\0\0\0\0\0", "ascii", "strict"),
2112 "foo")
2113 self.assertEqual(tarfile.nts(b"foo\0bar\0", "ascii", "strict"),
2114 "foo")
Lars Gustäbelb506dc32007-08-07 18:36:16 +00002115
Lars Gustäbelac3d1372011-10-14 12:46:40 +02002116 def test_read_number_fields(self):
2117 # Issue 13158: Test if GNU tar specific base-256 number fields
2118 # are decoded correctly.
2119 self.assertEqual(tarfile.nti(b"0000001\x00"), 1)
2120 self.assertEqual(tarfile.nti(b"7777777\x00"), 0o7777777)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002121 self.assertEqual(tarfile.nti(b"\x80\x00\x00\x00\x00\x20\x00\x00"),
2122 0o10000000)
2123 self.assertEqual(tarfile.nti(b"\x80\x00\x00\x00\xff\xff\xff\xff"),
2124 0xffffffff)
2125 self.assertEqual(tarfile.nti(b"\xff\xff\xff\xff\xff\xff\xff\xff"),
2126 -1)
2127 self.assertEqual(tarfile.nti(b"\xff\xff\xff\xff\xff\xff\xff\x9c"),
2128 -100)
2129 self.assertEqual(tarfile.nti(b"\xff\x00\x00\x00\x00\x00\x00\x00"),
2130 -0x100000000000000)
Lars Gustäbelac3d1372011-10-14 12:46:40 +02002131
Lars Gustäbelb7a688b2015-07-02 19:38:38 +02002132 # Issue 24514: Test if empty number fields are converted to zero.
2133 self.assertEqual(tarfile.nti(b"\0"), 0)
2134 self.assertEqual(tarfile.nti(b" \0"), 0)
2135
Lars Gustäbelac3d1372011-10-14 12:46:40 +02002136 def test_write_number_fields(self):
Lars Gustäbelb506dc32007-08-07 18:36:16 +00002137 self.assertEqual(tarfile.itn(1), b"0000001\x00")
Lars Gustäbelac3d1372011-10-14 12:46:40 +02002138 self.assertEqual(tarfile.itn(0o7777777), b"7777777\x00")
CAM Gerlache680c3d2019-03-21 09:44:51 -05002139 self.assertEqual(tarfile.itn(0o10000000, format=tarfile.GNU_FORMAT),
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002140 b"\x80\x00\x00\x00\x00\x20\x00\x00")
CAM Gerlache680c3d2019-03-21 09:44:51 -05002141 self.assertEqual(tarfile.itn(0xffffffff, format=tarfile.GNU_FORMAT),
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002142 b"\x80\x00\x00\x00\xff\xff\xff\xff")
CAM Gerlache680c3d2019-03-21 09:44:51 -05002143 self.assertEqual(tarfile.itn(-1, format=tarfile.GNU_FORMAT),
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002144 b"\xff\xff\xff\xff\xff\xff\xff\xff")
CAM Gerlache680c3d2019-03-21 09:44:51 -05002145 self.assertEqual(tarfile.itn(-100, format=tarfile.GNU_FORMAT),
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002146 b"\xff\xff\xff\xff\xff\xff\xff\x9c")
CAM Gerlache680c3d2019-03-21 09:44:51 -05002147 self.assertEqual(tarfile.itn(-0x100000000000000,
2148 format=tarfile.GNU_FORMAT),
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002149 b"\xff\x00\x00\x00\x00\x00\x00\x00")
Lars Gustäbelac3d1372011-10-14 12:46:40 +02002150
Joffrey F72d9b2b2018-02-26 16:02:21 -08002151 # Issue 32713: Test if itn() supports float values outside the
2152 # non-GNU format range
2153 self.assertEqual(tarfile.itn(-100.0, format=tarfile.GNU_FORMAT),
2154 b"\xff\xff\xff\xff\xff\xff\xff\x9c")
2155 self.assertEqual(tarfile.itn(8 ** 12 + 0.0, format=tarfile.GNU_FORMAT),
2156 b"\x80\x00\x00\x10\x00\x00\x00\x00")
2157 self.assertEqual(tarfile.nti(tarfile.itn(-0.1, format=tarfile.GNU_FORMAT)), 0)
2158
Lars Gustäbelac3d1372011-10-14 12:46:40 +02002159 def test_number_field_limits(self):
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002160 with self.assertRaises(ValueError):
2161 tarfile.itn(-1, 8, tarfile.USTAR_FORMAT)
2162 with self.assertRaises(ValueError):
2163 tarfile.itn(0o10000000, 8, tarfile.USTAR_FORMAT)
2164 with self.assertRaises(ValueError):
2165 tarfile.itn(-0x10000000001, 6, tarfile.GNU_FORMAT)
2166 with self.assertRaises(ValueError):
2167 tarfile.itn(0x10000000000, 6, tarfile.GNU_FORMAT)
Lars Gustäbelb506dc32007-08-07 18:36:16 +00002168
Martin Panter104dcda2016-01-16 06:59:13 +00002169 def test__all__(self):
Martin Panter5318d102016-01-16 11:01:14 +00002170 blacklist = {'version', 'grp', 'pwd', 'symlink_exception',
Martin Panter104dcda2016-01-16 06:59:13 +00002171 'NUL', 'BLOCKSIZE', 'RECORDSIZE', 'GNU_MAGIC',
2172 'POSIX_MAGIC', 'LENGTH_NAME', 'LENGTH_LINK',
2173 'LENGTH_PREFIX', 'REGTYPE', 'AREGTYPE', 'LNKTYPE',
2174 'SYMTYPE', 'CHRTYPE', 'BLKTYPE', 'DIRTYPE', 'FIFOTYPE',
2175 'CONTTYPE', 'GNUTYPE_LONGNAME', 'GNUTYPE_LONGLINK',
2176 'GNUTYPE_SPARSE', 'XHDTYPE', 'XGLTYPE', 'SOLARIS_XHDTYPE',
2177 'SUPPORTED_TYPES', 'REGULAR_TYPES', 'GNU_TYPES',
2178 'PAX_FIELDS', 'PAX_NAME_FIELDS', 'PAX_NUMBER_FIELDS',
2179 'stn', 'nts', 'nti', 'itn', 'calc_chksums', 'copyfileobj',
2180 'filemode',
2181 'EmptyHeaderError', 'TruncatedHeaderError',
2182 'EOFHeaderError', 'InvalidHeaderError',
Serhiy Storchaka2c1d3e32016-01-16 11:05:11 +02002183 'SubsequentHeaderError', 'ExFileObject',
Martin Panter104dcda2016-01-16 06:59:13 +00002184 'main'}
2185 support.check__all__(self, tarfile, blacklist=blacklist)
2186
Lars Gustäbelb506dc32007-08-07 18:36:16 +00002187
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002188class CommandLineTest(unittest.TestCase):
2189
Serhiy Storchaka255493c2014-02-05 20:54:43 +02002190 def tarfilecmd(self, *args, **kwargs):
2191 rc, out, err = script_helper.assert_python_ok('-m', 'tarfile', *args,
2192 **kwargs)
Antoine Pitrou3b7b1e52013-11-24 01:55:05 +01002193 return out.replace(os.linesep.encode(), b'\n')
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002194
2195 def tarfilecmd_failure(self, *args):
2196 return script_helper.assert_python_failure('-m', 'tarfile', *args)
2197
2198 def make_simple_tarfile(self, tar_name):
2199 files = [support.findfile('tokenize_tests.txt'),
2200 support.findfile('tokenize_tests-no-coding-cookie-'
2201 'and-utf8-bom-sig-only.txt')]
2202 self.addCleanup(support.unlink, tar_name)
2203 with tarfile.open(tar_name, 'w') as tf:
2204 for tardata in files:
2205 tf.add(tardata, arcname=os.path.basename(tardata))
2206
Serhiy Storchaka150cd192017-04-07 18:56:12 +03002207 def test_bad_use(self):
2208 rc, out, err = self.tarfilecmd_failure()
2209 self.assertEqual(out, b'')
2210 self.assertIn(b'usage', err.lower())
2211 self.assertIn(b'error', err.lower())
2212 self.assertIn(b'required', err.lower())
2213 rc, out, err = self.tarfilecmd_failure('-l', '')
2214 self.assertEqual(out, b'')
2215 self.assertNotEqual(err.strip(), b'')
2216
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002217 def test_test_command(self):
Serhiy Storchaka5e8c8092013-11-24 02:30:59 +02002218 for tar_name in testtarnames:
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002219 for opt in '-t', '--test':
2220 out = self.tarfilecmd(opt, tar_name)
2221 self.assertEqual(out, b'')
2222
2223 def test_test_command_verbose(self):
Serhiy Storchaka5e8c8092013-11-24 02:30:59 +02002224 for tar_name in testtarnames:
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002225 for opt in '-v', '--verbose':
2226 out = self.tarfilecmd(opt, '-t', tar_name)
2227 self.assertIn(b'is a tar archive.\n', out)
2228
2229 def test_test_command_invalid_file(self):
2230 zipname = support.findfile('zipdir.zip')
2231 rc, out, err = self.tarfilecmd_failure('-t', zipname)
2232 self.assertIn(b' is not a tar archive.', err)
2233 self.assertEqual(out, b'')
2234 self.assertEqual(rc, 1)
2235
Serhiy Storchaka5e8c8092013-11-24 02:30:59 +02002236 for tar_name in testtarnames:
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002237 with self.subTest(tar_name=tar_name):
2238 with open(tar_name, 'rb') as f:
2239 data = f.read()
2240 try:
2241 with open(tmpname, 'wb') as f:
2242 f.write(data[:511])
2243 rc, out, err = self.tarfilecmd_failure('-t', tmpname)
2244 self.assertEqual(out, b'')
2245 self.assertEqual(rc, 1)
2246 finally:
2247 support.unlink(tmpname)
2248
2249 def test_list_command(self):
Serhiy Storchaka255493c2014-02-05 20:54:43 +02002250 for tar_name in testtarnames:
2251 with support.captured_stdout() as t:
2252 with tarfile.open(tar_name, 'r') as tf:
2253 tf.list(verbose=False)
2254 expected = t.getvalue().encode('ascii', 'backslashreplace')
2255 for opt in '-l', '--list':
2256 out = self.tarfilecmd(opt, tar_name,
2257 PYTHONIOENCODING='ascii')
2258 self.assertEqual(out, expected)
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002259
2260 def test_list_command_verbose(self):
Serhiy Storchaka255493c2014-02-05 20:54:43 +02002261 for tar_name in testtarnames:
2262 with support.captured_stdout() as t:
2263 with tarfile.open(tar_name, 'r') as tf:
2264 tf.list(verbose=True)
2265 expected = t.getvalue().encode('ascii', 'backslashreplace')
2266 for opt in '-v', '--verbose':
2267 out = self.tarfilecmd(opt, '-l', tar_name,
2268 PYTHONIOENCODING='ascii')
2269 self.assertEqual(out, expected)
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002270
2271 def test_list_command_invalid_file(self):
2272 zipname = support.findfile('zipdir.zip')
2273 rc, out, err = self.tarfilecmd_failure('-l', zipname)
2274 self.assertIn(b' is not a tar archive.', err)
2275 self.assertEqual(out, b'')
2276 self.assertEqual(rc, 1)
2277
2278 def test_create_command(self):
2279 files = [support.findfile('tokenize_tests.txt'),
2280 support.findfile('tokenize_tests-no-coding-cookie-'
2281 'and-utf8-bom-sig-only.txt')]
2282 for opt in '-c', '--create':
2283 try:
2284 out = self.tarfilecmd(opt, tmpname, *files)
2285 self.assertEqual(out, b'')
2286 with tarfile.open(tmpname) as tar:
2287 tar.getmembers()
2288 finally:
2289 support.unlink(tmpname)
2290
2291 def test_create_command_verbose(self):
2292 files = [support.findfile('tokenize_tests.txt'),
2293 support.findfile('tokenize_tests-no-coding-cookie-'
2294 'and-utf8-bom-sig-only.txt')]
2295 for opt in '-v', '--verbose':
2296 try:
2297 out = self.tarfilecmd(opt, '-c', tmpname, *files)
2298 self.assertIn(b' file created.', out)
2299 with tarfile.open(tmpname) as tar:
2300 tar.getmembers()
2301 finally:
2302 support.unlink(tmpname)
2303
2304 def test_create_command_dotless_filename(self):
2305 files = [support.findfile('tokenize_tests.txt')]
2306 try:
2307 out = self.tarfilecmd('-c', dotlessname, *files)
2308 self.assertEqual(out, b'')
2309 with tarfile.open(dotlessname) as tar:
2310 tar.getmembers()
2311 finally:
2312 support.unlink(dotlessname)
2313
2314 def test_create_command_dot_started_filename(self):
2315 tar_name = os.path.join(TEMPDIR, ".testtar")
2316 files = [support.findfile('tokenize_tests.txt')]
2317 try:
2318 out = self.tarfilecmd('-c', tar_name, *files)
2319 self.assertEqual(out, b'')
2320 with tarfile.open(tar_name) as tar:
2321 tar.getmembers()
2322 finally:
2323 support.unlink(tar_name)
2324
Serhiy Storchaka832dd5f2015-02-10 08:45:53 +02002325 def test_create_command_compressed(self):
2326 files = [support.findfile('tokenize_tests.txt'),
2327 support.findfile('tokenize_tests-no-coding-cookie-'
2328 'and-utf8-bom-sig-only.txt')]
2329 for filetype in (GzipTest, Bz2Test, LzmaTest):
2330 if not filetype.open:
2331 continue
2332 try:
2333 tar_name = tmpname + '.' + filetype.suffix
2334 out = self.tarfilecmd('-c', tar_name, *files)
2335 with filetype.taropen(tar_name) as tar:
2336 tar.getmembers()
2337 finally:
2338 support.unlink(tar_name)
2339
Serhiy Storchakad27b4552013-11-24 01:53:29 +02002340 def test_extract_command(self):
2341 self.make_simple_tarfile(tmpname)
2342 for opt in '-e', '--extract':
2343 try:
2344 with support.temp_cwd(tarextdir):
2345 out = self.tarfilecmd(opt, tmpname)
2346 self.assertEqual(out, b'')
2347 finally:
2348 support.rmtree(tarextdir)
2349
2350 def test_extract_command_verbose(self):
2351 self.make_simple_tarfile(tmpname)
2352 for opt in '-v', '--verbose':
2353 try:
2354 with support.temp_cwd(tarextdir):
2355 out = self.tarfilecmd(opt, '-e', tmpname)
2356 self.assertIn(b' file is extracted.', out)
2357 finally:
2358 support.rmtree(tarextdir)
2359
2360 def test_extract_command_different_directory(self):
2361 self.make_simple_tarfile(tmpname)
2362 try:
2363 with support.temp_cwd(tarextdir):
2364 out = self.tarfilecmd('-e', tmpname, 'spamdir')
2365 self.assertEqual(out, b'')
2366 finally:
2367 support.rmtree(tarextdir)
2368
2369 def test_extract_command_invalid_file(self):
2370 zipname = support.findfile('zipdir.zip')
2371 with support.temp_cwd(tarextdir):
2372 rc, out, err = self.tarfilecmd_failure('-e', zipname)
2373 self.assertIn(b' is not a tar archive.', err)
2374 self.assertEqual(out, b'')
2375 self.assertEqual(rc, 1)
2376
2377
Lars Gustäbel01385812010-03-03 12:08:54 +00002378class ContextManagerTest(unittest.TestCase):
2379
2380 def test_basic(self):
2381 with tarfile.open(tarname) as tar:
2382 self.assertFalse(tar.closed, "closed inside runtime context")
2383 self.assertTrue(tar.closed, "context manager failed")
2384
2385 def test_closed(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002386 # The __enter__() method is supposed to raise OSError
Lars Gustäbel01385812010-03-03 12:08:54 +00002387 # if the TarFile object is already closed.
2388 tar = tarfile.open(tarname)
2389 tar.close()
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002390 with self.assertRaises(OSError):
Lars Gustäbel01385812010-03-03 12:08:54 +00002391 with tar:
2392 pass
2393
2394 def test_exception(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002395 # Test if the OSError exception is passed through properly.
Lars Gustäbel01385812010-03-03 12:08:54 +00002396 with self.assertRaises(Exception) as exc:
2397 with tarfile.open(tarname) as tar:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02002398 raise OSError
2399 self.assertIsInstance(exc.exception, OSError,
Lars Gustäbel01385812010-03-03 12:08:54 +00002400 "wrong exception raised in context manager")
2401 self.assertTrue(tar.closed, "context manager failed")
2402
2403 def test_no_eof(self):
2404 # __exit__() must not write end-of-archive blocks if an
2405 # exception was raised.
2406 try:
2407 with tarfile.open(tmpname, "w") as tar:
2408 raise Exception
2409 except:
2410 pass
2411 self.assertEqual(os.path.getsize(tmpname), 0,
2412 "context manager wrote an end-of-archive block")
2413 self.assertTrue(tar.closed, "context manager failed")
2414
2415 def test_eof(self):
2416 # __exit__() must write end-of-archive blocks, i.e. call
2417 # TarFile.close() if there was no error.
2418 with tarfile.open(tmpname, "w"):
2419 pass
2420 self.assertNotEqual(os.path.getsize(tmpname), 0,
2421 "context manager wrote no end-of-archive block")
2422
2423 def test_fileobj(self):
2424 # Test that __exit__() did not close the external file
2425 # object.
Antoine Pitrou95f55602010-09-23 18:36:46 +00002426 with open(tmpname, "wb") as fobj:
2427 try:
2428 with tarfile.open(fileobj=fobj, mode="w") as tar:
2429 raise Exception
2430 except:
2431 pass
2432 self.assertFalse(fobj.closed, "external file object was closed")
2433 self.assertTrue(tar.closed, "context manager failed")
Lars Gustäbel01385812010-03-03 12:08:54 +00002434
2435
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002436@unittest.skipIf(hasattr(os, "link"), "requires os.link to be missing")
2437class LinkEmulationTest(ReadTest, unittest.TestCase):
Lars Gustäbel1b512722010-06-03 12:45:16 +00002438
2439 # Test for issue #8741 regression. On platforms that do not support
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002440 # symbolic or hard links tarfile tries to extract these types of members
2441 # as the regular files they point to.
Lars Gustäbel1b512722010-06-03 12:45:16 +00002442 def _test_link_extraction(self, name):
2443 self.tar.extract(name, TEMPDIR)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002444 with open(os.path.join(TEMPDIR, name), "rb") as f:
2445 data = f.read()
Lars Gustäbel1b512722010-06-03 12:45:16 +00002446 self.assertEqual(md5sum(data), md5_regtype)
2447
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002448 # See issues #1578269, #8879, and #17689 for some history on these skips
Brian Curtind40e6f72010-07-08 21:39:08 +00002449 @unittest.skipIf(hasattr(os.path, "islink"),
2450 "Skip emulation - has os.path.islink but not os.link")
Lars Gustäbel1b512722010-06-03 12:45:16 +00002451 def test_hardlink_extraction1(self):
2452 self._test_link_extraction("ustar/lnktype")
2453
Brian Curtind40e6f72010-07-08 21:39:08 +00002454 @unittest.skipIf(hasattr(os.path, "islink"),
2455 "Skip emulation - has os.path.islink but not os.link")
Lars Gustäbel1b512722010-06-03 12:45:16 +00002456 def test_hardlink_extraction2(self):
2457 self._test_link_extraction("./ustar/linktest2/lnktype")
2458
Brian Curtin74e45612010-07-09 15:58:59 +00002459 @unittest.skipIf(hasattr(os, "symlink"),
2460 "Skip emulation if symlink exists")
Lars Gustäbel1b512722010-06-03 12:45:16 +00002461 def test_symlink_extraction1(self):
2462 self._test_link_extraction("ustar/symtype")
2463
Brian Curtin74e45612010-07-09 15:58:59 +00002464 @unittest.skipIf(hasattr(os, "symlink"),
2465 "Skip emulation if symlink exists")
Lars Gustäbel1b512722010-06-03 12:45:16 +00002466 def test_symlink_extraction2(self):
2467 self._test_link_extraction("./ustar/linktest2/symtype")
2468
2469
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002470class Bz2PartialReadTest(Bz2Test, unittest.TestCase):
Lars Gustäbel42e00912009-03-22 20:34:29 +00002471 # Issue5068: The _BZ2Proxy.read() method loops forever
2472 # on an empty or partial bzipped file.
2473
2474 def _test_partial_input(self, mode):
2475 class MyBytesIO(io.BytesIO):
2476 hit_eof = False
2477 def read(self, n):
2478 if self.hit_eof:
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002479 raise AssertionError("infinite loop detected in "
2480 "tarfile.open()")
Lars Gustäbel42e00912009-03-22 20:34:29 +00002481 self.hit_eof = self.tell() == len(self.getvalue())
2482 return super(MyBytesIO, self).read(n)
Lars Gustäbel9520a432009-11-22 18:48:49 +00002483 def seek(self, *args):
2484 self.hit_eof = False
2485 return super(MyBytesIO, self).seek(*args)
Lars Gustäbel42e00912009-03-22 20:34:29 +00002486
2487 data = bz2.compress(tarfile.TarInfo("foo").tobuf())
2488 for x in range(len(data) + 1):
Lars Gustäbel9520a432009-11-22 18:48:49 +00002489 try:
2490 tarfile.open(fileobj=MyBytesIO(data[:x]), mode=mode)
2491 except tarfile.ReadError:
2492 pass # we have no interest in ReadErrors
Lars Gustäbel42e00912009-03-22 20:34:29 +00002493
2494 def test_partial_input(self):
2495 self._test_partial_input("r")
2496
2497 def test_partial_input_bz2(self):
2498 self._test_partial_input("r:bz2")
2499
2500
Eric V. Smith7a803892015-04-15 10:27:58 -04002501def root_is_uid_gid_0():
2502 try:
2503 import pwd, grp
2504 except ImportError:
2505 return False
2506 if pwd.getpwuid(0)[0] != 'root':
2507 return False
2508 if grp.getgrgid(0)[0] != 'root':
2509 return False
2510 return True
2511
2512
Zachary Waread3e27a2015-05-12 23:57:21 -05002513@unittest.skipUnless(hasattr(os, 'chown'), "missing os.chown")
2514@unittest.skipUnless(hasattr(os, 'geteuid'), "missing os.geteuid")
Eric V. Smith7a803892015-04-15 10:27:58 -04002515class NumericOwnerTest(unittest.TestCase):
2516 # mock the following:
2517 # os.chown: so we can test what's being called
2518 # os.chmod: so the modes are not actually changed. if they are, we can't
2519 # delete the files/directories
2520 # os.geteuid: so we can lie and say we're root (uid = 0)
2521
2522 @staticmethod
2523 def _make_test_archive(filename_1, dirname_1, filename_2):
2524 # the file contents to write
2525 fobj = io.BytesIO(b"content")
2526
2527 # create a tar file with a file, a directory, and a file within that
2528 # directory. Assign various .uid/.gid values to them
2529 items = [(filename_1, 99, 98, tarfile.REGTYPE, fobj),
2530 (dirname_1, 77, 76, tarfile.DIRTYPE, None),
2531 (filename_2, 88, 87, tarfile.REGTYPE, fobj),
2532 ]
2533 with tarfile.open(tmpname, 'w') as tarfl:
2534 for name, uid, gid, typ, contents in items:
2535 t = tarfile.TarInfo(name)
2536 t.uid = uid
2537 t.gid = gid
2538 t.uname = 'root'
2539 t.gname = 'root'
2540 t.type = typ
2541 tarfl.addfile(t, contents)
2542
2543 # return the full pathname to the tar file
2544 return tmpname
2545
2546 @staticmethod
2547 @contextmanager
2548 def _setup_test(mock_geteuid):
2549 mock_geteuid.return_value = 0 # lie and say we're root
2550 fname = 'numeric-owner-testfile'
2551 dirname = 'dir'
2552
2553 # the names we want stored in the tarfile
2554 filename_1 = fname
2555 dirname_1 = dirname
2556 filename_2 = os.path.join(dirname, fname)
2557
2558 # create the tarfile with the contents we're after
2559 tar_filename = NumericOwnerTest._make_test_archive(filename_1,
2560 dirname_1,
2561 filename_2)
2562
2563 # open the tarfile for reading. yield it and the names of the items
2564 # we stored into the file
2565 with tarfile.open(tar_filename) as tarfl:
2566 yield tarfl, filename_1, dirname_1, filename_2
2567
2568 @unittest.mock.patch('os.chown')
2569 @unittest.mock.patch('os.chmod')
2570 @unittest.mock.patch('os.geteuid')
2571 def test_extract_with_numeric_owner(self, mock_geteuid, mock_chmod,
2572 mock_chown):
2573 with self._setup_test(mock_geteuid) as (tarfl, filename_1, _,
2574 filename_2):
2575 tarfl.extract(filename_1, TEMPDIR, numeric_owner=True)
2576 tarfl.extract(filename_2 , TEMPDIR, numeric_owner=True)
2577
2578 # convert to filesystem paths
2579 f_filename_1 = os.path.join(TEMPDIR, filename_1)
2580 f_filename_2 = os.path.join(TEMPDIR, filename_2)
2581
2582 mock_chown.assert_has_calls([unittest.mock.call(f_filename_1, 99, 98),
2583 unittest.mock.call(f_filename_2, 88, 87),
2584 ],
2585 any_order=True)
2586
2587 @unittest.mock.patch('os.chown')
2588 @unittest.mock.patch('os.chmod')
2589 @unittest.mock.patch('os.geteuid')
2590 def test_extractall_with_numeric_owner(self, mock_geteuid, mock_chmod,
2591 mock_chown):
2592 with self._setup_test(mock_geteuid) as (tarfl, filename_1, dirname_1,
2593 filename_2):
2594 tarfl.extractall(TEMPDIR, numeric_owner=True)
2595
2596 # convert to filesystem paths
2597 f_filename_1 = os.path.join(TEMPDIR, filename_1)
2598 f_dirname_1 = os.path.join(TEMPDIR, dirname_1)
2599 f_filename_2 = os.path.join(TEMPDIR, filename_2)
2600
2601 mock_chown.assert_has_calls([unittest.mock.call(f_filename_1, 99, 98),
2602 unittest.mock.call(f_dirname_1, 77, 76),
2603 unittest.mock.call(f_filename_2, 88, 87),
2604 ],
2605 any_order=True)
2606
2607 # this test requires that uid=0 and gid=0 really be named 'root'. that's
2608 # because the uname and gname in the test file are 'root', and extract()
2609 # will look them up using pwd and grp to find their uid and gid, which we
2610 # test here to be 0.
2611 @unittest.skipUnless(root_is_uid_gid_0(),
2612 'uid=0,gid=0 must be named "root"')
2613 @unittest.mock.patch('os.chown')
2614 @unittest.mock.patch('os.chmod')
2615 @unittest.mock.patch('os.geteuid')
2616 def test_extract_without_numeric_owner(self, mock_geteuid, mock_chmod,
2617 mock_chown):
2618 with self._setup_test(mock_geteuid) as (tarfl, filename_1, _, _):
2619 tarfl.extract(filename_1, TEMPDIR, numeric_owner=False)
2620
2621 # convert to filesystem paths
2622 f_filename_1 = os.path.join(TEMPDIR, filename_1)
2623
2624 mock_chown.assert_called_with(f_filename_1, 0, 0)
2625
2626 @unittest.mock.patch('os.geteuid')
2627 def test_keyword_only(self, mock_geteuid):
2628 with self._setup_test(mock_geteuid) as (tarfl, filename_1, _, _):
2629 self.assertRaises(TypeError,
2630 tarfl.extract, filename_1, TEMPDIR, False, True)
2631
2632
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002633def setUpModule():
Antoine Pitrou95f55602010-09-23 18:36:46 +00002634 support.unlink(TEMPDIR)
Antoine Pitrou941ee882009-11-11 20:59:38 +00002635 os.makedirs(TEMPDIR)
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00002636
Serhiy Storchaka5e8c8092013-11-24 02:30:59 +02002637 global testtarnames
2638 testtarnames = [tarname]
Antoine Pitrou95f55602010-09-23 18:36:46 +00002639 with open(tarname, "rb") as fobj:
2640 data = fobj.read()
Neal Norwitza4f651a2004-07-20 22:07:44 +00002641
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002642 # Create compressed tarfiles.
2643 for c in GzipTest, Bz2Test, LzmaTest:
2644 if c.open:
2645 support.unlink(c.tarname)
Serhiy Storchaka5e8c8092013-11-24 02:30:59 +02002646 testtarnames.append(c.tarname)
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002647 with c.open(c.tarname, "wb") as tar:
2648 tar.write(data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002649
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002650def tearDownModule():
2651 if os.path.exists(TEMPDIR):
Tim Goldene0bd2c52014-05-06 13:24:26 +01002652 support.rmtree(TEMPDIR)
Neal Norwitzb9ef4ae2003-01-05 23:19:43 +00002653
Neal Norwitz996acf12003-02-17 14:51:41 +00002654if __name__ == "__main__":
Serhiy Storchaka8b562922013-06-17 15:38:50 +03002655 unittest.main()