Skip to content

Commit b612154

Browse files
committed
Pass filter_function to TarFile._extract_one() during .extract()
1 parent e0f7c10 commit b612154

2 files changed

Lines changed: 102 additions & 1 deletion

File tree

Lib/tarfile.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2495,7 +2495,8 @@ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False,
24952495
tarinfo, unfiltered = self._get_extract_tarinfo(
24962496
member, filter_function, path)
24972497
if tarinfo is not None:
2498-
self._extract_one(tarinfo, path, set_attrs, numeric_owner)
2498+
self._extract_one(tarinfo, path, set_attrs, numeric_owner,
2499+
filter_function=filter_function)
24992500

25002501
def _get_extract_tarinfo(self, member, filter_function, path):
25012502
"""Get (filtered, unfiltered) TarInfos from *member*

Lib/test/test_tarfile.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33
import os
44
import io
5+
import tempfile
56
from hashlib import sha256
67
from contextlib import contextmanager, ExitStack
78
from random import Random
@@ -4304,6 +4305,105 @@ def test_chmod_outside_dir(self):
43044305
st_mode = cc.outerdir.stat().st_mode
43054306
self.assertNotEqual(st_mode & 0o777, 0o777)
43064307

4308+
@symlink_test
4309+
def test_chown_links_on_extract(self):
4310+
for link_type in (tarfile.SYMTYPE, tarfile.LNKTYPE):
4311+
buf = io.BytesIO()
4312+
with tarfile.open(fileobj=buf, mode='w') as tf:
4313+
# Create a regular file entry with uid/gid
4314+
info = tarfile.TarInfo(name="test.txt")
4315+
info.uid = 1337
4316+
info.gid = 1337
4317+
info.uname = ""
4318+
info.gname = ""
4319+
info.mode = 0o755
4320+
tf.addfile(info, io.BytesIO(b""))
4321+
4322+
# Create a link to the normal file.
4323+
link_info = tarfile.TarInfo(name="link")
4324+
link_info.type = link_type
4325+
link_info.linkname = "test.txt"
4326+
link_info.uid = 1337
4327+
link_info.gid = 1337
4328+
link_info.uname = ""
4329+
link_info.gname = ""
4330+
link_info.mode = 0o644
4331+
tf.addfile(link_info)
4332+
4333+
buf.seek(0)
4334+
with (
4335+
self.subTest(f"type={link_type!r}"),
4336+
tempfile.TemporaryDirectory() as tmpdir,
4337+
tarfile.open(fileobj=buf) as tar,
4338+
unittest.mock.patch("os.chown") as mock_chown,
4339+
unittest.mock.patch("os.lchown") as mock_lchown,
4340+
unittest.mock.patch("os.geteuid") as mock_geteuid,
4341+
):
4342+
# Set UID to 0 so chown() is attempted.
4343+
mock_geteuid.return_value = 0
4344+
tar.extract("link", path=tmpdir, filter='data')
4345+
extract_path = os.path.join(tmpdir, "link")
4346+
4347+
if link_type == tarfile.SYMTYPE:
4348+
mock_chown.assert_not_called()
4349+
mock_lchown.assert_called_once_with(extract_path, -1, -1)
4350+
else:
4351+
mock_chown.assert_has_calls([
4352+
unittest.mock.call(extract_path, -1, -1),
4353+
unittest.mock.call(extract_path, -1, -1)
4354+
])
4355+
mock_lchown.assert_not_called()
4356+
4357+
@symlink_test
4358+
def test_chown_links_on_extractall(self):
4359+
for link_type in (tarfile.SYMTYPE, tarfile.LNKTYPE):
4360+
buf = io.BytesIO()
4361+
with tarfile.open(fileobj=buf, mode='w') as tf:
4362+
# Create a regular file entry with uid/gid
4363+
info = tarfile.TarInfo(name="test.txt")
4364+
info.uid = 1337
4365+
info.gid = 1337
4366+
info.uname = ""
4367+
info.gname = ""
4368+
info.mode = 0o755
4369+
tf.addfile(info, io.BytesIO(b""))
4370+
4371+
# Create a link to the normal file.
4372+
link_info = tarfile.TarInfo(name="link")
4373+
link_info.type = link_type
4374+
link_info.linkname = "test.txt"
4375+
link_info.uid = 1337
4376+
link_info.gid = 1337
4377+
link_info.uname = ""
4378+
link_info.gname = ""
4379+
link_info.mode = 0o644
4380+
tf.addfile(link_info)
4381+
4382+
buf.seek(0)
4383+
with (
4384+
self.subTest(f"type={link_type!r}"),
4385+
tempfile.TemporaryDirectory() as tmpdir,
4386+
tarfile.open(fileobj=buf) as tar,
4387+
unittest.mock.patch("os.chown") as mock_chown,
4388+
unittest.mock.patch("os.lchown") as mock_lchown,
4389+
unittest.mock.patch("os.geteuid") as mock_geteuid,
4390+
):
4391+
# Set UID to 0 so chown() is attempted.
4392+
mock_geteuid.return_value = 0
4393+
tar.extractall(path=tmpdir, filter='data')
4394+
extract_link_path = os.path.join(tmpdir, "link")
4395+
extract_file_path = os.path.join(tmpdir, "test.txt")
4396+
4397+
if link_type == tarfile.SYMTYPE:
4398+
mock_chown.assert_called_once_with(extract_file_path, -1, -1)
4399+
mock_lchown.assert_called_once_with(extract_link_path, -1, -1)
4400+
else:
4401+
mock_chown.assert_has_calls([
4402+
unittest.mock.call(extract_file_path, -1, -1),
4403+
unittest.mock.call(extract_link_path, -1, -1)
4404+
])
4405+
mock_lchown.assert_not_called()
4406+
43074407
def test_link_fallback_normalizes(self):
43084408
# Make sure hardlink fallbacks work for non-normalized paths for all
43094409
# filters

0 commit comments

Comments
 (0)