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