Skip to content

Commit 5ae84a9

Browse files
zecakehjplatte
authored andcommitted
fix(sled): Fix broken raw redacted state events
A fix for (de)serialization of redacted state events in Ruma made old events undeserializable. Bump the DB version. Signed-off-by: Kévin Commaille <[email protected]>
1 parent 57c3224 commit 5ae84a9

File tree

1 file changed

+121
-12
lines changed

1 file changed

+121
-12
lines changed

crates/matrix-sdk-sled/src/state_store.rs

Lines changed: 121 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use ruma::{
4444
RoomVersionId, UserId,
4545
};
4646
use serde::{de::DeserializeOwned, Serialize};
47+
use serde_json::value::{RawValue as RawJsonValue, Value as JsonValue};
4748
use sled::{
4849
transaction::{ConflictableTransactionError, TransactionError},
4950
Config, Db, Transactional, Tree,
@@ -115,7 +116,7 @@ impl From<SledStoreError> for StoreError {
115116
}
116117
}
117118
}
118-
const DATABASE_VERSION: u8 = 2;
119+
const DATABASE_VERSION: u8 = 3;
119120

120121
const VERSION_KEY: &str = "state-store-version";
121122

@@ -438,17 +439,17 @@ impl SledStateStore {
438439

439440
debug!(old_version, new_version = DATABASE_VERSION, "Upgrading the Sled state store");
440441

441-
if old_version == 1 {
442-
if self.store_cipher.is_some() {
443-
// we stored some fields un-encrypted. Drop them to force re-creation
444-
return Err(SledStoreError::MigrationConflict {
445-
path: self.path.take().expect("Path must exist for a migration to fail"),
446-
old_version: old_version.into(),
447-
new_version: DATABASE_VERSION.into(),
448-
});
449-
}
450-
// no migration to handle
451-
self.set_db_version(2u8)?;
442+
if old_version == 1 && self.store_cipher.is_some() {
443+
// we stored some fields un-encrypted. Drop them to force re-creation
444+
return Err(SledStoreError::MigrationConflict {
445+
path: self.path.take().expect("Path must exist for a migration to fail"),
446+
old_version: old_version.into(),
447+
new_version: DATABASE_VERSION.into(),
448+
});
449+
}
450+
451+
if old_version < 3 {
452+
self.migrate_to_v3()?;
452453
return Ok(());
453454
}
454455

@@ -462,6 +463,54 @@ impl SledStateStore {
462463
})
463464
}
464465

466+
fn v3_fix_tree(&self, tree: &Tree, batch: &mut sled::Batch) -> Result<()> {
467+
fn maybe_fix_json(raw_json: &RawJsonValue) -> Result<Option<JsonValue>> {
468+
let json = raw_json.get();
469+
470+
if json.contains(r#""content":null"#) {
471+
let mut value: JsonValue = serde_json::from_str(json)?;
472+
if let Some(content) = value.get_mut("content") {
473+
if matches!(content, JsonValue::Null) {
474+
*content = JsonValue::Object(Default::default());
475+
return Ok(Some(value));
476+
}
477+
}
478+
}
479+
480+
Ok(None)
481+
}
482+
483+
for entry in tree.iter() {
484+
let (key, value) = entry?;
485+
let raw_json: Box<RawJsonValue> = self.deserialize_value(&value)?;
486+
487+
if let Some(fixed_json) = maybe_fix_json(&raw_json)? {
488+
batch.insert(key, self.serialize_value(&fixed_json)?);
489+
}
490+
}
491+
492+
Ok(())
493+
}
494+
495+
fn migrate_to_v3(&self) -> Result<()> {
496+
let mut room_info_batch = sled::Batch::default();
497+
self.v3_fix_tree(&self.room_info, &mut room_info_batch)?;
498+
499+
let mut room_state_batch = sled::Batch::default();
500+
self.v3_fix_tree(&self.room_state, &mut room_state_batch)?;
501+
502+
let ret: Result<(), TransactionError<SledStoreError>> = (&self.room_info, &self.room_state)
503+
.transaction(|(room_info, room_state)| {
504+
room_info.apply_batch(&room_info_batch)?;
505+
room_state.apply_batch(&room_state_batch)?;
506+
507+
Ok(())
508+
});
509+
ret?;
510+
511+
self.set_db_version(3u8)
512+
}
513+
465514
/// Open a `SledCryptoStore` that uses the same database as this store.
466515
///
467516
/// The given passphrase will be used to encrypt private data.
@@ -1551,9 +1600,15 @@ mod encrypted_tests {
15511600
#[cfg(test)]
15521601
mod migration {
15531602
use matrix_sdk_test::async_test;
1603+
use ruma::{
1604+
events::{AnySyncStateEvent, StateEventType},
1605+
room_id,
1606+
};
1607+
use serde_json::json;
15541608
use tempfile::TempDir;
15551609

15561610
use super::{MigrationConflictStrategy, Result, SledStateStore, SledStoreError};
1611+
use crate::state_store::ROOM_STATE;
15571612

15581613
#[async_test]
15591614
pub async fn migrating_v1_to_2_plain() -> Result<()> {
@@ -1638,4 +1693,58 @@ mod migration {
16381693
assert_eq!(std::fs::read_dir(folder.path())?.count(), 1);
16391694
Ok(())
16401695
}
1696+
1697+
#[async_test]
1698+
pub async fn migrating_v2_to_v3() {
1699+
// An event that fails to deserialize.
1700+
let wrong_redacted_state_event = json!({
1701+
"content": null,
1702+
"event_id": "$wrongevent",
1703+
"origin_server_ts": 1673887516047_u64,
1704+
"sender": "@example:localhost",
1705+
"state_key": "",
1706+
"type": "m.room.topic",
1707+
"unsigned": {
1708+
"redacted_because": {
1709+
"type": "m.room.redaction",
1710+
"sender": "@example:localhost",
1711+
"content": {},
1712+
"redacts": "$wrongevent",
1713+
"origin_server_ts": 1673893816047_u64,
1714+
"unsigned": {},
1715+
"event_id": "$redactionevent",
1716+
},
1717+
},
1718+
});
1719+
serde_json::from_value::<AnySyncStateEvent>(wrong_redacted_state_event.clone())
1720+
.unwrap_err();
1721+
1722+
let room_id = room_id!("!some_room:localhost");
1723+
let folder = TempDir::new().unwrap();
1724+
1725+
let store = SledStateStore::builder()
1726+
.path(folder.path().to_path_buf())
1727+
.passphrase("secret".to_owned())
1728+
.build()
1729+
.unwrap();
1730+
1731+
store
1732+
.room_state
1733+
.insert(
1734+
store.encode_key(ROOM_STATE, (room_id, StateEventType::RoomTopic, "")),
1735+
store.serialize_value(&wrong_redacted_state_event).unwrap(),
1736+
)
1737+
.unwrap();
1738+
store.set_db_version(2u8).unwrap();
1739+
drop(store);
1740+
1741+
let store = SledStateStore::builder()
1742+
.path(folder.path().to_path_buf())
1743+
.passphrase("secret".to_owned())
1744+
.build()
1745+
.unwrap();
1746+
let event =
1747+
store.get_state_event(room_id, StateEventType::RoomTopic, "").await.unwrap().unwrap();
1748+
event.deserialize().unwrap();
1749+
}
16411750
}

0 commit comments

Comments
 (0)