diff --git a/src/error.rs b/src/error.rs index 94e5bea..2d9091d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ use core::fmt::{self, Display}; -use alloc::String; +use alloc::{String, Vec}; #[cfg(any(test, not(feature = "no_std")))] use std::io; @@ -23,6 +23,19 @@ pub enum Error { by_blocks: u32, by_inodes: u32, }, + InodeNotFound { + inode: u32, + }, + NotADirectory { + inode: u32, + name: String, + }, + NotAbsolute { + name: String, + }, + NotFound { + name: String, + }, #[cfg(any(test, not(feature = "no_std")))] Io { inner: io::Error, @@ -49,6 +62,19 @@ impl Display for Error { 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, diff --git a/src/fs/sync.rs b/src/fs/sync.rs index e9e0e00..e783c08 100644 --- a/src/fs/sync.rs +++ b/src/fs/sync.rs @@ -1,7 +1,8 @@ use core::fmt::{self, Debug}; use core::nonzero::NonZero; +use core::iter::Iterator; -use alloc::Vec; +use alloc::{String, Vec}; use alloc::arc::Arc; use spin::{Mutex, MutexGuard}; @@ -42,6 +43,77 @@ impl> Synced> { Ext2::new(volume).map(|inner| Synced::with_inner(inner)) } + pub fn find_inode( + &self, + abs_path: &[u8], + ) -> Result<(Inode, Address), Error> { + pub fn inner<'a, S, V, I>( + fs: &Synced>, + inode: (u32, Inode, Address), + mut path: I, + abs_path: &[u8], + ) -> Result<(Inode, Address), Error> + where + S: SectorSize, + V: Volume, + I: Iterator, + { + let name = match path.next() { + Some(name) => name, + None => return Ok((inode.1, inode.2)), + }; + + let mut dir = match inode.1.directory() { + Some(dir) => dir, + None => { + return Err(Error::NotADirectory { + inode: inode.0, + name: String::from_utf8_lossy(abs_path).into_owned(), + }) + } + }; + + let num = match dir.find(|entry| { + entry.is_err() || entry.as_ref().unwrap().name == name + }) { + Some(Ok(entry)) => entry.inode, + Some(Err(err)) => return Err(err), + None => { + return Err(Error::NotFound { + name: String::from_utf8_lossy(abs_path).into_owned(), + }) + } + }; + + let inode = match fs.inode_nth(num) { + Some((inode, addr)) => (num as u32, inode, addr), + None => { + return Err(Error::InodeNotFound { + inode: inode.0 as u32, + }) + } + }; + + inner(fs, inode, path, abs_path) + } + + if abs_path.len() == 0 || 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, addr) = self.root_inode(); + + inner(self, (2, root, addr), path, abs_path) + } + pub fn root_inode(&self) -> (Inode, Address) { self.inode_nth(2).unwrap() } @@ -166,6 +238,29 @@ impl> Inode { Ok(offset) } + 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.and_then(|size| { + unsafe { + buf.set_len(size); + } + Ok(size) + }).or_else(|err| { + unsafe { + buf.set_len(0); + } + Err(err) + }) + } + pub fn blocks(&self) -> InodeBlocks { InodeBlocks { inode: self.clone(), @@ -174,8 +269,7 @@ impl> Inode { } pub fn directory(&self) -> Option> { - use sys::inode::TypePerm; - if unsafe { self.inner.type_perm.contains(TypePerm::DIRECTORY) } { + if self.is_dir() { Some(Directory { blocks: self.blocks(), offset: 0, @@ -190,6 +284,11 @@ impl> Inode { } } + pub fn is_dir(&self) -> bool { + use sys::inode::TypePerm; + unsafe { self.inner.type_perm.contains(TypePerm::DIRECTORY) } + } + pub fn block(&self, index: usize) -> Option> { self.try_block(index).ok().and_then(|block| block) } @@ -585,4 +684,19 @@ mod tests { 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.find_inode(b"/home/funky/README.md"); + + 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()); + } }