From 14bd58e741460ec5a601b52e82e154fcebc5ad83 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 22 Sep 2022 14:16:01 -0500 Subject: [PATCH] Prevents simultaneous database access from multiple AltStores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AltStore now sends a “WillAccessDatabase” notification before loading the persistent store, which causes other AltStore instances in memory to exit (either immediately, or upon returning to foreground). This prevents multiple AltStore instances from simultaneously accessing the same database, which could result in corrupted data (especially if they used different database model versions). --- AltStoreCore/Model/DatabaseManager.swift | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/AltStoreCore/Model/DatabaseManager.swift b/AltStoreCore/Model/DatabaseManager.swift index 9d517039..b91a7e8e 100644 --- a/AltStoreCore/Model/DatabaseManager.swift +++ b/AltStoreCore/Model/DatabaseManager.swift @@ -11,6 +11,15 @@ import CoreData import AltSign import Roxas +extension CFNotificationName +{ + fileprivate static let willAccessDatabase = CFNotificationName("com.rileytestut.AltStore.WillAccessDatabase" as CFString) +} + +private let ReceivedWillAccessDatabaseNotification: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void = { (center, observer, name, object, userInfo) in + DatabaseManager.shared.receivedWillAccessDatabaseNotification() +} + fileprivate class PersistentContainer: RSTPersistentContainer { override class func defaultDirectoryURL() -> URL @@ -43,10 +52,15 @@ public class DatabaseManager private let coordinator = NSFileCoordinator() private let coordinatorQueue = OperationQueue() + private var ignoreWillAccessDatabaseNotification = false + private init() { self.persistentContainer = PersistentContainer(name: "AltStore", bundle: Bundle(for: DatabaseManager.self)) self.persistentContainer.preferredMergePolicy = MergePolicy() + + let observer = Unmanaged.passUnretained(self).toOpaque() + CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, ReceivedWillAccessDatabaseNotification, CFNotificationName.willAccessDatabase.rawValue, nil, .deliverImmediately) } } @@ -73,6 +87,10 @@ public extension DatabaseManager guard !self.isStarted else { return finish(nil) } + // Quit any other running AltStore processes to prevent concurrent database access during and after migration. + self.ignoreWillAccessDatabaseNotification = true + CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), .willAccessDatabase, nil, nil, true) + self.migrateDatabaseToAppGroupIfNeeded { (result) in switch result { @@ -398,4 +416,14 @@ private extension DatabaseManager } } } + + func receivedWillAccessDatabaseNotification() + { + defer { self.ignoreWillAccessDatabaseNotification = false } + + // Ignore notifications sent by the current process. + guard !self.ignoreWillAccessDatabaseNotification else { return } + + exit(104) + } }