"""Helper functions for EWF image support."""
from dfvfs.lib import errors
from dfvfs.path import factory as path_spec_factory
[docs]
def EWFGlobPathSpec(file_system, path_spec):
"""Globs for path specifications according to the EWF naming schema.
Args:
file_system (FileSystem): file system.
path_spec (PathSpec): path specification.
Returns:
list[PathSpec]: path specifications that match the glob.
Raises:
PathSpecError: if the path specification is invalid.
RuntimeError: if the maximum number of supported segment files is
reached.
"""
if not path_spec.HasParent():
raise errors.PathSpecError("Unsupported path specification without parent.")
parent_path_spec = path_spec.parent
parent_location = getattr(parent_path_spec, "location", None)
if not parent_location:
raise errors.PathSpecError(
"Unsupported parent path specification without location."
)
parent_location, _, segment_extension = parent_location.rpartition(".")
segment_extension_start = segment_extension[0]
segment_extension_length = len(segment_extension)
if (
segment_extension_length not in [3, 4]
or not segment_extension.endswith("01")
or (
segment_extension_length == 3
and segment_extension_start not in ["E", "e", "s"]
)
or (segment_extension_length == 4 and not segment_extension.startswith("Ex"))
):
raise errors.PathSpecError(
(
f"Unsupported parent path specification invalid segment file "
f"extension: {segment_extension:s}"
)
)
segment_number = 1
segment_files = []
while True:
segment_location = f"{parent_location:s}.{segment_extension:s}"
# Note that we don't want to set the keyword arguments when not used
# because the path specification base class will check for unused
# keyword arguments and raise.
kwargs = path_spec_factory.Factory.GetProperties(parent_path_spec)
kwargs["location"] = segment_location
if parent_path_spec.parent is not None:
kwargs["parent"] = parent_path_spec.parent
segment_path_spec = path_spec_factory.Factory.NewPathSpec(
parent_path_spec.type_indicator, **kwargs
)
if not file_system.FileEntryExistsByPathSpec(segment_path_spec):
break
segment_files.append(segment_path_spec)
segment_number += 1
if segment_number <= 99:
if segment_extension_length == 3:
segment_extension = f"{segment_extension_start:s}{segment_number:02d}"
elif segment_extension_length == 4:
segment_extension = f"{segment_extension_start:s}x{segment_number:02d}"
else:
segment_index = segment_number - 100
if segment_extension_start in ["e", "s"]:
letter_offset = ord("a")
else:
letter_offset = ord("A")
segment_index, remainder = divmod(segment_index, 26)
third_letter = chr(letter_offset + remainder)
segment_index, remainder = divmod(segment_index, 26)
second_letter = chr(letter_offset + remainder)
first_letter = chr(ord(segment_extension_start) + segment_index)
if first_letter in ["[", "{"]:
raise RuntimeError("Unsupported number of segment files.")
if segment_extension_length == 3:
segment_extension = f"{first_letter:s}{second_letter:s}{third_letter:s}"
elif segment_extension_length == 4:
segment_extension = (
f"{first_letter:s}x{second_letter:s}{third_letter:s}"
)
return segment_files