diff --git a/crates/filemeta/src/filemeta.rs b/crates/filemeta/src/filemeta.rs index 80e5f4653..9f916dfec 100644 --- a/crates/filemeta/src/filemeta.rs +++ b/crates/filemeta/src/filemeta.rs @@ -1133,6 +1133,32 @@ mod test { assert!(fi.is_latest); } + #[test] + fn test_issue_2434_legacy_meta_v2_pool_compatibility() { + let data = create_issue_2434_legacy_meta_v2_pool_xlmeta().expect("Failed to load issue #2434 pool fixture"); + let (major, minor, header_ver, meta_ver) = FileMeta::read_format_versions(&data).unwrap(); + assert_eq!((major, minor, header_ver, meta_ver), (1, 3, 3, 2)); + + let fm = FileMeta::load(&data).expect("Failed to parse legacy issue #2434 pool xl.meta"); + assert_eq!(fm.meta_ver, 2); + assert_eq!(fm.versions.len(), 1); + assert_eq!(fm.versions[0].header.version_type, VersionType::Object); + + let fi = fm + .into_fileinfo(".rustfs.sys", "pool.bin", "", true, false, true) + .expect("Failed to extract file info from legacy issue #2434 pool xl.meta"); + assert_eq!(fi.size, 48); + assert_eq!(fi.num_versions, 1); + assert_eq!(fi.version_id, None); + assert_eq!(fi.metadata.get("etag").map(String::as_str), Some("8d270d7a184cfa30cc0bf09ea74fd964")); + assert_eq!( + fi.data_dir.map(|id| id.to_string()).as_deref(), + Some("2bcefaca-44dd-4f01-a79e-63eeb0dda396") + ); + assert!(fi.uses_legacy_checksum); + assert!(fi.is_latest); + } + #[test] fn test_legacy_v1_object_xlmeta_compatibility() { let data = create_legacy_v1_object_xlmeta().expect("Failed to create legacy v1 object xl.meta"); diff --git a/crates/filemeta/src/filemeta/version.rs b/crates/filemeta/src/filemeta/version.rs index 06b609545..0a5eeb33c 100644 --- a/crates/filemeta/src/filemeta/version.rs +++ b/crates/filemeta/src/filemeta/version.rs @@ -54,6 +54,62 @@ fn read_msgp_bin(rd: &mut R) -> Result> { Ok(buf) } +fn deserialize_legacy_uuid_bytes<'de, D>(deserializer: D) -> std::result::Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + struct LegacyUuidBytesVisitor; + + impl<'de> serde::de::Visitor<'de> for LegacyUuidBytesVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("nil or binary UUID bytes") + } + + fn visit_none(self) -> std::result::Result + where + E: serde::de::Error, + { + Ok(Vec::new()) + } + + fn visit_unit(self) -> std::result::Result + where + E: serde::de::Error, + { + Ok(Vec::new()) + } + + fn visit_bytes(self, value: &[u8]) -> std::result::Result + where + E: serde::de::Error, + { + Ok(value.to_vec()) + } + + fn visit_byte_buf(self, value: Vec) -> std::result::Result + where + E: serde::de::Error, + { + Ok(value) + } + + fn visit_seq(self, mut seq: A) -> std::result::Result + where + A: serde::de::SeqAccess<'de>, + { + let mut value = Vec::new(); + while let Some(byte) = seq.next_element()? { + value.push(byte); + } + Ok(value) + } + } + + deserializer.deserialize_any(LegacyUuidBytesVisitor) +} + fn decode_msgp_time_payload(ext_type: i8, payload: &[u8]) -> Result { let (secs, nanos) = match (ext_type, payload.len()) { (MSGPACK_TIME_EXT_LEGACY, 12) => { @@ -179,7 +235,9 @@ struct LegacyMetaV2Version { #[derive(Debug, Deserialize)] struct LegacyMetaV2Object { + #[serde(default, deserialize_with = "deserialize_legacy_uuid_bytes")] version_id: Vec, + #[serde(default, deserialize_with = "deserialize_legacy_uuid_bytes")] data_dir: Vec, erasure_algorithm: String, erasure_m: usize, @@ -201,6 +259,7 @@ struct LegacyMetaV2Object { #[derive(Debug, Deserialize)] struct LegacyMetaV2DeleteMarker { + #[serde(default, deserialize_with = "deserialize_legacy_uuid_bytes")] version_id: Vec, mod_time: Option, meta_sys: HashMap>, diff --git a/crates/filemeta/src/test_data.rs b/crates/filemeta/src/test_data.rs index 04d405d7e..704e9d2f8 100644 --- a/crates/filemeta/src/test_data.rs +++ b/crates/filemeta/src/test_data.rs @@ -145,6 +145,11 @@ pub fn create_issue_2265_legacy_meta_v2_config_xlmeta() -> Result> { decode_hex_fixture(include_str!("../tests/fixtures/issue_2265_legacy_meta_v2_config.hex")) } +/// Legacy pool xl.meta captured in issue #2434. Header/meta versions are 3/2. +pub fn create_issue_2434_legacy_meta_v2_pool_xlmeta() -> Result> { + decode_hex_fixture(include_str!("../tests/fixtures/issue_2434_legacy_meta_v2_pool.hex")) +} + fn write_legacy_time(wr: &mut Vec, ts: OffsetDateTime) { wr.push(MSGPACK_EXT8); wr.push(12); diff --git a/crates/filemeta/tests/fixtures/issue_2434_legacy_meta_v2_pool.hex b/crates/filemeta/tests/fixtures/issue_2434_legacy_meta_v2_pool.hex new file mode 100644 index 000000000..e8129f227 --- /dev/null +++ b/crates/filemeta/tests/fixtures/issue_2434_legacy_meta_v2_pool.hex @@ -0,0 +1 @@ +584c322001000300c6000000d6030201c42697c41000000000000000000000000000000000d3184f6c224d6172f1c4040000000001020001c4a994a64f626a656374dc0012c0c4102bcefaca44dd4f01a79e63eeb0dda396ab52656564536f6c6f6d6f6e0100ce00100000019101ab4869676877617948617368910191d92038643237306437613138346366613330636330626630396561373466643936349130913091c4003099cd07e9ccba112f36ce26f10ef10000008081a465746167d9203864323730643761313834636661333063633062663039656137346664393634c000ce66d2a556