diff --git a/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/data/SingletonModule.kt b/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/data/SingletonModule.kt index 8b26f2e..c818346 100644 --- a/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/data/SingletonModule.kt +++ b/app/src/main/java/com/github/nullptroma/wallenc/app/di/modules/data/SingletonModule.kt @@ -2,7 +2,10 @@ package com.github.nullptroma.wallenc.app.di.modules.data import android.content.Context import com.github.nullptroma.wallenc.app.di.modules.app.IoDispatcher +import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyDao +import com.github.nullptroma.wallenc.data.vaults.UnlockManager import com.github.nullptroma.wallenc.data.vaults.VaultsManager +import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import dagger.Module import dagger.Provides @@ -21,4 +24,14 @@ class SingletonModule { @ApplicationContext context: Context): IVaultsManager { return VaultsManager(ioDispatcher, context) } + + @Provides + @Singleton + fun provideUnlockManager(@IoDispatcher ioDispatcher: CoroutineDispatcher, + dao: StorageKeyDao): IUnlockManager { + return UnlockManager( + dao = dao, + ioDispatcher = ioDispatcher + ) + } } \ No newline at end of file diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/UnlockManager.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/UnlockManager.kt index 577de69..1ffc7dd 100644 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/UnlockManager.kt +++ b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/UnlockManager.kt @@ -1,14 +1,17 @@ package com.github.nullptroma.wallenc.data.vaults +import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyDao import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey +import com.github.nullptroma.wallenc.domain.encrypt.EncryptedStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import java.util.UUID -class UnlockManager: IUnlockManager { - private val _openedStorages = MutableStateFlow>(mapOf()) +class UnlockManager(dao: StorageKeyDao, ioDispatcher: CoroutineDispatcher): IUnlockManager { + private val _openedStorages = MutableStateFlow>(mapOf()) override val openedStorages: StateFlow> get() = _openedStorages diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorage.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorage.kt index 0889354..6e2fcb2 100644 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorage.kt +++ b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorage.kt @@ -1,29 +1,89 @@ package com.github.nullptroma.wallenc.data.vaults.local +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo import com.github.nullptroma.wallenc.domain.interfaces.IStorage -import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import java.util.UUID class LocalStorage( override val uuid: UUID, - override val isEncrypted: Boolean, absolutePath: String, - ioDispatcher: CoroutineDispatcher + ioDispatcher: CoroutineDispatcher, ) : IStorage { override val size: StateFlow get() = accessor.size override val numberOfFiles: StateFlow get() = accessor.numberOfFiles - override val name: StateFlow - get() = TODO("Добавить класс в Domain, который с помощью accessor будет читать и сохранять имя в скрытую папку") override val isAvailable: StateFlow get() = accessor.isAvailable - override val accessor: IStorageAccessor = LocalStorageAccessor(absolutePath, ioDispatcher) + override val accessor = LocalStorageAccessor(absolutePath, ioDispatcher) + + private val _encInfo = MutableStateFlow(null) + override val encInfo: StateFlow + get() = _encInfo + override val name: StateFlow + get() = TODO("Добавить класс в Domain, который с помощью accessor будет читать и сохранять имя в скрытую папку") + + private val encInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX" + + suspend fun init() { + accessor.init() + readEncInfo() + } + + private suspend fun readEncInfo() { + accessor.touchFile(encInfoFileName) + accessor.setHidden(encInfoFileName, true) + val reader = accessor.openRead(encInfoFileName) + var enc: StorageEncryptionInfo? = null + try { + enc = _jackson.readValue(reader, StorageEncryptionInfo::class.java) + reader.close() + } + catch(e: Exception) { + reader.close() + // чтение не удалось, значит нужно записать файл + enc = StorageEncryptionInfo( + isEncrypted = false, + encryptedTestData = null + ) + val writer = accessor.openWrite(encInfoFileName) + try { + _jackson.writeValue(writer, enc) + } + catch (e: Exception) { + TODO("Это никогда не должно произойти") + } + writer.close() + } + _encInfo.value = enc + } + + suspend fun setEncInfo(enc: StorageEncryptionInfo) { + accessor.touchFile(encInfoFileName) + accessor.setHidden(encInfoFileName, true) + + val writer = accessor.openWrite(encInfoFileName) + try { + _jackson.writeValue(writer, enc) + } + catch (e: Exception) { + TODO("Это никогда не должно произойти") + } + writer.close() + _encInfo.value = enc + } override suspend fun rename(newName: String) { TODO("Not yet implemented") } + + companion object { + const val ENC_INFO_FILE_POSTFIX = ".enc-info" + private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() } + } } \ No newline at end of file diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt index 6bf791d..928df63 100644 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt +++ b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalStorageAccessor.kt @@ -10,9 +10,9 @@ import com.github.nullptroma.wallenc.domain.datatypes.DataPackage import com.github.nullptroma.wallenc.domain.datatypes.DataPage import com.github.nullptroma.wallenc.domain.interfaces.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IFile +import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -20,13 +20,13 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File import java.io.InputStream import java.io.OutputStream import java.nio.file.Files import java.nio.file.Path +import java.time.Clock import kotlin.io.path.Path import kotlin.io.path.absolute import kotlin.io.path.fileSize @@ -38,7 +38,6 @@ class LocalStorageAccessor( private val ioDispatcher: CoroutineDispatcher ) : IStorageAccessor { private val _filesystemBasePath: Path = Path(filesystemBasePath).normalize().absolute() - private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() } private val _size = MutableStateFlow(null) override val size: StateFlow = _size @@ -55,11 +54,9 @@ class LocalStorageAccessor( private val _dirsUpdates = MutableSharedFlow>() override val dirsUpdates: SharedFlow> = _dirsUpdates - init { + suspend fun init() { // запускам сканирование хранилища - CoroutineScope(ioDispatcher).launch { - scanSizeAndNumOfFiles() - } + scanSizeAndNumOfFiles() } /** @@ -201,6 +198,10 @@ class LocalStorageAccessor( val filePath = Path(filesystemBasePath.pathString, storagePath) return from(filesystemBasePath, filePath.toFile()) } + + fun from(filesystemBasePath: Path, meta: IMetaInfo): LocalStorageFilePair? { + return from(filesystemBasePath, meta.path) + } } } @@ -227,7 +228,7 @@ class LocalStorageAccessor( } val pair = LocalStorageFilePair.from(_filesystemBasePath, file) - if(pair != null) { + if (pair != null) { workedFiles.add(pair.file.absolutePath) workedMetaFiles.add(pair.metaFile.absolutePath) @@ -259,7 +260,7 @@ class LocalStorageAccessor( size += CommonFile.metaInfo.size numOfFiles++ - if(numOfFiles % DATA_PAGE_LENGTH == 0) { + if (numOfFiles % DATA_PAGE_LENGTH == 0) { _size.value = size _numberOfFiles.value = numOfFiles } @@ -379,22 +380,58 @@ class LocalStorageAccessor( emit(page) }.flowOn(ioDispatcher) - private fun writeMeta(metaFile: File, meta: CommonMetaInfo) { + override suspend fun getFileInfo(path: String): IFile { + val pair = LocalStorageFilePair.from(_filesystemBasePath, path) + ?: throw Exception("Что то пошло не так") // TODO + return CommonFile( + metaInfo = pair.meta, + ) + } + + override suspend fun getDirInfo(path: String): IDirectory { + val pair = LocalStorageFilePair.from(_filesystemBasePath, path) + ?: throw Exception("Что то пошло не так") // TODO + return CommonDirectory( + metaInfo = pair.meta, + elementsCount = null + ) + } + + override suspend fun setHidden(path: String, hidden: Boolean) { + val pair = LocalStorageFilePair.from(_filesystemBasePath, path) + ?: throw Exception("Что то пошло не так") // TODO + if(pair.meta.isHidden == hidden) + return + val newMeta = pair.meta.copy(isHidden = hidden) + writeMeta(pair.metaFile, newMeta) + _filesUpdates.emit( + DataPage( + list = listOf(CommonFile( + metaInfo = newMeta + )), + pageLength = 1, + pageIndex = 0 + ) + ) + } + + + private fun writeMeta(metaFile: File, meta: IMetaInfo) { _jackson.writeValue(metaFile, meta) } private fun createFile(storagePath: String): CommonFile { val path = Path(_filesystemBasePath.pathString, storagePath) val file = path.toFile() - if(file.exists() && file.isDirectory) { + if (file.exists() && file.isDirectory) { throw Exception("Что то пошло не так") // TODO - } - else { + } else { file.createNewFile() } - val pair = LocalStorageFilePair.from(_filesystemBasePath, file) ?: throw Exception("Что то пошло не так") // TODO - val newMeta = pair.meta.copy(lastModified = java.time.Clock.systemUTC().instant()) + val pair = LocalStorageFilePair.from(_filesystemBasePath, file) + ?: throw Exception("Что то пошло не так") // TODO + val newMeta = pair.meta.copy(lastModified = Clock.systemUTC().instant()) writeMeta(pair.metaFile, newMeta) return CommonFile(newMeta) } @@ -402,15 +439,15 @@ class LocalStorageAccessor( private fun createDir(storagePath: String): CommonDirectory { val path = Path(_filesystemBasePath.pathString, storagePath) val file = path.toFile() - if(file.exists() && !file.isDirectory) { + if (file.exists() && !file.isDirectory) { throw Exception("Что то пошло не так") // TODO - } - else { + } else { Files.createDirectories(path) } - val pair = LocalStorageFilePair.from(_filesystemBasePath, file) ?: throw Exception("Что то пошло не так") // TODO - val newMeta = pair.meta.copy(lastModified = java.time.Clock.systemUTC().instant()) + val pair = LocalStorageFilePair.from(_filesystemBasePath, file) + ?: throw Exception("Что то пошло не так") // TODO + val newMeta = pair.meta.copy(lastModified = Clock.systemUTC().instant()) writeMeta(pair.metaFile, newMeta) return CommonDirectory(newMeta, 0) } @@ -425,7 +462,7 @@ class LocalStorageAccessor( override suspend fun delete(path: String) = withContext(ioDispatcher) { val pair = LocalStorageFilePair.from(_filesystemBasePath, path) - if(pair != null) { + if (pair != null) { pair.file.delete() pair.metaFile.delete() } @@ -433,17 +470,20 @@ class LocalStorageAccessor( override suspend fun openWrite(path: String): OutputStream = withContext(ioDispatcher) { touchFile(path) - val pair = LocalStorageFilePair.from(_filesystemBasePath, path) ?: throw Exception("Файла нет") // TODO + val pair = LocalStorageFilePair.from(_filesystemBasePath, path) + ?: throw Exception("Файла нет") // TODO return@withContext pair.file.outputStream() } override suspend fun openRead(path: String): InputStream = withContext(ioDispatcher) { - val pair = LocalStorageFilePair.from(_filesystemBasePath, path) ?: throw Exception("Файла нет") // TODO + val pair = LocalStorageFilePair.from(_filesystemBasePath, path) + ?: throw Exception("Файла нет") // TODO return@withContext pair.file.inputStream() } override suspend fun moveToTrash(path: String) = withContext(ioDispatcher) { - val pair = LocalStorageFilePair.from(_filesystemBasePath, path) ?: throw Exception("Файла нет") // TODO + val pair = LocalStorageFilePair.from(_filesystemBasePath, path) + ?: throw Exception("Файла нет") // TODO val newMeta = pair.meta.copy(isDeleted = true) writeMeta(pair.metaFile, newMeta) } @@ -451,5 +491,6 @@ class LocalStorageAccessor( companion object { private const val META_INFO_POSTFIX = ".wallenc-meta" private const val DATA_PAGE_LENGTH = 10 + private val _jackson = jacksonObjectMapper().apply { findAndRegisterModules() } } } \ No newline at end of file diff --git a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalVault.kt b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalVault.kt index 512c441..32e2b14 100644 --- a/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalVault.kt +++ b/data/src/main/java/com/github/nullptroma/wallenc/data/vaults/local/LocalVault.kt @@ -1,7 +1,7 @@ package com.github.nullptroma.wallenc.data.vaults.local import android.content.Context -import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey +import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo import com.github.nullptroma.wallenc.domain.enums.VaultType import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IVault @@ -44,7 +44,7 @@ class LocalVault(private val ioDispatcher: CoroutineDispatcher, context: Context } } - private fun readStorages() { + private suspend fun readStorages() { val path = _path.value if (path == null || !_isAvailable.value) return @@ -53,12 +53,12 @@ class LocalVault(private val ioDispatcher: CoroutineDispatcher, context: Context if (dirs != null) { _storages.value = dirs.map { val uuid = UUID.fromString(it.name) - LocalStorage(uuid, false, it.path, ioDispatcher) + return@map LocalStorage(uuid, it.path, ioDispatcher).apply { init() } } } } - override suspend fun createStorage(): IStorage = withContext(ioDispatcher) { + override suspend fun createStorage(): LocalStorage = withContext(ioDispatcher) { val path = _path.value if (path == null || !_isAvailable.value) throw Exception("Not available") @@ -66,7 +66,8 @@ class LocalVault(private val ioDispatcher: CoroutineDispatcher, context: Context val uuid = UUID.randomUUID() val next = Path(path.path, uuid.toString()) next.createDirectory() - val newStorage = LocalStorage(uuid, false, next.pathString, ioDispatcher) + val newStorage = LocalStorage(uuid, next.pathString, ioDispatcher) + newStorage.init() _storages.value = _storages.value.toMutableList().apply { add(newStorage) } @@ -74,15 +75,9 @@ class LocalVault(private val ioDispatcher: CoroutineDispatcher, context: Context } override suspend fun createStorage( - key: EncryptKey - ): IStorage = withContext(ioDispatcher) { - TODO("Not yet implemented") - } + enc: StorageEncryptionInfo + ): LocalStorage = withContext(ioDispatcher) { - override suspend fun createStorage( - key: EncryptKey, - uuid: UUID - ): IStorage = withContext(ioDispatcher) { TODO("Not yet implemented") } diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonFile.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonFile.kt index 4199828..d25b84e 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonFile.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/common/impl/CommonFile.kt @@ -1,5 +1,6 @@ package com.github.nullptroma.wallenc.domain.common.impl import com.github.nullptroma.wallenc.domain.interfaces.IFile +import com.github.nullptroma.wallenc.domain.interfaces.IMetaInfo -data class CommonFile(override val metaInfo: CommonMetaInfo) : IFile \ No newline at end of file +data class CommonFile(override val metaInfo: IMetaInfo) : IFile \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/datatypes/StorageEncryptionInfo.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/datatypes/StorageEncryptionInfo.kt new file mode 100644 index 0000000..38f88ee --- /dev/null +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/datatypes/StorageEncryptionInfo.kt @@ -0,0 +1,6 @@ +package com.github.nullptroma.wallenc.domain.datatypes + +data class StorageEncryptionInfo( + val isEncrypted: Boolean, + val encryptedTestData: String? +) \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorage.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorage.kt index c8e672e..d18c665 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorage.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorage.kt @@ -1,6 +1,7 @@ package com.github.nullptroma.wallenc.domain.encrypt import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey +import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo import com.github.nullptroma.wallenc.domain.interfaces.ILogger import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorageAccessor @@ -13,7 +14,6 @@ class EncryptedStorage( key: EncryptKey, logger: ILogger, ioDispatcher: CoroutineDispatcher, - override val isEncrypted: Boolean ) : IStorage { override val size: StateFlow get() = TODO("Not yet implemented") @@ -25,6 +25,8 @@ class EncryptedStorage( get() = TODO("Not yet implemented") override val isAvailable: StateFlow get() = TODO("Not yet implemented") + override val encInfo: StateFlow + get() = TODO("Not yet implemented") override val accessor: IStorageAccessor = EncryptedStorageAccessor(source.accessor, key, logger, ioDispatcher) diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt index 1b0989e..d8cf3e1 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/encrypt/EncryptedStorageAccessor.kt @@ -191,6 +191,22 @@ class EncryptedStorageAccessor( return flow } + override suspend fun getFileInfo(path: String): IFile { + val file = source.getFileInfo(encryptPath(path)) + val meta = decryptMeta(file.metaInfo) + return CommonFile(meta) + } + + override suspend fun getDirInfo(path: String): IDirectory { + val dir = source.getDirInfo(encryptPath(path)) + val meta = decryptMeta(dir.metaInfo) + return CommonDirectory(meta, dir.elementsCount) + } + + override suspend fun setHidden(path: String, hidden: Boolean) { + source.setHidden(encryptPath(path), hidden) + } + override suspend fun touchFile(path: String) { source.touchFile(encryptPath(path)) } diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageAccessor.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageAccessor.kt index 6d8df9a..466b104 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageAccessor.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageAccessor.kt @@ -31,7 +31,9 @@ interface IStorageAccessor { * @return Поток директорий */ fun getDirsFlow(path: String): Flow>> - + suspend fun getFileInfo(path: String): IFile + suspend fun getDirInfo(path: String): IDirectory + suspend fun setHidden(path: String, hidden: Boolean) suspend fun touchFile(path: String) suspend fun touchDir(path: String) suspend fun delete(path: String) diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageInfo.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageInfo.kt index c958f3f..3774745 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageInfo.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IStorageInfo.kt @@ -1,13 +1,14 @@ package com.github.nullptroma.wallenc.domain.interfaces +import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo import kotlinx.coroutines.flow.StateFlow import java.util.UUID sealed interface IStorageInfo { + val uuid: UUID + val isAvailable: StateFlow val size: StateFlow val numberOfFiles: StateFlow - val uuid: UUID - val isEncrypted: Boolean - val name: StateFlow - val isAvailable: StateFlow + val encInfo: StateFlow + val name: StateFlow } \ No newline at end of file diff --git a/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IVault.kt b/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IVault.kt index bb22e50..48813ba 100644 --- a/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IVault.kt +++ b/domain/src/main/java/com/github/nullptroma/wallenc/domain/interfaces/IVault.kt @@ -1,14 +1,12 @@ package com.github.nullptroma.wallenc.domain.interfaces -import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey +import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo import kotlinx.coroutines.flow.StateFlow -import java.util.UUID interface IVault : IVaultInfo { override val storages: StateFlow> suspend fun createStorage(): IStorage - suspend fun createStorage(key: EncryptKey): IStorage - suspend fun createStorage(key: EncryptKey, uuid: UUID): IStorage + suspend fun createStorage(enc: StorageEncryptionInfo): IStorage suspend fun remove(storage: IStorage) } \ No newline at end of file