From 30aed8bde77af56e27c3407e1e0b3fe782e90be7 Mon Sep 17 00:00:00 2001 From: Szymon Walter Date: Tue, 20 Mar 2018 15:17:16 +0100 Subject: [PATCH] add an `InodeBlocks` `Iterator` for iterating over the blocks of an inode --- ext2.img | Bin 4194304 -> 4194304 bytes src/fs.rs | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 1 + 3 files changed, 214 insertions(+), 7 deletions(-) diff --git a/ext2.img b/ext2.img index aa13d85580f8a1c5d625a06823825f898be6d778..89fa28bbc23f6814adf7bd700376eb65363c831c 100644 GIT binary patch delta 283 zcmWN=w++HT063Co!tqd{N{u?b^wG}%gA@!g%m|~5G0p^&Ofk(24Q82Ro&^?JVwn}1 utg^;B8*H-0HaqOH$36!fa>Ow$PB`U^bJ|>R$rab!aLXO{JiLT+{NDa5T21T# delta 283 zcmWN=H4eg10D$4tu!p<5Ymwrzv9omGG%sOb;~q>UVc;Mfqcc1ImwZx6Rq|!(#=~{4 z%G=Vv7wzmiH(}p}tx%;#od!Mh(nmi76bv%NFe8jI#yAs9GQ~7A%reJ33oNq4GApdI u#yT5pvc)z#?6Su`2OM(5F())R<&1MKXmQCE*W7T+9rrx&_!0K$_xuA>;Z6 { pub inner: T, @@ -90,11 +91,41 @@ where &mut self, &(ref inode, offset): &(Inode, Address), ) -> Result<(), Error> { - let slice = VolumeSlice::from_cast(inode, offset); + let slice = VolumeSlice::from_cast(&inode.inner, offset); let commit = slice.commit(); self.volume.commit(commit).map_err(|err| Error::from(err)) } + pub fn read_inode( + &self, + buf: &mut [u8], + inode: &Inode, + ) -> Result { + let total_size = inode.size(); + let mut read_size = 0; + let block_size = self.block_size(); + let offset = 0; + + for (data, _) in InodeBlocks::new(self, &inode) { + let data_size = block_size + .min(total_size - read_size) + .min(buf.len() - offset); + let end = offset + data_size; + buf[offset..end].copy_from_slice(&data[..data_size]); + read_size += data_size; + } + + Ok(read_size) + } + + pub fn write_inode( + &self, + &(ref inode, offset): &(Inode, Address), + buf: &[u8], + ) -> Result { + unimplemented!() + } + pub fn root_inode(&self) -> (Inode, Address) { self.inode_nth(2).unwrap() } @@ -173,6 +204,14 @@ where pub fn log_block_size(&self) -> u32 { self.superblock().log_block_size + 10 } + + pub fn sector_size(&self) -> usize { + S::SIZE + } + + pub fn log_sector_size(&self) -> u32 { + S::LOG_SIZE + } } pub struct Inodes<'a, S: Size, V: 'a + Volume>> { @@ -204,18 +243,141 @@ where let offset = Address::with_block_size( inodes_block, - index * self.inode_size, + (index * self.inode_size) as isize, self.log_block_size, ); - unsafe { - Inode::find_inode(self.volume, offset, self.inode_size).ok() - } + let raw = unsafe { + RawInode::find_inode(self.volume, offset, self.inode_size).ok() + }; + raw.map(|(raw, offset)| (Inode::new(raw), offset)) } else { None } } } +#[derive(Debug, Clone)] +pub struct Inode { + inner: RawInode, +} + +impl Inode { + pub fn new(inner: RawInode) -> Inode { + Inode { inner } + } + + pub fn block(&self, index: usize) -> Option> { + // number of blocks in direct table: 12 + // number of blocks in indirect table: block_size/4 + // why? + // - a block is n bytes long + // - a block address occupies 32 bits, or 4 bytes + // - thus, n/4 + // number of blocks in doubly table: (block_size/4)^2 + // why? + // - every entry in the doubly table points to another block + // - that's n/4 blocks, where n is the block size + // - every block contains n/4 block pointers + // - that's n/4 blocks with n/4 pointers each = (n/4)^2 + // number of blocks in triply table: (block_size/4)^3 + + let bs4 = self.block_size() / 4; + if index < 12 { + NonZero::new(self.inner.direct_pointer[index] as usize) + } else if index < bs4 { + unimplemented!("indirect pointer table"); + } else if index < bs4 * bs4 { + unimplemented!("doubly indirect pointer table"); + } else if index < bs4 * bs4 * bs4 { + unimplemented!("triply indirect pointer table"); + } else { + None + } + } + + pub fn in_use(&self) -> bool { + self.inner.hard_links > 0 + } + + pub fn uid(&self) -> u16 { + self.inner.uid + } + + pub fn sectors(&self) -> usize { + self.inner.sectors_count as usize + } + + pub fn size32(&self) -> u32 { + self.inner.size_low + } + + pub fn size64(&self) -> u64 { + self.inner.size_low as u64 | (self.inner.size_high as u64) << 32 + } + + #[cfg(target_pointer_width = "64")] + #[inline] + pub fn size(&self) -> usize { + self.size64() as usize + } + + #[cfg(target_pointer_width = "32")] + #[inline] + pub fn size(&self) -> usize { + self.size32() as usize + } +} + +pub struct InodeBlocks<'a, 'b, S: 'a + Size, V: 'a + Volume>> { + fs: &'a Ext2, + inode: &'b Inode, + index: usize, +} + +impl<'a, 'b, S: Size + Copy, V: 'a + Volume>> + InodeBlocks<'a, 'b, S, V> +where + Error: From, +{ + pub fn new( + fs: &'a Ext2, + inode: &'b Inode, + ) -> InodeBlocks<'a, 'b, S, V> { + InodeBlocks { + fs: fs, + inode, + index: 0, + } + } +} + +impl<'a, 'b, S: Size + Copy, V: 'a + Volume>> Iterator + for InodeBlocks<'a, 'b, S, V> +where + Error: From, +{ + type Item = (VolumeSlice<'a, u8, Address>, Address); + + fn next(&mut self) -> Option { + let block = self.inode.block(self.index); + block + .map(|block| { + let block = block.get(); + self.index += 1; + Address::with_block_size(block, 0, self.fs.log_block_size()) + ..Address::with_block_size( + block + 1, + 0, + self.fs.log_block_size(), + ) + }) + .and_then(|block| { + let offset = block.start; + self.fs.volume.slice(block).map(|slice| (slice, offset)) + }) + } +} + #[cfg(test)] mod tests { use std::fs::File; @@ -224,7 +386,7 @@ mod tests { use sector::{Address, Size512}; use volume::Volume; - use super::Ext2; + use super::{Ext2, InodeBlocks}; #[test] fn file_len() { @@ -281,4 +443,48 @@ mod tests { println!("{:?}", inode); } } + + #[test] + fn inode_blocks() { + use std::str; + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Ext2::::new(file).unwrap(); + + let inodes = fs.inodes().filter(|inode| { + inode.0.in_use() && inode.0.uid() == 1000 && inode.0.size() < 1024 + }); + for inode in inodes { + println!("{:?}", inode.0); + let size = inode.0.size(); + for block in InodeBlocks::new(&fs, &inode.0) { + let (data, _) = block; + assert_eq!(data.len(), fs.block_size()); + println!("{:?}", &data[..size]); + let _ = str::from_utf8(&data[..size]) + .map(|string| println!("{}", string)); + } + } + } + + #[test] + fn read_inode() { + use std::str; + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Ext2::::new(file).unwrap(); + + let inodes = fs.inodes().filter(|inode| { + inode.0.in_use() && inode.0.uid() == 1000 && inode.0.size() < 1024 + }); + for (inode, _) in inodes { + let mut buf = Vec::with_capacity(inode.size()); + unsafe { + buf.set_len(inode.size()); + } + let size = fs.read_inode(&mut buf[..], &inode); + assert_eq!(size, Ok(inode.size())); + unsafe { + buf.set_len(size.unwrap()); + } + } + } } diff --git a/src/lib.rs b/src/lib.rs index 0e47614..59a6381 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ #![feature(macro_lifetime_matcher)] #![feature(const_fn)] #![feature(step_trait)] +#![feature(nonzero)] #![cfg_attr(all(not(test), feature = "no_std"), no_std)] #[macro_use]