diff --git a/ext2-rs/Cargo.toml b/ext2-rs/Cargo.toml deleted file mode 100644 index f72ff11..0000000 --- a/ext2-rs/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "ext2" -version = "0.1.1" -authors = ["Szymon Walter ", -"able "] - - -[dependencies] -bitflags = "1.0" -rlibc = { version = "1.0", optional = true } -spin = "0.9.2" -genfs = "^0.1.4" - -[features] -default = ["no_std"] -no_std = ["rlibc"] diff --git a/ext2-rs/LICENSE.md b/ext2-rs/LICENSE.md deleted file mode 100644 index 9894777..0000000 --- a/ext2-rs/LICENSE.md +++ /dev/null @@ -1,22 +0,0 @@ -# ext2-rs -## an ext2 implementation - -Copyright © 2018, Szymon Walter - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from -the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -#### walter.szymon.98@gmail.com diff --git a/ext2-rs/README.md b/ext2-rs/README.md deleted file mode 100644 index 8dd53d6..0000000 --- a/ext2-rs/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# ext2-rs - -An OS and architecture independent implementation of ext2 in pure Rust. diff --git a/ext2-rs/rustfmt.toml b/ext2-rs/rustfmt.toml deleted file mode 100644 index 3450fc4..0000000 --- a/ext2-rs/rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -max_width = 80 -wrap_comments = true diff --git a/ext2-rs/src/error.rs b/ext2-rs/src/error.rs deleted file mode 100644 index f506ae9..0000000 --- a/ext2-rs/src/error.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! Errors -use { - alloc::string::String, - core::fmt::{self, Display}, -}; - -#[cfg(any(test, not(feature = "no_std")))] -use std::io; - -/// The set of all possible errors -#[derive(Debug)] -pub enum Error { - /// Generic error - Other(String), - /// Bad magic number - BadMagic { - /// The magic number - magic: u16, - }, - /// Out of bounds error - OutOfBounds { - /// index - index: usize, - }, - /// Address out of bounds - AddressOutOfBounds { - /// - sector: u32, - /// - offset: u32, - - /// - size: usize, - }, - /// Bad block group count - BadBlockGroupCount { - /// - by_blocks: u32, - /// - by_inodes: u32, - }, - /// Inode Not Found - InodeNotFound { - /// inode number - inode: u32, - }, - /// Inode is not a directory - NotADirectory { - /// inode number - inode: u32, - /// inode name - name: String, - }, - /// Not Absolute Path - NotAbsolute { - /// path name - name: String, - }, - - /// Not Found - NotFound { - /// inode name - name: String, - }, - // #[cfg(any(test, not(feature = "no_std")))] - // Io { inner: io::Error }, -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Other(ref msg) => write!(f, "{}", msg), - Error::BadMagic { - magic, - } => write!(f, "invalid magic value: {}", magic), - Error::OutOfBounds { - index, - } => write!(f, "index ouf of bounds: {}", index), - Error::AddressOutOfBounds { - sector, - offset, - size, - } => write!(f, "address ouf of bounds: {}:{} with a block size of: {}", - sector, offset, size), - Error::BadBlockGroupCount { - by_blocks, - by_inodes, - } => write!(f, "conflicting block group count data; by blocks: {}, by inodes: {}", by_blocks, by_inodes), - Error::InodeNotFound { - inode, - } => write!(f, "couldn't find inode no. {}", &inode), - Error::NotADirectory { - inode, - ref name, - } => write!(f, "inode no. {} at: {} is not a directory", inode, &name), - Error::NotAbsolute { - ref name, - } => write!(f, "{} is not an absolute path", &name), - Error::NotFound { - ref name, - } => write!(f, "couldn't find {}", &name), - #[cfg(any(test, not(feature = "no_std")))] - Error::Io { - ref inner, - } => write!(f, "io error: {}", inner), - } - } -} - -impl From for Error { - fn from(_: Infallible) -> Error { - unreachable!() - } -} - -#[cfg(any(test, not(feature = "no_std")))] -impl From for Error { - fn from(inner: io::Error) -> Error { - Error::Io { inner } - } -} - -/// Infalliable -pub enum Infallible {} diff --git a/ext2-rs/src/fs/mod.rs b/ext2-rs/src/fs/mod.rs deleted file mode 100644 index 1844e31..0000000 --- a/ext2-rs/src/fs/mod.rs +++ /dev/null @@ -1,177 +0,0 @@ -//! - -use { - alloc::vec::Vec, - core::mem, - error::Error, - sector::{Address, SectorSize}, - sys::{ - block_group::BlockGroupDescriptor, inode::Inode as RawInode, - superblock::Superblock, - }, - volume::Volume, -}; - -pub mod sync; - -#[allow(dead_code)] -pub(crate) struct Struct { - pub inner: T, - pub offset: Address, -} - -impl From<(T, Address)> for Struct { - #[inline] - fn from((inner, offset): (T, Address)) -> Struct { - Struct { inner, offset } - } -} - -/// Safe wrapper for raw sys structs -pub struct Ext2> { - // TODO: should this have some different vis? - pub(crate) volume: V, - pub(crate) superblock: Struct, - pub(crate) block_groups: Struct, S>, -} - -impl> Ext2 { - /// - pub fn new(volume: V) -> Result, Error> { - let superblock = unsafe { Struct::from(Superblock::find(&volume)?) }; - let block_groups_offset = Address::with_block_size( - superblock.inner.first_data_block + 1, - 0, - superblock.inner.log_block_size + 10, - ); - let block_groups_count = superblock - .inner - .block_group_count() - .map(|count| count as usize) - .map_err(|(a, b)| Error::BadBlockGroupCount { - by_blocks: a, - by_inodes: b, - })?; - let block_groups = unsafe { - BlockGroupDescriptor::find_descriptor_table( - &volume, - block_groups_offset, - block_groups_count, - )? - }; - let block_groups = Struct::from(block_groups); - Ok(Ext2 { - volume, - superblock, - block_groups, - }) - } - /// Return the version of the filesystem - pub fn version(&self) -> (u32, u16) { - ( - self.superblock.inner.rev_major, - self.superblock.inner.rev_minor, - ) - } - /// Return inode size - pub fn inode_size(&self) -> usize { - if self.version().0 == 0 { - mem::size_of::() - } else { - // note: inodes bigger than 128 are not supported - self.superblock.inner.inode_size as usize - } - } - /// - pub fn inodes_count(&self) -> usize { - self.superblock.inner.inodes_per_group as _ - } - /// - pub fn total_inodes_count(&self) -> usize { - self.superblock.inner.inodes_count as _ - } - /// - pub fn block_group_count(&self) -> Result { - self.superblock - .inner - .block_group_count() - .map(|count| count as usize) - .map_err(|(a, b)| Error::BadBlockGroupCount { - by_blocks: a, - by_inodes: b, - }) - } - /// - pub fn total_block_count(&self) -> usize { - self.superblock.inner.blocks_count as _ - } - /// - pub fn free_block_count(&self) -> usize { - self.superblock.inner.free_blocks_count as _ - } - /// - pub fn block_size(&self) -> usize { - self.superblock.inner.block_size() - } - /// - pub fn log_block_size(&self) -> u32 { - self.superblock.inner.log_block_size + 10 - } - /// - pub fn sector_size(&self) -> usize { - S::SIZE - } - /// - pub fn log_sector_size(&self) -> u32 { - S::LOG_SIZE - } -} - -#[cfg(test)] -mod tests { - use std::cell::RefCell; - use std::fs::File; - - use sector::{Address, Size512}; - use volume::Volume; - - use super::Ext2; - - #[test] - fn file_len() { - let file = RefCell::new(File::open("ext2.img").unwrap()); - assert_eq!( - Address::::from(2048_u64) - - Address::::from(1024_u64), - Address::::new(2, 0) - ); - assert_eq!( - unsafe { - file.slice_unchecked( - Address::::from(1024_u64) - ..Address::::from(2048_u64), - ) - .len() - }, - 1024 - ); - } - - #[test] - fn file() { - 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 vers = fs.version(); - println!("version: {}.{}", vers.0, vers.1); - assert_eq!(128, fs.inode_size()); - } -} diff --git a/ext2-rs/src/fs/sync.rs b/ext2-rs/src/fs/sync.rs deleted file mode 100644 index bda8360..0000000 --- a/ext2-rs/src/fs/sync.rs +++ /dev/null @@ -1,905 +0,0 @@ -//! - -use crate::sys::inode::TypePerm; - -use { - super::Ext2, - alloc::{ - sync::Arc, - {string::String, vec::Vec}, - }, - core::{ - fmt::{self, Debug}, - iter::Iterator, - num::NonZeroU32, - }, - error::Error, - genfs::*, - sector::{Address, SectorSize}, - spin::{Mutex, MutexGuard}, - sys::inode::Inode as RawInode, - volume::Volume, -}; -/// DOCME: what is this? -pub struct Synced { - inner: Arc>, -} - -impl Synced { - /// DOCME: what is this? - pub fn with_inner(inner: T) -> Synced { - Synced { - inner: Arc::new(Mutex::new(inner)), - } - } - - /// DOCME: what is this? - 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> { - /// DOCME: what is this? - pub fn new(volume: V) -> Result>, Error> { - Ext2::new(volume).map(Synced::with_inner) - } - /// Get the root inode. - pub fn root_inode(&self) -> Inode { - self.inode_nth(2).unwrap() - } - /// Get the inode at the given index. - pub fn inode_nth(&self, index: usize) -> Option> { - self.inodes_nth(index).next() - } - /// DOCME: what is this? - pub fn inodes(&self) -> Inodes { - self.inodes_nth(1) - } - /// DOCME: what is this? - 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, - } - } - /// DOCME: what is this? - pub fn sector_size(&self) -> usize { - S::SIZE - } - /// DOCME: what is this? - pub fn log_sector_size(&self) -> u32 { - S::LOG_SIZE - } -} - -impl> Fs for Synced> { - type Path = [u8]; - type PathOwned = Vec; - type File = Inode; - type Dir = Directory; - type DirEntry = DirectoryEntry; - type Metadata = (); // TODO - type Permissions = (); // TODO - type Error = Error; - - fn open( - &self, - abs_path: &Self::Path, - _options: &OpenOptions, - ) -> Result { - fn inner<'a, S, V, I>( - fs: &Synced>, - inode: Inode, - mut path: I, - abs_path: &[u8], - ) -> Result, Error> - where - S: SectorSize, - V: Volume, - I: Iterator, - { - let name = match path.next() { - Some(name) => name, - None => return Ok(inode), - }; - - let mut dir = - inode.directory().ok_or_else(|| Error::NotADirectory { - inode: inode.num, - name: String::from_utf8_lossy(abs_path).into_owned(), - })?; - - let entry = dir - .find(|entry| { - entry.is_err() || entry.as_ref().unwrap().name == name - }) - .ok_or_else(|| Error::NotFound { - name: String::from_utf8_lossy(abs_path).into_owned(), - })??; - - let inode = fs - .inode_nth(entry.inode) - .ok_or(Error::InodeNotFound { inode: inode.num })?; - - inner(fs, inode, path, abs_path) - } - - if abs_path.is_empty() || abs_path[0] != b'/' { - return Err(Error::NotAbsolute { - name: String::from_utf8_lossy(abs_path).into_owned(), - }); - } - - if abs_path == b"/" { - return Ok(self.root_inode()); - } - - let mut path = abs_path.split(|byte| *byte == b'/'); - path.next(); - let root = self.root_inode(); - - inner(self, root, path, abs_path) - } - - fn remove_file(&mut self, _path: &Self::Path) -> Result<(), Self::Error> { - unimplemented!() - } - - fn metadata( - &self, - _path: &Self::Path, - ) -> Result { - unimplemented!() - } - - fn symlink_metadata( - &self, - _path: &Self::Path, - ) -> Result { - unimplemented!() - } - - fn rename( - &mut self, - _from: &Self::Path, - _to: &Self::Path, - ) -> Result<(), Self::Error> { - unimplemented!() - } - - fn copy( - &mut self, - _from: &Self::Path, - _to: &Self::Path, - ) -> Result { - unimplemented!() - } - - fn hard_link( - &mut self, - _src: &Self::Path, - _dst: &Self::Path, - ) -> Result<(), Self::Error> { - unimplemented!() - } - - fn symlink( - &mut self, - _src: &Self::Path, - _dst: &Self::Path, - ) -> Result<(), Self::Error> { - unimplemented!() - } - - fn read_link( - &self, - _path: &Self::Path, - ) -> Result { - unimplemented!() - } - - fn canonicalize( - &self, - _path: &Self::Path, - ) -> Result { - unimplemented!() - } - - fn create_dir( - &mut self, - _path: &Self::Path, - _options: &DirOptions, - ) -> Result<(), Self::Error> { - unimplemented!() - } - - fn remove_dir(&mut self, _path: &Self::Path) -> Result<(), Self::Error> { - unimplemented!() - } - - fn remove_dir_all( - &mut self, - _path: &Self::Path, - ) -> Result<(), Self::Error> { - unimplemented!() - } - - fn read_dir(&self, path: &Self::Path) -> Result { - let inode = self.open(path, OpenOptions::new().read(true))?; - inode.directory().ok_or(Error::NotADirectory { - inode: inode.num, - name: String::from_utf8_lossy(path).into_owned(), - }) - } - - fn set_permissions( - &mut self, - _path: &Self::Path, - _perm: Self::Permissions, - ) -> Result<(), Self::Error> { - unimplemented!() - } -} - -impl> Debug for Synced> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Synced>", S::SIZE) - } -} - -#[derive(Debug, Clone)] -/// A collection of inodes. -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; - - 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, - (self.index - 1) as u32, - ) - }) - } else { - None - } - } -} - -#[derive(Debug)] -/// A single inode in an ext2 filesystem. -pub struct Inode> { - fs: Synced>, - inner: RawInode, - addr: Address, - num: u32, -} - -impl> Clone for Inode { - fn clone(&self) -> Self { - Inode { - fs: self.fs.clone(), - inner: self.inner, - addr: self.addr, - num: self.num, - } - } -} - -impl> Inode { - /// - pub fn new( - fs: Synced>, - inner: RawInode, - addr: Address, - num: u32, - ) -> Inode { - Inode { - fs, - inner, - addr, - num, - } - } - /// Read to the end of a buffer. - pub fn read_to_end(&self, buf: &mut Vec) -> Result { - let total_size = self.size(); - let capacity = buf.capacity(); - if capacity < total_size { - buf.reserve_exact(total_size - capacity); - } - unsafe { - buf.set_len(total_size); - } - let size = self.read(&mut buf[..]); - size.map(|size| { - unsafe { - buf.set_len(size); - } - size - }) - .map_err(|err| { - unsafe { - buf.set_len(0); - } - err - }) - } - /// Return blocks on a sector - pub fn blocks(&self) -> InodeBlocks { - InodeBlocks { - inode: self.clone(), - index: 0, - } - } - /// return a directory iterator - pub fn directory(&self) -> Option> { - if self.is_dir() { - Some(Directory { - blocks: self.blocks(), - offset: 0, - buffer: None, - block_size: { - let fs = self.fs.inner(); - fs.block_size() - }, - }) - } else { - None - } - } - /// Determine if an inode is a directory - pub fn is_dir(&self) -> bool { - { self.inner.type_perm }.contains(TypePerm::DIRECTORY) - // self.inner.type_perm.contains(TypePerm::DIRECTORY) - } - /// - pub fn block(&self, index: usize) -> Option { - self.try_block(index).ok().and_then(|block| block) - } - /// Try to get a 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(NonZeroU32::new(block.dynamic_cast::().0)) - }, - Err(err) => Err(err.into()), - } - } - - let fs = self.fs.inner(); - - let bs4 = fs.block_size() / 4; - let log_block_size = fs.log_block_size(); - - if index < 12 { - return Ok(NonZeroU32::new(self.inner.direct_pointer[index])); - } - - index -= 12; - - if index < bs4 { - let block = self.inner.indirect_pointer; - return block_index(&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( - &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( - &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( - &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( - &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( - &fs.volume, - block, - index & (bs4 - 1), - log_block_size, - ); - } - - Ok(None) - } - /// - pub fn in_use(&self) -> bool { - self.inner.hard_links > 0 - } - /// return the uid - 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 - } - - /// ableOS: expose type_perm - pub fn type_perm(&self) -> TypePerm { - self.inner.type_perm - } -} - -impl> File for Inode { - type Error = Error; - - 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), - } - } - - Ok(offset) - } - - fn write(&mut self, _buf: &[u8]) -> Result { - unimplemented!() - } - - fn flush(&mut self) -> Result<(), Self::Error> { - unimplemented!() - } - - fn seek(&mut self, _pos: SeekFrom) -> Result { - unimplemented!() - } -} - -/// -#[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 fs = self.inode.fs.inner(); - - let block = block.get(); - 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 slice = fs - .volume - .slice(offset..end) - .map(|slice| (slice.to_vec(), offset)) - .map_err(|err| err.into()); - Some(slice) - } -} - -#[derive(Debug, Clone)] -/// A directory structure -pub struct Directory> { - blocks: InodeBlocks, - offset: usize, - buffer: Option>, - block_size: usize, -} - -impl> Dir - for Directory -{ -} - -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, - inode: inode as usize, - ty, - })) - } -} - -#[derive(Clone)] -/// A directory entry -pub struct DirectoryEntry { - /// The name of the entry - pub name: Vec, - /// The inode of the entry - pub inode: usize, - /// - pub ty: u8, -} - -impl DirEntry for DirectoryEntry { - type Path = [u8]; - type PathOwned = Vec; - type Metadata = (); // TODO - type FileType = u8; // TODO: enum FileType - type Error = Error; - - fn path(&self) -> Self::PathOwned { - unimplemented!() - } - - fn metadata(&self) -> Result { - unimplemented!() - } - - fn file_type(&self) -> Result { - Ok(self.ty) - } - - fn file_name(&self) -> &Self::Path { - &self.name - } -} - -impl DirectoryEntry { - /// Turns a filename into a string for display - pub fn file_name_string(&self) -> String { - let mut filename = String::new(); - for ch in &self.name { - filename.push(*ch as char); - } - filename - } -} - -#[cfg(test)] -mod tests { - use std::cell::RefCell; - use std::fs::File; - - use genfs::{File as GenFile, Fs, OpenOptions}; - - 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 inner = fs.inner(); - - let vers = inner.version(); - println!("version: {}.{}", vers.0, vers.1); - assert_eq!(128, inner.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.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.in_use() && inode.uid() == 1000 && inode.size() < 1024 - }); - for inode in inodes { - println!("{:?}", inode); - let size = inode.size(); - for block in inode.blocks() { - let (data, _) = block.unwrap(); - assert_eq!(data.len(), { - let fs = fs.inner(); - 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.in_use() && inode.uid() == 1000 && inode.size() < 1024 - }); - for inode in inodes { - let mut buf = Vec::with_capacity(inode.size()); - unsafe { - buf.set_len(inode.size()); - } - let size = inode.read(&mut buf[..]); - 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.in_use() && inode.uid() == 1000 && inode.size() == 537600 - }); - for inode in inodes { - let mut buf = Vec::with_capacity(inode.size()); - unsafe { - buf.set_len(inode.size()); - } - let size = inode.read(&mut buf[..]); - 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(), - 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()); - } - - #[test] - fn find() { - use std::str; - let file = RefCell::new(File::open("ext2.img").unwrap()); - let fs = Synced::>::new(file).unwrap(); - - let found = fs.open(b"/home/funky/README.md", &OpenOptions::new()); - - assert!(found.is_ok()); - let inode = found.unwrap(); - let mut vec = Vec::new(); - assert!(inode.read_to_end(&mut vec).is_ok()); - println!("{}", str::from_utf8(&vec).unwrap()); - } -} diff --git a/ext2-rs/src/lib.rs b/ext2-rs/src/lib.rs deleted file mode 100644 index 506163a..0000000 --- a/ext2-rs/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Ext2 crate for ableOS - -#![deny(missing_docs)] -#![feature(min_specialization, step_trait, associated_type_defaults)] -#![cfg_attr(all(not(test), feature = "no_std"), no_std)] - -extern crate alloc; - -#[macro_use] -extern crate bitflags; -extern crate genfs; -extern crate spin; - -#[cfg(any(test, not(feature = "no_std")))] -extern crate core; - -pub mod error; -pub mod fs; -pub mod sector; -pub mod sys; -pub mod volume; - -#[cfg(test)] -mod tests { - use sys::block_group::*; - use sys::inode::*; - use sys::superblock::*; - - #[test] - fn sizes() { - use std::mem::size_of; - assert_eq!(size_of::(), 1024); - assert_eq!(size_of::(), 32); - assert_eq!(size_of::(), 128); - } -} diff --git a/ext2-rs/src/sector.rs b/ext2-rs/src/sector.rs deleted file mode 100644 index f657e3a..0000000 --- a/ext2-rs/src/sector.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Sector data. - -use core::{ - fmt::{self, Debug, Display, LowerHex}, - iter::Step, - marker::PhantomData, - ops::{Add, Sub}, -}; -/// Size of a sector in bytes -pub trait SectorSize: Clone + Copy + PartialEq + PartialOrd + 'static { - /// DOCME: What is this? - const LOG_SIZE: u32; - /// DOCME: What is this? - const SIZE: usize = 1 << Self::LOG_SIZE; - /// DOCME: What is this? - const OFFSET_MASK: u32 = (Self::SIZE - 1) as u32; -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -/// DOCME: What is this? -pub struct Size512; -impl SectorSize for Size512 { - const LOG_SIZE: u32 = 9; -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -/// DOCME: What is this? -pub struct Size1024; -impl SectorSize for Size1024 { - const LOG_SIZE: u32 = 10; -} -/// DOCME: What is this? -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -pub struct Size2048; -impl SectorSize for Size2048 { - const LOG_SIZE: u32 = 11; -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -/// DOCME: What is this? -pub struct Size4096; -impl SectorSize for Size4096 { - const LOG_SIZE: u32 = 12; -} - -/// Address in a physical sector -#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -pub struct Address { - sector: u32, - offset: u32, - _phantom: PhantomData, -} - -impl Address { - /// - pub unsafe fn new_unchecked(sector: u32, offset: u32) -> Address { - assert!((offset as usize) < S::SIZE, "offset out of sector bounds"); - let _phantom = PhantomData; - Address { - sector, - offset, - _phantom, - } - } - /// - pub fn new(sector: u32, offset: i32) -> Address { - let sector = (sector as i32 + (offset >> S::LOG_SIZE)) as u32; - let offset = offset.unsigned_abs() & S::OFFSET_MASK; - unsafe { Address::new_unchecked(sector, offset) } - } - /// - pub fn with_block_size( - block: u32, - offset: i32, - log_block_size: u32, - ) -> Address { - let block = (block as i32 + (offset >> log_block_size)) as u32; - let offset = offset.unsigned_abs() & ((1 << log_block_size) - 1); - - let log_diff = log_block_size as i32 - S::LOG_SIZE as i32; - let top_offset = offset >> S::LOG_SIZE; - let offset = offset & ((1 << S::LOG_SIZE) - 1); - let sector = block << log_diff | top_offset; - unsafe { Address::new_unchecked(sector, offset) } - } - /// - pub fn into_index(&self) -> u64 { - ((self.sector as u64) << S::LOG_SIZE) + self.offset as u64 - } - /// Get the size of the sector - pub const fn sector_size(&self) -> usize { - S::SIZE - } - /// DOCME: What is this? - pub const fn log_sector_size(&self) -> u32 { - S::LOG_SIZE - } - /// Return the sector number - pub fn sector(&self) -> u32 { - self.sector - } - /// Return the offset in the sector - pub fn offset(&self) -> u32 { - self.offset - } -} - -impl Step for Address { - fn steps_between(start: &Self, end: &Self) -> Option { - if end.sector >= start.sector { - Some(end.sector as usize - start.sector as usize) - } else { - None - } - } - - /* - fn replace_one(&mut self) -> Self { - mem::replace(self, Address::new(1, 0)) - } - - fn replace_zero(&mut self) -> Self { - mem::replace(self, Address::new(0, 0)) - } - - fn add_one(&self) -> Self { - Address::new(self.sector + 1, 0) - } - - fn sub_one(&self) -> Self { - Address::new(self.sector - 1, 0) - } - - fn add_usize(&self, n: usize) -> Option { - self.sector - .checked_add(n as u32) - .map(|sector| Address::new(sector, 0)) - } - */ - - fn forward_checked(_start: Self, count: usize) -> Option { - todo!("forward_checked: count: {}", count); - } - - fn backward_checked(_start: Self, count: usize) -> Option { - todo!("backward_checked count: {}", count); - } -} - -impl Display for Address { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}:{}", self.sector, self.offset) - } -} - -impl LowerHex for Address { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:x}:{:x}", self.sector, self.offset) - } -} - -impl From for Address { - fn from(idx: u64) -> Address { - let sector = idx >> S::LOG_SIZE; - let offset = idx & S::OFFSET_MASK as u64; - Address::new(sector as u32, offset as i32) - } -} - -impl From for Address { - fn from(idx: usize) -> Address { - let sector = idx >> S::LOG_SIZE; - let offset = idx & S::OFFSET_MASK as usize; - Address::new(sector as u32, offset as i32) - } -} - -impl Add for Address { - type Output = Address; - fn add(self, rhs: Address) -> Address { - Address::new( - self.sector + rhs.sector, - (self.offset + rhs.offset) as i32, - ) - } -} - -impl Sub for Address { - type Output = Address; - fn sub(self, rhs: Address) -> Address { - Address::new( - self.sector - rhs.sector, - self.offset as i32 - rhs.offset as i32, - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn conv() { - assert_eq!(Address::::new(0, 1024).into_index(), 1024); - assert_eq!(Address::::from(1024_u64).into_index(), 1024); - assert_eq!( - Address::::with_block_size(1, 256, 10).into_index(), - 1024 + 256 - ); - assert_eq!( - Address::::with_block_size(2, 0, 10).into_index(), - 2048 - ); - assert_eq!( - Address::::with_block_size(0, 1792, 10).into_index(), - 1792 - ); - } - - #[test] - fn arithmetic() { - assert_eq!( - Address::::new(0, 512), - Address::::new(1, 0), - ); - - assert_eq!( - Address::::new(2, -256), - Address::::new(1, 256), - ); - - let a = Address::::new(0, 1024); - let b = Address::::new(0, 1024); - assert_eq!(a + b, Address::::new(1, 0)); - assert_eq!((a + b).into_index(), 2048); - - let a = Address::::new(0, 2048); - let b = Address::::new(0, 256); - assert_eq!(a - b, Address::::new(3, 256)); - assert_eq!((a - b).into_index(), 1792); - } -} diff --git a/ext2-rs/src/sys/block_group.rs b/ext2-rs/src/sys/block_group.rs deleted file mode 100644 index 90bf165..0000000 --- a/ext2-rs/src/sys/block_group.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! - -use { - alloc::vec::Vec, - core::{fmt::Debug, mem}, - error::Error, - sector::{Address, SectorSize}, - volume::Volume, -}; - -/// The Block Group Descriptor Table contains a descriptor for each block group -/// within the file system. The number of block groups within the file system, -/// and correspondingly, the number of entries in the Block Group Descriptor -/// Table, is described above. Each descriptor contains information regarding -/// where important data structures for that group are located. -/// -/// The (`BlockGroupDescriptor`) table is located in the block immediately -/// following the Superblock. So if the block size (determined from a field in -/// the superblock) is 1024 bytes per block, the Block Group Descriptor Table -/// will begin at block 2. For any other block size, it will begin at block 1. -/// Remember that blocks are numbered starting at 0, and that block numbers -/// don't usually correspond to physical block addresses. -#[repr(C, packed)] -#[derive(Clone, Debug, Copy)] -pub struct BlockGroupDescriptor { - /// Block address of block usage bitmap - pub block_usage_addr: u32, - /// Block address of inode usage bitmap - pub inode_usage_addr: u32, - /// Starting block address of inode table - pub inode_table_block: u32, - /// Number of unallocated blocks in group - pub free_blocks_count: u16, - /// Number of unallocated inodes in group - pub free_inodes_count: u16, - /// Number of directories in group - pub dirs_count: u16, - #[doc(hidden)] - _reserved: [u8; 14], -} - -impl BlockGroupDescriptor { - /// Find a descriptor in a descriptor table - pub unsafe fn find_descriptor>( - haystack: &V, - offset: Address, - ) -> Result<(BlockGroupDescriptor, Address), Error> { - let end = - offset + Address::from(mem::size_of::()); - if haystack.size() < end { - return Err(Error::AddressOutOfBounds { - sector: end.sector(), - offset: end.offset(), - size: end.sector_size(), - }); - } - - let descr = haystack - .slice_unchecked(offset..end) - .dynamic_cast::(); - - Ok(descr) - } - /// find a descriptor table - pub unsafe fn find_descriptor_table>( - haystack: &V, - offset: Address, - count: usize, - ) -> Result<(Vec, Address), Error> { - let end = offset - + Address::from(count * mem::size_of::()); - if haystack.size() < end { - return Err(Error::AddressOutOfBounds { - sector: end.sector(), - offset: end.offset(), - size: end.sector_size(), - }); - } - - let mut vec = Vec::with_capacity(count); - for i in 0..count { - let offset = offset - + Address::from(i * mem::size_of::()); - vec.push({ - BlockGroupDescriptor::find_descriptor(haystack, offset)?.0 - }); - } - - Ok((vec, offset)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use sector::{Address, Size512}; - - #[test] - fn find() { - let volume = vec![0_u8; 4096]; - let table = unsafe { - BlockGroupDescriptor::find_descriptor_table( - &volume, - Address::::new(4, 0), - 8, - ) - }; - assert!( - table.is_ok(), - "Err({:?})", - table.err().unwrap_or_else(|| unreachable!()), - ); - let table = table.unwrap_or_else(|_| unreachable!()); - assert_eq!(table.0.len(), 8); - } -} diff --git a/ext2-rs/src/sys/inode.rs b/ext2-rs/src/sys/inode.rs deleted file mode 100644 index 0a129b6..0000000 --- a/ext2-rs/src/sys/inode.rs +++ /dev/null @@ -1,188 +0,0 @@ -//! -use { - core::{fmt::Debug, mem}, - error::Error, - sector::{Address, SectorSize}, - volume::Volume, -}; - -/// An inode is a structure on the disk that represents a file, directory, -/// symbolic link, etc. Inodes do not contain the data of the file / directory / -/// etc. that they represent. Instead, they link to the blocks that actually -/// contain the data. This lets the inodes themselves have a well-defined size -/// which lets them be placed in easily indexed arrays. Each block group has an -/// array of inodes it is responsible for, and conversely every inode within a -/// file system belongs to one of such tables (and one of such block groups). -#[repr(C, packed)] -#[derive(Clone, Debug, Copy)] -pub struct Inode { - /// Type and Permissions (see below) - pub type_perm: TypePerm, - /// User ID - pub uid: u16, - /// Lower 32 bits of size in bytes - pub size_low: u32, - /// Last Access Time (in POSIX time) - pub atime: u32, - /// Creation Time (in POSIX time) - pub ctime: u32, - /// Last Modification time (in POSIX time) - pub mtime: u32, - /// Deletion time (in POSIX time) - pub dtime: u32, - /// Group ID - pub gid: u16, - /// Count of hard links (directory entries) to this inode. When this - /// reaches 0, the data blocks are marked as unallocated. - pub hard_links: u16, - /// Count of disk sectors (not Ext2 blocks) in use by this inode, not - /// counting the actual inode structure nor directory entries linking - /// to the inode. - pub sectors_count: u32, - /// Flags - pub flags: Flags, - /// Operating System Specific value #1 - pub _os_specific_1: [u8; 4], - /// Direct block pointers - pub direct_pointer: [u32; 12], - /// Singly Indirect Block Pointer (Points to a block that is a list of - /// block pointers to data) - pub indirect_pointer: u32, - /// Doubly Indirect Block Pointer (Points to a block that is a list of - /// block pointers to Singly Indirect Blocks) - pub doubly_indirect: u32, - /// Triply Indirect Block Pointer (Points to a block that is a list of - /// block pointers to Doubly Indirect Blocks) - pub triply_indirect: u32, - /// Generation number (Primarily used for NFS) - pub gen_number: u32, - /// In Ext2 version 0, this field is reserved. In version >= 1, - /// Extended attribute block (File ACL). - pub ext_attribute_block: u32, - /// In Ext2 version 0, this field is reserved. In version >= 1, Upper - /// 32 bits of file size (if feature bit set) if it's a file, - /// Directory ACL if it's a directory - pub size_high: u32, - /// Block address of fragment - pub frag_block_addr: u32, - /// Operating System Specific Value #2 - pub _os_specific_2: [u8; 12], -} - -impl Inode { - /// Discover the inode location on the disk. - pub unsafe fn find_inode>( - haystack: &V, - offset: Address, - size: usize, - ) -> Result<(Inode, Address), Error> { - if size != mem::size_of::() { - unimplemented!("inodes with a size != 128"); - } - - let end = offset + Address::from(size); - if haystack.size() < end { - return Err(Error::AddressOutOfBounds { - sector: end.sector(), - offset: end.offset(), - size: end.sector_size(), - }); - } - - let inode = haystack - .slice_unchecked(offset..end) - .dynamic_cast::(); - - Ok(inode) - } -} - -bitflags! { - /// - // #[derive(Copy)] - pub struct TypePerm: u16 { - /// FIFO - const FIFO = 0x1000; - /// Character device - const CHAR_DEVICE = 0x2000; - /// Directory - const DIRECTORY = 0x4000; - /// Block device - const BLOCK_DEVICE = 0x6000; - /// Regular file - const FILE = 0x8000; - /// Symbolic link - const SYMLINK = 0xA000; - /// Unix socket - const SOCKET = 0xC000; - /// Other—execute permission - const O_EXEC = 0x001; - /// Other—write permission - const O_WRITE = 0x002; - /// Other—read permission - const O_READ = 0x004; - /// Group—execute permission - const G_EXEC = 0x008; - /// Group—write permission - const G_WRITE = 0x010; - /// Group—read permission - const G_READ = 0x020; - /// User—execute permission - const U_EXEC = 0x040; - /// User—write permission - const U_WRITE = 0x080; - /// User—read permission - const U_READ = 0x100; - /// Sticky Bit - const STICKY = 0x200; - /// Set group ID - const SET_GID = 0x400; - /// Set user ID - const SET_UID = 0x800; - } -} - -bitflags! { - /// Flags - pub struct Flags: u32 { - /// Secure deletion (not used) - const SECURE_DEL = 0x00000001; - /// Keep a copy of data when deleted (not used) - const KEEP_COPY = 0x00000002; - /// File compression (not used) - const COMPRESSION = 0x00000004; - /// Synchronous updates—new data is written immediately to disk - const SYNC_UPDATE = 0x00000008; - /// Immutable file (content cannot be changed) - const IMMUTABLE = 0x00000010; - /// Append only - const APPEND_ONLY = 0x00000020; - /// File is not included in 'dump' command - const NODUMP = 0x00000040; - /// Last accessed time should not updated - const DONT_ATIME = 0x00000080; - /// Hash indexed directory - const HASH_DIR = 0x00010000; - /// AFS directory - const AFS_DIR = 0x00020000; - /// Journal file data - const JOURNAL_DATA = 0x00040000; - } -} - -/// Unknown entry type -pub const UNKNOWN: u8 = 0; -/// FIFO entry type -pub const FIFO: u8 = 1; -/// Character device entry type -pub const CHAR_DEVICE: u8 = 2; -/// Directory entry type -pub const DIRECTORY: u8 = 3; -/// Block device entry type -pub const BLOCK_DEVICE: u8 = 4; -/// Regular file entry type -pub const FILE: u8 = 5; -/// Symbolic link entry type -pub const SYMLINK: u8 = 6; -/// Unix socket entry type -pub const SOCKET: u8 = 7; diff --git a/ext2-rs/src/sys/mod.rs b/ext2-rs/src/sys/mod.rs deleted file mode 100644 index 32ec8fb..0000000 --- a/ext2-rs/src/sys/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! - -pub mod block_group; -pub mod inode; -pub mod superblock; diff --git a/ext2-rs/src/sys/superblock.rs b/ext2-rs/src/sys/superblock.rs deleted file mode 100644 index d97952e..0000000 --- a/ext2-rs/src/sys/superblock.rs +++ /dev/null @@ -1,284 +0,0 @@ -//! Superblock information - -use { - core::{fmt::Debug, mem}, - error::Error, - sector::{Address, SectorSize}, - volume::Volume, -}; - -/// Ext2 signature (0xef53), used to help confirm the presence of Ext2 on a -/// volume -pub const EXT2_MAGIC: u16 = 0xef53; - -/// Filesystem is free of errors -pub const FS_CLEAN: u16 = 1; -/// Filesystem has errors -pub const FS_ERR: u16 = 2; - -/// Ignore errors -pub const ERR_IGNORE: u16 = 1; -/// Remount as read-only on error -pub const ERR_RONLY: u16 = 2; -/// Panic on error -pub const ERR_PANIC: u16 = 3; - -/// Creator OS is Linux -pub const OS_LINUX: u32 = 0; -/// Creator OS is Hurd -pub const OS_HURD: u32 = 1; -/// Creator OS is Masix -pub const OS_MASIX: u32 = 2; -/// Creator OS is FreeBSD -pub const OS_FREEBSD: u32 = 3; -/// Creator OS is a BSD4.4-Lite derivative -pub const OS_LITE: u32 = 4; - -/// The Superblock contains all information about the layout of the file system -/// and possibly contains other important information like what optional -/// features were used to create the file system. -/// -/// The Superblock is always located at byte 1024 from the beginning of the -/// volume and is exactly 1024 bytes in length. For example, if the disk uses -/// 512 byte sectors, the Superblock will begin at LBA 2 and will occupy all of -/// sector 2 and 3. -#[repr(C, packed)] -#[derive(Clone, Debug, Copy)] -pub struct Superblock { - // taken from https://wiki.osdev.org/Ext2 - /// Total number of inodes in file system - pub inodes_count: u32, - /// Total number of blocks in file system - pub blocks_count: u32, - /// Number of blocks reserved for superuser (see offset 80) - pub r_blocks_count: u32, - /// Total number of unallocated blocks - pub free_blocks_count: u32, - /// Total number of unallocated inodes - pub free_inodes_count: u32, - /// Block number of the block containing the superblock - pub first_data_block: u32, - /// log2 (block size) - 10. (In other words, the number to shift 1,024 - /// to the left by to obtain the block size) - pub log_block_size: u32, - /// log2 (fragment size) - 10. (In other words, the number to shift - /// 1,024 to the left by to obtain the fragment size) - pub log_frag_size: i32, - /// Number of blocks in each block group - pub blocks_per_group: u32, - /// Number of fragments in each block group - pub frags_per_group: u32, - /// Number of inodes in each block group - pub inodes_per_group: u32, - /// Last mount time (in POSIX time) - pub mtime: u32, - /// Last written time (in POSIX time) - pub wtime: u32, - /// Number of times the volume has been mounted since its last - /// consistency check (fsck) - pub mnt_count: u16, - /// Number of mounts allowed before a consistency check (fsck) must be - /// done - pub max_mnt_count: i16, - /// Ext2 signature (0xef53), used to help confirm the presence of Ext2 - /// on a volume - pub magic: u16, - /// File system state (see `FS_CLEAN` and `FS_ERR`) - pub state: u16, - /// What to do when an error is detected (see `ERR_IGNORE`, `ERR_RONLY` and - /// `ERR_PANIC`) - pub errors: u16, - /// Minor portion of version (combine with Major portion below to - /// construct full version field) - pub rev_minor: u16, - /// POSIX time of last consistency check (fsck) - pub lastcheck: u32, - /// Interval (in POSIX time) between forced consistency checks (fsck) - pub checkinterval: u32, - /// Operating system ID from which the filesystem on this volume was - /// created - pub creator_os: u32, - /// Major portion of version (combine with Minor portion above to - /// construct full version field) - pub rev_major: u32, - /// User ID that can use reserved blocks - pub block_uid: u16, - /// Group ID that can use reserved blocks - pub block_gid: u16, - - /// First non-reserved inode in file system. - pub first_inode: u32, - /// SectorSize of each inode structure in bytes. - pub inode_size: u16, - /// Block group that this superblock is part of (if backup copy) - pub block_group: u16, - /// Optional features present (features that are not required to read - /// or write, but usually result in a performance increase) - pub features_opt: FeaturesOptional, - /// Required features present (features that are required to be - /// supported to read or write) - pub features_req: FeaturesRequired, - /// Features that if not supported, the volume must be mounted - /// read-only) - pub features_ronly: FeaturesROnly, - /// File system ID (what is output by blkid) - pub fs_id: [u8; 16], - /// Volume name (C-style string: characters terminated by a 0 byte) - pub volume_name: [u8; 16], - /// Path volume was last mounted to (C-style string: characters - /// terminated by a 0 byte) - pub last_mnt_path: [u8; 64], - /// Compression algorithms used (see Required features above) - pub compression: u32, - /// Number of blocks to preallocate for files - pub prealloc_blocks_files: u8, - /// Number of blocks to preallocate for directories - pub prealloc_blocks_dirs: u8, - #[doc(hidden)] - _unused: [u8; 2], - /// Journal ID (same style as the File system ID above) - pub journal_id: [u8; 16], - /// Journal inode - pub journal_inode: u32, - /// Journal device - pub journal_dev: u32, - /// Head of orphan inode list - pub journal_orphan_head: u32, - #[doc(hidden)] - _reserved: [u8; 788], -} -impl Superblock { - /// Discover the location of the superblock in the given block device. - pub unsafe fn find>( - haystack: &V, - ) -> Result<(Superblock, Address), Error> { - let offset = Address::from(1024_usize); - let end = offset + Address::from(mem::size_of::()); - if haystack.size() < end { - return Err(Error::AddressOutOfBounds { - sector: end.sector(), - offset: end.offset(), - size: end.sector_size(), - }); - } - - let superblock = { - haystack - .slice_unchecked(offset..end) - .dynamic_cast::() - }; - - if superblock.0.magic != EXT2_MAGIC { - Err(Error::BadMagic { - magic: superblock.0.magic, - }) - } else { - Ok(superblock) - } - } - - #[inline] - /// Return the block size - pub fn block_size(&self) -> usize { - 1024 << self.log_block_size - } - - #[inline] - /// Return the fragment size - pub fn frag_size(&self) -> usize { - 1024 << self.log_frag_size - } - /// Return the number of blocks per group - pub fn block_group_count(&self) -> Result { - let blocks_mod = self.blocks_count % self.blocks_per_group; - let inodes_mod = self.inodes_count % self.inodes_per_group; - let blocks_inc = if blocks_mod == 0 { 0 } else { 1 }; - let inodes_inc = if inodes_mod == 0 { 0 } else { 1 }; - let by_blocks = self.blocks_count / self.blocks_per_group + blocks_inc; - let by_inodes = self.inodes_count / self.inodes_per_group + inodes_inc; - if by_blocks == by_inodes { - Ok(by_blocks) - } else { - Err((by_blocks, by_inodes)) - } - } -} - -bitflags! { - /// Optional features - pub struct FeaturesOptional: u32 { - /// Preallocate some number of (contiguous?) blocks (see - /// `Superblock::prealloc_blocks_dirs`) to a directory when creating a new one - const PREALLOCATE = 0x0001; - /// AFS server inodes exist - const AFS = 0x0002; - /// File system has a journal (Ext3) - const JOURNAL = 0x0004; - /// Inodes have extended attributes - const EXTENDED_INODE = 0x0008; - /// File system can resize itself for larger partitions - const SELF_RESIZE = 0x0010; - /// Directories use hash index - const HASH_INDEX = 0x0020; - } -} - -bitflags! { - /// Required features. If these are not supported; can't mount - pub struct FeaturesRequired: u32 { - /// Compression is used - const REQ_COMPRESSION = 0x0001; - /// Directory entries contain a type field - const REQ_DIRECTORY_TYPE = 0x0002; - /// File system needs to replay its journal - const REQ_REPLAY_JOURNAL = 0x0004; - /// File system uses a journal device - const REQ_JOURNAL_DEVICE = 0x0008; - } -} - -bitflags! { - /// ROnly features. If these are not supported; remount as read-only - pub struct FeaturesROnly: u32 { - /// Sparse superblocks and group descriptor tables - const RONLY_SPARSE = 0x0001; - /// File system uses a 64-bit file size - const RONLY_FILE_SIZE_64 = 0x0002; - /// Directory contents are stored in the form of a Binary Tree - const RONLY_BTREE_DIRECTORY = 0x0004; - } -} - -#[cfg(test)] -mod tests { - use super::*; - use sector::Size512; - - #[test] - fn find() { - let mut volume = vec![0_u8; 4096]; - // magic - volume[1024 + 56] = EXT2_MAGIC as u8; - volume[1024 + 57] = (EXT2_MAGIC >> 8) as u8; - let superblock = unsafe { Superblock::find::(&volume) }; - assert!( - superblock.is_ok(), - "Err({:?})", - superblock.err().unwrap_or_else(|| unreachable!()), - ); - } - - #[test] - fn superblock() { - use std::cell::RefCell; - use std::fs::File; - - let file = RefCell::new(File::open("ext2.img").unwrap()); - let superblock = unsafe { Superblock::find::(&file) }; - assert!( - superblock.is_ok(), - "Err({:?})", - superblock.err().unwrap_or_else(|| unreachable!()), - ); - } -} diff --git a/ext2-rs/src/volume/mod.rs b/ext2-rs/src/volume/mod.rs deleted file mode 100644 index d3d23b6..0000000 --- a/ext2-rs/src/volume/mod.rs +++ /dev/null @@ -1,371 +0,0 @@ -#![allow(missing_docs)] -use { - alloc::{ - borrow::{Cow, ToOwned}, - boxed::Box, - vec::Vec, - }, - core::{ - mem, - ops::{Deref, DerefMut, Range}, - slice, - }, - error::Error, - sector::{Address, SectorSize}, -}; - -pub mod size; -use self::size::Size; - -pub trait Volume { - type Error: Into; - - fn size(&self) -> Size; - fn commit( - &mut self, - slice: Option>, - ) -> Result<(), Self::Error>; - unsafe fn slice_unchecked<'a>( - &'a self, - range: Range>, - ) -> VolumeSlice<'a, T, S>; - - fn slice<'a>( - &'a self, - range: Range>, - ) -> Result, Self::Error>; -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct VolumeSlice<'a, T: 'a + Clone, S: SectorSize> { - inner: Cow<'a, [T]>, - index: Address, -} - -impl VolumeSlice<'static, T, S> { - pub fn with_static(inner: &'static [T]) -> VolumeSlice<'static, T, S> { - VolumeSlice { - inner: Cow::Borrowed(inner), - index: Address::new(0, 0), - } - } - - pub fn new_owned( - inner: <[T] as ToOwned>::Owned, - index: Address, - ) -> VolumeSlice<'static, T, S> { - VolumeSlice { - inner: Cow::Owned(inner), - index, - } - } -} - -impl<'a, T: Clone, S: SectorSize> VolumeSlice<'a, T, S> { - pub fn new(inner: &'a [T], index: Address) -> VolumeSlice<'a, T, S> { - VolumeSlice { - inner: Cow::Borrowed(inner), - index, - } - } - - pub fn is_mutated(&self) -> bool { - match self.inner { - Cow::Borrowed(_) => false, - Cow::Owned(_) => true, - } - } - - pub fn address(&self) -> Address { - self.index - } -} - -impl<'a, S: SectorSize> VolumeSlice<'a, u8, S> { - pub unsafe fn dynamic_cast(&self) -> (T, Address) { - assert!(self.inner.len() >= mem::size_of::()); - let index = self.index; - let cast = self.inner.as_ptr().cast::().read_unaligned(); - - // mem::transmute_copy(self.inner.as_ptr().as_ref().unwrap()); - (cast, index) - } - - pub fn from_cast( - cast: &'a T, - index: Address, - ) -> VolumeSlice<'a, u8, S> { - let len = mem::size_of::(); - let ptr = cast as *const T as *const u8; - let slice = unsafe { slice::from_raw_parts(ptr, len) }; - VolumeSlice::new(slice, index) - } -} - -impl<'a, T: Clone, S: SectorSize> VolumeSlice<'a, T, S> { - pub fn commit(self) -> Option> { - if self.is_mutated() { - Some(VolumeCommit::new(self.inner.into_owned(), self.index)) - } else { - None - } - } -} - -impl<'a, T: Clone, S: SectorSize> AsRef<[T]> for VolumeSlice<'a, T, S> { - fn as_ref(&self) -> &[T] { - self.inner.as_ref() - } -} - -impl<'a, T: Clone, S: SectorSize> AsMut<[T]> for VolumeSlice<'a, T, S> { - fn as_mut(&mut self) -> &mut [T] { - self.inner.to_mut().as_mut() - } -} - -impl<'a, T: Clone, S: SectorSize> Deref for VolumeSlice<'a, T, S> { - type Target = [T]; - - fn deref(&self) -> &Self::Target { - self.as_ref() - } -} - -impl<'a, T: Clone, S: SectorSize> DerefMut for VolumeSlice<'a, T, S> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.as_mut() - } -} - -pub struct VolumeCommit { - inner: Vec, - index: Address, -} - -impl VolumeCommit { - pub fn with_vec(inner: Vec) -> VolumeCommit { - VolumeCommit { - inner, - index: Address::new(0, 0), - } - } -} - -impl VolumeCommit { - pub fn new(inner: Vec, index: Address) -> VolumeCommit { - VolumeCommit { inner, index } - } - - pub fn into_inner(self) -> Vec { - self.inner - } - - pub fn address(&self) -> Address { - self.index - } -} - -impl AsRef<[T]> for VolumeCommit { - fn as_ref(&self) -> &[T] { - self.inner.as_ref() - } -} - -impl AsMut<[T]> for VolumeCommit { - fn as_mut(&mut self) -> &mut [T] { - self.inner.as_mut() - } -} - -impl Deref for VolumeCommit { - type Target = [T]; - - fn deref(&self) -> &Self::Target { - self.as_ref() - } -} - -impl DerefMut for VolumeCommit { - fn deref_mut(&mut self) -> &mut Self::Target { - self.as_mut() - } -} - -macro_rules! impl_slice { - (@inner $volume:ty $( , $lt:lifetime )* ) => { - impl<$( $lt, )* T: Clone, S: SectorSize> Volume - for $volume - { - type Error = Error; - - fn size(&self) -> Size { - Size::Bounded( - Address::from(>::as_ref(self).len()) - ) - } - - fn commit( - &mut self, - slice: Option>, - ) -> Result<(), Self::Error> { - slice.map(|slice| { - let index = slice.address().into_index() as usize; - let end = index + slice.as_ref().len(); - // XXX: it would be much better to drop the contents of dst - // and move the contents of slice instead of cloning - let dst = - &mut >::as_mut(self)[index..end]; - dst.clone_from_slice(slice.as_ref()); - }); - Ok(()) - } - - unsafe fn slice_unchecked<'a>( - &'a self, - range: Range>, - ) -> VolumeSlice<'a, T, S> { - let index = range.start; - let range = range.start.into_index() as usize - ..range.end.into_index() as usize; - VolumeSlice::new( - >::as_ref(self).get_unchecked(range), - index, - ) - } - - fn slice<'a>( - &'a self, - range: Range>, - ) -> Result, Self::Error> { - if self.size() >= range.end { - unsafe { Ok(self.slice_unchecked(range)) } - } else { - Err(Error::AddressOutOfBounds { - sector: range.end.sector(), - offset: range.end.offset(), - size: range.end.sector_size() - }) - } - } - } - }; - ($volume:ty) => { - impl_slice!(@inner $volume); - }; - ($volume:ty $( , $lt:lifetime )* ) => { - impl_slice!(@inner $volume $( , $lt )* ); - }; -} - -impl_slice!(&'b mut [T], 'b); -impl_slice!(Vec); -impl_slice!(Box<[T]>); - -#[cfg(any(test, not(feature = "no_std")))] -mod file { - use std::cell::RefCell; - use std::fs::File; - use std::io::{self, Read, Seek, SeekFrom, Write}; - use std::ops::Range; - - use sector::{Address, SectorSize}; - - use super::size::Size; - use super::{Volume, VolumeCommit, VolumeSlice}; - - impl Volume for RefCell { - type Error = io::Error; - - fn size(&self) -> Size { - Size::Bounded( - self.borrow() - .metadata() - .map(|data| Address::from(data.len())) - .unwrap_or(Address::new(0, 0)), - ) - } - - fn commit( - &mut self, - slice: Option>, - ) -> Result<(), Self::Error> { - slice - .map(|slice| { - let index = slice.address(); - let mut refmut = self.borrow_mut(); - refmut - .seek(SeekFrom::Start(index.into_index())) - .and_then(|_| refmut.write(slice.as_ref())) - .map(|_| ()) - }) - .unwrap_or(Ok(())) - } - - unsafe fn slice_unchecked<'a>( - &'a self, - range: Range>, - ) -> VolumeSlice<'a, u8, S> { - let index = range.start; - let len = range.end - range.start; - let mut vec = Vec::with_capacity(len.into_index() as usize); - vec.set_len(len.into_index() as usize); - let mut refmut = self.borrow_mut(); - refmut - .seek(SeekFrom::Start(index.into_index())) - .and_then(|_| refmut.read_exact(&mut vec[..])) - .unwrap_or_else(|err| { - panic!("could't read from File Volume: {:?}", err) - }); - VolumeSlice::new_owned(vec, index) - } - - fn slice<'a>( - &'a self, - range: Range>, - ) -> Result, Self::Error> { - let index = range.start; - let mut vec = Vec::with_capacity( - (range.end - range.start).into_index() as usize, - ); - unsafe { - vec.set_len((range.end - range.start).into_index() as usize); - } - let mut refmut = self.borrow_mut(); - refmut - .seek(SeekFrom::Start(index.into_index())) - .and_then(|_| refmut.read_exact(&mut vec[..])) - .map(move |_| VolumeSlice::new_owned(vec, index)) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use sector::{Address, Size512}; - - #[test] - fn volume() { - let mut volume = vec![0; 1024]; - let commit = { - let mut slice = volume - .slice( - Address::::from(256_u64) - ..Address::::from(512_u64), - ) - .unwrap(); - slice.iter_mut().for_each(|x| *x = 1); - slice.commit() - }; - assert!(volume.commit(commit).is_ok()); - - for (i, &x) in volume.iter().enumerate() { - if i < 256 || i >= 512 { - assert_eq!(x, 0); - } else { - assert_eq!(x, 1); - } - } - } -} diff --git a/ext2-rs/src/volume/size.rs b/ext2-rs/src/volume/size.rs deleted file mode 100644 index b8b5b26..0000000 --- a/ext2-rs/src/volume/size.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! - -use { - core::{ - cmp::Ordering, - fmt::{self, Display}, - }, - sector::{Address, SectorSize}, -}; - -#[derive(Clone, Copy, Debug, Hash)] -/// A size -pub enum Size { - /// An unbounded size - Unbounded, - /// A bounded size - Bounded(Address), -} - -impl Size { - /// Try to get the length of the sector - pub fn try_len(&self) -> Option> { - match *self { - Size::Unbounded => None, - Size::Bounded(n) => Some(n), - } - } - /// Get the length of the sector unsafely - /// - /// # Safety - /// - /// This function is unsafe because it does not check that the size is - /// bounded. - pub unsafe fn len(&self) -> Address { - match *self { - Size::Unbounded => panic!( - "attempt to convert `Size::Unbounded` to a concrete length" - ), - Size::Bounded(n) => n, - } - } -} - -impl Size { - /// Check if the size is unbounded - pub fn is_bounded(&self) -> bool { - match *self { - Size::Unbounded => false, - Size::Bounded(_) => true, - } - } -} - -impl Display for Size { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Size::Unbounded => write!(f, "Unbounded"), - Size::Bounded(n) => write!(f, "Bounded({})", n), - } - } -} - -impl PartialEq for Size { - fn eq(&self, rhs: &Self) -> bool { - match (self, rhs) { - (&Size::Unbounded, _) => false, - (_, &Size::Unbounded) => false, - (&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.eq(b), - } - } - - fn ne(&self, rhs: &Self) -> bool { - match (self, rhs) { - (&Size::Unbounded, _) => false, - (_, &Size::Unbounded) => false, - (&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.ne(b), - } - } -} - -impl PartialEq> for Size { - fn eq(&self, rhs: &Address) -> bool { - match *self { - Size::Unbounded => false, - Size::Bounded(ref n) => n.eq(rhs), - } - } - - fn ne(&self, rhs: &Address) -> bool { - match *self { - Size::Unbounded => false, - Size::Bounded(ref n) => n.eq(rhs), - } - } -} - -impl PartialOrd for Size { - fn partial_cmp(&self, rhs: &Self) -> Option { - match (self, rhs) { - (&Size::Unbounded, &Size::Unbounded) => None, - (&Size::Unbounded, _) => Some(Ordering::Greater), - (_, &Size::Unbounded) => Some(Ordering::Less), - (&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.partial_cmp(b), - } - } -} - -impl PartialOrd> for Size { - fn partial_cmp(&self, rhs: &Address) -> Option { - match *self { - Size::Unbounded => Some(Ordering::Greater), - Size::Bounded(ref n) => n.partial_cmp(rhs), - } - } -}