Skip to content

Commit 38f4e1e

Browse files
claudecberner
authored andcommitted
Enforce the kernel's 4096-byte minimum in set_max_write()
KernelConfig::set_max_write() documents that an out-of-range argument is rejected with the nearest value which will succeed, but it only checked the upper bound. Values below 4096 were accepted even though the kernel clamps max_write to at least 4096 (process_init_reply() in fs/fuse/inode.c), so the setting was silently ineffective: the filesystem could still receive write requests of up to 4096 bytes, larger than the limit it had configured. Reject values below a new MIN_WRITE_SIZE constant with that nearest valid value, replacing the previous zero-only check. Fixes #327 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TfPeUUeQKYjo26Y9mQG9sA
1 parent ae948ab commit 38f4e1e

3 files changed

Lines changed: 48 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# FUSE for Rust - Changelog
22

33
## Unreleased
4+
* `KernelConfig::set_max_write()` now rejects values below 4096 with the nearest valid value,
5+
per its documented contract (#327). The kernel clamps `max_write` to at least 4096, so a
6+
smaller value was accepted but ineffective: write requests of up to 4096 bytes still arrived
47
* Fix inverted mounted-check during session teardown: after the filesystem had already been
58
unmounted externally, fuser would attempt to unmount the mountpoint again, which could
69
unmount an unrelated filesystem mounted at the same path in the meantime

src/lib.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub use crate::reply::ReplyXattr;
7676
pub use crate::request_param::Request;
7777
pub use crate::session::BackgroundSession;
7878
use crate::session::MAX_WRITE_SIZE;
79+
use crate::session::MIN_WRITE_SIZE;
7980
pub use crate::session::Session;
8081
pub use crate::session::SessionACL;
8182
pub use crate::session::SessionUnmounter;
@@ -295,10 +296,13 @@ impl KernelConfig {
295296
///
296297
/// On success returns the previous value.
297298
/// # Errors
298-
/// If the argument is too large, returns the nearest value which will succeed.
299+
/// If the argument is too small or too large, returns the nearest value which will succeed.
299300
pub fn set_max_write(&mut self, value: u32) -> Result<u32, u32> {
300-
if value == 0 {
301-
return Err(1);
301+
// The kernel clamps max_write below 4096 up to 4096 (process_init_reply()
302+
// in fs/fuse/inode.c), so a smaller advertised value would not take
303+
// effect: write requests of up to 4096 bytes would still arrive
304+
if value < MIN_WRITE_SIZE as u32 {
305+
return Err(MIN_WRITE_SIZE as u32);
302306
}
303307
if value > MAX_WRITE_SIZE as u32 {
304308
return Err(MAX_WRITE_SIZE as u32);
@@ -1072,3 +1076,36 @@ pub fn spawn_mount<'a, FS: Filesystem + Send + 'static + 'a, P: AsRef<Path>>(
10721076
) -> io::Result<BackgroundSession> {
10731077
Session::new(filesystem, mountpoint.as_ref(), options).and_then(session::Session::spawn)
10741078
}
1079+
1080+
#[cfg(test)]
1081+
mod tests {
1082+
use super::*;
1083+
1084+
#[test]
1085+
fn kernel_config_set_max_write_bounds() {
1086+
let mut config = KernelConfig::new(InitFlags::empty(), 65536, Version(7, 31));
1087+
// Values the kernel would not honor (it clamps max_write below 4096 up
1088+
// to 4096) are rejected with the nearest value that will take effect
1089+
assert_eq!(config.set_max_write(0), Err(MIN_WRITE_SIZE as u32));
1090+
assert_eq!(
1091+
config.set_max_write(MIN_WRITE_SIZE as u32 - 1),
1092+
Err(MIN_WRITE_SIZE as u32)
1093+
);
1094+
// Values beyond the session's read buffer are rejected likewise
1095+
assert_eq!(
1096+
config.set_max_write(MAX_WRITE_SIZE as u32 + 1),
1097+
Err(MAX_WRITE_SIZE as u32)
1098+
);
1099+
// Rejected calls must leave the configured value unchanged
1100+
assert_eq!(config.max_write, MAX_WRITE_SIZE as u32);
1101+
// Both bounds themselves are accepted, returning the previous value
1102+
assert_eq!(
1103+
config.set_max_write(MIN_WRITE_SIZE as u32),
1104+
Ok(MAX_WRITE_SIZE as u32)
1105+
);
1106+
assert_eq!(
1107+
config.set_max_write(MAX_WRITE_SIZE as u32),
1108+
Ok(MIN_WRITE_SIZE as u32)
1109+
);
1110+
}
1111+
}

src/session.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ use crate::request::RequestWithSender;
5454
/// and 128k on other systems.
5555
pub(crate) const MAX_WRITE_SIZE: usize = 16 * 1024 * 1024;
5656

57+
/// The minimum write size the kernel will honor: process_init_reply() in the
58+
/// kernel's fs/fuse/inode.c clamps a smaller negotiated max_write up to 4096,
59+
/// so advertising less would not stop larger write requests from arriving.
60+
pub(crate) const MIN_WRITE_SIZE: usize = 4096;
61+
5762
#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
5863
/// How requests should be filtered based on the calling UID.
5964
pub enum SessionACL {

0 commit comments

Comments
 (0)