From 75b6685c160412679ad6e695ac8f09053f92e942 Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Sat, 10 Jan 2026 17:31:32 +0100 Subject: [PATCH 1/9] Perform path normalization in TarFile.extractall() --- Lib/tarfile.py | 1 + .../next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst | 1 + 2 files changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 7db3a40c9b33cf..f15f3109968730 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2437,6 +2437,7 @@ def extractall(self, path=".", members=None, *, numeric_owner=False, 'excluded by filter') continue dirpath = os.path.join(path, tarinfo.name) + dirpath = os.path.normpath(dirpath) try: lstat = os.lstat(dirpath) except FileNotFoundError: diff --git a/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst b/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst new file mode 100644 index 00000000000000..6351666447fbda --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst @@ -0,0 +1 @@ +Add path normalization to `TarFile.extractall()`. From c48bda863a61f2dbbd5bf71cdaff322ac4010c88 Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Sat, 10 Jan 2026 17:52:18 +0100 Subject: [PATCH 2/9] Fix blurb syntax --- .../next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst b/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst index 6351666447fbda..df509b1061206b 100644 --- a/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst +++ b/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst @@ -1 +1 @@ -Add path normalization to `TarFile.extractall()`. +:mod:`tarfile`: Add path normalization to :meth:`TarFile.extractall`. From 765776ae96bf27fb4a71ed0163c23c5d833d5955 Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Sat, 10 Jan 2026 20:52:16 +0100 Subject: [PATCH 3/9] Do not systematically normalize path in extractall --- Lib/tarfile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/tarfile.py b/Lib/tarfile.py index f15f3109968730..7db3a40c9b33cf 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2437,7 +2437,6 @@ def extractall(self, path=".", members=None, *, numeric_owner=False, 'excluded by filter') continue dirpath = os.path.join(path, tarinfo.name) - dirpath = os.path.normpath(dirpath) try: lstat = os.lstat(dirpath) except FileNotFoundError: From d41ed219562cee7af7a7013c3da2efc57a57b088 Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Sat, 10 Jan 2026 20:53:37 +0100 Subject: [PATCH 4/9] Replace tar separator by OS-specific separator in _get_extract_tarinfo --- Lib/tarfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 7db3a40c9b33cf..d83ded1e9ef28f 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2487,8 +2487,10 @@ def _get_extract_tarinfo(self, member, filter_function, path): if isinstance(member, str): unfiltered = self.getmember(member) + unfiltered = unfiltered.replace(r'/', os.sep) else: unfiltered = member + unfiltered.path = unfiltered.path.replace(r'/', os.sep) filtered = None try: From 4776a7cdedaaa9bfeab283d4cc04529ab85a27dd Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Sat, 10 Jan 2026 21:06:20 +0100 Subject: [PATCH 5/9] Update NEWS entry --- .../next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst b/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst index df509b1061206b..4cef7ccd1dcf8b 100644 --- a/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst +++ b/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst @@ -1 +1 @@ -:mod:`tarfile`: Add path normalization to :meth:`TarFile.extractall`. +:meth:`tarfile.TarFile._get_extract_tarinfo` now uses OS-specific separators. From d84c7bc40f394ff5c61a48a6f58691099af21d85 Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Mon, 12 Jan 2026 19:14:54 +0100 Subject: [PATCH 6/9] Only perform separator replacement if platform separator doesn't match tar separator --- Lib/tarfile.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/tarfile.py b/Lib/tarfile.py index d83ded1e9ef28f..7fe6a42e67098b 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2487,10 +2487,12 @@ def _get_extract_tarinfo(self, member, filter_function, path): if isinstance(member, str): unfiltered = self.getmember(member) - unfiltered = unfiltered.replace(r'/', os.sep) + if os.sep != r'/': + unfiltered = unfiltered.replace(r'/', os.sep) else: unfiltered = member - unfiltered.path = unfiltered.path.replace(r'/', os.sep) + if os.sep != r'/': + unfiltered.path = unfiltered.path.replace(r'/', os.sep) filtered = None try: From 4621b222a70c5db3c71f7309561616357648dcb6 Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Mon, 12 Jan 2026 20:38:23 +0100 Subject: [PATCH 7/9] Improve separator replacement --- Lib/tarfile.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 7fe6a42e67098b..4c7ebec954888a 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2487,13 +2487,11 @@ def _get_extract_tarinfo(self, member, filter_function, path): if isinstance(member, str): unfiltered = self.getmember(member) - if os.sep != r'/': - unfiltered = unfiltered.replace(r'/', os.sep) else: unfiltered = member - if os.sep != r'/': + + if os.sep != r'/': unfiltered.path = unfiltered.path.replace(r'/', os.sep) - filtered = None try: filtered = filter_function(unfiltered, path) From 9d94cc9af6971d42136c13f775cbd7e45e6b6672 Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Mon, 12 Jan 2026 21:28:24 +0100 Subject: [PATCH 8/9] Only perform path separator replacement at lstat callsite --- Lib/tarfile.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 4c7ebec954888a..9b0b66a21cb13c 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2438,7 +2438,7 @@ def extractall(self, path=".", members=None, *, numeric_owner=False, continue dirpath = os.path.join(path, tarinfo.name) try: - lstat = os.lstat(dirpath) + lstat = os.lstat(dirpath.replace(r'/', os.sep)) except FileNotFoundError: self._log_no_directory_fixup(tarinfo, 'missing') continue @@ -2489,9 +2489,7 @@ def _get_extract_tarinfo(self, member, filter_function, path): unfiltered = self.getmember(member) else: unfiltered = member - - if os.sep != r'/': - unfiltered.path = unfiltered.path.replace(r'/', os.sep) + filtered = None try: filtered = filter_function(unfiltered, path) From 88fc9150220b49f2d3d361b11ff0bba06451a0ad Mon Sep 17 00:00:00 2001 From: 0xd6cb6d73 <72226198+0xd6cb6d73@users.noreply.github.com> Date: Mon, 12 Jan 2026 21:40:43 +0100 Subject: [PATCH 9/9] Update blurb --- .../next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst b/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst index 4cef7ccd1dcf8b..562258bda17eff 100644 --- a/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst +++ b/Misc/NEWS.d/next/Library/2026-01-10-17-39-09.gh-issue-143663.9LEFAf.rst @@ -1 +1 @@ -:meth:`tarfile.TarFile._get_extract_tarinfo` now uses OS-specific separators. +:mod:`tarfile`: :meth:`TarFile.extractall` now uses OS-specific separators to call lstat. This fixes a failure case with raw Win32 paths on Windows.