Support getattr in reality
This commit is contained in:
parent
5a08dc1c88
commit
3cf535621d
|
@ -8,3 +8,6 @@ client_key = "ca/bread.key"
|
||||||
|
|
||||||
client_certificate = "ca/bread.crt"
|
client_certificate = "ca/bread.crt"
|
||||||
ca_certificate = "ca/ca.crt"
|
ca_certificate = "ca/ca.crt"
|
||||||
|
|
||||||
|
timeout = 60
|
||||||
|
keep_alive = 30
|
||||||
|
|
|
@ -24,4 +24,10 @@ pub struct ConnectionConfiguration {
|
||||||
|
|
||||||
/// Path to the DER-encoded CA certificate file (.crt).
|
/// Path to the DER-encoded CA certificate file (.crt).
|
||||||
pub ca_certificate: PathBuf,
|
pub ca_certificate: PathBuf,
|
||||||
|
|
||||||
|
/// Idle timeout in seconds.
|
||||||
|
pub timeout: u32,
|
||||||
|
|
||||||
|
/// Keep-alive in seconds
|
||||||
|
pub keep_alive: u32,
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,8 @@ async fn main() -> anyhow::Result<()> {
|
||||||
&config.connection.client_certificate,
|
&config.connection.client_certificate,
|
||||||
&config.connection.client_key,
|
&config.connection.client_key,
|
||||||
socket_addr,
|
socket_addr,
|
||||||
|
config.connection.timeout,
|
||||||
|
config.connection.keep_alive,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ use std::net::SocketAddr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use olivefs_common::io::read_file;
|
use olivefs_common::io::read_file;
|
||||||
use olivefs_common::messages::{DataCommand, DataResponse, FileMetadata, VnodeId};
|
use olivefs_common::messages::{DataCommand, DataResponse, FileMetadata, VnodeId};
|
||||||
|
@ -39,6 +40,8 @@ impl RequesterInternal {
|
||||||
cert_path: &Path,
|
cert_path: &Path,
|
||||||
cert_key_path: &Path,
|
cert_key_path: &Path,
|
||||||
server_endpoint: SocketAddr,
|
server_endpoint: SocketAddr,
|
||||||
|
timeout: u32,
|
||||||
|
keep_alive: u32,
|
||||||
) -> anyhow::Result<RequesterInternal> {
|
) -> anyhow::Result<RequesterInternal> {
|
||||||
let ca_cert_der = read_file(ca_path).await?;
|
let ca_cert_der = read_file(ca_path).await?;
|
||||||
let ca_cert = rustls::Certificate(ca_cert_der);
|
let ca_cert = rustls::Certificate(ca_cert_der);
|
||||||
|
@ -58,7 +61,14 @@ impl RequesterInternal {
|
||||||
// Set the protocol that's in use.
|
// Set the protocol that's in use.
|
||||||
client_crypto.alpn_protocols = vec![ALPN_PROTOCOL.to_vec()];
|
client_crypto.alpn_protocols = vec![ALPN_PROTOCOL.to_vec()];
|
||||||
|
|
||||||
let client_config = quinn::ClientConfig::new(Arc::new(client_crypto));
|
let mut client_config = quinn::ClientConfig::new(Arc::new(client_crypto));
|
||||||
|
let transport_config = Arc::get_mut(&mut client_config.transport).unwrap();
|
||||||
|
transport_config.max_idle_timeout(Some(
|
||||||
|
Duration::from_secs(timeout as u64).try_into().unwrap(),
|
||||||
|
));
|
||||||
|
transport_config.keep_alive_interval(Some(
|
||||||
|
Duration::from_secs(keep_alive as u64).try_into().unwrap(),
|
||||||
|
));
|
||||||
|
|
||||||
let mut client_endpoint =
|
let mut client_endpoint =
|
||||||
Endpoint::client(SocketAddr::from_str("0.0.0.0:0").unwrap()).unwrap();
|
Endpoint::client(SocketAddr::from_str("0.0.0.0:0").unwrap()).unwrap();
|
||||||
|
|
|
@ -73,6 +73,8 @@ pub enum FileKind {
|
||||||
Socket,
|
Socket,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod messages_std;
|
||||||
|
|
||||||
#[cfg(feature = "fuser")]
|
#[cfg(feature = "fuser")]
|
||||||
mod messages_fuser;
|
mod messages_fuser;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
use crate::messages::FileKind;
|
||||||
|
use std::fs::FileType;
|
||||||
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
|
||||||
|
impl From<std::fs::FileType> for FileKind {
|
||||||
|
fn from(ft: FileType) -> Self {
|
||||||
|
if ft.is_file() {
|
||||||
|
FileKind::RegularFile
|
||||||
|
} else if ft.is_dir() {
|
||||||
|
FileKind::Directory
|
||||||
|
} else if ft.is_symlink() {
|
||||||
|
FileKind::Symlink
|
||||||
|
} else if ft.is_block_device() {
|
||||||
|
FileKind::BlockDevice
|
||||||
|
} else if ft.is_char_device() {
|
||||||
|
FileKind::CharDevice
|
||||||
|
} else if ft.is_fifo() {
|
||||||
|
FileKind::NamedPipe
|
||||||
|
} else if ft.is_socket() {
|
||||||
|
FileKind::Socket
|
||||||
|
} else {
|
||||||
|
panic!("Unknown FileType");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,9 @@ server_key = "ca/server.key"
|
||||||
server_certificate = "ca/server.crt"
|
server_certificate = "ca/server.crt"
|
||||||
ca_certificate = "ca/ca.crt"
|
ca_certificate = "ca/ca.crt"
|
||||||
|
|
||||||
|
timeout = 60
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
root = "/home/rei/Music"
|
root = "/home/quail/Music"
|
||||||
|
|
||||||
[clients.bread]
|
[clients.bread]
|
||||||
|
|
|
@ -28,6 +28,9 @@ pub struct ListenConfiguration {
|
||||||
/// Path to the CA's DER-encoded certificate (.crt) file.
|
/// Path to the CA's DER-encoded certificate (.crt) file.
|
||||||
/// This is used to validate client certificates.
|
/// This is used to validate client certificates.
|
||||||
pub ca_certificate: PathBuf,
|
pub ca_certificate: PathBuf,
|
||||||
|
|
||||||
|
/// Idle timeout in seconds.
|
||||||
|
pub timeout: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::configuration::OlivefsServerConfiguration;
|
use crate::configuration::OlivefsServerConfiguration;
|
||||||
use crate::server::connections::handle_new_streams;
|
use crate::server::connections::handle_new_streams;
|
||||||
use crate::server::file_access::{ClientInformation, ClientSpecificState, ServerwideState};
|
use crate::server::file_access::{
|
||||||
|
ClientInformation, ClientSpecificState, InodeInfo, ServerwideState,
|
||||||
|
};
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
|
@ -10,7 +12,9 @@ use quinn::crypto::rustls::HandshakeData;
|
||||||
use quinn::{Connecting, Endpoint};
|
use quinn::{Connecting, Endpoint};
|
||||||
use rustls::{PrivateKey, RootCertStore};
|
use rustls::{PrivateKey, RootCertStore};
|
||||||
use std::net::ToSocketAddrs;
|
use std::net::ToSocketAddrs;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
use x509_parser::extensions::{GeneralName, ParsedExtension};
|
use x509_parser::extensions::{GeneralName, ParsedExtension};
|
||||||
use x509_parser::prelude::X509Certificate;
|
use x509_parser::prelude::X509Certificate;
|
||||||
use x509_parser::traits::FromDer;
|
use x509_parser::traits::FromDer;
|
||||||
|
@ -52,8 +56,15 @@ pub async fn listen(config: Arc<OlivefsServerConfiguration>) -> anyhow::Result<(
|
||||||
crypto.alpn_protocols = vec![ALPN_PROTOCOL.to_vec()];
|
crypto.alpn_protocols = vec![ALPN_PROTOCOL.to_vec()];
|
||||||
|
|
||||||
let crypto = Arc::new(crypto);
|
let crypto = Arc::new(crypto);
|
||||||
let server_config = quinn::ServerConfig::with_crypto(crypto);
|
let mut server_config = quinn::ServerConfig::with_crypto(crypto);
|
||||||
let (_ep, mut incoming) = Endpoint::server(server_config, socket_addr).unwrap();
|
Arc::get_mut(&mut server_config.transport)
|
||||||
|
.unwrap()
|
||||||
|
.max_idle_timeout(Some(
|
||||||
|
Duration::from_secs(config.listen.timeout as u64)
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
let (endpoint, mut incoming) = Endpoint::server(server_config, socket_addr).unwrap();
|
||||||
|
|
||||||
info!("Listening on {:?}.", socket_addr);
|
info!("Listening on {:?}.", socket_addr);
|
||||||
|
|
||||||
|
@ -148,14 +159,38 @@ pub async fn establish_connection(
|
||||||
.filter(|k| config.clients.contains_key(*k))
|
.filter(|k| config.clients.contains_key(*k))
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| anyhow!("No valid client names."))?;
|
.ok_or_else(|| anyhow!("No valid client names."))?;
|
||||||
|
info!("Selecting name: {:?}", chosen_name);
|
||||||
|
|
||||||
let client_config = &config.clients[chosen_name];
|
let client_config = &config.clients[chosen_name];
|
||||||
let root = config.service.root.clone();
|
let root = config.service.root.clone();
|
||||||
|
|
||||||
// Now determine what the client is allowed to do
|
// Now determine what the client is allowed to do
|
||||||
let client_info = Arc::new(ClientInformation { root });
|
let client_info = Arc::new(ClientInformation { root: root.clone() });
|
||||||
let client_state = Arc::new(ClientSpecificState::default());
|
let client_state = Arc::new(ClientSpecificState::default());
|
||||||
|
|
||||||
|
{
|
||||||
|
// Pre-populate the inode map with a null device (to fill slot 0) and the root (inode 1).
|
||||||
|
let mut inode_map = client_state.inode_map.write().await;
|
||||||
|
|
||||||
|
let root_inode_info = InodeInfo {
|
||||||
|
real_path: root.clone(),
|
||||||
|
};
|
||||||
|
let null_inode_info = InodeInfo {
|
||||||
|
real_path: PathBuf::from("/dev/null"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
inode_map.insert(null_inode_info),
|
||||||
|
0usize,
|
||||||
|
"null device should be inode 0 for safety"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
inode_map.insert(root_inode_info),
|
||||||
|
1usize,
|
||||||
|
"root should be inode 1 by fuser definition"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
tokio::spawn(async {
|
tokio::spawn(async {
|
||||||
if let Err(error) =
|
if let Err(error) =
|
||||||
handle_new_streams(new_conn, client_info, client_state, server_state).await
|
handle_new_streams(new_conn, client_info, client_state, server_state).await
|
||||||
|
|
|
@ -1,26 +1,34 @@
|
||||||
use crate::server::file_access::{ClientInformation, ClientSpecificState, ServerwideState};
|
use crate::server::file_access::{
|
||||||
|
ClientInformation, ClientSpecificState, FileAccess, ServerwideState,
|
||||||
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use olivefs_common::messages::DataCommand;
|
use olivefs_common::messages::DataCommand;
|
||||||
use olivefs_common::networking::read_bare_message;
|
use olivefs_common::networking::{read_bare_message, send_bare_message};
|
||||||
use quinn::{NewConnection, RecvStream, SendStream};
|
use quinn::{NewConnection, RecvStream, SendStream};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub async fn handle_command_stream(
|
pub async fn handle_command_stream(
|
||||||
_tx: SendStream,
|
mut tx: SendStream,
|
||||||
mut rx: RecvStream,
|
mut rx: RecvStream,
|
||||||
_client_info: Arc<ClientInformation>,
|
client_info: Arc<ClientInformation>,
|
||||||
_client_state: Arc<ClientSpecificState>,
|
client_state: Arc<ClientSpecificState>,
|
||||||
_server_state: Arc<ServerwideState>,
|
server_state: Arc<ServerwideState>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
let file_access = FileAccess {
|
||||||
|
client_info,
|
||||||
|
client_state,
|
||||||
|
server_state,
|
||||||
|
};
|
||||||
|
|
||||||
while let Some(command) = read_bare_message::<DataCommand>(&mut rx)
|
while let Some(command) = read_bare_message::<DataCommand>(&mut rx)
|
||||||
.await
|
.await
|
||||||
.context("Whilst waiting for Data Command")?
|
.context("Whilst waiting for Data Command")?
|
||||||
{
|
{
|
||||||
match command {
|
match command {
|
||||||
DataCommand::GetAttr { vnode: _ } => {
|
DataCommand::GetAttr { vnode } => {
|
||||||
info!("getattr Not Implemented");
|
send_bare_message(&mut tx, &file_access.getattr(vnode).await?).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
use olivefs_common::messages::{DataResponse, FileMetadata, VnodeId};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
@ -40,3 +42,137 @@ pub struct InodeInfo {
|
||||||
/// The real path that this inode refers to.
|
/// The real path that this inode refers to.
|
||||||
pub real_path: PathBuf,
|
pub real_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FileAccess {
|
||||||
|
pub client_info: Arc<ClientInformation>,
|
||||||
|
pub client_state: Arc<ClientSpecificState>,
|
||||||
|
pub server_state: Arc<ServerwideState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
mod error_codes {
|
||||||
|
pub const ENOENT: i32 = 2;
|
||||||
|
pub const EFAULT: i32 = 14;
|
||||||
|
pub const EDEADLK: i32 = 35;
|
||||||
|
pub const ENAMETOOLONG: i32 = 36;
|
||||||
|
pub const ENOLCK: i32 = 37;
|
||||||
|
pub const ENOSYS: i32 = 38;
|
||||||
|
pub const ENOTEMPTY: i32 = 39;
|
||||||
|
pub const ELOOP: i32 = 40;
|
||||||
|
pub const ENOMSG: i32 = 42;
|
||||||
|
pub const EIDRM: i32 = 43;
|
||||||
|
pub const ECHRNG: i32 = 44;
|
||||||
|
pub const EL2NSYNC: i32 = 45;
|
||||||
|
pub const EL3HLT: i32 = 46;
|
||||||
|
pub const EL3RST: i32 = 47;
|
||||||
|
pub const ELNRNG: i32 = 48;
|
||||||
|
pub const EUNATCH: i32 = 49;
|
||||||
|
pub const ENOCSI: i32 = 50;
|
||||||
|
pub const EL2HLT: i32 = 51;
|
||||||
|
pub const EBADE: i32 = 52;
|
||||||
|
pub const EBADR: i32 = 53;
|
||||||
|
pub const EXFULL: i32 = 54;
|
||||||
|
pub const ENOANO: i32 = 55;
|
||||||
|
pub const EBADRQC: i32 = 56;
|
||||||
|
pub const EBADSLT: i32 = 57;
|
||||||
|
pub const EMULTIHOP: i32 = 72;
|
||||||
|
pub const EOVERFLOW: i32 = 75;
|
||||||
|
pub const ENOTUNIQ: i32 = 76;
|
||||||
|
pub const EBADFD: i32 = 77;
|
||||||
|
pub const EBADMSG: i32 = 74;
|
||||||
|
pub const EREMCHG: i32 = 78;
|
||||||
|
pub const ELIBACC: i32 = 79;
|
||||||
|
pub const ELIBBAD: i32 = 80;
|
||||||
|
pub const ELIBSCN: i32 = 81;
|
||||||
|
pub const ELIBMAX: i32 = 82;
|
||||||
|
pub const ELIBEXEC: i32 = 83;
|
||||||
|
pub const EILSEQ: i32 = 84;
|
||||||
|
pub const ERESTART: i32 = 85;
|
||||||
|
pub const ESTRPIPE: i32 = 86;
|
||||||
|
pub const EUSERS: i32 = 87;
|
||||||
|
pub const ENOTSOCK: i32 = 88;
|
||||||
|
pub const EDESTADDRREQ: i32 = 89;
|
||||||
|
pub const EMSGSIZE: i32 = 90;
|
||||||
|
pub const EPROTOTYPE: i32 = 91;
|
||||||
|
pub const ENOPROTOOPT: i32 = 92;
|
||||||
|
pub const EPROTONOSUPPORT: i32 = 93;
|
||||||
|
pub const ESOCKTNOSUPPORT: i32 = 94;
|
||||||
|
pub const EOPNOTSUPP: i32 = 95;
|
||||||
|
pub const EPFNOSUPPORT: i32 = 96;
|
||||||
|
pub const EAFNOSUPPORT: i32 = 97;
|
||||||
|
pub const EADDRINUSE: i32 = 98;
|
||||||
|
pub const EADDRNOTAVAIL: i32 = 99;
|
||||||
|
pub const ENETDOWN: i32 = 100;
|
||||||
|
pub const ENETUNREACH: i32 = 101;
|
||||||
|
pub const ENETRESET: i32 = 102;
|
||||||
|
pub const ECONNABORTED: i32 = 103;
|
||||||
|
pub const ECONNRESET: i32 = 104;
|
||||||
|
pub const ENOBUFS: i32 = 105;
|
||||||
|
pub const EISCONN: i32 = 106;
|
||||||
|
pub const ENOTCONN: i32 = 107;
|
||||||
|
pub const ESHUTDOWN: i32 = 108;
|
||||||
|
pub const ETOOMANYREFS: i32 = 109;
|
||||||
|
pub const ETIMEDOUT: i32 = 110;
|
||||||
|
pub const ECONNREFUSED: i32 = 111;
|
||||||
|
pub const EHOSTDOWN: i32 = 112;
|
||||||
|
pub const EHOSTUNREACH: i32 = 113;
|
||||||
|
pub const EALREADY: i32 = 114;
|
||||||
|
pub const EINPROGRESS: i32 = 115;
|
||||||
|
pub const ESTALE: i32 = 116;
|
||||||
|
pub const EDQUOT: i32 = 122;
|
||||||
|
pub const ENOMEDIUM: i32 = 123;
|
||||||
|
pub const EMEDIUMTYPE: i32 = 124;
|
||||||
|
pub const ECANCELED: i32 = 125;
|
||||||
|
pub const ENOKEY: i32 = 126;
|
||||||
|
pub const EKEYEXPIRED: i32 = 127;
|
||||||
|
pub const EKEYREVOKED: i32 = 128;
|
||||||
|
pub const EKEYREJECTED: i32 = 129;
|
||||||
|
pub const EOWNERDEAD: i32 = 130;
|
||||||
|
pub const ENOTRECOVERABLE: i32 = 131;
|
||||||
|
pub const EHWPOISON: i32 = 133;
|
||||||
|
pub const ERFKILL: i32 = 132;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileAccess {
|
||||||
|
pub async fn getattr(&self, vnode: VnodeId) -> anyhow::Result<DataResponse<FileMetadata>> {
|
||||||
|
let inode_map = self.client_state.inode_map.read().await;
|
||||||
|
if let Some(inode_info) = inode_map.get(vnode.0 as usize) {
|
||||||
|
match tokio::fs::metadata(&inode_info.real_path).await {
|
||||||
|
Ok(metadata) => {
|
||||||
|
let file_metadata = FileMetadata {
|
||||||
|
ino: vnode,
|
||||||
|
size: metadata.size(),
|
||||||
|
blocks: metadata.blocks(),
|
||||||
|
atime: metadata.accessed().unwrap(),
|
||||||
|
mtime: metadata.modified().unwrap(),
|
||||||
|
ctime: metadata.created().unwrap(),
|
||||||
|
kind: metadata.file_type().into(),
|
||||||
|
perm: metadata.permissions().mode() as u16,
|
||||||
|
nlink: metadata.nlink() as u32,
|
||||||
|
uid: metadata.uid(),
|
||||||
|
gid: metadata.gid(),
|
||||||
|
rdev: metadata.rdev() as u32,
|
||||||
|
blksize: metadata.blksize() as u32,
|
||||||
|
};
|
||||||
|
Ok(DataResponse::Success(file_metadata))
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
let err_code = if let Some(err_code) = error.raw_os_error() {
|
||||||
|
err_code
|
||||||
|
} else {
|
||||||
|
error_codes::ENOENT
|
||||||
|
};
|
||||||
|
Ok(DataResponse::Error {
|
||||||
|
code: err_code,
|
||||||
|
message: format!("{:?} ({:?})", error, inode_info.real_path),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(DataResponse::Error {
|
||||||
|
code: error_codes::EFAULT,
|
||||||
|
message: "No inode info".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue