diff --git a/Cargo.toml b/Cargo.toml index 8f342a4..7a981fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,15 @@ name = "simple-filesystem" version = "0.0.1" authors = ["WangRunji "] +[[bin]] +name = "mksfs" +path = "src/bin/mksfs.rs" +required-features = ["std"] + [dependencies] bit-vec = { default-features = false, git = "https://github.com/AltSysrq/bit-vec.git" } # default-features contains 'std' static_assertions = "0.2.5" [features] -debug_print = [] \ No newline at end of file +debug_print = [] +std = [] \ No newline at end of file diff --git a/src/bin/mksfs.rs b/src/bin/mksfs.rs new file mode 100644 index 0000000..98d2cc6 --- /dev/null +++ b/src/bin/mksfs.rs @@ -0,0 +1,95 @@ +extern crate simple_filesystem; + +use std::env; +use std::fs; +use std::io::{Read, Write, Result}; +use std::path::Path; +use std::mem::uninitialized; +use simple_filesystem::*; + +fn main() -> Result<()> { + let args: Vec<_> = env::args().collect(); + let cmd = &args[1]; + let dir_path = Path::new(&args[2]); + let img_path = Path::new(&args[3]); + match cmd.as_str() { + "zip" => zip(dir_path, img_path), + "unzip" => unzip(dir_path, img_path), + _ => { + println!("USAGE: "); + panic!("Invalid command: {}", cmd); + }, + } +} + +fn zip(path: &Path, img_path: &Path) -> Result<()> { + let img = fs::OpenOptions::new().read(true).write(true).create(true).open(img_path)?; + let sfs = SimpleFileSystem::create(Box::new(img), 0x1000000); + let inode = sfs.root_inode(); + zip_dir(path, inode)?; + sfs.sync().expect("Failed to sync"); + Ok(()) +} + +fn zip_dir(path: &Path, inode: INodePtr) -> Result<()> { + let dir = fs::read_dir(path).expect("Failed to open dir"); + for entry in dir { + let entry = entry?; + let name_ = entry.file_name(); + let name = name_.to_str().unwrap(); + let type_ = entry.file_type()?; + if type_.is_file() { + let inode = inode.borrow_mut().create(name, FileType::File).expect("Failed to create INode"); + let mut file = fs::File::open(entry.path())?; + inode.borrow_mut().resize(file.metadata().unwrap().len() as usize).expect("Failed to resize INode"); + let mut buf: [u8; 4096] = unsafe { uninitialized() }; + let mut offset = 0usize; + let mut len = 4096; + while len == 4096 { + len = file.read(&mut buf)?; + inode.borrow().write_at(offset, &buf).expect("Failed to write image"); + offset += len; + } + } else if type_.is_dir() { + let inode = inode.borrow_mut().create(name, FileType::Dir).expect("Failed to create INode"); + zip_dir(entry.path().as_path(), inode)?; + } + } + Ok(()) +} + +fn unzip(path: &Path, img_path: &Path) -> Result<()> { + let img = fs::File::open(img_path)?; + let sfs = SimpleFileSystem::open(Box::new(img)).expect("Failed to open sfs"); + let inode = sfs.root_inode(); + fs::create_dir(&path)?; + unzip_dir(path, inode) +} + +fn unzip_dir(path: &Path, inode: INodePtr) -> Result<()> { + let files = inode.borrow().list().expect("Failed to list files from INode"); + for name in files.iter().skip(2) { + let inode = inode.borrow().lookup(name.as_str()).expect("Failed to lookup"); + let mut path = path.to_path_buf(); + path.push(name); + let info = inode.borrow().info().expect("Failed to get file info"); + match info.type_ { + FileType::File => { + let mut file = fs::File::create(&path)?; + let mut buf: [u8; 4096] = unsafe { uninitialized() }; + let mut offset = 0usize; + let mut len = 4096; + while len == 4096 { + len = inode.borrow().read_at(offset, buf.as_mut()).expect("Failed to read from INode"); + file.write(&buf[..len])?; + offset += len; + } + } + FileType::Dir => { + fs::create_dir(&path)?; + unzip_dir(path.as_path(), inode)?; + } + } + } + Ok(()) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index eeaab74..1999d19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,3 +31,28 @@ mod tests; pub use sfs::*; pub use vfs::*; pub use blocked_device::BlockedDevice; + +#[cfg(any(test, feature = "std"))] +pub mod std_impl { + use std::fs::{File, OpenOptions}; + use std::io::{Read, Write, Seek, SeekFrom}; + use super::Device; + + impl Device for File { + fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option { + let offset = offset as u64; + match self.seek(SeekFrom::Start(offset)) { + Ok(real_offset) if real_offset == offset => self.read(buf).ok(), + _ => None, + } + } + + fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option { + let offset = offset as u64; + match self.seek(SeekFrom::Start(offset)) { + Ok(real_offset) if real_offset == offset => self.write(buf).ok(), + _ => None, + } + } + } +} \ No newline at end of file diff --git a/src/tests.rs b/src/tests.rs index f8f4a82..00d45d5 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -9,24 +9,6 @@ use std::rc::Rc; use std::mem::uninitialized; use super::structs::{DiskEntry, AsBuf}; -impl Device for File { - fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option { - let offset = offset as u64; - match self.seek(SeekFrom::Start(offset)) { - Ok(real_offset) if real_offset == offset => self.read(buf).ok(), - _ => None, - } - } - - fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option { - let offset = offset as u64; - match self.seek(SeekFrom::Start(offset)) { - Ok(real_offset) if real_offset == offset => self.write(buf).ok(), - _ => None, - } - } -} - fn _open_sample_file() -> Rc { fs::copy("sfs.img","test.img").expect("failed to open sfs.img"); let file = OpenOptions::new()