#![deny(warnings)] extern crate fuse; extern crate redoxfs; extern crate syscall; 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, ReplyCreate, ReplyDirectory, ReplyEmpty, ReplyStatfs, ReplyWrite}; pub mod image; const TTL: Timespec = Timespec { sec: 1, nsec: 0 }; // 1 second const CREATE_TIME: Timespec = Timespec { sec: 0, nsec: 0 }; struct RedoxFS { fs: redoxfs::FileSystem, } fn node_attr(node: &(u64, redoxfs::Node)) -> FileAttr { FileAttr { ino: node.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: node.1.mode & redoxfs::Node::MODE_PERM, nlink: 1, uid: node.1.uid, gid: node.1.gid, rdev: 0, flags: 0, } } impl Filesystem for RedoxFS { fn lookup(&mut self, _req: &Request, parent_block: u64, name: &Path, reply: ReplyEntry) { match self.fs.find_node(name.to_str().unwrap(), parent_block) { Ok(node) => { reply.entry(&TTL, &node_attr(&node), 0); }, Err(err) => { reply.error(err.errno as i32); } } } fn getattr(&mut self, _req: &Request, block: u64, reply: ReplyAttr) { match self.fs.node(block) { Ok(node) => { reply.attr(&TTL, &node_attr(&node)); }, Err(err) => { reply.error(err.errno as i32); } } } fn setattr(&mut self, _req: &Request, block: u64, mode: Option<u32>, uid: Option<u32>, gid: Option<u32>, size: Option<u64>, _atime: Option<Timespec>, _mtime: Option<Timespec>, _fh: Option<u64>, _crtime: Option<Timespec>, _chgtime: Option<Timespec>, _bkuptime: Option<Timespec>, _flags: Option<u32>, reply: ReplyAttr) { if let Some(mode) = mode { match self.fs.node(block) { Ok(mut node) => if node.1.mode & redoxfs::Node::MODE_PERM != mode as u16 & redoxfs::Node::MODE_PERM { node.1.mode = (node.1.mode & ! redoxfs::Node::MODE_PERM) | (mode as u16 & redoxfs::Node::MODE_PERM); if let Err(err) = self.fs.write_at(node.0, &node.1) { reply.error(err.errno as i32); return; } }, Err(err) => { reply.error(err.errno as i32); return; } } } if let Some(uid) = uid { match self.fs.node(block) { Ok(mut node) => if node.1.uid != uid { node.1.uid = uid; if let Err(err) = self.fs.write_at(node.0, &node.1) { reply.error(err.errno as i32); return; } }, Err(err) => { reply.error(err.errno as i32); return; } } } if let Some(gid) = gid { match self.fs.node(block) { Ok(mut node) => if node.1.gid != gid { node.1.gid = gid; if let Err(err) = self.fs.write_at(node.0, &node.1) { reply.error(err.errno as i32); return; } }, Err(err) => { reply.error(err.errno as i32); return; } } } if let Some(truncate_size) = size { if let Err(err) = self.fs.node_set_len(block, truncate_size) { reply.error(err.errno as i32); return; } } match self.fs.node(block) { Ok(node) => { reply.attr(&TTL, &node_attr(&node)); }, Err(err) => { reply.error(err.errno as i32); } } } fn read(&mut self, _req: &Request, block: u64, _fh: u64, offset: u64, size: u32, reply: ReplyData) { let mut data = vec![0; size as usize]; match self.fs.read_node(block, offset, &mut data) { Ok(count) => { reply.data(&data[..count]); }, Err(err) => { reply.error(err.errno as i32); } } } fn write(&mut self, _req: &Request, block: u64, _fh: u64, offset: u64, data: &[u8], _flags: u32, reply: ReplyWrite) { match self.fs.write_node(block, offset, &data) { Ok(count) => { reply.written(count as u32); }, Err(err) => { reply.error(err.errno as i32); } } } fn flush(&mut self, _req: &Request, _ino: u64, _fh: u64, _lock_owner: u64, reply: ReplyEmpty) { reply.ok(); } fn fsync(&mut self, _req: &Request, _ino: u64, _fh: u64, _datasync: bool, reply: ReplyEmpty) { reply.ok(); } fn readdir(&mut self, _req: &Request, parent_block: u64, _fh: u64, offset: u64, mut reply: ReplyDirectory) { 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) => { reply.error(err.errno as i32); } } } fn create(&mut self, _req: &Request, parent_block: u64, name: &Path, mode: u32, flags: u32, reply: ReplyCreate) { match self.fs.create_node(redoxfs::Node::MODE_FILE | (mode as u16 & redoxfs::Node::MODE_PERM), name.to_str().unwrap(), parent_block) { Ok(node) => { println!("Create {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode); reply.created(&TTL, &node_attr(&node), 0, 0, flags); }, Err(error) => { reply.error(error.errno as i32); } } } fn mkdir(&mut self, _req: &Request, parent_block: u64, name: &Path, mode: u32, reply: ReplyEntry) { match self.fs.create_node(redoxfs::Node::MODE_DIR | (mode as u16 & redoxfs::Node::MODE_PERM), name.to_str().unwrap(), parent_block) { Ok(node) => { println!("Mkdir {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode); reply.entry(&TTL, &node_attr(&node), 0); }, Err(error) => { reply.error(error.errno as i32); } } } fn rmdir(&mut self, _req: &Request, parent_block: u64, name: &Path, reply: ReplyEmpty) { match self.fs.remove_node(redoxfs::Node::MODE_DIR, name.to_str().unwrap(), parent_block) { Ok(()) => { reply.ok(); }, Err(err) => { reply.error(err.errno as i32); } } } fn unlink(&mut self, _req: &Request, parent_block: u64, name: &Path, reply: ReplyEmpty) { match self.fs.remove_node(redoxfs::Node::MODE_FILE, name.to_str().unwrap(), parent_block) { Ok(()) => { reply.ok(); }, Err(err) => { reply.error(err.errno as i32); } } } fn statfs(&mut self, _req: &Request, _ino: u64, reply: ReplyStatfs) { let free = self.fs.header.1.free; match self.fs.node_len(free) { Ok(free_size) => { let bsize = 512; let blocks = self.fs.header.1.size/bsize; let bfree = free_size/bsize; reply.statfs(blocks, bfree, bfree, 0, 0, bsize as u32, 256, 0); }, Err(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"); } }