FUSE support for RedoxFS (WIP)
This commit is contained in:
@@ -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 = "*"
|
||||
|
||||
@@ -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<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)
|
||||
}
|
||||
}
|
||||
+158
@@ -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");
|
||||
}
|
||||
}
|
||||
+3
-71
@@ -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 */
|
||||
|
||||
+80
-2
@@ -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<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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user