# -*- coding: utf-8 -*-
"""Helper for generating bodyfile entries."""
from dfdatetime import definitions as dfdatetime_definitions
from dfvfs.lib import definitions as dfvfs_definitions
from dfvfs.vfs import ntfs_attribute as dfvfs_ntfs_attribute
from dfimagetools import definitions
[docs]
class BodyfileGenerator(object):
"""Bodyfile generator."""
_ESCAPE_CHARACTERS = {
'/': '\\/',
':': '\\:',
'\\': '\\\\',
'|': '\\|'}
_ESCAPE_CHARACTERS.update(definitions.NON_PRINTABLE_CHARACTERS)
_FILE_TYPES = {
0x1000: 'p',
0x2000: 'c',
0x4000: 'd',
0x6000: 'b',
0xa000: 'l',
0xc000: 's'}
_FILE_ATTRIBUTE_READONLY = 1
_FILE_ATTRIBUTE_HIDDEN = 2
_FILE_ATTRIBUTE_SYSTEM = 4
_TIMESTAMP_FORMAT_STRINGS = {
dfdatetime_definitions.PRECISION_1_NANOSECOND: '{0:d}.{1:09d}',
dfdatetime_definitions.PRECISION_10_NANOSECONDS: '{0:d}.{1:08d}',
dfdatetime_definitions.PRECISION_100_NANOSECONDS: '{0:d}.{1:07d}',
dfdatetime_definitions.PRECISION_1_MICROSECOND: '{0:d}.{1:06d}',
dfdatetime_definitions.PRECISION_10_MICROSECONDS: '{0:d}.{1:05d}',
dfdatetime_definitions.PRECISION_100_MICROSECONDS: '{0:d}.{1:04d}',
dfdatetime_definitions.PRECISION_1_MILLISECOND: '{0:d}.{1:03d}',
dfdatetime_definitions.PRECISION_10_MILLISECONDS: '{0:d}.{1:02d}',
dfdatetime_definitions.PRECISION_100_MILLISECONDS: '{0:d}.{1:01d}'}
[docs]
def __init__(self):
"""Initializes a bodyfile generator."""
super(BodyfileGenerator, self).__init__()
self._bodyfile_escape_characters = str.maketrans(self._ESCAPE_CHARACTERS)
self._root_file_entry_identifier = None
def _GetFileAttributeFlagsString(self, file_type, file_attribute_flags):
"""Retrieves a bodyfile string representation of file attributes flags.
Args:
file_type (str): bodyfile file type identifier.
file_attribute_flags (int): file attribute flags.
Returns:
str: bodyfile representation of the file attributes flags.
"""
string_parts = [file_type, 'r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x']
if (file_attribute_flags & self._FILE_ATTRIBUTE_READONLY or
file_attribute_flags & self._FILE_ATTRIBUTE_SYSTEM):
string_parts[2] = '-'
string_parts[5] = '-'
string_parts[8] = '-'
return ''.join(string_parts)
def _GetModeString(self, mode):
"""Retrieves a bodyfile string representation of a mode.
Args:
mode (int): mode.
Returns:
str: bodyfile representation of the mode.
"""
string_parts = 10 * ['-']
if mode & 0x0001:
string_parts[9] = 'x'
if mode & 0x0002:
string_parts[8] = 'w'
if mode & 0x0004:
string_parts[7] = 'r'
if mode & 0x0008:
string_parts[6] = 'x'
if mode & 0x0010:
string_parts[5] = 'w'
if mode & 0x0020:
string_parts[4] = 'r'
if mode & 0x0040:
string_parts[3] = 'x'
if mode & 0x0080:
string_parts[2] = 'w'
if mode & 0x0100:
string_parts[1] = 'r'
string_parts[0] = self._FILE_TYPES.get(mode & 0xf000, '-')
return ''.join(string_parts)
def _GetTimestamp(self, date_time):
"""Retrieves a bodyfile timestamp representation of a date time value.
Args:
date_time (dfdatetime.DateTimeValues): date time value.
Returns:
str: bodyfile timestamp representation of the date time value.
"""
if not date_time:
return ''
posix_timestamp, fraction_of_second = (
date_time.CopyToPosixTimestampWithFractionOfSecond())
format_string = self._TIMESTAMP_FORMAT_STRINGS.get(
date_time.precision, '{0:d}')
return format_string.format(posix_timestamp, fraction_of_second)
[docs]
def GetEntries(self, file_entry, path_segments):
"""Retrieves bodyfile entry representations of a file entry.
Args:
file_entry (dfvfs.FileEntry): file entry.
path_segments (str): path segments of the full path of the file entry.
Yields:
str: bodyfile entry.
"""
file_attribute_flags = None
parent_file_reference = None
if file_entry.type_indicator == dfvfs_definitions.TYPE_INDICATOR_FAT:
fsfat_file_entry = file_entry.GetFATFileEntry()
file_attribute_flags = fsfat_file_entry.file_attribute_flags
elif file_entry.type_indicator == dfvfs_definitions.TYPE_INDICATOR_NTFS:
mft_attribute_index = getattr(file_entry.path_spec, 'mft_attribute', None)
if mft_attribute_index is not None:
fsntfs_file_entry = file_entry.GetNTFSFileEntry()
file_attribute_flags = fsntfs_file_entry.file_attribute_flags
parent_file_reference = (
fsntfs_file_entry.get_parent_file_reference_by_attribute_index(
mft_attribute_index))
stat_attribute = file_entry.GetStatAttribute()
if stat_attribute.inode_number is None:
inode_string = ''
elif file_entry.type_indicator == dfvfs_definitions.TYPE_INDICATOR_FAT:
inode_string = f'0x{stat_attribute.inode_number:x}'
elif file_entry.type_indicator == dfvfs_definitions.TYPE_INDICATOR_NTFS:
mft_entry_number = stat_attribute.inode_number & 0xffffffffffff
mft_sequence_number = stat_attribute.inode_number >> 48
inode_string = f'{mft_entry_number:d}-{mft_sequence_number:d}'
else:
inode_string = f'{stat_attribute.inode_number:d}'
if file_entry.type_indicator not in (
dfvfs_definitions.TYPE_INDICATOR_FAT,
dfvfs_definitions.TYPE_INDICATOR_NTFS):
mode = getattr(stat_attribute, 'mode', None) or 0
mode_string = self._GetModeString(mode)
else:
if file_entry.entry_type == dfvfs_definitions.FILE_ENTRY_TYPE_DIRECTORY:
file_type = 'd'
elif file_entry.entry_type == dfvfs_definitions.FILE_ENTRY_TYPE_LINK:
file_type = 'l'
else:
file_type = '-'
if file_attribute_flags is None:
mode_string = ''.join([file_type] + (9 * ['-']))
else:
mode_string = self._GetFileAttributeFlagsString(
file_type, file_attribute_flags)
owner_identifier = ''
if stat_attribute.owner_identifier is not None:
owner_identifier = str(stat_attribute.owner_identifier)
group_identifier = ''
if stat_attribute.group_identifier is not None:
group_identifier = str(stat_attribute.group_identifier)
size = str(file_entry.size)
access_time = self._GetTimestamp(file_entry.access_time)
creation_time = self._GetTimestamp(file_entry.creation_time)
change_time = self._GetTimestamp(file_entry.change_time)
modification_time = self._GetTimestamp(file_entry.modification_time)
# TODO: add support to calculate MD5
md5_string = '0'
path_segments = [
segment.translate(self._bodyfile_escape_characters)
for segment in path_segments]
file_entry_name_value = '/'.join(path_segments) or '/'
if not file_entry.link:
name_value = file_entry_name_value
else:
if file_entry.type_indicator in (
dfvfs_definitions.TYPE_INDICATOR_FAT,
dfvfs_definitions.TYPE_INDICATOR_NTFS):
path_segments = file_entry.link.split('\\')
else:
path_segments = file_entry.link.split('/')
file_entry_link = '/'.join([
segment.translate(self._bodyfile_escape_characters)
for segment in path_segments])
name_value = ' -> '.join([file_entry_name_value, file_entry_link])
yield '|'.join([
md5_string, name_value, inode_string, mode_string, owner_identifier,
group_identifier, size, access_time, modification_time, change_time,
creation_time])
for data_stream in file_entry.data_streams:
if data_stream.name:
data_stream_name = data_stream.name.translate(
self._bodyfile_escape_characters)
data_stream_name_value = ':'.join([
file_entry_name_value, data_stream_name])
yield '|'.join([
md5_string, data_stream_name_value, inode_string, mode_string,
owner_identifier, group_identifier, size, access_time,
modification_time, change_time, creation_time])
for attribute in file_entry.attributes:
if isinstance(attribute, dfvfs_ntfs_attribute.FileNameNTFSAttribute):
if (attribute.name == file_entry.name and
attribute.parent_file_reference == parent_file_reference):
attribute_name_value = ' '.join([
file_entry_name_value, '($FILE_NAME)'])
access_time = self._GetTimestamp(attribute.access_time)
creation_time = self._GetTimestamp(attribute.creation_time)
change_time = self._GetTimestamp(attribute.entry_modification_time)
modification_time = self._GetTimestamp(attribute.modification_time)
yield '|'.join([
md5_string, attribute_name_value, inode_string, mode_string,
owner_identifier, group_identifier, size, access_time,
modification_time, change_time, creation_time])