feat: can run
This commit is contained in:
9
__fs/fuse/Cargo.lock
generated
9
__fs/fuse/Cargo.lock
generated
@@ -11,6 +11,9 @@ name = "fuse"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fuse 0.3.1",
|
"fuse 0.3.1",
|
||||||
|
"libc",
|
||||||
|
"sha1",
|
||||||
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -56,6 +59,12 @@ version = "0.3.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread-scoped"
|
name = "thread-scoped"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fuse = "0.3"
|
fuse = "0.3"
|
||||||
|
sha1 = "0.6"
|
||||||
|
libc = "0.2"
|
||||||
|
time = "0.1"
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
|
||||||
|
Mount
|
||||||
|
```
|
||||||
|
mkdir <DIR>
|
||||||
|
cargo r -- <DIR>
|
||||||
|
```
|
||||||
|
|
||||||
Unmount
|
Unmount
|
||||||
```
|
```
|
||||||
umount -f <MOUNT POINT>
|
umount -f <MOUNT POINT>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
https://github.com/Minoru/plentyfs
|
||||||
|
|||||||
@@ -1,3 +1,193 @@
|
|||||||
fn main() {
|
use std::convert::TryInto;
|
||||||
println!("Hello, world!");
|
use std::env;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
use fuse::{
|
||||||
|
FileAttr, FileType, Filesystem, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, Request,
|
||||||
|
};
|
||||||
|
use libc::ENOENT;
|
||||||
|
use sha1::Sha1;
|
||||||
|
use time::Timespec;
|
||||||
|
|
||||||
|
const FILES_COUNT: u64 = 1_000;
|
||||||
|
const FILE_SIZE: u64 = 1_048_576; // bytes (1 megabyte)
|
||||||
|
const BLOCK_SIZE: usize = 20; // bytes (the size of SHA-1 digest)
|
||||||
|
|
||||||
|
const TTL: Timespec = Timespec { sec: 1, nsec: 0 };
|
||||||
|
|
||||||
|
const ROOT_DIR_ATTR: FileAttr = FileAttr {
|
||||||
|
ino: 1,
|
||||||
|
size: 0,
|
||||||
|
blocks: 0,
|
||||||
|
atime: UNIX_EPOCH, // 1970-01-01 00:00:00
|
||||||
|
mtime: UNIX_EPOCH,
|
||||||
|
ctime: UNIX_EPOCH,
|
||||||
|
crtime: UNIX_EPOCH,
|
||||||
|
kind: FileType::Directory,
|
||||||
|
perm: 0o755,
|
||||||
|
nlink: 2,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
rdev: 0,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const UNIX_EPOCH: Timespec = Timespec { sec: 0, nsec: 0 };
|
||||||
|
|
||||||
|
const FILE_ATTR: FileAttr = FileAttr {
|
||||||
|
ino: 2,
|
||||||
|
size: FILE_SIZE,
|
||||||
|
blocks: 1,
|
||||||
|
atime: UNIX_EPOCH, // 1970-01-01 00:00:00
|
||||||
|
mtime: UNIX_EPOCH,
|
||||||
|
ctime: UNIX_EPOCH,
|
||||||
|
crtime: UNIX_EPOCH,
|
||||||
|
kind: FileType::RegularFile,
|
||||||
|
perm: 0o644,
|
||||||
|
nlink: 1,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
rdev: 0,
|
||||||
|
flags: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlentyFS {
|
||||||
|
/// Initial value of our bespoke RNG.
|
||||||
|
seed: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filesystem for PlentyFS {
|
||||||
|
fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) {
|
||||||
|
if parent != 1 {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match name.to_str().and_then(|s| s.parse::<u64>().ok()) {
|
||||||
|
Some(file_number) => {
|
||||||
|
if file_number < FILES_COUNT {
|
||||||
|
let file_attr = FileAttr {
|
||||||
|
ino: file_number + 2,
|
||||||
|
..FILE_ATTR
|
||||||
|
};
|
||||||
|
reply.entry(&TTL, &file_attr, 0);
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
|
||||||
|
if ino == 1 {
|
||||||
|
reply.attr(&TTL, &ROOT_DIR_ATTR);
|
||||||
|
} else if ino >= 2 && ino <= (FILES_COUNT + 1) {
|
||||||
|
let file_attr = FileAttr { ino, ..FILE_ATTR };
|
||||||
|
reply.attr(&TTL, &file_attr);
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(
|
||||||
|
&mut self,
|
||||||
|
_req: &Request,
|
||||||
|
ino: u64,
|
||||||
|
_fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
size: u32,
|
||||||
|
reply: ReplyData,
|
||||||
|
) {
|
||||||
|
if ino >= 2 && ino <= (FILES_COUNT + 1) {
|
||||||
|
let seed = generate_file_seed(self.seed, ino);
|
||||||
|
|
||||||
|
let first_block = (offset as u64) / (BLOCK_SIZE as u64);
|
||||||
|
let blocks_count = (size as u64 + 2 * BLOCK_SIZE as u64) / (BLOCK_SIZE as u64);
|
||||||
|
|
||||||
|
let mut data = vec![];
|
||||||
|
for block_no in first_block..(first_block + blocks_count) {
|
||||||
|
data.extend(&generate_block_data(seed, block_no));
|
||||||
|
}
|
||||||
|
|
||||||
|
let inside_offset = (offset as usize) % (BLOCK_SIZE as usize);
|
||||||
|
reply.data(&data[inside_offset..(inside_offset + size as usize)]);
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readdir(
|
||||||
|
&mut self,
|
||||||
|
_req: &Request,
|
||||||
|
ino: u64,
|
||||||
|
_fh: u64,
|
||||||
|
mut offset: i64,
|
||||||
|
mut reply: ReplyDirectory,
|
||||||
|
) {
|
||||||
|
if ino != 1 {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset == 0 {
|
||||||
|
reply.add(1, 1, FileType::Directory, ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset <= 1 {
|
||||||
|
reply.add(1, 1, FileType::Directory, ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset >= 2 {
|
||||||
|
offset -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for file_number in (0..FILES_COUNT).skip(offset as usize) {
|
||||||
|
let inode = file_number + 2;
|
||||||
|
let next_entry_offset = 2 + file_number + 1;
|
||||||
|
reply.add(
|
||||||
|
inode,
|
||||||
|
next_entry_offset as i64,
|
||||||
|
FileType::RegularFile,
|
||||||
|
file_number.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
reply.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mountpoint = env::args_os().nth(1).unwrap();
|
||||||
|
let options = ["-o", "ro", "-o", "fsname=plentyfs"]
|
||||||
|
.iter()
|
||||||
|
.map(|o| o.as_ref())
|
||||||
|
.collect::<Vec<&OsStr>>();
|
||||||
|
// TODO: replace PID by a proper source of entropy. Add an option for the user to set their own
|
||||||
|
// seed for reproducibility.
|
||||||
|
fuse::mount(
|
||||||
|
PlentyFS {
|
||||||
|
seed: std::process::id() as u64,
|
||||||
|
},
|
||||||
|
&mountpoint,
|
||||||
|
&options,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_file_seed(root_seed: u64, inode: u64) -> u64 {
|
||||||
|
let file_salt = "filesalt".as_bytes();
|
||||||
|
let (file_salt, _) = file_salt.split_at(std::mem::size_of::<u64>());
|
||||||
|
// TODO: refactor to make panics impossible.
|
||||||
|
let file_salt: [u8; 8] = file_salt.try_into().unwrap();
|
||||||
|
let file_salt = u64::from_le_bytes(file_salt);
|
||||||
|
|
||||||
|
// TODO: replace XOR with a better mixing technique.
|
||||||
|
root_seed ^ inode ^ file_salt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_block_data(seed: u64, block_no: u64) -> [u8; BLOCK_SIZE] {
|
||||||
|
let mut sha1 = Sha1::from(seed.to_le_bytes());
|
||||||
|
sha1.update(&block_no.to_le_bytes());
|
||||||
|
sha1.digest().bytes()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user