From b169b5c7f3b3cb0062dc1f3eb128fbbd074ef62f Mon Sep 17 00:00:00 2001 From: Jeffrey Kintscher Date: Thu, 9 May 2019 02:43:48 -0700 Subject: [PATCH 1/3] bpo-12800: Extracting a symlink from a tarball using the tarfile module in stream mode ('r|') fails with an exception when a file or symlink of the same name already exists. The fix is to remove the existing file or symlink before extraction. Tests are included. --- Lib/tarfile.py | 3 ++ Lib/test/test_tarfile.py | 64 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/Lib/tarfile.py b/Lib/tarfile.py index e2b60532f693d4..633251f2abdbec 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2232,6 +2232,9 @@ def makelink(self, tarinfo, targetpath): try: # For systems that support symbolic and hard links. if tarinfo.issym(): + if os.path.islink(targetpath) or os.path.isfile(targetpath): + os.unlink(targetpath) + os.symlink(tarinfo.linkname, targetpath) else: # See extract(). diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index be66f1f89e6f39..2fa84db96e9151 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -2580,6 +2580,70 @@ def test_partial_input_bz2(self): self._test_partial_input("r:bz2") +class SymlinkOverwriteTest(unittest.TestCase): + # The testcase checks for correct overwriting of an + # existing symlink (issue #12800) + + @support.skip_unless_symlink + def test_overwrite_symlink(self): + tmpdir = support.temp_cwd('overwrite_symlink') + source = 'source' + link = 'link' + try: + with open(source, 'wb'): + pass + os.symlink(source, link) + with tarfile.open(tmpname, 'w') as tar: + tar.add(source, arcname=os.path.basename(source)) + tar.add(link, arcname=os.path.basename(link)) + + with open(tmpname, 'rb') as fileobj: + with tarfile.open(fileobj=fileobj, mode='r|') as tar: + tar.extractall(path=support.SAVEDCWD) + finally: + try: + os.unlink(link) + except: + pass + + try: + os.unlink(source) + except: + pass + + @support.skip_unless_symlink + def test_overwrite_file_with_symlink(self): + tmpdir = support.temp_cwd('overwrite_file_with_symlink') + source = 'source' + link = 'link' + try: + with open(source, 'wb'): + pass + os.symlink(source, link) + with tarfile.open(tmpname, 'w') as tar: + tar.add(source, arcname=os.path.basename(source)) + tar.add(link, arcname=os.path.basename(link)) + + os.unlink(link) + + with open(link, 'wb'): + pass + + with open(tmpname, 'rb') as fileobj: + with tarfile.open(fileobj=fileobj, mode='r|') as tar: + tar.extractall(path=support.SAVEDCWD) + finally: + try: + os.unlink(link) + except: + pass + + try: + os.unlink(source) + except: + pass + + def root_is_uid_gid_0(): try: import pwd, grp From bce13d65b321cd57c0be738499cce83d3b081036 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" Date: Thu, 9 May 2019 18:37:38 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Library/2019-05-09-18-37-37.bpo-12800.TyjRQq.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2019-05-09-18-37-37.bpo-12800.TyjRQq.rst diff --git a/Misc/NEWS.d/next/Library/2019-05-09-18-37-37.bpo-12800.TyjRQq.rst b/Misc/NEWS.d/next/Library/2019-05-09-18-37-37.bpo-12800.TyjRQq.rst new file mode 100644 index 00000000000000..a1208cb176746a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-09-18-37-37.bpo-12800.TyjRQq.rst @@ -0,0 +1 @@ +Extracting a symlink from a tarball using the tarfile module in stream mode ('r|') fails with an exception when a file or symlink of the same name already exists. The fix is to remove the existing file or symlink before extraction. \ No newline at end of file From 700c3f0277472dca7e8a4b7dcf56b5d3b96c2dee Mon Sep 17 00:00:00 2001 From: Chris AtLee Date: Thu, 18 Jun 2020 18:37:13 -0400 Subject: [PATCH 3/3] Fix tests as per review comments --- Lib/test/test_tarfile.py | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 2fa84db96e9151..deab9eac99ade2 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -2586,10 +2586,9 @@ class SymlinkOverwriteTest(unittest.TestCase): @support.skip_unless_symlink def test_overwrite_symlink(self): - tmpdir = support.temp_cwd('overwrite_symlink') - source = 'source' - link = 'link' - try: + with support.temp_cwd('overwrite_symlink'): + source = 'source' + link = 'link' with open(source, 'wb'): pass os.symlink(source, link) @@ -2600,23 +2599,12 @@ def test_overwrite_symlink(self): with open(tmpname, 'rb') as fileobj: with tarfile.open(fileobj=fileobj, mode='r|') as tar: tar.extractall(path=support.SAVEDCWD) - finally: - try: - os.unlink(link) - except: - pass - - try: - os.unlink(source) - except: - pass @support.skip_unless_symlink def test_overwrite_file_with_symlink(self): - tmpdir = support.temp_cwd('overwrite_file_with_symlink') - source = 'source' - link = 'link' - try: + with support.temp_cwd('overwrite_file_with_symlink'): + source = 'source' + link = 'link' with open(source, 'wb'): pass os.symlink(source, link) @@ -2632,17 +2620,6 @@ def test_overwrite_file_with_symlink(self): with open(tmpname, 'rb') as fileobj: with tarfile.open(fileobj=fileobj, mode='r|') as tar: tar.extractall(path=support.SAVEDCWD) - finally: - try: - os.unlink(link) - except: - pass - - try: - os.unlink(source) - except: - pass - def root_is_uid_gid_0(): try: