mirror of
https://github.com/OMGeeky/gdriver2.git
synced 2026-02-15 22:14:31 +01:00
265 lines
8.5 KiB
Rust
265 lines
8.5 KiB
Rust
use std::collections::HashMap;
|
|
use std::ffi::{OsStr, OsString};
|
|
use std::os::raw::c_int;
|
|
use std::time::Duration;
|
|
|
|
use anyhow::anyhow;
|
|
use bimap::BiMap;
|
|
use fuser::{KernelConfig, ReplyEntry, ReplyOpen, Request};
|
|
use tarpc::context::current as current_context;
|
|
use tracing::*;
|
|
|
|
use gdriver_common::drive_structure::drive_id::{DriveId, ROOT_ID};
|
|
use gdriver_common::drive_structure::file_handle_flags::HandleFlags;
|
|
|
|
use gdriver_common::ipc::gdriver_service::{
|
|
errors::GDriverServiceError, GDriverServiceClient, GDriverSettings,
|
|
};
|
|
|
|
use crate::filesystem::attributes::read_inode_attributes_from_meta_file;
|
|
use crate::filesystem::errors::FilesystemError;
|
|
use crate::prelude::macros::*;
|
|
use crate::prelude::*;
|
|
|
|
mod macros;
|
|
|
|
//TODO2: Decide if this is a good TTL
|
|
const TTL: Duration = Duration::from_secs(2);
|
|
|
|
type Inode = u64;
|
|
type FileHandle = u64;
|
|
|
|
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
|
struct FileIdentifier {
|
|
parent: Inode,
|
|
name: OsString,
|
|
}
|
|
#[derive(Debug)]
|
|
pub struct DriveFilesystem {
|
|
gdriver_client: GDriverServiceClient,
|
|
|
|
entry_ids: BiMap<Inode, DriveId>,
|
|
ino_to_file_handles: HashMap<Inode, Vec<FileHandle>>,
|
|
next_ino: u64,
|
|
gdriver_settings: GDriverSettings,
|
|
entry_name_parent_to_ino: BiMap<FileIdentifier, Inode>,
|
|
}
|
|
|
|
impl DriveFilesystem {
|
|
pub fn new(gdriver_client: GDriverServiceClient) -> Self {
|
|
Self {
|
|
gdriver_client,
|
|
entry_ids: BiMap::new(),
|
|
ino_to_file_handles: HashMap::new(),
|
|
next_ino: 222,
|
|
gdriver_settings: GDriverSettings::default(),
|
|
entry_name_parent_to_ino: BiMap::new(),
|
|
}
|
|
}
|
|
fn generate_ino(&mut self) -> Inode {
|
|
let ino = self.next_ino;
|
|
self.next_ino += 1;
|
|
ino
|
|
}
|
|
}
|
|
|
|
//region DriveFilesystem ino_to_id
|
|
impl DriveFilesystem {
|
|
fn get_id_from_ino(&self, ino: Inode) -> Option<&DriveId> {
|
|
self.entry_ids.get_by_left(&ino)
|
|
}
|
|
fn get_ino_from_id(&mut self, id: DriveId) -> Inode {
|
|
let x = self.entry_ids.get_by_right(&id);
|
|
if let Some(ino) = x {
|
|
return *ino;
|
|
}
|
|
self.add_id(id)
|
|
}
|
|
fn remove_id(&mut self, id: DriveId) -> Result<Inode> {
|
|
if let Some((ino, _)) = self.entry_ids.remove_by_right(&id) {
|
|
Ok(ino)
|
|
} else {
|
|
Err(Box::from(anyhow!("could not find id {}", id)))
|
|
}
|
|
}
|
|
fn add_id(&mut self, id: DriveId) -> Inode {
|
|
let ino = self.generate_ino();
|
|
trace!("adding new ino for drive id: {} => {}", id, ino);
|
|
self.entry_ids.insert(ino, id);
|
|
ino
|
|
}
|
|
}
|
|
|
|
//endregion
|
|
mod attributes;
|
|
|
|
impl fuser::Filesystem for DriveFilesystem {
|
|
//region init
|
|
fn init(&mut self, _req: &Request<'_>, _config: &mut KernelConfig) -> StdResult<(), c_int> {
|
|
self.entry_ids.insert(1, ROOT_ID.clone());
|
|
self.gdriver_settings = send_request!(self.gdriver_client.get_settings(current_context()))
|
|
.map_err(|e| {
|
|
error!("Got a connection error while fetching settings: {e}");
|
|
libc::ECONNREFUSED
|
|
})?
|
|
.map_err(|e| {
|
|
error!("Got an error while fetching settings: {e}");
|
|
trace!("details: {e:?}");
|
|
libc::EBADMSG
|
|
})?;
|
|
|
|
Ok(())
|
|
}
|
|
//endregion
|
|
//region lookup
|
|
fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) {
|
|
if name.to_str().unwrap().contains('/') {
|
|
todo!("The name in lookup can contain multiple path segments, not just a single name, directly under the parent")
|
|
}
|
|
let metadata = utils::lookup::lookup(self, parent, name.to_os_string());
|
|
match metadata {
|
|
Ok(metadata) => {
|
|
reply.entry(&TTL, &metadata.into(), 0);
|
|
}
|
|
Err(e) => {
|
|
error!("Got an error during lookup: {e:?}");
|
|
match e {
|
|
FilesystemError::Rpc(_) => reply.error(libc::EREMOTEIO),
|
|
FilesystemError::IO(_) => reply.error(libc::EIO),
|
|
FilesystemError::Service(_) | FilesystemError::NotFound => {
|
|
reply.error(libc::ENOENT)
|
|
}
|
|
FilesystemError::Other(e) => {
|
|
dbg!(e);
|
|
todo!("Handle other errors and decide what error code should be used here")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//endregion
|
|
//region open
|
|
fn open(&mut self, _req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) {
|
|
let data = utils::lookup::open(self, ino, HandleFlags::from(flags));
|
|
match data {
|
|
Ok((file_header, flags)) => {
|
|
reply.opened(file_header, flags.into());
|
|
}
|
|
Err(e) => {
|
|
error!("Got an error during open: {e:?}");
|
|
match e {
|
|
FilesystemError::Rpc(_) => reply.error(libc::EREMOTEIO),
|
|
FilesystemError::NotFound => reply.error(libc::ENOENT),
|
|
FilesystemError::IO(_) => reply.error(libc::EIO),
|
|
e => {
|
|
dbg!(e);
|
|
todo!("Handle other errors and decide what error code should be used here")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//endregion
|
|
}
|
|
|
|
mod errors {
|
|
use std::error::Error;
|
|
|
|
use tarpc::client::RpcError;
|
|
|
|
use gdriver_common::ipc::gdriver_service::errors::GDriverServiceError;
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum FilesystemError {
|
|
#[error("Error while executing RPC: {0}")]
|
|
Rpc(#[from] RpcError),
|
|
#[error("Could not find entity specified")]
|
|
NotFound,
|
|
#[error("IO Error")]
|
|
IO(#[source] Box<dyn Error>),
|
|
#[error("Service returned Error: {0}")]
|
|
Service(#[from] GDriverServiceError),
|
|
#[error("Some other error occurred: {0}")]
|
|
Other(#[source] Box<dyn Error>),
|
|
}
|
|
}
|
|
|
|
mod utils {
|
|
use super::*;
|
|
|
|
pub mod lookup {
|
|
use futures::TryFutureExt;
|
|
|
|
use gdriver_common::ipc::gdriver_service::errors::GetFileByPathError;
|
|
|
|
use crate::filesystem::attributes::InodeAttributes;
|
|
use crate::filesystem::errors::FilesystemError;
|
|
|
|
use super::*;
|
|
|
|
pub fn lookup(
|
|
fs: &mut DriveFilesystem,
|
|
parent: Inode,
|
|
name: OsString,
|
|
) -> StdResult<InodeAttributes, FilesystemError> {
|
|
let id: DriveId;
|
|
let ino: Inode;
|
|
|
|
let name = name.to_os_string();
|
|
let ino_opt = fs.entry_name_parent_to_ino.get_by_left(&FileIdentifier {
|
|
parent,
|
|
name: name.clone(),
|
|
});
|
|
|
|
match ino_opt {
|
|
None => {
|
|
//we don't know this name with this parent already, so we have to look it up
|
|
let parent_id = fs
|
|
.entry_ids
|
|
.get_by_left(&parent)
|
|
.ok_or(FilesystemError::NotFound)?;
|
|
trace!(
|
|
"looking for child of parent:{} with name: {:?}",
|
|
parent_id,
|
|
name
|
|
);
|
|
id = send_request!(fs.gdriver_client.get_file_by_name(
|
|
current_context(),
|
|
name.to_os_string(),
|
|
parent_id.clone()
|
|
))?
|
|
.map_err(GDriverServiceError::from)?;
|
|
|
|
ino = fs.add_id(id.clone());
|
|
}
|
|
Some(i) => {
|
|
ino = *i;
|
|
id = fs
|
|
.get_id_from_ino(*i)
|
|
.ok_or(FilesystemError::NotFound)?
|
|
.clone();
|
|
}
|
|
}
|
|
let open_file_handles =
|
|
fs.ino_to_file_handles.get(&ino).map(Vec::len).unwrap_or(0) as u64;
|
|
send_request!(fs
|
|
.gdriver_client
|
|
.get_metadata_for_file(current_context(), id.clone()))?
|
|
.map_err(GDriverServiceError::from)?;
|
|
let meta_path = fs.gdriver_settings.get_metadata_file_path(&id);
|
|
let metadata = read_inode_attributes_from_meta_file(&meta_path, ino, open_file_handles)
|
|
.map_err(FilesystemError::IO)?;
|
|
Ok(metadata)
|
|
}
|
|
|
|
pub(crate) fn open(
|
|
fs: &mut DriveFilesystem,
|
|
inode: Inode,
|
|
flags: HandleFlags,
|
|
) -> StdResult<(FileHandle, HandleFlags), FilesystemError> {
|
|
dbg!(&fs, inode, flags);
|
|
todo!()
|
|
}
|
|
}
|
|
}
|