From 892b51221aa5a0d9eceb8a36c880d184bb2e8d75 Mon Sep 17 00:00:00 2001 From: Max Radermacher Date: Thu, 28 May 2026 11:29:51 -0500 Subject: [PATCH] Fix empty WAL file transfer during device transfer Co-authored-by: Sasha Weiss --- .../DeviceTransferService+Manifest.swift | 3 --- Signal/DeviceTransfer/DeviceTransferService.swift | 11 ++++++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Signal/DeviceTransfer/DeviceTransferService+Manifest.swift b/Signal/DeviceTransfer/DeviceTransferService+Manifest.swift index 31db5ef1a8..5873356832 100644 --- a/Signal/DeviceTransfer/DeviceTransferService+Manifest.swift +++ b/Signal/DeviceTransfer/DeviceTransferService+Manifest.swift @@ -32,9 +32,6 @@ extension DeviceTransferService { let wal: DeviceTransferProtoFile = try { let file = SSKEnvironment.shared.databaseStorageRef.grdbStorage.databaseWALFilePath let size = try OWSFileSystem.fileSize(ofPath: file) - guard size > 0 else { - throw OWSAssertionError("database wal is empty") - } estimatedTotalSize += size let fileBuilder = DeviceTransferProtoFile.builder( identifier: DeviceTransferService.databaseWALIdentifier, diff --git a/Signal/DeviceTransfer/DeviceTransferService.swift b/Signal/DeviceTransfer/DeviceTransferService.swift index 8074b2975d..73545572b4 100644 --- a/Signal/DeviceTransfer/DeviceTransferService.swift +++ b/Signal/DeviceTransfer/DeviceTransferService.swift @@ -5,6 +5,7 @@ import CryptoKit import Foundation +import GRDB import MultipeerConnectivity import SignalServiceKit @@ -366,7 +367,15 @@ class DeviceTransferService: NSObject, DeviceTransferServiceProtocol { taskGroup.addTask { // Make a copy of the database files within a write transaction so we can be confident // they aren't mutated during the copy. We then transfer these copies. - let dbCopy = try await SSKEnvironment.shared.databaseStorageRef.awaitableWrite { _ in + let dbCopy = try await SSKEnvironment.shared.databaseStorageRef.awaitableWrite { tx in + // The MultipeerConnectivity framework stalls if we try to send an empty + // file. The receiver requires a non-empty file. We can't send garbage + // (because that would corrupt the database), so mutate the database, force + // it to be written to the WAL file, and then send that result to our peer. + let store = NewKeyValueStore(collection: "DeviceTransferWAL") + store.writeValue(Randomness.generateRandomBytes(32), forKey: "MustBeNonEmpty", tx: tx) + store.removeValue(forKey: "MustBeNonEmpty", tx: tx) + sqlite3_db_cacheflush(tx.database.sqliteConnection!) do { let dbCopy = try Self.makeLocalCopy(databaseFile: database.database) let walCopy = try Self.makeLocalCopy(databaseFile: database.wal)