mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-06 17:57:38 +02:00
add superblock or device numbers path prepending
This commit is contained in:
@@ -633,11 +633,8 @@ class InodePages(plugins.PluginInterface):
|
||||
class RecoverFs(plugins.PluginInterface):
|
||||
"""Recovers the cached filesystem (directories, files, symlinks) into a compressed tarball.
|
||||
|
||||
Metadata aren't replicated to extracted objects and timestamps are set to the plugin run time. To prevent extraction errors related to long paths, please consider using https://github.com/mxmlnkn/ratarmount.
|
||||
To mount:
|
||||
"ratarmount recovered_fs.tar.gz ./recovered_fs_mounted/".
|
||||
To unmount:
|
||||
"umount ./recovered_fs_mounted/".
|
||||
Details: level 0 directories are named after the UUID of the parent superblock; metadata aren't replicated to extracted objects; objects modification time is set to the plugin run time.
|
||||
Troubleshooting: to fix extraction errors related to long paths, please consider using https://github.com/mxmlnkn/ratarmount.
|
||||
"""
|
||||
|
||||
_version = (1, 0, 0)
|
||||
@@ -673,6 +670,7 @@ class RecoverFs(plugins.PluginInterface):
|
||||
layer_name: str,
|
||||
tar: tarfile.TarFile,
|
||||
reg_inode_in: InodeInternal,
|
||||
path_prefix: str = "",
|
||||
mtime: float = None,
|
||||
) -> int:
|
||||
"""Extracts a REG inode content and writes it to a TarFile object.
|
||||
@@ -682,6 +680,7 @@ class RecoverFs(plugins.PluginInterface):
|
||||
layer_name: The name of the layer on which to operate
|
||||
tar: The TarFile object to write to
|
||||
reg_inode_in: The inode to extract content from
|
||||
path_prefix: A custom path prefix to prepend the inode path with
|
||||
mtime: The modification time to set the TarInfo object to
|
||||
|
||||
Returns:
|
||||
@@ -694,7 +693,7 @@ class RecoverFs(plugins.PluginInterface):
|
||||
inode_content_buffer.seek(0)
|
||||
handle_buffer_size = inode_content_buffer.getbuffer().nbytes
|
||||
|
||||
tar_info = tarfile.TarInfo(reg_inode_in.path)
|
||||
tar_info = tarfile.TarInfo(path_prefix + reg_inode_in.path)
|
||||
# The tarfile module only has read support for sparse files:
|
||||
# https://docs.python.org/3.12/library/tarfile.html#tarfile.LNKTYPE:~:text=and%20longlink%20extensions%2C-,read%2Donly%20support,-for%20all%20variants
|
||||
tar_info.type = tarfile.REGTYPE
|
||||
@@ -707,20 +706,20 @@ class RecoverFs(plugins.PluginInterface):
|
||||
return handle_buffer_size
|
||||
|
||||
@classmethod
|
||||
def _tar_add_dir_inode(
|
||||
def _tar_add_dir(
|
||||
cls,
|
||||
tar: tarfile.TarFile,
|
||||
reg_dir_in: InodeInternal,
|
||||
directory_path: str,
|
||||
mtime: float = None,
|
||||
) -> None:
|
||||
"""Adds a directory path to a TarFile object, based on a DIR inode.
|
||||
|
||||
Args:
|
||||
tar: The TarFile object to write to
|
||||
reg_dir_in: The inode to base the new directory on
|
||||
directory_path: The directory path to create
|
||||
mtime: The modification time to set the TarInfo object to
|
||||
"""
|
||||
tar_info = tarfile.TarInfo(reg_dir_in.path)
|
||||
tar_info = tarfile.TarInfo(directory_path)
|
||||
tar_info.type = tarfile.DIRTYPE
|
||||
tar_info.mode = 0o755
|
||||
if mtime is not None:
|
||||
@@ -774,11 +773,30 @@ class RecoverFs(plugins.PluginInterface):
|
||||
vmlinux_module_name=vmlinux_module_name,
|
||||
follow_symlinks=False,
|
||||
)
|
||||
visited_paths = set()
|
||||
|
||||
# Prefix paths by the super_block uuid's to prevent overlaps.
|
||||
# Switch to device major and device minor for older kernels (< 2.6.39-rc1).
|
||||
uuid_as_prefix = vmlinux.get_type("super_block").has_member("s_uuid")
|
||||
if not uuid_as_prefix:
|
||||
vollog.warning(
|
||||
"super_block struct does not support s_uuid attribute. Consequently, level 0 directories won't refer to the superblock uuid's, but to its device_major:device_minor numbers."
|
||||
)
|
||||
|
||||
visited_paths = seen_prefixes = set()
|
||||
for inode_in in inodes_iter:
|
||||
if inode_in.path in visited_paths:
|
||||
if uuid_as_prefix:
|
||||
prefix = f"/{inode_in.superblock.uuid}"
|
||||
else:
|
||||
prefix = f"/{inode_in.superblock.major}:{inode_in.superblock.minor}"
|
||||
prefixed_path = prefix + inode_in.path
|
||||
|
||||
if prefixed_path in visited_paths:
|
||||
continue
|
||||
visited_paths.add(inode_in.path)
|
||||
elif prefix not in seen_prefixes:
|
||||
self._tar_add_dir(tar, prefix, mtime)
|
||||
seen_prefixes.add(prefix)
|
||||
|
||||
visited_paths.add(prefixed_path)
|
||||
extracted_file_size = renderers.NotApplicableValue()
|
||||
|
||||
# Inodes parent directory is yielded first, which
|
||||
@@ -786,10 +804,15 @@ class RecoverFs(plugins.PluginInterface):
|
||||
# tarfile will take care of creating it anyway.
|
||||
if inode_in.inode.is_reg:
|
||||
extracted_file_size = self._tar_add_reg_inode(
|
||||
self.context, vmlinux_layer.name, tar, inode_in, mtime
|
||||
self.context,
|
||||
vmlinux_layer.name,
|
||||
tar,
|
||||
inode_in,
|
||||
prefix,
|
||||
mtime,
|
||||
)
|
||||
elif inode_in.inode.is_dir:
|
||||
self._tar_add_dir_inode(tar, inode_in, mtime)
|
||||
self._tar_add_dir(tar, prefixed_path, mtime)
|
||||
elif (
|
||||
inode_in.inode.is_link
|
||||
and inode_in.inode.has_member("i_link")
|
||||
@@ -799,7 +822,7 @@ class RecoverFs(plugins.PluginInterface):
|
||||
symlink_dest = inode_in.inode.i_link.dereference().cast(
|
||||
"string", max_length=255, encoding="utf-8", errors="replace"
|
||||
)
|
||||
self._tar_add_lnk(tar, inode_in.path, symlink_dest, mtime)
|
||||
self._tar_add_lnk(tar, prefixed_path, symlink_dest, mtime)
|
||||
# Set path to a user friendly representation before yielding
|
||||
inode_in.path = InodeUser.format_symlink(inode_in.path, symlink_dest)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user