mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 20:06:51 +03:00
Use truncate mode to replace the contents of existing files
`ContentResolver.openOutputStream(Uri)` does not truncate existing files. If the amount of data written is smaller than the file size, you end up with new data at the beginning of the file followed by old data at the end of the file.
This commit is contained in:
parent
539d198f8f
commit
b9b5cab772
7 changed files with 13 additions and 12 deletions
1
changelog.d/5663.bugfix
Normal file
1
changelog.d/5663.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fixed key export when overwriting existing files
|
|
@ -34,7 +34,7 @@ class KeysExporter @Inject constructor(
|
|||
suspend fun export(password: String, uri: Uri) {
|
||||
withContext(dispatchers.io) {
|
||||
val data = session.cryptoService().exportRoomKeys(password)
|
||||
context.contentResolver.openOutputStream(uri)
|
||||
context.contentResolver.openOutputStream(uri, "wt")
|
||||
?.use { it.write(data) }
|
||||
?: throw IllegalStateException("Unable to open file for writing")
|
||||
verifyExportedKeysOutputFileSize(uri, expectedSize = data.size.toLong())
|
||||
|
|
|
@ -165,7 +165,7 @@ class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<Fr
|
|||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
Try {
|
||||
withContext(Dispatchers.IO) {
|
||||
requireContext().contentResolver.openOutputStream(uri)
|
||||
requireContext().contentResolver.openOutputStream(uri, "wt")
|
||||
?.use { os ->
|
||||
os.write(data.toByteArray())
|
||||
os.flush()
|
||||
|
|
|
@ -81,7 +81,7 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
|
|||
val uri = activityResult.data?.data ?: return@registerStartForActivityResult
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri)!!))
|
||||
sharedViewModel.handle(BootstrapActions.SaveKeyToUri(requireContext().contentResolver!!.openOutputStream(uri, "wt")!!))
|
||||
} catch (failure: Throwable) {
|
||||
sharedViewModel.handle(BootstrapActions.SaveReqFailed)
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ class KeyRequestsFragment @Inject constructor() : VectorBaseFragment<FragmentDev
|
|||
when (it) {
|
||||
is KeyRequestEvents.SaveAudit -> {
|
||||
tryOrNull {
|
||||
requireContext().contentResolver?.openOutputStream(it.uri)
|
||||
requireContext().contentResolver?.openOutputStream(it.uri, "wt")
|
||||
?.use { os -> os.write(it.raw.toByteArray()) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ class KeysExporterTest {
|
|||
@Test
|
||||
fun `when exporting then writes exported keys to context output stream`() {
|
||||
givenFileDescriptorWithSize(size = A_ROOM_KEYS_EXPORT.size.toLong())
|
||||
val outputStream = context.givenOutputStreamFor(A_URI)
|
||||
val outputStream = context.givenOutputStreamFor(A_URI, mode = "wt")
|
||||
|
||||
runTest { keysExporter.export(A_PASSWORD, A_URI) }
|
||||
|
||||
|
@ -63,7 +63,7 @@ class KeysExporterTest {
|
|||
@Test
|
||||
fun `given different file size returned for export when exporting then throws UnexpectedExportKeysFileSizeException`() {
|
||||
givenFileDescriptorWithSize(size = 110)
|
||||
context.givenOutputStreamFor(A_URI)
|
||||
context.givenOutputStreamFor(A_URI, mode = "wt")
|
||||
|
||||
assertFailsWith<UnexpectedExportKeysFileSizeException> {
|
||||
runTest { keysExporter.export(A_PASSWORD, A_URI) }
|
||||
|
@ -72,7 +72,7 @@ class KeysExporterTest {
|
|||
|
||||
@Test
|
||||
fun `given output stream is unavailable for exporting to when exporting then throws IllegalStateException`() {
|
||||
context.givenMissingOutputStreamFor(A_URI)
|
||||
context.givenMissingOutputStreamFor(A_URI, mode = "wt")
|
||||
|
||||
assertFailsWith<IllegalStateException>(message = "Unable to open file for writing") {
|
||||
runTest { keysExporter.export(A_PASSWORD, A_URI) }
|
||||
|
@ -82,7 +82,7 @@ class KeysExporterTest {
|
|||
@Test
|
||||
fun `given exported file is missing after export when exporting then throws IllegalStateException`() {
|
||||
context.givenFileDescriptor(A_URI, mode = "r") { null }
|
||||
context.givenOutputStreamFor(A_URI)
|
||||
context.givenOutputStreamFor(A_URI, mode = "wt")
|
||||
|
||||
assertFailsWith<IllegalStateException>(message = "Exported file not found") {
|
||||
runTest { keysExporter.export(A_PASSWORD, A_URI) }
|
||||
|
|
|
@ -39,13 +39,13 @@ class FakeContext(
|
|||
every { contentResolver.openFileDescriptor(uri, mode, null) } returns fileDescriptor
|
||||
}
|
||||
|
||||
fun givenOutputStreamFor(uri: Uri): OutputStream {
|
||||
fun givenOutputStreamFor(uri: Uri, mode: String): OutputStream {
|
||||
val outputStream = mockk<OutputStream>(relaxed = true)
|
||||
every { contentResolver.openOutputStream(uri) } returns outputStream
|
||||
every { contentResolver.openOutputStream(uri, mode) } returns outputStream
|
||||
return outputStream
|
||||
}
|
||||
|
||||
fun givenMissingOutputStreamFor(uri: Uri) {
|
||||
every { contentResolver.openOutputStream(uri) } returns null
|
||||
fun givenMissingOutputStreamFor(uri: Uri, mode: String) {
|
||||
every { contentResolver.openOutputStream(uri, mode) } returns null
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue