remove corrupted snapshots when pruning
before a corrupted snapshot would DoS pruning
This commit is contained in:
parent
9339f9f0fb
commit
16c00be124
4 changed files with 38 additions and 3 deletions
|
@ -96,6 +96,7 @@ internal class Loader(
|
|||
}
|
||||
} catch (e: Exception) {
|
||||
log.error(e) { "Error writing cache file $cacheFile: " }
|
||||
cacheFile?.delete()
|
||||
}
|
||||
// get associated data for version, used for authenticated decryption
|
||||
val ad = crypto.getAdForVersion(version)
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.stevesoltys.seedvault.proto.Snapshot
|
|||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import org.calyxos.seedvault.core.backends.AppBackupFileType
|
||||
import org.calyxos.seedvault.core.backends.TopLevelFolder
|
||||
import java.security.GeneralSecurityException
|
||||
import java.time.LocalDate
|
||||
import java.time.temporal.ChronoField
|
||||
import java.time.temporal.TemporalAdjuster
|
||||
|
@ -42,9 +43,14 @@ internal class Pruner(
|
|||
val snapshotMap = mutableMapOf<Long, AppBackupFileType.Snapshot>()
|
||||
val snapshots = mutableListOf<Snapshot>()
|
||||
snapshotHandles.forEach { handle ->
|
||||
val snapshot = snapshotManager.loadSnapshot(handle) // exception is allowed to bubble up
|
||||
try {
|
||||
val snapshot = snapshotManager.loadSnapshot(handle)
|
||||
snapshotMap[snapshot.token] = handle
|
||||
snapshots.add(snapshot)
|
||||
} catch (e: GeneralSecurityException) {
|
||||
log.error(e) { "Error loading snapshot $handle, will remove: " }
|
||||
snapshotManager.removeSnapshot(handle)
|
||||
} // other exceptions (like IOException) are allowed to bubble up, so we try again
|
||||
}
|
||||
// find out which snapshots to keep
|
||||
val toKeep = getTokenToKeep(snapshotMap.keys)
|
||||
|
|
|
@ -113,6 +113,7 @@ internal class ApkBackup(
|
|||
val blobMap = chunkIds.associateWith { chunkId ->
|
||||
latestSnapshot.blobsMap[chunkId] ?: error("Missing blob for $chunkId")
|
||||
}
|
||||
// TODO could also check if all blobs are (still) available in BlobCache
|
||||
// important: add old APK to snapshot or it wouldn't be part of backup
|
||||
snapshotCreator.onApkBackedUp(packageInfo, oldApk, blobMap)
|
||||
return
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.calyxos.seedvault.core.backends.FileInfo
|
|||
import org.calyxos.seedvault.core.backends.TopLevelFolder
|
||||
import org.calyxos.seedvault.core.toHexString
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.security.GeneralSecurityException
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset.UTC
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -61,6 +62,32 @@ internal class PrunerTest : TransportTest() {
|
|||
pruner.removeOldSnapshotsAndPruneUnusedBlobs()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `corrupted snapshot gets removed`() = runBlocking {
|
||||
val snapshot = snapshot.copy { token = System.currentTimeMillis() }
|
||||
every { crypto.repoId } returns repoId
|
||||
every { backendManager.backend } returns backend
|
||||
coEvery {
|
||||
backend.list(folder, AppBackupFileType.Snapshot::class, callback = captureLambda())
|
||||
} answers {
|
||||
val fileInfo = FileInfo(snapshotHandle1, Random.nextLong(Long.MAX_VALUE))
|
||||
lambda<(FileInfo) -> Unit>().captured.invoke(fileInfo)
|
||||
}
|
||||
coEvery { snapshotManager.loadSnapshot(snapshotHandle1) } throws GeneralSecurityException()
|
||||
coEvery { snapshotManager.removeSnapshot(snapshotHandle1) } just Runs
|
||||
|
||||
// we only find blobs that are in snapshots
|
||||
val blobIds = snapshot.blobsMap.values.map { it.id.hexFromProto() }
|
||||
expectLoadingBlobs(blobIds)
|
||||
|
||||
// but since all snapshots got removed, we remove all blobs :(
|
||||
blobIds.forEach {
|
||||
coEvery { backend.remove(AppBackupFileType.Blob(repoId, it)) } just Runs
|
||||
}
|
||||
|
||||
pruner.removeOldSnapshotsAndPruneUnusedBlobs()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `three snapshots from same day don't remove blobs`() = runBlocking {
|
||||
val snapshotMap = mapOf(
|
||||
|
|
Loading…
Reference in a new issue