parent
48b3c2618a
commit
1eb25f7422
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "rcore-fs-fuse"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["WangRunji <wangrunji0408@163.com>"]
|
||||||
|
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"] }
|
@ -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<T: vfs::FileSystem> {
|
||||||
|
fs: Arc<T>,
|
||||||
|
inodes: BTreeMap<usize, Arc<vfs::INode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: vfs::FileSystem> VfsWrapper<T> {
|
||||||
|
fn new(fs: Arc<T>) -> 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<vfs::INode>> {
|
||||||
|
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<T: vfs::FileSystem> Filesystem for VfsWrapper<T> {
|
||||||
|
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::<u8>::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");
|
||||||
|
}
|
Loading…
Reference in new issue