Skip to content

Commit c9188e4

Browse files
committed
Add tests for multi-reader FUSE functionality
Add integration tests for the clone_fd and from_fd_initialized APIs: - clone_fd_multi_reader: Verifies clone_fd() creates a valid fd that can be used for multi-reader setups - from_fd_initialized_works: Tests that multiple reader threads can process FUSE requests concurrently using cloned fds
1 parent d69061e commit c9188e4

1 file changed

Lines changed: 164 additions & 1 deletion

File tree

tests/integration_tests.rs

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use fuser::{Filesystem, Session};
1+
use fuser::{Filesystem, Session, SessionACL};
22
use std::rc::Rc;
3+
use std::sync::atomic::{AtomicUsize, Ordering};
4+
use std::sync::Arc;
35
use std::thread;
46
use std::time::Duration;
57
use tempfile::TempDir;
@@ -22,3 +24,164 @@ fn unmount_no_send() {
2224
});
2325
session.run().unwrap();
2426
}
27+
28+
/// Test that clone_fd creates a working file descriptor for multi-reader setups.
29+
#[cfg(target_os = "linux")]
30+
#[test]
31+
fn clone_fd_multi_reader() {
32+
use std::os::fd::AsRawFd;
33+
34+
// Simple filesystem that tracks how many times getattr is called
35+
struct CountingFS {
36+
count: Arc<AtomicUsize>,
37+
}
38+
39+
impl Filesystem for CountingFS {
40+
fn getattr(
41+
&mut self,
42+
_req: &fuser::Request<'_>,
43+
ino: u64,
44+
_fh: Option<u64>,
45+
reply: fuser::ReplyAttr,
46+
) {
47+
self.count.fetch_add(1, Ordering::SeqCst);
48+
if ino == 1 {
49+
// Root directory
50+
reply.attr(
51+
&Duration::from_secs(1),
52+
&fuser::FileAttr {
53+
ino: 1,
54+
size: 0,
55+
blocks: 0,
56+
atime: std::time::UNIX_EPOCH,
57+
mtime: std::time::UNIX_EPOCH,
58+
ctime: std::time::UNIX_EPOCH,
59+
crtime: std::time::UNIX_EPOCH,
60+
kind: fuser::FileType::Directory,
61+
perm: 0o755,
62+
nlink: 2,
63+
uid: 0,
64+
gid: 0,
65+
rdev: 0,
66+
blksize: 4096,
67+
flags: 0,
68+
},
69+
);
70+
} else {
71+
reply.error(libc::ENOENT);
72+
}
73+
}
74+
}
75+
76+
let tmpdir: TempDir = tempfile::tempdir().unwrap();
77+
let count = Arc::new(AtomicUsize::new(0));
78+
79+
let session = Session::new(
80+
CountingFS {
81+
count: count.clone(),
82+
},
83+
tmpdir.path(),
84+
&[],
85+
)
86+
.unwrap();
87+
88+
// Clone the fd - this should succeed
89+
let cloned_fd = session.clone_fd().expect("clone_fd should succeed");
90+
91+
// Verify it's a valid fd (different from the original)
92+
assert!(cloned_fd.as_raw_fd() >= 0);
93+
94+
// Clean up
95+
drop(cloned_fd);
96+
drop(session);
97+
}
98+
99+
/// Test that from_fd_initialized creates a session that can process requests.
100+
#[cfg(target_os = "linux")]
101+
#[test]
102+
fn from_fd_initialized_works() {
103+
use std::sync::Barrier;
104+
105+
// Simple filesystem that responds to getattr
106+
#[derive(Clone)]
107+
struct SimpleFS;
108+
109+
impl Filesystem for SimpleFS {
110+
fn getattr(
111+
&mut self,
112+
_req: &fuser::Request<'_>,
113+
ino: u64,
114+
_fh: Option<u64>,
115+
reply: fuser::ReplyAttr,
116+
) {
117+
if ino == 1 {
118+
reply.attr(
119+
&Duration::from_secs(1),
120+
&fuser::FileAttr {
121+
ino: 1,
122+
size: 0,
123+
blocks: 0,
124+
atime: std::time::UNIX_EPOCH,
125+
mtime: std::time::UNIX_EPOCH,
126+
ctime: std::time::UNIX_EPOCH,
127+
crtime: std::time::UNIX_EPOCH,
128+
kind: fuser::FileType::Directory,
129+
perm: 0o755,
130+
nlink: 2,
131+
uid: 0,
132+
gid: 0,
133+
rdev: 0,
134+
blksize: 4096,
135+
flags: 0,
136+
},
137+
);
138+
} else {
139+
reply.error(libc::ENOENT);
140+
}
141+
}
142+
}
143+
144+
let tmpdir: TempDir = tempfile::tempdir().unwrap();
145+
146+
let mut session = Session::new(SimpleFS, tmpdir.path(), &[]).unwrap();
147+
let mut unmounter = session.unmount_callable();
148+
149+
// Clone fd for second reader
150+
let cloned_fd = session.clone_fd().expect("clone_fd should succeed");
151+
152+
// Barrier to synchronize reader threads
153+
let barrier = Arc::new(Barrier::new(3)); // 2 readers + 1 main thread
154+
155+
// Start second reader in a thread
156+
let barrier_clone = barrier.clone();
157+
let reader_handle = thread::spawn(move || {
158+
let mut reader_session = Session::from_fd_initialized(SimpleFS, cloned_fd, SessionACL::All);
159+
barrier_clone.wait(); // Signal ready
160+
// Run until unmount
161+
let _ = reader_session.run();
162+
});
163+
164+
// Start primary session in a thread
165+
let barrier_clone = barrier.clone();
166+
let session_handle = thread::spawn(move || {
167+
barrier_clone.wait(); // Signal ready
168+
let _ = session.run();
169+
});
170+
171+
// Wait for both readers to be ready
172+
barrier.wait();
173+
174+
// Give readers time to start processing
175+
thread::sleep(Duration::from_millis(100));
176+
177+
// Access the mountpoint - this triggers FUSE requests
178+
let _ = std::fs::metadata(tmpdir.path());
179+
180+
// Unmount to stop the sessions
181+
thread::sleep(Duration::from_millis(100));
182+
unmounter.unmount().unwrap();
183+
184+
// Wait for threads to finish
185+
let _ = session_handle.join();
186+
let _ = reader_handle.join();
187+
}

0 commit comments

Comments
 (0)