From c492efcb31775a65464cf68b4a249bc5bb8e6016 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Fri, 13 Oct 2023 10:04:40 +0700 Subject: [PATCH] ExtensionLoader: Set read-only to private extension files (#10007) --- .../extension/util/ExtensionLoader.kt | 4 +- .../tachiyomi/util/storage/FileExtensions.kt | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index f6ab9f577e..28161fcb33 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.util.lang.Hash +import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking @@ -97,7 +98,8 @@ internal object ExtensionLoader { val target = File(getPrivateExtensionDir(context), "${extension.packageName}.$PRIVATE_EXTENSION_EXTENSION") return try { - file.copyTo(target, overwrite = true) + file.delete() + file.copyAndSetReadOnlyTo(target, overwrite = true) if (currentExtension != null) { ExtensionInstallReceiver.notifyReplaced(context, extension.packageName) } else { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/storage/FileExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/storage/FileExtensions.kt index b939eb3016..94a65ddcd5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/storage/FileExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/storage/FileExtensions.kt @@ -23,3 +23,41 @@ fun File.getUriCompat(context: Context): Uri { this.toUri() } } + +/** + * Copies this file to the given [target] file while marking the file as read-only. + * + * @see File.copyTo + */ +fun File.copyAndSetReadOnlyTo(target: File, overwrite: Boolean = false, bufferSize: Int = DEFAULT_BUFFER_SIZE): File { + if (!this.exists()) { + throw NoSuchFileException(file = this, reason = "The source file doesn't exist.") + } + + if (target.exists()) { + if (!overwrite) { + throw FileAlreadyExistsException(file = this, other = target, reason = "The destination file already exists.") + } else if (!target.delete()) { + throw FileAlreadyExistsException(file = this, other = target, reason = "Tried to overwrite the destination, but failed to delete it.") + } + } + + if (this.isDirectory) { + if (!target.mkdirs()) { + throw FileSystemException(file = this, other = target, reason = "Failed to create target directory.") + } + } else { + target.parentFile?.mkdirs() + + this.inputStream().use { input -> + target.outputStream().use { output -> + // Set read-only + target.setReadOnly() + + input.copyTo(output, bufferSize) + } + } + } + + return target +}