Resolve issues around database conversion.

pull/1/head
Matthew Chen 7 years ago
parent 11a709a621
commit cc15092eb7

@ -6,7 +6,9 @@
#import "OWSDatabaseConverter.h" #import "OWSDatabaseConverter.h"
#import <Curve25519Kit/Randomness.h> #import <Curve25519Kit/Randomness.h>
#import <SignalServiceKit/OWSStorage.h> #import <SignalServiceKit/OWSStorage.h>
#import <SignalServiceKit/YapDatabaseConnection+OWS.h>
#import <YapDatabase/YapDatabase.h> #import <YapDatabase/YapDatabase.h>
#import <YapDatabase/YapDatabasePrivate.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -33,37 +35,122 @@ NS_ASSUME_NONNULL_BEGIN
return [Randomness generateRandomBytes:30]; return [Randomness generateRandomBytes:30];
} }
- (nullable NSString *)createUnconvertedDatabase:(NSData *)passwordData - (void)openYapDatabase:(NSString *)databaseFilePath
databasePassword:(NSData *)databasePassword
databaseBlock:(void (^_Nonnull)(YapDatabase *))databaseBlock
{
OWSAssert(databaseFilePath.length > 0);
OWSAssert(databasePassword.length > 0);
OWSAssert(databaseBlock);
DDLogVerbose(@"openYapDatabase: %@", databaseFilePath);
__weak YapDatabase *_Nullable weakDatabase = nil;
dispatch_queue_t snapshotQueue;
dispatch_queue_t writeQueue;
@autoreleasepool {
YapDatabaseOptions *options = [[YapDatabaseOptions alloc] init];
options.corruptAction = YapDatabaseCorruptAction_Fail;
options.cipherKeyBlock = ^{
return databasePassword;
};
options.enableMultiProcessSupport = YES;
OWSAssert(options.cipherDefaultkdfIterNumber == 0);
OWSAssert(options.kdfIterNumber == 0);
OWSAssert(options.cipherPageSize == 0);
OWSAssert(options.pragmaPageSize == 0);
OWSAssert(options.pragmaJournalSizeLimit == 0);
YapDatabase *database = [[YapDatabase alloc] initWithPath:databaseFilePath
serializer:nil
deserializer:[OWSStorage logOnFailureDeserializer]
options:options];
OWSAssert(database);
weakDatabase = database;
snapshotQueue = database->snapshotQueue;
writeQueue = database->writeQueue;
databaseBlock(database);
// Close the database.
database = nil;
}
// Flush the database's queues, which may contain lingering
// references to the database.
dispatch_sync(snapshotQueue,
^{
});
dispatch_sync(writeQueue,
^{
});
// Wait for notifications from writes to be fired.
{
XCTestExpectation *expectation = [self expectationWithDescription:@"Database modified notifications"];
dispatch_async(dispatch_get_main_queue(), ^{
// Database modified notifications are fired on the main queue.
// Once this block executes, the main queue has been flushed
// and we know that all database modified notifications are
// complete.
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:5.0
handler:^(NSError *error) {
if (error) {
NSLog(@"Timeout Error: %@", error);
}
}];
}
YapDatabase *_Nullable strongDatabase = weakDatabase;
OWSAssert(!strongDatabase);
}
- (nullable NSString *)createUnconvertedDatabase:(NSData *)databasePassword
{ {
NSString *temporaryDirectory = NSTemporaryDirectory(); NSString *temporaryDirectory = NSTemporaryDirectory();
NSString *filename = [NSUUID UUID].UUIDString; NSString *filename = [NSUUID UUID].UUIDString;
NSString *databaseFilePath = [temporaryDirectory stringByAppendingPathComponent:filename]; NSString *databaseFilePath = [temporaryDirectory stringByAppendingPathComponent:filename];
YapDatabaseOptions *options = [[YapDatabaseOptions alloc] init]; [self openYapDatabase:databaseFilePath
options.corruptAction = YapDatabaseCorruptAction_Fail; databasePassword:databasePassword
options.cipherKeyBlock = ^{ databaseBlock:^(YapDatabase *database) {
return passwordData; YapDatabaseConnection *dbConnection = database.newConnection;
}; [dbConnection setObject:@(YES) forKey:@"test_key_name" inCollection:@"test_collection_name"];
options.enableMultiProcessSupport = YES; [dbConnection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:nil];
}];
OWSAssert(options.cipherDefaultkdfIterNumber == 0);
OWSAssert(options.kdfIterNumber == 0); OWSAssert([[NSFileManager defaultManager] fileExistsAtPath:databaseFilePath]);
OWSAssert(options.cipherPageSize == 0);
OWSAssert(options.pragmaPageSize == 0); [self openYapDatabase:databaseFilePath
OWSAssert(options.pragmaJournalSizeLimit == 0); databasePassword:databasePassword
databaseBlock:^(YapDatabase *database) {
YapDatabase *database = [[YapDatabase alloc] initWithPath:databaseFilePath YapDatabaseConnection *dbConnection = database.newConnection;
serializer:nil id _Nullable value = [dbConnection objectForKey:@"test_key_name" inCollection:@"test_collection_name"];
deserializer:[OWSStorage logOnFailureDeserializer] OWSAssert([@(YES) isEqual:value]);
options:options]; }];
OWSAssert(database);
return database ? databaseFilePath : nil; OWSAssert([[NSFileManager defaultManager] fileExistsAtPath:databaseFilePath]);
NSError *_Nullable error = nil;
NSDictionary *fileAttributes =
[[NSFileManager defaultManager] attributesOfItemAtPath:databaseFilePath error:&error];
OWSAssert(fileAttributes && !error);
DDLogVerbose(@"%@ test database file size: %@", self.logTag, fileAttributes[NSFileSize]);
return databaseFilePath;
} }
- (void)testDoesDatabaseNeedToBeConverted_Unconverted - (void)testDoesDatabaseNeedToBeConverted_Unconverted
{ {
NSData *passwordData = [self randomDatabasePassword]; NSData *databasePassword = [self randomDatabasePassword];
NSString *_Nullable databaseFilePath = [self createUnconvertedDatabase:passwordData]; NSString *_Nullable databaseFilePath = [self createUnconvertedDatabase:databasePassword];
XCTAssertTrue([OWSDatabaseConverter doesDatabaseNeedToBeConverted:databaseFilePath]); XCTAssertTrue([OWSDatabaseConverter doesDatabaseNeedToBeConverted:databaseFilePath]);
} }
@ -74,10 +161,15 @@ NS_ASSUME_NONNULL_BEGIN
- (void)testDatabaseConversion - (void)testDatabaseConversion
{ {
NSData *passwordData = [self randomDatabasePassword]; NSData *databasePassword = [self randomDatabasePassword];
NSString *_Nullable databaseFilePath = [self createUnconvertedDatabase:passwordData]; NSString *_Nullable databaseFilePath = [self createUnconvertedDatabase:databasePassword];
XCTAssertTrue([OWSDatabaseConverter doesDatabaseNeedToBeConverted:databaseFilePath]); XCTAssertTrue([OWSDatabaseConverter doesDatabaseNeedToBeConverted:databaseFilePath]);
[OWSDatabaseConverter convertDatabaseIfNecessary:databaseFilePath]; NSError *_Nullable error =
[OWSDatabaseConverter convertDatabaseIfNecessary:databaseFilePath databasePassword:databasePassword];
if (error) {
DDLogError(@"%s error: %@", __PRETTY_FUNCTION__, error);
}
XCTAssertNil(error);
XCTAssertFalse([OWSDatabaseConverter doesDatabaseNeedToBeConverted:databaseFilePath]); XCTAssertFalse([OWSDatabaseConverter doesDatabaseNeedToBeConverted:databaseFilePath]);
} }

@ -12,8 +12,9 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
+ (void)convertDatabaseIfNecessary; + (nullable NSError *)convertDatabaseIfNecessary;
+ (void)convertDatabaseIfNecessary:(NSString *)databaseFilePath; + (nullable NSError *)convertDatabaseIfNecessary:(NSString *)databaseFilePath
databasePassword:(NSData *)databasePassword;
@end @end

@ -4,42 +4,64 @@
#import "OWSDatabaseConverter.h" #import "OWSDatabaseConverter.h"
#import "sqlite3.h" #import "sqlite3.h"
#import <SignalServiceKit/NSData+hexString.h>
#import <SignalServiceKit/OWSError.h> #import <SignalServiceKit/OWSError.h>
#import <SignalServiceKit/OWSFileSystem.h> #import <SignalServiceKit/OWSFileSystem.h>
#import <SignalServiceKit/TSStorageManager.h> #import <SignalServiceKit/TSStorageManager.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
const int kSqliteHeaderLength = 32; const NSUInteger kSqliteHeaderLength = 32;
@interface OWSStorage (OWSDatabaseConverter)
+ (YapDatabaseDeserializer)logOnFailureDeserializer;
@end
#pragma mark -
@implementation OWSDatabaseConverter @implementation OWSDatabaseConverter
+ (NSData *)readFirstNBytesOfDatabaseFile:(NSString *)filePath byteCount:(NSUInteger)byteCount
{
OWSAssert(filePath.length > 0);
@autoreleasepool {
NSError *error;
// We use NSDataReadingMappedAlways instead of NSDataReadingMappedIfSafe because
// we know the database will always exist for the duration of this instance of NSData.
NSData *_Nullable data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]
options:NSDataReadingMappedAlways
error:&error];
if (!data || error) {
DDLogError(@"%@ Couldn't read database file header.", self.logTag);
// TODO: Make a convenience method (on a category of NSException?) that
// flushes DDLog before raising a terminal exception.
[NSException raise:@"Couldn't read database file header" format:@""];
}
// Pull this constant out so that we can use it in our YapDatabase fork.
NSData *_Nullable headerData = [data subdataWithRange:NSMakeRange(0, byteCount)];
if (!headerData || headerData.length != byteCount) {
[NSException raise:@"Database file header has unexpected length"
format:@"Database file header has unexpected length: %zd", headerData.length];
}
return [headerData copy];
}
}
+ (BOOL)doesDatabaseNeedToBeConverted:(NSString *)databaseFilePath + (BOOL)doesDatabaseNeedToBeConverted:(NSString *)databaseFilePath
{ {
OWSAssert(databaseFilePath.length > 0); OWSAssert(databaseFilePath.length > 0);
if (![[NSFileManager defaultManager] fileExistsAtPath:databaseFilePath]) { if (![[NSFileManager defaultManager] fileExistsAtPath:databaseFilePath]) {
DDLogVerbose(@"%@ Skipping database conversion; no legacy database found.", self.logTag); DDLogVerbose(@"%@ database file not found.", self.logTag);
return NO; return nil;
}
NSError *error;
// We use NSDataReadingMappedAlways instead of NSDataReadingMappedIfSafe because
// we know the database will always exist for the duration of this instance of NSData.
NSData *_Nullable data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:databaseFilePath]
options:NSDataReadingMappedAlways
error:&error];
if (!data || error) {
DDLogError(@"%@ Couldn't read legacy database file header.", self.logTag);
// TODO: Make a convenience method (on a category of NSException?) that
// flushes DDLog before raising a terminal exception.
[NSException raise:@"Couldn't read legacy database file header" format:@""];
}
// Pull this constant out so that we can use it in our YapDatabase fork.
NSData *_Nullable headerData = [data subdataWithRange:NSMakeRange(0, kSqliteHeaderLength)];
if (!headerData || headerData.length != kSqliteHeaderLength) {
[NSException raise:@"Database database file header has unexpected length"
format:@"Database database file header has unexpected length: %zd", headerData.length];
} }
NSData *headerData = [self readFirstNBytesOfDatabaseFile:databaseFilePath byteCount:kSqliteHeaderLength];
OWSAssert(headerData);
NSString *kUnencryptedHeader = @"SQLite format 3\0"; NSString *kUnencryptedHeader = @"SQLite format 3\0";
NSData *unencryptedHeaderData = [kUnencryptedHeader dataUsingEncoding:NSUTF8StringEncoding]; NSData *unencryptedHeaderData = [kUnencryptedHeader dataUsingEncoding:NSUTF8StringEncoding];
BOOL isUnencrypted = [unencryptedHeaderData BOOL isUnencrypted = [unencryptedHeaderData
@ -52,36 +74,46 @@ const int kSqliteHeaderLength = 32;
return YES; return YES;
} }
+ (void)convertDatabaseIfNecessary + (nullable NSError *)convertDatabaseIfNecessary
{ {
NSString *databaseFilePath = [TSStorageManager legacyDatabaseFilePath]; NSString *databaseFilePath = [TSStorageManager legacyDatabaseFilePath];
[self convertDatabaseIfNecessary:databaseFilePath];
NSError *error;
NSData *_Nullable databasePassword = [OWSStorage tryToLoadDatabasePassword:&error];
if (!databasePassword || error) {
return (error
?: OWSErrorWithCodeDescription(
OWSErrorCodeDatabaseConversionFatalError, @"Failed to load database password"));
}
return [self convertDatabaseIfNecessary:databaseFilePath databasePassword:databasePassword];
} }
// TODO upon failure show user error UI // TODO upon failure show user error UI
// TODO upon failure anything we need to do "back out" partial migration // TODO upon failure anything we need to do "back out" partial migration
+ (void)convertDatabaseIfNecessary:(NSString *)databaseFilePath + (nullable NSError *)convertDatabaseIfNecessary:(NSString *)databaseFilePath
databasePassword:(NSData *)databasePassword
{ {
if (![self doesDatabaseNeedToBeConverted:databaseFilePath]) { if (![self doesDatabaseNeedToBeConverted:databaseFilePath]) {
return; return nil;
} }
[self convertDatabase:(NSString *)databaseFilePath]; return [self convertDatabase:(NSString *)databaseFilePath databasePassword:databasePassword];
} }
+ (nullable NSError *)convertDatabase:(NSString *)databaseFilePath + (nullable NSError *)convertDatabase:(NSString *)databaseFilePath databasePassword:(NSData *)databasePassword
{ {
OWSAssert(databaseFilePath.length > 0); OWSAssert(databaseFilePath.length > 0);
OWSAssert(databasePassword.length > 0);
NSError *error; NSData *headerData = [self readFirstNBytesOfDatabaseFile:databaseFilePath byteCount:kSqliteHeaderLength];
NSData *_Nullable databasePassword = [OWSStorage tryToLoadDatabasePassword:&error]; OWSAssert(headerData);
if (!databasePassword || error) {
return (error const NSUInteger kSQLCipherSaltLength = 16;
?: OWSErrorWithCodeDescription( OWSAssert(headerData.length >= kSQLCipherSaltLength);
OWSErrorCodeDatabaseConversionFatalError, @"Failed to load database password")); NSData *sqlCipherSaltData = [headerData subdataWithRange:NSMakeRange(0, kSQLCipherSaltLength)];
}
// TODO: // TODO: Write salt to keychain.
// Hello Matthew, // Hello Matthew,
// //
@ -181,139 +213,156 @@ const int kSqliteHeaderLength = 32;
return OWSErrorWithCodeDescription(OWSErrorCodeDatabaseConversionFatalError, @"Failed to set SQLCipher key"); return OWSErrorWithCodeDescription(OWSErrorCodeDatabaseConversionFatalError, @"Failed to set SQLCipher key");
} }
// TODO set plaintext pragma
// TODO modify first page
// TODO force checkpoint
// ----------------------------------------------------------- // -----------------------------------------------------------
// //
// This block was derived from [Yapdatabase configureDatabase]. // This block was derived from [Yapdatabase configureDatabase].
// {
// { //
// int status; // {
// // int status;
// // Set mandatory pragmas //
// // // Set mandatory pragmas
//
// MJK: this isn't relevant since we only migrate existing databses and never set a pageSize option.
// if (isNewDatabaseFile && (options.pragmaPageSize > 0))
// {
// NSString *pragma_page_size =
// [NSString stringWithFormat:@"PRAGMA page_size = %ld;", (long)options.pragmaPageSize];
//
// status = sqlite3_exec(db, [pragma_page_size UTF8String], NULL, NULL, NULL);
// if (status != SQLITE_OK)
// {
// YDBLogError(@"Error setting PRAGMA page_size: %d %s", status, sqlite3_errmsg(db));
// }
// }
//
status = sqlite3_exec(db, "PRAGMA journal_mode = WAL;", NULL, NULL, NULL);
if (status != SQLITE_OK) {
DDLogError(@"Error setting PRAGMA journal_mode: %d %s", status, sqlite3_errmsg(db));
return OWSErrorWithCodeDescription(OWSErrorCodeDatabaseConversionFatalError, @"Failed to set WAL mode");
}
// MJK: this isn't relevant since we only migrate existing databses and never set a pageSize option. // MJK: this isn't relevant since we only migrate existing databses
// if (isNewDatabaseFile && (options.pragmaPageSize > 0)) // if (isNewDatabaseFile)
// { // {
// NSString *pragma_page_size = // status = sqlite3_exec(db, "PRAGMA auto_vacuum = FULL; VACUUM;", NULL, NULL, NULL);
// [NSString stringWithFormat:@"PRAGMA page_size = %ld;", (long)options.pragmaPageSize]; // if (status != SQLITE_OK)
// // {
// status = sqlite3_exec(db, [pragma_page_size UTF8String], NULL, NULL, NULL); // YDBLogError(@"Error setting PRAGMA auto_vacuum: %d %s", status, sqlite3_errmsg(db));
// if (status != SQLITE_OK) // }
// { // }
// YDBLogError(@"Error setting PRAGMA page_size: %d %s", status, sqlite3_errmsg(db)); //
// }
// } // TODO verify we need to do this.
// Set synchronous to normal for THIS sqlite instance.
//
// This does NOT affect normal connections.
// That is, this does NOT affect YapDatabaseConnection instances.
// The sqlite connections of normal YapDatabaseConnection instances will follow the set pragmaSynchronous value.
//
// The reason we hardcode normal for this sqlite instance is because
// it's only used to write the initial snapshot value.
// And this doesn't need to be durable, as it is initialized to zero everytime.
//
// (This sqlite db is also used to perform checkpoints.
// But a normal value won't affect these operations,
// as they will perform sync operations whether the connection is normal or full.)
status = sqlite3_exec(db, "PRAGMA synchronous = NORMAL;", NULL, NULL, NULL);
if (status != SQLITE_OK) {
DDLogError(@"Error setting PRAGMA synchronous: %d %s", status, sqlite3_errmsg(db));
// This isn't critical, so we can continue.
}
// // Set journal_size_imit.
status = sqlite3_exec(db, "PRAGMA journal_mode = WAL;", NULL, NULL, NULL); //
if (status != SQLITE_OK) { // We only need to do set this pragma for THIS connection,
DDLogError(@"Error setting PRAGMA journal_mode: %d %s", status, sqlite3_errmsg(db)); // because it is the only connection that performs checkpoints.
return OWSErrorWithCodeDescription(OWSErrorCodeDatabaseConversionFatalError, @"Failed to set WAL mode");
}
// MJK: this isn't relevant since we only migrate existing databses NSInteger defaultPragmaJournalSizeLimit = 0;
// if (isNewDatabaseFile) NSString *pragma_journal_size_limit =
// { [NSString stringWithFormat:@"PRAGMA journal_size_limit = %ld;", (long)defaultPragmaJournalSizeLimit];
// status = sqlite3_exec(db, "PRAGMA auto_vacuum = FULL; VACUUM;", NULL, NULL, NULL);
// if (status != SQLITE_OK)
// {
// YDBLogError(@"Error setting PRAGMA auto_vacuum: %d %s", status, sqlite3_errmsg(db));
// }
// }
//
// TODO verify we need to do this. status = sqlite3_exec(db, [pragma_journal_size_limit UTF8String], NULL, NULL, NULL);
// Set synchronous to normal for THIS sqlite instance. if (status != SQLITE_OK) {
// DDLogError(@"Error setting PRAGMA journal_size_limit: %d %s", status, sqlite3_errmsg(db));
// This does NOT affect normal connections. // This isn't critical, so we can continue.
// That is, this does NOT affect YapDatabaseConnection instances. }
// The sqlite connections of normal YapDatabaseConnection instances will follow the set pragmaSynchronous value. //
// // // Set mmap_size (if needed).
// The reason we hardcode normal for this sqlite instance is because // //
// it's only used to write the initial snapshot value. // // This configures memory mapped I/O.
// And this doesn't need to be durable, as it is initialized to zero everytime. // // OWS: we currently don't set options.pragmaMMapSize, so we can ignore this code.
// // if (options.pragmaMMapSize > 0)
// (This sqlite db is also used to perform checkpoints. // {
// But a normal value won't affect these operations, // NSString *pragma_mmap_size =
// as they will perform sync operations whether the connection is normal or full.) // [NSString stringWithFormat:@"PRAGMA mmap_size = %ld;", (long)options.pragmaMMapSize];
status = sqlite3_exec(db, "PRAGMA synchronous = NORMAL;", NULL, NULL, NULL); //
if (status != SQLITE_OK) { // status = sqlite3_exec(db, [pragma_mmap_size UTF8String], NULL, NULL, NULL);
DDLogError(@"Error setting PRAGMA synchronous: %d %s", status, sqlite3_errmsg(db)); // if (status != SQLITE_OK)
// This isn't critical, so we can continue. // {
// YDBLogError(@"Error setting PRAGMA mmap_size: %d %s", status, sqlite3_errmsg(db));
// // This isn't critical, so we can continue.
// }
// }
//
// Disable autocheckpointing.
//
// YapDatabase has its own optimized checkpointing algorithm built-in.
// It knows the state of every active connection for the database,
// so it can invoke the checkpoint methods at the precise time in which a checkpoint can be most effective.
sqlite3_wal_autocheckpoint(db, 0);
// END DB setup copied from YapDatabase
// BEGIN SQLCipher migration
} }
// Set journal_size_imit.
//
// We only need to do set this pragma for THIS connection,
// because it is the only connection that performs checkpoints.
NSInteger defaultPragmaJournalSizeLimit = 0;
NSString *pragma_journal_size_limit =
[NSString stringWithFormat:@"PRAGMA journal_size_limit = %ld;", (long)defaultPragmaJournalSizeLimit];
status = sqlite3_exec(db, [pragma_journal_size_limit UTF8String], NULL, NULL, NULL); // -----------------------------------------------------------
if (status != SQLITE_OK) {
DDLogError(@"Error setting PRAGMA journal_size_limit: %d %s", status, sqlite3_errmsg(db));
// This isn't critical, so we can continue.
}
//
// // Set mmap_size (if needed).
// //
// // This configures memory mapped I/O.
// // OWS: we currently don't set options.pragmaMMapSize, so we can ignore this code.
// if (options.pragmaMMapSize > 0)
// {
// NSString *pragma_mmap_size =
// [NSString stringWithFormat:@"PRAGMA mmap_size = %ld;", (long)options.pragmaMMapSize];
//
// status = sqlite3_exec(db, [pragma_mmap_size UTF8String], NULL, NULL, NULL);
// if (status != SQLITE_OK)
// {
// YDBLogError(@"Error setting PRAGMA mmap_size: %d %s", status, sqlite3_errmsg(db));
// // This isn't critical, so we can continue.
// }
// }
//
// Disable autocheckpointing.
// //
// YapDatabase has its own optimized checkpointing algorithm built-in. // SQLCipher migration
// It knows the state of every active connection for the database,
// so it can invoke the checkpoint methods at the precise time in which a checkpoint can be most effective.
sqlite3_wal_autocheckpoint(db, 0);
// END DB setup copied from YapDatabase
// BEGIN SQLCipher migration
NSString *setPlainTextHeaderPragma =
[NSString stringWithFormat:@"PRAGMA cipher_plaintext_header_size = %d;", kSqliteHeaderLength];
status = sqlite3_exec(db, [setPlainTextHeaderPragma UTF8String], NULL, NULL, NULL);
if (status != SQLITE_OK) {
DDLogError(@"Error setting PRAGMA cipher_plaintext_header_size = %d: status: %d, error: %s",
kSqliteHeaderLength,
status,
sqlite3_errmsg(db));
return OWSErrorWithCodeDescription(
OWSErrorCodeDatabaseConversionFatalError, @"Failed to set PRAGMA cipher_plaintext_header_size");
}
// Modify the first page, so that SQLCipher will overwrite, respecting our new cipher_plaintext_header_size // if (NO)
NSString *tableName = [NSString stringWithFormat:@"signal-migration-%@", [NSUUID new].UUIDString]; // {
NSString *modificationSQL = //
[NSString stringWithFormat:@"CREATE TABLE %@(int a); INSERT INTO %@(a) VALUES (1);", tableName, tableName]; // NSString *setPlainTextHeaderPragma =
status = sqlite3_exec(db, [modificationSQL UTF8String], NULL, NULL, NULL); // [NSString stringWithFormat:@"PRAGMA cipher_plaintext_header_size = %zd;", kSqliteHeaderLength];
if (status != SQLITE_OK) { //
DDLogError(@"%@ Error modifying first page: %d, error: %s", self.logTag, status, sqlite3_errmsg(db)); // status = sqlite3_exec(db, [setPlainTextHeaderPragma UTF8String], NULL, NULL, NULL);
return OWSErrorWithCodeDescription(OWSErrorCodeDatabaseConversionFatalError, @"Error modifying first page"); // if (status != SQLITE_OK) {
} // DDLogError(@"Error setting PRAGMA cipher_plaintext_header_size = %zd: status: %d, error: %s",
// kSqliteHeaderLength,
// status,
// sqlite3_errmsg(db));
// return OWSErrorWithCodeDescription(
// OWSErrorCodeDatabaseConversionFatalError, @"Failed to set PRAGMA
// cipher_plaintext_header_size");
// }
//
// // Modify the first page, so that SQLCipher will overwrite, respecting our new cipher_plaintext_header_size
// NSString *tableName = [NSString stringWithFormat:@"signal-migration-%@", [NSUUID new].UUIDString];
// NSString *modificationSQL =
// [NSString stringWithFormat:@"CREATE TABLE %@(int a); INSERT INTO %@(a) VALUES (1);", tableName, tableName];
// status = sqlite3_exec(db, [modificationSQL UTF8String], NULL, NULL, NULL);
// if (status != SQLITE_OK) {
// DDLogError(@"%@ Error modifying first page: %d, error: %s", self.logTag, status, sqlite3_errmsg(db));
// return OWSErrorWithCodeDescription(OWSErrorCodeDatabaseConversionFatalError, @"Error modifying first
// page");
// }
//
// // Force a checkpoint so that the plaintext is written to the actual DB file, not just living in the WAL.
// // TODO do we need/want the earlier checkpoint if we're checkpointing here?
// sqlite3_wal_autocheckpoint(db, 0);
//
//
// sqlite3_close(db);
// return nil;
// }
// Force a checkpoint so that the plaintext is written to the actual DB file, not just living in the WAL. // TODO set plaintext pragma
// TODO do we need/want the earlier checkpoint if we're checkpointing here? // TODO modify first page
sqlite3_wal_autocheckpoint(db, 0); // TODO force checkpoint
return nil; return nil;
} }

Loading…
Cancel
Save