use super::constants::{
ADDRS_PER_INODE, F2FS_BLKSIZE, F2FS_INLINE_DATA, F2FS_INLINE_DENTRY, NIDS_PER_INODE,
};
#[cfg(test)]
use super::constants::F2FS_BLK_CSUM_OFFSET;
#[derive(Debug, Clone)]
pub struct F2fsInode {
pub mode: u16,
pub size: u64,
pub uid: u32,
pub gid: u32,
pub links: u32,
pub atime: u32,
pub ctime: u32,
pub mtime: u32,
pub blocks: u64,
pub generation: u32,
pub flags: u32,
pub inline_flags: u8,
pub i_addr: [u32; ADDRS_PER_INODE],
pub i_nid: [u32; NIDS_PER_INODE],
}
impl F2fsInode {
#[inline]
pub fn is_inline_data(&self) -> bool {
self.inline_flags & F2FS_INLINE_DATA != 0
}
#[inline]
pub fn is_inline_dentry(&self) -> bool {
self.inline_flags & F2FS_INLINE_DENTRY != 0
}
pub fn inline_payload<'a>(&self, block: &'a [u8]) -> &'a [u8] {
let off = I_ADDR_OFFSET;
let end = (off + ADDRS_PER_INODE * 4 + NIDS_PER_INODE * 4).min(block.len());
&block[off..end]
}
}
pub(crate) const I_ADDR_OFFSET: usize = 0xD0;
pub fn decode_inode_block(buf: &[u8]) -> crate::Result<F2fsInode> {
if buf.len() < F2FS_BLKSIZE {
return Err(crate::Error::InvalidImage(
"f2fs: short read on inode block".into(),
));
}
let r16 = |o: usize| u16::from_le_bytes(buf[o..o + 2].try_into().unwrap());
let r32 = |o: usize| u32::from_le_bytes(buf[o..o + 4].try_into().unwrap());
let r64 = |o: usize| u64::from_le_bytes(buf[o..o + 8].try_into().unwrap());
let mode = r16(0x00);
let inline_flags = buf[0x03];
let uid = r32(0x04);
let gid = r32(0x08);
let links = r32(0x0C);
let size = r64(0x10);
let blocks = r64(0x18);
let atime = r64(0x20) as u32;
let ctime = r64(0x28) as u32;
let mtime = r64(0x30) as u32;
let generation = r32(0x44);
let flags = r32(0x50);
let mut i_addr = [0u32; ADDRS_PER_INODE];
for (i, slot) in i_addr.iter_mut().enumerate() {
let o = I_ADDR_OFFSET + i * 4;
*slot = r32(o);
}
let nid_off = I_ADDR_OFFSET + ADDRS_PER_INODE * 4;
let mut i_nid = [0u32; NIDS_PER_INODE];
for (i, slot) in i_nid.iter_mut().enumerate() {
let o = nid_off + i * 4;
*slot = r32(o);
}
Ok(F2fsInode {
mode,
size,
uid,
gid,
links,
atime,
ctime,
mtime,
blocks,
generation,
flags,
inline_flags,
i_addr,
i_nid,
})
}
pub fn decode_direct_node(buf: &[u8]) -> crate::Result<Vec<u32>> {
if buf.len() < F2FS_BLKSIZE {
return Err(crate::Error::InvalidImage(
"f2fs: short read on direct node block".into(),
));
}
let mut out = Vec::with_capacity(super::constants::ADDRS_PER_BLOCK);
for i in 0..super::constants::ADDRS_PER_BLOCK {
let o = i * 4;
out.push(u32::from_le_bytes(buf[o..o + 4].try_into().unwrap()));
}
Ok(out)
}
pub fn decode_indirect_node(buf: &[u8]) -> crate::Result<Vec<u32>> {
if buf.len() < F2FS_BLKSIZE {
return Err(crate::Error::InvalidImage(
"f2fs: short read on indirect node block".into(),
));
}
let mut out = Vec::with_capacity(super::constants::NIDS_PER_BLOCK);
for i in 0..super::constants::NIDS_PER_BLOCK {
let o = i * 4;
out.push(u32::from_le_bytes(buf[o..o + 4].try_into().unwrap()));
}
Ok(out)
}
#[cfg(test)]
pub(crate) fn encode_inode_block(ino: &F2fsInode) -> Vec<u8> {
let mut buf = vec![0u8; F2FS_BLKSIZE];
buf[0x00..0x02].copy_from_slice(&ino.mode.to_le_bytes());
buf[0x03] = ino.inline_flags;
buf[0x04..0x08].copy_from_slice(&ino.uid.to_le_bytes());
buf[0x08..0x0C].copy_from_slice(&ino.gid.to_le_bytes());
buf[0x0C..0x10].copy_from_slice(&ino.links.to_le_bytes());
buf[0x10..0x18].copy_from_slice(&ino.size.to_le_bytes());
buf[0x18..0x20].copy_from_slice(&ino.blocks.to_le_bytes());
buf[0x20..0x28].copy_from_slice(&(ino.atime as u64).to_le_bytes());
buf[0x28..0x30].copy_from_slice(&(ino.ctime as u64).to_le_bytes());
buf[0x30..0x38].copy_from_slice(&(ino.mtime as u64).to_le_bytes());
buf[0x44..0x48].copy_from_slice(&ino.generation.to_le_bytes());
buf[0x50..0x54].copy_from_slice(&ino.flags.to_le_bytes());
for (i, a) in ino.i_addr.iter().enumerate() {
let o = I_ADDR_OFFSET + i * 4;
buf[o..o + 4].copy_from_slice(&a.to_le_bytes());
}
let nid_off = I_ADDR_OFFSET + ADDRS_PER_INODE * 4;
for (i, a) in ino.i_nid.iter().enumerate() {
let o = nid_off + i * 4;
buf[o..o + 4].copy_from_slice(&a.to_le_bytes());
}
let crc = crc32fast::hash(&buf[..F2FS_BLK_CSUM_OFFSET]);
buf[F2FS_BLK_CSUM_OFFSET..F2FS_BLK_CSUM_OFFSET + 4].copy_from_slice(&crc.to_le_bytes());
buf
}
#[cfg(test)]
pub(crate) fn encode_direct_node(ptrs: &[u32]) -> Vec<u8> {
let mut buf = vec![0u8; F2FS_BLKSIZE];
for (i, p) in ptrs.iter().enumerate() {
if i >= super::constants::ADDRS_PER_BLOCK {
break;
}
let o = i * 4;
buf[o..o + 4].copy_from_slice(&p.to_le_bytes());
}
buf
}
#[cfg(test)]
pub(crate) fn encode_indirect_node(nids: &[u32]) -> Vec<u8> {
let mut buf = vec![0u8; F2FS_BLKSIZE];
for (i, p) in nids.iter().enumerate() {
if i >= super::constants::NIDS_PER_BLOCK {
break;
}
let o = i * 4;
buf[o..o + 4].copy_from_slice(&p.to_le_bytes());
}
buf
}