# -*- coding: utf-8 -*-
"""The SleuthKit (TSK) file system implementation."""
import pytsk3
from dfvfs.lib import definitions
from dfvfs.lib import errors
from dfvfs.lib import tsk_image
from dfvfs.path import tsk_path_spec
from dfvfs.resolver import resolver
from dfvfs.vfs import file_system
from dfvfs.vfs import tsk_file_entry
[docs]
class TSKFileSystem(file_system.FileSystem):
"""File system that uses pytsk3."""
TYPE_INDICATOR = definitions.TYPE_INDICATOR_TSK
[docs]
def __init__(self, resolver_context, path_spec):
"""Initializes a file system.
Args:
resolver_context (Context): resolver context.
path_spec (PathSpec): a path specification.
"""
super(TSKFileSystem, self).__init__(resolver_context, path_spec)
self._file_object = None
self._tsk_file_system = None
self._tsk_fs_type = None
def _Close(self):
"""Closes a file system.
Raises:
IOError: if the close failed.
"""
self._tsk_file_system = None
self._file_object = None
def _Open(self, mode='rb'):
"""Opens the file system object defined by path specification.
Args:
mode (Optional[str]): file access mode.
Raises:
AccessError: if the access to open the file was denied.
IOError: if the file system object could not be opened.
PathSpecError: if the path specification is incorrect.
ValueError: if the path specification is invalid.
"""
if not self._path_spec.HasParent():
raise errors.PathSpecError(
'Unsupported path specification without parent.')
file_object = resolver.Resolver.OpenFileObject(
self._path_spec.parent, resolver_context=self._resolver_context)
tsk_image_object = tsk_image.TSKFileSystemImage(file_object)
tsk_file_system = pytsk3.FS_Info(tsk_image_object)
self._file_object = file_object
self._tsk_file_system = tsk_file_system
[docs]
def FileEntryExistsByPathSpec(self, path_spec):
"""Determines if a file entry for a path specification exists.
Args:
path_spec (PathSpec): path specification.
Returns:
bool: True if the file entry exists.
"""
# Opening a file by inode number is faster than opening a file by location.
tsk_file = None
inode = getattr(path_spec, 'inode', None)
location = getattr(path_spec, 'location', None)
try:
if inode is not None:
tsk_file = self._tsk_file_system.open_meta(inode=inode)
elif location is not None:
tsk_file = self._tsk_file_system.open(location)
except IOError:
pass
return tsk_file is not None
[docs]
def GetFileEntryByPathSpec(self, path_spec):
"""Retrieves a file entry for a path specification.
Args:
path_spec (PathSpec): path specification.
Returns:
TSKFileEntry: a file entry or None if not available.
"""
# Opening a file by inode number is faster than opening a file by location.
tsk_file = None
inode = getattr(path_spec, 'inode', None)
location = getattr(path_spec, 'location', None)
root_inode = self.GetRootInode()
if (location == self.LOCATION_ROOT or
(inode is not None and root_inode is not None and inode == root_inode)):
tsk_file = self._tsk_file_system.open(self.LOCATION_ROOT)
return tsk_file_entry.TSKFileEntry(
self._resolver_context, self, path_spec, tsk_file=tsk_file,
is_root=True)
try:
if inode is not None:
tsk_file = self._tsk_file_system.open_meta(inode=inode)
elif location is not None:
tsk_file = self._tsk_file_system.open(location)
except IOError:
pass
if tsk_file is None:
return None
# TODO: is there a way to determine the parent inode number here?
return tsk_file_entry.TSKFileEntry(
self._resolver_context, self, path_spec, tsk_file=tsk_file)
[docs]
def GetFsInfo(self):
"""Retrieves the file system info.
Returns:
pytsk3.FS_Info: file system info.
"""
return self._tsk_file_system
[docs]
def GetFsType(self):
"""Retrieves the file system type.
Returns:
pytsk3.TSK_FS_TYPE_ENUM: file system type.
"""
if self._tsk_fs_type is None:
self._tsk_fs_type = pytsk3.TSK_FS_TYPE_UNSUPP
if (not self._tsk_file_system or
not hasattr(self._tsk_file_system, 'info')):
return self._tsk_fs_type
self._tsk_fs_type = getattr(
self._tsk_file_system.info, 'ftype', pytsk3.TSK_FS_TYPE_UNSUPP)
return self._tsk_fs_type
[docs]
def GetRootFileEntry(self):
"""Retrieves the root file entry.
Returns:
TSKFileEntry: a file entry.
"""
kwargs = {}
root_inode = self.GetRootInode()
if root_inode is not None:
kwargs['inode'] = root_inode
kwargs['location'] = self.LOCATION_ROOT
kwargs['parent'] = self._path_spec.parent
path_spec = tsk_path_spec.TSKPathSpec(**kwargs)
return self.GetFileEntryByPathSpec(path_spec)
[docs]
def GetRootInode(self):
"""Retrieves the root inode.
Returns:
int: inode number or None if not available.
"""
# Note that because pytsk3.FS_Info does not explicitly define info
# we need to check if the attribute exists and has a value other
# than None
if getattr(self._tsk_file_system, 'info', None) is None:
return None
# Note that because pytsk3.TSK_FS_INFO does not explicitly define
# root_inum we need to check if the attribute exists and has a value
# other than None
return getattr(self._tsk_file_system.info, 'root_inum', None)
[docs]
def GetTSKFileByPathSpec(self, path_spec):
"""Retrieves the SleuthKit file object for a path specification.
Args:
path_spec (PathSpec): path specification.
Returns:
pytsk3.File: TSK file.
Raises:
PathSpecError: if the path specification is missing inode and location.
"""
# Opening a file by inode number is faster than opening a file
# by location.
inode = getattr(path_spec, 'inode', None)
location = getattr(path_spec, 'location', None)
if inode is not None:
tsk_file = self._tsk_file_system.open_meta(inode=inode)
elif location is not None:
tsk_file = self._tsk_file_system.open(location)
else:
raise errors.PathSpecError(
'Path specification missing inode and location.')
return tsk_file
[docs]
def IsExt(self):
"""Determines if the file system is ext2, ext3 or ext4.
Returns:
bool: True if the file system is ext.
"""
tsk_fs_type = self.GetFsType()
return tsk_fs_type in [
pytsk3.TSK_FS_TYPE_EXT2, pytsk3.TSK_FS_TYPE_EXT3,
pytsk3.TSK_FS_TYPE_EXT4, pytsk3.TSK_FS_TYPE_EXT_DETECT]
[docs]
def IsHFS(self):
"""Determines if the file system is HFS, HFS+ or HFSX.
Returns:
bool: True if the file system is HFS.
"""
tsk_fs_type = self.GetFsType()
return tsk_fs_type in [
pytsk3.TSK_FS_TYPE_HFS, pytsk3.TSK_FS_TYPE_HFS_DETECT]
[docs]
def IsNTFS(self):
"""Determines if the file system is NTFS.
Returns:
bool: True if the file system is NTFS.
"""
tsk_fs_type = self.GetFsType()
return tsk_fs_type in [
pytsk3.TSK_FS_TYPE_NTFS, pytsk3.TSK_FS_TYPE_NTFS_DETECT]