From 5c67c508cb04b432f4f1011e90df45d01dfb516c Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Mon, 6 Jul 2026 22:09:05 +0800 Subject: [PATCH] fix(filemeta): skip unknown fields when decoding MetaDeleteMarker (backlog#799 B17) (#4317) `MetaDeleteMarker::decode_from` returned an error on any field key it didn't recognize, unlike `MetaObject::decode_from` / `MetaObjectV1::decode_from`, which skip unknown fields. That breaks forward compatibility: a delete marker written by a newer version with an extra key fails to decode on an older binary. Skip the unknown field's value (`skip_msgp_value`) and continue, matching the object decoders. Adds a regression test. Refs backlog#799 (B17), tracked in rustfs/backlog#863. --- crates/filemeta/src/filemeta/version.rs | 30 ++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/filemeta/src/filemeta/version.rs b/crates/filemeta/src/filemeta/version.rs index 2cc46b886..b968a8e5e 100644 --- a/crates/filemeta/src/filemeta/version.rs +++ b/crates/filemeta/src/filemeta/version.rs @@ -2467,7 +2467,11 @@ impl MetaDeleteMarker { } } other => { - return Err(Error::other(format!("unsupported field in MetaDeleteMarker: {other}"))); + // Skip unknown fields for forward compatibility, matching + // MetaObject::decode_from. A newer writer's extra keys must not + // break decoding of a delete marker (backlog#799 B17). + tracing::debug!(field = %other, "MetaDeleteMarker::decode_from: skipping unknown field"); + skip_msgp_value(rd)?; } } } @@ -3617,4 +3621,28 @@ mod tests { assert!(matches!(err, Error::UuidParse(_))); } + + #[test] + fn delete_marker_decode_skips_unknown_fields_for_forward_compat() { + // A newer writer emits the three known fields plus an extra one. Decoding + // must skip the unknown field instead of erroring (backlog#799 B17). + let vid = Uuid::from_u128(0x1234); + let mut buf = Vec::new(); + rmp::encode::write_map_len(&mut buf, 4).unwrap(); + rmp::encode::write_str(&mut buf, "ID").unwrap(); + rmp::encode::write_bin(&mut buf, vid.as_bytes()).unwrap(); + rmp::encode::write_str(&mut buf, "MTime").unwrap(); + rmp::encode::write_sint(&mut buf, 1_700_000_000_000_000_000i64).unwrap(); + rmp::encode::write_str(&mut buf, "MetaSys").unwrap(); + rmp::encode::write_map_len(&mut buf, 0).unwrap(); + // Unknown field a future version added. + rmp::encode::write_str(&mut buf, "FutureField").unwrap(); + rmp::encode::write_str(&mut buf, "ignored").unwrap(); + + let mut dm = MetaDeleteMarker::default(); + dm.decode_from(&mut std::io::Cursor::new(buf)) + .expect("unknown fields must be skipped, not rejected"); + assert_eq!(dm.version_id, Some(vid)); + assert!(dm.mod_time.is_some()); + } }