Fix unable to patch images when app is hidden

This commit is contained in:
topjohnwu 2021-02-07 06:42:06 -08:00
parent f37e8f4ca8
commit e7350d5041
2 changed files with 64 additions and 54 deletions

View File

@ -207,7 +207,7 @@ dependencies {
implementation("io.noties.markwon:image:${vMarkwon}") implementation("io.noties.markwon:image:${vMarkwon}")
implementation("com.caverock:androidsvg:1.4") implementation("com.caverock:androidsvg:1.4")
val vLibsu = "3.1.0" val vLibsu = "3.1.1"
implementation("com.github.topjohnwu.libsu:core:${vLibsu}") implementation("com.github.topjohnwu.libsu:core:${vLibsu}")
implementation("com.github.topjohnwu.libsu:io:${vLibsu}") implementation("com.github.topjohnwu.libsu:io:${vLibsu}")

View File

@ -26,6 +26,7 @@ import com.topjohnwu.superuser.internal.NOPList
import com.topjohnwu.superuser.internal.UiThreadHandler import com.topjohnwu.superuser.internal.UiThreadHandler
import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFile
import com.topjohnwu.superuser.io.SuFileInputStream import com.topjohnwu.superuser.io.SuFileInputStream
import com.topjohnwu.superuser.io.SuFileOutputStream
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.jpountz.lz4.LZ4FrameInputStream import net.jpountz.lz4.LZ4FrameInputStream
@ -53,6 +54,7 @@ abstract class MagiskInstallImpl protected constructor(
private val shell = Shell.getShell() private val shell = Shell.getShell()
private val service: NetworkService by inject() private val service: NetworkService by inject()
protected val context: Context by inject(Protected) protected val context: Context by inject(Protected)
private val useRootDir = shell.isRoot && Info.noDataExec
private fun findImage(): Boolean { private fun findImage(): Boolean {
val bootPath = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh() val bootPath = "find_boot_image; echo \"\$BOOTIMAGE\"".fsh()
@ -109,8 +111,7 @@ abstract class MagiskInstallImpl protected constructor(
} ?: emptyArray() } ?: emptyArray()
for (lib in libs) { for (lib in libs) {
val name = lib.name.substring(3, lib.name.length - 3) val name = lib.name.substring(3, lib.name.length - 3)
val bin = File(installDir, name) symlink(lib.path, "$installDir/$name")
symlink(lib.path, bin.path)
} }
} }
@ -132,16 +133,41 @@ abstract class MagiskInstallImpl protected constructor(
return false return false
} }
if (useRootDir) {
// Move everything to tmpfs to workaround Samsung bullshit
SuFile(Const.TMPDIR).also {
arrayOf(
"rm -rf $it",
"mkdir -p $it",
"cp_readlink $installDir $it",
"rm -rf $installDir"
).sh()
installDir = it
}
}
return true return true
} }
// Optimization for SuFile I/O streams to skip an internal trial and error
private fun installDirFile(name: String): File {
return if (useRootDir)
SuFile(installDir, name)
else
File(installDir, name)
}
private fun InputStream.cleanPump(out: OutputStream) = withStreams(this, out) { src, _ ->
src.copyTo(out)
}
private fun newTarEntry(name: String, size: Long): TarEntry { private fun newTarEntry(name: String, size: Long): TarEntry {
console.add("-- Writing: $name") console.add("-- Writing: $name")
return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */)) return TarEntry(TarHeader.createHeader(name, size, 0, false, 420 /* 0644 */))
} }
@Throws(IOException::class) @Throws(IOException::class)
private fun handleTar(input: InputStream, output: OutputStream): OutputStream { private fun processTar(input: InputStream, output: OutputStream): OutputStream {
console.add("- Processing tar file") console.add("- Processing tar file")
val tarOut = TarOutputStream(output) val tarOut = TarOutputStream(output)
TarInputStream(input).use { tarIn -> TarInputStream(input).use { tarIn ->
@ -161,8 +187,8 @@ abstract class MagiskInstallImpl protected constructor(
val name = entry.name.replace(".lz4", "") val name = entry.name.replace(".lz4", "")
console.add("-- Extracting: $name") console.add("-- Extracting: $name")
val extract = File(installDir, name) val extract = installDirFile(name)
decompressedStream().writeTo(extract) decompressedStream().cleanPump(SuFileOutputStream.open(extract))
} else if (entry.name.contains("vbmeta.img")) { } else if (entry.name.contains("vbmeta.img")) {
val rawData = decompressedStream().readBytes() val rawData = decompressedStream().readBytes()
// Valid vbmeta.img should be at least 256 bytes // Valid vbmeta.img should be at least 256 bytes
@ -182,8 +208,8 @@ abstract class MagiskInstallImpl protected constructor(
} }
} }
} }
val boot = File(installDir, "boot.img") val boot = installDirFile("boot.img")
val recovery = File(installDir, "recovery.img") val recovery = installDirFile("recovery.img")
if (Config.recovery && recovery.exists() && boot.exists()) { if (Config.recovery && recovery.exists() && boot.exists()) {
// Install to recovery // Install to recovery
srcBoot = recovery srcBoot = recovery
@ -192,10 +218,11 @@ abstract class MagiskInstallImpl protected constructor(
"cd $installDir", "cd $installDir",
"./magiskboot unpack boot.img", "./magiskboot unpack boot.img",
"./magiskboot repack boot.img", "./magiskboot repack boot.img",
"cat new-boot.img > boot.img",
"./magiskboot cleanup", "./magiskboot cleanup",
"mv new-boot.img boot.img", "rm -f new-boot.img",
"cd /").sh() "cd /").sh()
boot.inputStream().use { SuFileInputStream.open(boot).use {
tarOut.putNextEntry(newTarEntry("boot.img", boot.length())) tarOut.putNextEntry(newTarEntry("boot.img", boot.length()))
it.copyTo(tarOut) it.copyTo(tarOut)
} }
@ -236,13 +263,14 @@ abstract class MagiskInstallImpl protected constructor(
} }
outStream = if (magic.contentEquals("ustar".toByteArray())) { outStream = if (magic.contentEquals("ustar".toByteArray())) {
// tar file
outFile = MediaStoreUtils.getFile("$filename.tar", true) outFile = MediaStoreUtils.getFile("$filename.tar", true)
handleTar(src, outFile!!.uri.outputStream()) processTar(src, outFile!!.uri.outputStream())
} else { } else {
// Raw image // raw image
srcBoot = File(installDir, "boot.img") srcBoot = installDirFile("boot.img")
console.add("- Copying image to cache") console.add("- Copying image to cache")
src.writeTo(srcBoot) src.cleanPump(SuFileOutputStream.open(srcBoot))
outFile = MediaStoreUtils.getFile("$filename.img", true) outFile = MediaStoreUtils.getFile("$filename.img", true)
outFile!!.uri.outputStream() outFile!!.uri.outputStream()
} }
@ -262,13 +290,13 @@ abstract class MagiskInstallImpl protected constructor(
// Output file // Output file
try { try {
val patched = File(installDir, "new-boot.img") val newBoot = installDirFile("new-boot.img")
if (outStream is TarOutputStream) { if (outStream is TarOutputStream) {
val name = if (srcBoot.path.contains("recovery")) "recovery.img" else "boot.img" val name = if (srcBoot.path.contains("recovery")) "recovery.img" else "boot.img"
outStream.putNextEntry(newTarEntry(name, patched.length())) outStream.putNextEntry(newTarEntry(name, newBoot.length()))
} }
withStreams(patched.inputStream(), outStream) { src, out -> src.copyTo(out) } SuFileInputStream.open(newBoot).cleanPump(outStream)
patched.delete() newBoot.delete()
console.add("") console.add("")
console.add("****************************") console.add("****************************")
@ -284,34 +312,16 @@ abstract class MagiskInstallImpl protected constructor(
// Fix up binaries // Fix up binaries
srcBoot.delete() srcBoot.delete()
"cp_readlink $installDir".sh() if (shell.isRoot) {
"fix_env $installDir".sh()
} else {
"cp_readlink $installDir".sh()
}
return true return true
} }
private fun patchBoot(): Boolean { private fun patchBoot(): Boolean {
val inRootDir = shell.isRoot && Info.noDataExec
val newBootImg: File
if (inRootDir) {
// Move everything to tmpfs to workaround Samsung bullshit
SuFile("${Const.TMPDIR}/install").also {
arrayOf(
"rm -rf $it",
"mkdir -p $it",
"cp_readlink $installDir $it",
"rm -rf $installDir"
).sh()
installDir = it
}
newBootImg = SuFile(installDir, "new-boot.img")
} else {
newBootImg = File(installDir, "new-boot.img")
// Create output files before hand
newBootImg.createNewFile()
File(installDir, "stock_boot.img").createNewFile()
}
var isSigned = false var isSigned = false
if (srcBoot.let { it !is SuFile || !it.isCharacter }) { if (srcBoot.let { it !is SuFile || !it.isCharacter }) {
try { try {
@ -323,10 +333,18 @@ abstract class MagiskInstallImpl protected constructor(
} }
} catch (e: IOException) { } catch (e: IOException) {
console.add("! Unable to check signature") console.add("! Unable to check signature")
Timber.e(e)
return false return false
} }
} }
val newBoot = installDirFile("new-boot.img")
if (!useRootDir) {
// Create output files before hand
newBoot.createNewFile()
File(installDir, "stock_boot.img").createNewFile()
}
val cmds = arrayOf( val cmds = arrayOf(
"cd $installDir", "cd $installDir",
"KEEPFORCEENCRYPT=${Config.keepEnc} " + "KEEPFORCEENCRYPT=${Config.keepEnc} " +
@ -343,8 +361,9 @@ abstract class MagiskInstallImpl protected constructor(
console.add("- Signing boot image with verity keys") console.add("- Signing boot image with verity keys")
val signed = File.createTempFile("signed", ".img", context.cacheDir) val signed = File.createTempFile("signed", ".img", context.cacheDir)
try { try {
withStreams(SuFileInputStream.open(newBootImg).buffered(), val src = SuFileInputStream.open(newBoot).buffered()
signed.outputStream().buffered()) { src, out -> val out = signed.outputStream().buffered()
withStreams(src, out) { _, _ ->
SignBoot.doSignature(null, null, src, out, "/boot") SignBoot.doSignature(null, null, src, out, "/boot")
} }
} catch (e: IOException) { } catch (e: IOException) {
@ -352,12 +371,7 @@ abstract class MagiskInstallImpl protected constructor(
Timber.e(e) Timber.e(e)
return false return false
} }
if (inRootDir) { job.add("cat $signed > $newBoot", "rm -f $signed")
job.add("cp -f $signed $newBootImg", "rm -f $signed")
} else {
signed.copyTo(newBootImg, overwrite = true)
signed.delete()
}
} }
job.exec() job.exec()
return true return true
@ -414,11 +428,7 @@ abstract class MagiskInstaller(
if (success) { if (success) {
console.add("- All done!") console.add("- All done!")
} else { } else {
if (installDir is SuFile) { Shell.sh("rm -rf $installDir").submit()
Shell.sh("rm -rf ${Const.TMPDIR}").submit()
} else {
Shell.sh("rm -rf $installDir").submit()
}
console.add("! Installation failed") console.add("! Installation failed")
} }
return success return success