Skip to content

Commit

Permalink
Added fsext back-end log2timeline#464
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimmetz committed Aug 10, 2020
1 parent 05d0d5c commit add269a
Show file tree
Hide file tree
Showing 15 changed files with 1,383 additions and 0 deletions.
8 changes: 8 additions & 0 deletions dependencies.ini
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ pypi_name: libfsapfs-python
rpm_name: libfsapfs-python3
version_property: get_version()

[pyfsext]
dpkg_name: libfsext-python3
l2tbinaries_name: libfsext
minimum_version: 20200810
pypi_name: libfsext-python
rpm_name: libfsext-python3
version_property: get_version()

[pyfsntfs]
dpkg_name: libfsntfs-python3
l2tbinaries_name: libfsntfs
Expand Down
1 change: 1 addition & 0 deletions dfvfs/analyzer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from dfvfs.analyzer import bzip2_analyzer_helper
from dfvfs.analyzer import cpio_analyzer_helper
from dfvfs.analyzer import ewf_analyzer_helper
from dfvfs.analyzer import ext_analyzer_helper
from dfvfs.analyzer import fvde_analyzer_helper
from dfvfs.analyzer import gzip_analyzer_helper
from dfvfs.analyzer import lvm_analyzer_helper
Expand Down
36 changes: 36 additions & 0 deletions dfvfs/analyzer/ext_analyzer_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
"""The EXT format analyzer helper implementation."""

from __future__ import unicode_literals

from dfvfs.analyzer import analyzer
from dfvfs.analyzer import analyzer_helper
from dfvfs.analyzer import specification
from dfvfs.lib import definitions


class EXTAnalyzerHelper(analyzer_helper.AnalyzerHelper):
"""EXT analyzer helper."""

FORMAT_CATEGORIES = frozenset([
definitions.FORMAT_CATEGORY_FILE_SYSTEM])

TYPE_INDICATOR = definitions.TYPE_INDICATOR_EXT

def GetFormatSpecification(self):
"""Retrieves the format specification.
Returns:
FormatSpecification: format specification or None if the format cannot
be defined by a specification object.
"""
format_specification = specification.FormatSpecification(
self.type_indicator)

# EXT file system signature.
format_specification.AddNewSignature(b'\x53\xef', offset=56)

return format_specification


analyzer.Analyzer.RegisterHelper(EXTAnalyzerHelper())
141 changes: 141 additions & 0 deletions dfvfs/file_io/ext_file_io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# -*- coding: utf-8 -*-
"""The Extended File System (EXT) file-like object implementation."""

from __future__ import unicode_literals

import os

from dfvfs.file_io import file_io
from dfvfs.lib import errors
from dfvfs.resolver import resolver


class EXTFile(file_io.FileIO):
"""File-like object using pyfsext.file_entry"""

def __init__(self, resolver_context):
"""Initializes a file-like object.
Args:
resolver_context (Context): resolver context.
"""
super(EXTFile, self).__init__(resolver_context)
self._file_system = None
self._fsext_file_entry = None

def _Close(self):
"""Closes the file-like object."""
self._fsext_file_entry = None

self._file_system.Close()
self._file_system = None

def _Open(self, path_spec=None, mode='rb'):
"""Opens the file-like object defined by path specification.
Args:
path_spec (PathSpec): path specification.
mode (Optional[str]): file access mode.
Raises:
AccessError: if the access to open the file was denied.
IOError: if the file-like object could not be opened.
NotSupported: if a data stream, like the resource or named fork, is
requested to be opened.
OSError: if the file-like object could not be opened.
PathSpecError: if the path specification is incorrect.
ValueError: if the path specification is invalid.
"""
if not path_spec:
raise ValueError('Missing path specification.')

data_stream = getattr(path_spec, 'data_stream', None)
if data_stream:
raise errors.NotSupported(
'Open data stream: {0:s} not supported.'.format(data_stream))

self._file_system = resolver.Resolver.OpenFileSystem(
path_spec, resolver_context=self._resolver_context)

file_entry = self._file_system.GetFileEntryByPathSpec(path_spec)
if not file_entry:
raise IOError('Unable to open file entry.')

fsext_file_entry = file_entry.GetEXTFileEntry()
if not fsext_file_entry:
raise IOError('Unable to open EXT file entry.')

self._fsext_file_entry = fsext_file_entry

# Note: that the following functions do not follow the style guide
# because they are part of the file-like object interface.
# pylint: disable=invalid-name

def read(self, size=None):
"""Reads a byte string from the file-like object at the current offset.
The function will read a byte string of the specified size or
all of the remaining data if no size was specified.
Args:
size (Optional[int]): number of bytes to read, where None is all
remaining data.
Returns:
bytes: data read.
Raises:
IOError: if the read failed.
OSError: if the read failed.
"""
if not self._is_open:
raise IOError('Not opened.')

return self._fsext_file_entry.read(size=size)

def seek(self, offset, whence=os.SEEK_SET):
"""Seeks to an offset within the file-like object.
Args:
offset (int): offset to seek to.
whence (Optional(int)): value that indicates whether offset is an absolute
or relative position within the file.
Raises:
IOError: if the seek failed.
OSError: if the seek failed.
"""
if not self._is_open:
raise IOError('Not opened.')

self._fsext_file_entry.seek(offset, whence)

def get_offset(self):
"""Retrieves the current offset into the file-like object.
Return:
int: current offset into the file-like object.
Raises:
IOError: if the file-like object has not been opened.
OSError: if the file-like object has not been opened.
"""
if not self._is_open:
raise IOError('Not opened.')

return self._fsext_file_entry.get_offset()

def get_size(self):
"""Retrieves the size of the file-like object.
Returns:
int: size of the file-like object data.
Raises:
IOError: if the file-like object has not been opened.
OSError: if the file-like object has not been opened.
"""
if not self._is_open:
raise IOError('Not opened.')

return self._fsext_file_entry.get_size()
3 changes: 3 additions & 0 deletions dfvfs/lib/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
TYPE_INDICATOR_ENCODED_STREAM = 'ENCODED_STREAM'
TYPE_INDICATOR_ENCRYPTED_STREAM = 'ENCRYPTED_STREAM'
TYPE_INDICATOR_EWF = 'EWF'
TYPE_INDICATOR_EXT = 'EXT'
TYPE_INDICATOR_FAKE = 'FAKE'
TYPE_INDICATOR_FVDE = 'FVDE'
TYPE_INDICATOR_GZIP = 'GZIP'
Expand Down Expand Up @@ -74,6 +75,7 @@

FILE_SYSTEM_TYPE_INDICATORS = frozenset([
TYPE_INDICATOR_APFS,
TYPE_INDICATOR_EXT,
TYPE_INDICATOR_FAKE,
TYPE_INDICATOR_NTFS,
TYPE_INDICATOR_TSK])
Expand All @@ -92,6 +94,7 @@
TYPE_INDICATOR_VSHADOW])

# The preferred back-ends.
PREFERRED_EXT_BACK_END = TYPE_INDICATOR_TSK
PREFERRED_NTFS_BACK_END = TYPE_INDICATOR_NTFS

# The NTFS attribute types.
Expand Down
1 change: 1 addition & 0 deletions dfvfs/path/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from dfvfs.path import encoded_stream_path_spec
from dfvfs.path import encrypted_stream_path_spec
from dfvfs.path import ewf_path_spec
from dfvfs.path import ext_path_spec
from dfvfs.path import fake_path_spec
from dfvfs.path import fvde_path_spec
from dfvfs.path import gzip_path_spec
Expand Down
55 changes: 55 additions & 0 deletions dfvfs/path/ext_path_spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
"""The EXT path specification implementation."""

from __future__ import unicode_literals

from dfvfs.lib import definitions
from dfvfs.path import factory
from dfvfs.path import path_spec


class EXTPathSpec(path_spec.PathSpec):
"""EXT path specification implementation.
Attributes:
inode (int): inode.
location (str): location.
"""

TYPE_INDICATOR = definitions.TYPE_INDICATOR_EXT

def __init__(
self, inode=None, location=None, parent=None, **kwargs):
"""Initializes a path specification.
Note that an EXT path specification must have a parent.
Args:
inode (Optional[int]): inode.
location (Optional[str]): location.
parent (Optional[PathSpec]): parent path specification.
Raises:
ValueError: when parent or both inode and location are not set.
"""
if (not inode and not location) or not parent:
raise ValueError('Missing inode and location, or parent value.')

super(EXTPathSpec, self).__init__(parent=parent, **kwargs)
self.inode = inode
self.location = location

@property
def comparable(self):
"""str: comparable representation of the path specification."""
string_parts = []

if self.inode is not None:
string_parts.append('inode: {0:d}'.format(self.inode))
if self.location is not None:
string_parts.append('location: {0:s}'.format(self.location))

return self._GetComparable(sub_comparable_string=', '.join(string_parts))


factory.Factory.RegisterPathSpec(EXTPathSpec)
41 changes: 41 additions & 0 deletions dfvfs/resolver_helpers/ext_resolver_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
"""The EXT path specification resolver helper implementation."""

from __future__ import unicode_literals

from dfvfs.file_io import ext_file_io
from dfvfs.lib import definitions
from dfvfs.resolver_helpers import manager
from dfvfs.resolver_helpers import resolver_helper
from dfvfs.vfs import ext_file_system


class EXTResolverHelper(resolver_helper.ResolverHelper):
"""EXT resolver helper."""

TYPE_INDICATOR = definitions.TYPE_INDICATOR_EXT

def NewFileObject(self, resolver_context):
"""Creates a new file-like object.
Args:
resolver_context (Context): resolver context.
Returns:
FileIO: file-like object.
"""
return ext_file_io.EXTFile(resolver_context)

def NewFileSystem(self, resolver_context):
"""Creates a new file system object.
Args:
resolver_context (Context): resolver context.
Returns:
FileSystem: file system.
"""
return ext_file_system.EXTFileSystem(resolver_context)


manager.ResolverHelperManager.RegisterHelper(EXTResolverHelper())
Loading

0 comments on commit add269a

Please sign in to comment.