Skip to content

Commit

Permalink
Changes to expose FAT data stream extents log2timeline#597
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimmetz committed Sep 25, 2022
1 parent 1b88b32 commit 6dd71be
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 45 deletions.
1 change: 1 addition & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ disable=assignment-from-none,
locally-disabled,
locally-enabled,
logging-format-interpolation,
logging-fstring-interpolation,
metaclass-assignment,
missing-param-doc,
no-absolute-import,
Expand Down
2 changes: 1 addition & 1 deletion config/dpkg/control
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Homepage: https://github.com/log2timeline/dfvfs

Package: python3-dfvfs
Architecture: all
Depends: libbde-python3 (>= 20220121), libewf-python3 (>= 20131210), libfsapfs-python3 (>= 20220709), libfsext-python3 (>= 20220829), libfsfat-python3 (>= 20220816), libfshfs-python3 (>= 20220831), libfsntfs-python3 (>= 20211229), libfsxfs-python3 (>= 20220829), libfvde-python3 (>= 20220121), libfwnt-python3 (>= 20210717), libluksde-python3 (>= 20220121), libmodi-python3 (>= 20210405), libphdi-python3 (>= 20220228), libqcow-python3 (>= 20201213), libsigscan-python3 (>= 20191221), libsmdev-python3 (>= 20140529), libsmraw-python3 (>= 20140612), libvhdi-python3 (>= 20201014), libvmdk-python3 (>= 20140421), libvsgpt-python3 (>= 20211115), libvshadow-python3 (>= 20160109), libvslvm-python3 (>= 20160109), python3-cffi-backend (>= 1.9.1), python3-cryptography (>= 2.0.2), python3-dfdatetime (>= 20220816), python3-dtfabric (>= 20220219), python3-idna (>= 2.5), python3-pytsk3 (>= 20210419), python3-pyxattr (>= 0.7.2), python3-yaml (>= 3.10), ${misc:Depends}
Depends: libbde-python3 (>= 20220121), libewf-python3 (>= 20131210), libfsapfs-python3 (>= 20220709), libfsext-python3 (>= 20220829), libfsfat-python3 (>= 20220925), libfshfs-python3 (>= 20220831), libfsntfs-python3 (>= 20211229), libfsxfs-python3 (>= 20220829), libfvde-python3 (>= 20220121), libfwnt-python3 (>= 20210717), libluksde-python3 (>= 20220121), libmodi-python3 (>= 20210405), libphdi-python3 (>= 20220228), libqcow-python3 (>= 20201213), libsigscan-python3 (>= 20191221), libsmdev-python3 (>= 20140529), libsmraw-python3 (>= 20140612), libvhdi-python3 (>= 20201014), libvmdk-python3 (>= 20140421), libvsgpt-python3 (>= 20211115), libvshadow-python3 (>= 20160109), libvslvm-python3 (>= 20160109), python3-cffi-backend (>= 1.9.1), python3-cryptography (>= 2.0.2), python3-dfdatetime (>= 20220816), python3-dtfabric (>= 20220219), python3-idna (>= 2.5), python3-pytsk3 (>= 20210419), python3-pyxattr (>= 0.7.2), python3-yaml (>= 3.10), ${misc:Depends}
Description: Python 3 module of dfVFS
dfVFS, or Digital Forensics Virtual File System, provides read-only access to
file-system objects from various storage media types and file formats. The goal
Expand Down
2 changes: 1 addition & 1 deletion dependencies.ini
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ version_property: get_version()
[pyfsfat]
dpkg_name: libfsfat-python3
l2tbinaries_name: libfsfat
minimum_version: 20220816
minimum_version: 20220925
pypi_name: libfsfat-python
rpm_name: libfsfat-python3
version_property: get_version()
Expand Down
26 changes: 26 additions & 0 deletions dfvfs/vfs/fat_file_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from dfvfs.path import fat_path_spec
from dfvfs.resolver import resolver
from dfvfs.vfs import attribute
from dfvfs.vfs import extent
from dfvfs.vfs import fat_directory
from dfvfs.vfs import file_entry

Expand Down Expand Up @@ -160,6 +161,31 @@ def size(self):
"""int: size of the file entry in bytes or None if not available."""
return self._fsfat_file_entry.size

def GetExtents(self):
"""Retrieves the extents.
Returns:
list[Extent]: the extents.
"""
if self.entry_type != definitions.FILE_ENTRY_TYPE_FILE:
return []

extents = []
for extent_index in range(self._fsfat_file_entry.number_of_extents):
extent_offset, extent_size, extent_flags = (
self._fsfat_file_entry.get_extent(extent_index))

if extent_flags & 0x1:
extent_type = definitions.EXTENT_TYPE_SPARSE
else:
extent_type = definitions.EXTENT_TYPE_DATA

data_stream_extent = extent.Extent(
extent_type=extent_type, offset=extent_offset, size=extent_size)
extents.append(data_stream_extent)

return extents

def GetFileObject(self, data_stream_name=''):
"""Retrieves a file-like object of a specific data stream.
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ libbde-python >= 20220121
libewf-python >= 20131210
libfsapfs-python >= 20220709
libfsext-python >= 20220829
libfsfat-python >= 20220816
libfsfat-python >= 20220925
libfshfs-python >= 20220831
libfsntfs-python >= 20211229
libfsxfs-python >= 20220829
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ requires = libbde-python3 >= 20220121
libewf-python3 >= 20131210
libfsapfs-python3 >= 20220709
libfsext-python3 >= 20220829
libfsfat-python3 >= 20220816
libfsfat-python3 >= 20220925
libfshfs-python3 >= 20220831
libfsntfs-python3 >= 20211229
libfsxfs-python3 >= 20220829
Expand Down
22 changes: 10 additions & 12 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@

version_tuple = (sys.version_info[0], sys.version_info[1])
if version_tuple < (3, 7):
print((
'Unsupported Python version: {0:s}, version 3.7 or higher '
'required.').format(sys.version))
print(f'Unsupported Python version: {sys.version:s}, version 3.7 or higher '
f'required.')
sys.exit(1)

# Change PYTHONPATH to include dfvfs so that we can get the version.
Expand Down Expand Up @@ -87,8 +86,8 @@ def _make_spec_file(self):
summary = line[9:]

elif line.startswith('BuildRequires: '):
line = 'BuildRequires: {0:s}-setuptools, {0:s}-devel'.format(
python_package)
line = (f'BuildRequires: {python_package:s}-setuptools, '
f'{python_package:s}-devel')

elif line.startswith('Requires: '):
requires = line[10:]
Expand All @@ -111,7 +110,7 @@ def _make_spec_file(self):

elif line.startswith('%files'):
lines = [
'%files -n {0:s}-%{{name}}'.format(python_package),
f'%files -n {python_package:s}-%{{name}}',
'%defattr(644,root,root,755)',
'%license LICENSE',
'%doc ACKNOWLEDGEMENTS AUTHORS README']
Expand All @@ -132,17 +131,16 @@ def _make_spec_file(self):
elif line.startswith('%prep'):
in_description = False

python_spec_file.append(
'%package -n {0:s}-%{{name}}'.format(python_package))
python_summary = 'Python 3 module of {0:s}'.format(summary)
python_spec_file.append(f'%package -n {python_package:s}-%{{name}}')
python_summary = f'Python 3 module of {summary:s}'

if requires:
python_spec_file.append('Requires: {0:s}'.format(requires))
python_spec_file.append(f'Requires: {requires:s}')

python_spec_file.extend([
'Summary: {0:s}'.format(python_summary),
f'Summary: {python_summary:s}',
'',
'%description -n {0:s}-%{{name}}'.format(python_package)])
f'%description -n {python_package:s}-%{{name}}'])

python_spec_file.extend(description)

Expand Down
26 changes: 26 additions & 0 deletions tests/vfs/fat_file_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,32 @@ def testGetDataStream(self):
data_stream = file_entry.GetDataStream('')
self.assertIsNotNone(data_stream)

def testGetExtents(self):
"""Tests the GetExtents function."""
path_spec = path_spec_factory.Factory.NewPathSpec(
definitions.TYPE_INDICATOR_FAT,
identifier=self._IDENTIFIER_ANOTHER_FILE,
location='\\a_directory\\another_file',
parent=self._raw_path_spec)
file_entry = self._file_system.GetFileEntryByPathSpec(path_spec)
self.assertIsNotNone(file_entry)

extents = file_entry.GetExtents()
self.assertEqual(len(extents), 1)

self.assertEqual(extents[0].extent_type, definitions.EXTENT_TYPE_DATA)
self.assertEqual(extents[0].offset, 31232)
self.assertEqual(extents[0].size, 2048)

path_spec = path_spec_factory.Factory.NewPathSpec(
definitions.TYPE_INDICATOR_FAT, identifier=self._IDENTIFIER_A_DIRECTORY,
location='\\a_directory', parent=self._raw_path_spec)
file_entry = self._file_system.GetFileEntryByPathSpec(path_spec)
self.assertIsNotNone(file_entry)

extents = file_entry.GetExtents()
self.assertEqual(len(extents), 0)

def testGetFileEntryByPathSpec(self):
"""Tests the GetFileEntryByPathSpec function."""
path_spec = path_spec_factory.Factory.NewPathSpec(
Expand Down
52 changes: 23 additions & 29 deletions utils/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,7 @@ def _CheckPythonModule(self, dependency):
"""
module_object = self._ImportPythonModule(dependency.name)
if not module_object:
status_message = 'missing: {0:s}'.format(dependency.name)
return False, status_message
return False, f'missing: {dependency.name:s}'

if not dependency.version_property:
return True, dependency.name
Expand Down Expand Up @@ -196,13 +195,11 @@ def _CheckPythonModuleVersion(
module_version = version_method()

if not module_version:
status_message = (
'unable to determine version information for: {0:s}').format(
module_name)
return False, status_message
return False, (
f'unable to determine version information for: {module_name:s}')

# Make sure the module version is a string.
module_version = '{0!s}'.format(module_version)
module_version = f'{module_version!s}'

# Split the version string and convert every digit into an integer.
# A string compare of both version strings will yield an incorrect result.
Expand All @@ -217,42 +214,38 @@ def _CheckPythonModuleVersion(
module_version_map = list(
map(int, self._VERSION_SPLIT_REGEX.split(module_version)))
except ValueError:
status_message = 'unable to parse module version: {0:s} {1:s}'.format(
module_name, module_version)
return False, status_message
return False, (
f'unable to parse module version: {module_name:s} {module_version:s}')

if minimum_version:
try:
minimum_version_map = list(
map(int, self._VERSION_SPLIT_REGEX.split(minimum_version)))
except ValueError:
status_message = 'unable to parse minimum version: {0:s} {1:s}'.format(
module_name, minimum_version)
return False, status_message
return False, (
f'unable to parse minimum version: {module_name:s} '
f'{minimum_version:s}')

if module_version_map < minimum_version_map:
status_message = (
'{0:s} version: {1!s} is too old, {2!s} or later required').format(
module_name, module_version, minimum_version)
return False, status_message
return False, (
f'{module_name:s} version: {module_version!s} is too old, '
f'{minimum_version!s} or later required')

if maximum_version:
try:
maximum_version_map = list(
map(int, self._VERSION_SPLIT_REGEX.split(maximum_version)))
except ValueError:
status_message = 'unable to parse maximum version: {0:s} {1:s}'.format(
module_name, maximum_version)
return False, status_message
return False, (
f'unable to parse maximum version: {module_name:s} '
f'{maximum_version:s}')

if module_version_map > maximum_version_map:
status_message = (
'{0:s} version: {1!s} is too recent, {2!s} or earlier '
'required').format(module_name, module_version, maximum_version)
return False, status_message
return False, (
f'{module_name:s} version: {module_version!s} is too recent, '
f'{maximum_version!s} or earlier required')

status_message = '{0:s} version: {1!s}'.format(module_name, module_version)
return True, status_message
return True, f'{module_name:s} version: {module_version!s}'

def _ImportPythonModule(self, module_name):
"""Imports a Python module.
Expand Down Expand Up @@ -292,10 +285,10 @@ def _PrintCheckDependencyStatus(
else:
status_indicator = '[FAILURE]'

print('{0:s}\t{1:s}'.format(status_indicator, status_message))
print(f'{status_indicator:s}\t{status_message:s}')

elif verbose_output:
print('[OK]\t\t{0:s}'.format(status_message))
print(f'[OK]\t\t{status_message:s}')

def CheckDependencies(self, verbose_output=True):
"""Checks the availability of the dependencies.
Expand Down Expand Up @@ -349,7 +342,8 @@ def CheckTestDependencies(self, verbose_output=True):
continue

result, status_message = self._CheckPythonModule(dependency)
if not result:

if not result and not dependency.is_optional:
check_result = False

self._PrintCheckDependencyStatus(
Expand Down

0 comments on commit 6dd71be

Please sign in to comment.