diff --git a/clients/filesystem-fuse/Makefile b/clients/filesystem-fuse/Makefile index 86dd2f22152..23ebd9e7004 100644 --- a/clients/filesystem-fuse/Makefile +++ b/clients/filesystem-fuse/Makefile @@ -62,6 +62,9 @@ doc-test: unit-test: doc-test cargo test --no-fail-fast --lib --all-features --workspace +test-it: + cargo test --test fuse_test + test-fuse-it: @bash ./tests/bin/run_fuse_testers.sh test diff --git a/clients/filesystem-fuse/src/filesystem.rs b/clients/filesystem-fuse/src/filesystem.rs index dcf35f8ebca..94d27c7074d 100644 --- a/clients/filesystem-fuse/src/filesystem.rs +++ b/clients/filesystem-fuse/src/filesystem.rs @@ -297,7 +297,7 @@ pub trait FileWriter: Sync + Send { #[cfg(test)] pub(crate) mod tests { use super::*; - use libc::{O_APPEND, O_CREAT, O_RDONLY}; + use libc::{O_CREAT, O_RDONLY, O_RDWR}; use std::collections::HashMap; use std::path::Component; @@ -548,7 +548,7 @@ pub(crate) mod tests { async fn test_create_file(&mut self, root_file_id: u64, name: &OsStr) -> FileHandle { let file = self .fs - .create_file(root_file_id, name, (O_CREAT | O_APPEND) as u32) + .create_file(root_file_id, name, (O_CREAT | O_RDWR) as u32) .await; assert!(file.is_ok()); let file = file.unwrap(); diff --git a/clients/filesystem-fuse/src/memory_filesystem.rs b/clients/filesystem-fuse/src/memory_filesystem.rs index f56e65ea33a..84ff162a8bd 100644 --- a/clients/filesystem-fuse/src/memory_filesystem.rs +++ b/clients/filesystem-fuse/src/memory_filesystem.rs @@ -91,7 +91,7 @@ impl PathFileSystem for MemoryFileSystem { Ok(results) } - async fn open_file(&self, path: &Path, _flags: OpenFileFlags) -> Result { + async fn open_file(&self, path: &Path, flags: OpenFileFlags) -> Result { let file_stat = self.stat(path).await?; let mut opened_file = OpenedFile::new(file_stat); match opened_file.file_stat.kind { @@ -105,8 +105,18 @@ impl PathFileSystem for MemoryFileSystem { .unwrap() .data .clone(); - opened_file.reader = Some(Box::new(MemoryFileReader { data: data.clone() })); - opened_file.writer = Some(Box::new(MemoryFileWriter { data: data })); + if flags.is_read() { + + opened_file.reader = Some(Box::new(MemoryFileReader { data: data.clone() })); + } + if flags.is_write() || flags.is_append() { + opened_file.writer = Some(Box::new(MemoryFileWriter { data: data.clone() })); + } + + if flags.is_truncate() { + let mut data = data.lock().unwrap(); + data.clear(); + } Ok(opened_file) } _ => Err(Errno::from(libc::EBADF)), @@ -117,27 +127,19 @@ impl PathFileSystem for MemoryFileSystem { self.open_file(path, flags).await } - async fn create_file(&self, path: &Path, _flags: OpenFileFlags) -> Result { - let mut file_map = self.file_map.write().unwrap(); - if file_map.contains_key(path) { + async fn create_file(&self, path: &Path, flags: OpenFileFlags) -> Result { + if self.file_map.read().unwrap().contains_key(path) && flags.is_exclusive() { return Err(Errno::from(libc::EEXIST)); } - let mut opened_file = OpenedFile::new(FileStat::new_file_filestat_with_path(path, 0)); - - let data = Arc::new(Mutex::new(Vec::new())); - file_map.insert( - opened_file.file_stat.path.clone(), + self.file_map.write().unwrap().insert( + path.to_path_buf(), MemoryFile { kind: RegularFile, - data: data.clone(), + data: Arc::new(Mutex::new(Vec::new())), }, ); - - opened_file.reader = Some(Box::new(MemoryFileReader { data: data.clone() })); - opened_file.writer = Some(Box::new(MemoryFileWriter { data: data })); - - Ok(opened_file) + self.open_file(path, flags).await } async fn create_dir(&self, path: &Path) -> Result { diff --git a/clients/filesystem-fuse/src/open_dal_filesystem.rs b/clients/filesystem-fuse/src/open_dal_filesystem.rs index d32b014d1f0..92202c968f5 100644 --- a/clients/filesystem-fuse/src/open_dal_filesystem.rs +++ b/clients/filesystem-fuse/src/open_dal_filesystem.rs @@ -120,7 +120,11 @@ impl PathFileSystem for OpenDalFileSystem { .map_err(opendal_error_to_errno)?; file.reader = Some(Box::new(FileReaderImpl { reader })); } - if flags.is_write() || flags.is_create() || flags.is_append() || flags.is_truncate() { + if !flags.is_create() && flags.is_append() { + return Err(Errno::from(libc::EBADF)); + } + + if flags.is_write() || flags.is_truncate() { let writer = self .op .writer_with(&file_name) diff --git a/clients/filesystem-fuse/tests/fuse_test.rs b/clients/filesystem-fuse/tests/fuse_test.rs index 41e385c49f1..37800f4388d 100644 --- a/clients/filesystem-fuse/tests/fuse_test.rs +++ b/clients/filesystem-fuse/tests/fuse_test.rs @@ -22,7 +22,8 @@ use gvfs_fuse::config::AppConfig; use gvfs_fuse::RUN_TEST_WITH_FUSE; use gvfs_fuse::{gvfs_mount, gvfs_unmount, test_enable_with}; use log::{error, info}; -use std::fs::File; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Seek, SeekFrom, Write}; use std::path::Path; use std::sync::Arc; use std::thread::sleep; @@ -89,11 +90,6 @@ impl Drop for FuseTest { fn test_fuse_with_memory_fs() { tracing_subscriber::fmt().init(); - panic::set_hook(Box::new(|info| { - error!("A panic occurred: {:?}", info); - process::exit(1); - })); - let mount_point = "target/gvfs"; let _ = fs::create_dir_all(mount_point); @@ -104,26 +100,31 @@ fn test_fuse_with_memory_fs() { }; test.setup(); - test_fuse_filesystem(mount_point); + + let test_dir = Path::new(&test.mount_point).join("test_dir"); + run_tests(&test_dir); +} + +fn run_tests(test_dir: &Path) { + fs::create_dir_all(test_dir).expect("Failed to create test dir"); + test_fuse_filesystem(test_dir); + test_big_file(test_dir); + test_open_file_flag(test_dir); } #[test] fn fuse_it_test_fuse() { test_enable_with!(RUN_TEST_WITH_FUSE); + let mount_point = Path::new("target/gvfs"); + let test_dir = mount_point.join("test_dir"); - test_fuse_filesystem("target/gvfs/gvfs_test"); + run_tests(&test_dir); } -fn test_fuse_filesystem(mount_point: &str) { +fn test_fuse_filesystem(test_path: &Path) { info!("Test startup"); - let base_path = Path::new(mount_point); - - if !file_exists(base_path) { - fs::create_dir_all(base_path).expect("Failed to create test dir"); - } - //test create file - let test_file = base_path.join("test_create"); + let test_file = test_path.join("test_create"); let file = File::create(&test_file).expect("Failed to create file"); assert!(file.metadata().is_ok(), "Failed to get file metadata"); assert!(file_exists(&test_file)); @@ -140,16 +141,16 @@ fn test_fuse_filesystem(mount_point: &str) { assert!(!file_exists(&test_file)); //test create directory - let test_dir = base_path.join("test_dir"); + let test_dir = test_path.join("test_dir"); fs::create_dir(&test_dir).expect("Failed to create directory"); //test create file in directory - let test_file = base_path.join("test_dir/test_file"); + let test_file = test_path.join("test_dir/test_file"); let file = File::create(&test_file).expect("Failed to create file"); assert!(file.metadata().is_ok(), "Failed to get file metadata"); //test write file in directory - let test_file = base_path.join("test_dir/test_read"); + let test_file = test_path.join("test_dir/test_read"); fs::write(&test_file, "read test").expect("Failed to write file"); //test read file in directory @@ -167,6 +168,121 @@ fn test_fuse_filesystem(mount_point: &str) { info!("Success test"); } +#[allow(clippy::needless_range_loop)] +fn test_big_file(test_dir: &Path) { + if !file_exists(test_dir) { + fs::create_dir_all(test_dir).expect("Failed to create test dir"); + } + + let test_file = test_dir.join("test_big_file"); + let mut file = File::create(&test_file).expect("Failed to create file"); + assert!(file.metadata().is_ok(), "Failed to get file metadata"); + assert!(file_exists(&test_file)); + + let round_size: usize = 1024 * 1024; + let round: u8 = 10; + + for i in 0..round { + let mut content = vec![0; round_size]; + for j in 0..round_size { + content[j] = (i as usize + j) as u8; + } + + file.write_all(&content).expect("Failed to write file"); + } + file.flush().expect("Failed to flush file"); + + file = File::open(&test_file).expect("Failed to open file"); + for i in 0..round { + let mut buffer = vec![0; round_size]; + file.read_exact(&mut buffer).unwrap(); + + for j in 0..round_size { + assert_eq!(buffer[j], (i as usize + j) as u8, "File content mismatch"); + } + } + + fs::remove_file(&test_file).expect("Failed to delete file"); + assert!(!file_exists(&test_file)); +} + +fn test_open_file_flag(test_dir: &Path) { + // test open file with read and write create flag + let file_path = test_dir.join("test_open_file"); + let mut file = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open(&file_path) + .expect("Failed to open file"); + // test file offset is 0 + let offset = file.stream_position().expect("Failed to seek file"); + assert_eq!(offset, 0, "File offset mismatch"); + + // test write can be done + let write_content = "write content"; + file.write_all(write_content.as_bytes()) + .expect("Failed to write file"); + let mut content = vec![0; write_content.len()]; + + // test read can be done + file.seek(SeekFrom::Start(0)).expect("Failed to seek file"); + file.read_exact(&mut content).expect("Failed to read file"); + assert_eq!(content, write_content.as_bytes(), "File content mismatch"); + + // test open file with read flag + let mut file = OpenOptions::new() + .read(true) + .open(&file_path) + .expect("Failed to open file"); + // test reaad can be done + let mut content = vec![0; write_content.len()]; + file.read_exact(&mut content).expect("Failed to read file"); + assert_eq!(content, write_content.as_bytes(), "File content mismatch"); + + // test write can be have error + let result = file.write_all(write_content.as_bytes()); + if let Err(e) = result { + assert_eq!(e.to_string(), "Bad file descriptor (os error 9)"); + } + + // test open file with truncate file + // test file size is not 0 + let old_file_size = file.metadata().expect("Failed to get file metadata").len(); + assert_eq!(old_file_size, write_content.len() as u64); + + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .open(&file_path) + .expect("Failed to open file"); + // validate file size is 0 + let file_size = file.metadata().expect("Failed to get file metadata").len(); + assert_eq!(file_size, 0, "File size mismatch"); + // validate file offset is 0 + let offset = file.stream_position().expect("Failed to seek file"); + assert_eq!(offset, 0, "File offset mismatch"); + + file.write_all(write_content.as_bytes()) + .expect("Failed to write file"); + + // test open file with append flag + let mut file = OpenOptions::new() + .append(true) + .open(&file_path) + .expect("Failed to open file"); + // test append + file.write_all(write_content.as_bytes()) + .expect("Failed to write file"); + let file_len = file.metadata().expect("Failed to get file metadata").len(); + // validate file size is 2 * write_content.len() + assert_eq!( + file_len, + 2 * write_content.len() as u64, + "File size mismatch" + ); +} + fn file_exists>(path: P) -> bool { fs::metadata(path).is_ok() }