use std::{io, path::{Path, PathBuf}}; use sha2::{Sha256, Digest}; use klog_types::FileInfo; pub fn files_root(data_dir: &Path) -> PathBuf { data_dir.join("files") } pub fn validate_filename(name: &str) -> bool { !name.is_empty() && name != "." && name != ".." && !name.contains('/') && !name.contains('\\') && !name.contains('\0') } pub async fn list_all_files(data_dir: &Path) -> io::Result> { let root = files_root(data_dir); let mut result = Vec::new(); let mut user_dirs = match tokio::fs::read_dir(&root).await { Ok(d) => d, Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(result), Err(e) => return Err(e), }; while let Some(user_entry) = user_dirs.next_entry().await? { if !user_entry.file_type().await?.is_dir() { continue; } let username = user_entry.file_name().to_string_lossy().to_string(); let user_dir = root.join(&username); let mut files = tokio::fs::read_dir(&user_dir).await?; while let Some(file_entry) = files.next_entry().await? { if !file_entry.file_type().await?.is_file() { continue; } let filename = file_entry.file_name().to_string_lossy().to_string(); let path = user_dir.join(&filename); let sha256 = hash_file(&path).await?; result.push(FileInfo { username: username.clone(), filename, sha256 }); } } Ok(result) } pub async fn hash_file(path: &Path) -> io::Result { let data = tokio::fs::read(path).await?; let mut hasher = Sha256::new(); hasher.update(&data); Ok(hex::encode(hasher.finalize())) } pub async fn read_file(data_dir: &Path, username: &str, filename: &str) -> io::Result> { tokio::fs::read(files_root(data_dir).join(username).join(filename)).await } pub async fn write_file(data_dir: &Path, username: &str, filename: &str, data: &[u8]) -> io::Result<()> { let dir = files_root(data_dir).join(username); tokio::fs::create_dir_all(&dir).await?; tokio::fs::write(dir.join(filename), data).await } pub async fn delete_file(data_dir: &Path, username: &str, filename: &str) -> io::Result { let path = files_root(data_dir).join(username).join(filename); match tokio::fs::remove_file(&path).await { Ok(()) => Ok(true), Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false), Err(e) => Err(e), } }