Skip to content

Commit

Permalink
Merge main into branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Poeloe committed Oct 29, 2024
2 parents a48d969 + 7d65c2b commit 754a8aa
Show file tree
Hide file tree
Showing 222 changed files with 4,786 additions and 1,397 deletions.
5 changes: 4 additions & 1 deletion dissect/target/filesystems/btrfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ def lstat(self) -> fsutil.stat_result:

# Add block information of the filesystem
st_info.st_blksize = entry.btrfs.sector_size
st_info.st_blocks = math.ceil(entry.size / st_info.st_blksize)

st_info.st_blocks = 0
if not self.is_dir():
st_info.st_blocks = (st_info.st_blksize // 512) * math.ceil(st_info.st_size / st_info.st_blksize)

return st_info
6 changes: 5 additions & 1 deletion dissect/target/filesystems/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class ConfigurationEntry(FilesystemEntry):
Behaves like a ``directory`` when :attr:`parser_items` is a :class:`.ConfigurationParser` or a ``dict``.
Behaves like a ``file`` otherwise.
Attributes:
Args:
parser_items: A dict-like object containing all configuration entries and values.
In most cases this is either a :class:`.ConfigurationParser` or ``dict``.
Otherwise, its the entry's value
Expand Down Expand Up @@ -247,10 +247,14 @@ def open(self) -> BinaryIO:
Returns:
A file-like object holding a byte representation of :attr:`parser_items`.
"""

if isinstance(self.parser_items, ConfigurationParser):
# Currently trying to open the underlying entry
return self.entry.open()

if isinstance(self.parser_items, bytes):
return io.BytesIO(self.parser_items)

output_data = self._write_value_mapping(self.parser_items)
return io.BytesIO(bytes(output_data, "utf-8"))

Expand Down
1 change: 1 addition & 0 deletions dissect/target/filesystems/extfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def lstat(self) -> fsutil.stat_result:
# Set birthtime if available
if self.entry.crtime:
st_info.st_birthtime = self.entry.crtime.timestamp()
st_info.st_birthtime_ns = self.entry.crtime_ns

# Set the nanosecond resolution separately
st_info.st_atime_ns = self.entry.atime_ns
Expand Down
5 changes: 5 additions & 0 deletions dissect/target/filesystems/jffs.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,9 @@ def lstat(self) -> fsutil.stat_result:
]
)

# JFFS2 block size is a function of the "erase size" of the underlying flash device.
# Linux stat reports the default block size, which is defined as 4k in libc.
st_info.st_blksize = 4096
st_info.st_blocks = (node.isize + 511) // 512 if self.is_file() else 0

return st_info
20 changes: 18 additions & 2 deletions dissect/target/filesystems/ntfs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import math
import stat
from typing import BinaryIO, Iterator, Optional

Expand Down Expand Up @@ -151,12 +152,14 @@ def lstat(self) -> fsutil.stat_result:
record = self.dereference()

size = 0
real_size = 0
if self.is_symlink():
mode = stat.S_IFLNK
elif self.is_file():
mode = stat.S_IFREG
try:
size = record.size(self.ads)
real_size = record.size(self.ads, allocated=True)
except NtfsFileNotFoundError as e:
# Occurs when it cannot find the the specific ads inside its attributes
raise FileNotFoundError from e
Expand All @@ -176,16 +179,29 @@ def lstat(self) -> fsutil.stat_result:
0,
size,
stdinfo.last_access_time.timestamp(),
stdinfo.last_change_time.timestamp(),
stdinfo.last_modification_time.timestamp(),
# ctime gets set to creation time for python <3.12 purposes
stdinfo.creation_time.timestamp(),
]
)

# Set the nanosecond resolution separately
st_info.st_atime_ns = stdinfo.last_access_time_ns
st_info.st_mtime_ns = stdinfo.last_change_time_ns
st_info.st_mtime_ns = stdinfo.last_modification_time_ns

st_info.st_ctime_ns = stdinfo.creation_time_ns

st_info.st_birthtime = stdinfo.creation_time.timestamp()
st_info.st_birthtime_ns = stdinfo.creation_time_ns

# real_size is none if the size is resident
st_info.st_blksize = record.ntfs.cluster_size
blocks = 0
if not record.resident:
blocks = math.ceil(real_size / 512)

st_info.st_blocks = blocks

return st_info

def attr(self) -> AttributeMap:
Expand Down
18 changes: 14 additions & 4 deletions dissect/target/filesystems/overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,25 @@ def __init__(self, path: Path, *args, **kwargs):

# append and mount every layer
for dest, layer in layers:
if layer.is_file() and dest in ["/etc/hosts", "/etc/hostname", "/etc/resolv.conf"]:
# we could have collected a layer reference that actually does not exist on the host
if not layer.exists():
log.warning(
"Can not mount layer %s for container %s as it does not exist on the host", layer, path.name
)
continue

# mount points can be files
if layer.is_file():
layer_fs = VirtualFilesystem()
layer_fs.map_file_fh("/etc/" + layer.name, layer.open("rb"))
dest = dest.split("/")[0]
layer_fs.map_file_fh(dest, layer.open("rb"))

# regular overlay2 layers are directories
# mount points can be directories too
else:
layer_fs = DirectoryFilesystem(layer)

self.append_layer().mount(dest, layer_fs)
log.info("Adding layer %s to destination %s", layer, dest)
self.append_layer().mount("/" if layer.is_file() else dest, layer_fs)

def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self.base_path}>"
9 changes: 8 additions & 1 deletion dissect/target/filesystems/xfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,15 @@ def lstat(self) -> fsutil.stat_result:
st_info.st_mtime_ns = self.entry.mtime_ns
st_info.st_ctime_ns = self.entry.ctime_ns

# XFS has a birth time, called crtime
st_info.st_blksize = self.fs.xfs.block_size
# Convert number of filesystem blocks to basic blocks
# Reference: https://github.com/torvalds/linux/blob/e32cde8d2bd7d251a8f9b434143977ddf13dcec6/fs/xfs/xfs_iops.c#L602 # noqa: E501
# Note that block size in XFS is always a multiple of 512, so the division below is safe
st_info.st_blocks = self.entry.nblocks * (self.fs.xfs.block_size // 512)

# XFS has a birth time, since inode version 3 (version 5 of filesystem)
st_info.st_birthtime = self.entry.crtime.timestamp()
st_info.st_birthtime_ns = self.entry.crtime_ns

return st_info

Expand Down
10 changes: 5 additions & 5 deletions dissect/target/helpers/compat/path_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ class _DissectScandirIterator:
The _DissectScandirIterator provides a context manager, so scandir can be called as:
```
with scandir(path) as it:
for entry in it
print(entry.name)
```
.. code-block:: python
with scandir(path) as it:
for entry in it
print(entry.name)
similar to os.scandir() behaviour since Python 3.6.
"""
Expand Down
Loading

0 comments on commit 754a8aa

Please sign in to comment.