ext4fs 0.1.0

Rust implementation of ext4 file system used in user space
Documentation
use std::{
    cmp,
    io::{Error, ErrorKind, Read, Seek},
};

use super::extent::Extent;

pub struct File<R> {
    reader: R,
    extents: Vec<Extent>,
    len: u64,
    current: u64,

    block_size: u64,
}

impl<R: Read + Seek> File<R> {
    pub(crate) fn new(reader: R, extents: Vec<Extent>, len: u64, block_size: u64) -> Self {
        Self {
            reader,
            extents,
            len,
            current: 0,
            block_size,
        }
    }
}

impl<R: Read + Seek> Read for File<R> {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        if buf.is_empty() || self.current >= self.len {
            return Ok(0);
        }

        let mut buf_pos = 0;

        let mut offset = 0;
        for e in &self.extents {
            let extent_size = e.len as u64 * self.block_size;
            if self.current >= offset + extent_size {
                offset = offset + extent_size;
                continue;
            }

            let file_remain_len = self.len - self.current;
            let buf_remain_len = (buf.len() - buf_pos) as u64;

            let temp = e.read_bytes(
                self.block_size,
                &mut self.reader,
                self.current - offset,
                cmp::min(file_remain_len, buf_remain_len),
            )?;
            buf[buf_pos..buf_pos + temp.len()].copy_from_slice(&temp);
            buf_pos += temp.len();
            self.current += temp.len() as u64;

            if buf_pos >= buf.len() {
                return Ok(buf_pos);
            }
        }

        Ok(buf_pos)
    }
}

impl<R: Read + Seek> Seek for File<R> {
    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
        self.current = match pos {
            std::io::SeekFrom::Start(offset) => offset,
            std::io::SeekFrom::End(offset) => {
                if !offset.is_negative() {
                    return Err(Error::new(
                        ErrorKind::Other,
                        format!("Expect negative offset"),
                    ));
                }
                self.len - offset.wrapping_abs() as u64
            }
            std::io::SeekFrom::Current(offset) => {
                if offset.is_negative() {
                    self.current - offset.wrapping_abs() as u64
                } else {
                    self.current + offset as u64
                }
            }
        };

        Ok(self.current)
    }
}