use std::{
collections::HashMap,
io::Error,
path::{Path, PathBuf},
sync::Arc,
};
use inode_alloc::InodeAlloc;
use libfuse_fs::{
overlayfs::{config, OverlayFs},
passthrough::{new_passthroughfs_layer, PassthroughArgs},
};
use rfuse3::raw::{Filesystem, Request};
use tokio::sync::Mutex;
use crate::{
dicfuse::{Dicfuse, DicfuseManager},
manager::ScorpioManager,
util::config as sconfig,
};
mod async_io;
mod inode_alloc;
#[allow(unused)]
#[derive(Clone)]
pub struct MegaFuse {
pub dic: Arc<Dicfuse>,
overlayfs: Arc<Mutex<HashMap<u64, Arc<OverlayFs>>>>, inodes_alloc: InodeAlloc,
}
#[allow(unused)]
impl MegaFuse {
pub async fn new() -> Self {
Self {
dic: DicfuseManager::global().await,
overlayfs: Arc::new(Mutex::new(HashMap::new())),
inodes_alloc: InodeAlloc::new(),
}
}
pub async fn new_from_manager(manager: &ScorpioManager) -> MegaFuse {
let megafuse = MegaFuse::new().await;
let store_path = sconfig::store_path();
for dir in &manager.works {
let _lower = PathBuf::from(store_path).join(&dir.hash);
megafuse
.overlay_mount(dir.node, &_lower, false, None)
.await
.unwrap();
}
megafuse
}
pub async fn overlay_mount<P: AsRef<Path>>(
&self,
inode: u64,
store_path: P,
need_cl: bool, cl_link: Option<&str>,
) -> std::io::Result<()> {
let lower = store_path.as_ref().join("lower");
let upper = store_path.as_ref().join("upper");
let mut lowerdir = vec![lower];
if need_cl {
if let Some(link) = cl_link {
let cl_path = store_path.as_ref().join("cl").join(link);
std::fs::create_dir_all(&cl_path)?;
lowerdir.insert(0, cl_path);
}
}
let upperdir = upper;
let config = config::Config {
mountpoint: PathBuf::new(),
do_import: true,
..Default::default()
};
let mut lower_layers = Vec::new();
for lower in &lowerdir {
let lower_path = Path::new(lower);
if lower_path.exists() {
let layer = new_passthroughfs_layer(PassthroughArgs {
root_dir: lower.clone(),
mapping: None::<String>,
})
.await?;
lower_layers.push(Arc::new(layer));
} else {
std::fs::create_dir_all(lower_path)?;
let layer = new_passthroughfs_layer(PassthroughArgs {
root_dir: lower.clone(),
mapping: None::<String>,
})
.await?;
lower_layers.push(Arc::new(layer));
}
}
let upper_path = Path::new(&upperdir);
if !upper_path.exists() {
std::fs::create_dir_all(&upperdir)?;
} else {
let entries = std::fs::read_dir(&upperdir)?;
for entry in entries {
let entry = entry?;
if entry.file_type()?.is_dir() {
std::fs::remove_dir_all(entry.path())?;
} else {
std::fs::remove_file(entry.path())?;
}
}
}
let upper_layer = Arc::new(
new_passthroughfs_layer(PassthroughArgs {
root_dir: upperdir.clone(),
mapping: None::<String>,
})
.await?,
);
let overlayfs = OverlayFs::new(Some(upper_layer), lower_layers, config, inode)?;
self.overlayfs
.lock()
.await
.insert(inode, Arc::new(overlayfs));
self.after_mount_new().await;
Ok(())
}
pub async fn remove_cl_layer_by_cl_link<P: AsRef<Path>>(
&self,
store_path: P,
cl_link: &str,
) -> std::io::Result<()> {
let cl_path = store_path.as_ref().join("cl").join(cl_link);
if cl_path.exists() {
std::fs::remove_dir_all(&cl_path)?;
}
Ok(())
}
pub async fn overlay_umount_byinode(&self, inode: u64) -> std::io::Result<()> {
if !self.is_mount(inode).await {
return Err(Error::new(
std::io::ErrorKind::NotFound,
"Overlay filesystem not mounted",
));
}
self.overlayfs.lock().await.remove(&inode);
Ok(())
}
pub async fn overlay_umount_bypath(&self, path: &str) -> std::io::Result<()> {
let item = self.dic.store.get_by_path(path).await?;
let inode = item.get_inode();
self.overlay_umount_byinode(inode).await
}
pub async fn get_inode(&self, path: &str) -> std::io::Result<u64> {
let item = self.dic.store.get_by_path(path).await?;
Ok(item.get_inode())
}
pub async fn is_mount(&self, inode: u64) -> bool {
self.overlayfs.lock().await.get(&inode).is_some()
}
pub async fn after_mount_new(&self) {
self.inodes_alloc.clear().await;
let map_lock = &self.overlayfs.lock().await;
for (inode, ovl_fs) in map_lock.iter() {
let inode_batch = self.inodes_alloc.alloc_inode(*inode).await;
ovl_fs.extend_inode_alloc(inode_batch).await;
let _ = ovl_fs.init(Request::default()).await;
}
}
}