Newer
Older
use self::fuser::MountOption;
use self::fuser::TimeOrNow;
use crate::mount::fuse::TimeOrNow::Now;
use crate::mount::fuse::TimeOrNow::SpecificTime;
use crate::{filesystem, Disk, Node, TreeData, TreePtr, BLOCK_SIZE};

Jeremy Soller
committed
FileAttr, FileType, Filesystem, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty,
ReplyEntry, ReplyStatfs, ReplyWrite, Request, Session,
};
const TTL: Duration = Duration::new(1, 0); // 1 second
const NULL_TIME: Duration = Duration::new(0, 0);
pub fn mount<D, P, T, F>(
filesystem: filesystem::FileSystem<D>,
mountpoint: P,
mut callback: F,
) -> io::Result<T>
where
D: Disk,
P: AsRef<Path>,
F: FnMut(&Path) -> T,
// One of the uses of this redoxfs fuse wrapper is to populate a filesystem
// while building the Redox OS kernel. This means that we need to write on
// a filesystem that belongs to `root`, which in turn means that we need to
// be `root`, thus that we need to allow `root` to have access.
let defer_permissions = [MountOption::CUSTOM("defer_permissions".to_owned())];
mountpoint,
if cfg!(target_os = "macos") {
&defer_permissions
} else {
&[]

Jeremy Soller
committed
pub struct Fuse<D: Disk> {
pub fs: filesystem::FileSystem<D>,
// Blocks is in 512 byte blocks, not in our block size
blocks: (node.data().size() + BLOCK_SIZE - 1) / BLOCK_SIZE * (BLOCK_SIZE / 512),
blksize: 512,
atime: SystemTime::UNIX_EPOCH + Duration::new(node.data().atime().0, node.data().atime().1),
mtime: SystemTime::UNIX_EPOCH + Duration::new(node.data().mtime().0, node.data().mtime().1),
ctime: SystemTime::UNIX_EPOCH + Duration::new(node.data().ctime().0, node.data().ctime().1),
crtime: UNIX_EPOCH + NULL_TIME,
perm: node.data().mode() & Node::MODE_PERM,
nlink: node.data().links(),
uid: node.data().uid(),
gid: node.data().gid(),

Jeremy Soller
committed
impl<D: Disk> Filesystem for Fuse<D> {
fn lookup(&mut self, _req: &Request, parent_id: u64, name: &OsStr, reply: ReplyEntry) {
let parent_ptr = TreePtr::new(parent_id as u32);
match self
.fs
.tx(|tx| tx.find_node(parent_ptr, name.to_str().unwrap()))
{
Err(err) => {
reply.error(err.errno as i32);
}
}
}
fn getattr(&mut self, _req: &Request, node_id: u64, reply: ReplyAttr) {
let node_ptr = TreePtr::<Node>::new(node_id as u32);
match self.fs.tx(|tx| tx.read_tree(node_ptr)) {
Err(err) => {
reply.error(err.errno as i32);
}
}
}
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
atime: Option<TimeOrNow>,
mtime: Option<TimeOrNow>,
_ctime: Option<SystemTime>,
_crtime: Option<SystemTime>,
_chgtime: Option<SystemTime>,
_bkuptime: Option<SystemTime>,
_flags: Option<u32>,
reply: ReplyAttr,
) {
let node_ptr = TreePtr::<Node>::new(node_id as u32);
let mut node = match self.fs.tx(|tx| tx.read_tree(node_ptr)) {
Ok(ok) => ok,
Err(err) => {
reply.error(err.errno as i32);
return;
}
};
let mut node_changed = false;
if node.data().mode() & Node::MODE_PERM != mode as u16 & Node::MODE_PERM {
let new_mode =
(node.data().mode() & Node::MODE_TYPE) | (mode as u16 & Node::MODE_PERM);
node.data_mut().set_mode(new_mode);
node_changed = true;
if node.data().uid() != uid {
node.data_mut().set_uid(uid);
node_changed = true;
if node.data().gid() != gid {
node.data_mut().set_gid(gid);
node_changed = true;
let atime_c = match atime {
SpecificTime(st) => st.duration_since(UNIX_EPOCH).unwrap(),
Now => SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
};
.set_atime(atime_c.as_secs(), atime_c.subsec_nanos());
let mtime_c = match mtime {
SpecificTime(st) => st.duration_since(UNIX_EPOCH).unwrap(),
Now => SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
};
.set_mtime(mtime_c.as_secs(), mtime_c.subsec_nanos());
if let Some(size) = size {
match self.fs.tx(|tx| tx.truncate_node_inner(&mut node, size)) {
Ok(ok) => {
if ok {
node_changed = true;
Err(err) => {
reply.error(err.errno as i32);
return;
}
}
}
let attr = node_attr(&node);
if node_changed {
if let Err(err) = self.fs.tx(|tx| tx.sync_tree(node)) {
let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
tx.read_node(
node_ptr,
cmp::max(0, offset) as u64,
&mut data,
atime.as_secs(),
atime.subsec_nanos(),
)
}) {
Err(err) => {
reply.error(err.errno as i32);
}
_write_flags: u32,
_flags: i32,
_lock_owner: Option<u64>,
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
tx.write_node(
node_ptr,
cmp::max(0, offset) as u64,
data,
mtime.as_secs(),
mtime.subsec_nanos(),
)
}) {
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();
}
_fh: u64,
offset: i64,
mut reply: ReplyDirectory,
) {
match self.fs.tx(|tx| tx.child_nodes(parent_ptr, &mut children)) {
let mut i;
let skip;
skip = 0;
i = 0;
let _full = reply.add(parent_id, i, FileType::Directory, ".");
} else {
i = offset + 1;
skip = offset as usize - 1;
}
for child in children.iter().skip(skip) {
//TODO: make it possible to get file type from directory entry
let node = match self.fs.tx(|tx| tx.read_tree(child.node_ptr())) {
Ok(ok) => ok,
Err(err) => {
reply.error(err.errno as i32);
return;
}
};
FileType::Directory
} else {
FileType::RegularFile
},
if full {
break;
reply.error(err.errno as i32);
}
}
}
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
tx.create_node(
parent_ptr,
name.to_str().unwrap(),
Node::MODE_FILE | (mode as u16 & Node::MODE_PERM),
ctime.as_secs(),
ctime.subsec_nanos(),
)
}) {
// println!("Create {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode);
reply.created(&TTL, &node_attr(&node), 0, 0, flags as u32);
Err(error) => {
reply.error(error.errno as i32);
}
}
}
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
tx.create_node(
parent_ptr,
name.to_str().unwrap(),
Node::MODE_DIR | (mode as u16 & Node::MODE_PERM),
ctime.as_secs(),
ctime.subsec_nanos(),
)
}) {
// println!("Mkdir {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode);
Err(error) => {
reply.error(error.errno as i32);
}
}
}
fn rmdir(&mut self, _req: &Request, parent_id: u64, name: &OsStr, reply: ReplyEmpty) {
let parent_ptr = TreePtr::<Node>::new(parent_id as u32);
.tx(|tx| tx.remove_node(parent_ptr, name.to_str().unwrap(), Node::MODE_DIR))
Err(err) => {
reply.error(err.errno as i32);
}
}
}
fn unlink(&mut self, _req: &Request, parent_id: u64, name: &OsStr, reply: ReplyEmpty) {
let parent_ptr = TreePtr::<Node>::new(parent_id as u32);
.tx(|tx| tx.remove_node(parent_ptr, name.to_str().unwrap(), Node::MODE_FILE))
reply.error(err.errno as i32);
}
}
}
fn statfs(&mut self, _req: &Request, _ino: u64, reply: ReplyStatfs) {
let bsize = BLOCK_SIZE;
let blocks = self.fs.header.size() / bsize;
let bfree = self.fs.allocator().free();
reply.statfs(blocks, bfree, bfree, 0, 0, bsize as u32, 256, 0);
name: &OsStr,
link: &Path,
reply: ReplyEntry,
) {
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
let node = tx.create_node(
parent_ptr,
name.to_str().unwrap(),
Node::MODE_SYMLINK | 0o777,
ctime.as_secs(),
ctime.subsec_nanos(),
)?;
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
tx.write_node(
node.ptr(),
0,
link.as_os_str().as_bytes(),
mtime.as_secs(),
mtime.subsec_nanos(),
)?;
Ok(node)
}) {
Err(error) => {
reply.error(error.errno as i32);
}
}
}
fn readlink(&mut self, _req: &Request, node_id: u64, reply: ReplyData) {
let node_ptr = TreePtr::<Node>::new(node_id as u32);
let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
tx.read_node(
node_ptr,
0,
&mut data,
atime.as_secs(),
atime.subsec_nanos(),
)
}) {
Err(err) => {
reply.error(err.errno as i32);
}
}
}
fn rename(
&mut self,
_req: &Request,
orig_parent: u64,
let orig_parent_ptr = TreePtr::<Node>::new(orig_parent as u32);
let orig_name = orig_name.to_str().expect("name is not utf-8");
let new_parent_ptr = TreePtr::<Node>::new(new_parent as u32);
let new_name = new_name.to_str().expect("name is not utf-8");
// TODO: improve performance
match self
.fs
.tx(|tx| tx.rename_node(orig_parent_ptr, orig_name, new_parent_ptr, new_name))
{
Ok(()) => reply.ok(),
Err(err) => reply.error(err.errno as i32),
}
}