|
4 | 4 | from typing import TYPE_CHECKING |
5 | 5 | from unittest.mock import patch |
6 | 6 |
|
| 7 | +import pytest |
| 8 | +from dissect.ntfs import NTFS |
| 9 | + |
7 | 10 | from dissect.target.filesystem import VirtualFilesystem |
| 11 | +from dissect.target.filesystems.dir import DirectoryFilesystem |
| 12 | +from dissect.target.helpers import loaderutil |
8 | 13 | from dissect.target.tools.mount import main as target_mount |
9 | 14 | from dissect.target.volume import Volume |
| 15 | +from tests._utils import absolute_path |
10 | 16 |
|
11 | 17 | if TYPE_CHECKING: |
12 | | - import pytest |
| 18 | + from pathlib import Path |
13 | 19 |
|
14 | 20 | from dissect.target.target import Target |
15 | 21 |
|
16 | 22 |
|
| 23 | +@pytest.fixture |
| 24 | +def mock_ntfs_dirfs(tmp_path: Path) -> Path: |
| 25 | + root = tmp_path |
| 26 | + |
| 27 | + # Cleanup first (conftest creates a MockTarget- tempfile) |
| 28 | + for item in root.iterdir(): |
| 29 | + item.unlink() |
| 30 | + |
| 31 | + # Create files |
| 32 | + (root / "$Boot").touch() |
| 33 | + (root / "$Extend_$Usnjrnl_$J").touch() |
| 34 | + (root / "$Secure_$SDS").touch() |
| 35 | + |
| 36 | + # Only need this to exist up until the root directory record to make dissect.ntfs happy |
| 37 | + with absolute_path("_data/plugins/filesystem/ntfs/mft/mft.raw").open("rb") as fh: |
| 38 | + (root / "$MFT").write_bytes(fh.read(10 * 1024)) |
| 39 | + |
| 40 | + return root |
| 41 | + |
| 42 | + |
17 | 43 | def test_duplicate_volume_name(target_bare: Target, monkeypatch: pytest.MonkeyPatch) -> None: |
18 | 44 | with monkeypatch.context() as m: |
19 | 45 | m.setattr("sys.argv", ["target-mount", "mock-target", "mock-mount"]) |
@@ -78,3 +104,53 @@ def test_mounting_multi_volume_filesystem(target_bare: Target, monkeypatch: pyte |
78 | 104 | vfs = MockDissectMount.call_args[0][0] |
79 | 105 |
|
80 | 106 | assert vfs.listdir("/filesystems") == ["first", "second"] |
| 107 | + |
| 108 | + |
| 109 | +def test_mounting_virtual_ntfs_filesystem( |
| 110 | + target_bare: Target, |
| 111 | + monkeypatch: pytest.MonkeyPatch, |
| 112 | + mock_ntfs_dirfs: Path, |
| 113 | +) -> None: |
| 114 | + with monkeypatch.context() as m: |
| 115 | + m.setattr("sys.argv", ["target-mount", "mock-target", "mock-mount"]) |
| 116 | + m.setattr("dissect.target.tools.mount.HAS_FUSE", True) |
| 117 | + |
| 118 | + with ( |
| 119 | + patch("dissect.target.tools.mount.open_target", return_value=target_bare), |
| 120 | + patch("dissect.target.tools.mount.FUSE", create=True) as MockFUSE, |
| 121 | + patch("dissect.target.tools.mount.DissectMount", create=True) as MockDissectMount, |
| 122 | + ): |
| 123 | + # Use the mock_ntfs_dirfs fixture for the NTFS files |
| 124 | + dir_fs = DirectoryFilesystem(mock_ntfs_dirfs) |
| 125 | + virtual_fs = VirtualFilesystem() |
| 126 | + virtual_fs.map_fs("", dir_fs) |
| 127 | + |
| 128 | + # Add the VirtualFileSystem |
| 129 | + target_bare.filesystems.add(virtual_fs) |
| 130 | + # Mount the VirtualFileSystem as c: |
| 131 | + target_bare.fs.mount("c:", virtual_fs) |
| 132 | + |
| 133 | + loaderutil.add_virtual_ntfs_filesystem( |
| 134 | + target_bare, |
| 135 | + virtual_fs, |
| 136 | + usnjrnl_path="$Extend_$Usnjrnl_$J", |
| 137 | + sds_path="$Secure_$SDS", |
| 138 | + ) |
| 139 | + |
| 140 | + # The loaderutil should now have made a mock NTFS |
| 141 | + ntfs_obj = getattr(virtual_fs, "ntfs", None) |
| 142 | + assert isinstance(ntfs_obj, NTFS) |
| 143 | + |
| 144 | + target_mount() |
| 145 | + |
| 146 | + MockFUSE.assert_called_once() |
| 147 | + MockDissectMount.assert_called_once() |
| 148 | + vfs = MockDissectMount.call_args[0][0] |
| 149 | + |
| 150 | + # Only the VirtualFileSystem should be available in /filesystems, not the mock NTFS |
| 151 | + assert vfs.listdir("/filesystems") == ["fs_0"] |
| 152 | + # The VirtualFileSystem should have the NTFS files |
| 153 | + required_ntfs_files = ["$Extend_$Usnjrnl_$J", "$MFT", "$Secure_$SDS", "$Boot"] |
| 154 | + assert all(file in required_ntfs_files for file in vfs.listdir("/filesystems/fs_0")) |
| 155 | + # The c: volume should be mounted, and should be the only mount |
| 156 | + assert vfs.listdir("/fs") == ["c:"] |
0 commit comments