2023-01-12 01:04:47 +00:00
|
|
|
"""
|
|
|
|
ReiserFS file system version 3 parser (other version have not been tested).
|
|
|
|
|
|
|
|
Author: Frederic Weisbecker
|
|
|
|
Creation date: 8 december 2006
|
|
|
|
|
|
|
|
Sources:
|
|
|
|
- http://p-nand-q.com/download/rfstool/reiserfs_docs.html
|
|
|
|
- http://homes.cerias.purdue.edu/~florian/reiser/reiserfs.php
|
|
|
|
- file://usr/src/linux-2.6.16.19/include/linux/reiserfs_fs.h
|
|
|
|
|
|
|
|
NOTES:
|
|
|
|
|
|
|
|
The most part of the description of the structures, their fields and their
|
|
|
|
comments decribed here comes from the file include/linux/reiserfs_fs.h
|
|
|
|
- written by Hans reiser - located in the Linux kernel 2.6.16.19 and from
|
|
|
|
the Reiserfs explanations in
|
|
|
|
http://p-nand-q.com/download/rfstool/reiserfs_docs.html written by Gerson
|
|
|
|
Kurz.
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from hachoir.parser import Parser
|
|
|
|
from hachoir.field import (FieldSet, Enum,
|
2023-10-07 23:04:41 +00:00
|
|
|
UInt16, UInt32, String, RawBytes, NullBytes, SeekableFieldSet, Bit)
|
2023-01-12 01:04:47 +00:00
|
|
|
from hachoir.core.endian import LITTLE_ENDIAN
|
|
|
|
|
|
|
|
|
|
|
|
class BlockState(Bit):
|
|
|
|
"""The state (used/free) of a ReiserFs Block"""
|
|
|
|
|
|
|
|
STATE = {
|
|
|
|
True: "used",
|
|
|
|
False: "free"
|
|
|
|
}
|
|
|
|
|
|
|
|
block_nb = 0
|
|
|
|
|
|
|
|
def __init__(self, parent, name, nb_block):
|
|
|
|
"""@param nb_block: Number of the block concerned"""
|
|
|
|
Bit.__init__(self, parent, name)
|
|
|
|
self.block_nb = self.__class__.block_nb
|
|
|
|
self.__class__.block_nb += 1
|
|
|
|
|
|
|
|
def createDescription(self):
|
|
|
|
return "State of the block %d" % self.block_nb
|
|
|
|
|
|
|
|
def createDisplay(self):
|
|
|
|
return self.STATE[Bit.createValue(self)]
|
|
|
|
|
|
|
|
|
|
|
|
class BitmapBlock(SeekableFieldSet):
|
|
|
|
""" The bitmap blocks are Reiserfs blocks where each byte contains
|
|
|
|
the state of 8 blocks in the filesystem. So each bit will describe
|
|
|
|
the state of a block to tell if it is used or not.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def createFields(self):
|
|
|
|
block_size = self["/superblock/blocksize"].value
|
|
|
|
|
|
|
|
for i in range(0, block_size * 8):
|
|
|
|
yield BlockState(self, "block[]", i)
|
|
|
|
|
|
|
|
|
|
|
|
class BitmapBlockGroup(SeekableFieldSet):
|
|
|
|
"""The group that manages the Bitmap Blocks"""
|
|
|
|
|
|
|
|
def createFields(self):
|
|
|
|
block_size = self["/superblock/blocksize"].value
|
|
|
|
nb_bitmap_block = self["/superblock/bmap_nr"].value
|
|
|
|
# Position of the first bitmap block
|
|
|
|
self.seekByte(REISER_FS.SUPERBLOCK_OFFSET + block_size, relative=False)
|
|
|
|
|
|
|
|
yield BitmapBlock(self, "BitmapBlock[]", "Bitmap blocks tells for each block if it is used")
|
|
|
|
# The other bitmap blocks
|
|
|
|
for i in range(1, nb_bitmap_block):
|
|
|
|
self.seekByte((block_size**2) * 8 * i, relative=False)
|
|
|
|
yield BitmapBlock(self, "BitmapBlock[]", "Bitmap blocks tells for each block if it is used")
|
|
|
|
|
|
|
|
|
|
|
|
class Journal_params(FieldSet):
|
|
|
|
static_size = 32 * 8
|
|
|
|
|
|
|
|
def createFields(self):
|
|
|
|
yield UInt32(self, "1st_block", "Journal 1st block number")
|
|
|
|
yield UInt32(self, "dev", "Journal device number")
|
|
|
|
yield UInt32(self, "size", "Size of the journal")
|
|
|
|
yield UInt32(self, "trans_max", "Max number of blocks in a transaction")
|
|
|
|
# TODO: Must be explained: it was sb_journal_block_count
|
|
|
|
yield UInt32(self, "magic", "Random value made on fs creation.")
|
|
|
|
yield UInt32(self, "max_batch", "Max number of blocks to batch into a trans")
|
|
|
|
yield UInt32(self, "max_commit_age", "In seconds, how old can an async commit be")
|
|
|
|
yield UInt32(self, "max_trans_age", "In seconds, how old can a transaction be")
|
|
|
|
|
|
|
|
def createDescription(self):
|
|
|
|
return "Parameters of the journal"
|
|
|
|
|
|
|
|
|
|
|
|
class SuperBlock(FieldSet):
|
|
|
|
# static_size = 204*8
|
|
|
|
|
|
|
|
UMOUNT_STATE = {1: "unmounted", 2: "not unmounted"}
|
|
|
|
HASH_FUNCTIONS = {
|
|
|
|
0: "UNSET_HASH",
|
|
|
|
1: "TEA_HASH",
|
|
|
|
2: "YURA_HASH",
|
|
|
|
3: "R5_HASH"
|
|
|
|
}
|
|
|
|
|
|
|
|
def createFields(self):
|
|
|
|
# TODO: This structure is normally divided in two parts:
|
|
|
|
# _reiserfs_super_block_v1
|
|
|
|
# _reiserfs_super_block
|
|
|
|
# It will be divided later to easily support older version of the first
|
|
|
|
# part
|
|
|
|
yield UInt32(self, "block_count", "Number of blocks")
|
|
|
|
yield UInt32(self, "free_blocks", "Number of free blocks")
|
|
|
|
yield UInt32(self, "root_block", "Root block number")
|
|
|
|
yield Journal_params(self, "Journal parameters")
|
|
|
|
yield UInt16(self, "blocksize", "Size of a block")
|
|
|
|
yield UInt16(self, "oid_maxsize", "Max size of object id array")
|
|
|
|
yield UInt16(self, "oid_cursize", "Current size of object id array")
|
|
|
|
yield Enum(UInt16(self, "umount_state", "Filesystem umounted or not"), self.UMOUNT_STATE)
|
|
|
|
yield String(self, "magic", 10, "Magic string", strip="\0")
|
|
|
|
# TODO: change the type of s_fs_state in Enum to have more details
|
|
|
|
# about this fsck state
|
|
|
|
yield UInt16(self, "fs_state", "Rebuilding phase of fsck ")
|
|
|
|
yield Enum(UInt32(self, "hash_function", "Hash function to sort names in a directory"), self.HASH_FUNCTIONS)
|
|
|
|
yield UInt16(self, "tree_height", "Height of disk tree")
|
|
|
|
yield UInt16(self, "bmap_nr", "Amount of bitmap blocks needed to address each block of file system")
|
|
|
|
# TODO: find a good description for this field
|
|
|
|
yield UInt16(self, "version", "Field only reliable on filesystem with non-standard journal")
|
|
|
|
yield UInt16(self, "reserved_for_journal", "Size in blocks of journal area on main device")
|
|
|
|
# TODO: same as above
|
|
|
|
yield UInt32(self, "inode_generation", "No description")
|
|
|
|
# TODO: same as above and should be an enum field
|
|
|
|
yield UInt32(self, "flags", "No description")
|
|
|
|
# TODO: Create a special Type to format this id
|
|
|
|
yield RawBytes(self, "uuid", 16, "Filesystem unique identifier")
|
|
|
|
yield String(self, "label", 16, "Filesystem volume label", strip="\0")
|
|
|
|
yield NullBytes(self, "unused", 88)
|
|
|
|
yield NullBytes(self, "Bytes before end of the block", self["blocksize"].value - 204)
|
|
|
|
|
|
|
|
def createDescription(self):
|
|
|
|
return "Superblock: ReiserFs Filesystem"
|
|
|
|
|
|
|
|
|
|
|
|
class REISER_FS(Parser):
|
|
|
|
PARSER_TAGS = {
|
|
|
|
"id": "reiserfs",
|
|
|
|
"category": "file_system",
|
|
|
|
# 130 blocks before the journal +
|
|
|
|
# Minimal size of journal (513 blocks) +
|
|
|
|
# 1 block for the rest
|
|
|
|
# And The Minimal size of a block is 512 bytes
|
|
|
|
"min_size": (130 + 513 + 1) * (512 * 8),
|
|
|
|
"description": "ReiserFS file system"
|
|
|
|
}
|
|
|
|
endian = LITTLE_ENDIAN
|
|
|
|
|
|
|
|
# Offsets (in bytes) of important information
|
|
|
|
SUPERBLOCK_OFFSET = 64 * 1024
|
|
|
|
MAGIC_OFFSET = SUPERBLOCK_OFFSET + 52
|
|
|
|
|
|
|
|
def validate(self):
|
|
|
|
# Let's look at the magic field in the superblock
|
|
|
|
magic = self.stream.readBytes(self.MAGIC_OFFSET * 8, 9).rstrip(b"\0")
|
|
|
|
if magic in (b"ReIsEr3Fs", b"ReIsErFs", b"ReIsEr2Fs"):
|
|
|
|
return True
|
|
|
|
return "Invalid magic string"
|
|
|
|
|
|
|
|
def createFields(self):
|
|
|
|
yield NullBytes(self, "padding[]", self.SUPERBLOCK_OFFSET)
|
|
|
|
yield SuperBlock(self, "superblock")
|
|
|
|
yield BitmapBlockGroup(self, "Group of bitmap blocks")
|