Commit a1cae735 authored by Jeremy Soller's avatar Jeremy Soller

FUSE support for RedoxFS (WIP)

parent b1805779
......@@ -11,9 +11,16 @@ authors = ["Jeremy Soller <jackpot51@gmail.com>"]
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 = "*"
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<Image> {
let file = try_disk!(OpenOptions::new().read(true).write(true).open(path));
Ok(Image {
file: file
})
}
pub fn create(path: &str, size: u64) -> Result<Image> {
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<usize> {
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<usize> {
try_disk!(self.file.seek(SeekFrom::Start(block * 512)));
let count = try_disk!(self.file.write(buffer));
Ok(count)
}
fn size(&mut self) -> Result<u64> {
let size = try_disk!(self.file.seek(SeekFrom::End(0)));
Ok(size)
}
}
//#![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");
}
}
......@@ -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<usize> {
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<usize> {
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<usize> {
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 */
......
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<usize> {
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<usize> {
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<usize> {
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))
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment