Мета информация о хранилищах хранится в бд
This commit is contained in:
@@ -3,13 +3,17 @@ package com.github.nullptroma.wallenc.data.db.app
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
|
||||
import com.github.nullptroma.wallenc.data.db.app.dao.StorageMetaInfoDao
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageMetaInfo
|
||||
|
||||
interface IAppDb {
|
||||
val storageKeyMapDao: StorageKeyMapDao
|
||||
val storageMetaInfoDao: StorageMetaInfoDao
|
||||
}
|
||||
|
||||
@Database(entities = [DbStorageKeyMap::class], version = 2, exportSchema = false)
|
||||
@Database(entities = [DbStorageKeyMap::class, DbStorageMetaInfo::class], version = 2, exportSchema = false)
|
||||
abstract class AppDb : IAppDb, RoomDatabase() {
|
||||
abstract override val storageKeyMapDao: StorageKeyMapDao
|
||||
abstract override val storageMetaInfoDao: StorageMetaInfoDao
|
||||
}
|
||||
@@ -12,11 +12,11 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
@Dao
|
||||
interface StorageKeyMapDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun add(keymap: DbStorageKeyMap)
|
||||
suspend fun add(keymap: DbStorageKeyMap)
|
||||
|
||||
@Query("SELECT * FROM storage_key_maps")
|
||||
fun getAll(): List<DbStorageKeyMap>
|
||||
suspend fun getAll(): List<DbStorageKeyMap>
|
||||
|
||||
@Delete
|
||||
fun delete(keymap: DbStorageKeyMap)
|
||||
suspend fun delete(keymap: DbStorageKeyMap)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.github.nullptroma.wallenc.data.db.app.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.UUID
|
||||
|
||||
@Dao
|
||||
interface StorageMetaInfoDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun add(metaInfo: DbStorageMetaInfo)
|
||||
|
||||
@Query("SELECT * FROM storage_meta_infos")
|
||||
suspend fun getAll(): List<DbStorageMetaInfo>
|
||||
|
||||
@Query("SELECT * FROM storage_meta_infos")
|
||||
fun getAllFlow(): Flow<List<DbStorageMetaInfo>>
|
||||
|
||||
@Query("SELECT * FROM storage_meta_infos WHERE uuid == :uuid")
|
||||
fun getMetaInfoFlow(uuid: UUID): Flow<DbStorageMetaInfo>
|
||||
|
||||
@Query("SELECT * FROM storage_meta_infos WHERE uuid == :uuid")
|
||||
suspend fun getMetaInfo(uuid: UUID): DbStorageMetaInfo?
|
||||
|
||||
@Delete
|
||||
suspend fun delete(metaInfo: DbStorageMetaInfo)
|
||||
}
|
||||
@@ -9,14 +9,14 @@ import java.util.UUID
|
||||
|
||||
@Entity(tableName = "storage_key_maps", primaryKeys = [ "source_uuid", "dest_uuid" ])
|
||||
data class DbStorageKeyMap(
|
||||
@ColumnInfo(name = "source_uuid") val sourceUuid: String,
|
||||
@ColumnInfo(name = "dest_uuid") val destUuid: String,
|
||||
@ColumnInfo(name = "source_uuid") val sourceUuid: UUID,
|
||||
@ColumnInfo(name = "dest_uuid") val destUuid: UUID,
|
||||
@ColumnInfo(name = "key") val key: ByteArray
|
||||
) {
|
||||
fun toModel(): StorageKeyMap {
|
||||
return StorageKeyMap(
|
||||
sourceUuid = UUID.fromString(sourceUuid),
|
||||
destUuid = UUID.fromString(destUuid),
|
||||
sourceUuid = sourceUuid,
|
||||
destUuid = destUuid,
|
||||
key = EncryptKey(key)
|
||||
)
|
||||
}
|
||||
@@ -44,8 +44,8 @@ data class DbStorageKeyMap(
|
||||
companion object {
|
||||
fun fromModel(keymap: StorageKeyMap): DbStorageKeyMap {
|
||||
return DbStorageKeyMap(
|
||||
sourceUuid = keymap.sourceUuid.toString(),
|
||||
destUuid = keymap.destUuid.toString(),
|
||||
sourceUuid = keymap.sourceUuid,
|
||||
destUuid = keymap.destUuid,
|
||||
key = keymap.key.bytes
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.github.nullptroma.wallenc.data.db.app.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
|
||||
import java.util.UUID
|
||||
|
||||
@Entity(tableName = "storage_meta_infos")
|
||||
data class DbStorageMetaInfo(
|
||||
@PrimaryKey @ColumnInfo(name = "uuid") val uuid: UUID,
|
||||
@ColumnInfo(name = "meta_info") val metaInfoJson: String
|
||||
)
|
||||
@@ -3,14 +3,20 @@ package com.github.nullptroma.wallenc.data.db.app.repository
|
||||
import com.github.nullptroma.wallenc.data.db.app.dao.StorageKeyMapDao
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageKeyMap
|
||||
import com.github.nullptroma.wallenc.data.model.StorageKeyMap
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class StorageKeyMapRepository(private val dao: StorageKeyMapDao) {
|
||||
fun getAll() = dao.getAll().map { it.toModel() }
|
||||
fun add(keymap: StorageKeyMap) {
|
||||
class StorageKeyMapRepository(
|
||||
private val dao: StorageKeyMapDao,
|
||||
private val ioDispatcher: CoroutineDispatcher
|
||||
) {
|
||||
suspend fun getAll() = withContext(ioDispatcher) { dao.getAll().map { it.toModel() } }
|
||||
suspend fun add(keymap: StorageKeyMap) = withContext(ioDispatcher) {
|
||||
val dbModel = DbStorageKeyMap.fromModel(keymap)
|
||||
dao.add(dbModel)
|
||||
}
|
||||
fun delete(keymap: StorageKeyMap) {
|
||||
|
||||
suspend fun delete(keymap: StorageKeyMap) = withContext(ioDispatcher) {
|
||||
val dbModel = DbStorageKeyMap.fromModel(keymap)
|
||||
dao.delete(dbModel)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.github.nullptroma.wallenc.data.db.app.repository
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.github.nullptroma.wallenc.data.db.app.dao.StorageMetaInfoDao
|
||||
import com.github.nullptroma.wallenc.data.db.app.model.DbStorageMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.UUID
|
||||
|
||||
class StorageMetaInfoRepository(
|
||||
private val dao: StorageMetaInfoDao,
|
||||
private val ioDispatcher: CoroutineDispatcher
|
||||
) {
|
||||
fun getAllFlow() = dao.getAllFlow()
|
||||
suspend fun getAll() = withContext(ioDispatcher) { dao.getAll() }
|
||||
suspend fun getMeta(uuid: UUID): CommonStorageMetaInfo? = withContext(ioDispatcher) {
|
||||
val json = dao.getMetaInfo(uuid)?.metaInfoJson ?: return@withContext null
|
||||
return@withContext jackson.readValue(
|
||||
json,
|
||||
CommonStorageMetaInfo::class.java
|
||||
)
|
||||
}
|
||||
|
||||
fun observeMeta(uuid: UUID): Flow<CommonStorageMetaInfo> {
|
||||
return dao.getMetaInfoFlow(uuid)
|
||||
.map { jackson.readValue(it.metaInfoJson, CommonStorageMetaInfo::class.java) }
|
||||
}
|
||||
|
||||
suspend fun setMeta(uuid: UUID, metaInfo: CommonStorageMetaInfo) = withContext(ioDispatcher) {
|
||||
val json = jackson.writeValueAsString(metaInfo)
|
||||
dao.add(DbStorageMetaInfo(uuid, json))
|
||||
}
|
||||
|
||||
fun createSingleStorageProvider(uuid: UUID): SingleStorageMetaInfoProvider {
|
||||
return SingleStorageMetaInfoProvider(this, uuid)
|
||||
}
|
||||
|
||||
class SingleStorageMetaInfoProvider (
|
||||
private val repo: StorageMetaInfoRepository,
|
||||
val uuid: UUID
|
||||
) {
|
||||
suspend fun get(): CommonStorageMetaInfo? = repo.getMeta(uuid)
|
||||
suspend fun set(metaInfo: CommonStorageMetaInfo) = repo.setMeta(uuid, metaInfo)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val jackson = jacksonObjectMapper().apply { findAndRegisterModules() }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults
|
||||
package com.github.nullptroma.wallenc.data.storages
|
||||
|
||||
import com.github.nullptroma.wallenc.data.db.app.repository.StorageKeyMapRepository
|
||||
import com.github.nullptroma.wallenc.data.db.app.repository.StorageMetaInfoRepository
|
||||
import com.github.nullptroma.wallenc.data.model.StorageKeyMap
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.EncryptedStorage
|
||||
import com.github.nullptroma.wallenc.data.storages.encrypt.EncryptedStorage
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
|
||||
@@ -20,7 +21,8 @@ import kotlinx.coroutines.withContext
|
||||
import java.util.UUID
|
||||
|
||||
class UnlockManager(
|
||||
private val repo: StorageKeyMapRepository,
|
||||
private val keymapRepository: StorageKeyMapRepository,
|
||||
private val metaInfoRepository: StorageMetaInfoRepository,
|
||||
private val ioDispatcher: CoroutineDispatcher,
|
||||
vaultsManager: IVaultsManager
|
||||
) : IUnlockManager {
|
||||
@@ -33,7 +35,7 @@ class UnlockManager(
|
||||
CoroutineScope(ioDispatcher).launch {
|
||||
vaultsManager.allStorages.collectLatest {
|
||||
mutex.lock()
|
||||
val allKeys = repo.getAll()
|
||||
val allKeys = keymapRepository.getAll()
|
||||
val allStorages = it.associateBy({ it.uuid }, { it })
|
||||
val map = _openedStorages.value?.toMutableMap() ?: mutableMapOf()
|
||||
for(keymap in allKeys) {
|
||||
@@ -49,11 +51,12 @@ class UnlockManager(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createEncryptedStorage(storage: IStorage, key: EncryptKey, uuid: UUID): EncryptedStorage {
|
||||
return EncryptedStorage(
|
||||
_source = storage,
|
||||
private suspend fun createEncryptedStorage(storage: IStorage, key: EncryptKey, uuid: UUID): EncryptedStorage {
|
||||
return EncryptedStorage.create(
|
||||
source = storage,
|
||||
key = key,
|
||||
ioDispatcher = ioDispatcher,
|
||||
metaInfoProvider = metaInfoRepository.createSingleStorageProvider(uuid),
|
||||
uuid = uuid
|
||||
)
|
||||
}
|
||||
@@ -80,7 +83,7 @@ class UnlockManager(
|
||||
val encStorage = createEncryptedStorage(storage, keymap.key, keymap.destUuid)
|
||||
opened[storage.uuid] = encStorage
|
||||
_openedStorages.value = opened
|
||||
repo.add(keymap)
|
||||
keymapRepository.add(keymap)
|
||||
mutex.unlock()
|
||||
}
|
||||
|
||||
@@ -97,7 +100,7 @@ class UnlockManager(
|
||||
remove(storage.uuid)
|
||||
}
|
||||
enc.dispose()
|
||||
repo.delete(model)
|
||||
keymapRepository.delete(model)
|
||||
mutex.unlock()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.github.nullptroma.wallenc.data.storages.encrypt
|
||||
|
||||
import com.github.nullptroma.wallenc.data.db.app.repository.StorageMetaInfoRepository
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageMetaInfo
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.DisposableHandle
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.UUID
|
||||
|
||||
class EncryptedStorage private constructor(
|
||||
private val source: IStorage,
|
||||
key: EncryptKey,
|
||||
private val ioDispatcher: CoroutineDispatcher,
|
||||
private val metaInfoProvider: StorageMetaInfoRepository.SingleStorageMetaInfoProvider,
|
||||
override val uuid: UUID = UUID.randomUUID()
|
||||
) : IStorage, DisposableHandle {
|
||||
override val size: StateFlow<Long?>
|
||||
get() = source.size
|
||||
override val numberOfFiles: StateFlow<Int?>
|
||||
get() = source.numberOfFiles
|
||||
|
||||
private val _metaInfo = MutableStateFlow<IStorageMetaInfo>(
|
||||
CommonStorageMetaInfo()
|
||||
)
|
||||
override val metaInfo: StateFlow<IStorageMetaInfo>
|
||||
get() = _metaInfo
|
||||
override val isVirtualStorage: Boolean = true
|
||||
|
||||
override val isAvailable: StateFlow<Boolean>
|
||||
get() = source.isAvailable
|
||||
override val accessor: EncryptedStorageAccessor =
|
||||
EncryptedStorageAccessor(source.accessor, key, ioDispatcher)
|
||||
|
||||
private suspend fun init() {
|
||||
readMeta()
|
||||
}
|
||||
|
||||
private suspend fun readMeta() = withContext(ioDispatcher) {
|
||||
var meta = metaInfoProvider.get()
|
||||
if(meta == null) {
|
||||
meta = CommonStorageMetaInfo()
|
||||
metaInfoProvider.set(meta)
|
||||
}
|
||||
_metaInfo.value = meta
|
||||
}
|
||||
|
||||
override suspend fun rename(newName: String) = withContext(ioDispatcher) {
|
||||
val cur = _metaInfo.value
|
||||
val newMeta = CommonStorageMetaInfo(
|
||||
encInfo = cur.encInfo,
|
||||
name = newName
|
||||
)
|
||||
_metaInfo.value = newMeta
|
||||
metaInfoProvider.set(newMeta)
|
||||
}
|
||||
|
||||
override suspend fun setEncInfo(encInfo: StorageEncryptionInfo) = withContext(ioDispatcher) {
|
||||
val cur = _metaInfo.value
|
||||
val newMeta = CommonStorageMetaInfo(
|
||||
encInfo = encInfo,
|
||||
name = cur.name
|
||||
)
|
||||
_metaInfo.value = newMeta
|
||||
metaInfoProvider.set(newMeta)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
accessor.dispose()
|
||||
}
|
||||
|
||||
companion object {
|
||||
suspend fun create(
|
||||
source: IStorage,
|
||||
key: EncryptKey,
|
||||
ioDispatcher: CoroutineDispatcher,
|
||||
metaInfoProvider: StorageMetaInfoRepository.SingleStorageMetaInfoProvider,
|
||||
uuid: UUID = UUID.randomUUID()
|
||||
): EncryptedStorage = withContext(ioDispatcher) {
|
||||
val storage = EncryptedStorage(
|
||||
source = source,
|
||||
key = key,
|
||||
ioDispatcher = ioDispatcher,
|
||||
metaInfoProvider = metaInfoProvider,
|
||||
uuid = uuid
|
||||
)
|
||||
storage.init()
|
||||
return@withContext storage
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.github.nullptroma.wallenc.data.storages.encrypt
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonDirectory
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonFile
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonMetaInfo
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.DataPackage
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
||||
import com.github.nullptroma.wallenc.domain.encrypt.Encryptor
|
||||
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.DisposableHandle
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
class EncryptedStorageAccessor(
|
||||
private val source: IStorageAccessor,
|
||||
key: EncryptKey,
|
||||
ioDispatcher: CoroutineDispatcher
|
||||
) : IStorageAccessor, DisposableHandle {
|
||||
private val job = Job()
|
||||
private val scope = CoroutineScope(ioDispatcher + job)
|
||||
|
||||
override val size: StateFlow<Long?> = source.size
|
||||
override val numberOfFiles: StateFlow<Int?> = source.numberOfFiles
|
||||
override val isAvailable: StateFlow<Boolean> = source.isAvailable
|
||||
|
||||
private val _filesUpdates = MutableSharedFlow<DataPackage<List<IFile>>>()
|
||||
override val filesUpdates: SharedFlow<DataPackage<List<IFile>>> = _filesUpdates
|
||||
|
||||
private val _dirsUpdates = MutableSharedFlow<DataPackage<List<IDirectory>>>()
|
||||
override val dirsUpdates: SharedFlow<DataPackage<List<IDirectory>>> = _dirsUpdates
|
||||
|
||||
private val encryptor = Encryptor(key.toAesKey())
|
||||
|
||||
init {
|
||||
collectSourceState()
|
||||
}
|
||||
|
||||
private fun collectSourceState() {
|
||||
scope.launch {
|
||||
launch {
|
||||
source.filesUpdates.collect {
|
||||
val files = it.data.map(::decryptEntity)
|
||||
_filesUpdates.emit(DataPackage(
|
||||
data = files,
|
||||
isLoading = it.isLoading,
|
||||
isError = it.isError
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
|
||||
source.dirsUpdates.collect {
|
||||
val dirs = it.data.map(::decryptEntity)
|
||||
_dirsUpdates.emit(DataPackage(
|
||||
data = dirs,
|
||||
isLoading = it.isLoading,
|
||||
isError = it.isError
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun encryptEntity(file: IFile): IFile {
|
||||
return CommonFile(encryptMeta(file.metaInfo))
|
||||
}
|
||||
|
||||
private fun decryptEntity(file: IFile): IFile {
|
||||
return CommonFile(decryptMeta(file.metaInfo))
|
||||
}
|
||||
|
||||
private fun encryptEntity(dir: IDirectory): IDirectory {
|
||||
return CommonDirectory(encryptMeta(dir.metaInfo), dir.elementsCount)
|
||||
}
|
||||
|
||||
private fun decryptEntity(dir: IDirectory): IDirectory {
|
||||
return CommonDirectory(decryptMeta(dir.metaInfo), dir.elementsCount)
|
||||
}
|
||||
|
||||
private fun encryptMeta(meta: IMetaInfo): CommonMetaInfo {
|
||||
return CommonMetaInfo(
|
||||
size = meta.size,
|
||||
isDeleted = meta.isDeleted,
|
||||
isHidden = meta.isHidden,
|
||||
lastModified = meta.lastModified,
|
||||
path = encryptPath(meta.path)
|
||||
)
|
||||
}
|
||||
|
||||
private fun decryptMeta(meta: IMetaInfo): CommonMetaInfo {
|
||||
return CommonMetaInfo(
|
||||
size = meta.size,
|
||||
isDeleted = meta.isDeleted,
|
||||
isHidden = meta.isHidden,
|
||||
lastModified = meta.lastModified,
|
||||
path = decryptPath(meta.path)
|
||||
)
|
||||
}
|
||||
|
||||
private fun encryptPath(pathStr: String): String {
|
||||
val path = Path(pathStr)
|
||||
val segments = mutableListOf<String>()
|
||||
for (segment in path)
|
||||
segments.add(encryptor.encryptString(segment.pathString))
|
||||
val res = Path("/",*(segments.toTypedArray()))
|
||||
return res.pathString
|
||||
}
|
||||
|
||||
private fun decryptPath(pathStr: String): String {
|
||||
val path = Path(pathStr)
|
||||
val segments = mutableListOf<String>()
|
||||
for (segment in path)
|
||||
segments.add(encryptor.decryptString(segment.pathString))
|
||||
val res = Path("/",*(segments.toTypedArray()))
|
||||
return res.pathString
|
||||
}
|
||||
|
||||
override suspend fun getAllFiles(): List<IFile> {
|
||||
return source.getAllFiles().map(::decryptEntity)
|
||||
}
|
||||
|
||||
override suspend fun getFiles(path: String): List<IFile> {
|
||||
return source.getFiles(encryptPath(path)).map(::decryptEntity)
|
||||
}
|
||||
|
||||
override fun getFilesFlow(path: String): Flow<DataPackage<List<IFile>>> {
|
||||
val flow = source.getFilesFlow(encryptPath(path)).map {
|
||||
DataPackage(
|
||||
data = it.data.map(::decryptEntity),
|
||||
isLoading = it.isLoading,
|
||||
isError = it.isError
|
||||
)
|
||||
}
|
||||
return flow
|
||||
}
|
||||
|
||||
override suspend fun getAllDirs(): List<IDirectory> {
|
||||
return source.getAllDirs().map(::decryptEntity)
|
||||
}
|
||||
|
||||
override suspend fun getDirs(path: String): List<IDirectory> {
|
||||
return source.getDirs(encryptPath(path)).map(::decryptEntity)
|
||||
}
|
||||
|
||||
override fun getDirsFlow(path: String): Flow<DataPackage<List<IDirectory>>> {
|
||||
val flow = source.getDirsFlow(encryptPath(path)).map {
|
||||
DataPackage(
|
||||
data = it.data.map(::decryptEntity),
|
||||
isLoading = it.isLoading,
|
||||
isError = it.isError
|
||||
)
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
override suspend fun touchDir(path: String) {
|
||||
source.touchDir(encryptPath(path))
|
||||
}
|
||||
|
||||
override suspend fun delete(path: String) {
|
||||
source.delete(encryptPath(path))
|
||||
}
|
||||
|
||||
override suspend fun openWrite(path: String): OutputStream {
|
||||
val stream = source.openWrite(encryptPath(path))
|
||||
return encryptor.encryptStream(stream)
|
||||
}
|
||||
|
||||
override suspend fun openRead(path: String): InputStream {
|
||||
val stream = source.openRead(encryptPath(path))
|
||||
return encryptor.decryptStream(stream)
|
||||
}
|
||||
|
||||
override suspend fun moveToTrash(path: String) {
|
||||
source.moveToTrash(encryptPath(path))
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
job.cancel()
|
||||
encryptor.dispose()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults.local
|
||||
package com.github.nullptroma.wallenc.data.storages.local
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.github.nullptroma.wallenc.domain.common.impl.CommonStorageMetaInfo
|
||||
@@ -24,14 +24,8 @@ class LocalStorage(
|
||||
override val numberOfFiles: StateFlow<Int?>
|
||||
get() = accessor.numberOfFiles
|
||||
|
||||
private val _metaInfo = MutableStateFlow(
|
||||
CommonStorageMetaInfo(
|
||||
encInfo = StorageEncryptionInfo(
|
||||
isEncrypted = false,
|
||||
encryptedTestData = null
|
||||
),
|
||||
name = null
|
||||
)
|
||||
private val _metaInfo = MutableStateFlow<IStorageMetaInfo>(
|
||||
CommonStorageMetaInfo()
|
||||
)
|
||||
override val metaInfo: StateFlow<IStorageMetaInfo>
|
||||
get() = _metaInfo
|
||||
@@ -40,49 +34,60 @@ class LocalStorage(
|
||||
get() = accessor.isAvailable
|
||||
private val _accessor = LocalStorageAccessor(absolutePath, ioDispatcher)
|
||||
override val accessor: IStorageAccessor = _accessor
|
||||
|
||||
private val encInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX"
|
||||
override val isVirtualStorage: Boolean = false
|
||||
private val metaInfoFileName: String = "$uuid$ENC_INFO_FILE_POSTFIX"
|
||||
|
||||
suspend fun init() {
|
||||
_accessor.init()
|
||||
readEncInfo()
|
||||
readMetaInfo()
|
||||
}
|
||||
|
||||
private suspend fun readEncInfo() = withContext(ioDispatcher) {
|
||||
var enc: StorageEncryptionInfo? = null
|
||||
private suspend fun readMetaInfo() = withContext(ioDispatcher) {
|
||||
var meta: CommonStorageMetaInfo
|
||||
var reader: InputStream? = null
|
||||
try {
|
||||
reader = _accessor.openReadSystemFile(encInfoFileName)
|
||||
enc = jackson.readValue(reader, StorageEncryptionInfo::class.java)
|
||||
reader = _accessor.openReadSystemFile(metaInfoFileName)
|
||||
meta = jackson.readValue(reader, CommonStorageMetaInfo::class.java)
|
||||
}
|
||||
catch(e: Exception) {
|
||||
// чтение не удалось, значит нужно записать файл
|
||||
enc = StorageEncryptionInfo(
|
||||
isEncrypted = false,
|
||||
encryptedTestData = null
|
||||
)
|
||||
setEncInfo(enc)
|
||||
meta = CommonStorageMetaInfo()
|
||||
updateMetaInfo(meta)
|
||||
}
|
||||
finally {
|
||||
reader?.close()
|
||||
}
|
||||
_metaInfo.value = _metaInfo.value.copy(encInfo = enc)
|
||||
_metaInfo.value = meta
|
||||
}
|
||||
|
||||
suspend fun setEncInfo(enc: StorageEncryptionInfo) = withContext(ioDispatcher) {
|
||||
val writer = _accessor.openWriteSystemFile(encInfoFileName)
|
||||
private suspend fun updateMetaInfo(meta: IStorageMetaInfo) = withContext(ioDispatcher) {
|
||||
val writer = _accessor.openWriteSystemFile(metaInfoFileName)
|
||||
try {
|
||||
jackson.writeValue(writer, enc)
|
||||
jackson.writeValue(writer, meta)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
TODO("Это никогда не должно произойти")
|
||||
throw e
|
||||
}
|
||||
writer.close()
|
||||
_metaInfo.value = _metaInfo.value.copy(encInfo = enc)
|
||||
finally {
|
||||
writer.close()
|
||||
}
|
||||
_metaInfo.value = meta
|
||||
}
|
||||
|
||||
override suspend fun rename(newName: String) {
|
||||
TODO("Not yet implemented")
|
||||
override suspend fun rename(newName: String) = withContext(ioDispatcher) {
|
||||
val curMeta = metaInfo.value
|
||||
updateMetaInfo(CommonStorageMetaInfo(
|
||||
encInfo = curMeta.encInfo,
|
||||
name = newName
|
||||
))
|
||||
}
|
||||
|
||||
override suspend fun setEncInfo(encInfo: StorageEncryptionInfo) = withContext(ioDispatcher) {
|
||||
val curMeta = metaInfo.value
|
||||
updateMetaInfo(CommonStorageMetaInfo(
|
||||
encInfo = encInfo,
|
||||
name = curMeta.name
|
||||
))
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults.local
|
||||
package com.github.nullptroma.wallenc.data.storages.local
|
||||
|
||||
import com.fasterxml.jackson.core.JacksonException
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
@@ -57,7 +57,7 @@ class LocalStorageAccessor(
|
||||
private val _dirsUpdates = MutableSharedFlow<DataPage<IDirectory>>()
|
||||
override val dirsUpdates: SharedFlow<DataPage<IDirectory>> = _dirsUpdates
|
||||
|
||||
suspend fun init() {
|
||||
suspend fun init() = withContext(ioDispatcher) {
|
||||
// запускам сканирование хранилища
|
||||
scanSizeAndNumOfFiles()
|
||||
}
|
||||
@@ -251,11 +251,11 @@ class LocalStorageAccessor(
|
||||
* Считает файлы и их размер. Не бросает исключения, если файлы недоступны
|
||||
* @throws none Если возникла ошибка, оставляет размер и количества файлов равными null
|
||||
*/
|
||||
private suspend fun scanSizeAndNumOfFiles() {
|
||||
private suspend fun scanSizeAndNumOfFiles() = withContext(ioDispatcher) {
|
||||
if (!checkAvailable()) {
|
||||
_size.value = null
|
||||
_numberOfFiles.value = null
|
||||
return
|
||||
return@withContext
|
||||
}
|
||||
|
||||
var size = 0L
|
||||
@@ -280,8 +280,8 @@ class LocalStorageAccessor(
|
||||
return@withContext listOf()
|
||||
|
||||
val list = mutableListOf<IFile>()
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, CommonFile ->
|
||||
list.add(CommonFile)
|
||||
scanStorage(baseStoragePath = "/", maxDepth = -1, fileCallback = { _, commonFile ->
|
||||
list.add(commonFile)
|
||||
})
|
||||
return@withContext list
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults.local
|
||||
package com.github.nullptroma.wallenc.data.vaults
|
||||
|
||||
import android.content.Context
|
||||
import com.github.nullptroma.wallenc.data.storages.local.LocalStorage
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
|
||||
import com.github.nullptroma.wallenc.domain.enums.VaultType
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.github.nullptroma.wallenc.data.vaults
|
||||
|
||||
import android.content.Context
|
||||
import com.github.nullptroma.wallenc.data.vaults.local.LocalVault
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVault
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
|
||||
|
||||
Reference in New Issue
Block a user