Initial commit in the middle of building up a proof of concept
This commit is contained in:
commit
95a9f56f64
|
@ -0,0 +1,7 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
|
||||
.idea
|
||||
/yama.iml
|
||||
/.project
|
||||
/.gdb_history
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"olivefs",
|
||||
"olivefsd",
|
||||
"olivefs_common",
|
||||
# maybe restructure in future;
|
||||
# "olivefs_com_client",
|
||||
# "olivefs_com_server"
|
||||
]
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
# Summary
|
||||
|
|
@ -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"
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
// TODO Trait needed to provide encryption
|
||||
pub trait EncryptionProvider {
|
||||
|
||||
}
|
||||
|
||||
// No encryption
|
||||
impl EncryptionProvider for () {
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
}
|
|
@ -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!())
|
||||
}
|
|
@ -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"
|
|
@ -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(())
|
||||
}
|
Loading…
Reference in New Issue