def _GetLink(self): """Retrieves the link. Returns: str: path of the linked file. """ if self._link is None: self._link = u'' if not self.IsLink(): return self._link tsk_file = self.GetTSKFile() # Note that because pytsk3.File does not explicitly defines info # we need to check if the attribute exists and has a value other # than None. if getattr(tsk_file, u'info', None) is None: return self._link # If pytsk3.FS_Info.open() was used file.info has an attribute meta # (pytsk3.TSK_FS_META) that contains the link. if getattr(tsk_file.info, u'meta', None) is None: return self._link # Note that the SleuthKit does not expose NTFS # IO_REPARSE_TAG_MOUNT_POINT or IO_REPARSE_TAG_SYMLINK as a link. link = getattr(tsk_file.info.meta, u'link', None) if link is None: return self._link try: # pytsk3 returns an UTF-8 encoded byte string without a leading # path segment separator. link = u'{0:s}{1:s}'.format(self._file_system.PATH_SEPARATOR, link.decode(u'utf8')) except UnicodeError: raise errors.BackEndError( u'pytsk3 returned a non UTF-8 formatted link.') self._link = link return self._link
def _ScanEncryptedVolumeNode(self, scan_context, scan_node): """Scans an encrypted volume node for supported formats. Args: scan_context (SourceScannerContext): source scanner context. scan_node (SourceScanNode): source scan node. Raises: BackEndError: if the scan node cannot be unlocked. ValueError: if the scan context or scan node is invalid. """ if scan_node.type_indicator == definitions.TYPE_INDICATOR_APFS_CONTAINER: # TODO: consider changes this when upstream changes have been made. # Currently pyfsapfs does not support reading from a volume as a device. # Also see: https://github.com/log2timeline/dfvfs/issues/332 container_file_entry = resolver.Resolver.OpenFileEntry( scan_node.path_spec, resolver_context=self._resolver_context) fsapfs_volume = container_file_entry.GetAPFSVolume() # TODO: unlocking the volume multiple times is inefficient cache volume # object in scan node and use is_locked = fsapfs_volume.is_locked() try: is_locked = not apfs_helper.APFSUnlockVolume( fsapfs_volume, scan_node.path_spec, resolver.Resolver.key_chain) except IOError as exception: raise errors.BackEndError( 'Unable to unlock APFS volume with error: {0!s}'.format( exception)) else: file_object = resolver.Resolver.OpenFileObject( scan_node.path_spec, resolver_context=self._resolver_context) is_locked = not file_object or file_object.is_locked if is_locked: scan_context.LockScanNode(scan_node.path_spec) # For BitLocker To Go add a scan node for the unencrypted part of # the volume. if scan_node.type_indicator == definitions.TYPE_INDICATOR_BDE: path_spec = self.ScanForFileSystem(scan_node.path_spec.parent) if path_spec: scan_context.AddScanNode(path_spec, scan_node.parent_node)
def __init__(self, resolver_context, file_system, path_spec, is_root=False, is_virtual=False, tsk_vs_part=None): """Initializes a file entry. Args: resolver_context (Context): resolver context. file_system (FileSystem): file system. path_spec (PathSpec): path specification. is_root (Optional[bool]): True if the file entry is the root file entry of the corresponding file system. is_virtual (Optional[bool]): True if the file entry is a virtual file entry emulated by the corresponding file system. tsk_vs_part (Optional[pytsk3.TSK_VS_PART_INFO]): TSK volume system part. Raises: BackEndError: when the TSK volume system part is missing in a non-virtual file entry. """ tsk_volume = file_system.GetTSKVolume() if not is_virtual and tsk_vs_part is None: tsk_vs_part, _ = tsk_partition.GetTSKVsPartByPathSpec( tsk_volume, path_spec) if not is_virtual and tsk_vs_part is None: raise errors.BackEndError( 'Missing TSK volume system part in non-virtual file entry.') super(TSKPartitionFileEntry, self).__init__(resolver_context, file_system, path_spec, is_root=is_root, is_virtual=is_virtual) self._name = None self._tsk_volume = tsk_volume self._tsk_vs_part = tsk_vs_part if is_virtual: self.entry_type = definitions.FILE_ENTRY_TYPE_DIRECTORY else: self.entry_type = definitions.FILE_ENTRY_TYPE_FILE
def _GetStat(self): """Retrieves the stat object. Returns: The stat object (instance of vfs.VFSStat). Raises: BackEndError: when the regf key is missing. """ if not self._regf_key: raise errors.BackEndError(u'Missing regf key.') stat_object = vfs_stat.VFSStat() # File data stat information. if self._regf_value: stat_object.size = self._regf_value.get_data_size() # Date and time stat information. if self._regf_value: timestamp = None else: timestamp = date_time.PosixTimestamp.FromFiletime( self._regf_key.get_last_written_time_as_integer()) if timestamp is not None: stat_object.mtime = timestamp # Ownership and permissions stat information. # TODO: add support for security key. # File entry type stat information. if self._regf_value: stat_object.type = stat_object.TYPE_FILE else: stat_object.type = stat_object.TYPE_DIRECTORY # TODO: add support for a link: # stat_object.type = stat_object.TYPE_LINK # Other stat information. stat_object.is_allocated = True return stat_object
def _GetStat(self): """Retrieves the stat object. Returns: The stat object (instance of vfs.VFSStat). Raises: BackEndError: when the vshadow store is missing in a non-virtual file entry. """ vshadow_store = self.GetVShadowStore() if not self._is_virtual and vshadow_store is None: raise errors.BackEndError( u'Missing vshadow store in non-virtual file entry.') stat_object = vfs_stat.VFSStat() # File data stat information. if vshadow_store is not None: stat_object.size = vshadow_store.volume_size # Date and time stat information. if vshadow_store is not None: timestamp = vshadow_store.get_creation_time_as_integer() date_time_values = dfdatetime_filetime.Filetime( timestamp=timestamp) stat_time, stat_time_nano = date_time_values.CopyToStatTimeTuple() if stat_time is not None: stat_object.crtime = stat_time stat_object.crtime_nano = stat_time_nano # Ownership and permissions stat information. # File entry type stat information. # The root file entry is virtual and should have type directory. if self._is_virtual: stat_object.type = stat_object.TYPE_DIRECTORY else: stat_object.type = stat_object.TYPE_FILE return stat_object
def HasExternalData(self): """Determines if the file entry has external stored data. Returns: A boolean to indicate the file entry has external data. Raises: BackEndError: when the vshadow store is missing in a non-virtual file entry. """ vshadow_store = self.GetVShadowStore() if not self._is_virtual and vshadow_store is None: raise errors.BackEndError( u'Missing vshadow store in non-virtual file entry.') if vshadow_store is None: return False return not vshadow_store.has_in_volume_data()
def _GetLink(self): """Retrieves the link. Returns: str: link. Raises: BackEndError: when the TAR info is missing in a non-virtual file entry. """ if self._link is None: tar_info = self.GetTARInfo() if not self._is_virtual and not tar_info: raise errors.BackEndError( u'Missing TAR info in non-virtual file entry.') if tar_info: self._link = tar_info.linkname return self._link
def _EntriesGenerator(self): """Retrieves directory entries. Since a directory can contain a vast number of entries using a generator is more memory efficient. Yields: A path specification (instance of path.OSPathSpec). Raises: AccessError: if the access to list the directory was denied. BackEndError: if the directory could not be listed. """ location = getattr(self.path_spec, u'location', None) if location is None: return # Windows will raise WindowsError, which can be caught by OSError, # if the process has not access to list the directory. The os.access() # function cannot be used since it will return true even when os.listdir() # fails. try: for directory_entry in os.listdir(location): directory_entry_location = self._file_system.JoinPath( [location, directory_entry]) yield os_path_spec.OSPathSpec( location=directory_entry_location) except OSError as exception: if exception.errno == errno.EACCES: exception_string = str(exception) if not isinstance(exception_string, py2to3.UNICODE_TYPE): exception_string = py2to3.UNICODE_TYPE(exception_string, errors=u'replace') raise errors.AccessError( u'Access to directory denied with error: {0:s}'.format( exception_string)) else: raise errors.BackEndError( u'Unable to list directory: {0:s} with error: {1:s}'. format(location, exception))
def __init__(self, resolver_context, file_system, path_spec, is_root=False, is_virtual=False, zip_info=None): """Initializes a file entry. Args: resolver_context (Context): resolver context. file_system (FileSystem): file system. path_spec (PathSpec): path specification. is_root (Optional[bool]): True if the file entry is the root file entry of the corresponding file system. is_virtual (Optional[bool]): True if the file entry is a virtual file entry emulated by the corresponding file system. zip_info (Optional[zipfile.ZipInfo]): ZIP information. Raises: BackEndError: when the zip info is missing in a non-virtual file entry. """ if not is_virtual and zip_info is None: zip_info = file_system.GetZipInfoByPathSpec(path_spec) if not is_virtual and zip_info is None: raise errors.BackEndError( 'Missing zip info in non-virtual file entry.') super(ZipFileEntry, self).__init__(resolver_context, file_system, path_spec, is_root=is_root, is_virtual=is_virtual) self._creator_system = getattr(zip_info, 'create_system', 0) self._external_attributes = getattr(zip_info, 'external_attr', 0) self._zip_info = zip_info if (is_virtual or self._external_attributes & self._MSDOS_FILE_ATTRIBUTES_IS_DIRECTORY): self.entry_type = definitions.FILE_ENTRY_TYPE_DIRECTORY else: self.entry_type = definitions.FILE_ENTRY_TYPE_FILE
def __init__(self, resolver_context, file_system, path_spec, is_root=False, is_virtual=False, vslvm_logical_volume=None): """Initializes a file entry. Args: resolver_context (Context): resolver context. file_system (FileSystem): file system. path_spec (PathSpec): path specification. is_root (Optional[bool]): True if the file entry is the root file entry of the corresponding file system. is_virtual (Optional[bool]): True if the file entry is a virtual file vslvm_logical_volume (Optional[pyvslvm.logical_volume]): a LVM logical volume. Raises: BackEndError: when LVM logical volume is missing for a non-virtual file entry. """ if not is_virtual and vslvm_logical_volume is None: vslvm_logical_volume = file_system.GetLVMLogicalVolumeByPathSpec( path_spec) if not is_virtual and vslvm_logical_volume is None: raise errors.BackEndError( 'Missing vslvm logical volume in non-virtual file entry.') super(LVMFileEntry, self).__init__(resolver_context, file_system, path_spec, is_root=is_root, is_virtual=is_virtual) self._name = None self._vslvm_logical_volume = vslvm_logical_volume if self._is_virtual: self.entry_type = definitions.FILE_ENTRY_TYPE_DIRECTORY else: self.entry_type = definitions.FILE_ENTRY_TYPE_FILE
def Decode(self, encoded_data): """Decode the encoded data. Args: encoded_data (byte): encoded data. Returns: tuple(bytes,bytes): decoded data and remaining encoded data. Raises: BackEndError: if the base32 stream cannot be decoded. """ try: decoded_data = base64.b32decode(encoded_data, casefold=False) except (TypeError, binascii.Error) as exception: raise errors.BackEndError( u'Unable to decode base32 stream with error: {0!s}.'.format( exception)) return decoded_data, b''
def __init__(self, resolver_context, file_system, path_spec, fsntfs_file_entry=None, is_root=False, is_virtual=False): """Initializes the file entry object. Args: resolver_context (Context): resolver context. file_system (FileSystem): file system. path_spec (PathSpec): path specification. fsntfs_file_entry (Optional[pyfsntfs.file_entry]): NTFS file entry. is_root (Optional[bool]): True if the file entry is the root file entry of the corresponding file system. is_virtual (Optional[bool]): True if the file entry is a virtual file entry emulated by the corresponding file system. Raises: BackEndError: if the pyfsntfs file entry is missing. """ if not fsntfs_file_entry: fsntfs_file_entry = file_system.GetNTFSFileEntryByPathSpec( path_spec) if not fsntfs_file_entry: raise errors.BackEndError('Missing pyfsntfs file entry.') super(NTFSFileEntry, self).__init__(resolver_context, file_system, path_spec, is_root=is_root, is_virtual=is_virtual) self._fsntfs_file_entry = fsntfs_file_entry if self._IsLink(fsntfs_file_entry.file_attribute_flags): self.entry_type = definitions.FILE_ENTRY_TYPE_LINK elif fsntfs_file_entry.has_directory_entries_index(): self.entry_type = definitions.FILE_ENTRY_TYPE_DIRECTORY else: self.entry_type = definitions.FILE_ENTRY_TYPE_FILE
def GetFileEntryByPathSpec(self, path_spec): """Retrieves a file entry for a path specification. Args: path_spec (PathSpec): path specification. Returns: NTFSFileEntry: file entry or None. Raises: BackEndError: if the file entry cannot be opened. """ # Opening a file by MFT entry is faster than opening a file by location. # However we need the index of the corresponding $FILE_NAME MFT attribute. fsntfs_file_entry = None location = getattr(path_spec, u'location', None) mft_attribute = getattr(path_spec, u'mft_attribute', None) mft_entry = getattr(path_spec, u'mft_entry', None) if (location == self.LOCATION_ROOT or mft_entry == self.MFT_ENTRY_ROOT_DIRECTORY): fsntfs_file_entry = self._fsntfs_volume.get_root_directory() return dfvfs.vfs.ntfs_file_entry.NTFSFileEntry( self._resolver_context, self, path_spec, fsntfs_file_entry=fsntfs_file_entry, is_root=True) try: if mft_attribute is not None and mft_entry is not None: fsntfs_file_entry = self._fsntfs_volume.get_file_entry(mft_entry) elif location is not None: fsntfs_file_entry = self._fsntfs_volume.get_file_entry_by_path(location) except IOError as exception: raise errors.BackEndError(exception) if fsntfs_file_entry is None: return return dfvfs.vfs.ntfs_file_entry.NTFSFileEntry( self._resolver_context, self, path_spec, fsntfs_file_entry=fsntfs_file_entry)
def Decode(self, encoded_data): """Decode the encoded data. Args: encoded_data: a byte string containing the encoded data. Returns: A tuple containing a byte string of the decoded data and the remaining encoded data. Raises: BackEndError: if the base32 stream cannot be decoded. """ try: decoded_data = base64.b32decode(encoded_data, casefold=False) except TypeError as exception: raise errors.BackEndError( u'Unable to decode base32 stream with error: {0:s}.'.format( exception)) return decoded_data, b''
def GetNumberOfRows(self): """Retrieves the number of rows in the table. Returns: An integer containing the number of rows. Raises: BackEndError: when the SQLite blob file-like object is missing. """ file_object = self.GetFileObject() if not file_object: raise errors.BackEndError( u'Unable to retrieve SQLite blob file-like object.') try: # TODO: move this function out of SQLiteBlobFile. self._number_of_entries = file_object.GetNumberOfRows() finally: file_object.close() return self._number_of_entries
def GetFileEntryByPathSpec(self, path_spec): """Retrieves a file entry for a path specification. Args: path_spec (PathSpec): path specification. Returns: APFSFileEntry: file entry or None if not available. Raises: BackEndError: if the file entry cannot be opened. """ # Opening a file by identifier is faster than opening a file by location. fsapfs_file_entry = None location = getattr(path_spec, 'location', None) identifier = getattr(path_spec, 'identifier', None) if (location == self.LOCATION_ROOT or identifier == self.ROOT_DIRECTORY_IDENTIFIER): fsapfs_file_entry = self._fsapfs_volume.get_root_directory() return apfs_file_entry.APFSFileEntry( self._resolver_context, self, path_spec, fsapfs_file_entry=fsapfs_file_entry, is_root=True) try: if identifier is not None: fsapfs_file_entry = self._fsapfs_volume.get_file_entry_by_identifier( identifier) elif location is not None: fsapfs_file_entry = self._fsapfs_volume.get_file_entry_by_path(location) except IOError as exception: raise errors.BackEndError(exception) if fsapfs_file_entry is None: return None return apfs_file_entry.APFSFileEntry( self._resolver_context, self, path_spec, fsapfs_file_entry=fsapfs_file_entry)
def _GetDataStreams(self): """Retrieves the data streams. Returns: list[NTFSDataStream]: data streams. Raises: BackEndError: if the pyfsntfs file entry is missing. """ if self._data_streams is None: fsntfs_file_entry = self.GetNTFSFileEntry() if not fsntfs_file_entry: raise errors.BackEndError(u'Missing pyfsntfs file entry.') self._data_streams = [] if fsntfs_file_entry.has_default_data_stream(): self._data_streams.append(NTFSDataStream(None)) for fsntfs_data_stream in fsntfs_file_entry.alternate_data_streams: self._data_streams.append(NTFSDataStream(fsntfs_data_stream)) return self._data_streams
def __init__(self, resolver_context, file_system, path_spec, is_root=False, is_virtual=False): """Initializes a file entry. Args: resolver_context (Context): resolver context. file_system (FileSystem): file system. path_spec (PathSpec): path specification. is_root (Optional[bool]): True if the file entry is the root file entry of the corresponding file system. is_virtual (Optional[bool]): True if the file entry is a virtual file entry emulated by the corresponding file system. Raises: BackEndError: when the vshadow store is missing in a non-virtual file entry. """ vshadow_store = file_system.GetVShadowStoreByPathSpec(path_spec) if not is_virtual and vshadow_store is None: raise errors.BackEndError( 'Missing vshadow store in non-virtual file entry.') super(VShadowFileEntry, self).__init__(resolver_context, file_system, path_spec, is_root=is_root, is_virtual=is_virtual) self._name = None self._vshadow_store = vshadow_store if self._is_virtual: self.entry_type = definitions.FILE_ENTRY_TYPE_DIRECTORY else: self.entry_type = definitions.FILE_ENTRY_TYPE_FILE
def _GetStat(self): """Retrieves the stat object. Returns: The stat object (instance of vfs.VFSStat). Raises: BackEndError: when the gzip file is missing. """ gzip_file = self.GetFileObject() if not gzip_file: raise errors.BackEndError( u'Unable to open gzip file: {0:s}.'.format( self.path_spec.comparable)) try: stat_object = vfs_stat.VFSStat() # File data stat information. stat_object.size = gzip_file.uncompressed_data_size # Date and time stat information. stat_object.mtime = gzip_file.modification_time # Ownership and permissions stat information. # File entry type stat information. stat_object.type = stat_object.TYPE_FILE # Other stat information. # gzip_file.comment # gzip_file.operating_system # gzip_file.original_filename finally: gzip_file.close() return stat_object
def _GetStat(self): """Retrieves the stat object. Returns: VFSStat: stat object. Raises: BackEndError: when the SQLite blob file-like object is missing. """ stat_object = super(SQLiteBlobFileEntry, self)._GetStat() if not self._is_virtual: file_object = self.GetFileObject() if not file_object: raise errors.BackEndError( 'Unable to retrieve SQLite blob file-like object.') try: stat_object.size = file_object.get_size() finally: file_object.close() return stat_object
def Decompress(self, compressed_data): """Decompresses the compressed data. Args: compressed_data (bytes): compressed data. Returns: tuple(bytes, bytes): uncompressed data and remaining compressed data. Raises: BackEndError: if the BZIP2 compressed stream cannot be decompressed. """ try: uncompressed_data = self._bz2_decompressor.decompress(compressed_data) remaining_compressed_data = getattr( self._bz2_decompressor, 'unused_data', b'') except (EOFError, IOError) as exception: raise errors.BackEndError(( 'Unable to decompress BZIP2 compressed stream with error: ' '{0!s}.').format(exception)) return uncompressed_data, remaining_compressed_data
def Decompress(self, compressed_data): """Decompresses the compressed data. Args: compressed_data (bytes): compressed data. Returns: tuple(bytes,bytes): uncompressed data and remaining compressed data. Raises: BackEndError: if the zlib compressed stream cannot be decompressed. """ try: uncompressed_data = self._zlib_decompressor.decompress(compressed_data) remaining_compressed_data = getattr( self._zlib_decompressor, u'unused_data', b'') except zlib.error as exception: raise errors.BackEndError(( u'Unable to decompress zlib compressed stream with error: ' u'{0!s}.').format(exception)) return uncompressed_data, remaining_compressed_data
def _GetAttributes(self): """Retrieves the attributes. Returns: list[NTFSAttribute]: attributes. Raises: BackEndError: if the pyfsntfs file entry is missing. """ if self._attributes is None: fsntfs_file_entry = self.GetNTFSFileEntry() if not fsntfs_file_entry: raise errors.BackEndError(u'Missing pyfsntfs file entry.') self._attributes = [] for fsntfs_attribute in fsntfs_file_entry.attributes: attribute_class = self._ATTRIBUTE_TYPE_CLASS_MAPPINGS.get( fsntfs_attribute.attribute_type, NTFSAttribute) attribute_object = attribute_class(fsntfs_attribute) self._attributes.append(attribute_object) return self._attributes
def __init__(self, resolver_context, file_system, path_spec, fsapfs_file_entry=None, is_root=False, is_virtual=False): """Initializes a file entry. Args: resolver_context (Context): resolver context. file_system (FileSystem): file system. path_spec (PathSpec): path specification. fsapfs_file_entry (Optional[pyfsapfs.file_entry]): APFS file entry. is_root (Optional[bool]): True if the file entry is the root file entry of the corresponding file system. is_virtual (Optional[bool]): True if the file entry is a virtual file entry emulated by the corresponding file system. Raises: BackEndError: if the pyfsapfs file entry is missing. """ if not fsapfs_file_entry: fsapfs_file_entry = file_system.GetAPFSFileEntryByPathSpec( path_spec) if not fsapfs_file_entry: raise errors.BackEndError('Missing pyfsapfs file entry.') super(APFSFileEntry, self).__init__(resolver_context, file_system, path_spec, is_root=is_root, is_virtual=is_virtual) self._fsapfs_file_entry = fsapfs_file_entry self.entry_type = self._ENTRY_TYPES.get( fsapfs_file_entry.file_mode & 0xf000, None)
def Decode(self, encoded_data): """Decode the encoded data. Args: encoded_data (byte): encoded data. Returns: tuple(bytes,bytes): decoded data and remaining encoded data. Raises: BackEndError: if the base64 stream cannot be decoded. """ try: # TODO: replace by libuna implementation or equivalent. The behavior of # base64.b64decode() does not raise TypeError for certain invalid base64 # data e.g. b'\x01\x02\x03\x04\x05\x06\x07\x08' these are silently # ignored. decoded_data = base64.b64decode(encoded_data) except (TypeError, binascii.Error) as exception: raise errors.BackEndError( u'Unable to decode base64 stream with error: {0!s}.'.format( exception)) return decoded_data, b''
def _ScanNode(self, scan_context, scan_node, auto_recurse=True): """Scans for supported formats using a scan node. Args: scan_context (SourceScannerContext): source scanner context. scan_node (SourceScanNode): source scan node. auto_recurse (Optional[bool]): True if the scan should automatically recurse as far as possible. Raises: BackEndError: if the source cannot be scanned. ValueError: if the scan context or scan node is invalid. """ if not scan_context: raise ValueError('Invalid scan context.') if not scan_node: raise ValueError('Invalid scan node.') scan_path_spec = scan_node.path_spec if not scan_node.IsSystemLevel(): system_level_file_entry = None else: system_level_file_entry = resolver.Resolver.OpenFileEntry( scan_node.path_spec, resolver_context=self._resolver_context) if system_level_file_entry is None: raise errors.BackEndError('Unable to open file entry.') if system_level_file_entry.IsDirectory(): scan_context.SetSourceType(definitions.SOURCE_TYPE_DIRECTORY) return source_path_spec = self.ScanForStorageMediaImage( scan_node.path_spec) if source_path_spec: scan_node.scanned = True scan_node = scan_context.AddScanNode(source_path_spec, scan_node) if system_level_file_entry.IsDevice(): scan_context.SetSourceType( definitions.SOURCE_TYPE_STORAGE_MEDIA_DEVICE) else: scan_context.SetSourceType( definitions.SOURCE_TYPE_STORAGE_MEDIA_IMAGE) if not auto_recurse: return # In case we did not find a storage media image type we keep looking # since not all RAW storage media image naming schemas are known and # its type can only detected by its content. source_path_spec = None while True: # No need to scan a file systems scan node for volume systems. if scan_node.type_indicator in definitions.FILE_SYSTEM_TYPE_INDICATORS: break # No need to scan a locked scan node e.g. an encrypted volume. if scan_context.IsLockedScanNode(scan_node.path_spec): break source_path_spec = self.ScanForVolumeSystem(scan_node.path_spec) # Nothing found in the volume system scan. if not source_path_spec: break if not scan_context.HasScanNode(source_path_spec): scan_node.scanned = True scan_node = scan_context.AddScanNode(source_path_spec, scan_node) if system_level_file_entry and system_level_file_entry.IsDevice( ): scan_context.SetSourceType( definitions.SOURCE_TYPE_STORAGE_MEDIA_DEVICE) else: scan_context.SetSourceType( definitions.SOURCE_TYPE_STORAGE_MEDIA_IMAGE) if scan_node.type_indicator in definitions.VOLUME_SYSTEM_TYPE_INDICATORS: # For VSS add a scan node for the current volume. if scan_node.type_indicator == definitions.TYPE_INDICATOR_VSHADOW: path_spec = self.ScanForFileSystem( scan_node.path_spec.parent) if path_spec: scan_context.AddScanNode(path_spec, scan_node.parent_node) # Determine the path specifications of the sub file entries. file_entry = resolver.Resolver.OpenFileEntry( scan_node.path_spec, resolver_context=self._resolver_context) for sub_file_entry in file_entry.sub_file_entries: sub_scan_node = scan_context.AddScanNode( sub_file_entry.path_spec, scan_node) # Since scanning for file systems in VSS snapshot volumes can # be expensive we only do this when explicitly asked for. if scan_node.type_indicator != definitions.TYPE_INDICATOR_VSHADOW: if auto_recurse or not scan_context.updated: self._ScanNode(scan_context, sub_scan_node, auto_recurse=auto_recurse) # We already have already scanned for the file systems in _ScanNode(). return elif scan_node.type_indicator in ( definitions.ENCRYPTED_VOLUME_TYPE_INDICATORS): file_object = resolver.Resolver.OpenFileObject( scan_node.path_spec, resolver_context=self._resolver_context) is_locked = not file_object or file_object.is_locked file_object.close() if is_locked: scan_context.LockScanNode(scan_node.path_spec) # For BitLocker To Go add a scan node for the unencrypted part # of the volume. if scan_node.type_indicator == definitions.TYPE_INDICATOR_BDE: path_spec = self.ScanForFileSystem( scan_node.path_spec.parent) if path_spec: scan_context.AddScanNode(path_spec, scan_node.parent_node) if not auto_recurse and scan_context.updated: return # Nothing new found. if not scan_context.updated: break # In case we did not find a volume system type we keep looking # since we could be dealing with a storage media image that contains # a single volume. # No need to scan the root of a volume system for a file system. if (scan_node.path_spec.type_indicator in (definitions.VOLUME_SYSTEM_TYPE_INDICATORS) and getattr(scan_node.path_spec, 'location', None) == '/'): pass # No need to scan a locked scan node e.g. an encrypted volume. elif scan_context.IsLockedScanNode(scan_node.path_spec): pass # Since scanning for file systems in VSS snapshot volumes can # be expensive we only do this when explicitly asked for. elif (scan_node.type_indicator == definitions.TYPE_INDICATOR_VSHADOW and auto_recurse and scan_node.path_spec != scan_path_spec): pass elif scan_node.type_indicator not in ( definitions.FILE_SYSTEM_TYPE_INDICATORS): source_path_spec = self.ScanForFileSystem(scan_node.path_spec) if not source_path_spec: # Since RAW storage media image can only be determined by naming schema # we could have single file that is not a RAW storage media image yet # matches the naming schema. if scan_node.path_spec.type_indicator == definitions.TYPE_INDICATOR_RAW: scan_node = scan_context.RemoveScanNode( scan_node.path_spec) # Make sure to override the previously assigned source type. scan_context.source_type = definitions.SOURCE_TYPE_FILE else: scan_context.SetSourceType(definitions.SOURCE_TYPE_FILE) elif not scan_context.HasScanNode(source_path_spec): scan_node.scanned = True scan_node = scan_context.AddScanNode(source_path_spec, scan_node) if system_level_file_entry and system_level_file_entry.IsDevice( ): scan_context.SetSourceType( definitions.SOURCE_TYPE_STORAGE_MEDIA_DEVICE) else: scan_context.SetSourceType( definitions.SOURCE_TYPE_STORAGE_MEDIA_IMAGE) # If all scans failed mark the scan node as scanned so we do not scan it # again. if not scan_node.scanned: scan_node.scanned = True return
def _EntriesGenerator(self): """Retrieves directory entries. Since a directory can contain a vast number of entries using a generator is more memory efficient. Yields: TSKPathSpec: path specification. Raises: BackEndError: if pytsk3 cannot open the directory. """ # Opening a file by inode number is faster than opening a file # by location. inode = getattr(self.path_spec, u'inode', None) location = getattr(self.path_spec, u'location', None) fs_info = self._file_system.GetFsInfo() tsk_directory = None try: if inode is not None: tsk_directory = fs_info.open_dir(inode=inode) elif location is not None: tsk_directory = fs_info.open_dir(path=location) except IOError as exception: raise errors.BackEndError( u'Unable to open directory with error: {0:s}'.format( exception)) if not tsk_directory: return for tsk_directory_entry in tsk_directory: # Note that because pytsk3.Directory does not explicitly defines info # we need to check if the attribute exists and has a value other # than None. if getattr(tsk_directory_entry, u'info', None) is None: continue # Note that because pytsk3.TSK_FS_FILE does not explicitly defines fs_info # we need to check if the attribute exists and has a value other # than None. if getattr(tsk_directory_entry.info, u'fs_info', None) is None: continue # Note that because pytsk3.TSK_FS_FILE does not explicitly defines meta # we need to check if the attribute exists and has a value other # than None. if getattr(tsk_directory_entry.info, u'meta', None) is None: # Most directory entries will have an "inode" but not all, e.g. # previously deleted files. Currently directory entries without # a pytsk3.TSK_FS_META object are ignored. continue # Note that because pytsk3.TSK_FS_META does not explicitly defines addr # we need to check if the attribute exists. if not hasattr(tsk_directory_entry.info.meta, u'addr'): continue directory_entry_inode = tsk_directory_entry.info.meta.addr directory_entry = None # Ignore references to self. if directory_entry_inode == inode: continue # On non-NTFS file systems ignore inode 0. if directory_entry_inode == 0 and not self._file_system.IsNTFS(): continue # Note that because pytsk3.TSK_FS_FILE does not explicitly defines name # we need to check if the attribute exists and has a value other # than None. if getattr(tsk_directory_entry.info, u'name', None) is not None: # Ignore file entries marked as "unallocated". flags = getattr(tsk_directory_entry.info.name, u'flags', 0) if int(flags) & pytsk3.TSK_FS_NAME_FLAG_UNALLOC: continue directory_entry = getattr(tsk_directory_entry.info.name, u'name', u'') try: # pytsk3 returns an UTF-8 encoded byte string. directory_entry = directory_entry.decode(u'utf8') except UnicodeError: # Continue here since we cannot represent the directory entry. continue if directory_entry: # Ignore references to self or parent. if directory_entry in [u'.', u'..']: continue if location == self._file_system.PATH_SEPARATOR: directory_entry = self._file_system.JoinPath( [directory_entry]) else: directory_entry = self._file_system.JoinPath( [location, directory_entry]) yield tsk_path_spec.TSKPathSpec(inode=directory_entry_inode, location=directory_entry, parent=self.path_spec.parent)
def _GetStat(self): """Retrieves the stat object. Returns: VFSStat: stat object. Raises: BackEndError: if the TSK File .info or .info.meta attribute is missing. """ tsk_file = self.GetTSKFile() if not tsk_file or not tsk_file.info or not tsk_file.info.meta: raise errors.BackEndError(u'Missing TSK File .info or .info.meta.') stat_object = vfs_stat.VFSStat() # File data stat information. stat_object.size = getattr(tsk_file.info.meta, u'size', None) # Date and time stat information. stat_time, stat_time_nano = self._TSKFileTimeCopyToStatTimeTuple( tsk_file, u'atime') if stat_time is not None: stat_object.atime = stat_time stat_object.atime_nano = stat_time_nano stat_time, stat_time_nano = self._TSKFileTimeCopyToStatTimeTuple( tsk_file, u'bkup') if stat_time is not None: stat_object.bkup = stat_time stat_object.bkup_nano = stat_time_nano stat_time, stat_time_nano = self._TSKFileTimeCopyToStatTimeTuple( tsk_file, u'ctime') if stat_time is not None: stat_object.ctime = stat_time stat_object.ctime_nano = stat_time_nano stat_time, stat_time_nano = self._TSKFileTimeCopyToStatTimeTuple( tsk_file, u'crtime') if stat_time is not None: stat_object.crtime = stat_time stat_object.crtime_nano = stat_time_nano stat_time, stat_time_nano = self._TSKFileTimeCopyToStatTimeTuple( tsk_file, u'dtime') if stat_time is not None: stat_object.dtime = stat_time stat_object.dtime_nano = stat_time_nano stat_time, stat_time_nano = self._TSKFileTimeCopyToStatTimeTuple( tsk_file, u'mtime') if stat_time is not None: stat_object.mtime = stat_time stat_object.mtime_nano = stat_time_nano # Ownership and permissions stat information. mode = getattr(tsk_file.info.meta, u'mode', None) if mode is not None: # We need to cast mode to an int since it is of type # pytsk3.TSK_FS_META_MODE_ENUM. stat_object.mode = int(mode) stat_object.uid = getattr(tsk_file.info.meta, u'uid', None) stat_object.gid = getattr(tsk_file.info.meta, u'gid', None) # File entry type stat information. # The type is an instance of pytsk3.TSK_FS_META_TYPE_ENUM. tsk_fs_meta_type = getattr(tsk_file.info.meta, u'type', pytsk3.TSK_FS_META_TYPE_UNDEF) if tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_REG: stat_object.type = stat_object.TYPE_FILE elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_DIR: stat_object.type = stat_object.TYPE_DIRECTORY elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_LNK: stat_object.type = stat_object.TYPE_LINK elif (tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_CHR or tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_BLK): stat_object.type = stat_object.TYPE_DEVICE elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_FIFO: stat_object.type = stat_object.TYPE_PIPE elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_SOCK: stat_object.type = stat_object.TYPE_SOCKET # TODO: implement support for: # pytsk3.TSK_FS_META_TYPE_UNDEF # pytsk3.TSK_FS_META_TYPE_SHAD # pytsk3.TSK_FS_META_TYPE_WHT # pytsk3.TSK_FS_META_TYPE_VIRT # Other stat information. stat_object.ino = getattr(tsk_file.info.meta, u'addr', None) # stat_object.dev = stat_info.st_dev # stat_object.nlink = getattr(tsk_file.info.meta, u'nlink', None) # stat_object.fs_type = u'Unknown' flags = getattr(tsk_file.info.meta, u'flags', 0) # The flags are an instance of pytsk3.TSK_FS_META_FLAG_ENUM. if int(flags) & pytsk3.TSK_FS_META_FLAG_ALLOC: stat_object.is_allocated = True else: stat_object.is_allocated = False return stat_object
def _GetStat(self): """Retrieves the stat object. Returns: VFSStat: stat object. Raises: BackEndError: if the pyfsntfs file entry is missing. """ fsntfs_file_entry = self.GetNTFSFileEntry() if not fsntfs_file_entry: raise errors.BackEndError(u'Missing pyfsntfs file entry.') stat_object = vfs_stat.VFSStat() # File data stat information. if fsntfs_file_entry.has_default_data_stream(): stat_object.size = fsntfs_file_entry.get_size() # Date and time stat information. timestamp = fsntfs_file_entry.get_access_time_as_integer() date_time_values = dfdatetime_filetime.Filetime(timestamp=timestamp) stat_time, stat_time_nano = date_time_values.CopyToStatTimeTuple() if stat_time is not None: stat_object.atime = stat_time stat_object.atime_nano = stat_time_nano timestamp = fsntfs_file_entry.get_creation_time_as_integer() date_time_values = dfdatetime_filetime.Filetime(timestamp=timestamp) stat_time, stat_time_nano = date_time_values.CopyToStatTimeTuple() if stat_time is not None: stat_object.crtime = stat_time stat_object.crtime_nano = stat_time_nano timestamp = fsntfs_file_entry.get_modification_time_as_integer() date_time_values = dfdatetime_filetime.Filetime(timestamp=timestamp) stat_time, stat_time_nano = date_time_values.CopyToStatTimeTuple() if stat_time is not None: stat_object.mtime = stat_time stat_object.mtime_nano = stat_time_nano timestamp = fsntfs_file_entry.get_entry_modification_time_as_integer() date_time_values = dfdatetime_filetime.Filetime(timestamp=timestamp) stat_time, stat_time_nano = date_time_values.CopyToStatTimeTuple() if stat_time is not None: stat_object.ctime = stat_time stat_object.ctime_nano = stat_time_nano # Ownership and permissions stat information. # TODO: stat_object.mode # TODO: stat_object.uid # TODO: stat_object.gid # File entry type stat information. if self._IsLink(fsntfs_file_entry.file_attribute_flags): stat_object.type = stat_object.TYPE_LINK elif fsntfs_file_entry.has_directory_entries_index(): stat_object.type = stat_object.TYPE_DIRECTORY else: stat_object.type = stat_object.TYPE_FILE # Other stat information. stat_object.ino = (fsntfs_file_entry.file_reference & _FILE_REFERENCE_MFT_ENTRY_BITMASK) stat_object.fs_type = u'NTFS' stat_object.is_allocated = fsntfs_file_entry.is_allocated() return stat_object
def _GetStat(self): """Retrieves the stat object (instance of vfs.VFSStat). Raises: BackEndError: If an OSError comes up it is caught and an BackEndError error is raised instead. Returns: Stat object (instance of VFSStat) or None if no location is set. """ location = getattr(self.path_spec, u'location', None) if location is None: return stat_object = vfs_stat.VFSStat() is_windows_device = False stat_info = None # Windows does not support running os.stat on device files so we use # libsmdev to do an initial check. if platform.system() == u'Windows': try: is_windows_device = pysmdev.check_device(location) except IOError: pass if is_windows_device: stat_object.type = stat_object.TYPE_DEVICE else: # We are only catching OSError. However on the Windows platform # a WindowsError can be raised as well. We are not catching that since # that error does not exist on non-Windows platforms. try: stat_info = os.stat(location) except OSError as exception: raise errors.BackEndError( u'Unable to retrieve stat object with error: {0:s}'.format( exception)) # File data stat information. stat_object.size = stat_info.st_size # Date and time stat information. stat_object.atime = stat_info.st_atime stat_object.ctime = stat_info.st_ctime stat_object.mtime = stat_info.st_mtime # Ownership and permissions stat information. stat_object.mode = stat.S_IMODE(stat_info.st_mode) stat_object.uid = stat_info.st_uid stat_object.gid = stat_info.st_gid # If location contains a trailing segment separator and points to # a symbolic link to a directory stat info will not indicate # the file entry as a symbolic link. The following check ensures # that the LINK type is correctly detected. is_link = os.path.islink(location) # File entry type stat information. # The stat info member st_mode can have multiple types e.g. # LINK and DIRECTORY in case of a symbolic link to a directory # dfVFS currently only supports one type so we need to check # for LINK first. if stat.S_ISLNK(stat_info.st_mode) or is_link: stat_object.type = stat_object.TYPE_LINK elif stat.S_ISREG(stat_info.st_mode): stat_object.type = stat_object.TYPE_FILE elif stat.S_ISDIR(stat_info.st_mode): stat_object.type = stat_object.TYPE_DIRECTORY elif (stat.S_ISCHR(stat_info.st_mode) or stat.S_ISBLK(stat_info.st_mode)): stat_object.type = stat_object.TYPE_DEVICE elif stat.S_ISFIFO(stat_info.st_mode): stat_object.type = stat_object.TYPE_PIPE elif stat.S_ISSOCK(stat_info.st_mode): stat_object.type = stat_object.TYPE_SOCKET # Other stat information. stat_object.ino = stat_info.st_ino # stat_info.st_dev # stat_info.st_nlink return stat_object