Skip to content

Commit e7c8608

Browse files
diqiu50jerryshao
authored andcommitted
[#5886] feat (gvfs-fuse): Implement an in-memory file system (#5915)
### What changes were proposed in this pull request? Implement an in-memory filesystem for testing and validating the FUSE framework. You need to implement the PathFilesystem trait and support basic file and directory operations: ### Why are the changes needed? Fix: #5886 ### Does this PR introduce _any_ user-facing change? No ### How was this patch tested? IT
1 parent 7d01ba6 commit e7c8608

14 files changed

+1170
-242
lines changed

clients/filesystem-fuse/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ fuse3 = { version = "0.8.1", "features" = ["tokio-runtime", "unprivileged"] }
4040
futures-util = "0.3.30"
4141
libc = "0.2.168"
4242
log = "0.4.22"
43+
once_cell = "1.20.2"
4344
tokio = { version = "1.38.0", features = ["full"] }
4445
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
45-

clients/filesystem-fuse/src/default_raw_filesystem.rs

+61-42
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
use crate::filesystem::{FileStat, PathFileSystem, RawFileSystem, Result};
19+
use crate::filesystem::{
20+
FileStat, PathFileSystem, RawFileSystem, Result, INITIAL_FILE_ID, ROOT_DIR_FILE_ID,
21+
ROOT_DIR_PARENT_FILE_ID, ROOT_DIR_PATH,
22+
};
2023
use crate::opened_file::{FileHandle, OpenFileFlags};
2124
use crate::opened_file_manager::OpenedFileManager;
22-
use crate::utils::join_file_path;
2325
use async_trait::async_trait;
2426
use bytes::Bytes;
2527
use fuse3::{Errno, FileType};
2628
use std::collections::HashMap;
29+
use std::ffi::OsStr;
30+
use std::path::{Path, PathBuf};
2731
use std::sync::atomic::AtomicU64;
2832
use tokio::sync::RwLock;
2933

@@ -43,16 +47,11 @@ pub struct DefaultRawFileSystem<T: PathFileSystem> {
4347
}
4448

4549
impl<T: PathFileSystem> DefaultRawFileSystem<T> {
46-
const INITIAL_FILE_ID: u64 = 10000;
47-
const ROOT_DIR_PARENT_FILE_ID: u64 = 1;
48-
const ROOT_DIR_FILE_ID: u64 = 1;
49-
const ROOT_DIR_NAME: &'static str = "";
50-
5150
pub(crate) fn new(fs: T) -> Self {
5251
Self {
5352
file_entry_manager: RwLock::new(FileEntryManager::new()),
5453
opened_file_manager: OpenedFileManager::new(),
55-
file_id_generator: AtomicU64::new(Self::INITIAL_FILE_ID),
54+
file_id_generator: AtomicU64::new(INITIAL_FILE_ID),
5655
fs,
5756
}
5857
}
@@ -70,7 +69,7 @@ impl<T: PathFileSystem> DefaultRawFileSystem<T> {
7069
.ok_or(Errno::from(libc::ENOENT))
7170
}
7271

73-
async fn get_file_entry_by_path(&self, path: &str) -> Option<FileEntry> {
72+
async fn get_file_entry_by_path(&self, path: &Path) -> Option<FileEntry> {
7473
self.file_entry_manager
7574
.read()
7675
.await
@@ -123,12 +122,12 @@ impl<T: PathFileSystem> DefaultRawFileSystem<T> {
123122
Ok(file.file_handle())
124123
}
125124

126-
async fn remove_file_entry_locked(&self, path: &str) {
125+
async fn remove_file_entry_locked(&self, path: &Path) {
127126
let mut file_manager = self.file_entry_manager.write().await;
128127
file_manager.remove(path);
129128
}
130129

131-
async fn insert_file_entry_locked(&self, parent_file_id: u64, file_id: u64, path: &str) {
130+
async fn insert_file_entry_locked(&self, parent_file_id: u64, file_id: u64, path: &Path) {
132131
let mut file_manager = self.file_entry_manager.write().await;
133132
file_manager.insert(parent_file_id, file_id, path);
134133
}
@@ -139,17 +138,17 @@ impl<T: PathFileSystem> RawFileSystem for DefaultRawFileSystem<T> {
139138
async fn init(&self) -> Result<()> {
140139
// init root directory
141140
self.insert_file_entry_locked(
142-
Self::ROOT_DIR_PARENT_FILE_ID,
143-
Self::ROOT_DIR_FILE_ID,
144-
Self::ROOT_DIR_NAME,
141+
ROOT_DIR_PARENT_FILE_ID,
142+
ROOT_DIR_FILE_ID,
143+
Path::new(ROOT_DIR_PATH),
145144
)
146145
.await;
147146
self.fs.init().await
148147
}
149148

150149
async fn get_file_path(&self, file_id: u64) -> Result<String> {
151150
let file_entry = self.get_file_entry(file_id).await?;
152-
Ok(file_entry.path)
151+
Ok(file_entry.path.to_string_lossy().to_string())
153152
}
154153

155154
async fn valid_file_handle_id(&self, file_id: u64, fh: u64) -> Result<()> {
@@ -174,12 +173,15 @@ impl<T: PathFileSystem> RawFileSystem for DefaultRawFileSystem<T> {
174173
Ok(file_stat)
175174
}
176175

177-
async fn lookup(&self, parent_file_id: u64, name: &str) -> Result<FileStat> {
176+
async fn lookup(&self, parent_file_id: u64, name: &OsStr) -> Result<FileStat> {
178177
let parent_file_entry = self.get_file_entry(parent_file_id).await?;
179-
let mut file_stat = self.fs.lookup(&parent_file_entry.path, name).await?;
178+
179+
let path = parent_file_entry.path.join(name);
180+
let mut file_stat = self.fs.stat(&path).await?;
180181
// fill the file id to file stat
181182
self.resolve_file_id_to_filestat(&mut file_stat, parent_file_id)
182183
.await;
184+
183185
Ok(file_stat)
184186
}
185187

@@ -203,11 +205,16 @@ impl<T: PathFileSystem> RawFileSystem for DefaultRawFileSystem<T> {
203205
.await
204206
}
205207

206-
async fn create_file(&self, parent_file_id: u64, name: &str, flags: u32) -> Result<FileHandle> {
208+
async fn create_file(
209+
&self,
210+
parent_file_id: u64,
211+
name: &OsStr,
212+
flags: u32,
213+
) -> Result<FileHandle> {
207214
let parent_file_entry = self.get_file_entry(parent_file_id).await?;
208215
let mut file_without_id = self
209216
.fs
210-
.create_file(&parent_file_entry.path, name, OpenFileFlags(flags))
217+
.create_file(&parent_file_entry.path.join(name), OpenFileFlags(flags))
211218
.await?;
212219

213220
file_without_id.set_file_id(parent_file_id, self.next_file_id());
@@ -226,9 +233,10 @@ impl<T: PathFileSystem> RawFileSystem for DefaultRawFileSystem<T> {
226233
Ok(opened_file_with_file_handle_id.file_handle())
227234
}
228235

229-
async fn create_dir(&self, parent_file_id: u64, name: &str) -> Result<u64> {
236+
async fn create_dir(&self, parent_file_id: u64, name: &OsStr) -> Result<u64> {
230237
let parent_file_entry = self.get_file_entry(parent_file_id).await?;
231-
let mut filestat = self.fs.create_dir(&parent_file_entry.path, name).await?;
238+
let path = parent_file_entry.path.join(name);
239+
let mut filestat = self.fs.create_dir(&path).await?;
232240

233241
filestat.set_file_id(parent_file_id, self.next_file_id());
234242

@@ -243,23 +251,23 @@ impl<T: PathFileSystem> RawFileSystem for DefaultRawFileSystem<T> {
243251
self.fs.set_attr(&file_entry.path, file_stat, true).await
244252
}
245253

246-
async fn remove_file(&self, parent_file_id: u64, name: &str) -> Result<()> {
254+
async fn remove_file(&self, parent_file_id: u64, name: &OsStr) -> Result<()> {
247255
let parent_file_entry = self.get_file_entry(parent_file_id).await?;
248-
self.fs.remove_file(&parent_file_entry.path, name).await?;
256+
let path = parent_file_entry.path.join(name);
257+
self.fs.remove_file(&path).await?;
249258

250259
// remove the file from file entry manager
251-
self.remove_file_entry_locked(&join_file_path(&parent_file_entry.path, name))
252-
.await;
260+
self.remove_file_entry_locked(&path).await;
253261
Ok(())
254262
}
255263

256-
async fn remove_dir(&self, parent_file_id: u64, name: &str) -> Result<()> {
264+
async fn remove_dir(&self, parent_file_id: u64, name: &OsStr) -> Result<()> {
257265
let parent_file_entry = self.get_file_entry(parent_file_id).await?;
258-
self.fs.remove_dir(&parent_file_entry.path, name).await?;
266+
let path = parent_file_entry.path.join(name);
267+
self.fs.remove_dir(&path).await?;
259268

260269
// remove the dir from file entry manager
261-
self.remove_file_entry_locked(&join_file_path(&parent_file_entry.path, name))
262-
.await;
270+
self.remove_file_entry_locked(&path).await;
263271
Ok(())
264272
}
265273

@@ -324,7 +332,7 @@ impl<T: PathFileSystem> RawFileSystem for DefaultRawFileSystem<T> {
324332
struct FileEntry {
325333
file_id: u64,
326334
parent_file_id: u64,
327-
path: String,
335+
path: PathBuf,
328336
}
329337

330338
/// FileEntryManager is manage all the file entries in memory. it is used manger the file relationship and name mapping.
@@ -333,7 +341,7 @@ struct FileEntryManager {
333341
file_id_map: HashMap<u64, FileEntry>,
334342

335343
// file_path_map is a map of file path to file entry.
336-
file_path_map: HashMap<String, FileEntry>,
344+
file_path_map: HashMap<PathBuf, FileEntry>,
337345
}
338346

339347
impl FileEntryManager {
@@ -348,21 +356,21 @@ impl FileEntryManager {
348356
self.file_id_map.get(&file_id).cloned()
349357
}
350358

351-
fn get_file_entry_by_path(&self, path: &str) -> Option<FileEntry> {
359+
fn get_file_entry_by_path(&self, path: &Path) -> Option<FileEntry> {
352360
self.file_path_map.get(path).cloned()
353361
}
354362

355-
fn insert(&mut self, parent_file_id: u64, file_id: u64, path: &str) {
363+
fn insert(&mut self, parent_file_id: u64, file_id: u64, path: &Path) {
356364
let file_entry = FileEntry {
357365
file_id,
358366
parent_file_id,
359-
path: path.to_string(),
367+
path: path.into(),
360368
};
361369
self.file_id_map.insert(file_id, file_entry.clone());
362-
self.file_path_map.insert(path.to_string(), file_entry);
370+
self.file_path_map.insert(path.into(), file_entry);
363371
}
364372

365-
fn remove(&mut self, path: &str) {
373+
fn remove(&mut self, path: &Path) {
366374
if let Some(file) = self.file_path_map.remove(path) {
367375
self.file_id_map.remove(&file.file_id);
368376
}
@@ -372,23 +380,34 @@ impl FileEntryManager {
372380
#[cfg(test)]
373381
mod tests {
374382
use super::*;
383+
use crate::filesystem::tests::TestRawFileSystem;
384+
use crate::memory_filesystem::MemoryFileSystem;
375385

376386
#[test]
377387
fn test_file_entry_manager() {
378388
let mut manager = FileEntryManager::new();
379-
manager.insert(1, 2, "a/b");
389+
manager.insert(1, 2, Path::new("a/b"));
380390
let file = manager.get_file_entry_by_id(2).unwrap();
381391
assert_eq!(file.file_id, 2);
382392
assert_eq!(file.parent_file_id, 1);
383-
assert_eq!(file.path, "a/b");
393+
assert_eq!(file.path, Path::new("a/b"));
384394

385-
let file = manager.get_file_entry_by_path("a/b").unwrap();
395+
let file = manager.get_file_entry_by_path(Path::new("a/b")).unwrap();
386396
assert_eq!(file.file_id, 2);
387397
assert_eq!(file.parent_file_id, 1);
388-
assert_eq!(file.path, "a/b");
398+
assert_eq!(file.path, Path::new("a/b"));
389399

390-
manager.remove("a/b");
400+
manager.remove(Path::new("a/b"));
391401
assert!(manager.get_file_entry_by_id(2).is_none());
392-
assert!(manager.get_file_entry_by_path("a/b").is_none());
402+
assert!(manager.get_file_entry_by_path(Path::new("a/b")).is_none());
403+
}
404+
405+
#[tokio::test]
406+
async fn test_default_raw_file_system() {
407+
let memory_fs = MemoryFileSystem::new().await;
408+
let raw_fs = DefaultRawFileSystem::new(memory_fs);
409+
let _ = raw_fs.init().await;
410+
let mut tester = TestRawFileSystem::new(raw_fs);
411+
tester.test_raw_file_system().await;
393412
}
394413
}

0 commit comments

Comments
 (0)