Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 12 additions & 0 deletions dissect/target/tools/mount.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,19 @@ def main() -> int:
vnames.setdefault(basename, []).append(v)
vfs.map_file_fh(f"volumes/{fname}", v)

fake_ntfs = set()
for i, fs in enumerate(t.filesystems):
ntfs_obj = getattr(fs, "ntfs", None)

if ntfs_obj in fake_ntfs:
continue

if ntfs_obj and fs.__type__ != "ntfs":
# A non ntfs filesystem with a "ntfs" object means that add_virtual_ntfs_filesystem was used.
# The fake ntfs object is added to the filesystem after its parent.
# We will mount an entry in filesystems/ for the virtual filesystem, not for the "fake" ntfs filesystem.
fake_ntfs.add(ntfs_obj)

volumes = fs.volume if isinstance(fs.volume, list) else [fs.volume]
for volume in volumes:
default_name = f"fs_{i}"
Expand Down
78 changes: 77 additions & 1 deletion tests/tools/test_mount.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,42 @@
from typing import TYPE_CHECKING
from unittest.mock import patch

import pytest
from dissect.ntfs import NTFS

from dissect.target.filesystem import VirtualFilesystem
from dissect.target.filesystems.dir import DirectoryFilesystem
from dissect.target.helpers import loaderutil
from dissect.target.tools.mount import main as target_mount
from dissect.target.volume import Volume
from tests._utils import absolute_path

if TYPE_CHECKING:
import pytest
from pathlib import Path

from dissect.target.target import Target


@pytest.fixture
def mock_ntfs_dirfs(tmp_path: Path) -> Path:
root = tmp_path

# Cleanup first (conftest creates a MockTarget- tempfile)
for item in root.iterdir():
item.unlink()
Comment on lines +25 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
root = tmp_path
# Cleanup first (conftest creates a MockTarget- tempfile)
for item in root.iterdir():
item.unlink()
root = tmp_path / "mock_ntfs"
root.mkdir()

Instead of removing the file that gets created by target_bare fixture, it is better to just create a new directory for it instead


# Create files
(root / "$Boot").touch()
(root / "$Extend_$Usnjrnl_$J").touch()
(root / "$Secure_$SDS").touch()

# Only need this to exist up until the root directory record to make dissect.ntfs happy
with absolute_path("_data/plugins/filesystem/ntfs/mft/mft.raw").open("rb") as fh:
(root / "$MFT").write_bytes(fh.read(10 * 1024))

return root


def test_duplicate_volume_name(target_bare: Target, monkeypatch: pytest.MonkeyPatch) -> None:
with monkeypatch.context() as m:
m.setattr("sys.argv", ["target-mount", "mock-target", "mock-mount"])
Expand Down Expand Up @@ -78,3 +104,53 @@ def test_mounting_multi_volume_filesystem(target_bare: Target, monkeypatch: pyte
vfs = MockDissectMount.call_args[0][0]

assert vfs.listdir("/filesystems") == ["first", "second"]


def test_mounting_virtual_ntfs_filesystem(
target_bare: Target,
monkeypatch: pytest.MonkeyPatch,
mock_ntfs_dirfs: Path,
) -> None:
with monkeypatch.context() as m:
m.setattr("sys.argv", ["target-mount", "mock-target", "mock-mount"])
m.setattr("dissect.target.tools.mount.HAS_FUSE", True)

with (
patch("dissect.target.tools.mount.open_target", return_value=target_bare),
patch("dissect.target.tools.mount.FUSE", create=True) as MockFUSE,
patch("dissect.target.tools.mount.DissectMount", create=True) as MockDissectMount,
):
# Use the mock_ntfs_dirfs fixture for the NTFS files
dir_fs = DirectoryFilesystem(mock_ntfs_dirfs)
virtual_fs = VirtualFilesystem()
virtual_fs.map_fs("", dir_fs)

# Add the VirtualFileSystem
target_bare.filesystems.add(virtual_fs)
# Mount the VirtualFileSystem as c:
target_bare.fs.mount("c:", virtual_fs)

loaderutil.add_virtual_ntfs_filesystem(
target_bare,
virtual_fs,
usnjrnl_path="$Extend_$Usnjrnl_$J",
sds_path="$Secure_$SDS",
)

# The loaderutil should now have made a mock NTFS
ntfs_obj = getattr(virtual_fs, "ntfs", None)
assert isinstance(ntfs_obj, NTFS)

target_mount()

MockFUSE.assert_called_once()
MockDissectMount.assert_called_once()
vfs = MockDissectMount.call_args[0][0]

# Only the VirtualFileSystem should be available in /filesystems, not the mock NTFS
assert vfs.listdir("/filesystems") == ["fs_0"]
# The VirtualFileSystem should have the NTFS files
required_ntfs_files = ["$Extend_$Usnjrnl_$J", "$MFT", "$Secure_$SDS", "$Boot"]
assert all(file in required_ntfs_files for file in vfs.listdir("/filesystems/fs_0"))
# The c: volume should be mounted, and should be the only mount
assert vfs.listdir("/fs") == ["c:"]