diff --git a/Cargo.toml b/Cargo.toml index 8cb7f81..13db878 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ required-features = ["std"] bit-vec = { default-features = false, git = "https://github.com/AltSysrq/bit-vec.git" } # default-features contains 'std' static_assertions = "0.3" spin = "0.4" +time = "0.1" [features] std = [] \ No newline at end of file diff --git a/rcore-fs-fuse/Cargo.toml b/rcore-fs-fuse/Cargo.toml new file mode 100644 index 0000000..5709123 --- /dev/null +++ b/rcore-fs-fuse/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rcore-fs-fuse" +version = "0.1.0" +authors = ["WangRunji "] +edition = "2018" + +[dependencies] +time = "0.1" +libc = "0.2" +log = "0.4" +fuse = "0.3" +structopt = "0.2" +env_logger = "0.3" +simple-filesystem = { path = "..", features = ["std"] } diff --git a/rcore-fs-fuse/src/main.rs b/rcore-fs-fuse/src/main.rs new file mode 100644 index 0000000..b11f048 --- /dev/null +++ b/rcore-fs-fuse/src/main.rs @@ -0,0 +1,137 @@ +use std::collections::btree_map::BTreeMap; +use std::ffi::OsStr; +use std::fs::OpenOptions; +use std::path::PathBuf; +use std::sync::Arc; + +use fuse::{FileAttr, Filesystem, FileType, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, Request}; +use libc; +use structopt::StructOpt; +use time::Timespec; +use log::*; + +use simple_filesystem::{sfs, vfs}; + +const TTL: Timespec = Timespec { sec: 1, nsec: 0 }; // 1 second + +struct VfsWrapper { + fs: Arc, + inodes: BTreeMap>, +} + +impl VfsWrapper { + fn new(fs: Arc) -> Self { + let mut inodes = BTreeMap::new(); + inodes.insert(1, fs.root_inode()); + VfsWrapper { fs, inodes } + } + fn trans_attr(info: vfs::FileInfo) -> FileAttr { + FileAttr { + ino: info.inode as u64, + size: info.size as u64, + blocks: info.blocks as u64, + atime: info.atime, + mtime: info.mtime, + ctime: info.ctime, + crtime: Timespec::new(0, 0), + kind: Self::trans_type(info.type_), + perm: info.mode, + nlink: info.nlinks as u32, + uid: info.uid as u32, + gid: info.gid as u32, + rdev: 0, + flags: 0, + } + } + fn trans_type(type_: vfs::FileType) -> FileType { + match type_ { + vfs::FileType::File => FileType::RegularFile, + vfs::FileType::Dir => FileType::Directory, + } + } + fn get_inode(&self, ino: u64) -> vfs::Result<&Arc> { + self.inodes.get(&(ino as usize)).ok_or(vfs::FsError::EntryNotFound) + } +} + +macro_rules! try_vfs { + ($reply:expr, $expr:expr) => (match $expr { + Ok(val) => val, + Err(err) => { + let error_code = match err { + vfs::FsError::EntryNotFound => libc::ENOENT, + _ => libc::EINVAL, + }; + $reply.error(error_code); + return; + } + }); +} + +impl Filesystem for VfsWrapper { + fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { + info!("lookup parent={} name={}", parent, name.to_str().unwrap()); + let inode = try_vfs!(reply, self.get_inode(parent)); + let target = try_vfs!(reply, inode.lookup(name.to_str().unwrap())); + let info = try_vfs!(reply, target.info()); + self.inodes.insert(info.inode, target); + let attr = Self::trans_attr(info); + reply.entry(&TTL, &attr, 0); + } + + fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) { + info!("getattr ino={}", ino); + let inode = try_vfs!(reply, self.get_inode(ino)); + let info = try_vfs!(reply, inode.info()); + let attr = Self::trans_attr(info); + reply.attr(&TTL, &attr); + } + + fn read(&mut self, _req: &Request, ino: u64, _fh: u64, offset: i64, size: u32, reply: ReplyData) { + info!("read ino={} offset={} size={}", ino, offset, size); + let inode = try_vfs!(reply, self.get_inode(ino)); + let mut data = Vec::::new(); + data.resize(size as usize, 0); + try_vfs!(reply, inode.read_at(offset as usize, data.as_mut_slice())); + reply.data(data.as_slice()); + } + + fn readdir(&mut self, _req: &Request, ino: u64, _fh: u64, offset: i64, mut reply: ReplyDirectory) { + info!("readdir ino={}, offset={}", ino, offset); + let inode = try_vfs!(reply, self.get_inode(ino)); + let info = try_vfs!(reply, inode.info()); + let count = info.size; + for i in offset as usize..count { + let name = inode.get_entry(i).unwrap(); + let inode = try_vfs!(reply, inode.find(name.as_str())); + let info = try_vfs!(reply, inode.info()); + let kind = Self::trans_type(info.type_); + let full = reply.add(info.inode as u64, i as i64 + 1, kind, name); + if full { + break; + } + } + reply.ok(); + } +} + +#[derive(Debug, StructOpt)] +struct Opt { + /// Image file + #[structopt(parse(from_os_str))] + image: PathBuf, + /// Mount point + #[structopt(parse(from_os_str))] + mount_point: PathBuf, +} + +fn main() { + env_logger::init().unwrap(); + let opt = Opt::from_args(); + let img = OpenOptions::new().read(true).write(true).open(&opt.image) + .expect("failed to open image"); + let sfs = sfs::SimpleFileSystem::open(Box::new(img)) + .expect("failed to open sfs"); + fuse::mount(VfsWrapper::new(sfs), &opt.mount_point, &[]) + .expect("failed to mount fs"); +} diff --git a/src/lib.rs b/src/lib.rs index 49c8810..04d218e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,8 +17,8 @@ macro_rules! eprintln { mod dirty; mod util; mod blocked_device; -mod vfs; -mod sfs; +pub mod vfs; +pub mod sfs; pub mod file; mod structs; #[cfg(test)] diff --git a/src/sfs.rs b/src/sfs.rs index 191851a..67f3e73 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -1,14 +1,17 @@ -use bit_vec::BitVec; -use alloc::{boxed::Box, vec::Vec, collections::BTreeMap, sync::{Arc, Weak}, string::String}; +use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::{Arc, Weak}, vec::Vec}; +use core::any::Any; +use core::fmt::{Debug, Error, Formatter}; use core::mem::uninitialized; use core::slice; -use core::fmt::{Debug, Formatter, Error}; -use core::any::Any; + +use bit_vec::BitVec; use spin::{Mutex, RwLock}; +use time::Timespec; + use crate::dirty::Dirty; use crate::structs::*; -use crate::vfs::{self, Device, INode, FileSystem, FsError}; use crate::util::*; +use crate::vfs::{self, Device, FileSystem, FsError, INode}; impl Device { fn read_block(&mut self, id: BlockId, offset: usize, buf: &mut [u8]) -> vfs::Result<()> { @@ -265,15 +268,21 @@ impl vfs::INode for INodeImpl { fn info(&self) -> vfs::Result { let disk_inode = self.disk_inode.read(); Ok(vfs::FileInfo { + inode: self.id, size: match disk_inode.type_ { FileType::File => disk_inode.size as usize, FileType::Dir => disk_inode.blocks as usize, _ => panic!("Unknown file type"), }, - mode: 0, + mode: 0o777, type_: vfs::FileType::from(disk_inode.type_.clone()), blocks: disk_inode.blocks as usize, + atime: Timespec::new(0, 0), + mtime: Timespec::new(0, 0), + ctime: Timespec::new(0, 0), nlinks: disk_inode.nlinks as usize, + uid: 0, + gid: 0, }) } fn sync(&self) -> vfs::Result<()> { diff --git a/src/vfs.rs b/src/vfs.rs index 1bc1619..89bbf9a 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -1,6 +1,7 @@ use alloc::{vec::Vec, string::String, sync::Arc}; use core::any::Any; use core::result; +use time::Timespec; /// Interface for FS to read & write /// TODO: use std::io::{Read, Write} @@ -80,15 +81,34 @@ impl INode { #[derive(Debug, Eq, PartialEq)] pub struct FileInfo { - // Note: for normal file size is the actuate file size - // for directory this is count of dirent. + /// Inode number + pub inode: usize, + /// Size in bytes + /// + /// SFS Note: for normal file size is the actuate file size + /// for directory this is count of dirent. pub size: usize, - pub mode: u32, - pub type_: FileType, + /// Size in blocks pub blocks: usize, - // Note: different from linux, "." and ".." count in nlinks - // this is same as original ucore. + /// Time of last access + pub atime: Timespec, + /// Time of last modification + pub mtime: Timespec, + /// Time of last change + pub ctime: Timespec, + /// Type of file + pub type_: FileType, + /// Permission + pub mode: u16, + /// Number of hard links + /// + /// SFS Note: different from linux, "." and ".." count in nlinks + /// this is same as original ucore. pub nlinks: usize, + /// User id + pub uid: usize, + /// Group id + pub gid: usize, } #[derive(Debug, Eq, PartialEq)]