From 0d01a87992f4978b02c016ff508fb66c128659f9 Mon Sep 17 00:00:00 2001 From: Szymon Walter Date: Thu, 22 Mar 2018 11:07:33 +0100 Subject: [PATCH 1/2] Add Thread-safe version of the ext2 implementation The `Synced` struct is a wrapper over the `Ext2` struct. It guarantees thread safety through an `Arc` and a spinlock (`spin::Mutex`). The `fs::sync` mod provides Thread-safe versions of structs found in `fs`. --- Cargo.toml | 1 + src/{fs.rs => fs/mod.rs} | 28 +- src/fs/sync.rs | 634 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/volume/mod.rs | 1 + 5 files changed, 652 insertions(+), 13 deletions(-) rename src/{fs.rs => fs/mod.rs} (97%) create mode 100644 src/fs/sync.rs diff --git a/Cargo.toml b/Cargo.toml index a216e65..8330f1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Szymon Walter "] [dependencies] bitflags = "1.0" rlibc = { version = "1.0", optional = true } +spin = "0.4" [features] default = ["no_std"] diff --git a/src/fs.rs b/src/fs/mod.rs similarity index 97% rename from src/fs.rs rename to src/fs/mod.rs index 17138a1..7889ffc 100644 --- a/src/fs.rs +++ b/src/fs/mod.rs @@ -1,5 +1,4 @@ use core::mem; -use core::slice; use core::fmt::{self, Debug}; use core::nonzero::NonZero; @@ -12,7 +11,9 @@ use sys::superblock::Superblock; use sys::block_group::BlockGroupDescriptor; use sys::inode::Inode as RawInode; -struct Struct { +pub mod sync; + +pub(crate) struct Struct { pub inner: T, pub offset: Address, } @@ -26,9 +27,10 @@ impl From<(T, Address)> for Struct { /// Safe wrapper for raw sys structs pub struct Ext2> { - volume: V, - superblock: Struct, - block_groups: Struct, S>, + // TODO: should this have some different vis? + pub(crate) volume: V, + pub(crate) superblock: Struct, + pub(crate) block_groups: Struct, S>, } impl> Ext2 { @@ -162,9 +164,9 @@ impl> Ext2 { (self.superblock().rev_major, self.superblock().rev_minor) } - pub fn inode_size<'vol>(&'vol self) -> usize { + pub fn inode_size(&self) -> usize { if self.version().0 == 0 { - mem::size_of::>() + mem::size_of::() } else { // note: inodes bigger than 128 are not supported self.superblock().inode_size as usize @@ -494,7 +496,7 @@ pub struct Directory< impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator for Directory<'vol, 'inode, S, V> { - type Item = Result, Error>; + type Item = Result; fn next(&mut self) -> Option { if self.buffer.is_none() || self.offset >= self.block_size { @@ -520,8 +522,7 @@ impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator let len = buffer[6]; let ty = buffer[7]; - let ptr = unsafe { buffer.as_ptr().add(8) }; - let name = unsafe { slice::from_raw_parts(ptr, len as usize) }; + let name = buffer[8..8 + len as usize].to_vec(); self.offset += size as usize; @@ -533,8 +534,9 @@ impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator } } -pub struct DirectoryEntry<'a> { - pub name: &'a [u8], +#[derive(Clone)] +pub struct DirectoryEntry { + pub name: Vec, pub inode: usize, pub ty: u8, } @@ -695,7 +697,7 @@ mod tests { for entry in dir { assert!(entry.is_ok()); let entry = entry.unwrap(); - let entry_name = str::from_utf8(entry.name).unwrap_or("?"); + let entry_name = str::from_utf8(&entry.name).unwrap_or("?"); println!("{}/{} => {}", name, entry_name, entry.inode,); if entry_name != "." && entry_name != ".." { walk( diff --git a/src/fs/sync.rs b/src/fs/sync.rs new file mode 100644 index 0000000..7b997f1 --- /dev/null +++ b/src/fs/sync.rs @@ -0,0 +1,634 @@ +use core::fmt::{self, Debug}; +use core::nonzero::NonZero; + +use alloc::Vec; +use alloc::arc::Arc; + +use spin::{Mutex, MutexGuard}; + +use error::Error; +use sector::{Address, SectorSize}; +use volume::Volume; +use sys::inode::Inode as RawInode; + +use super::Ext2; + +pub struct Synced { + inner: Arc>, +} + +impl Synced { + pub fn with_inner(inner: T) -> Synced { + Synced { + inner: Arc::new(Mutex::new(inner)), + } + } + + pub fn inner<'a>(&'a self) -> MutexGuard<'a, T> { + self.inner.lock() + } +} + +impl Clone for Synced { + fn clone(&self) -> Self { + Synced { + inner: self.inner.clone(), + } + } +} + +impl> Synced> { + pub fn new(volume: V) -> Result>, Error> { + Ext2::new(volume).map(|inner| Synced::with_inner(inner)) + } + + #[allow(dead_code)] + fn update_global(&mut self) -> Result<(), Error> { + self.inner().update_global() + } + + pub fn read_inode( + &self, + buf: &mut [u8], + inode: &Inode, + ) -> Result { + let total_size = inode.size(); + let block_size = self.block_size(); + let mut offset = 0; + + for block in inode.blocks() { + match block { + Ok((data, _)) => { + let data_size = block_size + .min(total_size - offset) + .min(buf.len() - offset); + let end = offset + data_size; + buf[offset..end].copy_from_slice(&data[..data_size]); + offset += data_size; + } + Err(err) => return Err(err.into()), + } + } + + Ok(offset) + } + + pub fn write_inode( + &self, + _inode: &(Inode, Address), + _buf: &[u8], + ) -> Result { + unimplemented!() + } + + pub fn root_inode(&self) -> (Inode, Address) { + self.inode_nth(2).unwrap() + } + + pub fn inode_nth(&self, index: usize) -> Option<(Inode, Address)> { + self.inodes_nth(index).next() + } + + pub fn inodes(&self) -> Inodes { + self.inodes_nth(1) + } + + pub fn inodes_nth(&self, index: usize) -> Inodes { + assert!(index > 0, "inodes are 1-indexed"); + let inner = self.inner(); + Inodes { + fs: self.clone(), + log_block_size: inner.log_block_size(), + inode_size: inner.inode_size(), + inodes_per_group: inner.inodes_count(), + inodes_count: inner.total_inodes_count(), + index, + } + } + + pub fn version(&self) -> (u32, u16) { + self.inner().version() + } + + pub fn inode_size(&self) -> usize { + self.inner().inode_size() + } + + pub fn inodes_count(&self) -> usize { + self.inner().inodes_count() + } + + pub fn total_inodes_count(&self) -> usize { + self.inner().total_inodes_count() + } + + pub fn block_group_count(&self) -> Result { + self.inner().block_group_count() + } + + pub fn total_block_count(&self) -> usize { + self.inner().total_block_count() + } + + pub fn free_block_count(&self) -> usize { + self.inner().free_block_count() + } + + pub fn block_size(&self) -> usize { + self.inner().block_size() + } + + pub fn log_block_size(&self) -> u32 { + self.inner().log_block_size() + } + + pub fn sector_size(&self) -> usize { + S::SIZE + } + + pub fn log_sector_size(&self) -> u32 { + S::LOG_SIZE + } +} + +impl> Debug for Synced> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Synced>", S::SIZE) + } +} + +#[derive(Debug, Clone)] +pub struct Inodes> { + fs: Synced>, + log_block_size: u32, + inode_size: usize, + inodes_per_group: usize, + inodes_count: usize, + index: usize, +} + +impl> Iterator for Inodes { + type Item = (Inode, Address); + + fn next(&mut self) -> Option { + if self.index < self.inodes_count { + let block_group = (self.index - 1) / self.inodes_per_group; + let index = (self.index - 1) % self.inodes_per_group; + self.index += 1; + + let fs = self.fs.inner(); + + let inodes_block = + fs.block_groups.inner[block_group].inode_table_block; + + let offset = Address::with_block_size( + inodes_block, + (index * self.inode_size) as i32, + self.log_block_size, + ); + let raw = unsafe { + RawInode::find_inode(&fs.volume, offset, self.inode_size).ok() + }; + raw.map(|(raw, offset)| (Inode::new(self.fs.clone(), raw), offset)) + } else { + None + } + } +} + +#[derive(Debug)] +pub struct Inode> { + fs: Synced>, + inner: RawInode, +} + +impl> Clone for Inode { + fn clone(&self) -> Self { + Inode { + fs: self.fs.clone(), + inner: self.inner, + } + } +} + +impl> Inode { + pub fn new(fs: Synced>, inner: RawInode) -> Inode { + Inode { fs, inner } + } + + pub fn blocks(&self) -> InodeBlocks { + InodeBlocks { + inode: self.clone(), + index: 0, + } + } + + pub fn directory(&self) -> Option> { + use sys::inode::TypePerm; + if unsafe { self.inner.type_perm.contains(TypePerm::DIRECTORY) } { + Some(Directory { + blocks: self.blocks(), + offset: 0, + buffer: None, + block_size: self.fs.block_size(), + }) + } else { + None + } + } + + pub fn block(&self, index: usize) -> Option> { + self.try_block(index).ok().and_then(|block| block) + } + + pub fn try_block( + &self, + mut index: usize, + ) -> Result>, Error> { + // 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 + + fn block_index>( + volume: &V, + block: u32, + index: usize, + log_block_size: u32, + ) -> Result>, Error> { + let offset = (index * 4) as i32; + let end = offset + 4; + let addr = Address::with_block_size(block, offset, log_block_size); + let end = Address::with_block_size(block, end, log_block_size); + let block = volume.slice(addr..end); + match block { + Ok(block) => unsafe { + Ok(NonZero::new(block.dynamic_cast::().0)) + }, + Err(err) => Err(err.into()), + } + } + + let bs4 = self.fs.block_size() / 4; + let log_block_size = self.fs.log_block_size(); + + if index < 12 { + return Ok(NonZero::new(self.inner.direct_pointer[index])); + } + + index -= 12; + + if index < bs4 { + let block = self.inner.indirect_pointer; + return block_index( + &self.fs.inner().volume, + block, + index, + log_block_size, + ); + } + + index -= bs4; + + if index < bs4 * bs4 { + let indirect_index = index >> (log_block_size + 2); + let block = match block_index( + &self.fs.inner().volume, + self.inner.doubly_indirect, + indirect_index, + log_block_size, + ) { + Ok(Some(block)) => block.get(), + Ok(None) => return Ok(None), + Err(err) => return Err(err), + }; + return block_index( + &self.fs.inner().volume, + block, + index & (bs4 - 1), + log_block_size, + ); + } + + index -= bs4 * bs4; + + if index < bs4 * bs4 * bs4 { + let doubly_index = index >> (2 * log_block_size + 4); + let indirect = match block_index( + &self.fs.inner().volume, + self.inner.triply_indirect, + doubly_index, + log_block_size, + ) { + Ok(Some(block)) => block.get(), + Ok(None) => return Ok(None), + Err(err) => return Err(err), + }; + let indirect_index = (index >> (log_block_size + 2)) & (bs4 - 1); + let block = match block_index( + &self.fs.inner().volume, + indirect as u32, + indirect_index, + log_block_size, + ) { + Ok(Some(block)) => block.get(), + Ok(None) => return Ok(None), + Err(err) => return Err(err), + }; + return block_index( + &self.fs.inner().volume, + block, + index & (bs4 - 1), + log_block_size, + ); + } + + Ok(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 + } +} + +#[derive(Debug, Clone)] +pub struct InodeBlocks> { + inode: Inode, + index: usize, +} + +impl> Iterator for InodeBlocks { + type Item = Result<(Vec, Address), Error>; + + fn next(&mut self) -> Option { + let block = self.inode.try_block(self.index); + let block = match block { + Ok(Some(ok)) => ok, + Ok(None) => return None, + Err(err) => return Some(Err(err)), + }; + + self.index += 1; + + let block = block.get(); + let log_block_size = self.inode.fs.log_block_size(); + let offset = Address::with_block_size(block, 0, log_block_size); + let end = Address::with_block_size(block + 1, 0, log_block_size); + + let fs = self.inode.fs.inner(); + let slice = fs.volume + .slice(offset..end) + .map(|slice| (slice.to_vec(), offset)) + .map_err(|err| err.into()); + Some(slice) + } +} + +#[derive(Debug, Clone)] +pub struct Directory> { + blocks: InodeBlocks, + offset: usize, + buffer: Option>, + block_size: usize, +} + +impl> Iterator for Directory { + type Item = Result; + + fn next(&mut self) -> Option { + if self.buffer.is_none() || self.offset >= self.block_size { + self.buffer = match self.blocks.next() { + None => return None, + Some(Ok((block, _))) => Some(block), + Some(Err(err)) => return Some(Err(err)), + }; + + self.offset = 0; + } + + let buffer = &self.buffer.as_ref().unwrap()[self.offset..]; + + let inode = buffer[0] as u32 | (buffer[1] as u32) << 8 + | (buffer[2] as u32) << 16 + | (buffer[3] as u32) << 24; + if inode == 0 { + return None; + } + + let size = buffer[4] as u16 | (buffer[5] as u16) << 8; + let len = buffer[6]; + let ty = buffer[7]; + + let name = buffer[8..8 + len as usize].to_vec(); + + self.offset += size as usize; + + Some(Ok(DirectoryEntry { + name: name, + inode: inode as usize, + ty: ty, + })) + } +} + +#[derive(Clone)] +pub struct DirectoryEntry { + pub name: Vec, + pub inode: usize, + pub ty: u8, +} + +#[cfg(test)] +mod tests { + use std::fs::File; + use std::cell::RefCell; + + use sector::{SectorSize, Size512}; + use volume::Volume; + + use super::{Ext2, Inode, Synced}; + + #[test] + fn file() { + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Synced::>::new(file); + + assert!( + fs.is_ok(), + "Err({:?})", + fs.err().unwrap_or_else(|| unreachable!()), + ); + + let fs = fs.unwrap(); + + let vers = fs.version(); + println!("version: {}.{}", vers.0, vers.1); + assert_eq!(128, fs.inode_size()); + } + + #[test] + fn inodes() { + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Synced::>::new(file); + + assert!( + fs.is_ok(), + "Err({:?})", + fs.err().unwrap_or_else(|| unreachable!()), + ); + + let fs = fs.unwrap(); + + let inodes = fs.inodes().filter(|inode| inode.0.in_use()); + for inode in inodes { + println!("{:?}", inode); + } + } + + #[test] + fn inode_blocks() { + use std::str; + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Synced::>::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 inode.0.blocks() { + let (data, _) = block.unwrap(); + assert_eq!(data.len(), fs.block_size()); + println!("{:?}", &data[..size]); + let _ = str::from_utf8(&data[..size]) + .map(|string| println!("{}", string)); + } + } + } + + #[test] + fn read_inode() { + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Synced::>::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!(size.is_ok()); + let size = size.unwrap(); + assert_eq!(size, inode.size()); + unsafe { + buf.set_len(size); + } + } + } + + #[test] + fn read_big() { + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Synced::>::new(file).unwrap(); + + let inodes = fs.inodes().filter(|inode| { + inode.0.in_use() && inode.0.uid() == 1000 + && inode.0.size() == 537600 + }); + 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!(size.is_ok()); + let size = size.unwrap(); + assert_eq!(size, inode.size()); + unsafe { + buf.set_len(size); + } + + for (i, &x) in buf.iter().enumerate() { + if i & 1 == 0 { + assert_eq!(x, b'u', "{}", i); + } else { + assert_eq!(x, b'\n', "{}", i); + } + } + } + } + + #[test] + fn walkdir() { + use std::str; + + fn walk<'vol, S: SectorSize, V: Volume>( + fs: &'vol Synced>, + inode: Inode, + name: String, + ) { + inode.directory().map(|dir| { + for entry in dir { + assert!(entry.is_ok()); + let entry = entry.unwrap(); + let entry_name = str::from_utf8(&entry.name).unwrap_or("?"); + println!("{}/{} => {}", name, entry_name, entry.inode,); + if entry_name != "." && entry_name != ".." { + walk( + fs, + fs.inode_nth(entry.inode).unwrap().0, + format!("{}/{}", name, entry_name), + ); + } + } + }); + } + + let file = RefCell::new(File::open("ext2.img").unwrap()); + let fs = Synced::>::new(file).unwrap(); + + let (root, _) = fs.root_inode(); + walk(&fs, root, String::new()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3b3be42..f6e749d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; #[macro_use] extern crate bitflags; +extern crate spin; #[cfg(any(test, not(feature = "no_std")))] extern crate core; diff --git a/src/volume/mod.rs b/src/volume/mod.rs index c5eaebf..f1b08e3 100644 --- a/src/volume/mod.rs +++ b/src/volume/mod.rs @@ -31,6 +31,7 @@ pub trait Volume { ) -> Result, Self::Error>; } +#[derive(Debug, Clone, PartialEq, Hash)] pub struct VolumeSlice<'a, T: 'a + Clone, S: SectorSize> { inner: Cow<'a, [T]>, index: Address, From 04d54ea3207170851fecec0fe0b3e43d0f460f69 Mon Sep 17 00:00:00 2001 From: Szymon Walter Date: Thu, 22 Mar 2018 11:23:58 +0100 Subject: [PATCH 2/2] Remove old non-thread-safe ext2 implementation Old implementation is now considered legacy. Thread-safety is now enforced for any non-trivial operations. All trivial operations are available on the regular `Ext2` struct found in `fs` module. `Synced` is now the only way to go. --- src/fs/mod.rs | 580 ++----------------------------------------------- src/fs/sync.rs | 146 +++++-------- 2 files changed, 66 insertions(+), 660 deletions(-) diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 7889ffc..8feccfa 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -1,12 +1,10 @@ use core::mem; -use core::fmt::{self, Debug}; -use core::nonzero::NonZero; use alloc::Vec; use error::Error; use sector::{Address, SectorSize}; -use volume::{Volume, VolumeSlice}; +use volume::Volume; use sys::superblock::Superblock; use sys::block_group::BlockGroupDescriptor; use sys::inode::Inode as RawInode; @@ -64,104 +62,11 @@ impl> Ext2 { }) } - #[allow(dead_code)] - fn update_global(&mut self) -> Result<(), Error> { - // superblock - { - let slice = VolumeSlice::from_cast( - &self.superblock.inner, - self.superblock.offset, - ); - let commit = slice.commit(); - self.volume.commit(commit).map_err(|err| err.into())?; - } - - // block group descriptors - let mut offset = self.block_groups.offset; - for descr in &self.block_groups.inner { - let slice = VolumeSlice::from_cast(descr, offset); - let commit = slice.commit(); - self.volume.commit(commit).map_err(|err| err.into())?; - offset = - offset + Address::from(mem::size_of::()); - } - - Ok(()) - } - - pub fn read_inode<'vol>( - &'vol self, - buf: &mut [u8], - inode: &Inode<'vol, S, V>, - ) -> Result { - let total_size = inode.size(); - let block_size = self.block_size(); - let mut offset = 0; - - for block in inode.blocks() { - match block { - Ok((data, _)) => { - let data_size = block_size - .min(total_size - offset) - .min(buf.len() - offset); - let end = offset + data_size; - buf[offset..end].copy_from_slice(&data[..data_size]); - offset += data_size; - } - Err(err) => return Err(err.into()), - } - } - - Ok(offset) - } - - pub fn write_inode<'vol>( - &'vol self, - _inode: &(Inode<'vol, S, V>, Address), - _buf: &[u8], - ) -> Result { - unimplemented!() - } - - pub fn root_inode<'vol>(&'vol self) -> (Inode<'vol, S, V>, Address) { - self.inode_nth(2).unwrap() - } - - pub fn inode_nth<'vol>( - &'vol self, - index: usize, - ) -> Option<(Inode<'vol, S, V>, Address)> { - self.inodes_nth(index).next() - } - - pub fn inodes<'vol>(&'vol self) -> Inodes<'vol, S, V> { - self.inodes_nth(1) - } - - pub fn inodes_nth<'vol>(&'vol self, index: usize) -> Inodes<'vol, S, V> { - assert!(index > 0, "inodes are 1-indexed"); - Inodes { - fs: self, - block_groups: &self.block_groups.inner, - log_block_size: self.log_block_size(), - inode_size: self.inode_size(), - inodes_per_group: self.inodes_count(), - inodes_count: self.total_inodes_count(), - index, - } - } - - fn superblock(&self) -> &Superblock { - &self.superblock.inner - } - - #[allow(dead_code)] - fn superblock_mut(&mut self) -> &mut Superblock { - &mut self.superblock.inner - } - pub fn version(&self) -> (u32, u16) { - (self.superblock().rev_major, self.superblock().rev_minor) + ( + self.superblock.inner.rev_major, + self.superblock.inner.rev_minor, + ) } pub fn inode_size(&self) -> usize { @@ -169,20 +74,21 @@ impl> Ext2 { mem::size_of::() } else { // note: inodes bigger than 128 are not supported - self.superblock().inode_size as usize + self.superblock.inner.inode_size as usize } } pub fn inodes_count(&self) -> usize { - self.superblock().inodes_per_group as _ + self.superblock.inner.inodes_per_group as _ } pub fn total_inodes_count(&self) -> usize { - self.superblock().inodes_count as _ + self.superblock.inner.inodes_count as _ } pub fn block_group_count(&self) -> Result { - self.superblock() + self.superblock + .inner .block_group_count() .map(|count| count as usize) .map_err(|(a, b)| Error::BadBlockGroupCount { @@ -192,19 +98,19 @@ impl> Ext2 { } pub fn total_block_count(&self) -> usize { - self.superblock().blocks_count as _ + self.superblock.inner.blocks_count as _ } pub fn free_block_count(&self) -> usize { - self.superblock().free_blocks_count as _ + self.superblock.inner.free_blocks_count as _ } pub fn block_size(&self) -> usize { - self.superblock().block_size() + self.superblock.inner.block_size() } pub fn log_block_size(&self) -> u32 { - self.superblock().log_block_size + 10 + self.superblock.inner.log_block_size + 10 } pub fn sector_size(&self) -> usize { @@ -216,340 +122,15 @@ impl> Ext2 { } } -impl> Debug for Ext2 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Ext2<{}>", S::SIZE) - } -} - -pub struct Inodes<'vol, S: SectorSize, V: 'vol + Volume> { - fs: &'vol Ext2, - block_groups: &'vol [BlockGroupDescriptor], - log_block_size: u32, - inode_size: usize, - inodes_per_group: usize, - inodes_count: usize, - index: usize, -} - -impl<'vol, S: SectorSize, V: 'vol + Volume> Iterator - for Inodes<'vol, S, V> -{ - type Item = (Inode<'vol, S, V>, Address); - - fn next(&mut self) -> Option { - if self.index < self.inodes_count { - let block_group = (self.index - 1) / self.inodes_per_group; - let index = (self.index - 1) % self.inodes_per_group; - self.index += 1; - - let inodes_block = self.block_groups[block_group].inode_table_block; - - let offset = Address::with_block_size( - inodes_block, - (index * self.inode_size) as i32, - self.log_block_size, - ); - let raw = unsafe { - RawInode::find_inode(&self.fs.volume, offset, self.inode_size) - .ok() - }; - raw.map(|(raw, offset)| (Inode::new(self.fs, raw), offset)) - } else { - None - } - } -} - -#[derive(Debug, Clone)] -pub struct Inode<'vol, S: SectorSize, V: 'vol + Volume> { - fs: &'vol Ext2, - inner: RawInode, -} - -impl<'vol, S: SectorSize, V: 'vol + Volume> Inode<'vol, S, V> { - pub fn new(fs: &'vol Ext2, inner: RawInode) -> Inode<'vol, S, V> { - Inode { fs, inner } - } - - pub fn blocks<'inode>(&'inode self) -> InodeBlocks<'vol, 'inode, S, V> { - InodeBlocks { - inode: self, - index: 0, - } - } - - pub fn directory<'inode>( - &'inode self, - ) -> Option> { - use sys::inode::TypePerm; - if unsafe { self.inner.type_perm.contains(TypePerm::DIRECTORY) } { - Some(Directory { - blocks: self.blocks(), - offset: 0, - buffer: None, - block_size: self.fs.block_size(), - }) - } else { - None - } - } - - pub fn block(&self, index: usize) -> Option> { - self.try_block(index).ok().and_then(|block| block) - } - - pub fn try_block( - &self, - mut index: usize, - ) -> Result>, Error> { - // 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 - - fn block_index>( - volume: &V, - block: u32, - index: usize, - log_block_size: u32, - ) -> Result>, Error> { - let offset = (index * 4) as i32; - let end = offset + 4; - let addr = Address::with_block_size(block, offset, log_block_size); - let end = Address::with_block_size(block, end, log_block_size); - let block = volume.slice(addr..end); - match block { - Ok(block) => unsafe { - Ok(NonZero::new(block.dynamic_cast::().0)) - }, - Err(err) => Err(err.into()), - } - } - - let bs4 = self.fs.block_size() / 4; - let log_block_size = self.fs.log_block_size(); - - if index < 12 { - return Ok(NonZero::new(self.inner.direct_pointer[index])); - } - - index -= 12; - - if index < bs4 { - let block = self.inner.indirect_pointer; - return block_index(&self.fs.volume, block, index, log_block_size); - } - - index -= bs4; - - if index < bs4 * bs4 { - let indirect_index = index >> (log_block_size + 2); - let block = match block_index( - &self.fs.volume, - self.inner.doubly_indirect, - indirect_index, - log_block_size, - ) { - Ok(Some(block)) => block.get(), - Ok(None) => return Ok(None), - Err(err) => return Err(err), - }; - return block_index( - &self.fs.volume, - block, - index & (bs4 - 1), - log_block_size, - ); - } - - index -= bs4 * bs4; - - if index < bs4 * bs4 * bs4 { - let doubly_index = index >> (2 * log_block_size + 4); - let indirect = match block_index( - &self.fs.volume, - self.inner.triply_indirect, - doubly_index, - log_block_size, - ) { - Ok(Some(block)) => block.get(), - Ok(None) => return Ok(None), - Err(err) => return Err(err), - }; - let indirect_index = (index >> (log_block_size + 2)) & (bs4 - 1); - let block = match block_index( - &self.fs.volume, - indirect as u32, - indirect_index, - log_block_size, - ) { - Ok(Some(block)) => block.get(), - Ok(None) => return Ok(None), - Err(err) => return Err(err), - }; - return block_index( - &self.fs.volume, - block, - index & (bs4 - 1), - log_block_size, - ); - } - - Ok(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< - 'vol: 'inode, - 'inode, - S: SectorSize, - V: 'vol + Volume, -> { - inode: &'inode Inode<'vol, S, V>, - index: usize, -} - -impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator - for InodeBlocks<'vol, 'inode, S, V> -{ - type Item = Result<(VolumeSlice<'vol, u8, S>, Address), Error>; - - fn next(&mut self) -> Option { - let block = self.inode.try_block(self.index); - let block = match block { - Ok(Some(ok)) => ok, - Ok(None) => return None, - Err(err) => return Some(Err(err)), - }; - - self.index += 1; - - let block = block.get(); - let log_block_size = self.inode.fs.log_block_size(); - let offset = Address::with_block_size(block, 0, log_block_size); - let end = Address::with_block_size(block + 1, 0, log_block_size); - - let slice = self.inode - .fs - .volume - .slice(offset..end) - .map(|slice| (slice, offset)) - .map_err(|err| err.into()); - Some(slice) - } -} - -pub struct Directory< - 'vol: 'inode, - 'inode, - S: SectorSize, - V: 'vol + Volume, -> { - blocks: InodeBlocks<'vol, 'inode, S, V>, - offset: usize, - buffer: Option>, - block_size: usize, -} - -impl<'vol, 'inode, S: SectorSize, V: 'vol + Volume> Iterator - for Directory<'vol, 'inode, S, V> -{ - type Item = Result; - - fn next(&mut self) -> Option { - if self.buffer.is_none() || self.offset >= self.block_size { - self.buffer = match self.blocks.next() { - None => return None, - Some(Ok((block, _))) => Some(block), - Some(Err(err)) => return Some(Err(err)), - }; - - self.offset = 0; - } - - let buffer = &self.buffer.as_ref().unwrap()[self.offset..]; - - let inode = buffer[0] as u32 | (buffer[1] as u32) << 8 - | (buffer[2] as u32) << 16 - | (buffer[3] as u32) << 24; - if inode == 0 { - return None; - } - - let size = buffer[4] as u16 | (buffer[5] as u16) << 8; - let len = buffer[6]; - let ty = buffer[7]; - - let name = buffer[8..8 + len as usize].to_vec(); - - self.offset += size as usize; - - Some(Ok(DirectoryEntry { - name: name, - inode: inode as usize, - ty: ty, - })) - } -} - -#[derive(Clone)] -pub struct DirectoryEntry { - pub name: Vec, - pub inode: usize, - pub ty: u8, -} - #[cfg(test)] mod tests { use std::fs::File; use std::cell::RefCell; - use sector::{Address, SectorSize, Size512}; + use sector::{Address, Size512}; use volume::Volume; - use super::{Ext2, Inode}; + use super::Ext2; #[test] fn file_len() { @@ -587,133 +168,4 @@ mod tests { println!("version: {}.{}", vers.0, vers.1); assert_eq!(128, fs.inode_size()); } - - #[test] - fn inodes() { - let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Ext2::::new(file); - - assert!( - fs.is_ok(), - "Err({:?})", - fs.err().unwrap_or_else(|| unreachable!()), - ); - - let fs = fs.unwrap(); - - let inodes = fs.inodes().filter(|inode| inode.0.in_use()); - for inode in inodes { - 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 inode.0.blocks() { - let (data, _) = block.unwrap(); - assert_eq!(data.len(), fs.block_size()); - println!("{:?}", &data[..size]); - let _ = str::from_utf8(&data[..size]) - .map(|string| println!("{}", string)); - } - } - } - - #[test] - fn read_inode() { - 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!(size.is_ok()); - let size = size.unwrap(); - assert_eq!(size, inode.size()); - unsafe { - buf.set_len(size); - } - } - } - - #[test] - fn read_big() { - 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() == 537600 - }); - 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!(size.is_ok()); - let size = size.unwrap(); - assert_eq!(size, inode.size()); - unsafe { - buf.set_len(size); - } - - for (i, &x) in buf.iter().enumerate() { - if i & 1 == 0 { - assert_eq!(x, b'u', "{}", i); - } else { - assert_eq!(x, b'\n', "{}", i); - } - } - } - } - - #[test] - fn walkdir() { - use std::str; - - fn walk<'vol, S: SectorSize, V: Volume>( - fs: &'vol Ext2, - inode: Inode<'vol, S, V>, - name: String, - ) { - inode.directory().map(|dir| { - for entry in dir { - assert!(entry.is_ok()); - let entry = entry.unwrap(); - let entry_name = str::from_utf8(&entry.name).unwrap_or("?"); - println!("{}/{} => {}", name, entry_name, entry.inode,); - if entry_name != "." && entry_name != ".." { - walk( - fs, - fs.inode_nth(entry.inode).unwrap().0, - format!("{}/{}", name, entry_name), - ); - } - } - }); - } - - let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Ext2::::new(file).unwrap(); - - let (root, _) = fs.root_inode(); - walk(&fs, root, String::new()); - } } diff --git a/src/fs/sync.rs b/src/fs/sync.rs index 7b997f1..e9e0e00 100644 --- a/src/fs/sync.rs +++ b/src/fs/sync.rs @@ -42,45 +42,6 @@ impl> Synced> { Ext2::new(volume).map(|inner| Synced::with_inner(inner)) } - #[allow(dead_code)] - fn update_global(&mut self) -> Result<(), Error> { - self.inner().update_global() - } - - pub fn read_inode( - &self, - buf: &mut [u8], - inode: &Inode, - ) -> Result { - let total_size = inode.size(); - let block_size = self.block_size(); - let mut offset = 0; - - for block in inode.blocks() { - match block { - Ok((data, _)) => { - let data_size = block_size - .min(total_size - offset) - .min(buf.len() - offset); - let end = offset + data_size; - buf[offset..end].copy_from_slice(&data[..data_size]); - offset += data_size; - } - Err(err) => return Err(err.into()), - } - } - - Ok(offset) - } - - pub fn write_inode( - &self, - _inode: &(Inode, Address), - _buf: &[u8], - ) -> Result { - unimplemented!() - } - pub fn root_inode(&self) -> (Inode, Address) { self.inode_nth(2).unwrap() } @@ -106,42 +67,6 @@ impl> Synced> { } } - pub fn version(&self) -> (u32, u16) { - self.inner().version() - } - - pub fn inode_size(&self) -> usize { - self.inner().inode_size() - } - - pub fn inodes_count(&self) -> usize { - self.inner().inodes_count() - } - - pub fn total_inodes_count(&self) -> usize { - self.inner().total_inodes_count() - } - - pub fn block_group_count(&self) -> Result { - self.inner().block_group_count() - } - - pub fn total_block_count(&self) -> usize { - self.inner().total_block_count() - } - - pub fn free_block_count(&self) -> usize { - self.inner().free_block_count() - } - - pub fn block_size(&self) -> usize { - self.inner().block_size() - } - - pub fn log_block_size(&self) -> u32 { - self.inner().log_block_size() - } - pub fn sector_size(&self) -> usize { S::SIZE } @@ -216,6 +141,31 @@ impl> Inode { Inode { fs, inner } } + pub fn read(&self, buf: &mut [u8]) -> Result { + let total_size = self.size(); + let block_size = { + let fs = self.fs.inner(); + fs.block_size() + }; + let mut offset = 0; + + for block in self.blocks() { + match block { + Ok((data, _)) => { + let data_size = block_size + .min(total_size - offset) + .min(buf.len() - offset); + let end = offset + data_size; + buf[offset..end].copy_from_slice(&data[..data_size]); + offset += data_size; + } + Err(err) => return Err(err.into()), + } + } + + Ok(offset) + } + pub fn blocks(&self) -> InodeBlocks { InodeBlocks { inode: self.clone(), @@ -230,7 +180,10 @@ impl> Inode { blocks: self.blocks(), offset: 0, buffer: None, - block_size: self.fs.block_size(), + block_size: { + let fs = self.fs.inner(); + fs.block_size() + }, }) } else { None @@ -278,8 +231,10 @@ impl> Inode { } } - let bs4 = self.fs.block_size() / 4; - let log_block_size = self.fs.log_block_size(); + let fs = self.fs.inner(); + + let bs4 = fs.block_size() / 4; + let log_block_size = fs.log_block_size(); if index < 12 { return Ok(NonZero::new(self.inner.direct_pointer[index])); @@ -289,12 +244,7 @@ impl> Inode { if index < bs4 { let block = self.inner.indirect_pointer; - return block_index( - &self.fs.inner().volume, - block, - index, - log_block_size, - ); + return block_index(&fs.volume, block, index, log_block_size); } index -= bs4; @@ -302,7 +252,7 @@ impl> Inode { if index < bs4 * bs4 { let indirect_index = index >> (log_block_size + 2); let block = match block_index( - &self.fs.inner().volume, + &fs.volume, self.inner.doubly_indirect, indirect_index, log_block_size, @@ -312,7 +262,7 @@ impl> Inode { Err(err) => return Err(err), }; return block_index( - &self.fs.inner().volume, + &fs.volume, block, index & (bs4 - 1), log_block_size, @@ -324,7 +274,7 @@ impl> Inode { if index < bs4 * bs4 * bs4 { let doubly_index = index >> (2 * log_block_size + 4); let indirect = match block_index( - &self.fs.inner().volume, + &fs.volume, self.inner.triply_indirect, doubly_index, log_block_size, @@ -335,7 +285,7 @@ impl> Inode { }; let indirect_index = (index >> (log_block_size + 2)) & (bs4 - 1); let block = match block_index( - &self.fs.inner().volume, + &fs.volume, indirect as u32, indirect_index, log_block_size, @@ -345,7 +295,7 @@ impl> Inode { Err(err) => return Err(err), }; return block_index( - &self.fs.inner().volume, + &fs.volume, block, index & (bs4 - 1), log_block_size, @@ -406,13 +356,13 @@ impl> Iterator for InodeBlocks { }; self.index += 1; + let fs = self.inode.fs.inner(); let block = block.get(); - let log_block_size = self.inode.fs.log_block_size(); + let log_block_size = fs.log_block_size(); let offset = Address::with_block_size(block, 0, log_block_size); let end = Address::with_block_size(block + 1, 0, log_block_size); - let fs = self.inode.fs.inner(); let slice = fs.volume .slice(offset..end) .map(|slice| (slice.to_vec(), offset)) @@ -497,10 +447,11 @@ mod tests { ); let fs = fs.unwrap(); + let inner = fs.inner(); - let vers = fs.version(); + let vers = inner.version(); println!("version: {}.{}", vers.0, vers.1); - assert_eq!(128, fs.inode_size()); + assert_eq!(128, inner.inode_size()); } #[test] @@ -536,7 +487,10 @@ mod tests { let size = inode.0.size(); for block in inode.0.blocks() { let (data, _) = block.unwrap(); - assert_eq!(data.len(), fs.block_size()); + assert_eq!(data.len(), { + let fs = fs.inner(); + fs.block_size() + }); println!("{:?}", &data[..size]); let _ = str::from_utf8(&data[..size]) .map(|string| println!("{}", string)); @@ -557,7 +511,7 @@ mod tests { unsafe { buf.set_len(inode.size()); } - let size = fs.read_inode(&mut buf[..], &inode); + let size = inode.read(&mut buf[..]); assert!(size.is_ok()); let size = size.unwrap(); assert_eq!(size, inode.size()); @@ -581,7 +535,7 @@ mod tests { unsafe { buf.set_len(inode.size()); } - let size = fs.read_inode(&mut buf[..], &inode); + let size = inode.read(&mut buf[..]); assert!(size.is_ok()); let size = size.unwrap(); assert_eq!(size, inode.size());