Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read MFT records based on segment numbers #29

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions dissect/ntfs/mft.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class Mft:
def __init__(self, fh: BinaryIO, ntfs: Optional[NTFS] = None):
self.fh = fh
self.ntfs = ntfs
self.record_size = self.ntfs._record_size if self.ntfs else DEFAULT_RECORD_SIZE
Zawadidone marked this conversation as resolved.
Show resolved Hide resolved
self.mft_size = self.get(FILE_NUMBER_MFT).size()
Zawadidone marked this conversation as resolved.
Show resolved Hide resolved
self.last_segment_number = self.mft_size // self.record_size

self.get = lru_cache(4096)(self.get)

Expand Down Expand Up @@ -110,9 +113,7 @@ def get(self, ref: Union[int, str, Instance], root: Optional[MftRecord] = None)
ref = segment_reference(ref)

if isinstance(ref, int):
record_size = self.ntfs._record_size if self.ntfs else DEFAULT_RECORD_SIZE

record = MftRecord.from_fh(self.fh, ref * record_size, ntfs=self.ntfs)
record = MftRecord.from_fh(self.fh, ref * self.record_size, ntfs=self.ntfs)
record.segment = ref
return record
elif isinstance(ref, str):
Expand All @@ -122,10 +123,30 @@ def get(self, ref: Union[int, str, Instance], root: Optional[MftRecord] = None)

def segments(self) -> Iterator[MftRecord]:
"""Yield all valid MFT records, regardless if they're allocated or not."""
Zawadidone marked this conversation as resolved.
Show resolved Hide resolved
record_size = self.ntfs._record_size if self.ntfs else DEFAULT_RECORD_SIZE
mft_size = self.get(FILE_NUMBER_MFT).size()

for segment in range(mft_size // record_size):
for segment in range(self.last_segment_number):
try:
yield self.get(segment)
except Error:
continue
except EOFError:
break

def segments_by_range(self, start: int, end: int) -> Iterator[MftRecord]:
"""Yield all valid MFT records within the specified segment range, regardless if they're allocated or not.

Args:
start (int): The starting segment number.
end (int): The ending segment number.

Raises:
Error: If the start segment number is higher than the end segment number.
"""

if start > end:
raise Error("The start segment cannot be higher than the end segment number")
Zawadidone marked this conversation as resolved.
Show resolved Hide resolved

for segment in range(start, end + 1):
try:
yield self.get(segment)
except Error:
Expand Down
13 changes: 13 additions & 0 deletions tests/test_mft.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,16 @@ def test_mft_record() -> None:
record = mft.get(0)
assert record.filename == "$MFT"
assert record.filenames() == ["$MFT"]


def test_mft_records_segment_number(mft_bin: BinaryIO) -> None:
fs = NTFS(mft=mft_bin)

assert fs.mft
assert len(list(fs.mft.segments())) == 37

records = list(fs.mft.segments_by_range(0, 4))

assert len(records) == 5
assert records[0].filename == "$MFT"
assert records[4].filename == "$AttrDef"