Skip to content

Commit 867566a

Browse files
kk7dsmarkgoddard
authored andcommitted
Add file format detection to format_inspector
Change-Id: If0a4251465507be035ffaf9d855299611637cfa9 (cherry picked from commit 79271eaa5c742a1741321198c43807857fb6ed94) (cherry picked from commit e1c36248c7660dea1bedfa8f1c0711a4b97d279c) (cherry picked from commit d54121d6a937fd50aae1018aede228a3c0985dce)
1 parent cc47b71 commit 867566a

File tree

2 files changed

+43
-67
lines changed

2 files changed

+43
-67
lines changed

glance/common/format_inspector.py

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -872,20 +872,52 @@ def close(self):
872872
self._source.close()
873873

874874

875+
ALL_FORMATS = {
876+
'raw': FileInspector,
877+
'qcow2': QcowInspector,
878+
'vhd': VHDInspector,
879+
'vhdx': VHDXInspector,
880+
'vmdk': VMDKInspector,
881+
'vdi': VDIInspector,
882+
'qed': QEDInspector,
883+
}
884+
885+
875886
def get_inspector(format_name):
876887
"""Returns a FormatInspector class based on the given name.
877888
878889
:param format_name: The name of the disk_format (raw, qcow2, etc).
879890
:returns: A FormatInspector or None if unsupported.
880891
"""
881-
formats = {
882-
'raw': FileInspector,
883-
'qcow2': QcowInspector,
884-
'vhd': VHDInspector,
885-
'vhdx': VHDXInspector,
886-
'vmdk': VMDKInspector,
887-
'vdi': VDIInspector,
888-
'qed': QEDInspector,
889-
}
890-
891-
return formats.get(format_name)
892+
893+
return ALL_FORMATS.get(format_name)
894+
895+
896+
def detect_file_format(filename):
897+
"""Attempts to detect the format of a file.
898+
899+
This runs through a file one time, running all the known inspectors in
900+
parallel. It stops reading the file once one of them matches or all of
901+
them are sure they don't match.
902+
903+
Returns the FileInspector that matched, if any. None if 'raw'.
904+
"""
905+
inspectors = {k: v() for k, v in ALL_FORMATS.items()}
906+
with open(filename, 'rb') as f:
907+
for chunk in chunked_reader(f):
908+
for format, inspector in list(inspectors.items()):
909+
try:
910+
inspector.eat_chunk(chunk)
911+
except ImageFormatError:
912+
# No match, so stop considering this format
913+
inspectors.pop(format)
914+
continue
915+
if (inspector.format_match and inspector.complete and
916+
format != 'raw'):
917+
# First complete match (other than raw) wins
918+
return inspector
919+
if all(i.complete for i in inspectors.values()):
920+
# If all the inspectors are sure they are not a match, avoid
921+
# reading to the end of the file to settle on 'raw'.
922+
break
923+
return inspectors['raw']

glance/tests/unit/common/test_format_inspector.py

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -208,62 +208,6 @@ def test_qcow2_feature_flag_checks(self):
208208
data[0x4F] = 0x80
209209
self.assertTrue(inspector.has_unknown_features)
210210

211-
def test_vmdk_safety_checks(self):
212-
region = format_inspector.CaptureRegion(0, 0)
213-
inspector = format_inspector.VMDKInspector()
214-
inspector.new_region('descriptor', region)
215-
216-
# This should be a legit VMDK descriptor which comments, blank lines,
217-
# an extent, some ddb content, and some header values.
218-
legit_desc = ['# This is a comment',
219-
'',
220-
' ',
221-
'createType=monolithicSparse',
222-
'RW 1234 SPARSE "foo.vmdk"',
223-
'ddb.adapterType = "MFM',
224-
'# EOF']
225-
region.data = ('\n'.join(legit_desc)).encode('ascii')
226-
region.length = len(region.data)
227-
self.assertTrue(inspector.safety_check())
228-
229-
# Any of these lines should trigger an error indicating that there is
230-
# something in the descriptor we don't understand
231-
bad_lines = [
232-
'#\U0001F4A9',
233-
'header Name=foo',
234-
'foo bar',
235-
'WR 123 SPARSE "foo.vmdk"',
236-
]
237-
238-
for bad_line in bad_lines:
239-
# Encode as UTF-8 purely so we can test that anything non-ASCII
240-
# will trigger the decode check
241-
region.data = bad_line.encode('utf-8')
242-
region.length = len(region.data)
243-
self.assertRaisesRegex(format_inspector.ImageFormatError,
244-
'Invalid VMDK descriptor',
245-
inspector.safety_check)
246-
247-
# Extents with slashes in the name fail the safety check
248-
region.data = b'RW 123 SPARSE "/etc/shadow"'
249-
region.length = len(region.data)
250-
self.assertFalse(inspector.safety_check())
251-
252-
# A descriptor that specifies no extents fails the safety check
253-
region.data = b'# Nothing'
254-
region.length = len(region.data)
255-
self.assertFalse(inspector.safety_check())
256-
257-
def test_vmdk_reject_footer(self):
258-
data = struct.pack('<4sIIQQQQIQQ', b'KDMV', 3, 0, 0, 0, 0, 1, 0, 0,
259-
format_inspector.VMDKInspector.GD_AT_END)
260-
inspector = format_inspector.VMDKInspector()
261-
inspector.region('header').data = data
262-
inspector.region('header').length = len(data)
263-
self.assertRaisesRegex(format_inspector.ImageFormatError,
264-
'footer',
265-
inspector.post_process)
266-
267211
def test_vdi(self):
268212
self._test_format('vdi')
269213

0 commit comments

Comments
 (0)