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

Change linter to Ruff #21

Merged
merged 1 commit into from
Jan 23, 2025
Merged
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
6 changes: 3 additions & 3 deletions dissect/thumbcache/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
__all__ = [
"Error",
"IndexEntry",
"ThumbnailIndex",
"ThumbcacheFile",
"ThumbcacheEntry",
"Thumbcache",
"ThumbcacheEntry",
"ThumbcacheFile",
"ThumbnailIndex",
]
6 changes: 4 additions & 2 deletions dissect/thumbcache/c_thumbcache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

from dissect.cstruct import cstruct

thumbcache_index_def = """
thumbcache_def = """
struct INDEX_HEADER_V1 {
char Signature[4]; // 0x00
uint32 Version; // 0x04
Expand Down Expand Up @@ -75,4 +77,4 @@
uint32 Unknown; // 0x28
}; // 0x2C
"""
c_thumbcache_index = cstruct().load(thumbcache_index_def)
c_thumbcache = cstruct().load(thumbcache_def)
8 changes: 0 additions & 8 deletions dissect/thumbcache/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
class Error(Exception):
"""A generic exception for the thumbcache module."""

pass


class NotAnIndexFileError(Error):
"""Raises if a thumbnail index signature could not be found."""

pass


class InvalidSignatureError(Error):
"""Raises if the signature does not match the expected value."""

pass


class UnknownThumbnailTypeError(Error):
"""Raises if an unknown thumbnail type was found."""

pass
53 changes: 27 additions & 26 deletions dissect/thumbcache/index.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from __future__ import annotations

from datetime import datetime
from typing import BinaryIO, Iterator
from typing import TYPE_CHECKING, BinaryIO

from dissect.util import ts

from dissect.thumbcache.c_thumbcache import c_thumbcache_index
from dissect.thumbcache.c_thumbcache import c_thumbcache
from dissect.thumbcache.exceptions import NotAnIndexFileError
from dissect.thumbcache.util import ThumbnailType

if TYPE_CHECKING:
from collections.abc import Iterator
from datetime import datetime

Check warning on line 13 in dissect/thumbcache/index.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/index.py#L12-L13

Added lines #L12 - L13 were not covered by tests

INDEX_ENTRIES = {
ThumbnailType.WINDOWS_7: 5,
ThumbnailType.WINDOWS_81: 11,
Expand All @@ -29,12 +32,12 @@
self._header = None

@property
def header(self) -> c_thumbcache_index.INDEX_HEADER_V1 | c_thumbcache_index.INDEX_HEADER_V2:
def header(self) -> c_thumbcache.INDEX_HEADER_V1 | c_thumbcache.INDEX_HEADER_V2:
if self._header is None:
self._header = self._find_header(self.fh)
return self._header

def _find_header(self, fh: BinaryIO) -> c_thumbcache_index.INDEX_HEADER_V1 | c_thumbcache_index.INDEX_HEADER_V2:
def _find_header(self, fh: BinaryIO) -> c_thumbcache.INDEX_HEADER_V1 | c_thumbcache.INDEX_HEADER_V2:
"""Searches for the header signature, and puts ``fh`` at the correct position.

From Windows 8.1 onward, the two fields seem to use a 64-bit format field
Expand All @@ -44,19 +47,19 @@
fh: The file to read the header and indexes from.

Returns:
A c_thumbcache_index.INDEX_HEADER structure.
A c_thumbcache.INDEX_HEADER structure.

Raises:
NotAThumbnailIndexFileError: If the ``IMMM`` signature could not be found.
"""
position = fh.tell()
buffer = fh.read(len(c_thumbcache_index.INDEX_HEADER_V1))
buffer = fh.read(len(c_thumbcache.INDEX_HEADER_V1))
offset = buffer.find(self._signature)

if offset == MAX_IMM_OFFSET:
fh.seek(position)

header = c_thumbcache_index.INDEX_HEADER_V2(fh)
header = c_thumbcache.INDEX_HEADER_V2(fh)
# From looking at the index files, it has a specific amount of information.
# It is alligned in the follwing way:
# INDEX_HEADER_V2
Expand All @@ -68,19 +71,18 @@

# Read one index entry from the file till only zero bytes
entry = IndexEntry(fh, header.Version)
entry.header
entry.cache_offsets
_ = entry.header
_ = entry.cache_offsets

# Read offset to first entry
zero_bytes = len(entry.header) + INDEX_ENTRIES.get(header.Version) * BYTES_IN_NUMBER - len(header)
fh.read(zero_bytes)
return header
elif offset == 0:
return c_thumbcache_index.INDEX_HEADER_V1(buffer)
else:
raise NotAnIndexFileError(
f"The index file signature {self._signature!r} could not be found at the expected location."
)
if offset == 0:
return c_thumbcache.INDEX_HEADER_V1(buffer)
raise NotAnIndexFileError(
f"The index file signature {self._signature!r} could not be found at the expected location."
)

@property
def version(self) -> int:
Expand All @@ -102,8 +104,8 @@
"""Returns all index entries that are actually used."""
for _ in range(self.total_entries):
entry = IndexEntry(self.fh, self.type)
entry.header
entry.cache_offsets
_ = entry.header
_ = entry.cache_offsets

if entry.in_use():
yield entry
Expand All @@ -119,21 +121,20 @@
@property
def header(
self,
) -> c_thumbcache_index.VISTA_ENTRY | c_thumbcache_index.WINDOWS7_ENTRY | c_thumbcache_index.WINDOWS8_ENTRY:
) -> c_thumbcache.VISTA_ENTRY | c_thumbcache.WINDOWS7_ENTRY | c_thumbcache.WINDOWS8_ENTRY:
if not self._header:
self._header = self._select_header()
return self._header

def _select_header(
self,
) -> c_thumbcache_index.VISTA_ENTRY | c_thumbcache_index.WINDOWS7_ENTRY | c_thumbcache_index.WINDOWS8_ENTRY:
) -> c_thumbcache.VISTA_ENTRY | c_thumbcache.WINDOWS7_ENTRY | c_thumbcache.WINDOWS8_ENTRY:
"""Selects header version according to the thumbnailtype."""
if self.type == ThumbnailType.WINDOWS_VISTA:
return c_thumbcache_index.VISTA_ENTRY(self.fh)
elif self.type == ThumbnailType.WINDOWS_7:
return c_thumbcache_index.WINDOWS7_ENTRY(self.fh)
else:
return c_thumbcache_index.WINDOWS8_ENTRY(self.fh)
return c_thumbcache.VISTA_ENTRY(self.fh)
if self.type == ThumbnailType.WINDOWS_7:
return c_thumbcache.WINDOWS7_ENTRY(self.fh)
return c_thumbcache.WINDOWS8_ENTRY(self.fh)

def in_use(self) -> bool:
return self.identifier != b"\x00" * IDENTIFIER_BYTES
Expand All @@ -156,7 +157,7 @@
"""
if not self._data:
size = INDEX_ENTRIES.get(self.type)
self._data = c_thumbcache_index.uint32[size](self.fh)
self._data = c_thumbcache.uint32[size](self.fh)
if self.type > ThumbnailType.WINDOWS_7:
# Alignment step
self.fh.read((size % 2) * BYTES_IN_NUMBER)
Expand Down
12 changes: 8 additions & 4 deletions dissect/thumbcache/thumbcache.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
from pathlib import Path
from typing import Iterator
from __future__ import annotations

from typing import TYPE_CHECKING

from dissect.thumbcache.index import IndexEntry, ThumbnailIndex
from dissect.thumbcache.thumbcache_file import ThumbcacheEntry, ThumbcacheFile

if TYPE_CHECKING:
from collections.abc import Iterator
from pathlib import Path

Check warning on line 10 in dissect/thumbcache/thumbcache.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/thumbcache.py#L9-L10

Added lines #L9 - L10 were not covered by tests


class Thumbcache:
"""This class combines the thumbnailindex and thumbcachefile.
Expand Down Expand Up @@ -50,8 +55,7 @@
def index_entries(self) -> Iterator[IndexEntry]:
"""Iterates through all the index entries that are in use."""
with self.index_file.open("rb") as i_file:
for entry in ThumbnailIndex(i_file).entries():
yield entry
yield from ThumbnailIndex(i_file).entries()

def _entries_from_offsets(self, offsets: list[int]) -> Iterator[tuple[Path, ThumbcacheEntry]]:
"""Retrieves Thumbcache entries from a ThumbcacheFile using offsets."""
Expand Down
49 changes: 25 additions & 24 deletions dissect/thumbcache/thumbcache_file.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
from __future__ import annotations

from typing import Any, BinaryIO
from typing import TYPE_CHECKING, Any, BinaryIO

from dissect.thumbcache.c_thumbcache import c_thumbcache_index
from dissect.thumbcache.c_thumbcache import c_thumbcache
from dissect.thumbcache.exceptions import (
InvalidSignatureError,
UnknownThumbnailTypeError,
)
from dissect.thumbcache.util import ThumbnailType, seek_and_return

if TYPE_CHECKING:
from collections.abc import Iterator

Check warning on line 13 in dissect/thumbcache/thumbcache_file.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/thumbcache_file.py#L13

Added line #L13 was not covered by tests

UNKNOWN_BYTES = 8


class ThumbcacheFile:
__slots__ = [
"fh",
"_cached_entries",
"_entries",
"_header",
"fh",
"offset",
"signature",
"type",
"size",
"offset",
"_entries",
"_cached_entries",
"type",
]

"""
Expand All @@ -39,21 +42,20 @@
self._header = self._get_header_type(self.fh)
self._cached_entries: dict[int, ThumbcacheEntry] = {}

def _get_header_type(self, fh: BinaryIO) -> c_thumbcache_index.CACHE_HEADER_VISTA | c_thumbcache_index.CACHE_HEADER:
tmp_header = c_thumbcache_index.CACHE_HEADER(fh)
def _get_header_type(self, fh: BinaryIO) -> c_thumbcache.CACHE_HEADER_VISTA | c_thumbcache.CACHE_HEADER:
tmp_header = c_thumbcache.CACHE_HEADER(fh)

if self._signature != tmp_header.Signature:
raise InvalidSignatureError(
f"The signature {tmp_header.Signature!r} does not match the expected {self._signature!r}"
)

if tmp_header.Version <= ThumbnailType.WINDOWS_7:
return c_thumbcache_index.CACHE_HEADER_VISTA(tmp_header.dumps())
else:
return tmp_header
return c_thumbcache.CACHE_HEADER_VISTA(tmp_header.dumps())
return tmp_header

@property
def header(self) -> c_thumbcache_index.CACHE_HEADER_VISTA | c_thumbcache_index.CACHE_HEADER:
def header(self) -> c_thumbcache.CACHE_HEADER_VISTA | c_thumbcache.CACHE_HEADER:
return self._header

@property
Expand All @@ -72,15 +74,15 @@
self._cached_entries[key] = item
return item

def __getattribute__(self, __name: str) -> Any:
def __getattribute__(self, name: str) -> Any:
try:
return object.__getattribute__(self, __name)
return object.__getattribute__(self, name)
except AttributeError:
pass

return getattr(self.header, __name.capitalize())
return getattr(self.header, name.capitalize())

def entries(self) -> list[ThumbcacheEntry]:
def entries(self) -> Iterator[ThumbcacheEntry]:
with seek_and_return(self.fh, self.fh.tell()):
try:
while True:
Expand Down Expand Up @@ -123,22 +125,21 @@
fh.read(UNKNOWN_BYTES)
additional_bytes += UNKNOWN_BYTES

self.data_checksum: bytes = c_thumbcache_index.char[8](fh)
self.header_checksum: bytes = c_thumbcache_index.char[8](fh)
self.data_checksum: bytes = c_thumbcache.char[8](fh)
self.header_checksum: bytes = c_thumbcache.char[8](fh)

self.identifier: str = c_thumbcache_index.wchar[self._header.IdentifierSize // 2](fh)
self.identifier: str = c_thumbcache.wchar[self._header.IdentifierSize // 2](fh)

header_size = len(self._header) + self._header.IdentifierSize + additional_bytes

self._data = fh.read(self._header.Size - header_size)

def _get_header(
self, thumbnail_type: ThumbnailType
) -> type[c_thumbcache_index.CACHE_ENTRY_VISTA | c_thumbcache_index.CACHE_ENTRY]:
) -> type[c_thumbcache.CACHE_ENTRY_VISTA | c_thumbcache.CACHE_ENTRY]:
if thumbnail_type == ThumbnailType.WINDOWS_VISTA:
return c_thumbcache_index.CACHE_ENTRY_VISTA
else:
return c_thumbcache_index.CACHE_ENTRY
return c_thumbcache.CACHE_ENTRY_VISTA
return c_thumbcache.CACHE_ENTRY

@property
def hash(self) -> str:
Expand Down
10 changes: 7 additions & 3 deletions dissect/thumbcache/tools/extract_images.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#!/bin/python3
from __future__ import annotations

Check warning on line 2 in dissect/thumbcache/tools/extract_images.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_images.py#L2

Added line #L2 was not covered by tests

from pathlib import Path
from typing import TYPE_CHECKING

Check warning on line 4 in dissect/thumbcache/tools/extract_images.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_images.py#L4

Added line #L4 was not covered by tests

from dissect.thumbcache.exceptions import Error
from dissect.thumbcache.thumbcache_file import ThumbcacheFile
from dissect.thumbcache.tools.utils import create_argument_parser, write_entry

if TYPE_CHECKING:
from pathlib import Path

Check warning on line 11 in dissect/thumbcache/tools/extract_images.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_images.py#L10-L11

Added lines #L10 - L11 were not covered by tests

def dump_entry_data(path: Path, output_dir: Path):

def dump_entry_data(path: Path, output_dir: Path) -> None:

Check warning on line 14 in dissect/thumbcache/tools/extract_images.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_images.py#L14

Added line #L14 was not covered by tests
with path.open("rb") as file:
try:
cache_file = ThumbcacheFile(file)
Expand All @@ -18,7 +22,7 @@
print(e)


def main():
def main() -> None:

Check warning on line 25 in dissect/thumbcache/tools/extract_images.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_images.py#L25

Added line #L25 was not covered by tests
parser = create_argument_parser("extract raw thumbcache entries")
args = parser.parse_args()
path: Path = args.cache_path
Expand Down
11 changes: 8 additions & 3 deletions dissect/thumbcache/tools/extract_with_index.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
from pathlib import Path
from __future__ import annotations

Check warning on line 1 in dissect/thumbcache/tools/extract_with_index.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_with_index.py#L1

Added line #L1 was not covered by tests

from typing import TYPE_CHECKING

Check warning on line 3 in dissect/thumbcache/tools/extract_with_index.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_with_index.py#L3

Added line #L3 was not covered by tests

from dissect.thumbcache.exceptions import Error
from dissect.thumbcache.thumbcache import Thumbcache
from dissect.thumbcache.tools.utils import create_argument_parser, write_entry

if TYPE_CHECKING:
from pathlib import Path

Check warning on line 10 in dissect/thumbcache/tools/extract_with_index.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_with_index.py#L9-L10

Added lines #L9 - L10 were not covered by tests


def dump_entry_data_through_index(path: Path, output_dir: Path, prefix: str):
def dump_entry_data_through_index(path: Path, output_dir: Path, prefix: str) -> None:

Check warning on line 13 in dissect/thumbcache/tools/extract_with_index.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_with_index.py#L13

Added line #L13 was not covered by tests
cache = Thumbcache(path=path, prefix=prefix)
try:
for location_path, entry in cache.entries():
Expand All @@ -15,7 +20,7 @@
print(e)


def main():
def main() -> None:

Check warning on line 23 in dissect/thumbcache/tools/extract_with_index.py

View check run for this annotation

Codecov / codecov/patch

dissect/thumbcache/tools/extract_with_index.py#L23

Added line #L23 was not covered by tests
parser = create_argument_parser("extract indexed entries")
parser.add_argument(
"--prefix",
Expand Down
Loading
Loading