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.
This commit is contained in:
Zhengchao An
2026-07-06 22:09:05 +08:00
committed by GitHub
parent 8ffe230a89
commit 5c67c508cb
+29 -1
View File
@@ -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());
}
}