Initial commit in the middle of building up a proof of concept

This commit is contained in:
Olivier 'reivilibre' 2022-01-16 22:19:27 +00:00
commit 95a9f56f64
16 changed files with 2957 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/target
**/*.rs.bk
.idea
/yama.iml
/.project
/.gdb_history

1948
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

9
Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[workspace]
members = [
"olivefs",
"olivefsd",
"olivefs_common",
# maybe restructure in future;
# "olivefs_com_client",
# "olivefs_com_server"
]

15
book.toml Normal file
View File

@ -0,0 +1,15 @@
[book]
authors = ["Olivier"]
language = "en"
multilingual = false
src = "docs"
title = "Olivefs Documentation"
description = "Documentation for Olivefs (a network file system)"
[output.html]
default-theme = "rust"
git-repository-url = "https://bics.ga/reivilibre/olivefs"
git-repository-icon = "fa-git-alt"
fold = { enable = true, level = 1 }
# TODO toml highlighting

2
docs/SUMMARY.md Normal file
View File

@ -0,0 +1,2 @@
# Summary

36
olivefs/Cargo.toml Normal file
View File

@ -0,0 +1,36 @@
[package]
name = "olivefs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# Robustness
log = "0.4.14"
env_logger = "0.9.0"
anyhow = "1.0.52"
bare-metrics-recorder = "0.1.0"
tracing = "0.1.29"
tracing-futures = { version = "0.2.5", features = ["tokio"] }
# Asynchronous
tokio = { version = "1.15.0", features = ["full"] }
# Serialisation
serde = { version = "1.0.133", features = ["derive"] }
serde_bare = "0.5.0"
toml = "0.5.8"
clap = { version = "3.0.7", features = ["derive"] }
# Networking
quinn = { version = "0.8.0", features = [] }
# Compression and Encryption
zstd = "0.9.2+zstd.1.5.1"
sodiumoxide = "0.2.7"
rcgen = "0.8.14"
# Filesystem
fuser = "0.10.0"
libc = "0.2.112"

693
olivefs/src/filesystem.rs Normal file
View File

@ -0,0 +1,693 @@
use std::ffi::OsStr;
use std::os::raw::c_int;
use std::path::Path;
use std::time::SystemTime;
use fuser::{Filesystem, KernelConfig, ReplyAttr, ReplyBmap, ReplyCreate, ReplyData, ReplyDirectory, ReplyDirectoryPlus, ReplyEmpty, ReplyEntry, ReplyIoctl, ReplyLock, ReplyLseek, ReplyOpen, ReplyStatfs, ReplyWrite, ReplyXattr, Request, TimeOrNow};
use libc::{ENOSYS, EPERM};
use log::{warn, debug, info};
pub mod encryption;
pub struct OliveFilesystem {
}
impl Filesystem for OliveFilesystem {
/// Initialize filesystem.
/// Called before any other filesystem method.
/// The kernel module connection can be configured using the KernelConfig object
fn init(&mut self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> {
Ok(())
}
/// Clean up filesystem.
/// Called on filesystem exit.
fn destroy(&mut self) {}
/// Look up a directory entry by name and get its attributes.
fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) {
warn!(
"[Not Implemented] lookup(parent: {:#x?}, name {:?})",
parent, name
);
reply.error(ENOSYS);
}
/// Forget about an inode.
/// The nlookup parameter indicates the number of lookups previously performed on
/// this inode. If the filesystem implements inode lifetimes, it is recommended that
/// inodes acquire a single reference on each lookup, and lose nlookup references on
/// each forget. The filesystem may ignore forget calls, if the inodes don't need to
/// have a limited lifetime. On unmount it is not guaranteed, that all referenced
/// inodes will receive a forget message.
fn forget(&mut self, _req: &Request<'_>, _ino: u64, _nlookup: u64) {}
///// Like forget, but take multiple forget requests at once for performance. The default
///// implementation will fallback to forget.
// #[cfg(feature = "abi-7-16")]
// fn batch_forget(&mut self, req: &Request<'_>, nodes: &[fuse_forget_one]) {
// for node in nodes {
// self.forget(req, node.nodeid, node.nlookup);
// }
// }
/// Get file attributes.
fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) {
warn!("[Not Implemented] getattr(ino: {:#x?})", ino);
reply.error(ENOSYS);
}
/// Set file attributes.
fn setattr(
&mut self,
_req: &Request<'_>,
ino: u64,
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
_atime: Option<TimeOrNow>,
_mtime: Option<TimeOrNow>,
_ctime: Option<SystemTime>,
fh: Option<u64>,
_crtime: Option<SystemTime>,
_chgtime: Option<SystemTime>,
_bkuptime: Option<SystemTime>,
flags: Option<u32>,
reply: ReplyAttr,
) {
debug!(
"[Not Implemented] setattr(ino: {:#x?}, mode: {:?}, uid: {:?}, \
gid: {:?}, size: {:?}, fh: {:?}, flags: {:?})",
ino, mode, uid, gid, size, fh, flags
);
reply.error(ENOSYS);
}
/// Read symbolic link.
fn readlink(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyData) {
debug!("[Not Implemented] readlink(ino: {:#x?})", ino);
reply.error(ENOSYS);
}
/// Create file node.
/// Create a regular file, character device, block device, fifo or socket node.
fn mknod(
&mut self,
_req: &Request<'_>,
parent: u64,
name: &OsStr,
mode: u32,
umask: u32,
rdev: u32,
reply: ReplyEntry,
) {
debug!(
"[Not Implemented] mknod(parent: {:#x?}, name: {:?}, mode: {}, \
umask: {:#x?}, rdev: {})",
parent, name, mode, umask, rdev
);
reply.error(ENOSYS);
}
/// Create a directory.
fn mkdir(
&mut self,
_req: &Request<'_>,
parent: u64,
name: &OsStr,
mode: u32,
umask: u32,
reply: ReplyEntry,
) {
debug!(
"[Not Implemented] mkdir(parent: {:#x?}, name: {:?}, mode: {}, umask: {:#x?})",
parent, name, mode, umask
);
reply.error(ENOSYS);
}
/// Remove a file.
fn unlink(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) {
debug!(
"[Not Implemented] unlink(parent: {:#x?}, name: {:?})",
parent, name,
);
reply.error(ENOSYS);
}
/// Remove a directory.
fn rmdir(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) {
debug!(
"[Not Implemented] rmdir(parent: {:#x?}, name: {:?})",
parent, name,
);
reply.error(ENOSYS);
}
/// Create a symbolic link.
fn symlink(
&mut self,
_req: &Request<'_>,
parent: u64,
name: &OsStr,
link: &Path,
reply: ReplyEntry,
) {
debug!(
"[Not Implemented] symlink(parent: {:#x?}, name: {:?}, link: {:?})",
parent, name, link,
);
reply.error(EPERM);
}
/// Rename a file.
fn rename(
&mut self,
_req: &Request<'_>,
parent: u64,
name: &OsStr,
newparent: u64,
newname: &OsStr,
flags: u32,
reply: ReplyEmpty,
) {
debug!(
"[Not Implemented] rename(parent: {:#x?}, name: {:?}, newparent: {:#x?}, \
newname: {:?}, flags: {})",
parent, name, newparent, newname, flags,
);
reply.error(ENOSYS);
}
/// Create a hard link.
fn link(
&mut self,
_req: &Request<'_>,
ino: u64,
newparent: u64,
newname: &OsStr,
reply: ReplyEntry,
) {
debug!(
"[Not Implemented] link(ino: {:#x?}, newparent: {:#x?}, newname: {:?})",
ino, newparent, newname
);
reply.error(EPERM);
}
/// Open a file.
/// Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and O_TRUNC) are
/// available in flags. Filesystem may store an arbitrary file handle (pointer, index,
/// etc) in fh, and use this in other all other file operations (read, write, flush,
/// release, fsync). Filesystem may also implement stateless file I/O and not store
/// anything in fh. There are also some flags (direct_io, keep_cache) which the
/// filesystem may set, to change the way the file is opened. See fuse_file_info
/// structure in <fuse_common.h> for more details.
fn open(&mut self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) {
reply.opened(0, 0);
}
/// Read data.
/// Read should send exactly the number of bytes requested except on EOF or error,
/// otherwise the rest of the data will be substituted with zeroes. An exception to
/// this is when the file has been opened in 'direct_io' mode, in which case the
/// return value of the read system call will reflect the return value of this
/// operation. fh will contain the value set by the open method, or will be undefined
/// if the open method didn't set any value.
///
/// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9
/// lock_owner: only supported with ABI >= 7.9
fn read(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
offset: i64,
size: u32,
flags: i32,
lock_owner: Option<u64>,
reply: ReplyData,
) {
warn!(
"[Not Implemented] read(ino: {:#x?}, fh: {}, offset: {}, size: {}, \
flags: {:#x?}, lock_owner: {:?})",
ino, fh, offset, size, flags, lock_owner
);
reply.error(ENOSYS);
}
/// Write data.
/// Write should return exactly the number of bytes requested except on error. An
/// exception to this is when the file has been opened in 'direct_io' mode, in
/// which case the return value of the write system call will reflect the return
/// value of this operation. fh will contain the value set by the open method, or
/// will be undefined if the open method didn't set any value.
///
/// write_flags: will contain FUSE_WRITE_CACHE, if this write is from the page cache. If set,
/// the pid, uid, gid, and fh may not match the value that would have been sent if write cachin
/// is disabled
/// flags: these are the file flags, such as O_SYNC. Only supported with ABI >= 7.9
/// lock_owner: only supported with ABI >= 7.9
fn write(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
offset: i64,
data: &[u8],
write_flags: u32,
flags: i32,
lock_owner: Option<u64>,
reply: ReplyWrite,
) {
debug!(
"[Not Implemented] write(ino: {:#x?}, fh: {}, offset: {}, data.len(): {}, \
write_flags: {:#x?}, flags: {:#x?}, lock_owner: {:?})",
ino,
fh,
offset,
data.len(),
write_flags,
flags,
lock_owner
);
reply.error(ENOSYS);
}
/// Flush method.
/// This is called on each close() of the opened file. Since file descriptors can
/// be duplicated (dup, dup2, fork), for one open call there may be many flush
/// calls. Filesystems shouldn't assume that flush will always be called after some
/// writes, or that if will be called at all. fh will contain the value set by the
/// open method, or will be undefined if the open method didn't set any value.
/// NOTE: the name of the method is misleading, since (unlike fsync) the filesystem
/// is not forced to flush pending writes. One reason to flush data, is if the
/// filesystem wants to return write errors. If the filesystem supports file locking
/// operations (setlk, getlk) it should remove all locks belonging to 'lock_owner'.
fn flush(&mut self, _req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) {
debug!(
"[Not Implemented] flush(ino: {:#x?}, fh: {}, lock_owner: {:?})",
ino, fh, lock_owner
);
reply.error(ENOSYS);
}
/// Release an open file.
/// Release is called when there are no more references to an open file: all file
/// descriptors are closed and all memory mappings are unmapped. For every open
/// call there will be exactly one release call. The filesystem may reply with an
/// error, but error values are not returned to close() or munmap() which triggered
/// the release. fh will contain the value set by the open method, or will be undefined
/// if the open method didn't set any value. flags will contain the same flags as for
/// open.
fn release(
&mut self,
_req: &Request<'_>,
_ino: u64,
_fh: u64,
_flags: i32,
_lock_owner: Option<u64>,
_flush: bool,
reply: ReplyEmpty,
) {
reply.ok();
}
/// Synchronize file contents.
/// If the datasync parameter is non-zero, then only the user data should be flushed,
/// not the meta data.
fn fsync(&mut self, _req: &Request<'_>, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) {
debug!(
"[Not Implemented] fsync(ino: {:#x?}, fh: {}, datasync: {})",
ino, fh, datasync
);
reply.error(ENOSYS);
}
/// Open a directory.
/// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh, and
/// use this in other all other directory stream operations (readdir, releasedir,
/// fsyncdir). Filesystem may also implement stateless directory I/O and not store
/// anything in fh, though that makes it impossible to implement standard conforming
/// directory stream operations in case the contents of the directory can change
/// between opendir and releasedir.
fn opendir(&mut self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) {
reply.opened(0, 0);
}
/// Read directory.
/// Send a buffer filled using buffer.fill(), with size not exceeding the
/// requested size. Send an empty buffer on end of stream. fh will contain the
/// value set by the opendir method, or will be undefined if the opendir method
/// didn't set any value.
fn readdir(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
offset: i64,
reply: ReplyDirectory,
) {
warn!(
"[Not Implemented] readdir(ino: {:#x?}, fh: {}, offset: {})",
ino, fh, offset
);
reply.error(ENOSYS);
}
/// Read directory.
/// Send a buffer filled using buffer.fill(), with size not exceeding the
/// requested size. Send an empty buffer on end of stream. fh will contain the
/// value set by the opendir method, or will be undefined if the opendir method
/// didn't set any value.
fn readdirplus(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
offset: i64,
reply: ReplyDirectoryPlus,
) {
debug!(
"[Not Implemented] readdirplus(ino: {:#x?}, fh: {}, offset: {})",
ino, fh, offset
);
reply.error(ENOSYS);
}
/// Release an open directory.
/// For every opendir call there will be exactly one releasedir call. fh will
/// contain the value set by the opendir method, or will be undefined if the
/// opendir method didn't set any value.
fn releasedir(
&mut self,
_req: &Request<'_>,
_ino: u64,
_fh: u64,
_flags: i32,
reply: ReplyEmpty,
) {
reply.ok();
}
/// Synchronize directory contents.
/// If the datasync parameter is set, then only the directory contents should
/// be flushed, not the meta data. fh will contain the value set by the opendir
/// method, or will be undefined if the opendir method didn't set any value.
fn fsyncdir(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
datasync: bool,
reply: ReplyEmpty,
) {
debug!(
"[Not Implemented] fsyncdir(ino: {:#x?}, fh: {}, datasync: {})",
ino, fh, datasync
);
reply.error(ENOSYS);
}
/// Get file system statistics.
fn statfs(&mut self, _req: &Request<'_>, _ino: u64, reply: ReplyStatfs) {
reply.statfs(0, 0, 0, 0, 0, 512, 255, 0);
}
/// Set an extended attribute.
fn setxattr(
&mut self,
_req: &Request<'_>,
ino: u64,
name: &OsStr,
_value: &[u8],
flags: i32,
position: u32,
reply: ReplyEmpty,
) {
debug!(
"[Not Implemented] setxattr(ino: {:#x?}, name: {:?}, flags: {:#x?}, position: {})",
ino, name, flags, position
);
reply.error(ENOSYS);
}
/// Get an extended attribute.
/// If `size` is 0, the size of the value should be sent with `reply.size()`.
/// If `size` is not 0, and the value fits, send it with `reply.data()`, or
/// `reply.error(ERANGE)` if it doesn't.
fn getxattr(
&mut self,
_req: &Request<'_>,
ino: u64,
name: &OsStr,
size: u32,
reply: ReplyXattr,
) {
debug!(
"[Not Implemented] getxattr(ino: {:#x?}, name: {:?}, size: {})",
ino, name, size
);
reply.error(ENOSYS);
}
/// List extended attribute names.
/// If `size` is 0, the size of the value should be sent with `reply.size()`.
/// If `size` is not 0, and the value fits, send it with `reply.data()`, or
/// `reply.error(ERANGE)` if it doesn't.
fn listxattr(&mut self, _req: &Request<'_>, ino: u64, size: u32, reply: ReplyXattr) {
debug!(
"[Not Implemented] listxattr(ino: {:#x?}, size: {})",
ino, size
);
reply.error(ENOSYS);
}
/// Remove an extended attribute.
fn removexattr(&mut self, _req: &Request<'_>, ino: u64, name: &OsStr, reply: ReplyEmpty) {
debug!(
"[Not Implemented] removexattr(ino: {:#x?}, name: {:?})",
ino, name
);
reply.error(ENOSYS);
}
/// Check file access permissions.
/// This will be called for the access() system call. If the 'default_permissions'
/// mount option is given, this method is not called. This method is not called
/// under Linux kernel versions 2.4.x
fn access(&mut self, _req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) {
debug!("[Not Implemented] access(ino: {:#x?}, mask: {})", ino, mask);
reply.error(ENOSYS);
}
/// Create and open a file.
/// If the file does not exist, first create it with the specified mode, and then
/// open it. Open flags (with the exception of O_NOCTTY) are available in flags.
/// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh,
/// and use this in other all other file operations (read, write, flush, release,
/// fsync). There are also some flags (direct_io, keep_cache) which the
/// filesystem may set, to change the way the file is opened. See fuse_file_info
/// structure in <fuse_common.h> for more details. If this method is not
/// implemented or under Linux kernel versions earlier than 2.6.15, the mknod()
/// and open() methods will be called instead.
fn create(
&mut self,
_req: &Request<'_>,
parent: u64,
name: &OsStr,
mode: u32,
umask: u32,
flags: i32,
reply: ReplyCreate,
) {
debug!(
"[Not Implemented] create(parent: {:#x?}, name: {:?}, mode: {}, umask: {:#x?}, \
flags: {:#x?})",
parent, name, mode, umask, flags
);
reply.error(ENOSYS);
}
/// Test for a POSIX file lock.
fn getlk(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
lock_owner: u64,
start: u64,
end: u64,
typ: i32,
pid: u32,
reply: ReplyLock,
) {
debug!(
"[Not Implemented] getlk(ino: {:#x?}, fh: {}, lock_owner: {}, start: {}, \
end: {}, typ: {}, pid: {})",
ino, fh, lock_owner, start, end, typ, pid
);
reply.error(ENOSYS);
}
/// Acquire, modify or release a POSIX file lock.
/// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but
/// otherwise this is not always the case. For checking lock ownership,
/// 'fi->owner' must be used. The l_pid field in 'struct flock' should only be
/// used to fill in this field in getlk(). Note: if the locking methods are not
/// implemented, the kernel will still allow file locking to work locally.
/// Hence these are only interesting for network filesystems and similar.
fn setlk(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
lock_owner: u64,
start: u64,
end: u64,
typ: i32,
pid: u32,
sleep: bool,
reply: ReplyEmpty,
) {
debug!(
"[Not Implemented] setlk(ino: {:#x?}, fh: {}, lock_owner: {}, start: {}, \
end: {}, typ: {}, pid: {}, sleep: {})",
ino, fh, lock_owner, start, end, typ, pid, sleep
);
reply.error(ENOSYS);
}
/// Map block index within file to block index within device.
/// Note: This makes sense only for block device backed filesystems mounted
/// with the 'blkdev' option
fn bmap(&mut self, _req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) {
debug!(
"[Not Implemented] bmap(ino: {:#x?}, blocksize: {}, idx: {})",
ino, blocksize, idx,
);
reply.error(ENOSYS);
}
/// control device
fn ioctl(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
flags: u32,
cmd: u32,
in_data: &[u8],
out_size: u32,
reply: ReplyIoctl,
) {
debug!(
"[Not Implemented] ioctl(ino: {:#x?}, fh: {}, flags: {}, cmd: {}, \
in_data.len(): {}, out_size: {})",
ino,
fh,
flags,
cmd,
in_data.len(),
out_size,
);
reply.error(ENOSYS);
}
/// Preallocate or deallocate space to a file
fn fallocate(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
offset: i64,
length: i64,
mode: i32,
reply: ReplyEmpty,
) {
debug!(
"[Not Implemented] fallocate(ino: {:#x?}, fh: {}, offset: {}, \
length: {}, mode: {})",
ino, fh, offset, length, mode
);
reply.error(ENOSYS);
}
/// Reposition read/write file offset
fn lseek(
&mut self,
_req: &Request<'_>,
ino: u64,
fh: u64,
offset: i64,
whence: i32,
reply: ReplyLseek,
) {
debug!(
"[Not Implemented] lseek(ino: {:#x?}, fh: {}, offset: {}, whence: {})",
ino, fh, offset, whence
);
reply.error(ENOSYS);
}
/// Copy the specified range from the source inode to the destination inode
fn copy_file_range(
&mut self,
_req: &Request<'_>,
ino_in: u64,
fh_in: u64,
offset_in: i64,
ino_out: u64,
fh_out: u64,
offset_out: i64,
len: u64,
flags: u32,
reply: ReplyWrite,
) {
debug!(
"[Not Implemented] copy_file_range(ino_in: {:#x?}, fh_in: {}, \
offset_in: {}, ino_out: {:#x?}, fh_out: {}, offset_out: {}, \
len: {}, flags: {})",
ino_in, fh_in, offset_in, ino_out, fh_out, offset_out, len, flags
);
reply.error(ENOSYS);
}
/// macOS only: Rename the volume. Set fuse_init_out.flags during init to
/// FUSE_VOL_RENAME to enable
#[cfg(target_os = "macos")]
fn setvolname(&mut self, _req: &Request<'_>, name: &OsStr, reply: ReplyEmpty) {
debug!("[Not Implemented] setvolname(name: {:?})", name);
reply.error(ENOSYS);
}
/// macOS only (undocumented)
#[cfg(target_os = "macos")]
fn exchange(
&mut self,
_req: &Request<'_>,
parent: u64,
name: &OsStr,
newparent: u64,
newname: &OsStr,
options: u64,
reply: ReplyEmpty,
) {
debug!(
"[Not Implemented] exchange(parent: {:#x?}, name: {:?}, newparent: {:#x?}, \
newname: {:?}, options: {})",
parent, name, newparent, newname, options
);
reply.error(ENOSYS);
}
/// macOS only: Query extended times (bkuptime and crtime). Set fuse_init_out.flags
/// during init to FUSE_XTIMES to enable
#[cfg(target_os = "macos")]
fn getxtimes(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyXTimes) {
debug!("[Not Implemented] getxtimes(ino: {:#x?})", ino);
reply.error(ENOSYS);
}
}

View File

@ -0,0 +1,10 @@
// TODO Trait needed to provide encryption
pub trait EncryptionProvider {
}
// No encryption
impl EncryptionProvider for () {
}

34
olivefs/src/main.rs Normal file
View File

@ -0,0 +1,34 @@
use std::path::{Path, PathBuf};
use crate::filesystem::OliveFilesystem;
use clap::Parser;
use env_logger::Env;
pub mod filesystem;
pub mod requester;
#[derive(Parser)]
pub enum OlivefsCommands {
Mount {
configuration_file: PathBuf,
mount_at: PathBuf
}
}
fn main() {
env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();
println!("Hello, world!");
let command: OlivefsCommands = OlivefsCommands::parse();
match command {
OlivefsCommands::Mount { configuration_file, mount_at } => {
let fs = OliveFilesystem {
};
//fuser::MountOption::
fuser::mount2(fs, mount_at, &[]).unwrap();
}
}
}

28
olivefs/src/requester.rs Normal file
View File

@ -0,0 +1,28 @@
use std::net::SocketAddr;
use std::str::FromStr;
use quinn::{Connection, Endpoint, EndpointConfig};
use tokio::sync::mpsc;
pub struct Requester {
}
pub struct RequesterInternal {
rx: mpsc::Receiver<()>,
connection: quinn::Connection
}
impl RequesterInternal {
pub async fn send(&mut self) -> anyhow::Result<()> {
// TODO use with_roots and only use the desired CA! ...
let x = quinn::ClientConfig::with_native_roots();
let ep = Endpoint::client(SocketAddr::from_str("0.0.0.0:0").unwrap()).unwrap();
let conn = ep.connect(SocketAddr::from_str("127.0.0.1:5051").unwrap(), "blah").unwrap().await.unwrap();
// conn.bi_streams and uni_streams are streams needed to listen out for new streams from the server.
// conn.datagrams is similar but for unreliable datagrams
let bi = conn.connection.open_bi().await?;
Ok(())
}
}

26
olivefs_common/Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[package]
name = "olivefs_common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# Robustness
log = "0.4.14"
env_logger = "0.9.0"
anyhow = "1.0.52"
# Asynchronous
tokio = { version = "1.15.0", features = ["full"] }
# Serialisation
serde = { version = "1.0.133", features = ["derive"] }
serde_bare = "0.5.0"
toml = "0.5.8"
# Networking
quinn = { version = "0.8.0", features = [] }
# Compression and Encryption
zstd = "0.9.2+zstd.1.5.1"

12
olivefs_common/src/lib.rs Normal file
View File

@ -0,0 +1,12 @@
pub mod messages;
pub mod networking;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}

View File

@ -0,0 +1,52 @@
use std::fmt::Debug;
use serde::{Serialize, Deserialize};
use serde::de::DeserializeOwned;
pub const COMMON_VERSION: &'static str = env!("CARGO_PKG_VERSION");
#[derive(Serialize, Deserialize, Debug, Clone)]
/// The first message sent in both ways in the primary control stream.
pub struct HelloMessage {
/// Version of the common library; used to determine compatibility.
/// In the event of a mismatch between client and server, the connection should cease.
pub protocol_version: String,
/// Informational version of each application used — not used for compatibility checks.
pub software_version: String,
}
impl HelloMessage {
/// Constructs a HelloMessage from:
/// - the version of the common package (as the protocol version, since they are one and the same)
/// - the software (client or server version): purely informational.
pub fn new(software_version: String) -> HelloMessage {
HelloMessage {
protocol_version: COMMON_VERSION.to_string(),
software_version
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
/// Sent by the client on any data stream.
pub enum DataCommand {
}
pub trait DataResponseBase: Serialize + DeserializeOwned + Debug + Clone + 'static {
}
#[derive(Serialize, Deserialize, Debug, Clone)]
/// Sent by the server in response to a DataCommand on the same data stream.
pub enum DataResponse<R: DataResponseBase> {
/// The command was successful.
Success(R),
/// The command failed. Here is some information about that.
Error {
code: i32,
// TODO should
message: String,
},
}

View File

@ -0,0 +1,22 @@
use quinn::{ReadExactError, RecvStream};
use serde::de::DeserializeOwned;
pub async fn read_control_message<M: DeserializeOwned>(recv_stream: &mut RecvStream) -> anyhow::Result<Option<M>> {
let mut u16_buf = [0u8; 2];
if let Err(err) = recv_stream.read_exact(&mut u16_buf).await {
return if err == ReadExactError::FinishedEarly {
Ok(None)
} else {
Err(err)?;
todo!()
};
}
let control_message_length = u16::from_be_bytes(u16_buf) as usize;
let mut control_message_bytes: Vec<u8> = vec![0u8; control_message_length];
recv_stream.read_exact(&mut control_message_bytes[..]).await?;
Ok(todo!())
}

31
olivefsd/Cargo.toml Normal file
View File

@ -0,0 +1,31 @@
[package]
name = "olivefsd"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# Robustness
log = "0.4.14"
env_logger = "0.9.0"
anyhow = "1.0.52"
bare-metrics-recorder = "0.1.0"
tracing = "0.1.29"
tracing-futures = { version = "0.2.5", features = ["tokio"] }
# Asynchronous
tokio = { version = "1.15.0", features = ["full"] }
futures-util = "0.3.19"
# Serialisation
serde = { version = "1.0.133", features = ["derive"] }
serde_bare = "0.5.0"
toml = "0.5.8"
# Networking
quinn = { version = "0.8.0", features = [] }
rustls = "0.20.2"
# Compression and Encryption
zstd = "0.9.2+zstd.1.5.1"

32
olivefsd/src/main.rs Normal file
View File

@ -0,0 +1,32 @@
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
use futures_util::StreamExt;
use quinn::Endpoint;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
println!("Hello, world!");
//let x = quinn::ServerConfig::with_native_roots();
let crypto = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(todo!())
.with_single_cert(todo!(), todo!())?;
let crypto = Arc::new(crypto);
let x = quinn::ServerConfig::with_crypto(crypto);
let (ep, mut incoming) = Endpoint::server(x, SocketAddr::from_str("127.0.0.1:5051").unwrap()).unwrap();
loop {
let next = incoming.next().await;
if let Some(next) = next {
let mut new_conn = next.await?;
let (incoming_tx, incoming_rx) = new_conn.bi_streams.next().await.unwrap()?;
//incoming_rx.
} else {
break;
}
}
Ok(())
}