From d91507d897431cdef73e4fd84fe916026a01c007 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 20 Feb 2018 17:37:14 -0500 Subject: [PATCH] Improve the robustness of the migration logic. --- Signal/src/AppDelegate.m | 49 +++++++++++++---- SignalMessaging/profiles/OWSProfileManager.h | 4 +- SignalMessaging/profiles/OWSProfileManager.m | 10 ++-- .../Messages/Attachments/TSAttachmentStream.h | 2 +- .../Messages/Attachments/TSAttachmentStream.m | 10 ++-- .../src/Storage/TSStorageManager.h | 5 +- .../src/Storage/TSStorageManager.m | 38 ++++++++----- .../src/Util/NSUserDefaults+OWS.m | 4 +- SignalServiceKit/src/Util/OWSAsserts.h | 7 +++ SignalServiceKit/src/Util/OWSError.h | 3 +- SignalServiceKit/src/Util/OWSFileSystem.h | 6 +-- SignalServiceKit/src/Util/OWSFileSystem.m | 53 ++++++++++++++----- 12 files changed, 139 insertions(+), 52 deletions(-) diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index b76a0891d..af994e92a 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -230,35 +230,64 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; - (BOOL)ensureIsReadyForAppExtensions { + // Given how sensitive this migration is, we verbosely + // log the contents of all involved paths before and after. + // + // TODO: Remove this logging once we have high confidence + // in our migration logic. + NSArray *paths = @[ + TSStorageManager.legacyDatabaseFilePath, + TSStorageManager.legacyDatabaseFilePath_SHM, + TSStorageManager.legacyDatabaseFilePath_WAL, + TSStorageManager.sharedDataDatabaseFilePath, + TSStorageManager.sharedDataDatabaseFilePath_SHM, + TSStorageManager.sharedDataDatabaseFilePath_WAL, + ]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + for (NSString *path in paths) { + if ([fileManager fileExistsAtPath:path]) { + DDLogInfo(@"%@ storage file: %@, %@", self.logTag, path, [OWSFileSystem fileSizeOfPath:path]); + } + } + if ([OWSPreferences isReadyForAppExtensions]) { return YES; } if ([NSFileManager.defaultManager fileExistsAtPath:TSStorageManager.legacyDatabaseFilePath]) { - DDLogInfo(@"%@ Database file size: %@", + DDLogInfo(@"%@ Legacy Database file size: %@", self.logTag, [OWSFileSystem fileSizeOfPath:TSStorageManager.legacyDatabaseFilePath]); - DDLogInfo(@"%@ \t SHM file size: %@", + DDLogInfo(@"%@ \t Legacy SHM file size: %@", self.logTag, [OWSFileSystem fileSizeOfPath:TSStorageManager.legacyDatabaseFilePath_SHM]); - DDLogInfo(@"%@ \t WAL file size: %@", + DDLogInfo(@"%@ \t Legacy WAL file size: %@", self.logTag, [OWSFileSystem fileSizeOfPath:TSStorageManager.legacyDatabaseFilePath_WAL]); } NSError *_Nullable error = [self convertDatabaseIfNecessary]; + + if (!error) { + [NSUserDefaults migrateToSharedUserDefaults]; + } + + if (!error) { + error = [TSStorageManager migrateToSharedData]; + } + if (!error) { + error = [OWSProfileManager migrateToSharedData]; + } + if (!error) { + error = [TSAttachmentStream migrateToSharedData]; + } + if (error) { OWSFail(@"%@ database conversion failed: %@", self.logTag, error); [self showLaunchFailureUI:error]; return NO; } - [NSUserDefaults migrateToSharedUserDefaults]; - - [TSStorageManager migrateToSharedData]; - [OWSProfileManager migrateToSharedData]; - [TSAttachmentStream migrateToSharedData]; - return YES; } @@ -304,6 +333,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; - (nullable NSError *)convertDatabaseIfNecessary { + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + NSString *databaseFilePath = [TSStorageManager legacyDatabaseFilePath]; if (![[NSFileManager defaultManager] fileExistsAtPath:databaseFilePath]) { DDLogVerbose(@"%@ no legacy database file found", self.logTag); diff --git a/SignalMessaging/profiles/OWSProfileManager.h b/SignalMessaging/profiles/OWSProfileManager.h index 64fba73cf..983363b59 100644 --- a/SignalMessaging/profiles/OWSProfileManager.h +++ b/SignalMessaging/profiles/OWSProfileManager.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import @@ -24,7 +24,7 @@ extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter; - (void)resetProfileStorage; -+ (void)migrateToSharedData; ++ (nullable NSError *)migrateToSharedData; #pragma mark - Local Profile diff --git a/SignalMessaging/profiles/OWSProfileManager.m b/SignalMessaging/profiles/OWSProfileManager.m index 7349e3390..e7c8651ce 100644 --- a/SignalMessaging/profiles/OWSProfileManager.m +++ b/SignalMessaging/profiles/OWSProfileManager.m @@ -1113,11 +1113,13 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640; return [[OWSFileSystem appSharedDataDirectoryPath] stringByAppendingPathComponent:@"ProfileAvatars"]; } -+ (void)migrateToSharedData ++ (nullable NSError *)migrateToSharedData { - [OWSFileSystem moveAppFilePath:self.legacyProfileAvatarsDirPath - sharedDataFilePath:self.sharedDataProfileAvatarsDirPath - exceptionName:@"ProfileManagerCouldNotMigrateProfileDirectory"]; + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + + return [OWSFileSystem moveAppFilePath:self.legacyProfileAvatarsDirPath + sharedDataFilePath:self.sharedDataProfileAvatarsDirPath + exceptionName:@"ProfileManagerCouldNotMigrateProfileDirectory"]; } - (NSString *)profileAvatarsDirPath diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h index acfd9b203..974e66b9d 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.h @@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN - (CGFloat)audioDurationSeconds; -+ (void)migrateToSharedData; ++ (nullable NSError *)migrateToSharedData; @end diff --git a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m index dd98b1902..3ef9bad23 100644 --- a/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m +++ b/SignalServiceKit/src/Messages/Attachments/TSAttachmentStream.m @@ -194,11 +194,13 @@ NS_ASSUME_NONNULL_BEGIN return [[OWSFileSystem appSharedDataDirectoryPath] stringByAppendingPathComponent:@"Attachments"]; } -+ (void)migrateToSharedData ++ (nullable NSError *)migrateToSharedData { - [OWSFileSystem moveAppFilePath:self.legacyAttachmentsDirPath - sharedDataFilePath:self.sharedDataAttachmentsDirPath - exceptionName:@"CouldNotMigrateAttachmentsDirectory"]; + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + + return [OWSFileSystem moveAppFilePath:self.legacyAttachmentsDirPath + sharedDataFilePath:self.sharedDataAttachmentsDirPath + exceptionName:@"CouldNotMigrateAttachmentsDirectory"]; } + (NSString *)attachmentsFolder diff --git a/SignalServiceKit/src/Storage/TSStorageManager.h b/SignalServiceKit/src/Storage/TSStorageManager.h index 7f6eb5041..912116c5c 100644 --- a/SignalServiceKit/src/Storage/TSStorageManager.h +++ b/SignalServiceKit/src/Storage/TSStorageManager.h @@ -21,13 +21,16 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage); + (YapDatabaseConnection *)dbReadConnection; + (YapDatabaseConnection *)dbReadWriteConnection; -+ (void)migrateToSharedData; ++ (nullable NSError *)migrateToSharedData; + (NSString *)databaseFilePath; + (NSString *)legacyDatabaseFilePath; + (NSString *)legacyDatabaseFilePath_SHM; + (NSString *)legacyDatabaseFilePath_WAL; ++ (NSString *)sharedDataDatabaseFilePath; ++ (NSString *)sharedDataDatabaseFilePath_SHM; ++ (NSString *)sharedDataDatabaseFilePath_WAL; @end diff --git a/SignalServiceKit/src/Storage/TSStorageManager.m b/SignalServiceKit/src/Storage/TSStorageManager.m index 2692e7660..6245b7e86 100644 --- a/SignalServiceKit/src/Storage/TSStorageManager.m +++ b/SignalServiceKit/src/Storage/TSStorageManager.m @@ -220,8 +220,10 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage) return [self.sharedDataDatabaseDirPath stringByAppendingPathComponent:self.databaseFilename_WAL]; } -+ (void)migrateToSharedData ++ (nullable NSError *)migrateToSharedData { + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + // Given how sensitive this migration is, we verbosely // log the contents of all involved paths before and after. NSArray *paths = @[ @@ -235,7 +237,7 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage) NSFileManager *fileManager = [NSFileManager defaultManager]; for (NSString *path in paths) { if ([fileManager fileExistsAtPath:path]) { - DDLogInfo(@"%@ migrateToSharedData before %@: %@", self.logTag, path, [OWSFileSystem fileSizeOfPath:path]); + DDLogInfo(@"%@ before migrateToSharedData: %@, %@", self.logTag, path, [OWSFileSystem fileSizeOfPath:path]); } } @@ -252,21 +254,33 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage) [OWSFileSystem protectFileOrFolderAtPath:self.legacyDatabaseFilePath_SHM]; [OWSFileSystem protectFileOrFolderAtPath:self.legacyDatabaseFilePath_WAL]; - [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath - sharedDataFilePath:self.sharedDataDatabaseFilePath - exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile]; - [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath_SHM - sharedDataFilePath:self.sharedDataDatabaseFilePath_SHM - exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile]; - [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath_WAL - sharedDataFilePath:self.sharedDataDatabaseFilePath_WAL - exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile]; + NSError *_Nullable error = nil; + error = [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath + sharedDataFilePath:self.sharedDataDatabaseFilePath + exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile]; + if (error) { + return error; + } + error = [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath_SHM + sharedDataFilePath:self.sharedDataDatabaseFilePath_SHM + exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile]; + if (error) { + return error; + } + error = [OWSFileSystem moveAppFilePath:self.legacyDatabaseFilePath_WAL + sharedDataFilePath:self.sharedDataDatabaseFilePath_WAL + exceptionName:TSStorageManagerExceptionName_CouldNotMoveDatabaseFile]; + if (error) { + return error; + } for (NSString *path in paths) { if ([fileManager fileExistsAtPath:path]) { - DDLogInfo(@"%@ migrateToSharedData after %@: %@", self.logTag, path, [OWSFileSystem fileSizeOfPath:path]); + DDLogInfo(@"%@ after migrateToSharedData: %@, %@", self.logTag, path, [OWSFileSystem fileSizeOfPath:path]); } } + + return nil; } + (NSString *)databaseFilePath diff --git a/SignalServiceKit/src/Util/NSUserDefaults+OWS.m b/SignalServiceKit/src/Util/NSUserDefaults+OWS.m index 76d9b698f..2206d7ca9 100644 --- a/SignalServiceKit/src/Util/NSUserDefaults+OWS.m +++ b/SignalServiceKit/src/Util/NSUserDefaults+OWS.m @@ -2,8 +2,8 @@ // Copyright (c) 2018 Open Whisper Systems. All rights reserved. // -#import "AppContext.h" #import "NSUserDefaults+OWS.h" +#import "AppContext.h" #import "TSConstants.h" NS_ASSUME_NONNULL_BEGIN @@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN + (void)migrateToSharedUserDefaults { + DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__); + NSUserDefaults *appUserDefaults = self.appUserDefaults; NSDictionary *dictionary = [NSUserDefaults standardUserDefaults].dictionaryRepresentation; diff --git a/SignalServiceKit/src/Util/OWSAsserts.h b/SignalServiceKit/src/Util/OWSAsserts.h index e4878eea4..cb3932b66 100755 --- a/SignalServiceKit/src/Util/OWSAsserts.h +++ b/SignalServiceKit/src/Util/OWSAsserts.h @@ -121,6 +121,13 @@ NS_ASSUME_NONNULL_BEGIN // This macro is intended for use in Objective-C. #define OWSAssertIsOnMainThread() OWSCAssert([NSThread isMainThread]) +#define OWSProdLogAndFail(_messageFormat, ...) \ + { \ + DDLogError(_messageFormat, ##__VA_ARGS__); \ + [DDLog flushLog]; \ + OWSFail(_messageFormat, ##__VA_ARGS__); \ + } + // This function is intended for use in Swift. void AssertIsOnMainThread(void); diff --git a/SignalServiceKit/src/Util/OWSError.h b/SignalServiceKit/src/Util/OWSError.h index f09018072..828d5401f 100644 --- a/SignalServiceKit/src/Util/OWSError.h +++ b/SignalServiceKit/src/Util/OWSError.h @@ -27,7 +27,8 @@ typedef NS_ENUM(NSInteger, OWSErrorCode) { OWSErrorCodeContactsUpdaterRateLimit = 777408, OWSErrorCodeCouldNotWriteAttachmentData = 777409, OWSErrorCodeMessageDeletedBeforeSent = 777410, - OWSErrorCodeDatabaseConversionFatalError = 777411 + OWSErrorCodeDatabaseConversionFatalError = 777411, + OWSErrorCodeMoveFileToSharedDataContainerError = 777412 }; extern NSString *const OWSErrorRecipientIdentifierKey; diff --git a/SignalServiceKit/src/Util/OWSFileSystem.h b/SignalServiceKit/src/Util/OWSFileSystem.h index db6643c11..df657e305 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.h +++ b/SignalServiceKit/src/Util/OWSFileSystem.h @@ -17,9 +17,9 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)cachesDirectoryPath; -+ (void)moveAppFilePath:(NSString *)oldFilePath - sharedDataFilePath:(NSString *)newFilePath - exceptionName:(NSString *)exceptionName; ++ (nullable NSError *)moveAppFilePath:(NSString *)oldFilePath + sharedDataFilePath:(NSString *)newFilePath + exceptionName:(NSString *)exceptionName; // Returns NO IFF the directory does not exist and could not be created. + (BOOL)ensureDirectoryExists:(NSString *)dirPath; diff --git a/SignalServiceKit/src/Util/OWSFileSystem.m b/SignalServiceKit/src/Util/OWSFileSystem.m index 06efb1f54..b6d4ce320 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.m +++ b/SignalServiceKit/src/Util/OWSFileSystem.m @@ -3,6 +3,7 @@ // #import "OWSFileSystem.h" +#import "OWSError.h" #import "TSConstants.h" NS_ASSUME_NONNULL_BEGIN @@ -107,23 +108,47 @@ NS_ASSUME_NONNULL_BEGIN return paths[0]; } -+ (void)moveAppFilePath:(NSString *)oldFilePath - sharedDataFilePath:(NSString *)newFilePath - exceptionName:(NSString *)exceptionName ++ (nullable NSError *)moveAppFilePath:(NSString *)oldFilePath + sharedDataFilePath:(NSString *)newFilePath + exceptionName:(NSString *)exceptionName { NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:oldFilePath]) { - return; + return nil; } DDLogInfo(@"%@ Moving file or directory from: %@ to: %@", self.logTag, oldFilePath, newFilePath); if ([fileManager fileExistsAtPath:newFilePath]) { - OWSFail(@"%@ Can't move file or directory from: %@ to: %@; destination already exists.", + // If a file/directory already exists at the destination, + // try to move it "aside" by renaming it with an extension. + NSString *legacyFilePath = + [[newFilePath stringByAppendingString:@"."] stringByAppendingString:[NSUUID UUID].UUIDString]; + DDLogInfo(@"%@ Trying to rename pre-existing destination file or directory from: %@ to: %@", + self.logTag, + newFilePath, + legacyFilePath); + + NSError *_Nullable error; + BOOL success = [fileManager moveItemAtPath:newFilePath toPath:legacyFilePath error:&error]; + if (!success || error) { + OWSProdLogAndFail( + @"%@ Could not move pre-existing destination file or directory from: %@ to: %@, error: %@", + self.logTag, + newFilePath, + legacyFilePath, + error); + return error; + } + } + + if ([fileManager fileExistsAtPath:newFilePath]) { + OWSProdLogAndFail(@"%@ Can't move file or directory from: %@ to: %@; destination already exists.", self.logTag, oldFilePath, newFilePath); - return; + return OWSErrorWithCodeDescription( + OWSErrorCodeMoveFileToSharedDataContainerError, @"Can't move file; destination already exists."); } NSDate *startDate = [NSDate new]; @@ -131,14 +156,12 @@ NS_ASSUME_NONNULL_BEGIN NSError *_Nullable error; BOOL success = [fileManager moveItemAtPath:oldFilePath toPath:newFilePath error:&error]; if (!success || error) { - NSString *errorDescription = - [NSString stringWithFormat:@"%@ Could not move file or directory from: %@ to: %@, error: %@", - self.logTag, - oldFilePath, - newFilePath, - error]; - OWSFail(@"%@", errorDescription); - OWSRaiseException(exceptionName, @"%@", errorDescription); + OWSProdLogAndFail(@"%@ Could not move file or directory from: %@ to: %@, error: %@", + self.logTag, + oldFilePath, + newFilePath, + error); + return error; } DDLogInfo(@"%@ Moved file or directory from: %@ to: %@ in: %f", @@ -153,6 +176,8 @@ NS_ASSUME_NONNULL_BEGIN dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self protectRecursiveContentsAtPath:newFilePath]; }); + + return nil; } + (BOOL)ensureDirectoryExists:(NSString *)dirPath