use alloc::{boxed::Box, vec::Vec};
use core::convert::TryInto;
use crate::{bmalloc::AbsoluteBN, config::*, endian::*};
pub const JOURNAL_FILE_INODE: u64 = 8;
pub const JBD2_MAGIC: u32 = 0xC03B_3998u32; pub const JOURNAL_BLOCK_COUNT: u32 = 32 * 1024 * 1024 / BLOCK_SIZE_U32;
pub const JOURANL_ESCAPE: u16 = 0x1;
pub const JBD2_FLAG_LAST_TAG: u16 = 0x8;
pub const JBD2_CRC32C_CHKSUM: u8 = 4; #[repr(C)]
pub struct Jbd2Update(pub AbsoluteBN, pub Box<[u8; BLOCK_SIZE]>);
#[repr(C)]
pub struct JBD2DEVSYSTEM {
pub jbd2_super_block: JournalSuperBllockS,
pub start_block: AbsoluteBN, pub max_len: u32, pub head: u32, pub sequence: u32, pub commit_queue: Vec<Jbd2Update>, }
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct JournalHeaderS {
pub h_magic: u32, pub h_blocktype: u32, pub h_sequence: u32, }
impl Default for JournalHeaderS {
fn default() -> Self {
JournalHeaderS {
h_magic: JBD2_MAGIC,
h_blocktype: 4, h_sequence: 0,
}
}
}
impl DiskFormat for JournalHeaderS {
fn from_disk_bytes(bytes: &[u8]) -> Self {
let h_magic = u32::from_be_bytes(bytes[0..4].try_into().unwrap());
let h_blocktype = u32::from_be_bytes(bytes[4..8].try_into().unwrap());
let h_sequence = u32::from_be_bytes(bytes[8..12].try_into().unwrap());
JournalHeaderS {
h_magic,
h_blocktype,
h_sequence,
}
}
fn to_disk_bytes(&self, bytes: &mut [u8]) {
bytes[0..4].copy_from_slice(&self.h_magic.to_be_bytes());
bytes[4..8].copy_from_slice(&self.h_blocktype.to_be_bytes());
bytes[8..12].copy_from_slice(&self.h_sequence.to_be_bytes());
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct JournalSuperBllockS {
pub s_header: JournalHeaderS,
pub s_blocksize: u32, pub s_maxlen: u32, pub s_first: u32,
pub s_sequence: u32, pub s_start: u32, pub s_errno: u32,
pub s_feature_compat: u32, pub s_feature_incompat: u32, pub s_feature_ro_compat: u32, pub s_uuid: [u8; 16], pub s_nr_users: u32, pub s_dynsuper: u32, pub s_max_transaction: u32, pub s_max_trans_data: u32, pub s_checksum_type: u8, pub s_padding2: [u8; 3],
pub s_padding: [u32; 42], pub s_checksum: u32,
pub s_users: [u8; 16 * 48], }
impl Default for JournalSuperBllockS {
fn default() -> Self {
let header = JournalHeaderS::default();
JournalSuperBllockS {
s_header: header,
s_blocksize: BLOCK_SIZE_U32,
s_maxlen: 4096,
s_first: 1,
s_sequence: 1,
s_start: 0,
s_errno: 0,
s_feature_compat: 0,
s_feature_incompat: 0,
s_feature_ro_compat: 0,
s_uuid: [0; 16],
s_nr_users: 1,
s_dynsuper: 0,
s_max_transaction: JOURNAL_BLOCK_COUNT,
s_max_trans_data: JOURNAL_BLOCK_COUNT * 10,
s_checksum_type: 0,
s_padding2: [0; 3],
s_padding: [0; 42],
s_checksum: 0,
s_users: [0; 768],
}
}
}
impl DiskFormat for JournalSuperBllockS {
fn from_disk_bytes(bytes: &[u8]) -> Self {
let s_header = JournalHeaderS::from_disk_bytes(&bytes[0..12]);
let s_blocksize = u32::from_be_bytes(bytes[12..16].try_into().unwrap());
let s_maxlen = u32::from_be_bytes(bytes[16..20].try_into().unwrap());
let s_first = u32::from_be_bytes(bytes[20..24].try_into().unwrap());
let s_sequence = u32::from_be_bytes(bytes[24..28].try_into().unwrap());
let s_start = u32::from_be_bytes(bytes[28..32].try_into().unwrap());
let s_errno = u32::from_be_bytes(bytes[32..36].try_into().unwrap());
let s_feature_compat = u32::from_be_bytes(bytes[36..40].try_into().unwrap());
let s_feature_incompat = u32::from_be_bytes(bytes[40..44].try_into().unwrap());
let s_feature_ro_compat = u32::from_be_bytes(bytes[44..48].try_into().unwrap());
let mut s_uuid = [0u8; 16];
s_uuid.copy_from_slice(&bytes[48..64]);
let s_nr_users = u32::from_be_bytes(bytes[64..68].try_into().unwrap());
let s_dynsuper = u32::from_be_bytes(bytes[68..72].try_into().unwrap());
let s_max_transaction = u32::from_be_bytes(bytes[72..76].try_into().unwrap());
let s_max_trans_data = u32::from_be_bytes(bytes[76..80].try_into().unwrap());
let s_checksum_type = bytes[80];
let mut s_padding2 = [0u8; 3];
s_padding2.copy_from_slice(&bytes[81..84]);
let mut s_padding = [0u32; 42];
let mut off = 84usize;
for elem in &mut s_padding {
*elem = u32::from_be_bytes(bytes[off..off + 4].try_into().unwrap());
off += 4;
}
let s_checksum = u32::from_be_bytes(bytes[0xFC..0x100].try_into().unwrap());
let mut s_users = [0u8; 16 * 48];
s_users.copy_from_slice(&bytes[0x100..0x100 + 16 * 48]);
JournalSuperBllockS {
s_header,
s_blocksize,
s_maxlen,
s_first,
s_sequence,
s_start,
s_errno,
s_feature_compat,
s_feature_incompat,
s_feature_ro_compat,
s_uuid,
s_nr_users,
s_dynsuper,
s_max_transaction,
s_max_trans_data,
s_checksum_type,
s_padding2,
s_padding,
s_checksum,
s_users,
}
}
fn to_disk_bytes(&self, bytes: &mut [u8]) {
self.s_header.to_disk_bytes(&mut bytes[0..12]);
bytes[12..16].copy_from_slice(&self.s_blocksize.to_be_bytes());
bytes[16..20].copy_from_slice(&self.s_maxlen.to_be_bytes());
bytes[20..24].copy_from_slice(&self.s_first.to_be_bytes());
bytes[24..28].copy_from_slice(&self.s_sequence.to_be_bytes());
bytes[28..32].copy_from_slice(&self.s_start.to_be_bytes());
bytes[32..36].copy_from_slice(&self.s_errno.to_be_bytes());
bytes[36..40].copy_from_slice(&self.s_feature_compat.to_be_bytes());
bytes[40..44].copy_from_slice(&self.s_feature_incompat.to_be_bytes());
bytes[44..48].copy_from_slice(&self.s_feature_ro_compat.to_be_bytes());
bytes[48..64].copy_from_slice(&self.s_uuid);
bytes[64..68].copy_from_slice(&self.s_nr_users.to_be_bytes());
bytes[68..72].copy_from_slice(&self.s_dynsuper.to_be_bytes());
bytes[72..76].copy_from_slice(&self.s_max_transaction.to_be_bytes());
bytes[76..80].copy_from_slice(&self.s_max_trans_data.to_be_bytes());
bytes[80] = self.s_checksum_type;
bytes[81..84].copy_from_slice(&self.s_padding2);
let mut off = 84usize;
for i in 0..42 {
bytes[off..off + 4].copy_from_slice(&self.s_padding[i].to_be_bytes());
off += 4;
}
bytes[0xFC..0x100].copy_from_slice(&self.s_checksum.to_be_bytes());
bytes[0x100..0x100 + 16 * 48].copy_from_slice(&self.s_users);
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct JournalBlockTagS {
pub t_blocknr: u32, pub t_checksum: u16, pub t_flags: u16,
}
impl DiskFormat for JournalBlockTagS {
fn from_disk_bytes(bytes: &[u8]) -> Self {
let t_blocknr = u32::from_be_bytes(bytes[0..4].try_into().unwrap());
let t_checksum = u16::from_be_bytes(bytes[4..6].try_into().unwrap());
let t_flags = u16::from_be_bytes(bytes[6..8].try_into().unwrap());
JournalBlockTagS {
t_blocknr,
t_checksum,
t_flags,
}
}
fn to_disk_bytes(&self, bytes: &mut [u8]) {
bytes[0..4].copy_from_slice(&self.t_blocknr.to_be_bytes());
bytes[4..6].copy_from_slice(&self.t_checksum.to_be_bytes());
bytes[6..8].copy_from_slice(&self.t_flags.to_be_bytes());
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct JouranlBlockTag3S {
pub t_blocknr: u32, pub t_flags: u32, pub t_blocknr_high: u32, pub t_checksum: u32,
}
impl DiskFormat for JouranlBlockTag3S {
fn from_disk_bytes(bytes: &[u8]) -> Self {
let t_blocknr = u32::from_be_bytes(bytes[0..4].try_into().unwrap());
let t_flags = u32::from_be_bytes(bytes[4..8].try_into().unwrap());
let t_blocknr_high = u32::from_be_bytes(bytes[8..12].try_into().unwrap());
let t_checksum = u32::from_be_bytes(bytes[12..16].try_into().unwrap());
JouranlBlockTag3S {
t_blocknr,
t_flags,
t_blocknr_high,
t_checksum,
}
}
fn to_disk_bytes(&self, bytes: &mut [u8]) {
bytes[0..4].copy_from_slice(&self.t_blocknr.to_be_bytes());
bytes[4..8].copy_from_slice(&self.t_flags.to_be_bytes());
bytes[8..12].copy_from_slice(&self.t_blocknr_high.to_be_bytes());
bytes[12..16].copy_from_slice(&self.t_checksum.to_be_bytes());
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Jbd2JournalBlockTail {
pub t_checksum: u32, }
impl DiskFormat for Jbd2JournalBlockTail {
fn from_disk_bytes(bytes: &[u8]) -> Self {
let t_checksum = u32::from_be_bytes(bytes[0..4].try_into().unwrap());
Jbd2JournalBlockTail { t_checksum }
}
fn to_disk_bytes(&self, bytes: &mut [u8]) {
bytes[0..4].copy_from_slice(&self.t_checksum.to_be_bytes());
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Jbd2JournalRevokeHeadS {
pub r_header: JournalHeaderS, pub r_count: u32,
}
impl DiskFormat for Jbd2JournalRevokeHeadS {
fn from_disk_bytes(bytes: &[u8]) -> Self {
let r_header = JournalHeaderS::from_disk_bytes(&bytes[0..12]);
let r_count = u32::from_be_bytes(bytes[12..16].try_into().unwrap());
Jbd2JournalRevokeHeadS { r_header, r_count }
}
fn to_disk_bytes(&self, bytes: &mut [u8]) {
self.r_header.to_disk_bytes(&mut bytes[0..12]);
bytes[12..16].copy_from_slice(&self.r_count.to_be_bytes());
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Jbd2JouranlRevokeTail {
pub r_checksum: u32, }
impl DiskFormat for Jbd2JouranlRevokeTail {
fn from_disk_bytes(bytes: &[u8]) -> Self {
let r_checksum = u32::from_be_bytes(bytes[0..4].try_into().unwrap());
Jbd2JouranlRevokeTail { r_checksum }
}
fn to_disk_bytes(&self, bytes: &mut [u8]) {
bytes[0..4].copy_from_slice(&self.r_checksum.to_be_bytes());
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CommitHeader {
pub h_header: JournalHeaderS, pub h_chksum_type: u8, pub h_chksum_size: u8, pub h_padding: [u8; 2], pub h_chksum: [u32; 8], pub h_commit_sec: u64, pub h_commit_nsec: u32, }
#[cfg(test)]
mod tests {
use DiskFormat;
use super::*;
#[test]
fn test_journal_header_roundtrip() {
let hdr = JournalHeaderS {
h_magic: JBD2_MAGIC,
h_blocktype: 2,
h_sequence: 0x1122_3344,
};
let mut buf = [0u8; 12];
hdr.to_disk_bytes(&mut buf);
assert_eq!(&buf[0..4], &JBD2_MAGIC.to_be_bytes());
assert_eq!(&buf[4..8], &2u32.to_be_bytes());
assert_eq!(&buf[8..12], &0x1122_3344u32.to_be_bytes());
let parsed = JournalHeaderS::from_disk_bytes(&buf);
assert_eq!(parsed.h_magic, JBD2_MAGIC);
assert_eq!(parsed.h_blocktype, 2);
assert_eq!(parsed.h_sequence, 0x1122_3344);
}
#[test]
fn test_journal_superblock_roundtrip() {
let header = JournalHeaderS {
h_magic: JBD2_MAGIC,
h_blocktype: 3,
h_sequence: 0xAABB_CCDD,
};
let sb = JournalSuperBllockS {
s_header: header,
s_blocksize: 4096,
s_maxlen: 1024,
s_first: 2,
s_sequence: 0x0102_0304,
s_start: 0x1122_3344,
s_errno: 0,
s_feature_compat: 0x1,
s_feature_incompat: 0x2,
s_feature_ro_compat: 0x0,
s_uuid: [0xAA; 16],
s_nr_users: 1,
s_dynsuper: 0,
s_max_transaction: 0,
s_max_trans_data: 0,
s_checksum_type: 4,
s_padding2: [0; 3],
s_padding: [0xDEAD_BEEFu32; 42],
s_checksum: 0xFEED_FACE,
s_users: [0x55u8; 16 * 48],
};
let mut buf = [0u8; 1024];
sb.to_disk_bytes(&mut buf);
assert_eq!(&buf[0..4], &JBD2_MAGIC.to_be_bytes());
assert_eq!(&buf[0xC..0x10], &sb.s_blocksize.to_be_bytes());
assert_eq!(&buf[0x10..0x14], &sb.s_maxlen.to_be_bytes());
assert_eq!(&buf[0x14..0x18], &sb.s_first.to_be_bytes());
assert_eq!(&buf[0x18..0x1C], &sb.s_sequence.to_be_bytes());
assert_eq!(&buf[0x1C..0x20], &sb.s_start.to_be_bytes());
assert_eq!(&buf[0xFC..0x100], &sb.s_checksum.to_be_bytes());
let parsed = JournalSuperBllockS::from_disk_bytes(&buf);
assert_eq!(parsed.s_header.h_magic, sb.s_header.h_magic);
assert_eq!(parsed.s_blocksize, sb.s_blocksize);
assert_eq!(parsed.s_maxlen, sb.s_maxlen);
assert_eq!(parsed.s_first, sb.s_first);
assert_eq!(parsed.s_sequence, sb.s_sequence);
assert_eq!(parsed.s_start, sb.s_start);
assert_eq!(parsed.s_checksum, sb.s_checksum);
assert_eq!(&parsed.s_users[..], &sb.s_users[..]);
}
#[test]
fn test_block_tag_and_tag3_roundtrip() {
let tag = JournalBlockTagS {
t_blocknr: 0xDEAD_BEEFu32,
t_checksum: 0xABCDu16,
t_flags: 0x0001,
};
let mut b = [0u8; 8];
tag.to_disk_bytes(&mut b);
assert_eq!(&b[0..4], &tag.t_blocknr.to_be_bytes());
assert_eq!(&b[4..6], &tag.t_checksum.to_be_bytes());
assert_eq!(&b[6..8], &tag.t_flags.to_be_bytes());
let parsed = JournalBlockTagS::from_disk_bytes(&b);
assert_eq!(parsed.t_blocknr, tag.t_blocknr);
assert_eq!(parsed.t_checksum, tag.t_checksum);
assert_eq!(parsed.t_flags, tag.t_flags);
let tag3 = JouranlBlockTag3S {
t_blocknr: 1,
t_flags: 2,
t_blocknr_high: 3,
t_checksum: 0xFEED_BEEFu32,
};
let mut b3 = [0u8; 16];
tag3.to_disk_bytes(&mut b3);
let parsed3 = JouranlBlockTag3S::from_disk_bytes(&b3);
assert_eq!(parsed3.t_blocknr, tag3.t_blocknr);
assert_eq!(parsed3.t_flags, tag3.t_flags);
assert_eq!(parsed3.t_blocknr_high, tag3.t_blocknr_high);
assert_eq!(parsed3.t_checksum, tag3.t_checksum);
}
#[test]
fn test_block_tail_and_revoke_roundtrip() {
let tail = Jbd2JournalBlockTail {
t_checksum: 0x1234_5678,
};
let mut b = [0u8; 4];
tail.to_disk_bytes(&mut b);
assert_eq!(&b[..], &tail.t_checksum.to_be_bytes());
let parsed = Jbd2JournalBlockTail::from_disk_bytes(&b);
assert_eq!(parsed.t_checksum, tail.t_checksum);
let revoke = Jbd2JournalRevokeHeadS {
r_header: JournalHeaderS {
h_magic: JBD2_MAGIC,
h_blocktype: 5,
h_sequence: 7,
},
r_count: 16,
};
let mut rb = [0u8; 16];
revoke.to_disk_bytes(&mut rb);
let parsed_revoke = Jbd2JournalRevokeHeadS::from_disk_bytes(&rb);
assert_eq!(parsed_revoke.r_header.h_magic, revoke.r_header.h_magic);
assert_eq!(parsed_revoke.r_count, revoke.r_count);
let rt = Jbd2JouranlRevokeTail {
r_checksum: 0xCAFEBABE,
};
let mut rtb = [0u8; 4];
rt.to_disk_bytes(&mut rtb);
let parsed_rt = Jbd2JouranlRevokeTail::from_disk_bytes(&rtb);
assert_eq!(parsed_rt.r_checksum, rt.r_checksum);
}
#[test]
fn test_commit_header_roundtrip() {
let hdr = JournalHeaderS {
h_magic: JBD2_MAGIC,
h_blocktype: 2,
h_sequence: 9,
};
let commit = CommitHeader {
h_header: hdr,
h_chksum_type: 4,
h_chksum_size: 4,
h_padding: [0u8; 2],
h_chksum: [0x1111_2222u32; 8],
h_commit_sec: 0x0102_0304_0506_0708u64,
h_commit_nsec: 0xAABB_CCDDu32,
};
let mut buf = [0u8; 64];
commit.to_disk_bytes(&mut buf);
let parsed = CommitHeader::from_disk_bytes(&buf);
assert_eq!(parsed.h_header.h_magic, commit.h_header.h_magic);
assert_eq!(parsed.h_chksum_type, commit.h_chksum_type);
assert_eq!(parsed.h_chksum_size, commit.h_chksum_size);
assert_eq!(parsed.h_chksum, commit.h_chksum);
assert_eq!(parsed.h_commit_sec, commit.h_commit_sec);
assert_eq!(parsed.h_commit_nsec, commit.h_commit_nsec);
}
}
impl DiskFormat for CommitHeader {
fn from_disk_bytes(bytes: &[u8]) -> Self {
let h_header = JournalHeaderS::from_disk_bytes(&bytes[0..12]);
let h_chksum_type = bytes[12];
let h_chksum_size = bytes[13];
let mut h_padding = [0u8; 2];
h_padding.copy_from_slice(&bytes[14..16]);
let mut h_chksum = [0u32; 8];
let mut off = 16usize;
for elem in &mut h_chksum {
*elem = u32::from_be_bytes(bytes[off..off + 4].try_into().unwrap());
off += 4;
}
let h_commit_sec = u64::from_be_bytes(bytes[48..56].try_into().unwrap());
let h_commit_nsec = u32::from_be_bytes(bytes[56..60].try_into().unwrap());
CommitHeader {
h_header,
h_chksum_type,
h_chksum_size,
h_padding,
h_chksum,
h_commit_sec,
h_commit_nsec,
}
}
fn to_disk_bytes(&self, bytes: &mut [u8]) {
self.h_header.to_disk_bytes(&mut bytes[0..12]);
bytes[12] = self.h_chksum_type;
bytes[13] = self.h_chksum_size;
bytes[14..16].copy_from_slice(&self.h_padding);
let mut off = 16usize;
for i in 0..8 {
bytes[off..off + 4].copy_from_slice(&self.h_chksum[i].to_be_bytes());
off += 4;
}
bytes[48..56].copy_from_slice(&self.h_commit_sec.to_be_bytes());
bytes[56..60].copy_from_slice(&self.h_commit_nsec.to_be_bytes());
}
}