Skip to content

Commit eec3035

Browse files
Merge pull request #81 from ajnelson-nist/review_tree_mft_py_with_mypy_strict
2 parents 1127470 + 72cfe41 commit eec3035

File tree

8 files changed

+97
-131
lines changed

8 files changed

+97
-131
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ check-mypy: \
5959
indxparse/INDXFind.py \
6060
indxparse/MFTINDX.py \
6161
indxparse/__init__.py \
62-
indxparse/list_mft.py
62+
indxparse/list_mft.py \
63+
indxparse/tree_mft.py
6364

6465
check-third_party:
6566
$(MAKE) \

indxparse/BinaryParser.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#
2525
# Version v.0.1
2626
import array
27-
import mmap
2827
import pickle
2928
import struct
3029
import sys
@@ -35,27 +34,6 @@
3534
verbose = False
3635

3736

38-
class Mmap(object):
39-
"""
40-
Convenience class for opening a read-only memory map for a file path.
41-
"""
42-
43-
def __init__(self, filename: str) -> None:
44-
super(Mmap, self).__init__()
45-
self._filename = filename
46-
self._f = None
47-
self._mmap = None
48-
49-
def __enter__(self):
50-
self._f = open(self._filename, "rb")
51-
self._mmap = mmap.mmap(self._f.fileno(), 0, access=mmap.ACCESS_READ)
52-
return self._mmap
53-
54-
def __exit__(self, type, value, traceback):
55-
self._mmap.close()
56-
self._f.close()
57-
58-
5937
def debug(*message):
6038
global verbose
6139
if verbose:

indxparse/MFT.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
# Version v.1.1.8
2727
import array
2828
import logging
29+
import mmap
2930
import os
3031
import struct
3132
import sys
@@ -1586,7 +1587,7 @@ def get(self, k):
15861587
class MFTEnumerator(object):
15871588
def __init__(
15881589
self,
1589-
buf: array.array,
1590+
buf: mmap.mmap,
15901591
record_cache=None,
15911592
path_cache=None,
15921593
) -> None:
@@ -1607,7 +1608,7 @@ def len(self) -> int:
16071608
else:
16081609
return floored_result + 1
16091610

1610-
def get_record_buf(self, record_num):
1611+
def get_record_buf(self, record_num: int) -> array.array:
16111612
"""
16121613
@raises OverrunBufferException: if the record_num is beyond the end of the MFT
16131614
"""
@@ -1766,7 +1767,7 @@ class MFTTree(object):
17661767

17671768
def __init__(
17681769
self,
1769-
buf: array.array,
1770+
buf: mmap.mmap,
17701771
) -> None:
17711772
super(MFTTree, self).__init__()
17721773
self._buf = buf

indxparse/extract_mft_record_slack.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,22 @@
2020
#
2121
# Alex Nelson, NIST, contributed to this file. Contributions of NIST
2222
# are not subject to US Copyright.
23+
import mmap
2324
import sys
2425

25-
from indxparse.BinaryParser import Mmap
2626
from indxparse.MFT import MFTEnumerator
2727

2828

2929
def main():
3030
filename = sys.argv[1]
3131

32-
with Mmap(filename) as buf:
33-
enum = MFTEnumerator(buf)
34-
for record in enum.enumerate_records():
35-
slack = record.slack_data()
36-
sys.stdout.write("\x00" * (1024 - len(slack)))
37-
sys.stdout.write(slack)
32+
with open(filename, "rb") as fh:
33+
with mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ) as mm:
34+
enum = MFTEnumerator(mm)
35+
for record in enum.enumerate_records():
36+
slack = record.slack_data()
37+
sys.stdout.write("\x00" * (1024 - len(slack)))
38+
sys.stdout.write(slack)
3839

3940

4041
if __name__ == "__main__":

indxparse/fuse-mft.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
import calendar
88
import errno
99
import inspect
10+
import mmap
1011
import os
1112
import stat
1213
import sys
14+
from typing import Dict
1315

1416
from fuse import FUSE, FuseOSError, Operations, fuse_get_context # type: ignore
1517

16-
from indxparse.BinaryParser import Mmap
1718
from indxparse.get_file_info import format_record
1819
from indxparse.MFT import Cache, MFTEnumerator, MFTRecord, MFTTree
1920
from indxparse.Progress import ProgressBarProgress
@@ -169,11 +170,11 @@ class MFTFuseOperations(Operations):
169170
MFTFuseOperations is a FUSE driver for NTFS MFT files.
170171
"""
171172

172-
def __init__(self, root, mfttree, buf):
173+
def __init__(self, root, mfttree, buf: mmap.mmap) -> None:
173174
self._root = root
174175
self._tree = mfttree
175176
self._buf = buf
176-
self._opened_files = {} # dict(int --> FH subclass)
177+
self._opened_files: Dict[int, FH] = {}
177178

178179
record_cache = Cache(1024)
179180
path_cache = Cache(1024)
@@ -402,11 +403,12 @@ def fsync(self, path, fdatasync, fh):
402403
def main():
403404
mft_filename = sys.argv[1]
404405
mountpoint = sys.argv[2]
405-
with Mmap(mft_filename) as buf:
406-
tree = MFTTree(buf)
407-
tree.build(progress_class=ProgressBarProgress)
408-
handler = MFTFuseOperations(mountpoint, tree, buf)
409-
FUSE(handler, mountpoint, foreground=True)
406+
with open(mft_filename, "rb") as fh:
407+
with mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ) as mm:
408+
tree = MFTTree(mm)
409+
tree.build(progress_class=ProgressBarProgress)
410+
handler = MFTFuseOperations(mountpoint, tree, mm)
411+
FUSE(handler, mountpoint, foreground=True)
410412

411413

412414
if __name__ == "__main__":

indxparse/get_file_info.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
import array
99
import datetime
1010
import logging
11+
import mmap
1112
import re
1213
from string import printable
1314
from typing import Any, Dict
1415

1516
from jinja2 import Template
1617

17-
from indxparse.BinaryParser import Mmap
1818
from indxparse.MFT import (
1919
ATTR_TYPE,
2020
MREF,
@@ -418,27 +418,28 @@ def main():
418418
if results.verbose:
419419
logging.basicConfig(level=logging.DEBUG)
420420

421-
with Mmap(results.mft) as buf:
422-
record_cache = Cache(results.cache_size)
423-
path_cache = Cache(results.cache_size)
421+
with open(results.mft, "rb") as fh:
422+
with mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ) as mm:
423+
record_cache = Cache(results.cache_size)
424+
path_cache = Cache(results.cache_size)
424425

425-
enum = MFTEnumerator(buf, record_cache=record_cache, path_cache=path_cache)
426+
enum = MFTEnumerator(mm, record_cache=record_cache, path_cache=path_cache)
426427

427-
should_use_inode = False
428-
try:
429-
record_num = int(results.record_or_path)
430-
should_use_inode = True
431-
except ValueError:
432428
should_use_inode = False
433-
434-
if should_use_inode:
435-
record = enum.get_record(record_num)
436-
path = results.prefix + enum.get_path(record)
437-
print_indx_info(record, path)
438-
else:
439-
path = results.record_or_path
440-
record = enum.get_record_by_path(path)
441-
print_indx_info(record, results.prefix + path)
429+
try:
430+
record_num = int(results.record_or_path)
431+
should_use_inode = True
432+
except ValueError:
433+
should_use_inode = False
434+
435+
if should_use_inode:
436+
record = enum.get_record(record_num)
437+
path = results.prefix + enum.get_path(record)
438+
print_indx_info(record, path)
439+
else:
440+
path = results.record_or_path
441+
record = enum.get_record_by_path(path)
442+
print_indx_info(record, results.prefix + path)
442443

443444

444445
if __name__ == "__main__":

indxparse/list_mft.py

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@
3737
import datetime
3838
import json
3939
import logging
40+
import mmap
4041
import sys
4142
import types
4243
from typing import Any, Dict, List, Optional, Type, Union
4344

4445
from jinja2 import Environment, Template
4546

46-
from indxparse.BinaryParser import Mmap
4747
from indxparse.get_file_info import make_model
4848
from indxparse.MFT import (
4949
ATTR_TYPE,
@@ -378,46 +378,48 @@ def main() -> None:
378378
else:
379379
progress_cls = NullProgress
380380

381-
with Mmap(results.filename) as buf:
382-
record_cache = Cache(results.cache_size)
383-
path_cache = Cache(results.cache_size)
384-
385-
enum = MFTEnumerator(buf, record_cache=record_cache, path_cache=path_cache)
386-
progress = progress_cls(enum.len())
387-
if use_default_output:
388-
for record, record_path in enum.enumerate_paths():
389-
output_mft_record(enum, record, results.prefix[0])
390-
progress.set_current(record.inode)
391-
elif results.json:
392-
393-
class MFTEncoder(json.JSONEncoder):
394-
def default(self, obj: Any) -> Any:
395-
if isinstance(obj, datetime.datetime):
396-
return obj.isoformat("T") + "Z"
397-
elif isinstance(obj, types.GeneratorType):
398-
return [o for o in obj]
399-
return json.JSONEncoder.default(self, obj)
400-
401-
print("[")
402-
record_count = 0
403-
for record, record_path in enum.enumerate_paths():
404-
if record_count > 0:
405-
print(",")
406-
record_count += 1
407-
m = make_model(record, record_path)
408-
print(json.dumps(m, cls=MFTEncoder, indent=2))
409-
progress.set_current(record.inode)
410-
print("]")
411-
else:
412-
for record, record_path in enum.enumerate_paths():
413-
sys.stdout.write(
414-
template.render(
415-
record=make_model(record, record_path), prefix=results.prefix[0]
381+
with open(results.filename, "rb") as fh:
382+
with mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ) as mm:
383+
record_cache = Cache(results.cache_size)
384+
path_cache = Cache(results.cache_size)
385+
386+
enum = MFTEnumerator(mm, record_cache=record_cache, path_cache=path_cache)
387+
progress = progress_cls(enum.len())
388+
if use_default_output:
389+
for record, record_path in enum.enumerate_paths():
390+
output_mft_record(enum, record, results.prefix[0])
391+
progress.set_current(record.inode)
392+
elif results.json:
393+
394+
class MFTEncoder(json.JSONEncoder):
395+
def default(self, obj: Any) -> Any:
396+
if isinstance(obj, datetime.datetime):
397+
return obj.isoformat("T") + "Z"
398+
elif isinstance(obj, types.GeneratorType):
399+
return [o for o in obj]
400+
return json.JSONEncoder.default(self, obj)
401+
402+
print("[")
403+
record_count = 0
404+
for record, record_path in enum.enumerate_paths():
405+
if record_count > 0:
406+
print(",")
407+
record_count += 1
408+
m = make_model(record, record_path)
409+
print(json.dumps(m, cls=MFTEncoder, indent=2))
410+
progress.set_current(record.inode)
411+
print("]")
412+
else:
413+
for record, record_path in enum.enumerate_paths():
414+
sys.stdout.write(
415+
template.render(
416+
record=make_model(record, record_path),
417+
prefix=results.prefix[0],
418+
)
419+
+ "\n"
416420
)
417-
+ "\n"
418-
)
419-
progress.set_current(record.inode)
420-
progress.set_complete()
421+
progress.set_current(record.inode)
422+
progress.set_complete()
421423

422424

423425
if __name__ == "__main__":

indxparse/tree_mft.py

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,6 @@
1010
from indxparse.MFT import Cache, MFTTree, MFTTreeNode
1111

1212

13-
class Mmap(object):
14-
"""
15-
Convenience class for opening a read-only memory map for a file path.
16-
"""
17-
18-
def __init__(self, filename):
19-
super(Mmap, self).__init__()
20-
self._filename = filename
21-
self._f = None
22-
self._mmap = None
23-
24-
def __enter__(self):
25-
self._f = open(self._filename, "rb")
26-
self._mmap = mmap.mmap(self._f.fileno(), 0, access=mmap.ACCESS_READ)
27-
return self._mmap
28-
29-
def __exit__(self, type, value, traceback):
30-
self._mmap.close()
31-
self._f.close()
32-
33-
3413
def main() -> None:
3514
parser = argparse.ArgumentParser(description="Parse MFT " "filesystem structures.")
3615
parser.add_argument(
@@ -52,19 +31,20 @@ def main() -> None:
5231
if results.verbose:
5332
logging.basicConfig(level=logging.DEBUG)
5433

55-
with Mmap(results.filename) as buf:
56-
record_cache = Cache(results.cache_size)
57-
path_cache = Cache(results.cache_size)
34+
with open(results.filename, "rb") as fh:
35+
with mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ) as mm:
36+
record_cache = Cache(results.cache_size)
37+
path_cache = Cache(results.cache_size)
5838

59-
tree = MFTTree(buf)
60-
tree.build(record_cache=record_cache, path_cache=path_cache)
39+
tree = MFTTree(mm)
40+
tree.build(record_cache=record_cache, path_cache=path_cache)
6141

62-
def rec(node: MFTTreeNode, prefix: str) -> None:
63-
print(prefix + node.get_filename())
64-
for child in node.get_children_nodes():
65-
rec(child, prefix + " ")
42+
def rec(node: MFTTreeNode, prefix: str) -> None:
43+
print(prefix + node.get_filename())
44+
for child in node.get_children_nodes():
45+
rec(child, prefix + " ")
6646

67-
rec(tree.get_root(), "")
47+
rec(tree.get_root(), "")
6848

6949

7050
if __name__ == "__main__":

0 commit comments

Comments
 (0)