From d3b484482ce3588e6200d7d14eb208d001d8c845 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 23 Apr 2018 16:24:59 -0400 Subject: [PATCH] Detect and handle corrupt database views. --- .../src/Messages/OWSBatchMessageProcessor.h | 2 + .../src/Messages/OWSBatchMessageProcessor.m | 5 + .../Messages/OWSDisappearingMessagesFinder.h | 5 +- .../Messages/OWSDisappearingMessagesFinder.m | 5 + .../OWSFailedAttachmentDownloadsJob.h | 4 +- .../OWSFailedAttachmentDownloadsJob.m | 5 + .../src/Messages/OWSFailedMessagesJob.h | 4 +- .../src/Messages/OWSFailedMessagesJob.m | 5 + .../src/Messages/OWSMessageReceiver.h | 2 + .../src/Messages/OWSMessageReceiver.m | 5 + .../src/Storage/OWSIncomingMessageFinder.h | 4 +- .../src/Storage/OWSIncomingMessageFinder.m | 5 + .../src/Storage/OWSMediaGalleryFinder.h | 1 + .../src/Storage/OWSMediaGalleryFinder.m | 5 + .../src/Storage/OWSPrimaryStorage.m | 93 ++++++++++++++++++- SignalServiceKit/src/Util/OWSAsserts.h | 7 ++ 16 files changed, 141 insertions(+), 16 deletions(-) diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h index c929dcee9..b4b3743b7 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h @@ -14,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSBatchMessageProcessor : NSObject + (instancetype)sharedInstance; + ++ (NSString *)databaseExtensionName; + (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage; - (void)enqueueEnvelopeData:(NSData *)envelopeData diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m index 4649ebc16..5b5c96b0f 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m @@ -464,6 +464,11 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo #pragma mark - class methods ++ (NSString *)databaseExtensionName +{ + return OWSMessageContentJobFinderExtensionName; +} + + (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage { [OWSMessageContentJobFinder asyncRegisterDatabaseExtension:storage]; diff --git a/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.h b/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.h index 808b6c487..b1e375e7c 100644 --- a/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.h +++ b/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.h @@ -25,9 +25,8 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable NSNumber *)nextExpirationTimestampWithTransaction:(YapDatabaseReadTransaction *_Nonnull)transaction; -/** - * Database extensions required for class to work. - */ ++ (NSString *)databaseExtensionName; + + (void)asyncRegisterDatabaseExtensions:(OWSStorage *)storage; #ifdef DEBUG diff --git a/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.m b/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.m index 4628f6410..cc3627430 100644 --- a/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.m +++ b/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.m @@ -196,6 +196,11 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess } #endif ++ (NSString *)databaseExtensionName +{ + return OWSDisappearingMessageFinderExpiresAtIndex; +} + + (void)asyncRegisterDatabaseExtensions:(OWSStorage *)storage { [storage asyncRegisterExtension:[self indexDatabaseExtension] withName:OWSDisappearingMessageFinderExpiresAtIndex]; diff --git a/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.h b/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.h index 6e694bddc..e858a49f6 100644 --- a/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.h +++ b/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.h @@ -14,9 +14,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)run; -/** - * Database extensions required for class to work. - */ ++ (NSString *)databaseExtensionName; + (void)asyncRegisterDatabaseExtensionsWithPrimaryStorage:(OWSStorage *)storage; #ifdef DEBUG diff --git a/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.m b/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.m index 1637ad827..34fbdd885 100644 --- a/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.m +++ b/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.m @@ -126,6 +126,11 @@ static NSString *const OWSFailedAttachmentDownloadsJobAttachmentStateIndex = @"i } #endif ++ (NSString *)databaseExtensionName +{ + return OWSFailedAttachmentDownloadsJobAttachmentStateIndex; +} + + (void)asyncRegisterDatabaseExtensionsWithPrimaryStorage:(OWSStorage *)storage { [storage asyncRegisterExtension:[self indexDatabaseExtension] diff --git a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.h b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.h index 90d760867..eea3d0822 100644 --- a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.h +++ b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.h @@ -14,9 +14,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)run; -/** - * Database extensions required for class to work. - */ ++ (NSString *)databaseExtensionName; + (void)asyncRegisterDatabaseExtensionsWithPrimaryStorage:(OWSStorage *)storage; #ifdef DEBUG diff --git a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m index 60abdbbf3..907870127 100644 --- a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m +++ b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m @@ -136,6 +136,11 @@ static NSString *const OWSFailedMessagesJobMessageStateIndex = @"index_outoing_m } #endif ++ (NSString *)databaseExtensionName +{ + return OWSFailedMessagesJobMessageStateIndex; +} + + (void)asyncRegisterDatabaseExtensionsWithPrimaryStorage:(OWSStorage *)storage { [storage asyncRegisterExtension:[self indexDatabaseExtension] withName:OWSFailedMessagesJobMessageStateIndex]; diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.h b/SignalServiceKit/src/Messages/OWSMessageReceiver.h index 8af362d77..ad7a0d957 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.h +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.h @@ -14,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSMessageReceiver : NSObject + (instancetype)sharedInstance; + ++ (NSString *)databaseExtensionName; + (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage; - (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope; diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.m b/SignalServiceKit/src/Messages/OWSMessageReceiver.m index 7c90b108a..0d4bfbaaa 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.m +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.m @@ -424,6 +424,11 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin #pragma mark - class methods ++ (NSString *)databaseExtensionName +{ + return OWSMessageDecryptJobFinderExtensionName; +} + + (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage { [OWSMessageDecryptJobFinder asyncRegisterDatabaseExtension:storage]; diff --git a/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.h b/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.h index 4a1e7bb50..d62800957 100644 --- a/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.h +++ b/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.h @@ -12,9 +12,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithPrimaryStorage:(OWSPrimaryStorage *)primaryStorage NS_DESIGNATED_INITIALIZER; -/** - * Must be called before using this finder. - */ ++ (NSString *)databaseExtensionName; + (void)asyncRegisterExtensionWithPrimaryStorage:(OWSStorage *)storage; /** diff --git a/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.m b/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.m index 94e4dedc2..cd5da26e9 100644 --- a/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.m +++ b/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.m @@ -92,6 +92,11 @@ NSString *const OWSIncomingMessageFinderColumnSourceDeviceId = @"OWSIncomingMess return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler]; } ++ (NSString *)databaseExtensionName +{ + return OWSIncomingMessageFinderExtensionName; +} + + (void)asyncRegisterExtensionWithPrimaryStorage:(OWSStorage *)storage { DDLogInfo(@"%@ registering async.", self.logTag); diff --git a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.h b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.h index 349d818bc..602e10a1e 100644 --- a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.h +++ b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.h @@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Extension registration ++ (NSString *)databaseExtensionName; + (void)asyncRegisterDatabaseExtensionsWithPrimaryStorage:(OWSStorage *)storage; @end diff --git a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m index 8d923a01a..5e8017a02 100644 --- a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m +++ b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m @@ -111,6 +111,11 @@ static NSString *const OWSMediaGalleryFinderExtensionName = @"OWSMediaGalleryFin #pragma mark - Extension registration ++ (NSString *)databaseExtensionName +{ + return OWSMediaGalleryFinderExtensionName; +} + + (void)asyncRegisterDatabaseExtensionsWithPrimaryStorage:(OWSStorage *)storage { [storage asyncRegisterExtension:[self mediaGalleryDatabaseExtension] diff --git a/SignalServiceKit/src/Storage/OWSPrimaryStorage.m b/SignalServiceKit/src/Storage/OWSPrimaryStorage.m index 3329e494b..d7f7284c8 100644 --- a/SignalServiceKit/src/Storage/OWSPrimaryStorage.m +++ b/SignalServiceKit/src/Storage/OWSPrimaryStorage.m @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN NSString *const OWSPrimaryStorageExceptionName_CouldNotCreateDatabaseDirectory = @"TSStorageManagerExceptionName_CouldNotCreateDatabaseDirectory"; -void runSyncRegistrationsForStorage(OWSStorage *storage) +void RunSyncRegistrationsForStorage(OWSStorage *storage) { OWSCAssert(storage); @@ -30,7 +30,7 @@ void runSyncRegistrationsForStorage(OWSStorage *storage) [TSDatabaseView registerCrossProcessNotifier:storage]; } -void runAsyncRegistrationsForStorage(OWSStorage *storage, dispatch_block_t completion) +void RunAsyncRegistrationsForStorage(OWSStorage *storage, dispatch_block_t completion) { OWSCAssert(storage); OWSCAssert(completion); @@ -63,6 +63,84 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage, dispatch_block_t compl [TSDatabaseView asyncRegisterLazyRestoreAttachmentsDatabaseView:storage completion:completion]; } +extern NSString *const TSThreadOutgoingMessageDatabaseViewExtensionName; +extern NSString *const TSUnseenDatabaseViewExtensionName; +extern NSString *const TSThreadSpecialMessagesDatabaseViewExtensionName; + +void VerifyRegistrationsForStorage(OWSStorage *storage) +{ + OWSCAssert(storage); + + // This should 1:1 correspond to the database view registrations + // done in RunSyncRegistrationsForStorage() and + // RunAsyncRegistrationsForStorage(). + NSArray *databaseViewNames = @[ + // We don't need to verify the cross process notifier. + // [TSDatabaseView registerCrossProcessNotifier:storage]; + + // [TSDatabaseView asyncRegisterThreadInteractionsDatabaseView:storage]; + TSMessageDatabaseViewExtensionName, + + // [TSDatabaseView asyncRegisterThreadDatabaseView:storage]; + TSThreadDatabaseViewExtensionName, + + // [TSDatabaseView asyncRegisterUnreadDatabaseView:storage]; + TSUnreadDatabaseViewExtensionName, + + // [storage asyncRegisterExtension:[TSDatabaseSecondaryIndexes registerTimeStampIndex] withName:@"idx"]; + @"idx", + + // [OWSMessageReceiver asyncRegisterDatabaseExtension:storage]; + [OWSMessageReceiver databaseExtensionName], + + // [OWSBatchMessageProcessor asyncRegisterDatabaseExtension:storage]; + [OWSBatchMessageProcessor databaseExtensionName], + + // [TSDatabaseView asyncRegisterUnseenDatabaseView:storage]; + TSUnseenDatabaseViewExtensionName, + + // [TSDatabaseView asyncRegisterThreadOutgoingMessagesDatabaseView:storage]; + TSThreadOutgoingMessageDatabaseViewExtensionName, + + // [TSDatabaseView asyncRegisterThreadSpecialMessagesDatabaseView:storage]; + TSThreadSpecialMessagesDatabaseViewExtensionName, + + // [OWSIncomingMessageFinder asyncRegisterExtensionWithPrimaryStorage:storage]; + [OWSIncomingMessageFinder databaseExtensionName], + + // [TSDatabaseView asyncRegisterSecondaryDevicesDatabaseView:storage]; + TSSecondaryDevicesDatabaseViewExtensionName, + + // [OWSDisappearingMessagesFinder asyncRegisterDatabaseExtensions:storage]; + [OWSDisappearingMessagesFinder databaseExtensionName], + + // [OWSFailedMessagesJob asyncRegisterDatabaseExtensionsWithPrimaryStorage:storage]; + [OWSFailedMessagesJob databaseExtensionName], + + // [OWSFailedAttachmentDownloadsJob asyncRegisterDatabaseExtensionsWithPrimaryStorage:storage]; + [OWSFailedAttachmentDownloadsJob databaseExtensionName], + + // [OWSMediaGalleryFinder asyncRegisterDatabaseExtensionsWithPrimaryStorage:storage]; + [OWSMediaGalleryFinder databaseExtensionName], + + // NOTE: Always pass the completion to the _LAST_ of the async database + // view registrations. + // [TSDatabaseView asyncRegisterLazyRestoreAttachmentsDatabaseView:storage completion:completion]; + TSLazyRestoreAttachmentsDatabaseViewExtensionName, + ]; + + __block BOOL hasMissingDatabaseView = NO; + [[storage newDatabaseConnection] asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction) { + for (NSString *databaseViewName in databaseViewNames) { + YapDatabaseViewTransaction *_Nullable viewTransaction = [transaction ext:databaseViewName]; + if (!viewTransaction) { + OWSProdLogAndCFail(@"VerifyRegistrationsForStorage missing database view: %@", databaseViewName); + hasMissingDatabaseView = YES; + } + } + }]; +} + #pragma mark - @interface OWSPrimaryStorage () @@ -119,7 +197,7 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage, dispatch_block_t compl - (void)runSyncRegistrations { - runSyncRegistrationsForStorage(self); + RunSyncRegistrationsForStorage(self); // See comments on OWSDatabaseConnection. // @@ -137,7 +215,7 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage, dispatch_block_t compl DDLogVerbose(@"%@ async registrations enqueuing.", self.logTag); - runAsyncRegistrationsForStorage(self, ^{ + RunAsyncRegistrationsForStorage(self, ^{ OWSAssertIsOnMainThread(); OWSAssert(!self.areAsyncRegistrationsComplete); @@ -147,9 +225,16 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage, dispatch_block_t compl self.areAsyncRegistrationsComplete = YES; completion(); + + [self verifyDatabaseViews]; }); } +- (void)verifyDatabaseViews +{ + VerifyRegistrationsForStorage(self); +} + + (void)protectFiles { DDLogInfo( diff --git a/SignalServiceKit/src/Util/OWSAsserts.h b/SignalServiceKit/src/Util/OWSAsserts.h index 84be8e747..cc719b424 100755 --- a/SignalServiceKit/src/Util/OWSAsserts.h +++ b/SignalServiceKit/src/Util/OWSAsserts.h @@ -128,6 +128,13 @@ NS_ASSUME_NONNULL_BEGIN OWSFail(_messageFormat, ##__VA_ARGS__); \ } +#define OWSProdLogAndCFail(_messageFormat, ...) \ + { \ + DDLogError(_messageFormat, ##__VA_ARGS__); \ + [DDLog flushLog]; \ + OWSCFail(_messageFormat, ##__VA_ARGS__); \ + } + // This function is intended for use in Swift. void SwiftAssertIsOnMainThread(NSString *functionName);