Skip to content

Commit 2f2b612

Browse files
author
casework
committed
Move path_spec exporting to filestat event exporter.
1 parent 91d9612 commit 2f2b612

File tree

7 files changed

+108
-77
lines changed

7 files changed

+108
-77
lines changed

case_plaso/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11

22
import rdflib
3-
from rdflib import RDF
4-
5-
# Store CASE binding in here, because it needs to be used in many modules.
6-
CASE = rdflib.Namespace('http://case.example.org/core#')
73

84
# Store custom properties and objects not defined in CASE as the PLASO prefix.
95
PLASO = rdflib.Namespace('http://plaso.example.org/core#')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# -*- coding: utf-8 -*-
22

33
from case_plaso.event_exporters import android_calls
4+
from case_plaso.event_exporters import filestat
45
from case_plaso.event_exporters import ntfs
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
2+
import os
3+
4+
from case import CASE
5+
from case_plaso import event_exporter, lib, mappings, property_bundles
6+
7+
8+
@event_exporter.register
9+
class FileStatExporter(event_exporter.EventExporter):
10+
11+
DATA_TYPE = 'fs:stat'
12+
13+
TIMESTAMP_MAP = {
14+
'atime': 'accessedTime',
15+
'ctime': 'metadataChangedTime',
16+
'crtime': 'createdTime',
17+
'mtime': 'modifiedTime'}
18+
19+
def __init__(self, document):
20+
super(FileStatExporter, self).__init__(document)
21+
self._path_spec_traces = {}
22+
23+
def export_path_spec(self, path_spec):
24+
"""Exports the given DFVFS path spec into the graph.
25+
26+
Returns: tuple containing URIRefs for Trace and File property bundle.
27+
"""
28+
comparable = path_spec.comparable
29+
if comparable in self._path_spec_traces:
30+
return self._path_spec_traces[comparable]
31+
32+
trace = self.document.create_trace()
33+
file_pb = trace.create_property_bundle('File')
34+
35+
self._path_spec_traces[comparable] = (trace, file_pb)
36+
37+
# Add file path information.
38+
location = getattr(path_spec, 'location', None)
39+
if location:
40+
file_pb.add('filePath', location)
41+
file_name, extension = os.path.splitext(os.path.basename(location))
42+
file_pb.add('fileName', file_name)
43+
if extension:
44+
file_pb.add('extension', extension)
45+
46+
file_pb.add(
47+
'fileSystemType', mappings.FileSystemType.get(path_spec.type_indicator, None))
48+
49+
# If path spec has a parent, create the parent then create a relationship
50+
# object pointing to its parent.
51+
if path_spec.HasParent():
52+
parent_trace, _ = self.export_path_spec(path_spec.parent)
53+
relationship = self.document.create_relationship(
54+
source=trace,
55+
target=parent_trace,
56+
kindOfRelationship=mappings.kindOfRelationship.get(
57+
path_spec.type_indicator, mappings.kindOfRelationship['_default']),
58+
# TODO: Not exactly sure what isDirectional means..
59+
isDirectional=True)
60+
61+
# Add a property bundle to relationship if available.
62+
property_bundles.construct(path_spec.type_indicator, relationship)
63+
64+
return trace, file_pb
65+
66+
def export_event(self, event):
67+
trace, file_pb = self.export_path_spec(event.pathspec)
68+
# NOTE: Re-adding the same property is fine. Duplicate triples will be removed.
69+
file_pb.add(
70+
'fileSystemType', mappings.FileSystemType.get(event.file_system_type, None))
71+
file_pb.add('isAllocated', event.is_allocated)
72+
file_pb.add('fileSize', getattr(event, 'file_size', None))
73+
# TODO: What is file_entry_type?
74+
75+
# Add timestamps.
76+
if event.timestamp_desc in self.TIMESTAMP_MAP:
77+
file_pb.add(
78+
self.TIMESTAMP_MAP[event.timestamp_desc],
79+
lib.convert_timestamp(event.timestamp))
80+
81+
# Add file system specific property bundles.
82+
# TODO: Is there anyway to get more information?
83+
elif event.timestamp_desc == 'bkup_time':
84+
trace.create_property_bundle(
85+
'HFSFileSystem',
86+
hfsBackupTime=lib.convert_timestamp(event.timestamp))
87+
elif event.timestamp_desc == 'dtime':
88+
trace.create_property_bundle(
89+
'ExtInode',
90+
extDeletionTime=lib.convert_timestamp(event.timestamp))

case_plaso/event_exporters/ntfs.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
from plaso.lib.eventdata import EventTimestamp
33

4+
from case import CASE
45
from case_plaso import event_exporter, lib
56

67

@@ -16,8 +17,9 @@ class NTFSExporter(event_exporter.EventExporter):
1617
EventTimestamp.ENTRY_MODIFICATION_TIME: 'mftFileNameRecordChangeTime'}
1718

1819
def export_event_data(self, event):
19-
# TODO: Figure out how to associate MftRecord pb with the accosiated
20+
# TODO: Figure out how to associate MftRecord pb with the associated
2021
# File pb so we don't have to make separate traces.
22+
# (The path spec points to the $Mft file.)
2123
trace = self.document.create_trace()
2224
pb = trace.create_property_bundle(
2325
'MftRecord',
@@ -28,7 +30,9 @@ def export_event_data(self, event):
2830

2931
def export_timestamp(self, event, pb):
3032
try:
31-
pb.add(self.TIMESTAMP_MAP[event.timestamp_desc], lib.convert_timestamp(event.timestamp))
33+
pb.add(
34+
self.TIMESTAMP_MAP[event.timestamp_desc],
35+
lib.convert_timestamp(event.timestamp))
3236
except KeyError:
3337
# TODO: Log this or something.
3438
pass

case_plaso/mappings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from dfvfs.lib import definitions as dfvfs_definitions
44

5-
from case_plaso import CASE
5+
from case import CASE
66

77
# Maps dfvfs compression methods to CASE CompressionMethod
88
CompressionMethod = {

case_plaso/plaso_exporter.py

Lines changed: 9 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11

22
import os
3-
import rdflib
4-
from rdflib import RDF, OWL, BNode, Literal
53
from dfvfs.lib import definitions as dfvfs_definitions
64
from plaso.storage import zip_file
75

@@ -26,13 +24,10 @@ def __init__(self, document):
2624
self.document = document
2725
# Add 'plaso' prefix used by custom property bundles to internal graph.
2826
self.document.graph.namespace_manager.bind('plaso', PLASO)
29-
self._path_spec_traces = {}
30-
self._event_traces = {}
3127
self._event_exporters = {}
3228

33-
def get_event_exporter(self, event):
34-
"""Retrieves event exporter for given event."""
35-
data_type = event.data_type
29+
def get_event_exporter(self, data_type):
30+
"""Retrieves event exporter for given event data_type."""
3631
if data_type not in self._event_exporters:
3732
self._event_exporters[data_type] = EventExporter.from_data_type(
3833
data_type, self.document)
@@ -43,43 +38,10 @@ def export_path_spec(self, path_spec):
4338
4439
Returns: tuple containing URIRefs for Trace and File property bundle.
4540
"""
46-
comparable = path_spec.comparable
47-
if comparable in self._path_spec_traces:
48-
# TODO: When filestat events call this, it will want to add more timestamps to the file pb.
49-
return self._path_spec_traces[comparable]
50-
51-
trace = self.document.create_trace()
52-
file_pb = trace.create_property_bundle('File')
53-
54-
self._path_spec_traces[comparable] = (trace, file_pb)
55-
56-
# Add file path information.
57-
location = getattr(path_spec, 'location', None)
58-
if location:
59-
file_pb.add('filePath', location)
60-
file_name, extension = os.path.splitext(os.path.basename(location))
61-
file_pb.add('fileName', file_name)
62-
file_pb.add('extension', extension)
63-
64-
file_pb.add(
65-
'fileSystemType', mappings.FileSystemType.get(path_spec.type_indicator, None))
66-
67-
# If path spec has a parent, create the parent then create a relationship
68-
# object pointing to its parent.
69-
if path_spec.HasParent():
70-
parent_trace, _ = self.export_path_spec(path_spec.parent)
71-
relationship = self.document.create_relationship(
72-
source=trace,
73-
target=parent_trace,
74-
kindOfRelationship=mappings.kindOfRelationship.get(
75-
path_spec.type_indicator, mappings.kindOfRelationship['_default']),
76-
# TODO: Not exactly sure what isDirectional means..
77-
isDirectional=True)
78-
79-
# Add a property bundle to relationship if available.
80-
property_bundles.construct(path_spec.type_indicator, relationship)
81-
82-
return trace, file_pb
41+
# The event exporter for 'fs:stat' contains functionality to export
42+
# path_specs.
43+
file_stat_exporter = self.get_event_exporter('fs:stat')
44+
return file_stat_exporter.export_path_spec(path_spec)
8345

8446
def export_event_source(self, event_source):
8547
if event_source.file_entry_type == dfvfs_definitions.FILE_ENTRY_TYPE_DEVICE:
@@ -96,20 +58,9 @@ def export_event_source(self, event_source):
9658
pass
9759

9860
def export_event(self, event):
99-
# Append extra file stat information to the "File" property bundle.
100-
if event.data_type == 'fs:stat':
101-
trace, file_pb = self.export_path_spec(event.pathspec)
102-
file_pb.add(
103-
'fileSystemType', mappings.FileSystemType.get(event.file_system_type, None))
104-
file_pb.add('isAllocated', event.is_allocated)
105-
file_pb.add('fileSize', getattr(event, 'file_size', None))
106-
107-
# # Add extra file system specific property bundles. (eg. MFtRecord, Inode)
108-
# property_bundles.construct(event.data_type, trace, event)
109-
110-
else:
111-
event_exporter = self.get_event_exporter(event)
112-
event_exporter.export_event(event)
61+
"""Exports the given plaso EventObject into the graph."""
62+
event_exporter = self.get_event_exporter(event.data_type)
63+
event_exporter.export_event(event)
11364

11465
def export_storage_file(self, storage_file):
11566
"""Extracts and exports plaso event data and sources into the graph."""

case_plaso/property_bundles.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
"""Contains constructors for converting dfvfs and plaso entities into CASE property bundles."""
22

3-
from rdflib import RDF, Literal, BNode
43
from dfvfs.lib import definitions as dfvfs_definitions
54

6-
from case_plaso import CASE, mappings
5+
from case_plaso import mappings
76

87

98
# Register functions based on indicators.
@@ -62,13 +61,3 @@ def SQLiteBlob(uco_object, path_spec):
6261
pb.add('rowIndex', path_spec.row_index)
6362
elif hasattr(path_spec, 'row_condition'):
6463
pb.add('rowCondition', ' '.join(path_spec.row_condition))
65-
66-
67-
@register('fs:stat:ntfs')
68-
def MftRecord(uco_object, event):
69-
uco_object.create_property_bundle(
70-
'MftRecord',
71-
mftFileID=getattr(event, 'file_reference', None),
72-
mftFlags=getattr(event, 'file_attribute_flags', None),
73-
mftParentID=getattr(event, 'parent_file_reference', None))
74-

0 commit comments

Comments
 (0)