Skip to content

Commit 318dbeb

Browse files
authored
Merge pull request #40 from nexB/extract-tar-return-error-messages
Return error messages in `extract_tar`
2 parents 95a6573 + 95f5606 commit 318dbeb

5 files changed

Lines changed: 90 additions & 5 deletions

File tree

src/container_inspector/utils.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,36 +97,46 @@ def extract_tar(location, target_dir, skip_symlinks=True):
9797
with tarfile.open(location) as tarball:
9898
# never extract character device, block and fifo files:
9999
# we extract dirs, files and links only
100-
to_extract = []
100+
error_messages = []
101101
for tarinfo in tarball:
102102
if TRACE: logger.debug(f'_extract_tar: {tarinfo}')
103103

104104
if tarinfo.isdev() or tarinfo.ischr() or tarinfo.isblk() or tarinfo.isfifo() or tarinfo.sparse:
105+
msg = f'_extract_tar: skipping unsupported {tarinfo} file type: block, chr, dev or sparse file'
106+
error_messages.append(msg)
105107
if TRACE:
106-
logger.debug(f'_extract_tar: skipping unsupported {tarinfo} file type: block, chr, dev or sparse file')
108+
logger.debug(msg)
107109
continue
108110

109111
if '..' in tarinfo.name:
110-
if TRACE: logger.debug(f'_extract_tar: skipping unsupported {tarinfo} with relative path')
112+
msg = f'_extract_tar: skipping unsupported {tarinfo} with relative path'
113+
error_messages.append(msg)
114+
if TRACE:
115+
logger.debug(msg)
111116
continue
112117

113118
if tarinfo.islnk() or tarinfo.issym():
114119
try:
115120
target = tarball._find_link_target(tarinfo)
116121
if not target:
122+
msg = f'_extract_tar: skipping link with missing target: {tarinfo}'
123+
error_messages.append(msg)
117124
if TRACE:
118-
logger.debug(f'_extract_tar: skipping link with missing target: {tarinfo}')
125+
logger.debug(msg)
119126
continue
120127

121128
except Exception:
122129
import traceback
130+
msg = f'_extract_tar: skipping link with missing target: {tarinfo}: {traceback.format_exc()}'
131+
error_messages.append(msg)
123132
if TRACE:
124-
logger.debug(f'_extract_tar: skipping link with missing target: {tarinfo}: {traceback.format_exc()}')
133+
logger.debug(msg)
125134
continue
126135

127136
tarinfo.mode = 0o755
128137
tarinfo.name = tarinfo.name.lstrip('/')
129138
tarball.extract(member=tarinfo, path=target_dir, set_attrs=False,)
139+
return error_messages
130140

131141

132142
def extract_tar_with_symlinks(location, target_dir):

tests/data/tar/absolute_path.tar

10 KB
Binary file not shown.

tests/data/tar/colon.tar.xz

188 Bytes
Binary file not shown.

tests/data/tar/tar_relative.tar

10 KB
Binary file not shown.

tests/test_utils.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
5+
# See https://github.com/nexB/container-inspector for support or download.
6+
# See https://aboutcode.org for more information about nexB OSS projects.
7+
#
8+
9+
import os
10+
11+
from commoncode import fileutils
12+
from commoncode import testcase
13+
14+
from container_inspector import utils
15+
16+
17+
def check_files(target_dir, expected):
18+
"""
19+
Walk test_dir.
20+
Check that all dirs are readable.
21+
Check that all files are:
22+
* non-special,
23+
* readable,
24+
* have a posix path that ends with one of the expected tuple paths.
25+
"""
26+
result = []
27+
28+
test_dir_path = fileutils.as_posixpath(target_dir)
29+
for top, _, files in os.walk(target_dir):
30+
for f in files:
31+
location = os.path.join(top, f)
32+
path = fileutils.as_posixpath(location)
33+
path = path.replace(test_dir_path, '').strip('/')
34+
result.append(path)
35+
36+
expected_content = sorted(expected)
37+
result = sorted(result)
38+
39+
assert result == expected_content
40+
41+
42+
class TestUtils(testcase.FileBasedTesting):
43+
test_data_dir = os.path.join(os.path.dirname(__file__), 'data')
44+
45+
def test_extract_tree_with_colon_in_filenames(self):
46+
expected = (
47+
'colon/libc6:amd64.list',
48+
)
49+
test_dir = self.get_test_loc('tar/colon.tar.xz')
50+
temp_dir = self.get_temp_dir()
51+
errors = utils.extract_tar(location=test_dir, target_dir=temp_dir)
52+
check_files(temp_dir, expected)
53+
assert not errors
54+
55+
def test_extract_tar_relative(self):
56+
expected = ()
57+
test_dir = self.get_test_loc('tar/tar_relative.tar')
58+
temp_dir = self.get_temp_dir()
59+
errors = utils.extract_tar(location=test_dir, target_dir=temp_dir)
60+
check_files(temp_dir, expected)
61+
assert errors
62+
for error in errors:
63+
assert 'skipping unsupported' in error
64+
assert 'with relative path' in error
65+
66+
def test_extract_tar_absolute(self):
67+
expected = (
68+
'tmp/subdir/a.txt',
69+
'tmp/subdir/b.txt',
70+
)
71+
test_dir = self.get_test_loc('tar/absolute_path.tar')
72+
temp_dir = self.get_temp_dir()
73+
errors = utils.extract_tar(location=test_dir, target_dir=temp_dir)
74+
check_files(temp_dir, expected)
75+
assert not errors

0 commit comments

Comments
 (0)