use std::io::Read;
use super::constants::{
ADDRS_PER_BLOCK, ADDRS_PER_INODE, F2FS_BLKSIZE, NEW_ADDR, NID_DIRECT_1, NID_DIRECT_2,
NID_INDIRECT_1, NID_INDIRECT_2, NID_TRIPLE_INDIRECT, NIDS_PER_BLOCK, NULL_ADDR,
};
use super::inode::{F2fsInode, decode_direct_node, decode_indirect_node, decode_inode_block};
use super::nat::lookup_node;
use crate::Result;
use crate::block::BlockDevice;
use super::checkpoint::Checkpoint;
use super::superblock::Superblock;
pub fn logical_to_physical(
dev: &mut dyn BlockDevice,
sb: &Superblock,
cp: &Checkpoint,
inode: &F2fsInode,
idx: u64,
) -> Result<u32> {
if idx < ADDRS_PER_INODE as u64 {
return Ok(inode.i_addr[idx as usize]);
}
let mut rel = idx - ADDRS_PER_INODE as u64;
for nid_idx in [NID_DIRECT_1, NID_DIRECT_2] {
let span = ADDRS_PER_BLOCK as u64;
if rel < span {
return resolve_via_direct_node(dev, sb, cp, inode.i_nid[nid_idx], rel as usize);
}
rel -= span;
}
for nid_idx in [NID_INDIRECT_1, NID_INDIRECT_2] {
let span = (NIDS_PER_BLOCK as u64) * (ADDRS_PER_BLOCK as u64);
if rel < span {
let outer = (rel / ADDRS_PER_BLOCK as u64) as usize;
let inner = (rel % ADDRS_PER_BLOCK as u64) as usize;
return resolve_via_indirect_node(dev, sb, cp, inode.i_nid[nid_idx], outer, inner);
}
rel -= span;
}
let span = (NIDS_PER_BLOCK as u64).pow(2) * ADDRS_PER_BLOCK as u64;
if rel < span {
let outer = (rel / ((NIDS_PER_BLOCK as u64) * ADDRS_PER_BLOCK as u64)) as usize;
let mid = ((rel / ADDRS_PER_BLOCK as u64) % NIDS_PER_BLOCK as u64) as usize;
let inner = (rel % ADDRS_PER_BLOCK as u64) as usize;
let top_nid = inode.i_nid[NID_TRIPLE_INDIRECT];
if top_nid == 0 {
return Ok(NULL_ADDR);
}
let top_blk = lookup_node(dev, sb, cp, top_nid)?.block;
let mut blk = vec![0u8; F2FS_BLKSIZE];
dev.read_at(top_blk as u64 * sb.block_size() as u64, &mut blk)?;
let nids = decode_indirect_node(&blk)?;
let mid_nid = nids[outer];
if mid_nid == 0 {
return Ok(NULL_ADDR);
}
return resolve_via_indirect_node(dev, sb, cp, mid_nid, mid, inner);
}
Err(crate::Error::InvalidImage(format!(
"f2fs: logical block {idx} exceeds maximum file size"
)))
}
fn resolve_via_direct_node(
dev: &mut dyn BlockDevice,
sb: &Superblock,
cp: &Checkpoint,
nid: u32,
inner: usize,
) -> Result<u32> {
if nid == 0 {
return Ok(NULL_ADDR);
}
let blk = lookup_node(dev, sb, cp, nid)?.block;
let mut buf = vec![0u8; F2FS_BLKSIZE];
dev.read_at(blk as u64 * sb.block_size() as u64, &mut buf)?;
let ptrs = decode_direct_node(&buf)?;
Ok(ptrs[inner])
}
fn resolve_via_indirect_node(
dev: &mut dyn BlockDevice,
sb: &Superblock,
cp: &Checkpoint,
nid: u32,
outer: usize,
inner: usize,
) -> Result<u32> {
if nid == 0 {
return Ok(NULL_ADDR);
}
let blk = lookup_node(dev, sb, cp, nid)?.block;
let mut buf = vec![0u8; F2FS_BLKSIZE];
dev.read_at(blk as u64 * sb.block_size() as u64, &mut buf)?;
let nids = decode_indirect_node(&buf)?;
resolve_via_direct_node(dev, sb, cp, nids[outer], inner)
}
pub struct FileReader<'a> {
pub(crate) dev: &'a mut dyn BlockDevice,
pub(crate) sb: Superblock,
pub(crate) cp: Checkpoint,
pub(crate) inode: F2fsInode,
pub(crate) inode_block: Vec<u8>,
pub(crate) pos: u64,
pub(crate) block_buf: Vec<u8>,
pub(crate) cached_block: u64,
}
impl<'a> FileReader<'a> {
pub(crate) fn new(
dev: &'a mut dyn BlockDevice,
sb: Superblock,
cp: Checkpoint,
inode_block: Vec<u8>,
) -> Result<Self> {
let inode = decode_inode_block(&inode_block)?;
Ok(Self {
dev,
sb,
cp,
inode,
inode_block,
pos: 0,
block_buf: vec![0u8; F2FS_BLKSIZE],
cached_block: u64::MAX,
})
}
fn fill_logical_block(&mut self, idx: u64) -> std::io::Result<()> {
if self.cached_block == idx {
return Ok(());
}
if self.inode.is_inline_data() {
self.block_buf.fill(0);
let payload = self.inode.inline_payload(&self.inode_block);
let n = payload.len().min(F2FS_BLKSIZE);
self.block_buf[..n].copy_from_slice(&payload[..n]);
self.cached_block = idx;
return Ok(());
}
let phys = logical_to_physical(self.dev, &self.sb, &self.cp, &self.inode, idx)
.map_err(std::io::Error::other)?;
if phys == NULL_ADDR || phys == NEW_ADDR {
self.block_buf.fill(0);
} else {
self.dev
.read_at(
phys as u64 * self.sb.block_size() as u64,
&mut self.block_buf,
)
.map_err(std::io::Error::other)?;
}
self.cached_block = idx;
Ok(())
}
}
impl<'a> Read for FileReader<'a> {
fn read(&mut self, out: &mut [u8]) -> std::io::Result<usize> {
let total = self.inode.size;
if self.pos >= total || out.is_empty() {
return Ok(0);
}
let bs = F2FS_BLKSIZE as u64;
let idx = self.pos / bs;
let off = (self.pos % bs) as usize;
self.fill_logical_block(idx)?;
let remaining_in_block = F2FS_BLKSIZE - off;
let remaining_in_file = (total - self.pos) as usize;
let n = out.len().min(remaining_in_block).min(remaining_in_file);
out[..n].copy_from_slice(&self.block_buf[off..off + n]);
self.pos += n as u64;
Ok(n)
}
}