diff --git a/Cargo.toml b/Cargo.toml index af3ccbedf0..f870c62b78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,16 @@ authors = ["Jeremy Soller "] name = "redoxfs" path = "src/lib.rs" + +[[bin]] +name = "redoxfs-fuse" +path = "fuse/main.rs" + [[bin]] name = "redoxfs-utility" path = "utility/main.rs" [dependencies] redox-system = {path = "../system/"} +fuse = "0.2" +time = "*" diff --git a/fuse/image.rs b/fuse/image.rs new file mode 100644 index 0000000000..ae8bf6f88a --- /dev/null +++ b/fuse/image.rs @@ -0,0 +1,56 @@ +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write, Seek, SeekFrom}; + +use redoxfs::Disk; + +use system::error::{Error, Result, EIO}; + +macro_rules! try_disk { + ($expr:expr) => (match $expr { + Ok(val) => val, + Err(err) => { + println!("Disk I/O Error: {}", err); + return Err(Error::new(EIO)); + } + }) +} + +pub struct Image { + file: File +} + +impl Image { + pub fn open(path: &str) -> Result { + let file = try_disk!(OpenOptions::new().read(true).write(true).open(path)); + Ok(Image { + file: file + }) + } + + pub fn create(path: &str, size: u64) -> Result { + let file = try_disk!(OpenOptions::new().read(true).write(true).create(true).open(path)); + try_disk!(file.set_len(size)); + Ok(Image { + file: file + }) + } +} + +impl Disk for Image { + fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result { + try_disk!(self.file.seek(SeekFrom::Start(block * 512))); + let count = try_disk!(self.file.read(buffer)); + Ok(count) + } + + fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result { + try_disk!(self.file.seek(SeekFrom::Start(block * 512))); + let count = try_disk!(self.file.write(buffer)); + Ok(count) + } + + fn size(&mut self) -> Result { + let size = try_disk!(self.file.seek(SeekFrom::End(0))); + Ok(size) + } +} diff --git a/fuse/main.rs b/fuse/main.rs new file mode 100644 index 0000000000..91cc91ca65 --- /dev/null +++ b/fuse/main.rs @@ -0,0 +1,158 @@ +//#![deny(warnings)] + +extern crate fuse; +extern crate redoxfs; +extern crate system; +extern crate time; + +use image::Image; +use std::env; +use std::path::Path; +use time::Timespec; +use fuse::{FileType, FileAttr, Filesystem, Request, ReplyData, ReplyEntry, ReplyAttr, ReplyDirectory}; +use system::error::ENOENT; + +pub mod image; + +const TTL: Timespec = Timespec { sec: 1, nsec: 0 }; // 1 second + +const CREATE_TIME: Timespec = Timespec { sec: 0, nsec: 0 }; + +const HELLO_TXT_CONTENT: &'static str = "Hello World!\n"; + +struct RedoxFS { + fs: redoxfs::FileSystem, +} + +impl Filesystem for RedoxFS { + fn lookup (&mut self, _req: &Request, ino: u64, name: &Path, reply: ReplyEntry) { + let parent_block = self.fs.header.0 + ino; + println!("lookup: {} {:?}", parent_block, name); + match self.fs.find_node(name.to_str().unwrap(), parent_block) { + Ok(node) => { + println!("lookup: {:?}", node); + reply.entry(&TTL, &FileAttr { + ino: node.0 - self.fs.header.0, + size: node.1.extents[0].length, + blocks: (node.1.extents[0].length + 511)/512, + atime: CREATE_TIME, + mtime: CREATE_TIME, + ctime: CREATE_TIME, + crtime: CREATE_TIME, + kind: if node.1.is_dir() { + FileType::Directory + } else { + FileType::RegularFile + }, + perm: 0o777, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + }, 0) + }, + Err(err) => { + println!("lookup: {}", err); + reply.error(err.errno as i32); + } + } + } + + fn getattr (&mut self, _req: &Request, ino: u64, reply: ReplyAttr) { + let block = self.fs.header.0 + ino; + println!("getattr: {}", block); + match self.fs.node(block) { + Ok(node) => { + println!("getattr: {:?}", node); + reply.attr(&TTL, &FileAttr { + ino: node.0 - self.fs.header.0, + size: node.1.extents[0].length, + blocks: (node.1.extents[0].length + 511)/512, + atime: CREATE_TIME, + mtime: CREATE_TIME, + ctime: CREATE_TIME, + crtime: CREATE_TIME, + kind: if node.1.is_dir() { + FileType::Directory + } else { + FileType::RegularFile + }, + perm: 0o777, + nlink: 1, + uid: 0, + gid: 0, + rdev: 0, + flags: 0, + }); + }, + Err(err) => { + println!("getattr: {}", err); + reply.error(err.errno as i32); + } + } + } + + fn read (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, _size: u32, reply: ReplyData) { + if ino == 2 { + reply.data(&HELLO_TXT_CONTENT.as_bytes()[offset as usize..]); + } else { + reply.error(ENOENT as i32); + } + } + + fn readdir (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, mut reply: ReplyDirectory) { + let parent_block = self.fs.header.0 + ino; + println!("readdir: {}", parent_block); + let mut children = Vec::new(); + match self.fs.child_nodes(&mut children, parent_block) { + Ok(()) => { + if offset == 0 { + let mut i = 0; + reply.add(parent_block - self.fs.header.0, i, FileType::Directory, "."); + i += 1; + reply.add(parent_block - self.fs.header.0, i, FileType::Directory, ".."); + i += 1; + for child in children.iter() { + reply.add(child.0 - self.fs.header.0, i, if child.1.is_dir() { + FileType::Directory + } else { + FileType::RegularFile + }, child.1.name().unwrap()); + i += 1; + } + } + reply.ok(); + }, + Err(err) => { + println!("readdir: {}", err); + reply.error(err.errno as i32); + } + } + } +} + +fn main () { + if let Some(path) = env::args().nth(1) { + //Open an existing image + match Image::open(&path) { + Ok(disk) => match redoxfs::FileSystem::open(Box::new(disk)) { + Ok(filesystem) => { + println!("redoxfs: opened filesystem {}", path); + + if let Some(mountpoint) = env::args_os().nth(2) { + fuse::mount(RedoxFS { + fs: filesystem + }, &mountpoint, &[]); + } else { + println!("redoxfs: no mount point provided"); + } + }, + Err(err) => println!("redoxfs: failed to open filesystem {}: {}", path, err) + }, + Err(err) => println!("redoxfs: failed to open image {}: {}", path, err) + } + } else { + println!("redoxfs: no disk image provided"); + } +} diff --git a/scheme/scheme.rs b/scheme/scheme.rs index a393f056e2..60e2f6605f 100644 --- a/scheme/scheme.rs +++ b/scheme/scheme.rs @@ -22,21 +22,6 @@ impl FileScheme { files: BTreeMap::new() } } - - fn path_nodes(&mut self, path: &str, nodes: &mut Vec<(u64, Node)>) -> Result<(u64, Node)> { - let mut block = self.fs.header.1.root; - nodes.push(try!(self.fs.node(block))); - - for part in path.split('/') { - if ! part.is_empty() { - let node = try!(self.fs.find_node(part, block)); - block = node.0; - nodes.push(node); - } - } - - Ok(nodes.pop().unwrap()) - } } impl Scheme for FileScheme { @@ -111,70 +96,17 @@ impl Scheme for FileScheme { fn mkdir(&mut self, url: &str, _mode: usize) -> Result { let path = url.split(':').nth(1).unwrap_or("").trim_matches('/'); - - let mut nodes = Vec::new(); - match self.path_nodes(path, &mut nodes) { - Ok(_node) => Err(Error::new(EEXIST)), - Err(err) => if err.errno == ENOENT { - let mut last_part = String::new(); - for part in path.split('/') { - if ! part.is_empty() { - last_part = part.to_string(); - } - } - if ! last_part.is_empty() { - if let Some(parent) = nodes.last() { - self.fs.create_node(Node::MODE_DIR, &last_part, parent.0).and(Ok(0)) - } else { - Err(Error::new(EPERM)) - } - } else { - Err(Error::new(EPERM)) - } - } else { - Err(err) - } - } + self.fs.mkdir(path) } fn rmdir(&mut self, url: &str) -> Result { let path = url.split(':').nth(1).unwrap_or("").trim_matches('/'); - - let mut nodes = Vec::new(); - let child = try!(self.path_nodes(path, &mut nodes)); - if let Some(parent) = nodes.last() { - if child.1.is_dir() { - if let Ok(child_name) = child.1.name() { - self.fs.remove_node(Node::MODE_DIR, child_name, parent.0).and(Ok(0)) - } else { - Err(Error::new(ENOENT)) - } - } else { - Err(Error::new(ENOTDIR)) - } - } else { - Err(Error::new(EPERM)) - } + self.fs.rmdir(path) } fn unlink(&mut self, url: &str) -> Result { let path = url.split(':').nth(1).unwrap_or("").trim_matches('/'); - - let mut nodes = Vec::new(); - let child = try!(self.path_nodes(path, &mut nodes)); - if let Some(parent) = nodes.last() { - if ! child.1.is_dir() { - if let Ok(child_name) = child.1.name() { - self.fs.remove_node(Node::MODE_FILE, child_name, parent.0).and(Ok(0)) - } else { - Err(Error::new(ENOENT)) - } - } else { - Err(Error::new(EISDIR)) - } - } else { - Err(Error::new(EPERM)) - } + self.fs.unlink(path) } /* Resource operations */ diff --git a/src/filesystem.rs b/src/filesystem.rs index b61845586f..993dfac6fa 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,8 +1,9 @@ use alloc::boxed::Box; -use collections::vec::Vec; +use collections::{String, Vec}; +use collections::borrow::ToOwned; -use system::error::{Result, Error, EEXIST, EISDIR, ENOENT, ENOSPC, ENOTDIR, ENOTEMPTY}; +use system::error::{Result, Error, EEXIST, EISDIR, ENOENT, ENOSPC, ENOTDIR, ENOTEMPTY, EPERM}; use super::{Disk, ExNode, Extent, Header, Node}; @@ -141,6 +142,21 @@ impl FileSystem { self.find_node(name, parent.1.next) } + fn path_nodes(&mut self, path: &str, nodes: &mut Vec<(u64, Node)>) -> Result<(u64, Node)> { + let mut block = self.header.1.root; + nodes.push(try!(self.node(block))); + + for part in path.split('/') { + if ! part.is_empty() { + let node = try!(self.find_node(part, block)); + block = node.0; + nodes.push(node); + } + } + + Ok(nodes.pop().unwrap()) + } + fn insert_blocks(&mut self, block: u64, length: u64, parent_block: u64) -> Result<()> { if parent_block == 0 { return Err(Error::new(ENOSPC)); @@ -407,4 +423,66 @@ impl FileSystem { Ok(size) } } + + pub fn mkdir(&mut self, path: &str) -> Result { + let mut nodes = Vec::new(); + match self.path_nodes(path, &mut nodes) { + Ok(_node) => Err(Error::new(EEXIST)), + Err(err) => if err.errno == ENOENT { + let mut last_part = String::new(); + for part in path.split('/') { + if ! part.is_empty() { + last_part = part.to_owned(); + } + } + if ! last_part.is_empty() { + if let Some(parent) = nodes.last() { + self.create_node(Node::MODE_DIR, &last_part, parent.0).and(Ok(0)) + } else { + Err(Error::new(EPERM)) + } + } else { + Err(Error::new(EPERM)) + } + } else { + Err(err) + } + } + } + + pub fn rmdir(&mut self, path: &str) -> Result { + let mut nodes = Vec::new(); + let child = try!(self.path_nodes(path, &mut nodes)); + if let Some(parent) = nodes.last() { + if child.1.is_dir() { + if let Ok(child_name) = child.1.name() { + self.remove_node(Node::MODE_DIR, child_name, parent.0).and(Ok(0)) + } else { + Err(Error::new(ENOENT)) + } + } else { + Err(Error::new(ENOTDIR)) + } + } else { + Err(Error::new(EPERM)) + } + } + + pub fn unlink(&mut self, path: &str) -> Result { + let mut nodes = Vec::new(); + let child = try!(self.path_nodes(path, &mut nodes)); + if let Some(parent) = nodes.last() { + if ! child.1.is_dir() { + if let Ok(child_name) = child.1.name() { + self.remove_node(Node::MODE_FILE, child_name, parent.0).and(Ok(0)) + } else { + Err(Error::new(ENOENT)) + } + } else { + Err(Error::new(EISDIR)) + } + } else { + Err(Error::new(EPERM)) + } + } }