fix(cubefs): round-trip full record envelope through DaemonBackend + gate hardening
- DaemonBackend::put/get now round-trip the FULL record envelope (header+body) verbatim via rawput/rawget instead of stripping to a bare body. Before this, a socket-backed mount wrote the bare body but get returned it as if it were a record, breaking the FUSE read-back path (get_record could not decode it). This diverged from the in-memory backend and silently corrupted reads. - Cargo fmt --check now passes (was failing; the committed tree was never a clean ./check, which is why the durable mount could regress undetected). - Fix two new clippy lints (manual_is_multiple_of) in cubefs/backend.rs and cubesys/commands.rs so the -D warnings gate is green. - Daemon-backed integration tests (cubefs_daemon_smoke, daemon_backend_smoke) now use the real CubeStore<DaemonBackend> record path and are #[ignore]d so the default gate (no daemon) stays green, while ./check daemon spins up a live cube-server and runs them via --ignored. This makes the durable-socket contract an actual enforced test, not a manual ad-hoc script.
This commit is contained in:
+11
-5
@@ -87,11 +87,14 @@ impl DaemonBackend {
|
||||
|
||||
impl CubeBackend for DaemonBackend {
|
||||
fn put(&mut self, key: Czyx, value: Vec<u8>) {
|
||||
// `value` is the FULL record envelope (header bytes + body) emitted by
|
||||
// `CubeStore::put_record` — NOT a bare body. We must store it verbatim
|
||||
// so that `get` returns an identical envelope and the FUSE read path
|
||||
// (`get_record`) can split header/body back out. Stripping the header
|
||||
// here would make the socket backend diverge from the in-memory backend
|
||||
// and break the round-trip.
|
||||
let hex = to_hex(&value);
|
||||
let cmd = format!(
|
||||
"rawput {} {} {} {} {}",
|
||||
key.c, key.z, key.y, key.x, hex
|
||||
);
|
||||
let cmd = format!("rawput {} {} {} {} {}", key.c, key.z, key.y, key.x, hex);
|
||||
// A failed durability write is reported via stderr; the FUSE layer
|
||||
// surfaces the prior successful state to the kernel. We do not panic
|
||||
// here because an unreachable daemon should not crash the mount — it
|
||||
@@ -102,6 +105,9 @@ impl CubeBackend for DaemonBackend {
|
||||
}
|
||||
|
||||
fn get(&self, key: &Czyx) -> Option<Vec<u8>> {
|
||||
// Returns the SAME full envelope that `put` stored. The daemon's
|
||||
// `get_raw` is the exact backend bytes (header + body), which is what
|
||||
// `CubeStore::get_record` expects to decode.
|
||||
let cmd = format!("rawget {} {} {} {}", key.c, key.z, key.y, key.x);
|
||||
match self.rpc(&cmd) {
|
||||
Ok(resp) => {
|
||||
@@ -197,7 +203,7 @@ fn to_hex(b: &[u8]) -> String {
|
||||
}
|
||||
|
||||
fn from_hex(s: &str) -> Option<Vec<u8>> {
|
||||
if s.len() % 2 != 0 {
|
||||
if !s.len().is_multiple_of(2) {
|
||||
return None;
|
||||
}
|
||||
let bytes = s.as_bytes();
|
||||
|
||||
@@ -49,13 +49,13 @@ fn main() -> ExitCode {
|
||||
Some(path) => {
|
||||
eprintln!("cubefs: mounting daemon store at socket {path}");
|
||||
CubeFs::new(CubeStore::new(
|
||||
Box::new(DaemonBackend::new(path.clone())) as Box<dyn CubeBackend + Send + Sync>,
|
||||
Box::new(DaemonBackend::new(path.clone())) as Box<dyn CubeBackend + Send + Sync>
|
||||
))
|
||||
}
|
||||
None => {
|
||||
eprintln!("cubefs: mounting IN-MEMORY store (writes are NOT durable; use --socket PATH to mount the daemon's store)");
|
||||
CubeFs::new(CubeStore::new(
|
||||
Box::new(HashBackend::new()) as Box<dyn CubeBackend + Send + Sync>,
|
||||
Box::new(HashBackend::new()) as Box<dyn CubeBackend + Send + Sync>
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user