Skip to content

Commit

Permalink
Added LVM support log2timeline#89
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimmetz committed Jan 30, 2016
1 parent 5b740aa commit 41a5fb6
Show file tree
Hide file tree
Showing 42 changed files with 1,532 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ language: python
python:
- "2.7"
before_install:
- if test `uname -s` = 'Linux'; then sudo add-apt-repository ppa:gift/dev -y && sudo apt-get update -q && sudo apt-get install libbde-python libewf-python libfsntfs-python libqcow-python libsigscan-python libsmdev-python libsmraw-python libtsk libvhdi-python libvmdk-python libvshadow-python python-construct python-coverage python-coveralls python-crypto python-docopt python-protobuf python-six pytsk3; fi
- if test `uname -s` = 'Linux'; then sudo add-apt-repository ppa:gift/dev -y && sudo apt-get update -q && sudo apt-get install libbde-python libewf-python libfsntfs-python libqcow-python libsigscan-python libsmdev-python libsmraw-python libtsk libvhdi-python libvmdk-python libvshadow-python libvslvm-python python-construct python-coverage python-coveralls python-crypto python-docopt python-protobuf python-six pytsk3; fi
script:
- ./run_tests.py
- ./setup.py build
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ install:
- ps: $Output = Invoke-Expression -Command "& 'C:\\Program Files\\7-Zip\\7z.exe' -y -oC:\\Projects\\ x C:\\Projects\\sqlite-dll-win32-x86-3080803.zip 2>&1"
- cmd: copy C:\Projects\sqlite3.dll C:\Python27\DLLs\
- cmd: git clone https://github.com/log2timeline/l2tdevtools.git && move l2tdevtools ..\
- cmd: mkdir dependencies && set PYTHONPATH=..\l2tdevtools && "%PYTHON%\\python.exe" ..\l2tdevtools\tools\update.py --download-directory dependencies --machine-type x86 --msi-targetdir "%PYTHON%" construct libfsntfs-python libqcow-python libsigscan-python libsmdev-python libsmraw-python libvhdi-python libvmdk-python libvshadow-python protobuf pybde pycrypto pyewf python-gflags pytsk3 six
- cmd: mkdir dependencies && set PYTHONPATH=..\l2tdevtools && "%PYTHON%\\python.exe" ..\l2tdevtools\tools\update.py --download-directory dependencies --machine-type x86 --msi-targetdir "%PYTHON%" construct libfsntfs-python libqcow-python libsigscan-python libsmdev-python libsmraw-python libvhdi-python libvmdk-python libvshadow-python libvslvm-python protobuf pybde pycrypto pyewf python-gflags pytsk3 six

build: off

Expand Down
15 changes: 2 additions & 13 deletions config/dpkg/control
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,18 @@ Source: dfvfs
Section: python
Priority: extra
Maintainer: dfVFS development team <[email protected]>
Build-Depends: debhelper (>= 7), python-all (>= 2.7~), python-setuptools, python3-all (>= 3.2~), python3-setuptools
Build-Depends: debhelper (>= 7), python-all (>= 2.7~), python-setuptools
Standards-Version: 3.9.5
X-Python-Version: >= 2.7
X-Python3-Version: >= 3.2
Homepage: https://github.com/log2timeline/dfvfs/

Package: python-dfvfs
Architecture: all
Depends: libprotobuf7|libprotobuf8, libbde-python, libewf-python, libfsntfs-python, libqcow-python, libsigscan-python, libsmdev-python, libsmraw-python, libvhdi-python, libvmdk-python, libvshadow-python, python-construct, python-pycrypto, python-protobuf, python-six, pytsk3, ${python:Depends}, ${misc:Depends}
Depends: libprotobuf7|libprotobuf8, libbde-python, libewf-python, libfsntfs-python, libqcow-python, libsigscan-python, libsmdev-python, libsmraw-python, libvhdi-python, libvmdk-python, libvshadow-python, libvslvm-python, python-construct, python-pycrypto, python-protobuf, python-six, pytsk3, ${python:Depends}, ${misc:Depends}
Description: Digital Forensics Virtual File System (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
of dfVFS is to provide a generic interface for accessing file-system objects,
for which it uses several back-ends that provide the actual implementation of
the various storage media types, volume systems and file systems.

Package: python3-dfvfs
Architecture: all
Depends: libprotobuf7|libprotobuf8, libbde-python3, libewf-python3, libfsntfs-python3, libqcow-python3, libsigscan-python3, libsmdev-python3, libsmraw-python3, libvhdi-python3, libvmdk-python3, libvshadow-python3, python3-contruct, python3-pycrypto, python3-protobuf, python3-six, pytsk3, ${python:Depends}, ${misc:Depends}
Description: Digital Forensics Virtual File System (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
of dfVFS is to provide a generic interface for accessing file-system objects,
for which it uses several back-ends that provide the actual implementation of
the various storage media types, volume systems and file systems.

1 change: 1 addition & 0 deletions dfvfs/analyzer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from dfvfs.analyzer import bzip2_analyzer_helper
from dfvfs.analyzer import ewf_analyzer_helper
from dfvfs.analyzer import gzip_analyzer_helper
from dfvfs.analyzer import lvm_analyzer_helper
from dfvfs.analyzer import ntfs_analyzer_helper
from dfvfs.analyzer import qcow_analyzer_helper
from dfvfs.analyzer import tar_analyzer_helper
Expand Down
33 changes: 33 additions & 0 deletions dfvfs/analyzer/lvm_analyzer_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
"""The Logical Volume Manager (LVM) format analyzer helper implementation."""

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


class LVMAnalyzerHelper(analyzer_helper.AnalyzerHelper):
"""Class that implements the LVM analyzer helper."""

FORMAT_CATEGORIES = frozenset([
definitions.FORMAT_CATEGORY_VOLUME_SYSTEM])

TYPE_INDICATOR = definitions.TYPE_INDICATOR_LVM

def GetFormatSpecification(self):
"""Retrieves the format specification."""
format_specification = specification.FormatSpecification(
self.type_indicator)

# LVM signature.
format_specification.AddNewSignature(b'LABELONE', offset=0)
format_specification.AddNewSignature(b'LABELONE', offset=512)
format_specification.AddNewSignature(b'LABELONE', offset=1024)
format_specification.AddNewSignature(b'LABELONE', offset=1536)

return format_specification


# Register the analyzer helpers with the analyzer.
analyzer.Analyzer.RegisterHelper(LVMAnalyzerHelper())
3 changes: 2 additions & 1 deletion dfvfs/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
u'pysmraw': 20140612,
u'pyvhdi': 20131210,
u'pyvmdk': 20140421,
u'pyvshadow': 20160110}
u'pyvshadow': 20160110,
u'pyvslvm': 20160109}

# The tuple values are:
# module_name, version_attribute_name, minimum_version, maximum_version
Expand Down
130 changes: 130 additions & 0 deletions dfvfs/file_io/lvm_file_io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
"""The Logical Volume Manager (LVM) file-like object implementation."""

import os

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


class LVMFile(file_io.FileIO):
"""Class that implements a file-like object using pyvslvm."""

def __init__(self, resolver_context):
"""Initializes the file-like object.
Args:
resolver_context: the resolver context (instance of Context).
"""
super(LVMFile, self).__init__(resolver_context)
self._file_system = None
self._vslvm_logical_volume = None

def _Close(self):
"""Closes the file-like object.
Raises:
IOError: if the close failed.
"""
self._vslvm_logical_volume = 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: optional path specification (instance of PathSpec).
mode: optional file access mode. The default is 'rb' read-only binary.
Raises:
AccessError: if the access to open the file was denied.
IOError: 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(u'Missing path specfication.')

volume_index = lvm.LVMPathSpecGetVolumeIndex(path_spec)
if volume_index is None:
raise errors.PathSpecError(
u'Unable to retrieve volume index from path specification.')

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

if (volume_index < 0 or
volume_index >= vslvm_volume_group.number_of_logical_volumes):
raise errors.PathSpecError((
u'Unable to retrieve LVM logical volume index: {0:d} from path '
u'specification.').format(volume_index))

self._vslvm_logical_volume = vslvm_volume_group.get_logical_volume(
volume_index)

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

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 integer value containing the number of bytes to read.
Default is all remaining data (None).
Returns:
A byte string containing the data read.
Raises:
IOError: if the read failed.
"""
if not self._is_open:
raise IOError(u'Not opened.')

return self._vslvm_logical_volume.read(size)

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

self._vslvm_logical_volume.seek(offset, whence)

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

return self._vslvm_logical_volume.get_offset()

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

return self._vslvm_logical_volume.size
2 changes: 2 additions & 0 deletions dfvfs/lib/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
TYPE_INDICATOR_EWF = u'EWF'
TYPE_INDICATOR_FAKE = u'FAKE'
TYPE_INDICATOR_GZIP = u'GZIP'
TYPE_INDICATOR_LVM = u'LVM'
TYPE_INDICATOR_MOUNT = u'MOUNT'
TYPE_INDICATOR_NTFS = u'NTFS'
TYPE_INDICATOR_OS = u'OS'
Expand Down Expand Up @@ -53,6 +54,7 @@
TYPE_INDICATOR_VMDK])

VOLUME_SYSTEM_TYPE_INDICATORS = frozenset([
TYPE_INDICATOR_LVM,
TYPE_INDICATOR_TSK_PARTITION,
TYPE_INDICATOR_VSHADOW])

Expand Down
28 changes: 28 additions & 0 deletions dfvfs/lib/lvm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""Helper functions for Logical Volume Manager (LVM) support."""


def LVMPathSpecGetVolumeIndex(path_spec):
"""Retrieves the volume index from the path specification.
Args:
path_spec: the path specification (instance of PathSpec).
"""
volume_index = getattr(path_spec, u'volume_index', None)

if volume_index is None:
location = getattr(path_spec, u'location', None)

if location is None or not location.startswith(u'/lvm'):
return

volume_index = None
try:
volume_index = int(location[4:], 10) - 1
except ValueError:
pass

if volume_index is None or volume_index < 0:
return

return volume_index
1 change: 1 addition & 0 deletions dfvfs/path/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from dfvfs.path import ewf_path_spec
from dfvfs.path import fake_path_spec
from dfvfs.path import gzip_path_spec
from dfvfs.path import lvm_path_spec
from dfvfs.path import mount_path_spec
from dfvfs.path import ntfs_path_spec
from dfvfs.path import os_path_spec
Expand Down
53 changes: 53 additions & 0 deletions dfvfs/path/lvm_path_spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
"""The Logical Volume Manager (LVM) path specification implementation."""

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


class LVMPathSpec(path_spec.PathSpec):
"""Class that implements the LVM path specification.
Attributes:
location: string containing the location.
volume_index: integer containing the volume index.
"""

TYPE_INDICATOR = definitions.TYPE_INDICATOR_LVM

def __init__(self, location=None, parent=None, volume_index=None, **kwargs):
"""Initializes the path specification object.
Note that the LVM path specification must have a parent.
Args:
location: optional string containing the location.
parent: optional parent path specification (instance of PathSpec).
volume_index: optional integer containing the volume index.
Raises:
ValueError: when parent is not set.
"""
if not parent:
raise ValueError(u'Missing parent value.')

super(LVMPathSpec, self).__init__(parent=parent, **kwargs)
self.location = location
self.volume_index = volume_index

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

if self.location is not None:
string_parts.append(u'location: {0:s}'.format(self.location))
if self.volume_index is not None:
string_parts.append(u'volume index: {0:d}'.format(self.volume_index))

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


# Register the path specification with the factory.
factory.Factory.RegisterPathSpec(LVMPathSpec)
5 changes: 5 additions & 0 deletions dfvfs/resolver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
from dfvfs.resolver import fake_resolver_helper
from dfvfs.resolver import gzip_resolver_helper

try:
from dfvfs.resolver import lvm_resolver_helper
except ImportError:
pass

try:
from dfvfs.resolver import ntfs_resolver_helper
except ImportError:
Expand Down
42 changes: 42 additions & 0 deletions dfvfs/resolver/lvm_resolver_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
"""The LVM path specification resolver helper implementation."""

# This is necessary to prevent a circular import.
import dfvfs.file_io.lvm_file_io
import dfvfs.vfs.lvm_file_system

from dfvfs.lib import definitions
from dfvfs.resolver import resolver
from dfvfs.resolver import resolver_helper


class LVMResolverHelper(resolver_helper.ResolverHelper):
"""Class that implements the Logical Volume Manager (LVM) resolver helper."""

TYPE_INDICATOR = definitions.TYPE_INDICATOR_LVM

def NewFileObject(self, resolver_context):
"""Creates a new file-like object.
Args:
resolver_context: the resolver context (instance of resolver.Context).
Returns:
The file-like object (instance of file_io.FileIO).
"""
return dfvfs.file_io.lvm_file_io.LVMFile(resolver_context)

def NewFileSystem(self, resolver_context):
"""Creates a new file system object.
Args:
resolver_context: the resolver context (instance of resolver.Context).
Returns:
The file system object (instance of vfs.FileSystem).
"""
return dfvfs.vfs.lvm_file_system.LVMFileSystem(resolver_context)


# Register the resolver helpers with the resolver.
resolver.Resolver.RegisterHelper(LVMResolverHelper())
Loading

0 comments on commit 41a5fb6

Please sign in to comment.