Better database loader implementation

This commit is contained in:
J-Jamet
2022-01-04 21:00:23 +01:00
parent 5c75c6c7d3
commit 8ff57e3004
13 changed files with 108 additions and 127 deletions

View File

@@ -60,7 +60,6 @@ class LoadDatabaseRunnable(private val context: Context,
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
LoadedKey.generateNewCipherKey(),
mFixDuplicateUUID,
progressTaskUpdater)
}

View File

@@ -35,21 +35,16 @@ class MergeDatabaseRunnable(private val context: Context,
private val mLoadDatabaseResult: ((Result) -> Unit)?)
: ActionRunnable() {
private var tempCipherKey: LoadedKey? = null
override fun onStartRun() {
tempCipherKey = mDatabase.binaryCache.loadedCipherKey
mDatabase.wasReloaded = true
}
override fun onActionRun() {
try {
mDatabase.mergeData(context.contentResolver,
UriUtil.getBinaryDir(context),
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
tempCipherKey ?: LoadedKey.generateNewCipherKey(),
progressTaskUpdater)
} catch (e: LoadDatabaseException) {
setError(e)
@@ -59,7 +54,6 @@ class MergeDatabaseRunnable(private val context: Context,
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
} else {
tempCipherKey = null
mDatabase.clearAndClose(context)
}
}

View File

@@ -35,10 +35,7 @@ class ReloadDatabaseRunnable(private val context: Context,
private val mLoadDatabaseResult: ((Result) -> Unit)?)
: ActionRunnable() {
private var tempCipherKey: LoadedKey? = null
override fun onStartRun() {
tempCipherKey = mDatabase.binaryCache.loadedCipherKey
// Clear before we load
mDatabase.clear(UriUtil.getBinaryDir(context))
mDatabase.wasReloaded = true
@@ -47,11 +44,9 @@ class ReloadDatabaseRunnable(private val context: Context,
override fun onActionRun() {
try {
mDatabase.reloadData(context.contentResolver,
UriUtil.getBinaryDir(context),
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
tempCipherKey ?: LoadedKey.generateNewCipherKey(),
progressTaskUpdater)
} catch (e: LoadDatabaseException) {
setError(e)
@@ -61,7 +56,6 @@ class ReloadDatabaseRunnable(private val context: Context,
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
} else {
tempCipherKey = null
mDatabase.clearAndClose(context)
}
}

View File

@@ -30,7 +30,6 @@ import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
import com.kunzisoft.keepass.database.element.binary.BinaryCache
import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.database.element.binary.LoadedKey
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.DatabaseKDB
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
@@ -143,7 +142,7 @@ class Database {
fun removeCustomIcon(customIcon: IconImageCustom) {
iconDrawableFactory.clearFromCache(customIcon)
iconsManager.removeCustomIcon(binaryCache, customIcon.uuid)
iconsManager.removeCustomIcon(customIcon.uuid)
mDatabaseKDBX?.addDeletedObject(customIcon.uuid)
}
@@ -575,7 +574,6 @@ class Database {
contentResolver: ContentResolver,
cacheDirectory: File,
isRAMSufficient: (memoryWanted: Long) -> Boolean,
tempCipherKey: LoadedKey,
fixDuplicateUUID: Boolean,
progressTaskUpdater: ProgressTaskUpdater?) {
@@ -596,22 +594,30 @@ class Database {
// Read database stream for the first time
readDatabaseStream(contentResolver, uri,
{ databaseInputStream ->
DatabaseInputKDB(cacheDirectory, isRAMSufficient)
.openDatabase(databaseInputStream,
mainCredential.masterPassword,
keyFileInputStream,
tempCipherKey,
progressTaskUpdater,
fixDuplicateUUID)
val databaseKDB = DatabaseKDB().apply {
binaryCache.cacheDirectory = cacheDirectory
changeDuplicateId = fixDuplicateUUID
}
DatabaseInputKDB(databaseKDB)
.openDatabase(databaseInputStream,
mainCredential.masterPassword,
keyFileInputStream,
progressTaskUpdater)
databaseKDB
},
{ databaseInputStream ->
DatabaseInputKDBX(cacheDirectory, isRAMSufficient)
.openDatabase(databaseInputStream,
mainCredential.masterPassword,
keyFileInputStream,
tempCipherKey,
progressTaskUpdater,
fixDuplicateUUID)
val databaseKDBX = DatabaseKDBX().apply {
binaryCache.cacheDirectory = cacheDirectory
changeDuplicateId = fixDuplicateUUID
}
DatabaseInputKDBX(databaseKDBX).apply {
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
openDatabase(databaseInputStream,
mainCredential.masterPassword,
keyFileInputStream,
progressTaskUpdater)
}
databaseKDBX
}
)
} catch (e: FileNotFoundException) {
@@ -628,43 +634,55 @@ class Database {
@Throws(LoadDatabaseException::class)
fun mergeData(contentResolver: ContentResolver,
cacheDirectory: File,
isRAMSufficient: (memoryWanted: Long) -> Boolean,
tempCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?) {
// New database instance to get new changes
val databaseToMerge = Database()
databaseToMerge.fileUri = this.fileUri
try {
databaseToMerge.fileUri?.let { databaseUri ->
// TODO Merge KDB
var databaseMerger: DatabaseKDBXMerger? = null
databaseToMerge.readDatabaseStream(contentResolver, databaseUri,
{ databaseInputStream ->
DatabaseInputKDB(cacheDirectory, isRAMSufficient)
val databaseKDB = DatabaseKDB()
this.mDatabaseKDB?.let {
databaseKDB.binaryCache = it.binaryCache
}
DatabaseInputKDB(databaseKDB)
.openDatabase(databaseInputStream,
masterKey,
tempCipherKey,
progressTaskUpdater)
databaseKDB
},
{ databaseInputStream ->
DatabaseInputKDBX(cacheDirectory, isRAMSufficient)
.openDatabase(databaseInputStream,
val databaseKDBX = DatabaseKDBX()
// Share cache
this.mDatabaseKDBX?.let {
databaseKDBX.binaryCache = it.binaryCache
}
databaseMerger = DatabaseKDBXMerger(databaseKDBX)
DatabaseInputKDBX(databaseKDBX).apply {
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
openDatabase(databaseInputStream,
masterKey,
tempCipherKey,
progressTaskUpdater)
}
databaseKDBX
}
)
databaseToMerge.mDatabaseKDBX?.let { databaseKDBXToMerge ->
databaseMerger?.merge(databaseKDBXToMerge)
}
} ?: run {
Log.e(TAG, "Database URI is null, database cannot be reloaded")
throw IODatabaseException()
}
// TODO Merge KDB
mDatabaseKDBX?.let { databaseKDBX ->
databaseToMerge.mDatabaseKDBX?.let { databaseKDBXToMerge ->
DatabaseKDBXMerger(databaseKDBX).merge(databaseKDBXToMerge)
}
}
} catch (e: Exception) {
throw LoadDatabaseException(e)
} finally {
@@ -674,9 +692,7 @@ class Database {
@Throws(LoadDatabaseException::class)
fun reloadData(contentResolver: ContentResolver,
cacheDirectory: File,
isRAMSufficient: (memoryWanted: Long) -> Boolean,
tempCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?) {
// Retrieve the stream from the old database URI
@@ -684,18 +700,28 @@ class Database {
fileUri?.let { oldDatabaseUri ->
readDatabaseStream(contentResolver, oldDatabaseUri,
{ databaseInputStream ->
DatabaseInputKDB(cacheDirectory, isRAMSufficient)
val databaseKDB = DatabaseKDB()
mDatabaseKDB?.let {
databaseKDB.binaryCache = it.binaryCache
}
DatabaseInputKDB(databaseKDB)
.openDatabase(databaseInputStream,
masterKey,
tempCipherKey,
progressTaskUpdater)
databaseKDB
},
{ databaseInputStream ->
DatabaseInputKDBX(cacheDirectory, isRAMSufficient)
.openDatabase(databaseInputStream,
val databaseKDBX = DatabaseKDBX()
mDatabaseKDBX?.let {
databaseKDBX.binaryCache = it.binaryCache
}
DatabaseInputKDBX(databaseKDBX).apply {
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
openDatabase(databaseInputStream,
masterKey,
tempCipherKey,
progressTaskUpdater)
}
databaseKDBX
}
)
} ?: run {

View File

@@ -28,29 +28,18 @@ import java.util.*
class DeletedObject : Parcelable {
var uuid: UUID = DatabaseVersioned.UUID_ZERO
private var mDeletionTime: DateInstant? = null
var deletionTime: DateInstant = DateInstant()
constructor()
constructor(uuid: UUID, deletionTime: DateInstant = DateInstant()) {
this.uuid = uuid
this.mDeletionTime = deletionTime
this.deletionTime = deletionTime
}
constructor(parcel: Parcel) {
uuid = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: DatabaseVersioned.UUID_ZERO
mDeletionTime = parcel.readParcelable(DateInstant::class.java.classLoader)
}
fun getDeletionTime(): DateInstant {
if (mDeletionTime == null) {
mDeletionTime = DateInstant(System.currentTimeMillis())
}
return mDeletionTime!!
}
fun setDeletionTime(deletionTime: DateInstant) {
this.mDeletionTime = deletionTime
deletionTime = parcel.readParcelable(DateInstant::class.java.classLoader) ?: deletionTime
}
override fun equals(other: Any?): Boolean {
@@ -69,7 +58,7 @@ class DeletedObject : Parcelable {
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(ParcelUuid(uuid), flags)
parcel.writeParcelable(mDeletionTime, flags)
parcel.writeParcelable(deletionTime, flags)
}
override fun describeContents(): Int {

View File

@@ -336,6 +336,10 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
iconsManager.addCustomIcon(customIconId, name, lastModificationTime, smallSize, result)
}
fun removeCustomIcon(iconUuid: UUID) {
iconsManager.removeCustomIcon(iconUuid)
}
fun isCustomIconBinaryDuplicate(binary: BinaryData): Boolean {
return iconsManager.isCustomIconBinaryDuplicate(binary)
}

View File

@@ -61,7 +61,7 @@ abstract class DatabaseVersioned<
* Can be used to temporarily store database elements
*/
var binaryCache = BinaryCache()
val iconsManager = IconsManager(binaryCache)
var iconsManager = IconsManager(binaryCache)
var attachmentPool = AttachmentPool(binaryCache)
var changeDuplicateId = false

View File

@@ -28,7 +28,7 @@ import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.K
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
import java.util.*
class IconsManager(binaryCache: BinaryCache) {
class IconsManager(private var binaryCache: BinaryCache) {
private val standardCache = List(NB_ICONS) {
IconImageStandard(it)
@@ -72,7 +72,7 @@ class IconsManager(binaryCache: BinaryCache) {
return customCache.isBinaryDuplicate(binaryData)
}
fun removeCustomIcon(binaryCache: BinaryCache, iconUuid: UUID) {
fun removeCustomIcon(iconUuid: UUID) {
val binary = customCache[iconUuid]
customCache.remove(iconUuid)
try {

View File

@@ -21,16 +21,12 @@ package com.kunzisoft.keepass.database.file.input
import android.util.Log
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.binary.LoadedKey
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import java.io.File
import java.io.InputStream
abstract class DatabaseInput<D : DatabaseVersioned<*, *, *, *>>
(protected val cacheDirectory: File,
protected val isRAMSufficient: (memoryWanted: Long) -> Boolean) {
abstract class DatabaseInput<D : DatabaseVersioned<*, *, *, *>> (protected var mDatabase: D) {
private var startTimeKey = System.currentTimeMillis()
private var startTimeContent = System.currentTimeMillis()
@@ -49,17 +45,13 @@ abstract class DatabaseInput<D : DatabaseVersioned<*, *, *, *>>
abstract fun openDatabase(databaseInputStream: InputStream,
password: String?,
keyfileInputStream: InputStream?,
loadedCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean = false): D
progressTaskUpdater: ProgressTaskUpdater?): D
@Throws(LoadDatabaseException::class)
abstract fun openDatabase(databaseInputStream: InputStream,
masterKey: ByteArray,
loadedCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean = false): D
progressTaskUpdater: ProgressTaskUpdater?): D
protected fun startKeyTimer(progressTaskUpdater: ProgressTaskUpdater?) {
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)

View File

@@ -23,7 +23,6 @@ package com.kunzisoft.keepass.database.file.input
import com.kunzisoft.encrypt.HashManager
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.element.binary.LoadedKey
import com.kunzisoft.keepass.database.element.database.DatabaseKDB
import com.kunzisoft.keepass.database.element.entry.EntryKDB
import com.kunzisoft.keepass.database.element.group.GroupKDB
@@ -46,21 +45,15 @@ import kotlin.collections.HashMap
/**
* Load a KDB database file.
*/
class DatabaseInputKDB(cacheDirectory: File,
isRAMSufficient: (memoryWanted: Long) -> Boolean)
: DatabaseInput<DatabaseKDB>(cacheDirectory, isRAMSufficient) {
private lateinit var mDatabase: DatabaseKDB
class DatabaseInputKDB(database: DatabaseKDB)
: DatabaseInput<DatabaseKDB>(database) {
@Throws(LoadDatabaseException::class)
override fun openDatabase(databaseInputStream: InputStream,
password: String?,
keyfileInputStream: InputStream?,
loadedCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean): DatabaseKDB {
return openDatabase(databaseInputStream, progressTaskUpdater, fixDuplicateUUID) {
mDatabase.binaryCache.loadedCipherKey = loadedCipherKey
progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDB {
return openDatabase(databaseInputStream, progressTaskUpdater) {
mDatabase.retrieveMasterKey(password, keyfileInputStream)
}
}
@@ -68,11 +61,8 @@ class DatabaseInputKDB(cacheDirectory: File,
@Throws(LoadDatabaseException::class)
override fun openDatabase(databaseInputStream: InputStream,
masterKey: ByteArray,
loadedCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean): DatabaseKDB {
return openDatabase(databaseInputStream, progressTaskUpdater, fixDuplicateUUID) {
mDatabase.binaryCache.loadedCipherKey = loadedCipherKey
progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDB {
return openDatabase(databaseInputStream, progressTaskUpdater) {
mDatabase.masterKey = masterKey
}
}
@@ -80,7 +70,6 @@ class DatabaseInputKDB(cacheDirectory: File,
@Throws(LoadDatabaseException::class)
private fun openDatabase(databaseInputStream: InputStream,
progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean,
assignMasterKey: (() -> Unit)? = null): DatabaseKDB {
try {
@@ -107,10 +96,6 @@ class DatabaseInputKDB(cacheDirectory: File,
throw VersionDatabaseException()
}
mDatabase = DatabaseKDB()
mDatabase.binaryCache.cacheDirectory = cacheDirectory
mDatabase.changeDuplicateId = fixDuplicateUUID
assignMasterKey?.invoke()
// Select algorithm

View File

@@ -63,12 +63,10 @@ import javax.crypto.CipherInputStream
import javax.crypto.Mac
import kotlin.math.min
class DatabaseInputKDBX(cacheDirectory: File,
isRAMSufficient: (memoryWanted: Long) -> Boolean)
: DatabaseInput<DatabaseKDBX>(cacheDirectory, isRAMSufficient) {
class DatabaseInputKDBX(database: DatabaseKDBX)
: DatabaseInput<DatabaseKDBX>(database) {
private var randomStream: StreamCipher? = null
private lateinit var mDatabase: DatabaseKDBX
private var hashOfHeader: ByteArray? = null
@@ -97,15 +95,18 @@ class DatabaseInputKDBX(cacheDirectory: File,
private var entryCustomDataKey: String? = null
private var entryCustomDataValue: String? = null
private var isRAMSufficient: (memoryWanted: Long) -> Boolean = {true}
fun setMethodToCheckIfRAMIsSufficient(method: (memoryWanted: Long) -> Boolean) {
this.isRAMSufficient = method
}
@Throws(LoadDatabaseException::class)
override fun openDatabase(databaseInputStream: InputStream,
password: String?,
keyfileInputStream: InputStream?,
loadedCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean): DatabaseKDBX {
return openDatabase(databaseInputStream, progressTaskUpdater, fixDuplicateUUID) {
mDatabase.binaryCache.loadedCipherKey = loadedCipherKey
progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDBX {
return openDatabase(databaseInputStream, progressTaskUpdater) {
mDatabase.retrieveMasterKey(password, keyfileInputStream)
}
}
@@ -113,11 +114,8 @@ class DatabaseInputKDBX(cacheDirectory: File,
@Throws(LoadDatabaseException::class)
override fun openDatabase(databaseInputStream: InputStream,
masterKey: ByteArray,
loadedCipherKey: LoadedKey,
progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean): DatabaseKDBX {
return openDatabase(databaseInputStream, progressTaskUpdater, fixDuplicateUUID) {
mDatabase.binaryCache.loadedCipherKey = loadedCipherKey
progressTaskUpdater: ProgressTaskUpdater?): DatabaseKDBX {
return openDatabase(databaseInputStream, progressTaskUpdater) {
mDatabase.masterKey = masterKey
}
}
@@ -125,14 +123,9 @@ class DatabaseInputKDBX(cacheDirectory: File,
@Throws(LoadDatabaseException::class)
private fun openDatabase(databaseInputStream: InputStream,
progressTaskUpdater: ProgressTaskUpdater?,
fixDuplicateUUID: Boolean,
assignMasterKey: (() -> Unit)? = null): DatabaseKDBX {
try {
startKeyTimer(progressTaskUpdater)
mDatabase = DatabaseKDBX()
mDatabase.binaryCache.cacheDirectory = cacheDirectory
mDatabase.changeDuplicateId = fixDuplicateUUID
val header = DatabaseHeaderKDBX(mDatabase)
@@ -704,7 +697,7 @@ class DatabaseInputKDBX(cacheDirectory: File,
KdbContext.DeletedObject -> if (name.equals(DatabaseKDBXXML.ElemUuid, ignoreCase = true)) {
ctxDeletedObject?.uuid = readUuid(xpp)
} else if (name.equals(DatabaseKDBXXML.ElemDeletionTime, ignoreCase = true)) {
ctxDeletedObject?.setDeletionTime(readDateInstant(xpp))
ctxDeletedObject?.deletionTime = readDateInstant(xpp)
} else {
readUnknown(xpp)
}

View File

@@ -592,7 +592,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
xml.startTag(null, DatabaseKDBXXML.ElemDeletedObject)
writeUuid(DatabaseKDBXXML.ElemUuid, value.uuid)
writeDateInstant(DatabaseKDBXXML.ElemDeletionTime, value.getDeletionTime())
writeDateInstant(DatabaseKDBXXML.ElemDeletionTime, value.deletionTime)
xml.endTag(null, DatabaseKDBXXML.ElemDeletedObject)
}

View File

@@ -52,17 +52,22 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
val deletedObjectId = deletedObject.uuid
val databaseEntry = database.getEntryById(deletedObjectId)
val databaseGroup = database.getGroupById(deletedObjectId)
val databaseIconModificationTime = database.getCustomIcon(deletedObjectId).lastModificationTime
if (databaseEntry != null
&& deletedObject.getDeletionTime().date
&& deletedObject.deletionTime.date
.after(databaseEntry.lastModificationTime.date)) {
database.removeEntryFrom(databaseEntry, databaseEntry.parent)
}
if (databaseGroup != null
&& deletedObject.getDeletionTime().date
&& deletedObject.deletionTime.date
.after(databaseGroup.lastModificationTime.date)) {
database.removeGroupFrom(databaseGroup, databaseGroup.parent)
}
// TODO Remove icon
if (databaseIconModificationTime != null
&& deletedObject.deletionTime.date
.after(databaseIconModificationTime.date)) {
database.removeCustomIcon(deletedObjectId)
}
}
}
@@ -83,7 +88,7 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
// If it's a deleted object, but another instance was updated
// If entry parent to add exists and in current database
if (deletedObject == null
|| deletedObject.getDeletionTime().date
|| deletedObject.deletionTime.date
.before(databaseEntryToMerge.lastModificationTime.date)
|| parentEntry != null) {
database.addEntryTo(databaseEntryToMerge, parentEntry)
@@ -120,7 +125,7 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
if (databaseGroup == null) {
// If group parent to add exists and in current database
if (deletedObject == null
|| deletedObject.getDeletionTime().date
|| deletedObject.deletionTime.date
.before(databaseGroupToMerge.lastModificationTime.date)
|| parentGroup != null) {
database.addGroupTo(databaseGroupToMerge, parentGroup)