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"
|
||||
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).
|
||||
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_key,
|
||||
socket_addr,
|
||||
config.connection.timeout,
|
||||
config.connection.keep_alive,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::net::SocketAddr;
|
|||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use olivefs_common::io::read_file;
|
||||
use olivefs_common::messages::{DataCommand, DataResponse, FileMetadata, VnodeId};
|
||||
|
@ -39,6 +40,8 @@ impl RequesterInternal {
|
|||
cert_path: &Path,
|
||||
cert_key_path: &Path,
|
||||
server_endpoint: SocketAddr,
|
||||
timeout: u32,
|
||||
keep_alive: u32,
|
||||
) -> anyhow::Result<RequesterInternal> {
|
||||
let ca_cert_der = read_file(ca_path).await?;
|
||||
let ca_cert = rustls::Certificate(ca_cert_der);
|
||||
|
@ -58,7 +61,14 @@ impl RequesterInternal {
|
|||
// Set the protocol that's in use.
|
||||
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 =
|
||||
Endpoint::client(SocketAddr::from_str("0.0.0.0:0").unwrap()).unwrap();
|
||||
|
|
|
@ -73,6 +73,8 @@ pub enum FileKind {
|
|||
Socket,
|
||||
}
|
||||
|
||||
mod messages_std;
|
||||
|
||||
#[cfg(feature = "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"
|
||||
ca_certificate = "ca/ca.crt"
|
||||
|
||||
timeout = 60
|
||||
|
||||
[service]
|
||||
root = "/home/rei/Music"
|
||||
root = "/home/quail/Music"
|
||||
|
||||
[clients.bread]
|
||||
|
|
|
@ -28,6 +28,9 @@ pub struct ListenConfiguration {
|
|||
/// Path to the CA's DER-encoded certificate (.crt) file.
|
||||
/// This is used to validate client certificates.
|
||||
pub ca_certificate: PathBuf,
|
||||
|
||||
/// Idle timeout in seconds.
|
||||
pub timeout: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::configuration::OlivefsServerConfiguration;
|
||||
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 futures_util::StreamExt;
|
||||
use log::{error, info, warn};
|
||||
|
@ -10,7 +12,9 @@ use quinn::crypto::rustls::HandshakeData;
|
|||
use quinn::{Connecting, Endpoint};
|
||||
use rustls::{PrivateKey, RootCertStore};
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use x509_parser::extensions::{GeneralName, ParsedExtension};
|
||||
use x509_parser::prelude::X509Certificate;
|
||||
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()];
|
||||
|
||||
let crypto = Arc::new(crypto);
|
||||
let server_config = quinn::ServerConfig::with_crypto(crypto);
|
||||
let (_ep, mut incoming) = Endpoint::server(server_config, socket_addr).unwrap();
|
||||
let mut server_config = quinn::ServerConfig::with_crypto(crypto);
|
||||
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);
|
||||
|
||||
|
@ -148,14 +159,38 @@ pub async fn establish_connection(
|
|||
.filter(|k| config.clients.contains_key(*k))
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("No valid client names."))?;
|
||||
info!("Selecting name: {:?}", chosen_name);
|
||||
|
||||
let client_config = &config.clients[chosen_name];
|
||||
let root = config.service.root.clone();
|
||||
|
||||
// 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());
|
||||
|
||||
{
|
||||
// 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 {
|
||||
if let Err(error) =
|
||||
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 futures_util::StreamExt;
|
||||
use log::{error, info};
|
||||
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 std::sync::Arc;
|
||||
|
||||
pub async fn handle_command_stream(
|
||||
_tx: SendStream,
|
||||
mut tx: SendStream,
|
||||
mut rx: RecvStream,
|
||||
_client_info: Arc<ClientInformation>,
|
||||
_client_state: Arc<ClientSpecificState>,
|
||||
_server_state: Arc<ServerwideState>,
|
||||
client_info: Arc<ClientInformation>,
|
||||
client_state: Arc<ClientSpecificState>,
|
||||
server_state: Arc<ServerwideState>,
|
||||
) -> anyhow::Result<()> {
|
||||
let file_access = FileAccess {
|
||||
client_info,
|
||||
client_state,
|
||||
server_state,
|
||||
};
|
||||
|
||||
while let Some(command) = read_bare_message::<DataCommand>(&mut rx)
|
||||
.await
|
||||
.context("Whilst waiting for Data Command")?
|
||||
{
|
||||
match command {
|
||||
DataCommand::GetAttr { vnode: _ } => {
|
||||
info!("getattr Not Implemented");
|
||||
DataCommand::GetAttr { vnode } => {
|
||||
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 std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
@ -40,3 +42,137 @@ pub struct InodeInfo {
|
|||
/// The real path that this inode refers to.
|
||||
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