cubefs: surface daemon put failures as FUSE EIO via put_checked

Keep CubeBackend::put returning () for source compat across cubesys/cubecrypt/
cube-bench; add put_checked() (default Ok(())) letting DaemonBackend report a
real socket error. FUSE create/write/truncate now call put_record_checked and
map the failure to FsError::WriteFailed -> errno 5 (EIO).

Verified: ./check (fmt+test+clippy) green; canonical ./check mount 57/57.
This commit is contained in:
luulu
2026-08-13 07:50:05 -04:00
parent 314df8e42d
commit f40b44823d
3 changed files with 160 additions and 11 deletions
+38 -3
View File
@@ -24,6 +24,15 @@ use std::collections::HashMap;
pub trait CubeBackend {
/// Store `value` at `key`.
fn put(&mut self, key: Czyx, value: Vec<u8>);
/// Store `value` at `key`, returning `Err` if the write cannot be
/// durably committed (e.g. the daemon socket is unreachable for
/// `DaemonBackend`). The default implementation ignores the result so
/// existing backends stay source-compatible; `DaemonBackend` overrides it.
/// The FUSE write path calls this variant and surfaces `Err` as `EIO`.
fn put_checked(&mut self, key: Czyx, value: Vec<u8>) -> Result<(), String> {
self.put(key, value);
Ok(())
}
/// Fetch the value at `key`, if present.
fn get(&self, key: &Czyx) -> Option<Vec<u8>>;
/// Remove the value at `key`.
@@ -356,7 +365,25 @@ impl<B: CubeBackend> CubeStore<B> {
buf.extend_from_slice(&(hdr_bytes.len() as u32).to_le_bytes());
buf.extend_from_slice(&hdr_bytes);
buf.extend_from_slice(body);
self.backend.put(label, buf);
self.backend.put(label, buf)
}
/// Like [`CubeStore::put_record`] but returns `Err` if the backend cannot
/// durably commit (e.g. `DaemonBackend` with an unreachable socket). The
/// FUSE write path uses this variant so a failed daemon write becomes
/// `EIO` instead of a silently-dropped write.
pub fn put_record_checked(
&mut self,
label: Czyx,
header: &CubeHeader,
body: &[u8],
) -> Result<(), String> {
let hdr_bytes = record_codec::encode_header(header);
let mut buf = Vec::with_capacity(4 + hdr_bytes.len() + body.len());
buf.extend_from_slice(&(hdr_bytes.len() as u32).to_le_bytes());
buf.extend_from_slice(&hdr_bytes);
buf.extend_from_slice(body);
self.backend.put_checked(label, buf)
}
/// Fetch and split a record into `(header, body)`.
@@ -376,10 +403,18 @@ impl<B: CubeBackend> CubeStore<B> {
Some((hdr, body))
}
/// Raw backend access (delegates put/get/delete for non-record payloads).
/// Raw backend write (non-record payloads, e.g. ACL/xattr/volume buckets).
pub fn put_raw(&mut self, key: Czyx, value: Vec<u8>) {
self.backend.put(key, value);
self.backend.put(key, value)
}
/// Like [`CubeStore::put_raw`] but returns `Err` if the backend cannot
/// durably commit. Used by the FUSE write path for best-effort metadata;
/// callers may `.ok()` it or surface the error.
pub fn put_raw_checked(&mut self, key: Czyx, value: Vec<u8>) -> Result<(), String> {
self.backend.put_checked(key, value)
}
/// Raw backend get.
pub fn get_raw(&self, key: &Czyx) -> Option<Vec<u8>> {
self.backend.get(key)