Created
May 27, 2018 18:53
-
-
Save starfleetcadet75/20c3c1a6f62573cefc8e0a2ee1b4d7a8 to your computer and use it in GitHub Desktop.
Using FUSE in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [package] | |
| name = "rfuse" | |
| version = "0.1.0" | |
| [dependencies] | |
| fuse = "0.3" | |
| libc = "0.2.41" | |
| time = "0.1.40" | |
| serde_json = "1.0.18" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| extern crate serde_json; | |
| extern crate fuse; | |
| extern crate libc; | |
| extern crate time; | |
| use fuse::{FileAttr, FileType, Filesystem, Request, ReplyAttr, ReplyData, ReplyEntry, ReplyDirectory}; | |
| use time::Timespec; | |
| use libc::ENOENT; | |
| use std::env; | |
| use std::path::Path; | |
| use std::ffi::OsStr; | |
| use std::collections::BTreeMap; | |
| struct JsonFilesystem { | |
| /// JSON Value representing files and contents as Key-Value pairs. | |
| tree: serde_json::Value, | |
| /// Maps filenames to their inodes. | |
| inodes: BTreeMap<String, u64>, | |
| /// Maps inodes to their file attributes. | |
| attrs: BTreeMap<u64, FileAttr>, | |
| } | |
| impl JsonFilesystem { | |
| fn new(tree: serde_json::Value) -> JsonFilesystem { | |
| let mut attrs = BTreeMap::new(); | |
| let mut inodes = BTreeMap::new(); | |
| let ts = time::now().to_timespec(); | |
| // Create entry for the root directory | |
| attrs.insert(1, FileAttr { | |
| ino: 1, | |
| size: 0, | |
| blocks: 0, | |
| atime: ts, | |
| mtime: ts, | |
| ctime: ts, | |
| crtime: ts, | |
| kind: FileType::Directory, | |
| perm: 0o755, | |
| nlink: 0, | |
| uid: 0, | |
| gid: 0, | |
| rdev: 0, | |
| flags: 0, | |
| }); | |
| inodes.insert("/".to_string(), 1); | |
| // Create entries for each file in the JSON tree | |
| for (i, (key, value)) in tree.as_object().unwrap().iter().enumerate() { | |
| let attr = FileAttr { | |
| ino: i as u64 + 2, | |
| size: value.to_string().len() as u64, | |
| blocks: 0, | |
| atime: ts, | |
| mtime: ts, | |
| ctime: ts, | |
| crtime: ts, | |
| kind: FileType::RegularFile, | |
| perm: 0o644, | |
| nlink: 0, | |
| uid: 0, | |
| gid: 0, | |
| rdev: 0, | |
| flags: 0, | |
| }; | |
| attrs.insert(attr.ino, attr); | |
| inodes.insert(key.clone(), attr.ino); | |
| } | |
| JsonFilesystem { | |
| tree: tree.clone(), | |
| inodes: inodes, | |
| attrs: attrs, | |
| } | |
| } | |
| } | |
| impl Filesystem for JsonFilesystem { | |
| fn getattr(&mut self, _req: &Request, inode: u64, reply: ReplyAttr) { | |
| println!("getattr(inode={})", inode); | |
| match self.attrs.get(&inode) { | |
| Some(attr) => { | |
| let ttl = Timespec::new(1, 0); | |
| reply.attr(&ttl, attr); | |
| }, | |
| None => reply.error(ENOENT), | |
| }; | |
| } | |
| fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) { | |
| println!("lookup(parent={}, name={})", parent, name.to_str().unwrap()); | |
| // Use the filename to lookup the inode | |
| let inode = match self.inodes.get(name.to_str().unwrap()) { | |
| Some(inode) => inode, | |
| None => { | |
| reply.error(ENOENT); | |
| return; | |
| }, | |
| }; | |
| match self.attrs.get(inode) { | |
| Some(attr) => { | |
| let ttl = Timespec::new(1, 0); | |
| reply.entry(&ttl, attr, 0); | |
| }, | |
| None => reply.error(ENOENT), | |
| }; | |
| } | |
| fn read(&mut self, _req: &Request, ino: u64, fh: u64, offset: i64, size: u32, reply: ReplyData) { | |
| println!("read(inode={}, fh={}, offset={}, size={})", ino, fh, offset, size); | |
| for (key, &inode) in self.inodes.iter() { | |
| if inode == ino { | |
| let value = self.tree.get(key).unwrap(); | |
| reply.data(value.to_string().as_bytes()); | |
| return; | |
| } | |
| } | |
| reply.error(ENOENT); | |
| } | |
| fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: i64, mut reply: ReplyDirectory) { | |
| println!("readdir(inode={}, fh={}, offset={})", ino, fh, offset); | |
| if ino == 1 { | |
| if offset == 0 { | |
| reply.add(1, 0, FileType::Directory, &Path::new(".")); | |
| reply.add(1, 1, FileType::Directory, &Path::new("..")); | |
| for (key, &inode) in &self.inodes { | |
| // Skip over the root inode | |
| if inode == 1 { | |
| continue; | |
| } | |
| let offset = inode as i64; | |
| println!("\tkey={}, inode={}, offset={}", key, inode, offset); | |
| reply.add(inode, offset, FileType::RegularFile, key); | |
| } | |
| } | |
| reply.ok(); | |
| } else { | |
| reply.error(ENOENT); | |
| } | |
| } | |
| } | |
| fn main() { | |
| let data = r#"{ | |
| "file1": "Random contents", | |
| "file2": 124346, | |
| "file3": "42" | |
| }"#; | |
| let tree: serde_json::Value = serde_json::from_str(data).unwrap(); | |
| let fs = JsonFilesystem::new(tree); | |
| // Get the mountpoint from the first argument | |
| let mountpoint = match env::args().nth(1) { | |
| Some(path) => path, | |
| None => { | |
| println!("Usage: {} <MOUNTPOINT>", env::args().nth(0).unwrap()); | |
| return; | |
| } | |
| }; | |
| fuse::mount(fs, &mountpoint, &[]).expect("Failed to mount filesystem"); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment