Handle files as tree on fuse side
This commit is contained in:
+118
-44
@@ -18,8 +18,8 @@ use notify::{Event, EventKind, RecursiveMode, Watcher};
|
||||
use sea_orm::entity::prelude::*;
|
||||
use twox_hash::XxHash64;
|
||||
|
||||
use crate::file_watcher::FileWatcher;
|
||||
use crate::music_metadata::MusicMetadata;
|
||||
use crate::{file_watcher::FileWatcher, music_metadata};
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
enum FileType {
|
||||
@@ -30,6 +30,7 @@ enum FileType {
|
||||
#[derive(Debug)]
|
||||
struct LocalItem {
|
||||
inode: INodeNo,
|
||||
parent_inode: INodeNo,
|
||||
name: String,
|
||||
original_path: PathBuf,
|
||||
local_path: PathBuf,
|
||||
@@ -179,6 +180,7 @@ impl LocalOrigin {
|
||||
|
||||
let local_root = LocalItem {
|
||||
inode: INodeNo::ROOT,
|
||||
parent_inode: INodeNo::ROOT,
|
||||
name: "/".to_string(),
|
||||
original_path: source.to_path_buf(),
|
||||
local_path: destination.to_path_buf(),
|
||||
@@ -212,10 +214,6 @@ impl LocalOrigin {
|
||||
} else {
|
||||
FileType::File
|
||||
};
|
||||
|
||||
let mut local_path = PathBuf::new();
|
||||
local_path.push(name.clone());
|
||||
|
||||
let music_metadata =
|
||||
if item_path.extension().and_then(std::ffi::OsStr::to_str) == Some("flac") {
|
||||
MusicMetadata::parse_music_metadata(&item_path)
|
||||
@@ -223,9 +221,19 @@ impl LocalOrigin {
|
||||
None
|
||||
};
|
||||
|
||||
let mut local_path = PathBuf::new();
|
||||
if music_metadata.is_some() {
|
||||
let music_metadata = music_metadata.clone().unwrap();
|
||||
local_path.push(music_metadata.artist.join("-"));
|
||||
local_path.push(music_metadata.album);
|
||||
}
|
||||
local_path.push(name.clone());
|
||||
|
||||
let inode = INodeNo(metadata.ino());
|
||||
let parent_inode = LocalOrigin::ensure_virtual_dirs(&local_path, source, map);
|
||||
let local_item = LocalItem {
|
||||
inode,
|
||||
parent_inode,
|
||||
name,
|
||||
original_path: item_path.clone(),
|
||||
local_path,
|
||||
@@ -281,6 +289,61 @@ impl LocalOrigin {
|
||||
FileType::File => fuser::FileType::RegularFile,
|
||||
};
|
||||
}
|
||||
|
||||
fn virtual_inode(path: &str) -> INodeNo {
|
||||
let mut hasher = XxHash64::with_seed(5678);
|
||||
hasher.write(path.as_bytes());
|
||||
INodeNo(hasher.finish() | (1u64 << 63))
|
||||
}
|
||||
|
||||
fn ensure_virtual_dirs(
|
||||
local_path: &Path,
|
||||
source: &Path,
|
||||
map: &mut BTreeMap<INodeNo, LocalItem>,
|
||||
) -> INodeNo {
|
||||
let components: Vec<_> = local_path.components().collect();
|
||||
|
||||
if components.len() <= 1 {
|
||||
return INodeNo::ROOT;
|
||||
}
|
||||
|
||||
let mut current_parent = INodeNo::ROOT;
|
||||
let mut current_path = String::new();
|
||||
|
||||
for component in &components[..components.len() - 1] {
|
||||
let comp_str = component.as_os_str().to_str().unwrap_or_default();
|
||||
if !current_path.is_empty() {
|
||||
current_path.push('/');
|
||||
}
|
||||
current_path.push_str(comp_str);
|
||||
|
||||
let virt_ino = LocalOrigin::virtual_inode(¤t_path);
|
||||
|
||||
if !map.contains_key(&virt_ino) {
|
||||
let metadata = fs::metadata(source).unwrap();
|
||||
let virt_item = LocalItem {
|
||||
inode: virt_ino,
|
||||
parent_inode: current_parent,
|
||||
name: comp_str.to_string(),
|
||||
original_path: source.to_path_buf(),
|
||||
local_path: PathBuf::from(¤t_path),
|
||||
file_type: FileType::Directory,
|
||||
metadata,
|
||||
music_metadata: None,
|
||||
hash: 0,
|
||||
};
|
||||
let virt_item = LocalItem {
|
||||
hash: virt_item.compute_hash(),
|
||||
..virt_item
|
||||
};
|
||||
map.insert(virt_ino, virt_item);
|
||||
}
|
||||
|
||||
current_parent = virt_ino;
|
||||
}
|
||||
|
||||
return current_parent;
|
||||
}
|
||||
}
|
||||
|
||||
impl Filesystem for LocalOrigin {
|
||||
@@ -313,46 +376,40 @@ impl Filesystem for LocalOrigin {
|
||||
) {
|
||||
println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
|
||||
|
||||
if ino == INodeNo::ROOT {
|
||||
if offset == 0 {
|
||||
let _ = reply.add(
|
||||
INodeNo::ROOT,
|
||||
0,
|
||||
fuser::FileType::Directory,
|
||||
&Path::new("."),
|
||||
);
|
||||
let _ = reply.add(
|
||||
INodeNo::ROOT,
|
||||
1,
|
||||
fuser::FileType::Directory,
|
||||
&Path::new(".."),
|
||||
);
|
||||
let files = self.files.lock().unwrap();
|
||||
|
||||
let parent_inode = match files.get(&ino) {
|
||||
Some(dir) => dir.parent_inode,
|
||||
None => {
|
||||
reply.error(Errno::ENOENT);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO fix unwrap
|
||||
for (i, (key, value)) in self
|
||||
.files
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|(key, _)| **key != INodeNo::ROOT)
|
||||
.skip(offset as usize)
|
||||
.enumerate()
|
||||
{
|
||||
let offset: u64 = 2 + i as u64;
|
||||
let file_type: fuser::FileType = LocalOrigin::file_type(value.file_type);
|
||||
let path = &value.name;
|
||||
|
||||
let buffer_full: bool = reply.add(*key, offset, file_type, path);
|
||||
|
||||
if buffer_full {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reply.ok();
|
||||
} else {
|
||||
reply.error(Errno::ENOSYS);
|
||||
if offset == 0 {
|
||||
let _ = reply.add(ino, 0, fuser::FileType::Directory, &Path::new("."));
|
||||
let _ = reply.add(
|
||||
parent_inode,
|
||||
1,
|
||||
fuser::FileType::Directory,
|
||||
&Path::new(".."),
|
||||
);
|
||||
}
|
||||
|
||||
for (i, (key, value)) in files
|
||||
.iter()
|
||||
.filter(|(_, v)| v.parent_inode == ino && v.inode != ino)
|
||||
.skip(offset as usize)
|
||||
.enumerate()
|
||||
{
|
||||
let entry_offset: u64 = 2 + i as u64;
|
||||
let file_type: fuser::FileType = LocalOrigin::file_type(value.file_type);
|
||||
let buffer_full: bool = reply.add(*key, entry_offset, file_type, &value.name);
|
||||
if buffer_full {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reply.ok();
|
||||
}
|
||||
|
||||
fn lookup(
|
||||
@@ -369,7 +426,7 @@ impl Filesystem for LocalOrigin {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|item| item.1.name == name.to_str().unwrap())
|
||||
.find(|item| item.1.parent_inode == parent && item.1.name == name.to_str().unwrap())
|
||||
{
|
||||
Some(item) => {
|
||||
let ttl = Duration::new(1, 0);
|
||||
@@ -471,11 +528,28 @@ impl From<&LocalItem> for ActiveModel {
|
||||
|
||||
impl From<(LocalItemEntity, fs::Metadata)> for LocalItem {
|
||||
fn from((entity, metadata): (LocalItemEntity, fs::Metadata)) -> Self {
|
||||
let local_path = PathBuf::from(&entity.local_path);
|
||||
let parent_inode = {
|
||||
let components: Vec<_> = local_path.components().collect();
|
||||
if components.len() <= 1 {
|
||||
INodeNo::ROOT
|
||||
} else {
|
||||
let mut parent_path = String::new();
|
||||
for (i, comp) in components[..components.len() - 1].iter().enumerate() {
|
||||
if i > 0 {
|
||||
parent_path.push('/');
|
||||
}
|
||||
parent_path.push_str(comp.as_os_str().to_str().unwrap_or_default());
|
||||
}
|
||||
LocalOrigin::virtual_inode(&parent_path)
|
||||
}
|
||||
};
|
||||
let local_item = LocalItem {
|
||||
inode: INodeNo(entity.inode as u64),
|
||||
parent_inode,
|
||||
name: entity.name,
|
||||
original_path: PathBuf::from(entity.original_path),
|
||||
local_path: PathBuf::from(entity.local_path),
|
||||
local_path,
|
||||
file_type: match entity.file_type.as_str() {
|
||||
"directory" => FileType::Directory,
|
||||
_ => FileType::File,
|
||||
|
||||
@@ -7,12 +7,12 @@ use symphonia::core::{
|
||||
probe::Hint,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct MusicMetadata {
|
||||
artist: Vec<String>,
|
||||
album: String,
|
||||
track_number: i32,
|
||||
track_title: String,
|
||||
pub artist: Vec<String>,
|
||||
pub album: String,
|
||||
pub track_number: i32,
|
||||
pub track_title: String,
|
||||
}
|
||||
|
||||
impl MusicMetadata {
|
||||
|
||||
Reference in New Issue
Block a user