linux: page_cache plugin: lazy file initialization and avoid redundant inode page cache walk during dumps not showing output with dumping to file.

This commit is contained in:
Gustavo Moreira
2025-01-16 19:50:14 +11:00
parent 3c92d7f7b7
commit c1497410b4
@@ -8,7 +8,7 @@ import datetime
from dataclasses import dataclass, astuple
from typing import List, Set, Type, Iterable
from volatility3.framework import renderers, interfaces
from volatility3.framework import renderers, interfaces, exceptions
from volatility3.framework.renderers import format_hints
from volatility3.framework.interfaces import plugins
from volatility3.framework.configuration import requirements
@@ -453,16 +453,18 @@ class InodePages(plugins.PluginInterface):
# created, saving both disk space and I/O time.
# Additionally, using the page index will guarantee that each page is written at the
# appropriate file position.
inode_size = inode.i_size
try:
with open_method(filename) as f:
inode_size = inode.i_size
f.truncate(inode_size)
file_initialized = False
with open_method(filename) as file_obj:
for page_idx, page_content in inode.get_contents():
current_fp = page_idx * vmlinux_layer.page_size
max_length = inode_size - current_fp
page_bytes = page_content[:max_length]
if current_fp + len(page_bytes) > inode_size:
page_bytes_len = min(max_length, len(page_content))
if (
current_fp >= inode_size
or current_fp + page_bytes_len > inode_size
):
vollog.error(
"Page out of file bounds: inode 0x%x, inode size %d, page index %d",
inode.vol.offset,
@@ -470,10 +472,20 @@ class InodePages(plugins.PluginInterface):
page_idx,
)
continue
page_bytes = page_content[:page_bytes_len]
f.seek(current_fp)
f.write(page_bytes)
if not file_initialized:
# Lazy initialization to avoid truncating the file until we are
# certain there is something to write
file_obj.truncate(inode_size)
file_initialized = True
file_obj.seek(current_fp)
file_obj.write(page_bytes)
except exceptions.LinuxPageCacheException:
vollog.error(
f"Error dumping cached pages for inode at {inode.vol.offset:#x}"
)
except OSError as e:
vollog.error("Unable to write to file (%s): %s", filename, e)
@@ -514,31 +526,44 @@ class InodePages(plugins.PluginInterface):
return None
inode_size = inode.i_size
for page_obj in inode.get_pages():
page_vaddr = page_obj.vol.offset
page_paddr = page_obj.to_paddr()
page_mapping_addr = page_obj.mapping
page_index = int(page_obj.index)
page_file_offset = page_index * vmlinux_layer.page_size
dump_safe = (
page_file_offset < inode_size
and page_mapping_addr
and page_mapping_addr.is_readable()
)
page_flags_list = page_obj.get_flags_list()
page_flags = ",".join([x.replace("PG_", "") for x in page_flags_list])
fields = (
page_vaddr,
page_paddr,
page_mapping_addr,
page_index,
dump_safe,
page_flags,
)
if not self.config["dump"]:
try:
for page_obj in inode.get_pages():
if page_obj.mapping != inode.i_mapping:
vollog.warning(
f"Cached page at {page_obj.vol.offset:#x} has a mismatched address space with the inode. Skipping page"
)
continue
page_vaddr = page_obj.vol.offset
page_paddr = page_obj.to_paddr()
page_mapping_addr = page_obj.mapping
page_index = int(page_obj.index)
page_file_offset = page_index * vmlinux_layer.page_size
dump_safe = (
page_file_offset < inode_size
and page_mapping_addr
and page_mapping_addr.is_readable()
)
page_flags_list = page_obj.get_flags_list()
page_flags = ",".join(
[x.replace("PG_", "") for x in page_flags_list]
)
fields = (
page_vaddr,
page_paddr,
page_mapping_addr,
page_index,
dump_safe,
page_flags,
)
yield 0, fields
yield 0, fields
except exceptions.LinuxPageCacheException:
vollog.warning(
f"Page cache for inode at {inode.vol.offset:#x} is corrupt"
)
if self.config["dump"]:
else:
open_method = self.open
inode_address = inode.vol.offset
filename = open_method.sanitize_filename(f"inode_0x{inode_address:x}.dmp")