mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-12 13:52:26 +00:00
Compare commits
100 Commits
canary-281
...
canary-281
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e3edb8883 | ||
|
|
3b8b61bf35 | ||
|
|
6f90456036 | ||
|
|
f56fd4e215 | ||
|
|
aa35aac5d5 | ||
|
|
1f162b819d | ||
|
|
52ef1d1cb2 | ||
|
|
f14e3a89cc | ||
|
|
95d3eac2e0 | ||
|
|
8e73536e02 | ||
|
|
12a0870bc9 | ||
|
|
6ff82c4e86 | ||
|
|
c64de35375 | ||
|
|
ee5283f4e8 | ||
|
|
bd0e954fea | ||
|
|
675471a49e | ||
|
|
c90e73ccec | ||
|
|
a43c1267d8 | ||
|
|
e8958c6b5c | ||
|
|
e8a3bf82c6 | ||
|
|
27fd79176a | ||
|
|
28d86a3454 | ||
|
|
c6c1a17ae6 | ||
|
|
2b47d47215 | ||
|
|
0e82df9e10 | ||
|
|
893821ad88 | ||
|
|
6b80fbfa99 | ||
|
|
8c3c7d0194 | ||
|
|
b94a3d9f2f | ||
|
|
442d0b5ddc | ||
|
|
494615d9a0 | ||
|
|
afbfb81837 | ||
|
|
3ed4e258a3 | ||
|
|
dddd41c95b | ||
|
|
5f2ca81e86 | ||
|
|
c9eac0c438 | ||
|
|
b6b34f7612 | ||
|
|
e55c413261 | ||
|
|
0399cde50a | ||
|
|
019eb03823 | ||
|
|
363410e1c0 | ||
|
|
fc2ef21660 | ||
|
|
18cb659ff3 | ||
|
|
63231d97ce | ||
|
|
9ac81a8a25 | ||
|
|
79af2787ae | ||
|
|
f5f9b285c0 | ||
|
|
6c05f2ae85 | ||
|
|
29043e1684 | ||
|
|
b73d4a7022 | ||
|
|
ad95e8951b | ||
|
|
bf591fca12 | ||
|
|
dcf027884d | ||
|
|
584f3820fe | ||
|
|
3c7c46307a | ||
|
|
4d80361805 | ||
|
|
9a74e19117 | ||
|
|
b1e17706a4 | ||
|
|
caad129d69 | ||
|
|
da58571ce5 | ||
|
|
2aa7f1c094 | ||
|
|
823e31a91b | ||
|
|
fb926ae302 | ||
|
|
e0489eeffd | ||
|
|
dc9d5a4cac | ||
|
|
143743d0b0 | ||
|
|
563f0d5ad5 | ||
|
|
c99f4a591b | ||
|
|
449204e380 | ||
|
|
a85c4c6528 | ||
|
|
d203a6fff6 | ||
|
|
6c612d66d7 | ||
|
|
540253a55b | ||
|
|
15b7c4ccd1 | ||
|
|
442d5335ea | ||
|
|
8a80eea597 | ||
|
|
5e35703091 | ||
|
|
b7ca73f431 | ||
|
|
a14fc90f07 | ||
|
|
c913f7ec74 | ||
|
|
7f6c9e8411 | ||
|
|
bb02ea3a20 | ||
|
|
3981c9665e | ||
|
|
88628fdf3c | ||
|
|
0469817781 | ||
|
|
a786801141 | ||
|
|
ab86732c89 | ||
|
|
59622d1688 | ||
|
|
58a25a3e2b | ||
|
|
15dca29a87 | ||
|
|
46980819c0 | ||
|
|
4fb6a7268c | ||
|
|
c05e963f37 | ||
|
|
7f7f625864 | ||
|
|
b25aa8295a | ||
|
|
15a605765c | ||
|
|
b575c95710 | ||
|
|
a48a9c858a | ||
|
|
0d8d6290a3 | ||
|
|
4dcd733ddd |
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -12,13 +12,10 @@
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
tools/** binary
|
||||
tools/rustup_wrapper/** -binary
|
||||
*.jar binary
|
||||
*.exe binary
|
||||
*.apk binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.ttf binary
|
||||
|
||||
# Help GitHub detect languages
|
||||
native/jni/external/** linguist-vendored
|
||||
native/jni/systemproperties/** linguist-language=C++
|
||||
|
||||
23
.github/actions/setup/action.yml
vendored
23
.github/actions/setup/action.yml
vendored
@@ -26,6 +26,15 @@ runs:
|
||||
|
||||
- name: Cache sccache
|
||||
uses: actions/cache@v4
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
with:
|
||||
path: .sccache
|
||||
key: sccache-${{ runner.os }}-${{ github.sha }}
|
||||
restore-keys: sccache-${{ runner.os }}-
|
||||
|
||||
- name: Restore sccache
|
||||
uses: actions/cache/restore@v4
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
with:
|
||||
path: .sccache
|
||||
key: sccache-${{ runner.os }}-${{ github.sha }}
|
||||
@@ -55,7 +64,7 @@ runs:
|
||||
|
||||
- name: Cache Gradle dependencies
|
||||
uses: actions/cache@v4
|
||||
if: inputs.is-asset-build == 'true'
|
||||
if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }}
|
||||
with:
|
||||
path: |
|
||||
.gradle/caches
|
||||
@@ -66,7 +75,7 @@ runs:
|
||||
|
||||
- name: Restore Gradle dependencies
|
||||
uses: actions/cache/restore@v4
|
||||
if: inputs.is-asset-build == 'false'
|
||||
if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }}
|
||||
with:
|
||||
path: |
|
||||
.gradle/caches
|
||||
@@ -78,19 +87,17 @@ runs:
|
||||
|
||||
- name: Cache Gradle build cache
|
||||
uses: actions/cache@v4
|
||||
if: inputs.is-asset-build == 'true'
|
||||
if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }}
|
||||
with:
|
||||
path: |
|
||||
.gradle/caches/build-cache-*
|
||||
path: .gradle/caches/build-cache-*
|
||||
key: gradle-build-cache-${{ github.sha }}
|
||||
restore-keys: gradle-build-cache-
|
||||
|
||||
- name: Restore Gradle build cache
|
||||
uses: actions/cache/restore@v4
|
||||
if: inputs.is-asset-build == 'false'
|
||||
if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }}
|
||||
with:
|
||||
path: |
|
||||
.gradle/caches/build-cache-*
|
||||
path: .gradle/caches/build-cache-*
|
||||
key: gradle-build-cache-${{ github.sha }}
|
||||
restore-keys: gradle-build-cache-
|
||||
enableCrossOsArchive: true
|
||||
|
||||
13
.github/workflows/build.yml
vendored
13
.github/workflows/build.yml
vendored
@@ -1,6 +1,16 @@
|
||||
name: Magisk Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- "app/**"
|
||||
- "native/**"
|
||||
- "buildSrc/**"
|
||||
- "build.py"
|
||||
- "gradle.properties"
|
||||
- "gradle/libs.versions.toml"
|
||||
- ".github/workflows/build.yml"
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
@@ -71,6 +81,7 @@ jobs:
|
||||
name: Test API ${{ matrix.version }} (x86_64)
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -117,6 +128,7 @@ jobs:
|
||||
name: Test API ${{ matrix.version }} (x86)
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -158,6 +170,7 @@ jobs:
|
||||
name: Test ${{ matrix.device }}
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
env:
|
||||
CF_HOME: /home/runner/aosp_cf_phone
|
||||
strategy:
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -4,18 +4,12 @@
|
||||
[submodule "lz4"]
|
||||
path = native/src/external/lz4
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "bzip2"]
|
||||
path = native/src/external/bzip2
|
||||
url = https://github.com/nemequ/bzip2.git
|
||||
[submodule "xz"]
|
||||
path = native/src/external/xz
|
||||
url = https://github.com/xz-mirror/xz.git
|
||||
[submodule "libcxx"]
|
||||
path = native/src/external/libcxx
|
||||
url = https://github.com/topjohnwu/libcxx.git
|
||||
[submodule "zlib"]
|
||||
path = native/src/external/zlib
|
||||
url = https://android.googlesource.com/platform/external/zlib
|
||||
[submodule "zopfli"]
|
||||
path = native/src/external/zopfli
|
||||
url = https://github.com/google/zopfli.git
|
||||
|
||||
@@ -22,7 +22,7 @@ Click the icon below to download Magisk apk.
|
||||
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28102)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28103)
|
||||
|
||||
## Useful Links
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ object RebootMenu {
|
||||
activity.getSystemService<PowerManager>()?.isRebootingUserspaceSupported == true) {
|
||||
menu.menu.findItem(R.id.action_reboot_userspace).isVisible = true
|
||||
}
|
||||
if (Const.Version.isCanary()) {
|
||||
if (Const.Version.atLeast_28_0()) {
|
||||
menu.menu.findItem(R.id.action_reboot_safe_mode).isChecked = Config.bootloop >= 2
|
||||
} else {
|
||||
menu.menu.findItem(R.id.action_reboot_safe_mode).isVisible = false
|
||||
|
||||
@@ -14,7 +14,7 @@ object Const {
|
||||
else Build.SUPPORTED_32_BIT_ABIS.firstOrNull()
|
||||
|
||||
// Paths
|
||||
const val MAGISK_PATH = "/data/adb/modules"
|
||||
const val MODULE_PATH = "/data/adb/modules"
|
||||
const val TMPDIR = "/dev/tmp"
|
||||
const val MAGISK_LOG = "/cache/magisk.log"
|
||||
|
||||
@@ -28,6 +28,7 @@ object Const {
|
||||
|
||||
fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
|
||||
fun atLeast_25_0() = Info.env.versionCode >= 25000 || isCanary()
|
||||
fun atLeast_28_0() = Info.env.versionCode >= 28000 || isCanary()
|
||||
fun isCanary() = isCanary(Info.env.versionCode)
|
||||
|
||||
fun isCanary(ver: Int) = ver > 0 && ver % 100 != 0
|
||||
|
||||
@@ -47,7 +47,6 @@ object Info {
|
||||
private set
|
||||
private var crypto = ""
|
||||
|
||||
var hasGMS = true
|
||||
val isEmulator =
|
||||
Build.DEVICE.contains("vsoc")
|
||||
|| getProperty("ro.kernel.qemu", "0") == "1"
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.squareup.moshi.Moshi
|
||||
import com.topjohnwu.magisk.ProviderInstaller
|
||||
import com.topjohnwu.magisk.core.BuildConfig
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||
import okhttp3.Cache
|
||||
import okhttp3.ConnectionSpec
|
||||
@@ -72,9 +71,7 @@ fun createOkHttpClient(context: Context): OkHttpClient {
|
||||
chain.proceed(request.build())
|
||||
}
|
||||
|
||||
if (!ProviderInstaller.install(context)) {
|
||||
Info.hasGMS = false
|
||||
}
|
||||
ProviderInstaller.install(context)
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.nio.ExtendedFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
@@ -12,7 +13,7 @@ import java.io.IOException
|
||||
import java.util.Locale
|
||||
|
||||
data class LocalModule(
|
||||
private val path: String,
|
||||
private val base: ExtendedFile,
|
||||
) : Module() {
|
||||
private val svc get() = ServiceLocator.networkService
|
||||
|
||||
@@ -24,20 +25,18 @@ data class LocalModule(
|
||||
var description: String = ""
|
||||
var updateInfo: OnlineModule? = null
|
||||
var outdated = false
|
||||
|
||||
private var updateUrl: String = ""
|
||||
private val removeFile = RootUtils.fs.getFile(path, "remove")
|
||||
private val disableFile = RootUtils.fs.getFile(path, "disable")
|
||||
private val updateFile = RootUtils.fs.getFile(path, "update")
|
||||
private val riruFolder = RootUtils.fs.getFile(path, "riru")
|
||||
private val zygiskFolder = RootUtils.fs.getFile(path, "zygisk")
|
||||
private val unloaded = RootUtils.fs.getFile(zygiskFolder, "unloaded")
|
||||
|
||||
val updated: Boolean get() = updateFile.exists()
|
||||
val isRiru: Boolean get() = (id == "riru-core") || riruFolder.exists()
|
||||
val isZygisk: Boolean get() = zygiskFolder.exists()
|
||||
val zygiskUnloaded: Boolean get() = unloaded.exists()
|
||||
val hasAction: Boolean;
|
||||
private val removeFile = base.getChildFile("remove")
|
||||
private val disableFile = base.getChildFile("disable")
|
||||
private val updateFile = base.getChildFile("update")
|
||||
val zygiskFolder = base.getChildFile("zygisk")
|
||||
|
||||
val updated get() = updateFile.exists()
|
||||
val isRiru = (id == "riru-core") || base.getChildFile("riru").exists()
|
||||
val isZygisk = zygiskFolder.exists()
|
||||
val zygiskUnloaded = zygiskFolder.getChildFile("unloaded").exists()
|
||||
val hasAction = base.getChildFile("action.sh").exists()
|
||||
|
||||
var enable: Boolean
|
||||
get() = !disableFile.exists()
|
||||
@@ -90,19 +89,16 @@ data class LocalModule(
|
||||
|
||||
init {
|
||||
runCatching {
|
||||
parseProps(Shell.cmd("dos2unix < $path/module.prop").exec().out)
|
||||
parseProps(Shell.cmd("dos2unix < $base/module.prop").exec().out)
|
||||
}
|
||||
|
||||
if (id.isEmpty()) {
|
||||
val sep = path.lastIndexOf('/')
|
||||
id = path.substring(sep + 1)
|
||||
id = base.name
|
||||
}
|
||||
|
||||
if (name.isEmpty()) {
|
||||
name = id
|
||||
}
|
||||
|
||||
hasAction = RootUtils.fs.getFile(path, "action.sh").exists()
|
||||
}
|
||||
|
||||
suspend fun fetch(): Boolean {
|
||||
@@ -125,14 +121,14 @@ data class LocalModule(
|
||||
|
||||
companion object {
|
||||
|
||||
fun loaded() = RootUtils.fs.getFile(Const.MAGISK_PATH).exists()
|
||||
fun loaded() = RootUtils.fs.getFile(Const.MODULE_PATH).exists()
|
||||
|
||||
suspend fun installed() = withContext(Dispatchers.IO) {
|
||||
RootUtils.fs.getFile(Const.MAGISK_PATH)
|
||||
RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||
.listFiles()
|
||||
.orEmpty()
|
||||
.filter { !it.isFile && !it.isHidden }
|
||||
.map { LocalModule("${Const.MAGISK_PATH}/${it.name}") }
|
||||
.map { LocalModule(it) }
|
||||
.sortedBy { it.name.lowercase(Locale.ROOT) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ByteArrayStream extends ByteArrayOutputStream {
|
||||
|
||||
@@ -27,4 +28,8 @@ public class ByteArrayStream extends ByteArrayOutputStream {
|
||||
public ByteArrayInputStream getInputStream() {
|
||||
return new ByteArrayInputStream(buf, 0, count);
|
||||
}
|
||||
|
||||
public ByteBuffer toByteBuffer() {
|
||||
return ByteBuffer.wrap(buf, 0, count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ public class SignApk {
|
||||
privateKey[0] = key;
|
||||
|
||||
// Generate, in memory, an APK signed using standard JAR Signature Scheme.
|
||||
ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream();
|
||||
ByteArrayStream v1SignedApkBuf = new ByteArrayStream();
|
||||
JarOutputStream outputJar = new JarOutputStream(v1SignedApkBuf);
|
||||
// Use maximum compression for compressed entries because the APK lives forever on
|
||||
// the system partition.
|
||||
@@ -519,8 +519,7 @@ public class SignApk {
|
||||
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
|
||||
signFile(manifest, publicKey, privateKey, timestamp, outputJar);
|
||||
outputJar.close();
|
||||
ByteBuffer v1SignedApk = ByteBuffer.wrap(v1SignedApkBuf.toByteArray());
|
||||
v1SignedApkBuf.reset();
|
||||
ByteBuffer v1SignedApk = v1SignedApkBuf.toByteBuffer();
|
||||
|
||||
ByteBuffer[] outputChunks;
|
||||
List<ApkSignerV2.SignerConfig> signerConfigs = createV2SignerConfigs(privateKey, publicKey,
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||
import com.topjohnwu.magisk.core.utils.DummyList
|
||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||
@@ -47,7 +46,6 @@ import java.io.OutputStream
|
||||
import java.io.PushbackInputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.security.SecureRandom
|
||||
import java.util.Arrays
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@@ -191,7 +189,8 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun InputStream.copyAndCloseOut(out: OutputStream) = out.use { copyAll(it) }
|
||||
private suspend fun InputStream.copyAndCloseOut(out: OutputStream) =
|
||||
out.use { copyAll(it, 1024 * 1024) }
|
||||
|
||||
private class NoAvailableStream(s: InputStream) : FilterInputStream(s) {
|
||||
// Make sure available is never called on the actual stream and always return 0
|
||||
@@ -225,8 +224,8 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
console.add("- Processing tar file")
|
||||
var entry: TarArchiveEntry? = tarIn.nextEntry
|
||||
|
||||
fun TarArchiveEntry.decompressedStream(): InputStream {
|
||||
val stream = if (name.endsWith(".lz4"))
|
||||
fun decompressedStream(): InputStream {
|
||||
val stream = if (tarIn.currentEntry.name.endsWith(".lz4"))
|
||||
FramedLZ4CompressorInputStream(tarIn, true) else tarIn
|
||||
return NoAvailableStream(stream)
|
||||
}
|
||||
@@ -252,9 +251,9 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
|
||||
if (bootItem != null) {
|
||||
console.add("-- Extracting: ${bootItem.name}")
|
||||
entry.decompressedStream().copyAndCloseOut(bootItem.file.newOutputStream())
|
||||
decompressedStream().copyAndCloseOut(bootItem.file.newOutputStream())
|
||||
} else if (entry.name.contains("vbmeta.img")) {
|
||||
val rawData = entry.decompressedStream().readBytes()
|
||||
val rawData = decompressedStream().readBytes()
|
||||
// Valid vbmeta.img should be at least 256 bytes
|
||||
if (rawData.size < 256)
|
||||
continue
|
||||
@@ -287,7 +286,7 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
} else {
|
||||
console.add("-- Copying : ${entry.name}")
|
||||
tarOut.putArchiveEntry(entry)
|
||||
tarIn.copyAll(tarOut, bufferSize = 1024 * 1024)
|
||||
tarIn.copyAll(tarOut)
|
||||
tarOut.closeArchiveEntry()
|
||||
}
|
||||
entry = tarIn.nextEntry ?: break
|
||||
@@ -429,7 +428,7 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
|
||||
// Process input file
|
||||
try {
|
||||
PushbackInputStream(uri.inputStream(), 512).use { src ->
|
||||
PushbackInputStream(uri.inputStream().buffered(1024 * 1024), 512).use { src ->
|
||||
val head = ByteArray(512)
|
||||
if (src.read(head) != head.size) {
|
||||
console.add("! Invalid input file")
|
||||
@@ -438,12 +437,13 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
src.unread(head)
|
||||
|
||||
val magic = head.copyOf(4)
|
||||
val tarMagic = Arrays.copyOfRange(head, 257, 262)
|
||||
val tarMagic = head.copyOfRange(257, 262)
|
||||
|
||||
srcBoot = if (tarMagic.contentEquals("ustar".toByteArray())) {
|
||||
// tar file
|
||||
outFile = MediaStoreUtils.getFile("$destName.tar")
|
||||
outStream = TarArchiveOutputStream(outFile.uri.outputStream()).also {
|
||||
val os = outFile.uri.outputStream().buffered(1024 * 1024)
|
||||
outStream = TarArchiveOutputStream(os).also {
|
||||
it.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
||||
it.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
||||
}
|
||||
@@ -500,7 +500,7 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
bootItem.file = newBoot
|
||||
bootItem.copyTo(outStream as TarArchiveOutputStream)
|
||||
} else {
|
||||
newBoot.newInputStream().copyAndClose(outStream)
|
||||
newBoot.newInputStream().use { it.copyAll(outStream, 1024 * 1024) }
|
||||
}
|
||||
newBoot.delete()
|
||||
|
||||
@@ -514,6 +514,8 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
outFile.delete()
|
||||
Timber.e(e)
|
||||
return false
|
||||
} finally {
|
||||
outStream.close()
|
||||
}
|
||||
|
||||
// Fix up binaries
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
@Throws(IOException::class)
|
||||
suspend fun File.unzip(folder: File, path: String = "", junkPath: Boolean = false) {
|
||||
ZipFile.Builder().setFile(this).get().use { zip ->
|
||||
for (entry in zip.entries) {
|
||||
if (!entry.name.startsWith(path) || entry.isDirectory) {
|
||||
// Ignore directories, only create files
|
||||
continue
|
||||
}
|
||||
val name = if (junkPath)
|
||||
entry.name.substring(entry.name.lastIndexOf('/') + 1)
|
||||
else
|
||||
entry.name
|
||||
val dest = File(folder, name)
|
||||
dest.parentFile?.mkdirs()
|
||||
dest.outputStream().use { out -> zip.getInputStream(entry).copyAll(out) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
suspend fun InputStream.unzip(folder: File, path: String = "", junkPath: Boolean = false) {
|
||||
ZipArchiveInputStream(this).use { zin ->
|
||||
var entry: ZipArchiveEntry
|
||||
while (true) {
|
||||
entry = zin.nextEntry ?: break
|
||||
if (!entry.name.startsWith(path) || entry.isDirectory) {
|
||||
// Ignore directories, only create files
|
||||
continue
|
||||
}
|
||||
val name = if (junkPath)
|
||||
entry.name.substring(entry.name.lastIndexOf('/') + 1)
|
||||
else
|
||||
entry.name
|
||||
|
||||
val dest = File(folder, name)
|
||||
dest.parentFile?.mkdirs()
|
||||
dest.outputStream().use { out -> zin.copyAll(out) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,17 @@ import androidx.annotation.Keep
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import com.topjohnwu.magisk.core.model.module.LocalModule
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume.assumeTrue
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -21,6 +29,17 @@ class AdditionalTest : BaseTest {
|
||||
private const val SHELL_PKG = "com.android.shell"
|
||||
private const val LSPOSED_CATEGORY = "org.lsposed.manager.LAUNCH_MANAGER"
|
||||
private const val LSPOSED_PKG = "org.lsposed.manager"
|
||||
|
||||
private lateinit var modules: List<LocalModule>
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun before() {
|
||||
BaseTest.prerequisite()
|
||||
runBlocking {
|
||||
modules = LocalModule.installed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -29,9 +48,24 @@ class AdditionalTest : BaseTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLaunchLsposedManager() {
|
||||
fun testModuleCount() {
|
||||
var expected = 0
|
||||
if (Environment.testModules()) expected +=2
|
||||
if (Environment.lsposed()) expected++
|
||||
if (Environment.shamiko()) expected++
|
||||
assertEquals("Module count incorrect", expected, modules.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLsposed() {
|
||||
assumeTrue(Environment.lsposed())
|
||||
|
||||
val module = modules.find { it.id == "zygisk_lsposed" }
|
||||
assertNotNull("zygisk_lsposed is not installed", module)
|
||||
module!!
|
||||
assertFalse("zygisk_lsposed is not enabled", module.zygiskUnloaded)
|
||||
|
||||
// Launch lsposed manager to ensure the module is active
|
||||
uiAutomation.executeShellCommand(
|
||||
"am start -c $LSPOSED_CATEGORY $SHELL_PKG/.BugreportWarningActivity"
|
||||
).let { pfd -> AutoCloseInputStream(pfd).use { it.readBytes() } }
|
||||
@@ -42,4 +76,39 @@ class AdditionalTest : BaseTest {
|
||||
device.wait(Until.hasObject(By.res(pattern)), TimeUnit.SECONDS.toMillis(10))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModule01() {
|
||||
assumeTrue(Environment.testModules())
|
||||
|
||||
val module = modules.find { it.id == "test_01" }
|
||||
assertNotNull("test_01 is not installed", module)
|
||||
assertTrue(
|
||||
"/system/etc/newfile should exist",
|
||||
RootUtils.fs.getFile("/system/etc/newfile").exists()
|
||||
)
|
||||
assertFalse(
|
||||
"/system/bin/screenrecord should not exist",
|
||||
RootUtils.fs.getFile("/system/bin/screenrecord").exists()
|
||||
)
|
||||
module!!
|
||||
assertTrue("test_01 should be zygisk unloaded", module.zygiskUnloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModule02() {
|
||||
assumeTrue(Environment.testModules())
|
||||
|
||||
val module = modules.find { it.id == "test_02" }
|
||||
assertNotNull("test_02 is not installed", module)
|
||||
module!!
|
||||
assertTrue("test_02 should be zygisk unloaded", module.zygiskUnloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModule03() {
|
||||
assumeTrue(Environment.testModules())
|
||||
|
||||
assertNull("test_03 should be removed", modules.find { it.id == "test_03" })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import org.junit.Assert.assertTrue
|
||||
interface BaseTest {
|
||||
val instrumentation: Instrumentation
|
||||
get() = InstrumentationRegistry.getInstrumentation()
|
||||
val context: Context get() = instrumentation.targetContext
|
||||
val appContext: Context get() = instrumentation.targetContext
|
||||
val testContext: Context get() = instrumentation.context
|
||||
val uiAutomation: UiAutomation get() = instrumentation.uiAutomation
|
||||
val device: UiDevice get() = UiDevice.getInstance(instrumentation)
|
||||
|
||||
|
||||
@@ -6,21 +6,28 @@ import androidx.annotation.Keep
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.topjohnwu.magisk.core.BuildConfig.APP_PACKAGE_NAME
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.download.DownloadNotifier
|
||||
import com.topjohnwu.magisk.core.download.DownloadProcessor
|
||||
import com.topjohnwu.magisk.core.ktx.cachedFile
|
||||
import com.topjohnwu.magisk.core.model.module.LocalModule
|
||||
import com.topjohnwu.magisk.core.tasks.AppMigration
|
||||
import com.topjohnwu.magisk.core.tasks.FlashZip
|
||||
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
import com.topjohnwu.superuser.CallbackList
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.nio.ExtendedFile
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assume.assumeTrue
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
@Keep
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@@ -31,12 +38,21 @@ class Environment : BaseTest {
|
||||
@JvmStatic
|
||||
fun before() = BaseTest.prerequisite()
|
||||
|
||||
fun lsposed(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= 27 && Build.VERSION.SDK_INT <= 34
|
||||
// The kernel running on emulators < API 26 does not play well with
|
||||
// magic mount. Skip module tests on those legacy platforms.
|
||||
fun testModules(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= 26
|
||||
}
|
||||
|
||||
private const val LSPOSED_URL =
|
||||
"https://github.com/LSPosed/LSPosed/releases/download/v1.9.2/LSPosed-v1.9.2-7024-zygisk-release.zip"
|
||||
fun lsposed(): Boolean {
|
||||
return Build.VERSION.SDK_INT in 27..34
|
||||
}
|
||||
|
||||
fun shamiko(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= 27
|
||||
}
|
||||
|
||||
private const val MODULE_ERROR = "Module zip processing incorrect"
|
||||
}
|
||||
|
||||
object TimberLog : CallbackList<String>(Runnable::run) {
|
||||
@@ -45,34 +61,125 @@ class Environment : BaseTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkModuleZip(file: File) {
|
||||
// Make sure module processing is correct
|
||||
ZipFile.Builder().setFile(file).get().use { zip ->
|
||||
val meta = zip.entries
|
||||
.asSequence()
|
||||
.filter { it.name.startsWith("META-INF") }
|
||||
.toMutableList()
|
||||
assertEquals(MODULE_ERROR, 6, meta.size)
|
||||
|
||||
val binary = zip.getInputStream(
|
||||
zip.getEntry("META-INF/com/google/android/update-binary")
|
||||
).use { it.readBytes() }
|
||||
val ref = appContext.assets.open("module_installer.sh").use { it.readBytes() }
|
||||
assertArrayEquals(MODULE_ERROR, ref, binary)
|
||||
|
||||
val script = zip.getInputStream(
|
||||
zip.getEntry("META-INF/com/google/android/updater-script")
|
||||
).use { it.readBytes() }
|
||||
assertArrayEquals(MODULE_ERROR, "#MAGISK\n".toByteArray(), script)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupModule01(root: ExtendedFile) {
|
||||
val error = "test_01 setup failed"
|
||||
val path = root.getChildFile("test_01")
|
||||
|
||||
// Create /system/etc/newfile
|
||||
val etc = path.getChildFile("system").getChildFile("etc")
|
||||
assertTrue(error, etc.mkdirs())
|
||||
assertTrue(error, etc.getChildFile("newfile").createNewFile())
|
||||
|
||||
// Delete /system/bin/screenrecord
|
||||
val bin = path.getChildFile("system").getChildFile("bin")
|
||||
assertTrue(error, bin.mkdirs())
|
||||
assertTrue(error, Shell.cmd("mknod $bin/screenrecord c 0 0").exec().isSuccess)
|
||||
|
||||
// Create an empty zygisk folder
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, module.zygiskFolder.mkdir())
|
||||
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupModule02(root: ExtendedFile) {
|
||||
val error = "test_02 setup failed"
|
||||
val path = root.getChildFile("test_02")
|
||||
|
||||
// Create invalid zygisk libraries
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, module.zygiskFolder.mkdirs())
|
||||
assertTrue(error, module.zygiskFolder.getChildFile("armeabi-v7a.so").createNewFile())
|
||||
assertTrue(error, module.zygiskFolder.getChildFile("arm64-v8a.so").createNewFile())
|
||||
assertTrue(error, module.zygiskFolder.getChildFile("x86.so").createNewFile())
|
||||
assertTrue(error, module.zygiskFolder.getChildFile("x86_64.so").createNewFile())
|
||||
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupModule03(root: ExtendedFile) {
|
||||
val error = "test_03 setup failed"
|
||||
val path = root.getChildFile("test_03")
|
||||
|
||||
// Create a new module but mark is as "remove"
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, path.mkdirs())
|
||||
assertTrue(error, path.getChildFile("service.sh").createNewFile())
|
||||
module.remove = true
|
||||
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setupMagisk() {
|
||||
fun setupEnvironment() {
|
||||
runBlocking {
|
||||
assertTrue(
|
||||
"Magisk setup failed",
|
||||
MagiskInstaller.Emulator(TimberLog, TimberLog).exec()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setupLsposed() {
|
||||
assumeTrue(lsposed())
|
||||
|
||||
val notify = object : DownloadNotifier {
|
||||
override val context = this@Environment.context
|
||||
override val context = appContext
|
||||
override fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit) {}
|
||||
}
|
||||
val processor = DownloadProcessor(notify)
|
||||
val zip = context.cachedFile("lsposed.zip")
|
||||
|
||||
val shamiko = appContext.cachedFile("shamiko.zip")
|
||||
runBlocking {
|
||||
ServiceLocator.networkService.fetchFile(LSPOSED_URL).byteStream().use {
|
||||
processor.handleModule(it, zip.toUri())
|
||||
testContext.assets.open("shamiko.zip").use {
|
||||
processor.handleModule(it, shamiko.toUri())
|
||||
}
|
||||
assertTrue(
|
||||
"LSPosed installation failed",
|
||||
FlashZip(zip.toUri(), TimberLog, TimberLog).exec()
|
||||
)
|
||||
checkModuleZip(shamiko)
|
||||
if (shamiko()) {
|
||||
assertTrue(
|
||||
"Shamiko installation failed",
|
||||
FlashZip(shamiko.toUri(), TimberLog, TimberLog).exec()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val lsp = appContext.cachedFile("lsposed.zip")
|
||||
runBlocking {
|
||||
testContext.assets.open("lsposed.zip").use {
|
||||
processor.handleModule(it, lsp.toUri())
|
||||
}
|
||||
checkModuleZip(lsp)
|
||||
if (lsposed()) {
|
||||
assertTrue(
|
||||
"LSPosed installation failed",
|
||||
FlashZip(lsp.toUri(), TimberLog, TimberLog).exec()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (testModules()) {
|
||||
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||
setupModule01(root)
|
||||
setupModule02(root)
|
||||
setupModule03(root)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +189,7 @@ class Environment : BaseTest {
|
||||
assertTrue(
|
||||
"App hiding failed",
|
||||
AppMigration.patchAndHide(
|
||||
context = context,
|
||||
context = appContext,
|
||||
label = "Settings",
|
||||
pkg = "repackaged.$APP_PACKAGE_NAME"
|
||||
)
|
||||
@@ -95,7 +202,7 @@ class Environment : BaseTest {
|
||||
runBlocking {
|
||||
assertTrue(
|
||||
"App restoration failed",
|
||||
AppMigration.restoreApp(context)
|
||||
AppMigration.restoreApp(appContext)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ public class ProviderInstaller {
|
||||
|
||||
private static final String GMS_PACKAGE_NAME = "com.google.android.gms";
|
||||
|
||||
public static boolean install(Context context) {
|
||||
public static void install(Context context) {
|
||||
try {
|
||||
// Check if gms is a system app
|
||||
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(GMS_PACKAGE_NAME, 0);
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try installing new SSL provider from Google Play Service
|
||||
@@ -22,9 +22,7 @@ public class ProviderInstaller {
|
||||
.loadClass("com.google.android.gms.common.security.ProviderInstallerImpl")
|
||||
.getMethod("insertProvider", Context.class)
|
||||
.invoke(null, gms);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
setupAppCommon()
|
||||
setupTestApk()
|
||||
|
||||
dependencies {
|
||||
implementation(libs.test.runner)
|
||||
|
||||
201
build.py
201
build.py
@@ -2,7 +2,6 @@
|
||||
import argparse
|
||||
import copy
|
||||
import glob
|
||||
import lzma
|
||||
import multiprocessing
|
||||
import os
|
||||
import platform
|
||||
@@ -57,14 +56,6 @@ if is_windows:
|
||||
if not sys.version_info >= (3, 8):
|
||||
error("Requires Python 3.8+")
|
||||
|
||||
try:
|
||||
sdk_path = Path(os.environ["ANDROID_HOME"])
|
||||
except KeyError:
|
||||
try:
|
||||
sdk_path = Path(os.environ["ANDROID_SDK_ROOT"])
|
||||
except KeyError:
|
||||
error("Please set Android SDK path to environment variable ANDROID_HOME")
|
||||
|
||||
cpu_count = multiprocessing.cpu_count()
|
||||
os_name = platform.system().lower()
|
||||
|
||||
@@ -80,17 +71,6 @@ default_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
||||
support_targets = default_targets | {"resetprop"}
|
||||
rust_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
||||
|
||||
# Common paths
|
||||
ndk_root = sdk_path / "ndk"
|
||||
ndk_path = ndk_root / "magisk"
|
||||
ndk_build = ndk_path / "ndk-build"
|
||||
rust_bin = ndk_path / "toolchains" / "rust" / "bin"
|
||||
llvm_bin = ndk_path / "toolchains" / "llvm" / "prebuilt" / f"{os_name}-x86_64" / "bin"
|
||||
cargo = rust_bin / "cargo"
|
||||
gradlew = Path.cwd() / "gradlew"
|
||||
adb_path = sdk_path / "platform-tools" / "adb"
|
||||
native_gen_path = Path("native", "out", "generated").resolve()
|
||||
|
||||
# Global vars
|
||||
config = {}
|
||||
args = {}
|
||||
@@ -162,10 +142,6 @@ def cmd_out(cmds: list):
|
||||
)
|
||||
|
||||
|
||||
def xz(data):
|
||||
return lzma.compress(data, preset=9, check=lzma.CHECK_NONE)
|
||||
|
||||
|
||||
###############
|
||||
# Build Native
|
||||
###############
|
||||
@@ -256,6 +232,7 @@ def build_cpp_src(targets: set):
|
||||
|
||||
|
||||
def run_cargo(cmds):
|
||||
ensure_paths()
|
||||
env = os.environ.copy()
|
||||
env["PATH"] = f'{rust_bin}{os.pathsep}{env["PATH"]}'
|
||||
env["CARGO_BUILD_RUSTC"] = str(rust_bin / f"rustc{EXE_EXT}")
|
||||
@@ -333,6 +310,7 @@ def dump_flag_header():
|
||||
flag_txt += f'#define MAGISK_VER_CODE {config["versionCode"]}\n'
|
||||
flag_txt += f"#define MAGISK_DEBUG {0 if args.release else 1}\n"
|
||||
|
||||
native_gen_path = Path("native", "out", "generated")
|
||||
native_gen_path.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||
write_if_diff(native_gen_path / "flags.h", flag_txt)
|
||||
|
||||
@@ -342,6 +320,8 @@ def dump_flag_header():
|
||||
|
||||
|
||||
def build_native():
|
||||
ensure_paths()
|
||||
|
||||
# Verify NDK install
|
||||
try:
|
||||
with open(Path(ndk_path, "ONDK_VERSION"), "r") as ondk_ver:
|
||||
@@ -401,13 +381,14 @@ def find_jdk():
|
||||
if no_jdk:
|
||||
error(
|
||||
"Please set Android Studio's path to environment variable ANDROID_STUDIO,\n"
|
||||
+ "or install JDK 17 and make sure 'javac' is available in PATH"
|
||||
+ "or install JDK 21 and make sure 'javac' is available in PATH"
|
||||
)
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def build_apk(module: str):
|
||||
ensure_paths()
|
||||
env = find_jdk()
|
||||
|
||||
build_type = "Release" if args.release else "Debug"
|
||||
@@ -479,6 +460,7 @@ def build_test():
|
||||
|
||||
|
||||
def cleanup():
|
||||
ensure_paths()
|
||||
support_targets = {"native", "cpp", "rust", "app"}
|
||||
if args.targets:
|
||||
targets = set(args.targets) & support_targets
|
||||
@@ -520,6 +502,17 @@ def build_all():
|
||||
############
|
||||
|
||||
|
||||
def clippy_cli():
|
||||
args.force_out = True
|
||||
os.chdir(Path("native", "src"))
|
||||
cmds = ["clippy", "--no-deps", "--target", ""]
|
||||
for triple in build_abis.values():
|
||||
cmds[3] = triple
|
||||
run_cargo(cmds)
|
||||
run_cargo(cmds + ["--release"])
|
||||
os.chdir(Path("..", ".."))
|
||||
|
||||
|
||||
def cargo_cli():
|
||||
args.force_out = True
|
||||
if len(args.commands) >= 1 and args.commands[0] == "--":
|
||||
@@ -530,6 +523,7 @@ def cargo_cli():
|
||||
|
||||
|
||||
def setup_ndk():
|
||||
ensure_paths()
|
||||
ndk_ver = config["ondkVersion"]
|
||||
url = f"https://github.com/topjohnwu/ondk/releases/download/{ndk_ver}/ondk-{ndk_ver}-{os_name}.tar.xz"
|
||||
ndk_archive = url.split("/")[-1]
|
||||
@@ -548,14 +542,54 @@ def setup_ndk():
|
||||
mv(ondk_path, ndk_path)
|
||||
|
||||
|
||||
def setup_rustup():
|
||||
wrapper_dir = Path(args.wrapper_dir)
|
||||
rm_rf(wrapper_dir)
|
||||
wrapper_dir.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||
if "CARGO_HOME" in os.environ:
|
||||
cargo_home = Path(os.environ["CARGO_HOME"])
|
||||
else:
|
||||
cargo_home = Path.home() / ".cargo"
|
||||
cargo_bin = cargo_home / "bin"
|
||||
for src in cargo_bin.iterdir():
|
||||
tgt = wrapper_dir / src.name
|
||||
tgt.symlink_to(f"rustup{EXE_EXT}")
|
||||
|
||||
# Build rustup_wrapper
|
||||
wrapper_src = Path("tools", "rustup_wrapper")
|
||||
cargo_toml = wrapper_src / "Cargo.toml"
|
||||
cmds = ["build", "--release", f"--manifest-path={cargo_toml}"]
|
||||
if args.verbose > 1:
|
||||
cmds.append("--verbose")
|
||||
run_cargo(cmds)
|
||||
|
||||
# Replace rustup with wrapper
|
||||
wrapper = wrapper_dir / (f"rustup{EXE_EXT}")
|
||||
wrapper.unlink(missing_ok=True)
|
||||
cp(wrapper_src / "target" / "release" / (f"rustup_wrapper{EXE_EXT}"), wrapper)
|
||||
wrapper.chmod(0o755)
|
||||
|
||||
|
||||
##################
|
||||
# AVD and testing
|
||||
##################
|
||||
|
||||
|
||||
def push_files(script):
|
||||
if args.build:
|
||||
build_all()
|
||||
ensure_adb()
|
||||
|
||||
abi = cmd_out([adb_path, "shell", "getprop", "ro.product.cpu.abi"])
|
||||
if not abi:
|
||||
error("Cannot detect emulator ABI")
|
||||
|
||||
apk = Path(
|
||||
config["outdir"], ("app-release.apk" if args.release else "app-debug.apk")
|
||||
)
|
||||
if args.apk:
|
||||
apk = Path(args.apk)
|
||||
else:
|
||||
apk = Path(
|
||||
config["outdir"], ("app-release.apk" if args.release else "app-debug.apk")
|
||||
)
|
||||
|
||||
# Extract busybox from APK
|
||||
busybox = Path(config["outdir"], "busybox")
|
||||
@@ -577,9 +611,6 @@ def push_files(script):
|
||||
|
||||
|
||||
def setup_avd():
|
||||
if not args.skip:
|
||||
build_all()
|
||||
|
||||
header("* Setting up emulator")
|
||||
|
||||
push_files(Path("scripts", "avd_magisk.sh"))
|
||||
@@ -590,17 +621,8 @@ def setup_avd():
|
||||
|
||||
|
||||
def patch_avd_file():
|
||||
if not args.skip:
|
||||
build_all()
|
||||
|
||||
input = Path(args.image)
|
||||
if args.output:
|
||||
output = Path(args.output)
|
||||
else:
|
||||
output = input.parent / f"{input.name}.magisk"
|
||||
|
||||
src_file = f"/data/local/tmp/{input.name}"
|
||||
out_file = f"{src_file}.magisk"
|
||||
output = Path(args.output)
|
||||
|
||||
header(f"* Patching {input.name}")
|
||||
|
||||
@@ -610,6 +632,9 @@ def patch_avd_file():
|
||||
if proc.returncode != 0:
|
||||
error("adb push failed!")
|
||||
|
||||
src_file = f"/data/local/tmp/{input.name}"
|
||||
out_file = f"{src_file}.magisk"
|
||||
|
||||
proc = execv([adb_path, "shell", "sh", "/data/local/tmp/avd_patch.sh", src_file])
|
||||
if proc.returncode != 0:
|
||||
error("avd_patch.sh failed!")
|
||||
@@ -621,37 +646,48 @@ def patch_avd_file():
|
||||
header(f"Output: {output}")
|
||||
|
||||
|
||||
def setup_rustup():
|
||||
wrapper_dir = Path(args.wrapper_dir)
|
||||
rm_rf(wrapper_dir)
|
||||
wrapper_dir.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||
if "CARGO_HOME" in os.environ:
|
||||
cargo_home = Path(os.environ["CARGO_HOME"])
|
||||
else:
|
||||
cargo_home = Path.home() / ".cargo"
|
||||
cargo_bin = cargo_home / "bin"
|
||||
for src in cargo_bin.iterdir():
|
||||
tgt = wrapper_dir / src.name
|
||||
tgt.symlink_to(src)
|
||||
|
||||
# Build rustup_wrapper
|
||||
wrapper_src = Path("tools", "rustup_wrapper")
|
||||
cargo_toml = wrapper_src / "Cargo.toml"
|
||||
cmds = ["build", "--release", f"--manifest-path={cargo_toml}"]
|
||||
if args.verbose > 1:
|
||||
cmds.append("--verbose")
|
||||
run_cargo(cmds)
|
||||
|
||||
# Replace rustup with wrapper
|
||||
wrapper = wrapper_dir / (f"rustup{EXE_EXT}")
|
||||
wrapper.unlink(missing_ok=True)
|
||||
cp(wrapper_src / "target" / "release" / (f"rustup_wrapper{EXE_EXT}"), wrapper)
|
||||
wrapper.chmod(0o755)
|
||||
##########################
|
||||
# Config, paths, argparse
|
||||
##########################
|
||||
|
||||
|
||||
##################
|
||||
# Config and args
|
||||
##################
|
||||
def ensure_paths():
|
||||
global sdk_path, ndk_root, ndk_path, ndk_build, rust_bin
|
||||
global llvm_bin, cargo, gradlew, adb_path, native_gen_path
|
||||
|
||||
# Skip if already initialized
|
||||
if "sdk_path" in globals():
|
||||
return
|
||||
|
||||
try:
|
||||
sdk_path = Path(os.environ["ANDROID_HOME"])
|
||||
except KeyError:
|
||||
try:
|
||||
sdk_path = Path(os.environ["ANDROID_SDK_ROOT"])
|
||||
except KeyError:
|
||||
error("Please set Android SDK path to environment variable ANDROID_HOME")
|
||||
|
||||
ndk_root = sdk_path / "ndk"
|
||||
ndk_path = ndk_root / "magisk"
|
||||
ndk_build = ndk_path / "ndk-build"
|
||||
rust_bin = ndk_path / "toolchains" / "rust" / "bin"
|
||||
llvm_bin = (
|
||||
ndk_path / "toolchains" / "llvm" / "prebuilt" / f"{os_name}-x86_64" / "bin"
|
||||
)
|
||||
cargo = rust_bin / "cargo"
|
||||
adb_path = sdk_path / "platform-tools" / "adb"
|
||||
gradlew = Path.cwd() / "gradlew"
|
||||
|
||||
|
||||
# We allow using several functionality with only ADB
|
||||
def ensure_adb():
|
||||
global adb_path
|
||||
if "adb_path" not in globals():
|
||||
adb_path = shutil.which("adb")
|
||||
if not adb_path:
|
||||
error("Command 'adb' cannot be found in PATH")
|
||||
else:
|
||||
adb_path = Path(adb_path)
|
||||
|
||||
|
||||
def parse_props(file):
|
||||
@@ -751,17 +787,19 @@ def parse_args():
|
||||
ndk_parser = subparsers.add_parser("ndk", help="setup Magisk NDK")
|
||||
|
||||
emu_parser = subparsers.add_parser("emulator", help="setup AVD for development")
|
||||
emu_parser.add_argument("apk", help="a Magisk APK to use", nargs="?")
|
||||
emu_parser.add_argument(
|
||||
"-s", "--skip", action="store_true", help="skip building binaries and the app"
|
||||
"-b", "--build", action="store_true", help="build before patching"
|
||||
)
|
||||
|
||||
avd_patch_parser = subparsers.add_parser(
|
||||
"avd_patch", help="patch AVD ramdisk.img or init_boot.img"
|
||||
)
|
||||
avd_patch_parser.add_argument("image", help="path to ramdisk.img or init_boot.img")
|
||||
avd_patch_parser.add_argument("output", help="optional output file name", nargs="?")
|
||||
avd_patch_parser.add_argument("output", help="output file name")
|
||||
avd_patch_parser.add_argument("--apk", help="a Magisk APK to use")
|
||||
avd_patch_parser.add_argument(
|
||||
"-s", "--skip", action="store_true", help="skip building binaries and the app"
|
||||
"-b", "--build", action="store_true", help="build before patching"
|
||||
)
|
||||
|
||||
cargo_parser = subparsers.add_parser(
|
||||
@@ -769,6 +807,8 @@ def parse_args():
|
||||
)
|
||||
cargo_parser.add_argument("commands", nargs=argparse.REMAINDER)
|
||||
|
||||
clippy_parser = subparsers.add_parser("clippy", help="run clippy on Rust sources")
|
||||
|
||||
rustup_parser = subparsers.add_parser("rustup", help="setup rustup wrapper")
|
||||
rustup_parser.add_argument(
|
||||
"wrapper_dir", help="path to setup rustup wrapper binaries"
|
||||
@@ -778,6 +818,7 @@ def parse_args():
|
||||
all_parser.set_defaults(func=build_all)
|
||||
native_parser.set_defaults(func=build_native)
|
||||
cargo_parser.set_defaults(func=cargo_cli)
|
||||
clippy_parser.set_defaults(func=clippy_cli)
|
||||
rustup_parser.set_defaults(func=setup_rustup)
|
||||
app_parser.set_defaults(func=build_app)
|
||||
stub_parser.set_defaults(func=build_stub)
|
||||
@@ -794,7 +835,13 @@ def parse_args():
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
args = parse_args()
|
||||
load_config()
|
||||
vars(args)["force_out"] = False
|
||||
args.func()
|
||||
def main():
|
||||
global args
|
||||
args = parse_args()
|
||||
load_config()
|
||||
vars(args)["force_out"] = False
|
||||
args.func()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -74,7 +74,7 @@ fun Project.setupCommon() {
|
||||
compileSdkVersion(35)
|
||||
buildToolsVersion = "35.0.1"
|
||||
ndkPath = "$sdkDirectory/ndk/magisk"
|
||||
ndkVersion = "28.0.12674087"
|
||||
ndkVersion = "29.0.13113456"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 23
|
||||
@@ -115,6 +115,27 @@ fun Project.setupCommon() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.downloadFile(url: String, checksum: String): File {
|
||||
val file = layout.buildDirectory.file(checksum).get().asFile
|
||||
if (file.exists()) {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
file.inputStream().use { md.update(it.readAllBytes()) }
|
||||
val hash = HexFormat.of().formatHex(md.digest())
|
||||
if (hash != checksum) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
if (!file.exists()) {
|
||||
file.parentFile.mkdirs()
|
||||
URI(url).toURL().openStream().use { dl ->
|
||||
file.outputStream().use {
|
||||
dl.copyTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
const val BUSYBOX_DOWNLOAD_URL =
|
||||
"https://github.com/topjohnwu/magisk-files/releases/download/files/busybox-1.36.1.1.zip"
|
||||
const val BUSYBOX_ZIP_CHECKSUM =
|
||||
@@ -144,24 +165,7 @@ fun Project.setupCoreLib() {
|
||||
|
||||
val downloadBusybox by tasks.registering(Copy::class) {
|
||||
dependsOn(syncLibs)
|
||||
val bb = layout.buildDirectory.file(BUSYBOX_ZIP_CHECKSUM).get().asFile
|
||||
if (bb.exists()) {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
bb.inputStream().use { md.update(it.readAllBytes()) }
|
||||
val hash = HexFormat.of().formatHex(md.digest())
|
||||
if (hash != BUSYBOX_ZIP_CHECKSUM) {
|
||||
bb.delete()
|
||||
}
|
||||
}
|
||||
if (!bb.exists()) {
|
||||
bb.parentFile.mkdirs()
|
||||
URI(BUSYBOX_DOWNLOAD_URL).toURL().openStream().use { dl ->
|
||||
bb.outputStream().use {
|
||||
dl.copyTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
from(zipTree(bb))
|
||||
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
|
||||
include(abiList.map { "$it/libbusybox.so" })
|
||||
into("src/main/jniLibs")
|
||||
}
|
||||
@@ -462,3 +466,31 @@ fun Project.setupStubApk() {
|
||||
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
|
||||
}
|
||||
}
|
||||
|
||||
const val LSPOSED_DOWNLOAD_URL =
|
||||
"https://github.com/LSPosed/LSPosed/releases/download/v1.9.2/LSPosed-v1.9.2-7024-zygisk-release.zip"
|
||||
const val LSPOSED_CHECKSUM =
|
||||
"0ebc6bcb465d1c4b44b7220ab5f0252e6b4eb7fe43da74650476d2798bb29622"
|
||||
|
||||
const val SHAMIKO_DOWNLOAD_URL =
|
||||
"https://github.com/LSPosed/LSPosed.github.io/releases/download/shamiko-383/Shamiko-v1.2.1-383-release.zip"
|
||||
const val SHAMIKO_CHECKSUM =
|
||||
"93754a038c2d8f0e985bad45c7303b96f70a93d8335060e50146f028d3a9b13f"
|
||||
|
||||
fun Project.setupTestApk() {
|
||||
setupAppCommon()
|
||||
|
||||
androidApp.applicationVariants.all {
|
||||
val variantCapped = name.replaceFirstChar { it.uppercase() }
|
||||
val dlTask by tasks.register("download${variantCapped}Lsposed", Sync::class) {
|
||||
from(downloadFile(LSPOSED_DOWNLOAD_URL, LSPOSED_CHECKSUM)) {
|
||||
rename { "lsposed.zip" }
|
||||
}
|
||||
from(downloadFile(SHAMIKO_DOWNLOAD_URL, SHAMIKO_CHECKSUM)) {
|
||||
rename { "shamiko.zip" }
|
||||
}
|
||||
into("src/${this@all.name}/assets")
|
||||
}
|
||||
mergeAssetsProvider.configure { dependsOn(dlTask) }
|
||||
}
|
||||
}
|
||||
|
||||
13
docs/faq.md
13
docs/faq.md
@@ -7,9 +7,10 @@ If you have USB debugging enabled in developer options, connect your phone to th
|
||||
If unfortunately you do not have USB debugging enabled you can boot using the Safe Mode key combo to cause Magisk to create an empty file named 'disable' in modules directories which disables modules when next booted with Magisk. Most modern Android devices support such a special key combo at boot to enter system Safe Mode as an emergency option, but **please note** that Magisk's key combo detection occurs _earlier_ than system detection so the key combo timing indicated by many online guides may need to be altered to activate Magisk's Safe Mode. (It's possible to activate system Safe Mode but not Magisk Safe Mode and vice versa.)
|
||||
|
||||
The following details should ensure that modules are properly disabled:
|
||||
1) Many online guides for entering Safe Mode say 'When the animated logo appears, press and hold the volume down button until the system boots' or similar. This may actually be _too late_ for Magisk detection however and result in activating system Safe Mode but modules are not disabled.
|
||||
2) By pressing the volume down button some seconds before the animation and releasing it as soon as the boot animation appears, Magisk's Safe Mode should be activated without activating system Safe Mode (thus avoiding disabling other device and app settings) and the device should then simply boot to normal system with modules disabled.
|
||||
3) By pressing the volume down button some seconds before the animation and holding it until the system boots, both Magisk's Safe Mode and system Safe Mode should be activated. Next, after booting back to normal system, modules will be disabled.
|
||||
|
||||
1. Many online guides for entering Safe Mode say 'When the animated logo appears, press and hold the volume down button until the system boots' or similar. This may actually be _too late_ for Magisk detection however and result in activating system Safe Mode but modules are not disabled.
|
||||
2. By pressing the volume down button some seconds before the animation and releasing it as soon as the boot animation appears, Magisk's Safe Mode should be activated without activating system Safe Mode (thus avoiding disabling other device and app settings) and the device should then simply boot to normal system with modules disabled.
|
||||
3. By pressing the volume down button some seconds before the animation and holding it until the system boots, both Magisk's Safe Mode and system Safe Mode should be activated. Next, after booting back to normal system, modules will be disabled.
|
||||
|
||||
### Q: Why is X app detecting root?
|
||||
|
||||
@@ -20,3 +21,9 @@ Magisk no longer handles root hiding. There are plenty of Magisk/Zygisk modules
|
||||
When hiding the Magisk app, it will install a "stub" APK that has nothing in it. The only functionality this stub app has is downloading the full Magisk app APK into its internal storage and dynamically loading it. Due to the fact that the APK is literally _empty_, it does not contain the image resource for the app icon.
|
||||
|
||||
When you open the hidden Magisk app, it will offer you the option to create a shortcut in the homescreen (which has both the correct app name and icon) for your convenience. You can also manually ask the app to create the icon in app settings.
|
||||
|
||||
### Q: How to use Magisk in the emulator?
|
||||
|
||||
With the emulator running and accessible via ADB, run `./build.py emulator <path to Magisk APK>` to temporarily install Magisk on to the emulator. The patch is not persistent, meaning Magisk will be lost after a reboot, so re-execute the script to emulate a reboot if required.
|
||||
|
||||
The script is only tested on the official Android Virtual Device (AVD) shipped alongside Android Studio; other emulators may work, but the emulator must have SELinux enabled.
|
||||
|
||||
@@ -30,5 +30,5 @@ android.nonFinalResIds=false
|
||||
|
||||
# Magisk
|
||||
magisk.stubVersion=40
|
||||
magisk.versionCode=28102
|
||||
magisk.ondkVersion=r28.2
|
||||
magisk.versionCode=28103
|
||||
magisk.ondkVersion=r29.0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[versions]
|
||||
kotlin = "2.1.0"
|
||||
android = "8.8.0"
|
||||
ksp = "2.1.0-1.0.29"
|
||||
kotlin = "2.1.10"
|
||||
android = "8.9.0"
|
||||
ksp = "2.1.10-1.0.31"
|
||||
rikka = "1.3.0"
|
||||
navigation = "2.8.4"
|
||||
libsu = "6.0.0"
|
||||
|
||||
@@ -8,5 +8,5 @@ target-dir = "../out/rust"
|
||||
|
||||
[unstable]
|
||||
build-std = ["std", "panic_abort"]
|
||||
build-std-features = ["panic_immediate_abort"]
|
||||
build-std-features = ["panic_immediate_abort", "optimize_for_size"]
|
||||
profile-rustflags = true
|
||||
|
||||
@@ -18,8 +18,6 @@ LOCAL_SRC_FILES := \
|
||||
core/applets.cpp \
|
||||
core/magisk.cpp \
|
||||
core/daemon.cpp \
|
||||
core/bootstages.cpp \
|
||||
core/socket.cpp \
|
||||
core/scripting.cpp \
|
||||
core/selinux.cpp \
|
||||
core/sqlite.cpp \
|
||||
@@ -30,9 +28,7 @@ LOCAL_SRC_FILES := \
|
||||
core/su/su.cpp \
|
||||
core/su/connect.cpp \
|
||||
core/su/pts.cpp \
|
||||
core/su/su_daemon.cpp \
|
||||
core/zygisk/entry.cpp \
|
||||
core/zygisk/main.cpp \
|
||||
core/zygisk/module.cpp \
|
||||
core/zygisk/hook.cpp \
|
||||
core/deny/cli.cpp \
|
||||
@@ -67,11 +63,9 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libinit-rs
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
init/init.cpp \
|
||||
init/mount.cpp \
|
||||
init/rootdir.cpp \
|
||||
init/getinfo.cpp \
|
||||
init/twostage.cpp \
|
||||
init/selinux.cpp \
|
||||
init/init-rs.cpp
|
||||
|
||||
@@ -79,6 +73,7 @@ LOCAL_LDFLAGS := -static
|
||||
|
||||
ifdef B_CRT0
|
||||
LOCAL_STATIC_LIBRARIES += crt0
|
||||
LOCAL_LDFLAGS += -Wl,--defsym=vfprintf=tiny_vfprintf
|
||||
endif
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@@ -93,8 +88,6 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libbase \
|
||||
liblzma \
|
||||
liblz4 \
|
||||
libbz2 \
|
||||
libz \
|
||||
libzopfli \
|
||||
libboot-rs
|
||||
|
||||
@@ -125,8 +118,6 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libpolicy \
|
||||
libpolicy-rs
|
||||
|
||||
LOCAL_SRC_FILES := sepolicy/main.cpp
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
|
||||
305
native/src/Cargo.lock
generated
305
native/src/Cargo.lock
generated
@@ -10,17 +10,20 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "argh"
|
||||
version = "0.1.12"
|
||||
source = "git+https://github.com/google/argh.git?rev=1c632b046d084e7bde86b82dfc969b30b4647c8c#1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ff18325c8a36b82f992e533ece1ec9f9a9db446bd1c14d4f936bac88fcd240"
|
||||
dependencies = [
|
||||
"argh_derive",
|
||||
"argh_shared",
|
||||
"rust-fuzzy-search",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argh_derive"
|
||||
version = "0.1.12"
|
||||
source = "git+https://github.com/google/argh.git?rev=1c632b046d084e7bde86b82dfc969b30b4647c8c#1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb7b2b83a50d329d5d8ccc620f5c7064028828538bdf5646acd60dc1f767803"
|
||||
dependencies = [
|
||||
"argh_shared",
|
||||
"proc-macro2",
|
||||
@@ -30,11 +33,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "argh_shared"
|
||||
version = "0.1.12"
|
||||
source = "git+https://github.com/google/argh.git?rev=1c632b046d084e7bde86b82dfc969b30b4647c8c#1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@@ -96,18 +97,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.20.0"
|
||||
version = "1.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
|
||||
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
|
||||
dependencies = [
|
||||
"bytemuck_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck_derive"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
|
||||
checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -122,9 +123,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.3"
|
||||
version = "1.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -137,18 +138,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.23"
|
||||
version = "4.5.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.23"
|
||||
version = "4.5.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -173,9 +174,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.0-rc.3"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ff6be19477a1bd5441f382916a89bc2a0b2c35db6d41e0f6e8538bf6d6463f"
|
||||
checksum = "1cb3c4a0d3776f7535c32793be81d6d5fec0d48ac70955d9834e643aa249a52f"
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
@@ -199,22 +200,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.6.0-rc.6"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d748d1f5b807ee6d0df5a548d0130417295c3aaed1dcbbb3d6a2e7106e11fcca"
|
||||
checksum = "96272c2ff28b807e09250b180ad1fb7889a3258f7455759b5c3c58b719467130"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"num-traits",
|
||||
"rand_core",
|
||||
"serdect",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -230,9 +232,19 @@ dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-primes"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76db6c35962f23f58ab08c156831c55caf69fe85a63ae3780d4fdb24c38b32b6"
|
||||
dependencies = [
|
||||
"crypto-bigint",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.133"
|
||||
version = "1.0.137"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-cmd",
|
||||
@@ -243,7 +255,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-gen"
|
||||
version = "0.7.133"
|
||||
version = "0.7.137"
|
||||
dependencies = [
|
||||
"codespan-reporting",
|
||||
"proc-macro2",
|
||||
@@ -253,7 +265,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-cmd"
|
||||
version = "1.0.133"
|
||||
version = "1.0.137"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
@@ -264,11 +276,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.133"
|
||||
version = "1.0.137"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.133"
|
||||
version = "1.0.137"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -300,6 +312,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.0-pre.9"
|
||||
@@ -371,9 +392,9 @@ checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
@@ -426,31 +447,34 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
name = "libbz2-rs-sys"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
checksum = "0864a00c8d019e36216b69c2c4ce50b83b7bd966add3cf5ba554ec44f8bebcf5"
|
||||
dependencies = [
|
||||
"spin",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.168"
|
||||
version = "0.2.170"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
name = "libz-rs-sys"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
checksum = "902bc563b5d65ad9bba616b490842ef0651066a1a1dc3ce1087113ffcb873c8d"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||
|
||||
[[package]]
|
||||
name = "magisk"
|
||||
@@ -461,6 +485,7 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"cxx",
|
||||
"cxx-gen",
|
||||
"derive",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pb-rs",
|
||||
@@ -474,6 +499,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"argh",
|
||||
"base",
|
||||
"block-buffer",
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"cxx",
|
||||
@@ -481,6 +507,8 @@ dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"fdt",
|
||||
"libbz2-rs-sys",
|
||||
"libz-rs-sys",
|
||||
"num-traits",
|
||||
"p256",
|
||||
"p384",
|
||||
@@ -488,6 +516,7 @@ dependencies = [
|
||||
"pb-rs",
|
||||
"quick-protobuf",
|
||||
"rsa",
|
||||
"sec1",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"size",
|
||||
@@ -508,6 +537,7 @@ dependencies = [
|
||||
name = "magiskpolicy"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"argh",
|
||||
"base",
|
||||
"cxx",
|
||||
"cxx-gen",
|
||||
@@ -535,23 +565,6 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"libm",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.4.2"
|
||||
@@ -563,26 +576,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -590,7 +583,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -663,23 +655,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.11.0-rc.1"
|
||||
version = "0.11.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eacd2c7141f32aef1cfd1ad0defb5287a3d94592d7ab57c1ae20e3f9f1f0db1f"
|
||||
checksum = "f22636de7c995e997ed3d8d2949b7414d4faba3efa7312a6c0e75d875a14bdd4"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primefield"
|
||||
version = "0.14.0-pre.0"
|
||||
@@ -697,9 +680,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -714,33 +697,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
@@ -762,15 +725,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.10.0-pre.3"
|
||||
version = "0.10.0-pre.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07058e83b684989ab0559f9e22322f4e3f7e49147834ed0bae40486b9e70473c"
|
||||
checksum = "e82e90f434676d49758cab5b3ff2cc1fd8f12bf7d79b70c6088bc5a4e7c63270"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"crypto-bigint",
|
||||
"crypto-primes",
|
||||
"digest",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
@@ -782,10 +744,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.18"
|
||||
name = "rust-fuzzy-search"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
@@ -803,24 +771,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
version = "1.0.218"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
version = "1.0.218"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serdect"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f42f67da2385b51a5f9652db9c93d78aeaf7610bf5ec366080b6de810604af53"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.11.0-pre.4"
|
||||
@@ -861,21 +839,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "size"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fed904c7fb2856d868b92464fc8fa597fce366edea1a9cbfaa8cb5fe080bd6d"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
checksum = "1b6709c7b6754dca1311b3c73e79fcce40dd414c782c66d88e8823030093b02b"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
@@ -901,9 +867,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
version = "2.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -921,18 +887,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.6"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.6"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -941,9 +907,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tls_codec"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a"
|
||||
checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b"
|
||||
dependencies = [
|
||||
"tls_codec_derive",
|
||||
"zeroize",
|
||||
@@ -951,9 +917,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tls_codec_derive"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c"
|
||||
checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -962,15 +928,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@@ -1084,27 +1050,6 @@ dependencies = [
|
||||
"tls_codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
@@ -1124,3 +1069,9 @@ dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b20717f0917c908dc63de2e44e97f1e6b126ca58d0e391cee86d504eb8fbd05"
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
[workspace]
|
||||
exclude = ["external"]
|
||||
members = ["base", "boot", "core", "init", "sepolicy"]
|
||||
members = ["base", "boot", "core", "core/derive", "init", "sepolicy"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.0"
|
||||
edition = "2024"
|
||||
|
||||
[workspace.dependencies]
|
||||
cxx = { path = "external/cxx-rs" }
|
||||
cxx-gen = { path = "external/cxx-rs/gen/lib" }
|
||||
@@ -12,25 +16,30 @@ num-traits = "0.2"
|
||||
num-derive = "0.4"
|
||||
thiserror = "2.0"
|
||||
byteorder = "1"
|
||||
size = "0.4"
|
||||
size = "0.5"
|
||||
sha1 = "0.11.0-pre.4"
|
||||
sha2 = "=0.11.0-pre.4"
|
||||
sha2 = "0.11.0-pre.4"
|
||||
digest = "0.11.0-pre.9"
|
||||
p256 = "0.14.0-pre.2"
|
||||
p384 = "0.14.0-pre.2"
|
||||
p521 = "0.14.0-pre.2"
|
||||
rsa = "0.10.0-pre.3"
|
||||
rsa = "0.10.0-pre.4"
|
||||
x509-cert = "0.3.0-pre.0"
|
||||
der = "0.8.0-rc.1"
|
||||
bytemuck = "1.16"
|
||||
fdt = "0.1"
|
||||
const_format = "0.2"
|
||||
bit-set = "0.8"
|
||||
syn = "2"
|
||||
quote = "1"
|
||||
proc-macro2 = "1"
|
||||
argh = { version = "0.1.13", default-features = false }
|
||||
libz-rs-sys = { version = "0.4.2", default-features = false, features=["c-allocator"] }
|
||||
libbz2-rs-sys = { version = "0.1.3", default-features = false, features = ["c-allocator"] }
|
||||
|
||||
[workspace.dependencies.argh]
|
||||
git = "https://github.com/google/argh.git"
|
||||
rev = "1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
||||
default-features = false
|
||||
# Pin version to prevent cargo update break builds
|
||||
block-buffer = "=0.11.0-rc.3"
|
||||
sec1 = "=0.8.0-rc.3"
|
||||
|
||||
[workspace.dependencies.pb-rs]
|
||||
git = "https://github.com/tafia/quick-protobuf.git"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "base"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::gen::gen_cxx_binding;
|
||||
use crate::codegen::gen_cxx_binding;
|
||||
|
||||
#[path = "../include/gen.rs"]
|
||||
mod gen;
|
||||
#[path = "../include/codegen.rs"]
|
||||
mod codegen;
|
||||
|
||||
fn main() {
|
||||
gen_cxx_binding("base-rs");
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use cxx::{type_id, ExternType};
|
||||
use libc::c_char;
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::min;
|
||||
use std::ffi::{CStr, FromBytesWithNulError, OsStr};
|
||||
use std::fmt::{Arguments, Debug, Display, Formatter, Write};
|
||||
use std::fmt::{Debug, Display, Formatter, Write};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::Utf8Error;
|
||||
use std::{fmt, mem, slice, str};
|
||||
|
||||
use cxx::{type_id, ExternType};
|
||||
use libc::c_char;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::slice_from_ptr_mut;
|
||||
@@ -18,23 +18,114 @@ use crate::slice_from_ptr_mut;
|
||||
// Several Utf8CStr types:
|
||||
//
|
||||
// Utf8CStr: can only exist as reference, similar to &str
|
||||
// Utf8CString: dynamically sized buffer allocated on the heap
|
||||
// Utf8CString: dynamically sized buffer allocated on the heap, similar to String
|
||||
// Utf8CStrBufRef: reference to a fixed sized buffer
|
||||
// Utf8CStrBufArr<N>: fixed sized buffer allocated on the stack
|
||||
//
|
||||
// For easier usage, please use the helper functions in cstr_buf.
|
||||
//
|
||||
// In most cases, these are the types being used
|
||||
//
|
||||
// &Utf8CStr: whenever a printable null terminated string is needed
|
||||
// &mut dyn Utf8CStrWrite: whenever we need a buffer that only needs to support appending
|
||||
// strings to the end, and has to be null terminated
|
||||
// &mut dyn Utf8CStrBuf: whenever we need a buffer that needs to support appending
|
||||
// strings to the end, and has to be null terminated
|
||||
// &mut dyn Utf8CStrBuf: whenever we need a pre-allocated buffer that is large enough to fit
|
||||
// in the result, and has to be null terminated
|
||||
//
|
||||
// All types dereferences to &Utf8CStr.
|
||||
// Utf8CString, Utf8CStrBufRef, and Utf8CStrBufArr<N> implements Utf8CStrWrite.
|
||||
// Utf8CStrBufRef and Utf8CStrBufArr<N> implements Utf8CStrBuf.
|
||||
// Utf8CString, Utf8CStrBufRef, and Utf8CStrBufArr<N> implements Utf8CStrBuf.
|
||||
|
||||
fn utf8_cstr_buf_append(buf: &mut dyn Utf8CStrBuf, s: &[u8]) -> usize {
|
||||
// Public helper functions
|
||||
|
||||
pub mod cstr_buf {
|
||||
use super::{Utf8CStrBufArr, Utf8CStrBufRef, Utf8CString};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn with_capacity(capacity: usize) -> Utf8CString {
|
||||
Utf8CString::with_capacity(capacity)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn default() -> Utf8CStrBufArr<4096> {
|
||||
Utf8CStrBufArr::default()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn new<const N: usize>() -> Utf8CStrBufArr<N> {
|
||||
Utf8CStrBufArr::new()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn wrap(buf: &mut [u8]) -> Utf8CStrBufRef {
|
||||
Utf8CStrBufRef::from(buf)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn wrap_ptr<'a>(buf: *mut u8, len: usize) -> Utf8CStrBufRef<'a> {
|
||||
unsafe { Utf8CStrBufRef::from_ptr(buf, len) }
|
||||
}
|
||||
}
|
||||
|
||||
// Trait definitions
|
||||
|
||||
pub trait Utf8CStrBuf:
|
||||
Write + AsRef<Utf8CStr> + AsMut<Utf8CStr> + Deref<Target = Utf8CStr> + DerefMut
|
||||
{
|
||||
// The length of the string without the terminating null character.
|
||||
// assert_true(len <= capacity - 1)
|
||||
fn len(&self) -> usize;
|
||||
// Set the length of the string
|
||||
//
|
||||
// It is your responsibility to:
|
||||
// 1. Null terminate the string by setting the next byte after len to null
|
||||
// 2. Ensure len <= capacity - 1
|
||||
// 3. All bytes from 0 to len is valid UTF-8 and does not contain null
|
||||
unsafe fn set_len(&mut self, len: usize);
|
||||
fn push_str(&mut self, s: &str) -> usize;
|
||||
fn push_lossy(&mut self, s: &[u8]) -> usize;
|
||||
// The capacity of the internal buffer. The maximum string length this buffer can contain
|
||||
// is capacity - 1, because the last byte is reserved for the terminating null character.
|
||||
fn capacity(&self) -> usize;
|
||||
fn clear(&mut self);
|
||||
|
||||
#[inline(always)]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
trait Utf8CStrBufWithSlice: Utf8CStrBuf {
|
||||
fn buf(&self) -> &[u8];
|
||||
unsafe fn mut_buf(&mut self) -> &mut [u8];
|
||||
}
|
||||
|
||||
trait AsUtf8CStr {
|
||||
fn as_utf8_cstr(&self) -> &Utf8CStr;
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr;
|
||||
}
|
||||
|
||||
impl<T: Utf8CStrBufWithSlice> AsUtf8CStr for T {
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr(&self) -> &Utf8CStr {
|
||||
// SAFETY: the internal buffer is always UTF-8 checked
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
unsafe { Utf8CStr::from_bytes_unchecked(self.buf().get_unchecked(..(self.len() + 1))) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
||||
// SAFETY: the internal buffer is always UTF-8 checked
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
unsafe {
|
||||
let len = self.len() + 1;
|
||||
Utf8CStr::from_bytes_unchecked_mut(self.mut_buf().get_unchecked_mut(..len))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for Utf8CString
|
||||
|
||||
fn utf8_cstr_buf_append(buf: &mut dyn Utf8CStrBufWithSlice, s: &[u8]) -> usize {
|
||||
let mut used = buf.len();
|
||||
if used >= buf.capacity() - 1 {
|
||||
// Truncate
|
||||
@@ -51,7 +142,7 @@ fn utf8_cstr_buf_append(buf: &mut dyn Utf8CStrBuf, s: &[u8]) -> usize {
|
||||
len
|
||||
}
|
||||
|
||||
fn utf8_cstr_append_lossy(buf: &mut dyn Utf8CStrWrite, s: &[u8]) -> usize {
|
||||
fn utf8_cstr_append_lossy(buf: &mut dyn Utf8CStrBuf, s: &[u8]) -> usize {
|
||||
let mut len = 0_usize;
|
||||
for chunk in s.utf8_chunks() {
|
||||
len += buf.push_str(chunk.valid());
|
||||
@@ -62,44 +153,6 @@ fn utf8_cstr_append_lossy(buf: &mut dyn Utf8CStrWrite, s: &[u8]) -> usize {
|
||||
len
|
||||
}
|
||||
|
||||
// Trait definitions
|
||||
|
||||
pub trait Utf8CStrWrite:
|
||||
Write + AsRef<Utf8CStr> + AsMut<Utf8CStr> + Deref<Target = Utf8CStr> + DerefMut
|
||||
{
|
||||
fn len(&self) -> usize;
|
||||
#[inline(always)]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
fn push_str(&mut self, s: &str) -> usize;
|
||||
fn push_lossy(&mut self, s: &[u8]) -> usize;
|
||||
fn clear(&mut self);
|
||||
}
|
||||
|
||||
pub trait Utf8CStrBuf: Utf8CStrWrite {
|
||||
fn buf(&self) -> &[u8];
|
||||
|
||||
// Modifying the underlying buffer or length is unsafe because it can either:
|
||||
// 1. Break null termination
|
||||
// 2. Break UTF-8 validation
|
||||
// 3. Introduce inner null byte in the string
|
||||
unsafe fn mut_buf(&mut self) -> &mut [u8];
|
||||
unsafe fn set_len(&mut self, len: usize);
|
||||
|
||||
#[inline(always)]
|
||||
fn capacity(&self) -> usize {
|
||||
self.buf().len()
|
||||
}
|
||||
}
|
||||
|
||||
trait AsUtf8CStr {
|
||||
fn as_utf8_cstr(&self) -> &Utf8CStr;
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr;
|
||||
}
|
||||
|
||||
// Implementation for Utf8CString
|
||||
|
||||
pub trait StringExt {
|
||||
fn nul_terminate(&mut self) -> &mut [u8];
|
||||
}
|
||||
@@ -132,13 +185,24 @@ impl StringExt for PathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Utf8CString(String);
|
||||
|
||||
impl Default for Utf8CString {
|
||||
fn default() -> Self {
|
||||
Utf8CString::with_capacity(256)
|
||||
}
|
||||
}
|
||||
|
||||
impl Utf8CString {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
pub fn with_capacity(capacity: usize) -> Utf8CString {
|
||||
Utf8CString::from(String::with_capacity(capacity))
|
||||
}
|
||||
|
||||
pub fn ensure_capacity(&mut self, capacity: usize) {
|
||||
if self.capacity() >= capacity {
|
||||
return;
|
||||
}
|
||||
self.0.reserve(capacity - self.0.len())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,16 +215,28 @@ impl AsUtf8CStr for Utf8CString {
|
||||
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
||||
Utf8CStr::from_string(&mut self.0)
|
||||
// SAFETY: the internal string is always null terminated
|
||||
unsafe {
|
||||
mem::transmute(slice::from_raw_parts_mut(
|
||||
self.0.as_mut_ptr(),
|
||||
self.0.len() + 1,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Utf8CStrWrite for Utf8CString {
|
||||
impl Utf8CStrBuf for Utf8CString {
|
||||
#[inline(always)]
|
||||
fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
unsafe fn set_len(&mut self, len: usize) {
|
||||
unsafe {
|
||||
self.0.as_mut_vec().set_len(len);
|
||||
}
|
||||
}
|
||||
|
||||
fn push_str(&mut self, s: &str) -> usize {
|
||||
self.0.push_str(s);
|
||||
self.0.nul_terminate();
|
||||
@@ -172,6 +248,10 @@ impl Utf8CStrWrite for Utf8CString {
|
||||
utf8_cstr_append_lossy(self, s)
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
self.0.capacity()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
self.0.nul_terminate();
|
||||
@@ -185,24 +265,9 @@ impl From<String> for Utf8CString {
|
||||
}
|
||||
}
|
||||
|
||||
// Implementations for Utf8CStrBuf
|
||||
|
||||
impl<T: Utf8CStrBuf> AsUtf8CStr for T {
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr(&self) -> &Utf8CStr {
|
||||
// SAFETY: the internal buffer is always UTF-8 checked
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
unsafe { Utf8CStr::from_bytes_unchecked(self.buf().get_unchecked(..(self.len() + 1))) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
||||
// SAFETY: the internal buffer is always UTF-8 checked
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
unsafe {
|
||||
let len = self.len() + 1;
|
||||
Utf8CStr::from_bytes_unchecked_mut(self.mut_buf().get_unchecked_mut(..len))
|
||||
}
|
||||
impl Borrow<Utf8CStr> for Utf8CString {
|
||||
fn borrow(&self) -> &Utf8CStr {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +279,7 @@ pub struct Utf8CStrBufRef<'a> {
|
||||
|
||||
impl<'a> Utf8CStrBufRef<'a> {
|
||||
pub unsafe fn from_ptr(buf: *mut u8, len: usize) -> Utf8CStrBufRef<'a> {
|
||||
Self::from(slice_from_ptr_mut(buf, len))
|
||||
unsafe { Self::from(slice_from_ptr_mut(buf, len)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +290,7 @@ impl<'a> From<&'a mut [u8]> for Utf8CStrBufRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Utf8CStrBuf for Utf8CStrBufRef<'_> {
|
||||
impl Utf8CStrBufWithSlice for Utf8CStrBufRef<'_> {
|
||||
#[inline(always)]
|
||||
fn buf(&self) -> &[u8] {
|
||||
self.buf
|
||||
@@ -235,11 +300,6 @@ impl Utf8CStrBuf for Utf8CStrBufRef<'_> {
|
||||
unsafe fn mut_buf(&mut self) -> &mut [u8] {
|
||||
self.buf
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn set_len(&mut self, len: usize) {
|
||||
self.used = len;
|
||||
}
|
||||
}
|
||||
|
||||
// UTF-8 validated + null terminated buffer on the stack
|
||||
@@ -257,7 +317,7 @@ impl<const N: usize> Utf8CStrBufArr<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Utf8CStrBuf for Utf8CStrBufArr<N> {
|
||||
impl<const N: usize> Utf8CStrBufWithSlice for Utf8CStrBufArr<N> {
|
||||
#[inline(always)]
|
||||
fn buf(&self) -> &[u8] {
|
||||
&self.buf
|
||||
@@ -267,16 +327,6 @@ impl<const N: usize> Utf8CStrBuf for Utf8CStrBufArr<N> {
|
||||
unsafe fn mut_buf(&mut self) -> &mut [u8] {
|
||||
&mut self.buf
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn set_len(&mut self, len: usize) {
|
||||
self.used = len;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn capacity(&self) -> usize {
|
||||
N
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Utf8CStrBufArr<4096> {
|
||||
@@ -310,13 +360,6 @@ impl Utf8CStr {
|
||||
Self::from_cstr(CStr::from_bytes_with_nul(buf)?)
|
||||
}
|
||||
|
||||
pub fn from_bytes_mut(buf: &mut [u8]) -> Result<&mut Utf8CStr, StrErr> {
|
||||
CStr::from_bytes_with_nul(buf)?;
|
||||
str::from_utf8(buf)?;
|
||||
// Both condition checked
|
||||
unsafe { Ok(mem::transmute::<&mut [u8], &mut Utf8CStr>(buf)) }
|
||||
}
|
||||
|
||||
pub fn from_string(s: &mut String) -> &mut Utf8CStr {
|
||||
let buf = s.nul_terminate();
|
||||
// SAFETY: the null byte is explicitly added to the buffer
|
||||
@@ -324,13 +367,13 @@ impl Utf8CStr {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_bytes_unchecked(buf: &[u8]) -> &Utf8CStr {
|
||||
mem::transmute(buf)
|
||||
pub const unsafe fn from_bytes_unchecked(buf: &[u8]) -> &Utf8CStr {
|
||||
unsafe { mem::transmute(buf) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_bytes_unchecked_mut(buf: &mut [u8]) -> &mut Utf8CStr {
|
||||
mem::transmute(buf)
|
||||
unsafe fn from_bytes_unchecked_mut(buf: &mut [u8]) -> &mut Utf8CStr {
|
||||
unsafe { mem::transmute(buf) }
|
||||
}
|
||||
|
||||
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> Result<&'a Utf8CStr, StrErr> {
|
||||
@@ -341,8 +384,10 @@ impl Utf8CStr {
|
||||
}
|
||||
|
||||
pub unsafe fn from_ptr_unchecked<'a>(ptr: *const c_char) -> &'a Utf8CStr {
|
||||
let cstr = CStr::from_ptr(ptr);
|
||||
Self::from_bytes_unchecked(cstr.to_bytes_with_nul())
|
||||
unsafe {
|
||||
let cstr = CStr::from_ptr(ptr);
|
||||
Self::from_bytes_unchecked(cstr.to_bytes_with_nul())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -397,6 +442,16 @@ impl DerefMut for Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Utf8CStr {
|
||||
type Owned = Utf8CString;
|
||||
|
||||
fn to_owned(&self) -> Utf8CString {
|
||||
let mut s = Utf8CString::with_capacity(self.len() + 1);
|
||||
s.push_str(self.as_str());
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
// Notice that we only implement ExternType on Utf8CStr *reference*
|
||||
unsafe impl ExternType for &Utf8CStr {
|
||||
type Id = type_id!("rust::Utf8CStr");
|
||||
@@ -446,47 +501,106 @@ impl DerefMut for FsPath {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FsPathBuf<'a>(&'a mut dyn Utf8CStrWrite);
|
||||
#[repr(transparent)]
|
||||
pub struct FsPathFollow(Utf8CStr);
|
||||
|
||||
impl<'a> FsPathBuf<'a> {
|
||||
pub fn new(value: &'a mut dyn Utf8CStrWrite) -> Self {
|
||||
value.clear();
|
||||
FsPathBuf(value)
|
||||
impl Deref for FsPathFollow {
|
||||
type Target = Utf8CStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Utf8CStr {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FsPathFollow {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Utf8CStr {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
enum Utf8CStrBufOwned<const N: usize> {
|
||||
Dynamic(Utf8CString),
|
||||
Fixed(Utf8CStrBufArr<N>),
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for Utf8CStrBufOwned<N> {
|
||||
type Target = dyn Utf8CStrBuf;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Utf8CStrBufOwned::Dynamic(s) => s,
|
||||
Utf8CStrBufOwned::Fixed(arr) => arr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DerefMut for Utf8CStrBufOwned<N> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Utf8CStrBufOwned::Dynamic(s) => s,
|
||||
Utf8CStrBufOwned::Fixed(arr) => arr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FsPathBuf<const N: usize>(Utf8CStrBufOwned<N>);
|
||||
|
||||
impl FsPathBuf<0> {
|
||||
pub fn new_dynamic(capacity: usize) -> Self {
|
||||
FsPathBuf(Utf8CStrBufOwned::Dynamic(Utf8CString::with_capacity(
|
||||
capacity,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FsPathBuf<4096> {
|
||||
fn default() -> Self {
|
||||
FsPathBuf(Utf8CStrBufOwned::Fixed(cstr_buf::default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FsPathBuf<N> {
|
||||
pub fn new() -> Self {
|
||||
FsPathBuf(Utf8CStrBufOwned::Fixed(cstr_buf::new::<N>()))
|
||||
}
|
||||
|
||||
pub fn join<T: AsRef<str>>(self, path: T) -> Self {
|
||||
fn inner(buf: &mut dyn Utf8CStrWrite, path: &str) {
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
pub fn join<T: AsRef<str>>(mut self, path: T) -> Self {
|
||||
fn inner(buf: &mut dyn Utf8CStrBuf, path: &str) {
|
||||
if path.starts_with('/') {
|
||||
buf.clear();
|
||||
} else {
|
||||
}
|
||||
if !buf.is_empty() && !buf.ends_with('/') {
|
||||
buf.push_str("/");
|
||||
}
|
||||
buf.push_str(path);
|
||||
}
|
||||
inner(self.0, path.as_ref());
|
||||
inner(self.0.deref_mut(), path.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn join_fmt<T: Display>(self, name: T) -> Self {
|
||||
fn inner(buf: &mut dyn Utf8CStrWrite, path: Arguments) {
|
||||
buf.write_fmt(path).ok();
|
||||
}
|
||||
inner(self.0, format_args!("/{}", name));
|
||||
pub fn join_fmt<T: Display>(mut self, name: T) -> Self {
|
||||
self.0.write_fmt(format_args!("/{}", name)).ok();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for FsPathBuf<'_> {
|
||||
impl<const N: usize> Deref for FsPathBuf<N> {
|
||||
type Target = FsPath;
|
||||
|
||||
fn deref(&self) -> &FsPath {
|
||||
FsPath::from(&self.0)
|
||||
FsPath::from(self.0.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FsPathBuf<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
FsPath::from_mut(&mut self.0)
|
||||
impl<const N: usize> DerefMut for FsPathBuf<N> {
|
||||
fn deref_mut(&mut self) -> &mut FsPath {
|
||||
FsPath::from_mut(self.0.deref_mut())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,13 +686,14 @@ macro_rules! impl_str {
|
||||
impl_str!(
|
||||
(Utf8CStr,)
|
||||
(FsPath,)
|
||||
(FsPathBuf<'_>,)
|
||||
(FsPathFollow,)
|
||||
(FsPathBuf<N>, const N: usize)
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
);
|
||||
|
||||
macro_rules! impl_str_write {
|
||||
macro_rules! impl_str_buf {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> Write for $t {
|
||||
#[inline(always)]
|
||||
@@ -610,20 +725,24 @@ macro_rules! impl_str_write {
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_str_write!(
|
||||
impl_str_buf!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
);
|
||||
|
||||
macro_rules! impl_str_buf {
|
||||
macro_rules! impl_str_buf_with_slice {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> Utf8CStrWrite for $t {
|
||||
impl<$($g)*> Utf8CStrBuf for $t {
|
||||
#[inline(always)]
|
||||
fn len(&self) -> usize {
|
||||
self.used
|
||||
}
|
||||
#[inline(always)]
|
||||
unsafe fn set_len(&mut self, len: usize) {
|
||||
self.used = len;
|
||||
}
|
||||
#[inline(always)]
|
||||
fn push_str(&mut self, s: &str) -> usize {
|
||||
utf8_cstr_buf_append(self, s.as_bytes())
|
||||
}
|
||||
@@ -632,6 +751,10 @@ macro_rules! impl_str_buf {
|
||||
utf8_cstr_append_lossy(self, s)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn capacity(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn clear(&mut self) {
|
||||
self.buf[0] = b'\0';
|
||||
self.used = 0;
|
||||
@@ -640,31 +763,32 @@ macro_rules! impl_str_buf {
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_str_buf!(
|
||||
impl_str_buf_with_slice!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
);
|
||||
|
||||
// The cstr! macro is copied from https://github.com/bytecodealliance/rustix/blob/main/src/cstr.rs
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cstr {
|
||||
($($str:tt)*) => {{
|
||||
assert!(
|
||||
!($($str)*).bytes().any(|b| b == b'\0'),
|
||||
"cstr argument contains embedded NUL bytes",
|
||||
);
|
||||
($str:expr) => {{
|
||||
const NULL_STR: &str = $crate::const_format::concatcp!($str, "\0");
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
$crate::Utf8CStr::from_bytes_unchecked($crate::const_format::concatcp!($($str)*, "\0")
|
||||
.as_bytes())
|
||||
$crate::Utf8CStr::from_bytes_unchecked(NULL_STR.as_bytes())
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! raw_cstr {
|
||||
($($str:tt)*) => {{
|
||||
$crate::cstr!($($str)*).as_ptr()
|
||||
($str:expr) => {{
|
||||
$crate::cstr!($str).as_ptr()
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! path {
|
||||
($str:expr) => {{
|
||||
$crate::FsPath::from($crate::cstr!($str))
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -9,47 +9,53 @@ use libc::{c_char, mode_t};
|
||||
use crate::files::map_file_at;
|
||||
pub(crate) use crate::xwrap::*;
|
||||
use crate::{
|
||||
clone_attr, cstr, fclone_attr, fd_path, map_fd, map_file, slice_from_ptr, CxxResultExt,
|
||||
Directory, FsPath, Utf8CStr, Utf8CStrBufRef,
|
||||
clone_attr, cstr, cstr_buf, fclone_attr, fd_path, map_fd, map_file, slice_from_ptr,
|
||||
CxxResultExt, Directory, FsPath, Utf8CStr,
|
||||
};
|
||||
|
||||
pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
|
||||
let mut buf = Utf8CStrBufRef::from(buf);
|
||||
let mut buf = cstr_buf::wrap(buf);
|
||||
fd_path(fd, &mut buf)
|
||||
.log_cxx_with_msg(|w| w.write_str("fd_path failed"))
|
||||
.map_or(-1_isize, |_| buf.len() as isize)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.realpath(&mut buf)
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = cstr_buf::wrap_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.realpath(&mut buf)
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[export_name = "mkdirs"]
|
||||
#[unsafe(export_name = "mkdirs")]
|
||||
unsafe extern "C" fn mkdirs_for_cxx(path: *const c_char, mode: mode_t) -> i32 {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p).mkdirs(mode).map_or(-1, |_| 0),
|
||||
Err(_) => -1,
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p).mkdirs(mode).map_or(-1, |_| 0),
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[export_name = "rm_rf"]
|
||||
#[unsafe(export_name = "rm_rf")]
|
||||
unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p).remove_all().is_ok(),
|
||||
Err(_) => false,
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p).remove_all().is_ok(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
|
||||
fn inner(fd: OwnedFd) -> io::Result<()> {
|
||||
Directory::try_from(fd)?.remove_all()
|
||||
@@ -83,110 +89,122 @@ pub(crate) unsafe fn readlinkat_for_cxx(
|
||||
buf: *mut u8,
|
||||
bufsz: usize,
|
||||
) -> isize {
|
||||
// readlinkat() may fail on x86 platform, returning random value
|
||||
// instead of number of bytes placed in buf (length of link)
|
||||
cfg_if! {
|
||||
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
|
||||
libc::memset(buf.cast(), 0, bufsz);
|
||||
let mut r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
|
||||
if r > 0 {
|
||||
r = libc::strlen(buf.cast()) as isize;
|
||||
}
|
||||
} else {
|
||||
let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
|
||||
if r >= 0 {
|
||||
*buf.offset(r) = b'\0';
|
||||
unsafe {
|
||||
// readlinkat() may fail on x86 platform, returning random value
|
||||
// instead of number of bytes placed in buf (length of link)
|
||||
cfg_if! {
|
||||
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
|
||||
libc::memset(buf.cast(), 0, bufsz);
|
||||
let mut r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
|
||||
if r > 0 {
|
||||
r = libc::strlen(buf.cast()) as isize;
|
||||
}
|
||||
} else {
|
||||
let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
|
||||
if r >= 0 {
|
||||
*buf.offset(r) = b'\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[export_name = "cp_afc"]
|
||||
#[unsafe(export_name = "cp_afc")]
|
||||
unsafe extern "C" fn cp_afc_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.copy_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("cp_afc {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.copy_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("cp_afc {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[export_name = "mv_path"]
|
||||
#[unsafe(export_name = "mv_path")]
|
||||
unsafe extern "C" fn mv_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.move_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("mv_path {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.move_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("mv_path {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[export_name = "link_path"]
|
||||
#[unsafe(export_name = "link_path")]
|
||||
unsafe extern "C" fn link_path_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.link_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("link_path {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return src
|
||||
.link_to(dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("link_path {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[export_name = "clone_attr"]
|
||||
#[unsafe(export_name = "clone_attr")]
|
||||
unsafe extern "C" fn clone_attr_for_cxx(src: *const c_char, dest: *const c_char) -> bool {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return clone_attr(src, dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("clone_attr {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
unsafe {
|
||||
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||
let src = FsPath::from(src);
|
||||
let dest = FsPath::from(dest);
|
||||
return clone_attr(src, dest)
|
||||
.log_cxx_with_msg(|w| {
|
||||
w.write_fmt(format_args!("clone_attr {} -> {} failed", src, dest))
|
||||
})
|
||||
.is_ok();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[export_name = "fclone_attr"]
|
||||
#[unsafe(export_name = "fclone_attr")]
|
||||
unsafe extern "C" fn fclone_attr_for_cxx(a: RawFd, b: RawFd) -> bool {
|
||||
fclone_attr(a, b)
|
||||
.log_cxx_with_msg(|w| w.write_str("fclone_attr failed"))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
#[export_name = "cxx$utf8str$new"]
|
||||
#[unsafe(export_name = "cxx$utf8str$new")]
|
||||
unsafe extern "C" fn str_new(this: &mut &Utf8CStr, s: *const u8, len: usize) {
|
||||
*this = Utf8CStr::from_bytes(slice_from_ptr(s, len)).unwrap_or(cstr!(""));
|
||||
unsafe {
|
||||
*this = Utf8CStr::from_bytes(slice_from_ptr(s, len)).unwrap_or(cstr!(""));
|
||||
}
|
||||
}
|
||||
|
||||
#[export_name = "cxx$utf8str$ptr"]
|
||||
#[unsafe(export_name = "cxx$utf8str$ptr")]
|
||||
unsafe extern "C" fn str_ptr(this: &&Utf8CStr) -> *const u8 {
|
||||
this.as_ptr().cast()
|
||||
}
|
||||
|
||||
#[export_name = "cxx$utf8str$len"]
|
||||
#[unsafe(export_name = "cxx$utf8str$len")]
|
||||
unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize {
|
||||
this.len()
|
||||
}
|
||||
|
||||
420
native/src/base/dir.rs
Normal file
420
native/src/base/dir.rs
Normal file
@@ -0,0 +1,420 @@
|
||||
use crate::cxx_extern::readlinkat_for_cxx;
|
||||
use crate::{
|
||||
cstr, cstr_buf, errno, fd_path, fd_set_attr, FileAttr, FsPath, LibcReturn, Utf8CStr,
|
||||
Utf8CStrBuf,
|
||||
};
|
||||
use libc::{dirent, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::ops::Deref;
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
use std::{io, mem, slice};
|
||||
|
||||
pub struct DirEntry<'a> {
|
||||
dir: &'a Directory,
|
||||
entry: &'a dirent,
|
||||
d_name_len: usize,
|
||||
}
|
||||
|
||||
impl DirEntry<'_> {
|
||||
pub fn name(&self) -> &CStr {
|
||||
unsafe {
|
||||
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
|
||||
self.d_name.as_ptr().cast(),
|
||||
self.d_name_len,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
self.dir.path(buf)?;
|
||||
buf.push_str("/");
|
||||
buf.push_lossy(self.name().to_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.d_type == libc::DT_DIR
|
||||
}
|
||||
|
||||
pub fn is_file(&self) -> bool {
|
||||
self.d_type == libc::DT_REG
|
||||
}
|
||||
|
||||
pub fn is_symlink(&self) -> bool {
|
||||
self.d_type == libc::DT_LNK
|
||||
}
|
||||
|
||||
pub fn is_block_device(&self) -> bool {
|
||||
self.d_type == libc::DT_BLK
|
||||
}
|
||||
|
||||
pub fn is_char_device(&self) -> bool {
|
||||
self.d_type == libc::DT_CHR
|
||||
}
|
||||
|
||||
pub fn is_fifo(&self) -> bool {
|
||||
self.d_type == libc::DT_FIFO
|
||||
}
|
||||
|
||||
pub fn is_socket(&self) -> bool {
|
||||
self.d_type == libc::DT_SOCK
|
||||
}
|
||||
|
||||
pub fn unlink(&self) -> io::Result<()> {
|
||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||
unsafe {
|
||||
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
buf.clear();
|
||||
unsafe {
|
||||
let r = readlinkat_for_cxx(
|
||||
self.dir.as_raw_fd(),
|
||||
self.d_name.as_ptr(),
|
||||
buf.as_mut_ptr().cast(),
|
||||
buf.capacity(),
|
||||
)
|
||||
.check_os_err()? as usize;
|
||||
buf.set_len(r);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn open_fd(&self, flags: i32) -> io::Result<RawFd> {
|
||||
unsafe { self.dir.open_raw_fd(self.name(), flags, 0) }
|
||||
}
|
||||
|
||||
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
||||
if !self.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
||||
}
|
||||
unsafe { Directory::try_from(OwnedFd::from_raw_fd(self.open_fd(O_RDONLY)?)) }
|
||||
}
|
||||
|
||||
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
||||
if self.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
||||
}
|
||||
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
|
||||
}
|
||||
|
||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||
let mut path = cstr_buf::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).get_attr()
|
||||
}
|
||||
|
||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||
let mut path = cstr_buf::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).set_attr(attr)
|
||||
}
|
||||
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
let mut path = cstr_buf::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).get_secontext(con)
|
||||
}
|
||||
|
||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||
let mut path = cstr_buf::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).set_secontext(con)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DirEntry<'_> {
|
||||
type Target = dirent;
|
||||
|
||||
fn deref(&self) -> &dirent {
|
||||
self.entry
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Directory {
|
||||
dirp: *mut libc::DIR,
|
||||
}
|
||||
|
||||
pub enum WalkResult {
|
||||
Continue,
|
||||
Abort,
|
||||
Skip,
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
pub fn open(path: &Utf8CStr) -> io::Result<Directory> {
|
||||
let dirp = unsafe { libc::opendir(path.as_ptr()) }.check_os_err()?;
|
||||
Ok(Directory { dirp })
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> io::Result<Option<DirEntry<'_>>> {
|
||||
*errno() = 0;
|
||||
let e = unsafe { libc::readdir(self.dirp) };
|
||||
if e.is_null() {
|
||||
return if *errno() != 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
// Skip both "." and ".."
|
||||
unsafe {
|
||||
let entry = &*e;
|
||||
let d_name = CStr::from_ptr(entry.d_name.as_ptr());
|
||||
if d_name == cstr!(".") || d_name == cstr!("..") {
|
||||
self.read()
|
||||
} else {
|
||||
let e = DirEntry {
|
||||
dir: self,
|
||||
entry,
|
||||
d_name_len: d_name.to_bytes_with_nul().len(),
|
||||
};
|
||||
Ok(Some(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self) {
|
||||
unsafe { libc::rewinddir(self.dirp) }
|
||||
}
|
||||
|
||||
unsafe fn open_raw_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
|
||||
unsafe {
|
||||
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_fd(&self, name: &Utf8CStr, flags: i32, mode: i32) -> io::Result<OwnedFd> {
|
||||
unsafe {
|
||||
self.open_raw_fd(name.as_cstr(), flags, mode)
|
||||
.map(|fd| OwnedFd::from_raw_fd(fd))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_path(&self, path: &CStr) -> bool {
|
||||
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
||||
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
||||
// Use fstatat to check the existence of a file instead.
|
||||
unsafe {
|
||||
let mut st: libc::stat = mem::zeroed();
|
||||
libc::fstatat(
|
||||
self.as_raw_fd(),
|
||||
path.as_ptr(),
|
||||
&mut st,
|
||||
libc::AT_SYMLINK_NOFOLLOW,
|
||||
) == 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
fd_path(self.as_raw_fd(), buf)
|
||||
}
|
||||
|
||||
pub fn post_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> io::Result<WalkResult> {
|
||||
self.post_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn pre_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> io::Result<WalkResult> {
|
||||
self.pre_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self) -> io::Result<()> {
|
||||
self.post_order_walk(|e| {
|
||||
e.unlink()?;
|
||||
Ok(WalkResult::Continue)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
while let Some(ref e) = self.read()? {
|
||||
let attr = e.get_attr()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
if e.is_dir() {
|
||||
unsafe {
|
||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||
}
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
src.copy_into(&dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else if e.is_file() {
|
||||
let mut src = e.open_as_file(O_RDONLY)?;
|
||||
let mut dest = unsafe {
|
||||
File::from_raw_fd(dir.open_raw_fd(
|
||||
e.name(),
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
0o777,
|
||||
)?)
|
||||
};
|
||||
std::io::copy(&mut src, &mut dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else if e.is_symlink() {
|
||||
let mut path = cstr_buf::default();
|
||||
e.read_link(&mut path)?;
|
||||
unsafe {
|
||||
libc::symlinkat(path.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
||||
.as_os_err()?;
|
||||
}
|
||||
new_entry.set_attr(&attr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
let dir_fd = self.as_raw_fd();
|
||||
while let Some(ref e) = self.read()? {
|
||||
if e.is_dir() && dir.contains_path(e.name()) {
|
||||
// Destination folder exists, needs recursive move
|
||||
let mut src = e.open_as_dir()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
src.move_into(&dest)?;
|
||||
return e.unlink();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
libc::renameat(
|
||||
dir_fd,
|
||||
e.d_name.as_ptr(),
|
||||
dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
)
|
||||
.as_os_err()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn link_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
let dir_fd = self.as_raw_fd();
|
||||
while let Some(ref e) = self.read()? {
|
||||
if e.is_dir() {
|
||||
unsafe {
|
||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||
}
|
||||
let attr = e.get_attr()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
src.link_into(&dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else {
|
||||
unsafe {
|
||||
libc::linkat(
|
||||
dir_fd,
|
||||
e.d_name.as_ptr(),
|
||||
dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
0,
|
||||
)
|
||||
.as_os_err()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
fn post_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> io::Result<WalkResult> {
|
||||
use WalkResult::*;
|
||||
loop {
|
||||
match self.read()? {
|
||||
None => return Ok(Continue),
|
||||
Some(ref e) => {
|
||||
if e.is_dir() {
|
||||
let mut dir = e.open_as_dir()?;
|
||||
if let Abort = dir.post_order_walk_impl(f)? {
|
||||
return Ok(Abort);
|
||||
}
|
||||
}
|
||||
match f(e)? {
|
||||
Abort => return Ok(Abort),
|
||||
Skip => return Ok(Continue),
|
||||
Continue => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> io::Result<WalkResult> {
|
||||
use WalkResult::*;
|
||||
loop {
|
||||
match self.read()? {
|
||||
None => return Ok(Continue),
|
||||
Some(ref e) => match f(e)? {
|
||||
Abort => return Ok(Abort),
|
||||
Skip => continue,
|
||||
Continue => {
|
||||
if e.is_dir() {
|
||||
let mut dir = e.open_as_dir()?;
|
||||
if let Abort = dir.pre_order_walk_impl(f)? {
|
||||
return Ok(Abort);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<OwnedFd> for Directory {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(fd: OwnedFd) -> io::Result<Self> {
|
||||
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) }.check_os_err()?;
|
||||
Ok(Directory { dirp })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Directory {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
unsafe { libc::dirfd(self.dirp) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsFd for Directory {
|
||||
fn as_fd(&self) -> BorrowedFd {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Directory {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
libc::closedir(self.dirp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::cxx_extern::readlinkat_for_cxx;
|
||||
use crate::{
|
||||
cstr, errno, error, FsPath, FsPathBuf, LibcReturn, Utf8CStr, Utf8CStrBuf, Utf8CStrBufArr,
|
||||
Utf8CStrWrite,
|
||||
cstr_buf, errno, error, Directory, FsPath, FsPathBuf, FsPathFollow, LibcReturn, Utf8CStr,
|
||||
Utf8CStrBuf,
|
||||
};
|
||||
use bytemuck::{bytes_of_mut, Pod};
|
||||
use bytemuck::{bytes_of, bytes_of_mut, Pod};
|
||||
use libc::{
|
||||
c_uint, dirent, makedev, mode_t, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY,
|
||||
c_uint, makedev, mode_t, stat, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY,
|
||||
O_RDWR, O_TRUNC, O_WRONLY,
|
||||
};
|
||||
use mem::MaybeUninit;
|
||||
@@ -14,13 +13,10 @@ use std::cmp::min;
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::Deref;
|
||||
use std::os::fd::{AsFd, BorrowedFd, IntoRawFd};
|
||||
use std::os::fd::{AsFd, BorrowedFd};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::{io, mem, ptr, slice};
|
||||
|
||||
pub fn __open_fd_impl(path: &Utf8CStr, flags: i32, mode: mode_t) -> io::Result<OwnedFd> {
|
||||
@@ -41,8 +37,7 @@ macro_rules! open_fd {
|
||||
}
|
||||
|
||||
pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
let mut arr = Utf8CStrBufArr::<40>::new();
|
||||
let path = FsPathBuf::new(&mut arr).join("/proc/self/fd").join_fmt(fd);
|
||||
let path = FsPathBuf::default().join("/proc/self/fd").join_fmt(fd);
|
||||
path.read_link(buf)
|
||||
}
|
||||
|
||||
@@ -123,6 +118,7 @@ impl<T: BufRead> BufReadExt for T {
|
||||
|
||||
pub trait WriteExt {
|
||||
fn write_zeros(&mut self, len: usize) -> io::Result<()>;
|
||||
fn write_pod<F: Pod>(&mut self, data: &F) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: Write> WriteExt for T {
|
||||
@@ -135,12 +131,16 @@ impl<T: Write> WriteExt for T {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_pod<F: Pod>(&mut self, data: &F) -> io::Result<()> {
|
||||
self.write_all(bytes_of(data))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FileAttr {
|
||||
pub st: libc::stat,
|
||||
#[cfg(feature = "selinux")]
|
||||
pub con: Utf8CStrBufArr<128>,
|
||||
pub con: crate::Utf8CStrBufArr<128>,
|
||||
}
|
||||
|
||||
impl FileAttr {
|
||||
@@ -148,7 +148,7 @@ impl FileAttr {
|
||||
FileAttr {
|
||||
st: unsafe { mem::zeroed() },
|
||||
#[cfg(feature = "selinux")]
|
||||
con: Utf8CStrBufArr::new(),
|
||||
con: crate::Utf8CStrBufArr::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,398 +187,13 @@ impl FileAttr {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
const XATTR_NAME_SELINUX: &[u8] = b"security.selinux\0";
|
||||
|
||||
pub struct DirEntry<'a> {
|
||||
dir: &'a Directory,
|
||||
entry: &'a dirent,
|
||||
d_name_len: usize,
|
||||
}
|
||||
|
||||
impl DirEntry<'_> {
|
||||
pub fn d_name(&self) -> &CStr {
|
||||
unsafe {
|
||||
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
|
||||
self.d_name.as_ptr().cast(),
|
||||
self.d_name_len,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
self.dir.path(buf)?;
|
||||
buf.push_str("/");
|
||||
buf.push_lossy(self.d_name().to_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.d_type == libc::DT_DIR
|
||||
}
|
||||
|
||||
pub fn is_file(&self) -> bool {
|
||||
self.d_type == libc::DT_REG
|
||||
}
|
||||
|
||||
pub fn is_symlink(&self) -> bool {
|
||||
self.d_type == libc::DT_LNK
|
||||
}
|
||||
|
||||
pub fn is_block_device(&self) -> bool {
|
||||
self.d_type == libc::DT_BLK
|
||||
}
|
||||
|
||||
pub fn is_char_device(&self) -> bool {
|
||||
self.d_type == libc::DT_CHR
|
||||
}
|
||||
|
||||
pub fn is_fifo(&self) -> bool {
|
||||
self.d_type == libc::DT_FIFO
|
||||
}
|
||||
|
||||
pub fn is_socket(&self) -> bool {
|
||||
self.d_type == libc::DT_SOCK
|
||||
}
|
||||
|
||||
pub fn unlink(&self) -> io::Result<()> {
|
||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||
unsafe {
|
||||
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
buf.clear();
|
||||
unsafe {
|
||||
let r = readlinkat_for_cxx(
|
||||
self.dir.as_raw_fd(),
|
||||
self.d_name.as_ptr(),
|
||||
buf.as_mut_ptr().cast(),
|
||||
buf.capacity(),
|
||||
)
|
||||
.check_os_err()? as usize;
|
||||
buf.set_len(r);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn open_fd(&self, flags: i32) -> io::Result<RawFd> {
|
||||
self.dir.open_fd(self.d_name(), flags, 0)
|
||||
}
|
||||
|
||||
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
||||
if !self.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
||||
}
|
||||
unsafe { Directory::try_from(OwnedFd::from_raw_fd(self.open_fd(O_RDONLY)?)) }
|
||||
}
|
||||
|
||||
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
||||
if self.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
||||
}
|
||||
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
|
||||
}
|
||||
|
||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||
let mut path = Utf8CStrBufArr::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).get_attr()
|
||||
}
|
||||
|
||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||
let mut path = Utf8CStrBufArr::default();
|
||||
self.path(&mut path)?;
|
||||
FsPath::from(&path).set_attr(attr)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DirEntry<'_> {
|
||||
type Target = dirent;
|
||||
|
||||
fn deref(&self) -> &dirent {
|
||||
self.entry
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Directory {
|
||||
dirp: *mut libc::DIR,
|
||||
}
|
||||
|
||||
pub enum WalkResult {
|
||||
Continue,
|
||||
Abort,
|
||||
Skip,
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
pub fn open(path: &Utf8CStr) -> io::Result<Directory> {
|
||||
let dirp = unsafe { libc::opendir(path.as_ptr()) }.check_os_err()?;
|
||||
Ok(Directory { dirp })
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> io::Result<Option<DirEntry<'_>>> {
|
||||
*errno() = 0;
|
||||
let e = unsafe { libc::readdir(self.dirp) };
|
||||
if e.is_null() {
|
||||
return if *errno() != 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
// Skip both "." and ".."
|
||||
unsafe {
|
||||
let entry = &*e;
|
||||
let d_name = CStr::from_ptr(entry.d_name.as_ptr());
|
||||
if d_name == cstr!(".") || d_name == cstr!("..") {
|
||||
self.read()
|
||||
} else {
|
||||
let e = DirEntry {
|
||||
dir: self,
|
||||
entry,
|
||||
d_name_len: d_name.to_bytes_with_nul().len(),
|
||||
};
|
||||
Ok(Some(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self) {
|
||||
unsafe { libc::rewinddir(self.dirp) }
|
||||
}
|
||||
|
||||
unsafe fn open_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
|
||||
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
|
||||
}
|
||||
|
||||
pub fn contains_path(&self, path: &CStr) -> bool {
|
||||
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
||||
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
||||
// Use fstatat to check the existence of a file instead.
|
||||
unsafe {
|
||||
let mut st: libc::stat = mem::zeroed();
|
||||
libc::fstatat(
|
||||
self.as_raw_fd(),
|
||||
path.as_ptr(),
|
||||
&mut st,
|
||||
libc::AT_SYMLINK_NOFOLLOW,
|
||||
) == 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
fd_path(self.as_raw_fd(), buf)
|
||||
}
|
||||
|
||||
pub fn post_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> io::Result<WalkResult> {
|
||||
self.post_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn pre_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> io::Result<WalkResult> {
|
||||
self.pre_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self) -> io::Result<()> {
|
||||
self.post_order_walk(|e| {
|
||||
e.unlink()?;
|
||||
Ok(WalkResult::Continue)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
while let Some(ref e) = self.read()? {
|
||||
let attr = e.get_attr()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
if e.is_dir() {
|
||||
unsafe {
|
||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||
}
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
src.copy_into(&dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else if e.is_file() {
|
||||
let mut src = e.open_as_file(O_RDONLY)?;
|
||||
let mut dest = unsafe {
|
||||
File::from_raw_fd(dir.open_fd(
|
||||
e.d_name(),
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
0o777,
|
||||
)?)
|
||||
};
|
||||
std::io::copy(&mut src, &mut dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else if e.is_symlink() {
|
||||
let mut path = Utf8CStrBufArr::default();
|
||||
e.read_link(&mut path)?;
|
||||
unsafe {
|
||||
libc::symlinkat(path.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
||||
.as_os_err()?;
|
||||
}
|
||||
new_entry.set_attr(&attr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
let dir_fd = self.as_raw_fd();
|
||||
while let Some(ref e) = self.read()? {
|
||||
if e.is_dir() && dir.contains_path(e.d_name()) {
|
||||
// Destination folder exists, needs recursive move
|
||||
let mut src = e.open_as_dir()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
src.move_into(&dest)?;
|
||||
return e.unlink();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
libc::renameat(
|
||||
dir_fd,
|
||||
e.d_name.as_ptr(),
|
||||
dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
)
|
||||
.as_os_err()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn link_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||
let dir_fd = self.as_raw_fd();
|
||||
while let Some(ref e) = self.read()? {
|
||||
if e.is_dir() {
|
||||
unsafe {
|
||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||
}
|
||||
let attr = e.get_attr()?;
|
||||
let new_entry = DirEntry {
|
||||
dir,
|
||||
entry: e.entry,
|
||||
d_name_len: e.d_name_len,
|
||||
};
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = new_entry.open_as_dir()?;
|
||||
src.link_into(&dest)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else {
|
||||
unsafe {
|
||||
libc::linkat(
|
||||
dir_fd,
|
||||
e.d_name.as_ptr(),
|
||||
dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
0,
|
||||
)
|
||||
.as_os_err()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
fn post_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> io::Result<WalkResult> {
|
||||
use WalkResult::*;
|
||||
loop {
|
||||
match self.read()? {
|
||||
None => return Ok(Continue),
|
||||
Some(ref e) => {
|
||||
if e.is_dir() {
|
||||
let mut dir = e.open_as_dir()?;
|
||||
if let Abort = dir.post_order_walk_impl(f)? {
|
||||
return Ok(Abort);
|
||||
}
|
||||
}
|
||||
match f(e)? {
|
||||
Abort => return Ok(Abort),
|
||||
Skip => return Ok(Continue),
|
||||
Continue => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> io::Result<WalkResult> {
|
||||
use WalkResult::*;
|
||||
loop {
|
||||
match self.read()? {
|
||||
None => return Ok(Continue),
|
||||
Some(ref e) => match f(e)? {
|
||||
Abort => return Ok(Abort),
|
||||
Skip => continue,
|
||||
Continue => {
|
||||
if e.is_dir() {
|
||||
let mut dir = e.open_as_dir()?;
|
||||
if let Abort = dir.pre_order_walk_impl(f)? {
|
||||
return Ok(Abort);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<OwnedFd> for Directory {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(fd: OwnedFd) -> io::Result<Self> {
|
||||
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) }.check_os_err()?;
|
||||
Ok(Directory { dirp })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Directory {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
unsafe { libc::dirfd(self.dirp) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsFd for Directory {
|
||||
fn as_fd(&self) -> BorrowedFd {
|
||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Directory {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
libc::closedir(self.dirp);
|
||||
}
|
||||
}
|
||||
}
|
||||
const XATTR_NAME_SELINUX: &CStr = c"security.selinux";
|
||||
|
||||
impl FsPath {
|
||||
pub fn follow_link(&self) -> &FsPathFollow {
|
||||
unsafe { mem::transmute(self) }
|
||||
}
|
||||
|
||||
pub fn open(&self, flags: i32) -> io::Result<File> {
|
||||
Ok(File::from(open_fd!(self, flags)?))
|
||||
}
|
||||
@@ -588,7 +203,10 @@ impl FsPath {
|
||||
}
|
||||
|
||||
pub fn exists(&self) -> bool {
|
||||
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
||||
unsafe {
|
||||
let mut st: stat = mem::zeroed();
|
||||
libc::lstat(self.as_ptr(), &mut st) == 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rename_to<T: AsRef<Utf8CStr>>(&self, name: T) -> io::Result<()> {
|
||||
@@ -608,13 +226,14 @@ impl FsPath {
|
||||
self.remove()
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
buf.clear();
|
||||
unsafe {
|
||||
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr().cast(), buf.capacity() - 1)
|
||||
.check_os_err()? as usize;
|
||||
*buf.mut_buf().get_unchecked_mut(r) = b'\0';
|
||||
buf.set_len(r);
|
||||
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1)
|
||||
.check_os_err()? as isize;
|
||||
*(buf.as_mut_ptr().offset(r) as *mut u8) = b'\0';
|
||||
buf.set_len(r as usize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -636,7 +255,7 @@ impl FsPath {
|
||||
if self.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut arr = Utf8CStrBufArr::default();
|
||||
let mut arr = cstr_buf::default();
|
||||
arr.push_str(self);
|
||||
let mut off = 1;
|
||||
unsafe {
|
||||
@@ -688,16 +307,7 @@ impl FsPath {
|
||||
libc::lstat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
{
|
||||
let sz = libc::lgetxattr(
|
||||
self.as_ptr(),
|
||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
||||
attr.con.as_mut_ptr().cast(),
|
||||
attr.con.capacity(),
|
||||
)
|
||||
.check_os_err()?;
|
||||
attr.con.set_len((sz - 1) as usize);
|
||||
}
|
||||
self.get_secontext(&mut attr.con)?;
|
||||
}
|
||||
Ok(attr)
|
||||
}
|
||||
@@ -711,19 +321,45 @@ impl FsPath {
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
if !attr.con.is_empty() {
|
||||
libc::lsetxattr(
|
||||
self.as_ptr(),
|
||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
||||
attr.con.as_ptr().cast(),
|
||||
attr.con.len() + 1,
|
||||
0,
|
||||
)
|
||||
.as_os_err()?;
|
||||
self.set_secontext(&attr.con)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
unsafe {
|
||||
let sz = libc::lgetxattr(
|
||||
self.as_ptr(),
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
con.as_mut_ptr().cast(),
|
||||
con.capacity(),
|
||||
);
|
||||
if sz < 1 {
|
||||
con.clear();
|
||||
if *errno() != libc::ENODATA {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
} else {
|
||||
con.set_len((sz - 1) as usize);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||
unsafe {
|
||||
libc::lsetxattr(
|
||||
self.as_ptr(),
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
con.as_ptr().cast(),
|
||||
con.len() + 1,
|
||||
0,
|
||||
)
|
||||
.as_os_err()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_to(&self, path: &FsPath) -> io::Result<()> {
|
||||
let attr = self.get_attr()?;
|
||||
if attr.is_dir() {
|
||||
@@ -739,7 +375,7 @@ impl FsPath {
|
||||
let mut dest = path.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o777)?;
|
||||
std::io::copy(&mut src, &mut dest)?;
|
||||
} else if attr.is_symlink() {
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let mut buf = cstr_buf::default();
|
||||
self.read_link(&mut buf)?;
|
||||
unsafe {
|
||||
libc::symlink(buf.as_ptr(), path.as_ptr()).as_os_err()?;
|
||||
@@ -781,7 +417,7 @@ impl FsPath {
|
||||
unsafe { libc::symlink(self.as_ptr(), path.as_ptr()).as_os_err() }
|
||||
}
|
||||
|
||||
pub fn parent(&self, buf: &mut dyn Utf8CStrWrite) -> bool {
|
||||
pub fn parent(&self, buf: &mut dyn Utf8CStrBuf) -> bool {
|
||||
buf.clear();
|
||||
if let Some(parent) = Path::new(self.as_str()).parent() {
|
||||
let bytes = parent.as_os_str().as_bytes();
|
||||
@@ -795,6 +431,69 @@ impl FsPath {
|
||||
}
|
||||
}
|
||||
|
||||
impl FsPathFollow {
|
||||
pub fn exists(&self) -> bool {
|
||||
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
||||
}
|
||||
|
||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||
let mut attr = FileAttr::new();
|
||||
unsafe {
|
||||
libc::stat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
self.get_secontext(&mut attr.con)?;
|
||||
}
|
||||
Ok(attr)
|
||||
}
|
||||
|
||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||
unsafe {
|
||||
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
||||
libc::chown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).as_os_err()?;
|
||||
|
||||
#[cfg(feature = "selinux")]
|
||||
if !attr.con.is_empty() {
|
||||
self.set_secontext(&attr.con)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
unsafe {
|
||||
let sz = libc::getxattr(
|
||||
self.as_ptr(),
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
con.as_mut_ptr().cast(),
|
||||
con.capacity(),
|
||||
);
|
||||
if sz < 1 {
|
||||
con.clear();
|
||||
if *errno() != libc::ENODATA {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
} else {
|
||||
con.set_len((sz - 1) as usize);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||
unsafe {
|
||||
libc::setxattr(
|
||||
self.as_ptr(),
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
con.as_ptr().cast(),
|
||||
con.len() + 1,
|
||||
0,
|
||||
)
|
||||
.as_os_err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
||||
let mut attr = FileAttr::new();
|
||||
unsafe {
|
||||
@@ -804,12 +503,17 @@ pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
||||
{
|
||||
let sz = libc::fgetxattr(
|
||||
fd,
|
||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
attr.con.as_mut_ptr().cast(),
|
||||
attr.con.capacity(),
|
||||
)
|
||||
.check_os_err()?;
|
||||
attr.con.set_len((sz - 1) as usize);
|
||||
);
|
||||
if sz < 1 {
|
||||
if *errno() != libc::ENODATA {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
} else {
|
||||
attr.con.set_len((sz - 1) as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(attr)
|
||||
@@ -824,7 +528,7 @@ pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> io::Result<()> {
|
||||
if !attr.con.is_empty() {
|
||||
libc::fsetxattr(
|
||||
fd,
|
||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
||||
XATTR_NAME_SELINUX.as_ptr(),
|
||||
attr.con.as_ptr().cast(),
|
||||
attr.con.len() + 1,
|
||||
0,
|
||||
@@ -889,7 +593,7 @@ impl Drop for MappedFile {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
// Don't use the declaration from the libc crate as request should be u32 not i32
|
||||
fn ioctl(fd: RawFd, request: u32, ...) -> i32;
|
||||
}
|
||||
@@ -1030,32 +734,3 @@ pub fn parse_mount_info(pid: &str) -> Vec<MountInfo> {
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub enum SharedFd {
|
||||
#[default]
|
||||
None,
|
||||
Shared(Arc<OwnedFd>),
|
||||
}
|
||||
|
||||
impl From<OwnedFd> for SharedFd {
|
||||
fn from(fd: OwnedFd) -> Self {
|
||||
SharedFd::Shared(Arc::new(fd))
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedFd {
|
||||
pub const fn new() -> Self {
|
||||
SharedFd::None
|
||||
}
|
||||
|
||||
// This is unsafe because we cannot create multiple mutable references to the same fd.
|
||||
// This can only be safely used if and only if the underlying fd points to a pipe,
|
||||
// and the read/write operations performed on the file involves bytes less than PIPE_BUF.
|
||||
pub unsafe fn as_file(&self) -> Option<ManuallyDrop<File>> {
|
||||
match self {
|
||||
SharedFd::None => None,
|
||||
SharedFd::Shared(arc) => Some(ManuallyDrop::new(File::from_raw_fd(arc.as_raw_fd()))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
#include "../base-rs.hpp"
|
||||
|
||||
using rust::xpipe2;
|
||||
using rust::fd_path;
|
||||
using rust::fd_path;
|
||||
using kv_pairs = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
@@ -8,6 +8,8 @@ use num_traits::FromPrimitive;
|
||||
|
||||
pub use cstr::*;
|
||||
use cxx_extern::*;
|
||||
pub use dir::*;
|
||||
pub use ffi::fork_dont_care;
|
||||
pub use files::*;
|
||||
pub use logging::*;
|
||||
pub use misc::*;
|
||||
@@ -15,6 +17,7 @@ pub use result::*;
|
||||
|
||||
mod cstr;
|
||||
mod cxx_extern;
|
||||
mod dir;
|
||||
mod files;
|
||||
mod logging;
|
||||
mod misc;
|
||||
@@ -42,6 +45,7 @@ pub mod ffi {
|
||||
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
|
||||
|
||||
fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>;
|
||||
fn fork_dont_care() -> i32;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
|
||||
@@ -7,7 +7,7 @@ use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::ffi::LogLevelCxx;
|
||||
use crate::{Utf8CStr, Utf8CStrBufArr};
|
||||
use crate::{cstr_buf, Utf8CStr};
|
||||
|
||||
// Ugly hack to avoid using enum
|
||||
#[allow(non_snake_case, non_upper_case_globals)]
|
||||
@@ -84,7 +84,7 @@ fn log_with_writer<F: FnOnce(LogWriter)>(level: LogLevel, f: F) {
|
||||
}
|
||||
f(logger.write);
|
||||
if matches!(level, LogLevel::ErrorCxx) && (logger.flags & LogFlag::ExitOnError) != 0 {
|
||||
exit(1);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ pub fn log_from_cxx(level: LogLevelCxx, msg: &Utf8CStr) {
|
||||
|
||||
pub fn log_with_formatter<F: FnOnce(Formatter) -> fmt::Result>(level: LogLevel, f: F) {
|
||||
log_with_writer(level, |write| {
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let mut buf = cstr_buf::default();
|
||||
f(&mut buf).ok();
|
||||
write(level, &buf);
|
||||
});
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::fmt::Arguments;
|
||||
use std::io::Write;
|
||||
use std::process::exit;
|
||||
use std::{fmt, io, slice, str};
|
||||
|
||||
use crate::{ffi, StrErr, Utf8CStr};
|
||||
use argh::EarlyExit;
|
||||
use libc::c_char;
|
||||
|
||||
use crate::{ffi, StrErr, Utf8CStr};
|
||||
use std::fmt::Arguments;
|
||||
use std::io::Write;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::process::exit;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, io, slice, str};
|
||||
|
||||
pub fn errno() -> &'static mut i32 {
|
||||
unsafe { &mut *libc::__errno() }
|
||||
@@ -15,20 +16,24 @@ pub fn errno() -> &'static mut i32 {
|
||||
// When len is 0, don't care whether buf is null or not
|
||||
#[inline]
|
||||
pub unsafe fn slice_from_ptr<'a, T>(buf: *const T, len: usize) -> &'a [T] {
|
||||
if len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
slice::from_raw_parts(buf, len)
|
||||
unsafe {
|
||||
if len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
slice::from_raw_parts(buf, len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When len is 0, don't care whether buf is null or not
|
||||
#[inline]
|
||||
pub unsafe fn slice_from_ptr_mut<'a, T>(buf: *mut T, len: usize) -> &'a mut [T] {
|
||||
if len == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
slice::from_raw_parts_mut(buf, len)
|
||||
unsafe {
|
||||
if len == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
slice::from_raw_parts_mut(buf, len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,3 +166,52 @@ impl<T: Write> fmt::Write for FmtAdaptor<'_, T> {
|
||||
self.0.write_fmt(args).map_err(|_| fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AtomicArc<T> {
|
||||
ptr: AtomicPtr<T>,
|
||||
}
|
||||
|
||||
impl<T> AtomicArc<T> {
|
||||
pub fn new(arc: Arc<T>) -> AtomicArc<T> {
|
||||
let raw = Arc::into_raw(arc);
|
||||
Self {
|
||||
ptr: AtomicPtr::new(raw as *mut _),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(&self) -> Arc<T> {
|
||||
let raw = self.ptr.load(Ordering::Acquire);
|
||||
// SAFETY: the raw pointer is always created from Arc::into_raw
|
||||
let arc = ManuallyDrop::new(unsafe { Arc::from_raw(raw) });
|
||||
ManuallyDrop::into_inner(arc.clone())
|
||||
}
|
||||
|
||||
fn swap_ptr(&self, raw: *const T) -> Arc<T> {
|
||||
let prev = self.ptr.swap(raw as *mut _, Ordering::AcqRel);
|
||||
// SAFETY: the raw pointer is always created from Arc::into_raw
|
||||
unsafe { Arc::from_raw(prev) }
|
||||
}
|
||||
|
||||
pub fn swap(&self, arc: Arc<T>) -> Arc<T> {
|
||||
let raw = Arc::into_raw(arc);
|
||||
self.swap_ptr(raw)
|
||||
}
|
||||
|
||||
pub fn store(&self, arc: Arc<T>) {
|
||||
// Drop the previous value
|
||||
let _ = self.swap(arc);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for AtomicArc<T> {
|
||||
fn drop(&mut self) {
|
||||
// Drop the internal value
|
||||
let _ = self.swap_ptr(std::ptr::null());
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Default for AtomicArc<T> {
|
||||
fn default() -> Self {
|
||||
Self::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ impl<T> SilentResultExt<T> for Option<T> {
|
||||
pub trait ResultExt<T> {
|
||||
fn log(self) -> LoggedResult<T>;
|
||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
|
||||
fn log_ok(self);
|
||||
}
|
||||
|
||||
// Internal C++ bridging logging routines
|
||||
@@ -105,6 +106,17 @@ impl<T, R: Loggable<T>> ResultExt<T> for R {
|
||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
self.do_log_msg(LogLevel::Error, Some(Location::caller()), f)
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn log_ok(self) {
|
||||
self.log().ok();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
#[cfg(debug_assertions)]
|
||||
fn log_ok(self) {
|
||||
self.do_log(LogLevel::Error, Some(Location::caller())).ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Loggable<T> for LoggedResult<T> {
|
||||
|
||||
@@ -15,7 +15,6 @@ int xopenat(int dirfd, const char *pathname, int flags, mode_t mode = 0);
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count);
|
||||
ssize_t xread(int fd, void *buf, size_t count);
|
||||
ssize_t xxread(int fd, void *buf, size_t count);
|
||||
off64_t xlseek64(int fd, off64_t offset, int whence);
|
||||
int xsetns(int fd, int nstype);
|
||||
int xunshare(int flags);
|
||||
DIR *xopendir(const char *name);
|
||||
@@ -26,33 +25,22 @@ int xsocket(int domain, int type, int protocol);
|
||||
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int xlisten(int sockfd, int backlog);
|
||||
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
|
||||
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags);
|
||||
ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags);
|
||||
int xaccess(const char *path, int mode);
|
||||
int xfaccessat(int dirfd, const char *pathname, int mode, int flags);
|
||||
int xstat(const char *pathname, struct stat *buf);
|
||||
int xlstat(const char *pathname, struct stat *buf);
|
||||
int xfstat(int fd, struct stat *buf);
|
||||
int xfstatat(int dirfd, const char *pathname, struct stat *buf, int flags);
|
||||
int xdup(int fd);
|
||||
int xdup2(int oldfd, int newfd);
|
||||
int xdup3(int oldfd, int newfd, int flags);
|
||||
ssize_t xreadlink(const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
||||
ssize_t xreadlinkat(
|
||||
int dirfd, const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
||||
int xsymlink(const char *target, const char *linkpath);
|
||||
int xsymlinkat(const char *target, int newdirfd, const char *linkpath);
|
||||
int xlinkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags);
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data);
|
||||
int xumount(const char *target);
|
||||
int xumount2(const char *target, int flags);
|
||||
int xrename(const char *oldpath, const char *newpath);
|
||||
int xmkdir(const char *pathname, mode_t mode);
|
||||
int xmkdirs(const char *pathname, mode_t mode);
|
||||
int xmkdirat(int dirfd, const char *pathname, mode_t mode);
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||
pid_t xfork();
|
||||
int xpoll(pollfd *fds, nfds_t nfds, int timeout);
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
|
||||
use libc::{
|
||||
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
|
||||
ssize_t, SYS_dup3,
|
||||
ssize_t,
|
||||
};
|
||||
|
||||
use crate::cxx_extern::readlinkat_for_cxx;
|
||||
use crate::{errno, raw_cstr, CxxResultExt, FsPath, Utf8CStr, Utf8CStrBufRef};
|
||||
use crate::{CxxResultExt, FsPath, Utf8CStr, Utf8CStrBufRef, errno, raw_cstr};
|
||||
|
||||
fn ptr_to_str<'a, T>(ptr: *const T) -> &'a str {
|
||||
if ptr.is_null() {
|
||||
@@ -50,93 +49,107 @@ mod c_export {
|
||||
|
||||
use crate::{slice_from_ptr, slice_from_ptr_mut};
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
||||
super::xwrite(fd, slice_from_ptr(buf, bufsz))
|
||||
unsafe { super::xwrite(fd, slice_from_ptr(buf, bufsz)) }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xxread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize {
|
||||
super::xxread(fd, slice_from_ptr_mut(buf, bufsz))
|
||||
unsafe { super::xxread(fd, slice_from_ptr_mut(buf, bufsz)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.realpath(&mut buf)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("realpath {} failed", p)))
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.realpath(&mut buf)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("realpath {} failed", p)))
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.read_link(&mut buf)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("readlink {} failed", p)))
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => {
|
||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||
FsPath::from(p)
|
||||
.read_link(&mut buf)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("readlink {} failed", p)))
|
||||
.map_or(-1, |_| buf.len() as isize)
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xreadlinkat(
|
||||
dirfd: RawFd,
|
||||
path: *const c_char,
|
||||
buf: *mut u8,
|
||||
bufsz: usize,
|
||||
) -> isize {
|
||||
let r = readlinkat_for_cxx(dirfd, path, buf, bufsz);
|
||||
if r < 0 {
|
||||
perror!("readlinkat {}", ptr_to_str(path))
|
||||
unsafe {
|
||||
let r = readlinkat_for_cxx(dirfd, path, buf, bufsz);
|
||||
if r < 0 {
|
||||
perror!("readlinkat {}", ptr_to_str(path))
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfopen(path: *const c_char, mode: *const c_char) -> *mut libc::FILE {
|
||||
let fp = libc::fopen(path, mode);
|
||||
if fp.is_null() {
|
||||
perror!("fopen {}", ptr_to_str(path));
|
||||
unsafe {
|
||||
let fp = libc::fopen(path, mode);
|
||||
if fp.is_null() {
|
||||
perror!("fopen {}", ptr_to_str(path));
|
||||
}
|
||||
fp
|
||||
}
|
||||
fp
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfdopen(fd: RawFd, mode: *const c_char) -> *mut libc::FILE {
|
||||
let fp = libc::fdopen(fd, mode);
|
||||
if fp.is_null() {
|
||||
perror!("fdopen");
|
||||
unsafe {
|
||||
let fp = libc::fdopen(fd, mode);
|
||||
if fp.is_null() {
|
||||
perror!("fdopen");
|
||||
}
|
||||
fp
|
||||
}
|
||||
fp
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xopen(path: *const c_char, flags: i32, mode: mode_t) -> RawFd {
|
||||
let r = libc::open(path, flags, mode as c_uint);
|
||||
if r < 0 {
|
||||
perror!("open {}", ptr_to_str(path));
|
||||
unsafe {
|
||||
let r = libc::open(path, flags, mode as c_uint);
|
||||
if r < 0 {
|
||||
perror!("open {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xopenat(dirfd: RawFd, path: *const c_char, flags: i32, mode: mode_t) -> RawFd {
|
||||
let r = libc::openat(dirfd, path, flags, mode as c_uint);
|
||||
if r < 0 {
|
||||
perror!("openat {}", ptr_to_str(path));
|
||||
unsafe {
|
||||
let r = libc::openat(dirfd, path, flags, mode as c_uint);
|
||||
if r < 0 {
|
||||
perror!("openat {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
// Fully write data slice
|
||||
@@ -168,7 +181,7 @@ fn xwrite(fd: RawFd, data: &[u8]) -> isize {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xread(fd: RawFd, buf: *mut c_void, bufsz: usize) -> isize {
|
||||
unsafe {
|
||||
let r = libc::read(fd, buf, bufsz);
|
||||
@@ -208,17 +221,6 @@ fn xxread(fd: RawFd, data: &mut [u8]) -> isize {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn xlseek64(fd: RawFd, offset: i64, whence: i32) -> i64 {
|
||||
unsafe {
|
||||
let r = libc::lseek64(fd, offset, whence);
|
||||
if r < 0 {
|
||||
perror!("lseek64");
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::pipe2(fds.as_mut_ptr(), flags);
|
||||
@@ -229,7 +231,7 @@ pub(crate) fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsetns(fd: RawFd, nstype: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::setns(fd, nstype);
|
||||
@@ -240,7 +242,7 @@ extern "C" fn xsetns(fd: RawFd, nstype: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xunshare(flags: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::unshare(flags);
|
||||
@@ -251,16 +253,18 @@ extern "C" fn xunshare(flags: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xopendir(path: *const c_char) -> *mut libc::DIR {
|
||||
let dp = libc::opendir(path);
|
||||
if dp.is_null() {
|
||||
perror!("opendir {}", ptr_to_str(path));
|
||||
unsafe {
|
||||
let dp = libc::opendir(path);
|
||||
if dp.is_null() {
|
||||
perror!("opendir {}", ptr_to_str(path));
|
||||
}
|
||||
dp
|
||||
}
|
||||
dp
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR {
|
||||
unsafe {
|
||||
let dp = libc::fdopendir(fd);
|
||||
@@ -271,27 +275,29 @@ extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xreaddir(dirp: *mut libc::DIR) -> *mut libc::dirent {
|
||||
*errno() = 0;
|
||||
loop {
|
||||
let e = libc::readdir(dirp);
|
||||
if e.is_null() {
|
||||
if *errno() != 0 {
|
||||
perror!("readdir")
|
||||
}
|
||||
} else {
|
||||
// Filter out . and ..
|
||||
let s = (*e).d_name.as_ptr();
|
||||
if libc::strcmp(s, raw_cstr!(".")) == 0 || libc::strcmp(s, raw_cstr!("..")) == 0 {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
return e;
|
||||
unsafe {
|
||||
*errno() = 0;
|
||||
loop {
|
||||
let e = libc::readdir(dirp);
|
||||
if e.is_null() {
|
||||
if *errno() != 0 {
|
||||
perror!("readdir")
|
||||
}
|
||||
} else {
|
||||
// Filter out . and ..
|
||||
let s = (*e).d_name.as_ptr();
|
||||
if libc::strcmp(s, raw_cstr!(".")) == 0 || libc::strcmp(s, raw_cstr!("..")) == 0 {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsetsid() -> i32 {
|
||||
unsafe {
|
||||
let r = libc::setsid();
|
||||
@@ -302,7 +308,7 @@ extern "C" fn xsetsid() -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsocket(domain: i32, ty: i32, protocol: i32) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::socket(domain, ty, protocol);
|
||||
@@ -313,16 +319,18 @@ extern "C" fn xsocket(domain: i32, ty: i32, protocol: i32) -> RawFd {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xbind(socket: i32, address: *const sockaddr, len: socklen_t) -> i32 {
|
||||
let r = libc::bind(socket, address, len);
|
||||
if r < 0 {
|
||||
perror!("bind");
|
||||
unsafe {
|
||||
let r = libc::bind(socket, address, len);
|
||||
if r < 0 {
|
||||
perror!("bind");
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xlisten(socket: i32, backlog: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::listen(socket, backlog);
|
||||
@@ -333,103 +341,56 @@ extern "C" fn xlisten(socket: i32, backlog: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xaccept4(
|
||||
sockfd: RawFd,
|
||||
addr: *mut sockaddr,
|
||||
len: *mut socklen_t,
|
||||
flg: i32,
|
||||
) -> RawFd {
|
||||
let fd = libc::accept4(sockfd, addr, len, flg);
|
||||
if fd < 0 {
|
||||
perror!("accept4");
|
||||
unsafe {
|
||||
let fd = libc::accept4(sockfd, addr, len, flg);
|
||||
if fd < 0 {
|
||||
perror!("accept4");
|
||||
}
|
||||
fd
|
||||
}
|
||||
fd
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xsendmsg(fd: RawFd, msg: *const libc::msghdr, flags: i32) -> ssize_t {
|
||||
let r = libc::sendmsg(fd, msg, flags);
|
||||
if r < 0 {
|
||||
perror!("sendmsg");
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xrecvmsg(fd: RawFd, msg: *mut libc::msghdr, flags: i32) -> ssize_t {
|
||||
let r = libc::recvmsg(fd, msg, flags);
|
||||
if r < 0 {
|
||||
perror!("recvmsg");
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xaccess(path: *const c_char, mode: i32) -> i32 {
|
||||
let r = libc::access(path, mode);
|
||||
if r < 0 {
|
||||
perror!("access {}", ptr_to_str(path));
|
||||
unsafe {
|
||||
let r = libc::access(path, mode);
|
||||
if r < 0 {
|
||||
perror!("access {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xfaccessat(dirfd: RawFd, path: *const c_char, mode: i32, flags: i32) -> i32 {
|
||||
#[allow(unused_mut)]
|
||||
let mut r = libc::faccessat(dirfd, path, mode, flags);
|
||||
if r < 0 {
|
||||
perror!("faccessat {}", ptr_to_str(path));
|
||||
}
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
if r > 0 && *errno() == 0 {
|
||||
r = 0
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xstat(path: *const c_char, buf: *mut libc::stat) -> i32 {
|
||||
let r = libc::stat(path, buf);
|
||||
if r < 0 {
|
||||
perror!("stat {}", ptr_to_str(path));
|
||||
unsafe {
|
||||
let r = libc::stat(path, buf);
|
||||
if r < 0 {
|
||||
perror!("stat {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xlstat(path: *const c_char, buf: *mut libc::stat) -> i32 {
|
||||
let r = libc::lstat(path, buf);
|
||||
if r < 0 {
|
||||
perror!("lstat {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfstat(fd: RawFd, buf: *mut libc::stat) -> i32 {
|
||||
let r = libc::fstat(fd, buf);
|
||||
if r < 0 {
|
||||
perror!("fstat");
|
||||
unsafe {
|
||||
let r = libc::fstat(fd, buf);
|
||||
if r < 0 {
|
||||
perror!("fstat");
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xfstatat(
|
||||
dirfd: RawFd,
|
||||
path: *const c_char,
|
||||
buf: *mut libc::stat,
|
||||
flags: i32,
|
||||
) -> i32 {
|
||||
let r = libc::fstatat(dirfd, path, buf, flags);
|
||||
if r < 0 {
|
||||
perror!("fstatat {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xdup(oldfd: RawFd) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::dup(oldfd);
|
||||
@@ -440,7 +401,7 @@ extern "C" fn xdup(oldfd: RawFd) -> RawFd {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::dup2(oldfd, newfd);
|
||||
@@ -451,59 +412,18 @@ extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn xdup3(oldfd: RawFd, newfd: RawFd, flags: i32) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::syscall(SYS_dup3, oldfd, newfd, flags) as RawFd;
|
||||
if fd < 0 {
|
||||
perror!("dup3");
|
||||
}
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xsymlink(target: *const c_char, linkpath: *const c_char) -> i32 {
|
||||
let r = libc::symlink(target, linkpath);
|
||||
if r < 0 {
|
||||
perror!("symlink {} -> {}", ptr_to_str(target), ptr_to_str(linkpath));
|
||||
unsafe {
|
||||
let r = libc::symlink(target, linkpath);
|
||||
if r < 0 {
|
||||
perror!("symlink {} -> {}", ptr_to_str(target), ptr_to_str(linkpath));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xsymlinkat(
|
||||
target: *const c_char,
|
||||
dirfd: RawFd,
|
||||
linkpath: *const c_char,
|
||||
) -> i32 {
|
||||
let r = libc::symlinkat(target, dirfd, linkpath);
|
||||
if r < 0 {
|
||||
perror!(
|
||||
"symlinkat {} -> {}",
|
||||
ptr_to_str(target),
|
||||
ptr_to_str(linkpath)
|
||||
);
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xlinkat(
|
||||
olddirfd: RawFd,
|
||||
target: *const c_char,
|
||||
newdirfd: RawFd,
|
||||
linkpath: *const c_char,
|
||||
flags: i32,
|
||||
) -> i32 {
|
||||
let r = libc::linkat(olddirfd, target, newdirfd, linkpath, flags);
|
||||
if r < 0 {
|
||||
perror!("linkat {} -> {}", ptr_to_str(target), ptr_to_str(linkpath));
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xmount(
|
||||
src: *const c_char,
|
||||
target: *const c_char,
|
||||
@@ -511,101 +431,78 @@ unsafe extern "C" fn xmount(
|
||||
flags: c_ulong,
|
||||
data: *const c_void,
|
||||
) -> i32 {
|
||||
let r = libc::mount(src, target, fstype, flags, data);
|
||||
if r < 0 {
|
||||
perror!("mount {} -> {}", ptr_to_str(src), ptr_to_str(target));
|
||||
unsafe {
|
||||
let r = libc::mount(src, target, fstype, flags, data);
|
||||
if r < 0 {
|
||||
perror!("mount {} -> {}", ptr_to_str(src), ptr_to_str(target));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xumount(target: *const c_char) -> i32 {
|
||||
let r = libc::umount(target);
|
||||
if r < 0 {
|
||||
perror!("umount {}", ptr_to_str(target));
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xumount2(target: *const c_char, flags: i32) -> i32 {
|
||||
let r = libc::umount2(target, flags);
|
||||
if r < 0 {
|
||||
perror!("umount2 {}", ptr_to_str(target));
|
||||
unsafe {
|
||||
let r = libc::umount2(target, flags);
|
||||
if r < 0 {
|
||||
perror!("umount2 {}", ptr_to_str(target));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xrename(oldname: *const c_char, newname: *const c_char) -> i32 {
|
||||
let r = libc::rename(oldname, newname);
|
||||
if r < 0 {
|
||||
perror!("rename {} -> {}", ptr_to_str(oldname), ptr_to_str(newname));
|
||||
unsafe {
|
||||
let r = libc::rename(oldname, newname);
|
||||
if r < 0 {
|
||||
perror!("rename {} -> {}", ptr_to_str(oldname), ptr_to_str(newname));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
|
||||
let r = libc::mkdir(path, mode);
|
||||
if r < 0 && *errno() != libc::EEXIST {
|
||||
perror!("mkdir {}", ptr_to_str(path));
|
||||
unsafe {
|
||||
let r = libc::mkdir(path, mode);
|
||||
if r < 0 && *errno() != libc::EEXIST {
|
||||
perror!("mkdir {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p)
|
||||
.mkdirs(mode)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("mkdirs {} failed", p)))
|
||||
.map_or(-1, |_| 0),
|
||||
Err(_) => -1,
|
||||
unsafe {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(p) => FsPath::from(p)
|
||||
.mkdirs(mode)
|
||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("mkdirs {} failed", p)))
|
||||
.map_or(-1, |_| 0),
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xmkdirat(dirfd: RawFd, path: *const c_char, mode: mode_t) -> i32 {
|
||||
let r = libc::mkdirat(dirfd, path, mode);
|
||||
if r < 0 && *errno() != libc::EEXIST {
|
||||
perror!("mkdirat {}", ptr_to_str(path));
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xsendfile(
|
||||
out_fd: RawFd,
|
||||
in_fd: RawFd,
|
||||
offset: *mut off_t,
|
||||
count: usize,
|
||||
) -> isize {
|
||||
let r = libc::sendfile(out_fd, in_fd, offset, count);
|
||||
if r < 0 {
|
||||
perror!("sendfile");
|
||||
unsafe {
|
||||
let r = libc::sendfile(out_fd, in_fd, offset, count);
|
||||
if r < 0 {
|
||||
perror!("sendfile");
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xmmap(
|
||||
addr: *mut c_void,
|
||||
len: usize,
|
||||
prot: i32,
|
||||
flags: i32,
|
||||
fd: RawFd,
|
||||
offset: off_t,
|
||||
) -> *mut c_void {
|
||||
let r = libc::mmap(addr, len, prot, flags, fd, offset);
|
||||
if r == libc::MAP_FAILED {
|
||||
perror!("mmap");
|
||||
return ptr::null_mut();
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xfork() -> i32 {
|
||||
unsafe {
|
||||
let r = libc::fork();
|
||||
@@ -616,20 +513,24 @@ extern "C" fn xfork() -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xpoll(fds: *mut pollfd, nfds: nfds_t, timeout: i32) -> i32 {
|
||||
let r = libc::poll(fds, nfds, timeout);
|
||||
if r < 0 {
|
||||
perror!("poll");
|
||||
unsafe {
|
||||
let r = libc::poll(fds, nfds, timeout);
|
||||
if r < 0 {
|
||||
perror!("poll");
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
|
||||
let r = libc::mknod(pathname, mode, dev);
|
||||
if r < 0 {
|
||||
perror!("mknod {}", ptr_to_str(pathname));
|
||||
unsafe {
|
||||
let r = libc::mknod(pathname, mode, dev);
|
||||
if r < 0 {
|
||||
perror!("mknod {}", ptr_to_str(pathname));
|
||||
}
|
||||
r
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "magiskboot"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
@@ -25,8 +25,12 @@ p256 = { workspace = true }
|
||||
p384 = { workspace = true }
|
||||
p521 = { workspace = true }
|
||||
rsa = { workspace = true, features = ["sha2"] }
|
||||
block-buffer = { workspace = true }
|
||||
sec1 = { workspace = true }
|
||||
x509-cert = { workspace = true }
|
||||
der = { workspace = true, features = ["derive", "pem"] }
|
||||
fdt = { workspace = true }
|
||||
bytemuck = { workspace = true, features = ["derive", "min_const_generics"] }
|
||||
num-traits = { workspace = true }
|
||||
libz-rs-sys = { workspace = true }
|
||||
libbz2-rs-sys = { workspace = true }
|
||||
|
||||
@@ -431,9 +431,20 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
|
||||
}
|
||||
if (k_fmt == ZIMAGE) {
|
||||
z_hdr = reinterpret_cast<const zimage_hdr *>(kernel);
|
||||
if (const void *gzip = memmem(kernel, hdr->kernel_size(), GZIP1_MAGIC "\x08\x00", 4)) {
|
||||
|
||||
const uint8_t* found_pos = 0;
|
||||
|
||||
for (const uint8_t* search_pos = kernel + 0x28; search_pos < kernel + hdr->kernel_size(); search_pos++) {
|
||||
// ^^^^^^ +0x28 to search after zimage header and magic
|
||||
if (check_fmt_lg(search_pos, hdr->kernel_size() - (search_pos - kernel)) != UNKNOWN) {
|
||||
found_pos = search_pos;
|
||||
search_pos = kernel + hdr->kernel_size();
|
||||
}
|
||||
}
|
||||
|
||||
if (found_pos != 0) {
|
||||
fprintf(stderr, "ZIMAGE_KERNEL\n");
|
||||
z_info.hdr_sz = (const uint8_t *) gzip - kernel;
|
||||
z_info.hdr_sz = (const uint8_t *) found_pos - kernel;
|
||||
|
||||
// Find end of piggy
|
||||
uint32_t zImage_size = z_hdr->end - z_hdr->start;
|
||||
@@ -457,7 +468,7 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
|
||||
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "! Could not find zImage gzip piggy, keeping raw kernel\n");
|
||||
fprintf(stderr, "! Could not find zImage piggy, keeping raw kernel\n");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use pb_rs::{types::FileDescriptor, ConfigBuilder};
|
||||
|
||||
use crate::gen::gen_cxx_binding;
|
||||
use crate::codegen::gen_cxx_binding;
|
||||
|
||||
#[path = "../include/gen.rs"]
|
||||
mod gen;
|
||||
#[path = "../include/codegen.rs"]
|
||||
mod codegen;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=proto/update_metadata.proto");
|
||||
|
||||
287
native/src/boot/bzlib.h
Normal file
287
native/src/boot/bzlib.h
Normal file
@@ -0,0 +1,287 @@
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- Public header file for the library. ---*/
|
||||
/*--- bzlib.h ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
This file is part of bzip2/libbzip2, a program and library for
|
||||
lossless, block-sorting data compression.
|
||||
|
||||
bzip2/libbzip2 version 1.1.0 of 6 September 2010
|
||||
Copyright (C) 1996-2010 Julian Seward <jseward@acm.org>
|
||||
|
||||
Please read the WARNING, DISCLAIMER and PATENTS sections in the
|
||||
README file.
|
||||
|
||||
This program is released under the terms of the license contained
|
||||
in the file LICENSE.
|
||||
------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#ifndef _BZLIB_H
|
||||
#define _BZLIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BZ_RUN 0
|
||||
#define BZ_FLUSH 1
|
||||
#define BZ_FINISH 2
|
||||
|
||||
#define BZ_OK 0
|
||||
#define BZ_RUN_OK 1
|
||||
#define BZ_FLUSH_OK 2
|
||||
#define BZ_FINISH_OK 3
|
||||
#define BZ_STREAM_END 4
|
||||
#define BZ_SEQUENCE_ERROR (-1)
|
||||
#define BZ_PARAM_ERROR (-2)
|
||||
#define BZ_MEM_ERROR (-3)
|
||||
#define BZ_DATA_ERROR (-4)
|
||||
#define BZ_DATA_ERROR_MAGIC (-5)
|
||||
#define BZ_IO_ERROR (-6)
|
||||
#define BZ_UNEXPECTED_EOF (-7)
|
||||
#define BZ_OUTBUFF_FULL (-8)
|
||||
#define BZ_CONFIG_ERROR (-9)
|
||||
|
||||
typedef
|
||||
struct {
|
||||
char *next_in;
|
||||
unsigned int avail_in;
|
||||
unsigned int total_in_lo32;
|
||||
unsigned int total_in_hi32;
|
||||
|
||||
char *next_out;
|
||||
unsigned int avail_out;
|
||||
unsigned int total_out_lo32;
|
||||
unsigned int total_out_hi32;
|
||||
|
||||
void *state;
|
||||
|
||||
void *(*bzalloc)(void *,int,int);
|
||||
void (*bzfree)(void *,void *);
|
||||
void *opaque;
|
||||
}
|
||||
bz_stream;
|
||||
|
||||
|
||||
#ifndef BZ_IMPORT
|
||||
#define BZ_EXPORT
|
||||
#endif
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
/* Need a definitition for FILE */
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# ifdef small
|
||||
/* windows.h define small to char */
|
||||
# undef small
|
||||
# endif
|
||||
# ifndef WINAPI
|
||||
# define WINAPI
|
||||
# endif
|
||||
# ifdef BZ_EXPORT
|
||||
# define BZ_API(func) WINAPI func
|
||||
# define BZ_EXTERN extern
|
||||
# else
|
||||
/* import windows dll dynamically */
|
||||
# define BZ_API(func) (WINAPI * func)
|
||||
# define BZ_EXTERN
|
||||
# endif
|
||||
#else
|
||||
# define BZ_API(func) func
|
||||
#endif
|
||||
|
||||
#ifndef BZ_EXTERN
|
||||
#define BZ_EXTERN extern
|
||||
#endif
|
||||
|
||||
/*-- Core (low-level) library functions --*/
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompressInit) (
|
||||
bz_stream* strm,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompress) (
|
||||
bz_stream* strm,
|
||||
int action
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) (
|
||||
bz_stream* strm
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) (
|
||||
bz_stream *strm,
|
||||
int verbosity,
|
||||
int small
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompress) (
|
||||
bz_stream* strm
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) (
|
||||
bz_stream *strm
|
||||
);
|
||||
|
||||
|
||||
|
||||
/*-- High(er) level library functions --*/
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
#define BZ_MAX_UNUSED 5000
|
||||
|
||||
typedef void BZFILE;
|
||||
|
||||
BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) (
|
||||
int* bzerror,
|
||||
FILE* f,
|
||||
int verbosity,
|
||||
int small,
|
||||
void* unused,
|
||||
int nUnused
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzReadClose) (
|
||||
int* bzerror,
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void** unused,
|
||||
int* nUnused
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzRead) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) (
|
||||
int* bzerror,
|
||||
FILE* f,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWrite) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWriteClose) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
int abandon,
|
||||
unsigned int* nbytes_in,
|
||||
unsigned int* nbytes_out
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) (
|
||||
int* bzerror,
|
||||
BZFILE* b,
|
||||
int abandon,
|
||||
unsigned int* nbytes_in_lo32,
|
||||
unsigned int* nbytes_in_hi32,
|
||||
unsigned int* nbytes_out_lo32,
|
||||
unsigned int* nbytes_out_hi32
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/*-- Utility functions --*/
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) (
|
||||
char* dest,
|
||||
unsigned int* destLen,
|
||||
char* source,
|
||||
unsigned int sourceLen,
|
||||
int blockSize100k,
|
||||
int verbosity,
|
||||
int workFactor
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) (
|
||||
char* dest,
|
||||
unsigned int* destLen,
|
||||
char* source,
|
||||
unsigned int sourceLen,
|
||||
int small,
|
||||
int verbosity
|
||||
);
|
||||
|
||||
|
||||
/*--
|
||||
Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
|
||||
to support better zlib compatibility.
|
||||
This code is not _officially_ part of libbzip2 (yet);
|
||||
I haven't tested it, documented it, or considered the
|
||||
threading-safeness of it.
|
||||
If this code breaks, please contact both Yoshioka and me.
|
||||
--*/
|
||||
|
||||
BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
|
||||
void
|
||||
);
|
||||
|
||||
#ifndef BZ_NO_STDIO
|
||||
BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
|
||||
const char *path,
|
||||
const char *mode
|
||||
);
|
||||
|
||||
BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
|
||||
int fd,
|
||||
const char *mode
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzread) (
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzwrite) (
|
||||
BZFILE* b,
|
||||
void* buf,
|
||||
int len
|
||||
);
|
||||
|
||||
BZ_EXTERN int BZ_API(BZ2_bzflush) (
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN void BZ_API(BZ2_bzclose) (
|
||||
BZFILE* b
|
||||
);
|
||||
|
||||
BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
|
||||
BZFILE *b,
|
||||
int *errnum
|
||||
);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------*/
|
||||
/*--- end bzlib.h ---*/
|
||||
/*-------------------------------------------------------------*/
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include <zlib.h>
|
||||
#include <bzlib.h>
|
||||
#include "bzlib.h"
|
||||
#include <lzma.h>
|
||||
#include <lz4.h>
|
||||
#include <lz4frame.h>
|
||||
@@ -153,7 +153,7 @@ class zopfli_encoder : public chunk_out_stream {
|
||||
public:
|
||||
explicit zopfli_encoder(out_strm_ptr &&base) :
|
||||
chunk_out_stream(std::move(base), ZOPFLI_MASTER_BLOCK_SIZE),
|
||||
zo{}, out(nullptr), outsize(0), crc(crc32_z(0L, Z_NULL, 0)), in_total(0), bp(0) {
|
||||
zo{}, out(nullptr), outsize(0), crc(crc32(0L, Z_NULL, 0)), in_total(0), bp(0) {
|
||||
ZopfliInitOptions(&zo);
|
||||
|
||||
// This config is already better than gzip -9
|
||||
@@ -198,7 +198,7 @@ protected:
|
||||
auto in = static_cast<const unsigned char *>(buf);
|
||||
|
||||
in_total += len;
|
||||
crc = crc32_z(crc, in, len);
|
||||
crc = crc32(crc, in, len);
|
||||
|
||||
ZopfliDeflatePart(&zo, 2, final, in, 0, len, &bp, &out, &outsize);
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ use base::libc::{
|
||||
S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
||||
};
|
||||
use base::{
|
||||
log_err, map_args, BytesExt, EarlyExitExt, FsPath, LoggedResult, MappedFile, ResultExt,
|
||||
Utf8CStr, Utf8CStrBufArr, Utf8CStrWrite, WriteExt,
|
||||
cstr_buf, log_err, map_args, BytesExt, EarlyExitExt, FsPath, LoggedResult, MappedFile,
|
||||
ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
||||
};
|
||||
|
||||
use crate::check_env;
|
||||
@@ -344,7 +344,7 @@ impl Cpio {
|
||||
let out = Utf8CStr::from_string(out);
|
||||
let out = FsPath::from(out);
|
||||
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let mut buf = cstr_buf::default();
|
||||
|
||||
// Make sure its parent directories exist
|
||||
if out.parent(&mut buf) {
|
||||
@@ -746,8 +746,7 @@ impl Display for CpioEntry {
|
||||
Size::from_bytes(self.data.len())
|
||||
.format()
|
||||
.with_style(Style::Abbreviated)
|
||||
.with_base(Base::Base10)
|
||||
.to_string(),
|
||||
.with_base(Base::Base10),
|
||||
self.rdevmajor,
|
||||
self.rdevminor,
|
||||
)
|
||||
@@ -755,9 +754,9 @@ impl Display for CpioEntry {
|
||||
}
|
||||
|
||||
pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
fn inner(argc: i32, argv: *const *const c_char) -> LoggedResult<()> {
|
||||
let res: LoggedResult<()> = try {
|
||||
if argc < 1 {
|
||||
return Err(log_err!("No arguments"));
|
||||
Err(log_err!("No arguments"))?;
|
||||
}
|
||||
|
||||
let cmds = map_args(argc, argv)?;
|
||||
@@ -807,7 +806,7 @@ pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
CpioAction::Add(Add { mode, path, file }) => cpio.add(*mode, path, file)?,
|
||||
CpioAction::Extract(Extract { paths }) => {
|
||||
if !paths.is_empty() && paths.len() != 2 {
|
||||
return Err(log_err!("invalid arguments"));
|
||||
Err(log_err!("invalid arguments"))?;
|
||||
}
|
||||
let mut it = paths.iter_mut();
|
||||
cpio.extract(it.next(), it.next())?;
|
||||
@@ -819,10 +818,8 @@ pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
};
|
||||
}
|
||||
cpio.dump(file)?;
|
||||
Ok(())
|
||||
}
|
||||
inner(argc, argv)
|
||||
.log_with_msg(|w| w.write_str("Failed to process cpio"))
|
||||
};
|
||||
res.log_with_msg(|w| w.write_str("Failed to process cpio"))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{cell::UnsafeCell, process::exit};
|
||||
use argh::FromArgs;
|
||||
use fdt::{
|
||||
node::{FdtNode, NodeProperty},
|
||||
Fdt,
|
||||
Fdt, FdtError,
|
||||
};
|
||||
|
||||
use base::{
|
||||
@@ -171,15 +171,17 @@ fn for_each_fdt<F: FnMut(usize, Fdt) -> LoggedResult<()>>(
|
||||
if slice.len() < 40 {
|
||||
break;
|
||||
}
|
||||
let fdt = Fdt::new(slice)?;
|
||||
let fdt = match Fdt::new(slice) {
|
||||
Err(FdtError::BufferTooSmall) => {
|
||||
eprintln!("dtb.{:04} is truncated", dtb_num);
|
||||
break;
|
||||
}
|
||||
Ok(fdt) => fdt,
|
||||
e => e?,
|
||||
};
|
||||
|
||||
let size = fdt.total_size();
|
||||
|
||||
if size > slice.len() {
|
||||
eprintln!("dtb.{:04} is truncated", dtb_num);
|
||||
break;
|
||||
}
|
||||
|
||||
f(dtb_num, fdt)?;
|
||||
|
||||
dtb_num += 1;
|
||||
@@ -273,9 +275,9 @@ fn dtb_patch(file: &Utf8CStr) -> LoggedResult<bool> {
|
||||
}
|
||||
|
||||
pub fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
fn inner(argc: i32, argv: *const *const c_char) -> LoggedResult<()> {
|
||||
let res: LoggedResult<()> = try {
|
||||
if argc < 1 {
|
||||
return Err(log_err!("No arguments"));
|
||||
Err(log_err!("No arguments"))?;
|
||||
}
|
||||
let cmds = map_args(argc, argv)?;
|
||||
|
||||
@@ -299,9 +301,7 @@ pub fn dtb_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
inner(argc, argv)
|
||||
.log_with_msg(|w| w.write_str("Failed to process dtb"))
|
||||
};
|
||||
res.log_with_msg(|w| w.write_str("Failed to process dtb"))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(btree_extract_if)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(try_blocks)]
|
||||
|
||||
pub use libz_rs_sys::*;
|
||||
pub use libbz2_rs_sys::*;
|
||||
pub use base;
|
||||
use cpio::cpio_commands;
|
||||
use dtb::dtb_commands;
|
||||
@@ -21,6 +24,14 @@ mod sign;
|
||||
|
||||
#[cxx::bridge]
|
||||
pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("../base/include/base.hpp");
|
||||
|
||||
#[namespace = "rust"]
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("compress.hpp");
|
||||
fn decompress(buf: &[u8], fd: i32) -> bool;
|
||||
@@ -51,10 +62,10 @@ pub mod ffi {
|
||||
#[namespace = "rust"]
|
||||
#[allow(unused_unsafe)]
|
||||
extern "Rust" {
|
||||
unsafe fn extract_boot_from_payload(
|
||||
partition: *const c_char,
|
||||
in_path: *const c_char,
|
||||
out_path: *const c_char,
|
||||
fn extract_boot_from_payload(
|
||||
partition: Utf8CStrRef,
|
||||
in_path: Utf8CStrRef,
|
||||
out_path: Utf8CStrRef,
|
||||
) -> bool;
|
||||
unsafe fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool;
|
||||
unsafe fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool;
|
||||
@@ -70,5 +81,5 @@ pub mod ffi {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn check_env(env: &str) -> bool {
|
||||
env::var(env).map_or(false, |var| var == "true")
|
||||
env::var(env).is_ok_and(|var| var == "true")
|
||||
}
|
||||
|
||||
@@ -217,8 +217,8 @@ int main(int argc, char *argv[]) {
|
||||
} else if (argc > 2 && action == "extract") {
|
||||
return rust::extract_boot_from_payload(
|
||||
argv[2],
|
||||
argc > 3 ? argv[3] : nullptr,
|
||||
argc > 4 ? argv[4] : nullptr
|
||||
argc > 3 ? argv[3] : "",
|
||||
argc > 4 ? argv[4] : ""
|
||||
) ? 0 : 1;
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
|
||||
@@ -63,15 +63,17 @@ fn remove_pattern(buf: &mut [u8], pattern_matcher: unsafe fn(&[u8]) -> Option<us
|
||||
|
||||
pub fn patch_verity(buf: &mut [u8]) -> usize {
|
||||
unsafe fn match_verity_pattern(buf: &[u8]) -> Option<usize> {
|
||||
match_patterns!(
|
||||
buf,
|
||||
b"verifyatboot",
|
||||
b"verify",
|
||||
b"avb_keys",
|
||||
b"avb",
|
||||
b"support_scfs",
|
||||
b"fsverity"
|
||||
)
|
||||
unsafe {
|
||||
match_patterns!(
|
||||
buf,
|
||||
b"verifyatboot",
|
||||
b"verify",
|
||||
b"avb_keys",
|
||||
b"avb",
|
||||
b"support_scfs",
|
||||
b"fsverity"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
remove_pattern(buf, match_verity_pattern)
|
||||
@@ -79,7 +81,7 @@ pub fn patch_verity(buf: &mut [u8]) -> usize {
|
||||
|
||||
pub fn patch_encryption(buf: &mut [u8]) -> usize {
|
||||
unsafe fn match_encryption_pattern(buf: &[u8]) -> Option<usize> {
|
||||
match_patterns!(buf, b"forceencrypt", b"forcefdeorfbe", b"fileencryption")
|
||||
unsafe { match_patterns!(buf, b"forceencrypt", b"forcefdeorfbe", b"fileencryption") }
|
||||
}
|
||||
|
||||
remove_pattern(buf, match_encryption_pattern)
|
||||
@@ -95,13 +97,13 @@ fn hex2byte(hex: &[u8]) -> Vec<u8> {
|
||||
let low = bytes[1].to_ascii_uppercase() - b'0';
|
||||
let h = if high > 9 { high - 7 } else { high };
|
||||
let l = if low > 9 { low - 7 } else { low };
|
||||
v.push(h << 4 | l);
|
||||
v.push((h << 4) | l);
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
pub fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool {
|
||||
fn inner(file: &[u8], from: &[u8], to: &[u8]) -> LoggedResult<bool> {
|
||||
let res: LoggedResult<bool> = try {
|
||||
let file = Utf8CStr::from_bytes(file)?;
|
||||
let from = Utf8CStr::from_bytes(from)?;
|
||||
let to = Utf8CStr::from_bytes(to)?;
|
||||
@@ -114,8 +116,7 @@ pub fn hexpatch(file: &[u8], from: &[u8], to: &[u8]) -> bool {
|
||||
for off in &v {
|
||||
eprintln!("Patch @ {:#010X} [{}] -> [{}]", off, from, to);
|
||||
}
|
||||
|
||||
Ok(!v.is_empty())
|
||||
}
|
||||
inner(file, from, to).unwrap_or(false)
|
||||
!v.is_empty()
|
||||
};
|
||||
res.unwrap_or(false)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Read, Seek, SeekFrom, Write};
|
||||
use std::os::fd::{AsRawFd, FromRawFd};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, Read, Seek, SeekFrom, Write},
|
||||
os::fd::{AsRawFd, FromRawFd},
|
||||
};
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use quick_protobuf::{BytesReader, MessageRead};
|
||||
|
||||
use base::libc::c_char;
|
||||
use base::{error, LoggedError, LoggedResult, ReadSeekExt, StrErr, Utf8CStr};
|
||||
use base::{ResultExt, WriteExt};
|
||||
|
||||
use crate::ffi;
|
||||
use crate::proto::update_metadata::mod_InstallOperation::Type;
|
||||
use crate::proto::update_metadata::DeltaArchiveManifest;
|
||||
use crate::{
|
||||
ffi,
|
||||
proto::update_metadata::{mod_InstallOperation::Type, DeltaArchiveManifest},
|
||||
};
|
||||
use base::{
|
||||
error, ffi::Utf8CStrRef, LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt,
|
||||
};
|
||||
|
||||
macro_rules! bad_payload {
|
||||
($msg:literal) => {{
|
||||
@@ -178,28 +180,23 @@ fn do_extract_boot_from_payload(
|
||||
}
|
||||
|
||||
pub fn extract_boot_from_payload(
|
||||
in_path: *const c_char,
|
||||
partition: *const c_char,
|
||||
out_path: *const c_char,
|
||||
in_path: Utf8CStrRef,
|
||||
partition: Utf8CStrRef,
|
||||
out_path: Utf8CStrRef,
|
||||
) -> bool {
|
||||
fn inner(
|
||||
in_path: *const c_char,
|
||||
partition: *const c_char,
|
||||
out_path: *const c_char,
|
||||
) -> LoggedResult<()> {
|
||||
let in_path = unsafe { Utf8CStr::from_ptr(in_path) }?;
|
||||
let partition = match unsafe { Utf8CStr::from_ptr(partition) } {
|
||||
Ok(s) => Some(s),
|
||||
Err(StrErr::NullPointerError) => None,
|
||||
Err(e) => Err(e)?,
|
||||
let res: LoggedResult<()> = try {
|
||||
let partition = if partition.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(partition)
|
||||
};
|
||||
let out_path = match unsafe { Utf8CStr::from_ptr(out_path) } {
|
||||
Ok(s) => Some(s),
|
||||
Err(StrErr::NullPointerError) => None,
|
||||
Err(e) => Err(e)?,
|
||||
let out_path = if out_path.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(out_path)
|
||||
};
|
||||
do_extract_boot_from_payload(in_path, partition, out_path)
|
||||
.log_with_msg(|w| w.write_str("Failed to extract from payload"))
|
||||
}
|
||||
inner(in_path, partition, out_path).is_ok()
|
||||
do_extract_boot_from_payload(in_path, partition, out_path)?
|
||||
};
|
||||
res.log_with_msg(|w| w.write_str("Failed to extract from payload"))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
@@ -157,20 +157,32 @@ struct Signer {
|
||||
impl Signer {
|
||||
fn from_private_key(key: &[u8]) -> LoggedResult<Signer> {
|
||||
let digest: Box<dyn DynDigest>;
|
||||
let key = if let Ok(rsa) = RsaPrivateKey::from_pkcs8_der(key) {
|
||||
digest = Box::<Sha256>::default();
|
||||
SigningKey::SHA256withRSA(RsaSigningKey::<Sha256>::new(rsa))
|
||||
} else if let Ok(ec) = P256SigningKey::from_pkcs8_der(key) {
|
||||
digest = Box::<Sha256>::default();
|
||||
SigningKey::SHA256withECDSA(ec)
|
||||
} else if let Ok(ec) = P384SigningKey::from_pkcs8_der(key) {
|
||||
digest = Box::<Sha384>::default();
|
||||
SigningKey::SHA384withECDSA(ec)
|
||||
} else if let Ok(ec) = P521SigningKey::from_pkcs8_der(key) {
|
||||
digest = Box::<Sha512>::default();
|
||||
SigningKey::SHA521withECDSA(ec)
|
||||
} else {
|
||||
return Err(log_err!("Unsupported private key"));
|
||||
let key = match RsaPrivateKey::from_pkcs8_der(key) {
|
||||
Ok(rsa) => {
|
||||
digest = Box::<Sha256>::default();
|
||||
SigningKey::SHA256withRSA(RsaSigningKey::<Sha256>::new(rsa))
|
||||
}
|
||||
_ => match P256SigningKey::from_pkcs8_der(key) {
|
||||
Ok(ec) => {
|
||||
digest = Box::<Sha256>::default();
|
||||
SigningKey::SHA256withECDSA(ec)
|
||||
}
|
||||
_ => match P384SigningKey::from_pkcs8_der(key) {
|
||||
Ok(ec) => {
|
||||
digest = Box::<Sha384>::default();
|
||||
SigningKey::SHA384withECDSA(ec)
|
||||
}
|
||||
_ => match P521SigningKey::from_pkcs8_der(key) {
|
||||
Ok(ec) => {
|
||||
digest = Box::<Sha512>::default();
|
||||
SigningKey::SHA521withECDSA(ec)
|
||||
}
|
||||
_ => {
|
||||
return Err(log_err!("Unsupported private key"));
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
Ok(Signer { digest, key })
|
||||
}
|
||||
@@ -254,7 +266,7 @@ impl BootSignature {
|
||||
}
|
||||
|
||||
pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool {
|
||||
fn inner(img: &BootImage, cert: *const c_char) -> LoggedResult<()> {
|
||||
let res: LoggedResult<()> = try {
|
||||
let tail = img.tail();
|
||||
// Don't use BootSignature::from_der because tail might have trailing zeros
|
||||
let mut reader = SliceReader::new(tail)?;
|
||||
@@ -268,9 +280,8 @@ pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool {
|
||||
Err(e) => Err(e)?,
|
||||
};
|
||||
sig.verify(img.payload())?;
|
||||
Ok(())
|
||||
}
|
||||
inner(img, cert).is_ok()
|
||||
};
|
||||
res.is_ok()
|
||||
}
|
||||
|
||||
enum Bytes {
|
||||
@@ -296,12 +307,7 @@ pub fn sign_boot_image(
|
||||
cert: *const c_char,
|
||||
key: *const c_char,
|
||||
) -> Vec<u8> {
|
||||
fn inner(
|
||||
payload: &[u8],
|
||||
name: *const c_char,
|
||||
cert: *const c_char,
|
||||
key: *const c_char,
|
||||
) -> LoggedResult<Vec<u8>> {
|
||||
let res: LoggedResult<Vec<u8>> = try {
|
||||
// Process arguments
|
||||
let name = unsafe { Utf8CStr::from_ptr(name) }?;
|
||||
let cert = match unsafe { Utf8CStr::from_ptr(cert) } {
|
||||
@@ -337,7 +343,7 @@ pub fn sign_boot_image(
|
||||
authenticated_attributes: attr,
|
||||
signature: OctetString::new(sig)?,
|
||||
};
|
||||
sig.to_der().log()
|
||||
}
|
||||
inner(payload, name, cert, key).unwrap_or_default()
|
||||
sig.to_der()?
|
||||
};
|
||||
res.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "magisk"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
@@ -17,6 +17,7 @@ pb-rs = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
base = { path = "../base", features = ["selinux"] }
|
||||
derive = { path = "derive" }
|
||||
cxx = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
num-derive = { workspace = true }
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <linux/input.h>
|
||||
#include <libgen.h>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool zygisk_enabled = false;
|
||||
|
||||
/*********
|
||||
* Setup *
|
||||
*********/
|
||||
|
||||
static bool magisk_env() {
|
||||
char buf[4096];
|
||||
|
||||
LOGI("* Initializing Magisk environment\n");
|
||||
|
||||
ssprintf(buf, sizeof(buf), "%s/0/%s/install", APP_DATA_DIR, JAVA_PACKAGE_NAME);
|
||||
// Alternative binaries paths
|
||||
const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", buf };
|
||||
for (auto alt : alt_bin) {
|
||||
if (access(alt, F_OK) == 0) {
|
||||
rm_rf(DATABIN);
|
||||
cp_afc(alt, DATABIN);
|
||||
rm_rf(alt);
|
||||
}
|
||||
}
|
||||
rm_rf("/cache/data_adb");
|
||||
|
||||
// Directories in /data/adb
|
||||
chmod(SECURE_DIR, 0700);
|
||||
xmkdir(DATABIN, 0755);
|
||||
xmkdir(MODULEROOT, 0755);
|
||||
xmkdir(SECURE_DIR "/post-fs-data.d", 0755);
|
||||
xmkdir(SECURE_DIR "/service.d", 0755);
|
||||
restorecon();
|
||||
|
||||
if (access(DATABIN "/busybox", X_OK))
|
||||
return false;
|
||||
|
||||
ssprintf(buf, sizeof(buf), "%s/" BBPATH "/busybox", get_magisk_tmp());
|
||||
mkdir(dirname(buf), 0755);
|
||||
cp_afc(DATABIN "/busybox", buf);
|
||||
exec_command_async(buf, "--install", "-s", dirname(buf));
|
||||
|
||||
// magisk32 and magiskpolicy are not installed into ramdisk and has to be copied
|
||||
// from data to magisk tmp
|
||||
if (access(DATABIN "/magisk32", X_OK) == 0) {
|
||||
ssprintf(buf, sizeof(buf), "%s/magisk32", get_magisk_tmp());
|
||||
cp_afc(DATABIN "/magisk32", buf);
|
||||
}
|
||||
if (access(DATABIN "/magiskpolicy", X_OK) == 0) {
|
||||
ssprintf(buf, sizeof(buf), "%s/magiskpolicy", get_magisk_tmp());
|
||||
cp_afc(DATABIN "/magiskpolicy", buf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void unlock_blocks() {
|
||||
int fd, dev, OFF = 0;
|
||||
|
||||
auto dir = xopen_dir("/dev/block");
|
||||
if (!dir)
|
||||
return;
|
||||
dev = dirfd(dir.get());
|
||||
|
||||
for (dirent *entry; (entry = readdir(dir.get()));) {
|
||||
if (entry->d_type == DT_BLK) {
|
||||
if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0)
|
||||
continue;
|
||||
if (ioctl(fd, BLKROSET, &OFF) < 0)
|
||||
PLOGE("unlock %s", entry->d_name);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8)))
|
||||
|
||||
static bool check_key_combo() {
|
||||
uint8_t bitmask[(KEY_MAX + 1) / 8];
|
||||
vector<int> events;
|
||||
constexpr char name[] = "/dev/.ev";
|
||||
|
||||
// First collect candidate events that accepts volume down
|
||||
for (int minor = 64; minor < 96; ++minor) {
|
||||
if (xmknod(name, S_IFCHR | 0444, makedev(13, minor)))
|
||||
continue;
|
||||
int fd = open(name, O_RDONLY | O_CLOEXEC);
|
||||
unlink(name);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
memset(bitmask, 0, sizeof(bitmask));
|
||||
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
|
||||
if (test_bit(KEY_VOLUMEDOWN, bitmask))
|
||||
events.push_back(fd);
|
||||
else
|
||||
close(fd);
|
||||
}
|
||||
if (events.empty())
|
||||
return false;
|
||||
|
||||
run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); });
|
||||
|
||||
// Check if volume down key is held continuously for more than 3 seconds
|
||||
for (int i = 0; i < 300; ++i) {
|
||||
bool pressed = false;
|
||||
for (const int &fd : events) {
|
||||
memset(bitmask, 0, sizeof(bitmask));
|
||||
ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
|
||||
if (test_bit(KEY_VOLUMEDOWN, bitmask)) {
|
||||
pressed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pressed)
|
||||
return false;
|
||||
// Check every 10ms
|
||||
usleep(10000);
|
||||
}
|
||||
LOGD("KEY_VOLUMEDOWN detected: enter safe mode\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/***********************
|
||||
* Boot Stage Handlers *
|
||||
***********************/
|
||||
|
||||
bool MagiskD::post_fs_data() const noexcept {
|
||||
setup_logfile();
|
||||
|
||||
LOGI("** post-fs-data mode running\n");
|
||||
|
||||
preserve_stub_apk();
|
||||
|
||||
if (access(SECURE_DIR, F_OK) != 0) {
|
||||
if (SDK_INT < 24) {
|
||||
xmkdir(SECURE_DIR, 0700);
|
||||
} else {
|
||||
LOGE(SECURE_DIR " is not present, abort\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
prune_su_access();
|
||||
|
||||
if (!magisk_env()) {
|
||||
LOGE("* Magisk environment incomplete, abort\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check safe mode
|
||||
int bootloop_cnt = get_db_setting(DbEntryKey::BootloopCount);
|
||||
// Increment the boot counter
|
||||
set_db_setting(DbEntryKey::BootloopCount, bootloop_cnt + 1);
|
||||
bool safe_mode = bootloop_cnt >= 2 || get_prop("persist.sys.safemode", true) == "1" ||
|
||||
get_prop("ro.sys.safemode") == "1" || check_key_combo();
|
||||
|
||||
if (safe_mode) {
|
||||
LOGI("* Safe mode triggered\n");
|
||||
// Disable all modules and zygisk so next boot will be clean
|
||||
disable_modules();
|
||||
set_db_setting(DbEntryKey::ZygiskConfig, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
exec_common_scripts("post-fs-data");
|
||||
zygisk_enabled = get_db_setting(DbEntryKey::ZygiskConfig);
|
||||
initialize_denylist();
|
||||
setup_mounts();
|
||||
handle_modules();
|
||||
load_modules();
|
||||
return false;
|
||||
}
|
||||
|
||||
void MagiskD::late_start() const noexcept {
|
||||
setup_logfile();
|
||||
|
||||
LOGI("** late_start service mode running\n");
|
||||
|
||||
exec_common_scripts("service");
|
||||
exec_module_scripts("service");
|
||||
}
|
||||
|
||||
void MagiskD::boot_complete() const noexcept {
|
||||
setup_logfile();
|
||||
|
||||
LOGI("** boot-complete triggered\n");
|
||||
|
||||
// Reset the bootloop counter once we have boot-complete
|
||||
set_db_setting(DbEntryKey::BootloopCount, 0);
|
||||
|
||||
// At this point it's safe to create the folder
|
||||
if (access(SECURE_DIR, F_OK) != 0)
|
||||
xmkdir(SECURE_DIR, 0700);
|
||||
|
||||
// Ensure manager exists
|
||||
get_manager(0, nullptr, true);
|
||||
|
||||
reset_zygisk(true);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
use pb_rs::{types::FileDescriptor, ConfigBuilder};
|
||||
|
||||
use crate::gen::gen_cxx_binding;
|
||||
use crate::codegen::gen_cxx_binding;
|
||||
|
||||
#[path = "../include/gen.rs"]
|
||||
mod gen;
|
||||
#[path = "../include/codegen.rs"]
|
||||
mod codegen;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=resetprop/proto/persistent_properties.proto");
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <libgen.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
@@ -134,23 +136,57 @@ void MagiskD::reboot() const noexcept {
|
||||
exec_command_sync("/system/bin/reboot");
|
||||
}
|
||||
|
||||
bool get_client_cred(int fd, sock_cred *cred) {
|
||||
socklen_t len = sizeof(ucred);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &len) != 0)
|
||||
return false;
|
||||
char buf[4096];
|
||||
len = sizeof(buf);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &len) != 0)
|
||||
len = 0;
|
||||
buf[len] = '\0';
|
||||
cred->context = buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read_string(int fd, std::string &str) {
|
||||
str.clear();
|
||||
int len = read_int(fd);
|
||||
str.resize(len);
|
||||
return xxread(fd, str.data(), len) == len;
|
||||
}
|
||||
|
||||
string read_string(int fd) {
|
||||
string str;
|
||||
read_string(fd, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void write_string(int fd, string_view str) {
|
||||
if (fd < 0) return;
|
||||
write_int(fd, str.size());
|
||||
xwrite(fd, str.data(), str.size());
|
||||
}
|
||||
|
||||
static void handle_request_async(int client, int code, const sock_cred &cred) {
|
||||
auto &daemon = MagiskD::Get();
|
||||
switch (code) {
|
||||
case +RequestCode::DENYLIST:
|
||||
denylist_handler(client, &cred);
|
||||
break;
|
||||
case +RequestCode::SUPERUSER:
|
||||
su_daemon_handler(client, &cred);
|
||||
daemon.su_daemon_handler(client, cred);
|
||||
break;
|
||||
case +RequestCode::ZYGOTE_RESTART:
|
||||
case +RequestCode::ZYGOTE_RESTART: {
|
||||
LOGI("** zygote restarted\n");
|
||||
MagiskD().prune_su_access();
|
||||
daemon.prune_su_access();
|
||||
scan_deny_apps();
|
||||
reset_zygisk(false);
|
||||
daemon.zygisk_reset(false);
|
||||
close(client);
|
||||
break;
|
||||
}
|
||||
case +RequestCode::SQLITE_CMD:
|
||||
MagiskD().db_exec(client);
|
||||
daemon.db_exec(client);
|
||||
break;
|
||||
case +RequestCode::REMOVE_MODULES: {
|
||||
int do_reboot = read_int(client);
|
||||
@@ -158,12 +194,12 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
|
||||
write_int(client, 0);
|
||||
close(client);
|
||||
if (do_reboot) {
|
||||
MagiskD().reboot();
|
||||
daemon.reboot();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case +RequestCode::ZYGISK:
|
||||
zygisk_handler(client, &cred);
|
||||
daemon.zygisk_handler(client);
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
@@ -285,7 +321,7 @@ static void handle_request(pollfd *pfd) {
|
||||
exec_task([=, fd = client.release()] { handle_request_async(fd, code, cred); });
|
||||
} else {
|
||||
exec_task([=, fd = client.release()] {
|
||||
MagiskD().boot_stage_handler(fd, code);
|
||||
MagiskD::Get().boot_stage_handler(fd, code);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -328,7 +364,7 @@ static void daemon_entry() {
|
||||
setcon(MAGISK_PROC_CON);
|
||||
|
||||
rust::daemon_entry();
|
||||
SDK_INT = MagiskD().sdk_int();
|
||||
SDK_INT = MagiskD::Get().sdk_int();
|
||||
|
||||
// Escape from cgroup
|
||||
int pid = getpid();
|
||||
@@ -378,7 +414,6 @@ static void daemon_entry() {
|
||||
|
||||
default_new(poll_map);
|
||||
default_new(poll_fds);
|
||||
default_new(module_list);
|
||||
|
||||
// Register handler for main socket
|
||||
pollfd main_socket_pfd = { fd, POLLIN, 0 };
|
||||
@@ -455,3 +490,116 @@ int connect_daemon(int req, bool create) {
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
bool setup_magisk_env() {
|
||||
char buf[4096];
|
||||
|
||||
LOGI("* Initializing Magisk environment\n");
|
||||
|
||||
ssprintf(buf, sizeof(buf), "%s/0/%s/install", APP_DATA_DIR, JAVA_PACKAGE_NAME);
|
||||
// Alternative binaries paths
|
||||
const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", buf };
|
||||
for (auto alt : alt_bin) {
|
||||
if (access(alt, F_OK) == 0) {
|
||||
rm_rf(DATABIN);
|
||||
cp_afc(alt, DATABIN);
|
||||
rm_rf(alt);
|
||||
}
|
||||
}
|
||||
rm_rf("/cache/data_adb");
|
||||
|
||||
// Directories in /data/adb
|
||||
chmod(SECURE_DIR, 0700);
|
||||
xmkdir(DATABIN, 0755);
|
||||
xmkdir(MODULEROOT, 0755);
|
||||
xmkdir(SECURE_DIR "/post-fs-data.d", 0755);
|
||||
xmkdir(SECURE_DIR "/service.d", 0755);
|
||||
restorecon();
|
||||
|
||||
if (access(DATABIN "/busybox", X_OK))
|
||||
return false;
|
||||
|
||||
ssprintf(buf, sizeof(buf), "%s/" BBPATH "/busybox", get_magisk_tmp());
|
||||
mkdir(dirname(buf), 0755);
|
||||
cp_afc(DATABIN "/busybox", buf);
|
||||
exec_command_async(buf, "--install", "-s", dirname(buf));
|
||||
|
||||
// magisk32 and magiskpolicy are not installed into ramdisk and has to be copied
|
||||
// from data to magisk tmp
|
||||
if (access(DATABIN "/magisk32", X_OK) == 0) {
|
||||
ssprintf(buf, sizeof(buf), "%s/magisk32", get_magisk_tmp());
|
||||
cp_afc(DATABIN "/magisk32", buf);
|
||||
}
|
||||
if (access(DATABIN "/magiskpolicy", X_OK) == 0) {
|
||||
ssprintf(buf, sizeof(buf), "%s/magiskpolicy", get_magisk_tmp());
|
||||
cp_afc(DATABIN "/magiskpolicy", buf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void unlock_blocks() {
|
||||
int fd, dev, OFF = 0;
|
||||
|
||||
auto dir = xopen_dir("/dev/block");
|
||||
if (!dir)
|
||||
return;
|
||||
dev = dirfd(dir.get());
|
||||
|
||||
for (dirent *entry; (entry = readdir(dir.get()));) {
|
||||
if (entry->d_type == DT_BLK) {
|
||||
if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0)
|
||||
continue;
|
||||
if (ioctl(fd, BLKROSET, &OFF) < 0)
|
||||
PLOGE("unlock %s", entry->d_name);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8)))
|
||||
|
||||
bool check_key_combo() {
|
||||
uint8_t bitmask[(KEY_MAX + 1) / 8];
|
||||
vector<int> events;
|
||||
constexpr char name[] = "/dev/.ev";
|
||||
|
||||
// First collect candidate events that accepts volume down
|
||||
for (int minor = 64; minor < 96; ++minor) {
|
||||
if (xmknod(name, S_IFCHR | 0444, makedev(13, minor)))
|
||||
continue;
|
||||
int fd = open(name, O_RDONLY | O_CLOEXEC);
|
||||
unlink(name);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
memset(bitmask, 0, sizeof(bitmask));
|
||||
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
|
||||
if (test_bit(KEY_VOLUMEDOWN, bitmask))
|
||||
events.push_back(fd);
|
||||
else
|
||||
close(fd);
|
||||
}
|
||||
if (events.empty())
|
||||
return false;
|
||||
|
||||
run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); });
|
||||
|
||||
// Check if volume down key is held continuously for more than 3 seconds
|
||||
for (int i = 0; i < 300; ++i) {
|
||||
bool pressed = false;
|
||||
for (const int &fd : events) {
|
||||
memset(bitmask, 0, sizeof(bitmask));
|
||||
ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
|
||||
if (test_bit(KEY_VOLUMEDOWN, bitmask)) {
|
||||
pressed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pressed)
|
||||
return false;
|
||||
// Check every 10ms
|
||||
usleep(10000);
|
||||
}
|
||||
LOGD("KEY_VOLUMEDOWN detected: enter safe mode\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
use crate::consts::{MAGISK_FULL_VER, MAIN_CONFIG};
|
||||
use crate::consts::{MAGISK_FULL_VER, MAIN_CONFIG, SECURE_DIR};
|
||||
use crate::db::Sqlite3;
|
||||
use crate::ffi::{get_magisk_tmp, RequestCode};
|
||||
use crate::ffi::{
|
||||
check_key_combo, disable_modules, exec_common_scripts, exec_module_scripts, get_magisk_tmp,
|
||||
initialize_denylist, setup_magisk_env, DbEntryKey, ModuleInfo, RequestCode,
|
||||
};
|
||||
use crate::get_prop;
|
||||
use crate::logging::{magisk_logging, start_log_daemon};
|
||||
use crate::logging::{magisk_logging, setup_logfile, start_log_daemon};
|
||||
use crate::mount::{clean_mounts, setup_mounts};
|
||||
use crate::package::ManagerInfo;
|
||||
use crate::su::SuInfo;
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use base::{
|
||||
cstr, info, libc, open_fd, BufReadExt, Directory, FsPath, FsPathBuf, LoggedResult, ReadExt,
|
||||
Utf8CStr, Utf8CStrBufArr,
|
||||
cstr, error, info, libc, open_fd, path, AtomicArc, BufReadExt, FsPathBuf, ResultExt, Utf8CStr,
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
use bytemuck::bytes_of;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{BufReader, Read, Write};
|
||||
use std::io::BufReader;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
// Global magiskd singleton
|
||||
@@ -60,16 +63,29 @@ pub struct MagiskD {
|
||||
pub sql_connection: Mutex<Option<Sqlite3>>,
|
||||
pub manager_info: Mutex<ManagerInfo>,
|
||||
boot_stage_lock: Mutex<BootStateFlags>,
|
||||
pub module_list: OnceLock<Vec<ModuleInfo>>,
|
||||
pub zygiskd_sockets: Mutex<(Option<UnixStream>, Option<UnixStream>)>,
|
||||
pub zygisk_enabled: AtomicBool,
|
||||
pub zygote_start_count: AtomicU32,
|
||||
pub cached_su_info: AtomicArc<SuInfo>,
|
||||
sdk_int: i32,
|
||||
pub is_emulator: bool,
|
||||
is_recovery: bool,
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
pub fn get() -> &'static MagiskD {
|
||||
unsafe { MAGISKD.get().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
pub fn is_recovery(&self) -> bool {
|
||||
self.is_recovery
|
||||
}
|
||||
|
||||
pub fn zygisk_enabled(&self) -> bool {
|
||||
self.zygisk_enabled.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn sdk_int(&self) -> i32 {
|
||||
self.sdk_int
|
||||
}
|
||||
@@ -82,39 +98,87 @@ impl MagiskD {
|
||||
}
|
||||
}
|
||||
|
||||
// app_id = app_no + AID_APP_START
|
||||
// app_no range: [0, 9999]
|
||||
pub fn get_app_no_list(&self) -> BitSet {
|
||||
let mut list = BitSet::new();
|
||||
let _: LoggedResult<()> = try {
|
||||
let mut app_data_dir = Directory::open(self.app_data_dir())?;
|
||||
// For each user
|
||||
loop {
|
||||
let entry = match app_data_dir.read()? {
|
||||
None => break,
|
||||
Some(e) => e,
|
||||
};
|
||||
let mut user_dir = match entry.open_as_dir() {
|
||||
Err(_) => continue,
|
||||
Ok(dir) => dir,
|
||||
};
|
||||
// For each package
|
||||
loop {
|
||||
match user_dir.read()? {
|
||||
None => break,
|
||||
Some(e) => {
|
||||
let attr = e.get_attr()?;
|
||||
let app_id = to_app_id(attr.st.st_uid as i32);
|
||||
if (AID_APP_START..=AID_APP_END).contains(&app_id) {
|
||||
let app_no = app_id - AID_APP_START;
|
||||
list.insert(app_no as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn post_fs_data(&self) -> bool {
|
||||
setup_logfile();
|
||||
info!("** post-fs-data mode running");
|
||||
|
||||
self.preserve_stub_apk();
|
||||
|
||||
// Check secure dir
|
||||
let secure_dir = path!(SECURE_DIR);
|
||||
if !secure_dir.exists() {
|
||||
if self.sdk_int < 24 {
|
||||
secure_dir.mkdir(0o700).log_ok();
|
||||
} else {
|
||||
error!("* {} is not present, abort", SECURE_DIR);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
list
|
||||
}
|
||||
|
||||
self.prune_su_access();
|
||||
|
||||
if !setup_magisk_env() {
|
||||
error!("* Magisk environment incomplete, abort");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check safe mode
|
||||
let boot_cnt = self.get_db_setting(DbEntryKey::BootloopCount);
|
||||
self.set_db_setting(DbEntryKey::BootloopCount, boot_cnt + 1)
|
||||
.log()
|
||||
.ok();
|
||||
let safe_mode = boot_cnt >= 2
|
||||
|| get_prop(cstr!("persist.sys.safemode"), true) == "1"
|
||||
|| get_prop(cstr!("ro.sys.safemode"), false) == "1"
|
||||
|| check_key_combo();
|
||||
|
||||
if safe_mode {
|
||||
info!("* Safe mode triggered");
|
||||
// Disable all modules and zygisk so next boot will be clean
|
||||
disable_modules();
|
||||
self.set_db_setting(DbEntryKey::ZygiskConfig, 0).log_ok();
|
||||
return true;
|
||||
}
|
||||
|
||||
exec_common_scripts(cstr!("post-fs-data"));
|
||||
self.zygisk_enabled.store(
|
||||
self.get_db_setting(DbEntryKey::ZygiskConfig) != 0,
|
||||
Ordering::Release,
|
||||
);
|
||||
initialize_denylist();
|
||||
setup_mounts();
|
||||
let modules = self.handle_modules();
|
||||
self.module_list.set(modules).ok();
|
||||
clean_mounts();
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn late_start(&self) {
|
||||
setup_logfile();
|
||||
info!("** late_start service mode running");
|
||||
|
||||
exec_common_scripts(cstr!("service"));
|
||||
if let Some(module_list) = self.module_list.get() {
|
||||
exec_module_scripts(cstr!("service"), module_list);
|
||||
}
|
||||
}
|
||||
|
||||
fn boot_complete(&self) {
|
||||
setup_logfile();
|
||||
info!("** boot-complete triggered");
|
||||
|
||||
// Reset the bootloop counter once we have boot-complete
|
||||
self.set_db_setting(DbEntryKey::BootloopCount, 0).log_ok();
|
||||
|
||||
// At this point it's safe to create the folder
|
||||
let secure_dir = path!(SECURE_DIR);
|
||||
if !secure_dir.exists() {
|
||||
secure_dir.mkdir(0o700).log_ok();
|
||||
}
|
||||
|
||||
self.ensure_manager();
|
||||
self.zygisk_reset(true)
|
||||
}
|
||||
|
||||
pub fn boot_stage_handler(&self, client: i32, code: i32) {
|
||||
@@ -164,8 +228,7 @@ pub fn daemon_entry() {
|
||||
|| get_prop(cstr!("ro.product.device"), false).contains("vsoc");
|
||||
|
||||
// Load config status
|
||||
let mut buf = Utf8CStrBufArr::<64>::new();
|
||||
let path = FsPathBuf::new(&mut buf)
|
||||
let path = FsPathBuf::<64>::new()
|
||||
.join(get_magisk_tmp())
|
||||
.join(MAIN_CONFIG);
|
||||
let mut is_recovery = false;
|
||||
@@ -181,7 +244,7 @@ pub fn daemon_entry() {
|
||||
}
|
||||
|
||||
let mut sdk_int = -1;
|
||||
if let Ok(file) = FsPath::from(cstr!("/system/build.prop")).open(O_RDONLY | O_CLOEXEC) {
|
||||
if let Ok(file) = path!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) {
|
||||
let mut file = BufReader::new(file);
|
||||
file.foreach_props(|key, val| {
|
||||
if key == "ro.build.version.sdk" {
|
||||
@@ -203,6 +266,7 @@ pub fn daemon_entry() {
|
||||
sdk_int,
|
||||
is_emulator,
|
||||
is_recovery,
|
||||
zygote_start_count: AtomicU32::new(1),
|
||||
..Default::default()
|
||||
};
|
||||
MAGISKD.set(magiskd).ok();
|
||||
@@ -238,43 +302,3 @@ fn check_data() -> bool {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_magiskd() -> &'static MagiskD {
|
||||
unsafe { MAGISKD.get().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
pub trait IpcRead {
|
||||
fn ipc_read_int(&mut self) -> io::Result<i32>;
|
||||
fn ipc_read_string(&mut self) -> io::Result<String>;
|
||||
}
|
||||
|
||||
impl<T: Read> IpcRead for T {
|
||||
fn ipc_read_int(&mut self) -> io::Result<i32> {
|
||||
let mut val: i32 = 0;
|
||||
self.read_pod(&mut val)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn ipc_read_string(&mut self) -> io::Result<String> {
|
||||
let len = self.ipc_read_int()?;
|
||||
let mut val = "".to_string();
|
||||
self.take(len as u64).read_to_string(&mut val)?;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IpcWrite {
|
||||
fn ipc_write_int(&mut self, val: i32) -> io::Result<()>;
|
||||
fn ipc_write_string(&mut self, val: &str) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: Write> IpcWrite for T {
|
||||
fn ipc_write_int(&mut self, val: i32) -> io::Result<()> {
|
||||
self.write_all(bytes_of(&val))
|
||||
}
|
||||
|
||||
fn ipc_write_string(&mut self, val: &str) -> io::Result<()> {
|
||||
self.ipc_write_int(val.len() as i32)?;
|
||||
self.write_all(val.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#![allow(improper_ctypes, improper_ctypes_definitions)]
|
||||
use crate::daemon::{IpcRead, IpcWrite, MagiskD, MAGISKD};
|
||||
use crate::daemon::{MagiskD, MAGISKD};
|
||||
use crate::ffi::{
|
||||
open_and_init_db, sqlite3, sqlite3_errstr, DbEntryKey, DbSettings, DbStatement, DbValues,
|
||||
MntNsMode, MultiuserMode, RootAccess,
|
||||
open_and_init_db, sqlite3, sqlite3_errstr, DbEntryKey, DbStatement, DbValues, MntNsMode,
|
||||
};
|
||||
use crate::socket::{IpcRead, IpcWrite};
|
||||
use base::{LoggedResult, ResultExt, Utf8CStr};
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
use std::ffi::c_void;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter};
|
||||
@@ -54,16 +56,33 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RootAccess {
|
||||
fn default() -> Self {
|
||||
RootAccess::AppsAndAdb
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct DbSettings {
|
||||
pub root_access: RootAccess,
|
||||
pub multiuser_mode: MultiuserMode,
|
||||
pub mnt_ns: MntNsMode,
|
||||
pub boot_count: i32,
|
||||
pub denylist: bool,
|
||||
pub zygisk: bool,
|
||||
}
|
||||
|
||||
impl Default for MultiuserMode {
|
||||
fn default() -> Self {
|
||||
MultiuserMode::OwnerOnly
|
||||
}
|
||||
#[repr(i32)]
|
||||
#[derive(Default, FromPrimitive)]
|
||||
pub enum RootAccess {
|
||||
Disabled,
|
||||
AppsOnly,
|
||||
AdbOnly,
|
||||
#[default]
|
||||
AppsAndAdb,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Default, FromPrimitive)]
|
||||
pub enum MultiuserMode {
|
||||
#[default]
|
||||
OwnerOnly,
|
||||
OwnerManaged,
|
||||
User,
|
||||
}
|
||||
|
||||
impl Default for MntNsMode {
|
||||
@@ -72,10 +91,6 @@ impl Default for MntNsMode {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_default_db_settings() -> DbSettings {
|
||||
DbSettings::default()
|
||||
}
|
||||
|
||||
impl DbEntryKey {
|
||||
fn to_str(self) -> &'static str {
|
||||
match self {
|
||||
@@ -103,8 +118,10 @@ impl SqlTable for DbSettings {
|
||||
}
|
||||
}
|
||||
match key {
|
||||
"root_access" => self.root_access = RootAccess { repr: value },
|
||||
"multiuser_mode" => self.multiuser_mode = MultiuserMode { repr: value },
|
||||
"root_access" => self.root_access = RootAccess::from_i32(value).unwrap_or_default(),
|
||||
"multiuser_mode" => {
|
||||
self.multiuser_mode = MultiuserMode::from_i32(value).unwrap_or_default()
|
||||
}
|
||||
"mnt_ns" => self.mnt_ns = MntNsMode { repr: value },
|
||||
"denylist" => self.denylist = value != 0,
|
||||
"zygisk" => self.zygisk = value != 0,
|
||||
@@ -121,7 +138,7 @@ unsafe impl Send for Sqlite3 {}
|
||||
type SqlBindCallback = Option<unsafe extern "C" fn(*mut c_void, i32, Pin<&mut DbStatement>) -> i32>;
|
||||
type SqlExecCallback = Option<unsafe extern "C" fn(*mut c_void, &[String], &DbValues)>;
|
||||
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
fn sql_exec_impl(
|
||||
db: *mut sqlite3,
|
||||
sql: &str,
|
||||
@@ -143,16 +160,18 @@ struct DbArgs<'a> {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn bind_arguments(v: *mut c_void, idx: i32, stmt: Pin<&mut DbStatement>) -> i32 {
|
||||
let args = &mut *(v as *mut DbArgs<'_>);
|
||||
if args.curr < args.args.len() {
|
||||
let arg = &args.args[args.curr];
|
||||
args.curr += 1;
|
||||
match *arg {
|
||||
Text(v) => stmt.bind_text(idx, v),
|
||||
Integer(v) => stmt.bind_int64(idx, v),
|
||||
unsafe {
|
||||
let args = &mut *(v as *mut DbArgs<'_>);
|
||||
if args.curr < args.args.len() {
|
||||
let arg = &args.args[args.curr];
|
||||
args.curr += 1;
|
||||
match *arg {
|
||||
Text(v) => stmt.bind_text(idx, v),
|
||||
Integer(v) => stmt.bind_int64(idx, v),
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +180,10 @@ unsafe extern "C" fn read_db_row<T: SqlTable>(
|
||||
columns: &[String],
|
||||
values: &DbValues,
|
||||
) {
|
||||
let table = &mut *(v as *mut T);
|
||||
table.on_row(columns, values);
|
||||
unsafe {
|
||||
let table = &mut *(v as *mut T);
|
||||
table.on_row(columns, values);
|
||||
}
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
@@ -172,10 +193,9 @@ impl MagiskD {
|
||||
let raw_db = open_and_init_db();
|
||||
*db = NonNull::new(raw_db).map(Sqlite3);
|
||||
}
|
||||
if let Some(ref mut db) = *db {
|
||||
f(db.0.as_ptr())
|
||||
} else {
|
||||
-1
|
||||
match *db {
|
||||
Some(ref mut db) => f(db.0.as_ptr()),
|
||||
_ => -1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,8 +249,8 @@ impl MagiskD {
|
||||
pub fn get_db_setting(&self, key: DbEntryKey) -> i32 {
|
||||
// Get default values
|
||||
let mut val = match key {
|
||||
DbEntryKey::RootAccess => RootAccess::default().repr,
|
||||
DbEntryKey::SuMultiuserMode => MultiuserMode::default().repr,
|
||||
DbEntryKey::RootAccess => RootAccess::default() as i32,
|
||||
DbEntryKey::SuMultiuserMode => MultiuserMode::default() as i32,
|
||||
DbEntryKey::SuMntNs => MntNsMode::default().repr,
|
||||
DbEntryKey::DenylistConfig => 0,
|
||||
DbEntryKey::ZygiskConfig => self.is_emulator as i32,
|
||||
@@ -285,7 +305,7 @@ impl MagiskD {
|
||||
fn db_exec_for_client(&self, fd: OwnedFd) -> LoggedResult<()> {
|
||||
let mut file = File::from(fd);
|
||||
let mut reader = BufReader::new(&mut file);
|
||||
let sql = reader.ipc_read_string()?;
|
||||
let sql: String = reader.read_decodable()?;
|
||||
let mut writer = BufWriter::new(&mut file);
|
||||
let mut output_fn = |columns: &[String], values: &DbValues| {
|
||||
let mut out = "".to_string();
|
||||
@@ -297,22 +317,14 @@ impl MagiskD {
|
||||
out.push('=');
|
||||
out.push_str(values.get_text(i as i32));
|
||||
}
|
||||
writer.ipc_write_string(&out).log().ok();
|
||||
writer.write_encodable(&out).log_ok();
|
||||
};
|
||||
self.db_exec_with_rows(&sql, &[], &mut output_fn);
|
||||
writer.ipc_write_string("").log()
|
||||
writer.write_encodable("").log()
|
||||
}
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
pub fn get_db_settings_for_cxx(&self, cfg: &mut DbSettings) -> bool {
|
||||
cfg.zygisk = self.is_emulator;
|
||||
self.db_exec_with_rows("SELECT * FROM settings", &[], cfg)
|
||||
.sql_result()
|
||||
.log()
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool {
|
||||
self.set_db_setting(key, value).log().is_ok()
|
||||
}
|
||||
@@ -324,7 +336,7 @@ impl MagiskD {
|
||||
}
|
||||
}
|
||||
|
||||
#[export_name = "sql_exec_rs"]
|
||||
#[unsafe(export_name = "sql_exec_rs")]
|
||||
unsafe extern "C" fn sql_exec_for_cxx(
|
||||
sql: &str,
|
||||
bind_callback: SqlBindCallback,
|
||||
@@ -332,14 +344,16 @@ unsafe extern "C" fn sql_exec_for_cxx(
|
||||
exec_callback: SqlExecCallback,
|
||||
exec_cookie: *mut c_void,
|
||||
) -> i32 {
|
||||
MAGISKD.get().unwrap_unchecked().with_db(|db| {
|
||||
sql_exec_impl(
|
||||
db,
|
||||
sql,
|
||||
bind_callback,
|
||||
bind_cookie,
|
||||
exec_callback,
|
||||
exec_cookie,
|
||||
)
|
||||
})
|
||||
unsafe {
|
||||
MAGISKD.get().unwrap_unchecked().with_db(|db| {
|
||||
sql_exec_impl(
|
||||
db,
|
||||
sql,
|
||||
bind_callback,
|
||||
bind_cookie,
|
||||
exec_callback,
|
||||
exec_cookie,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <base.hpp>
|
||||
#include <sqlite.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
#include "deny.hpp"
|
||||
|
||||
@@ -108,12 +109,11 @@ static bool proc_name_match(int pid, string_view name) {
|
||||
|
||||
bool proc_context_match(int pid, string_view context) {
|
||||
char buf[PATH_MAX];
|
||||
sprintf(buf, "/proc/%d/attr/current", pid);
|
||||
if (auto fp = open_file(buf, "re")) {
|
||||
fgets(buf, sizeof(buf), fp.get());
|
||||
if (str_starts(buf, context)) {
|
||||
return true;
|
||||
}
|
||||
char con[1024];
|
||||
|
||||
sprintf(buf, "/proc/%d", pid);
|
||||
if (lgetfilecon(buf, { con, sizeof(con) }) >= 0) {
|
||||
return str_starts(con, context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -370,7 +370,7 @@ int enable_deny() {
|
||||
|
||||
denylist_enforced = true;
|
||||
|
||||
if (!zygisk_enabled) {
|
||||
if (!MagiskD::Get().zygisk_enabled()) {
|
||||
if (new_daemon_thread(&logcat)) {
|
||||
denylist_enforced = false;
|
||||
return DenyResponse::ERROR;
|
||||
@@ -385,7 +385,7 @@ int enable_deny() {
|
||||
}
|
||||
}
|
||||
|
||||
MagiskD().set_db_setting(DbEntryKey::DenylistConfig, true);
|
||||
MagiskD::Get().set_db_setting(DbEntryKey::DenylistConfig, true);
|
||||
return DenyResponse::OK;
|
||||
}
|
||||
|
||||
@@ -393,13 +393,13 @@ int disable_deny() {
|
||||
if (denylist_enforced.exchange(false)) {
|
||||
LOGI("* Disable DenyList\n");
|
||||
}
|
||||
MagiskD().set_db_setting(DbEntryKey::DenylistConfig, false);
|
||||
MagiskD::Get().set_db_setting(DbEntryKey::DenylistConfig, false);
|
||||
return DenyResponse::OK;
|
||||
}
|
||||
|
||||
void initialize_denylist() {
|
||||
if (!denylist_enforced) {
|
||||
if (MagiskD().get_db_setting(DbEntryKey::DenylistConfig))
|
||||
if (MagiskD::Get().get_db_setting(DbEntryKey::DenylistConfig))
|
||||
enable_deny();
|
||||
}
|
||||
}
|
||||
@@ -429,3 +429,12 @@ bool is_deny_target(int uid, string_view process) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void update_deny_flags(int uid, rust::Str process, uint32_t &flags) {
|
||||
if (is_deny_target(uid, { process.begin(), process.end() })) {
|
||||
flags |= +ZygiskStateFlags::ProcessOnDenyList;
|
||||
}
|
||||
if (denylist_enforced) {
|
||||
flags |= +ZygiskStateFlags::DenyListEnforced;
|
||||
}
|
||||
}
|
||||
|
||||
13
native/src/core/derive/Cargo.toml
Normal file
13
native/src/core/derive/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "derive"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { workspace = true }
|
||||
quote = { workspace = true }
|
||||
proc-macro2 = { workspace = true }
|
||||
124
native/src/core/derive/decodable.rs
Normal file
124
native/src/core/derive/decodable.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam};
|
||||
|
||||
pub(crate) fn derive_decodable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = input.ident;
|
||||
|
||||
// Add a bound `T: Decodable` to every type parameter T.
|
||||
let mut generics = input.generics;
|
||||
for param in &mut generics.params {
|
||||
if let GenericParam::Type(ref mut type_param) = *param {
|
||||
type_param
|
||||
.bounds
|
||||
.push(parse_quote!(crate::socket::Decodable));
|
||||
}
|
||||
}
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let sum = gen_size_sum(&input.data);
|
||||
let encode = gen_encode(&input.data);
|
||||
let decode = gen_decode(&input.data);
|
||||
|
||||
let expanded = quote! {
|
||||
// The generated impl.
|
||||
impl #impl_generics crate::socket::Encodable for #name #ty_generics #where_clause {
|
||||
fn encoded_len(&self) -> usize {
|
||||
#sum
|
||||
}
|
||||
fn encode(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
|
||||
#encode
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl #impl_generics crate::socket::Decodable for #name #ty_generics #where_clause {
|
||||
fn decode(r: &mut impl std::io::Read) -> std::io::Result<Self> {
|
||||
let val = #decode;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
};
|
||||
proc_macro::TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
// Generate an expression to sum up the size of each field.
|
||||
fn gen_size_sum(data: &Data) -> TokenStream {
|
||||
match *data {
|
||||
Data::Struct(ref data) => {
|
||||
match data.fields {
|
||||
Fields::Named(ref fields) => {
|
||||
// Expands to an expression like
|
||||
//
|
||||
// 0 + self.x.encoded_len() + self.y.encoded_len() + self.z.encoded_len()
|
||||
let recurse = fields.named.iter().map(|f| {
|
||||
let name = &f.ident;
|
||||
quote_spanned! { f.span() =>
|
||||
crate::socket::Encodable::encoded_len(&self.#name)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
0 #(+ #recurse)*
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
Data::Enum(_) | Data::Union(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Generate an expression to encode each field.
|
||||
fn gen_encode(data: &Data) -> TokenStream {
|
||||
match *data {
|
||||
Data::Struct(ref data) => {
|
||||
match data.fields {
|
||||
Fields::Named(ref fields) => {
|
||||
// Expands to an expression like
|
||||
//
|
||||
// self.x.encode(w)?; self.y.encode(w)?; self.z.encode(w)?;
|
||||
let recurse = fields.named.iter().map(|f| {
|
||||
let name = &f.ident;
|
||||
quote_spanned! { f.span() =>
|
||||
crate::socket::Encodable::encode(&self.#name, w)?;
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
#(#recurse)*
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
Data::Enum(_) | Data::Union(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Generate an expression to decode each field.
|
||||
fn gen_decode(data: &Data) -> TokenStream {
|
||||
match *data {
|
||||
Data::Struct(ref data) => {
|
||||
match data.fields {
|
||||
Fields::Named(ref fields) => {
|
||||
// Expands to an expression like
|
||||
//
|
||||
// Self { x: Decodable::decode(r)?, y: Decodable::decode(r)?, }
|
||||
let recurse = fields.named.iter().map(|f| {
|
||||
let name = &f.ident;
|
||||
quote_spanned! { f.span() =>
|
||||
#name: crate::socket::Decodable::decode(r)?,
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
Self { #(#recurse)* }
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
Data::Enum(_) | Data::Union(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
8
native/src/core/derive/lib.rs
Normal file
8
native/src/core/derive/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
mod decodable;
|
||||
|
||||
#[proc_macro_derive(Decodable)]
|
||||
pub fn derive_decodable(input: TokenStream) -> TokenStream {
|
||||
decodable::derive_decodable(input)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <pthread.h>
|
||||
#include <poll.h>
|
||||
#include <string>
|
||||
@@ -7,7 +8,8 @@
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
#include "socket.hpp"
|
||||
#include <base.hpp>
|
||||
|
||||
#include "../core-rs.hpp"
|
||||
|
||||
#define AID_ROOT 0
|
||||
@@ -26,21 +28,56 @@ enum class RespondCode : int {
|
||||
END
|
||||
};
|
||||
|
||||
struct module_info {
|
||||
std::string name;
|
||||
int z32 = -1;
|
||||
#if defined(__LP64__)
|
||||
int z64 = -1;
|
||||
#endif
|
||||
};
|
||||
struct ModuleInfo;
|
||||
|
||||
extern bool zygisk_enabled;
|
||||
extern std::vector<module_info> *module_list;
|
||||
extern std::string native_bridge;
|
||||
|
||||
void reset_zygisk(bool restore);
|
||||
// Daemon
|
||||
int connect_daemon(int req, bool create = false);
|
||||
const char *get_magisk_tmp();
|
||||
void unlock_blocks();
|
||||
bool setup_magisk_env();
|
||||
bool check_key_combo();
|
||||
void restore_zygisk_prop();
|
||||
|
||||
// Sockets
|
||||
struct sock_cred : public ucred {
|
||||
std::string context;
|
||||
};
|
||||
|
||||
template<typename T> requires(std::is_trivially_copyable_v<T>)
|
||||
T read_any(int fd) {
|
||||
T val;
|
||||
if (xxread(fd, &val, sizeof(val)) != sizeof(val))
|
||||
return -1;
|
||||
return val;
|
||||
}
|
||||
|
||||
template<typename T> requires(std::is_trivially_copyable_v<T>)
|
||||
void write_any(int fd, T val) {
|
||||
if (fd < 0) return;
|
||||
xwrite(fd, &val, sizeof(val));
|
||||
}
|
||||
|
||||
bool get_client_cred(int fd, sock_cred *cred);
|
||||
static inline int read_int(int fd) { return read_any<int>(fd); }
|
||||
static inline void write_int(int fd, int val) { write_any(fd, val); }
|
||||
std::string read_string(int fd);
|
||||
bool read_string(int fd, std::string &str);
|
||||
void write_string(int fd, std::string_view str);
|
||||
|
||||
template<typename T> requires(std::is_trivially_copyable_v<T>)
|
||||
void write_vector(int fd, const std::vector<T> &vec) {
|
||||
write_int(fd, vec.size());
|
||||
xwrite(fd, vec.data(), vec.size() * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T> requires(std::is_trivially_copyable_v<T>)
|
||||
bool read_vector(int fd, std::vector<T> &vec) {
|
||||
int size = read_int(fd);
|
||||
vec.resize(size);
|
||||
return xread(fd, vec.data(), size * sizeof(T)) == size * sizeof(T);
|
||||
}
|
||||
|
||||
// Poll control
|
||||
using poll_callback = void(*)(pollfd*);
|
||||
@@ -54,20 +91,17 @@ void exec_task(std::function<void()> &&task);
|
||||
|
||||
// Daemon handlers
|
||||
void denylist_handler(int client, const sock_cred *cred);
|
||||
void su_daemon_handler(int client, const sock_cred *cred);
|
||||
void zygisk_handler(int client, const sock_cred *cred);
|
||||
|
||||
// Module stuffs
|
||||
void handle_modules();
|
||||
void load_modules();
|
||||
void disable_modules();
|
||||
void remove_modules();
|
||||
void exec_module_scripts(const char *stage);
|
||||
|
||||
// Scripting
|
||||
void install_apk(rust::Utf8CStr apk);
|
||||
void uninstall_pkg(rust::Utf8CStr pkg);
|
||||
void exec_common_scripts(rust::Utf8CStr stage);
|
||||
void exec_module_scripts(rust::Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list);
|
||||
void exec_script(const char *script);
|
||||
void exec_common_scripts(const char *stage);
|
||||
void exec_module_scripts(const char *stage, const std::vector<std::string_view> &modules);
|
||||
void clear_pkg(const char *pkg, int user_id);
|
||||
[[noreturn]] void install_module(const char *file);
|
||||
|
||||
@@ -78,3 +112,16 @@ void initialize_denylist();
|
||||
void scan_deny_apps();
|
||||
bool is_deny_target(int uid, std::string_view process);
|
||||
void revert_unmount(int pid = -1) noexcept;
|
||||
void update_deny_flags(int uid, rust::Str process, uint32_t &flags);
|
||||
|
||||
// MagiskSU
|
||||
void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode);
|
||||
void app_log(const SuAppRequest &info, SuPolicy policy, bool notify);
|
||||
void app_notify(const SuAppRequest &info, SuPolicy policy);
|
||||
int app_request(const SuAppRequest &info);
|
||||
|
||||
// Rust bindings
|
||||
static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }
|
||||
static inline rust::String resolve_preinit_dir_rs(rust::Utf8CStr base_dir) {
|
||||
return resolve_preinit_dir(base_dir.c_str());
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
const char *get_magisk_tmp();
|
||||
void install_apk(rust::Utf8CStr apk);
|
||||
void uninstall_pkg(rust::Utf8CStr pkg);
|
||||
|
||||
// Rust bindings
|
||||
static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }
|
||||
static inline rust::String resolve_preinit_dir_rs(rust::Utf8CStr base_dir) {
|
||||
return resolve_preinit_dir(base_dir.c_str());
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct sock_cred : public ucred {
|
||||
std::string context;
|
||||
};
|
||||
|
||||
bool get_client_cred(int fd, sock_cred *cred);
|
||||
std::vector<int> recv_fds(int sockfd);
|
||||
int recv_fd(int sockfd);
|
||||
int send_fds(int sockfd, const int *fds, int cnt);
|
||||
int send_fd(int sockfd, int fd);
|
||||
int read_int(int fd);
|
||||
int read_int_be(int fd);
|
||||
void write_int(int fd, int val);
|
||||
void write_int_be(int fd, int val);
|
||||
std::string read_string(int fd);
|
||||
bool read_string(int fd, std::string &str);
|
||||
void write_string(int fd, std::string_view str);
|
||||
|
||||
template<typename T> requires(std::is_trivially_copyable_v<T>)
|
||||
void write_vector(int fd, const std::vector<T> &vec) {
|
||||
write_int(fd, static_cast<int>(vec.size()));
|
||||
xwrite(fd, vec.data(), vec.size() * sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T> requires(std::is_trivially_copyable_v<T>)
|
||||
bool read_vector(int fd, std::vector<T> &vec) {
|
||||
int size = read_int(fd);
|
||||
if (size == -1) return false;
|
||||
vec.resize(size);
|
||||
return xread(fd, vec.data(), size * sizeof(T)) == size * sizeof(T);
|
||||
}
|
||||
@@ -2,15 +2,25 @@
|
||||
#![feature(try_blocks)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(fn_traits)]
|
||||
#![feature(unix_socket_ancillary_data)]
|
||||
#![feature(unix_socket_peek)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use base::Utf8CStr;
|
||||
use daemon::{daemon_entry, get_magiskd, MagiskD};
|
||||
use db::get_default_db_settings;
|
||||
use crate::ffi::SuRequest;
|
||||
use crate::socket::Encodable;
|
||||
use base::{libc, Utf8CStr};
|
||||
use cxx::{type_id, ExternType};
|
||||
use daemon::{daemon_entry, MagiskD};
|
||||
use derive::Decodable;
|
||||
use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging};
|
||||
use mount::{clean_mounts, find_preinit_device, revert_unmount, setup_mounts};
|
||||
use mount::{find_preinit_device, revert_unmount};
|
||||
use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
|
||||
use su::get_default_root_settings;
|
||||
use socket::{recv_fd, recv_fds, send_fd, send_fds};
|
||||
use std::fs::File;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::DerefMut;
|
||||
use std::os::fd::FromRawFd;
|
||||
use zygisk::zygisk_should_load_module;
|
||||
|
||||
#[path = "../include/consts.rs"]
|
||||
mod consts;
|
||||
@@ -20,8 +30,11 @@ mod logging;
|
||||
mod mount;
|
||||
mod package;
|
||||
mod resetprop;
|
||||
mod socket;
|
||||
mod su;
|
||||
mod zygisk;
|
||||
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
#[cxx::bridge]
|
||||
pub mod ffi {
|
||||
#[repr(i32)]
|
||||
@@ -49,6 +62,72 @@ pub mod ffi {
|
||||
END,
|
||||
}
|
||||
|
||||
enum DbEntryKey {
|
||||
RootAccess,
|
||||
SuMultiuserMode,
|
||||
SuMntNs,
|
||||
DenylistConfig,
|
||||
ZygiskConfig,
|
||||
BootloopCount,
|
||||
SuManager,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum MntNsMode {
|
||||
Global,
|
||||
Requester,
|
||||
Isolate,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum SuPolicy {
|
||||
Query,
|
||||
Deny,
|
||||
Allow,
|
||||
}
|
||||
|
||||
struct ModuleInfo {
|
||||
name: String,
|
||||
z32: i32,
|
||||
z64: i32,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum ZygiskRequest {
|
||||
GetInfo,
|
||||
ConnectCompanion,
|
||||
GetModDir,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
enum ZygiskStateFlags {
|
||||
ProcessGrantedRoot = 0x00000001,
|
||||
ProcessOnDenyList = 0x00000002,
|
||||
DenyListEnforced = 0x40000000,
|
||||
ProcessIsMagiskApp = 0x80000000,
|
||||
}
|
||||
|
||||
#[derive(Decodable)]
|
||||
struct SuRequest {
|
||||
target_uid: i32,
|
||||
target_pid: i32,
|
||||
login: bool,
|
||||
keep_env: bool,
|
||||
shell: String,
|
||||
command: String,
|
||||
context: String,
|
||||
gids: Vec<u32>,
|
||||
}
|
||||
|
||||
struct SuAppRequest<'a> {
|
||||
uid: i32,
|
||||
pid: i32,
|
||||
eval_uid: i32,
|
||||
mgr_pkg: &'a str,
|
||||
mgr_uid: i32,
|
||||
request: &'a SuRequest,
|
||||
}
|
||||
|
||||
extern "C++" {
|
||||
include!("include/resetprop.hpp");
|
||||
|
||||
@@ -67,152 +146,123 @@ pub mod ffi {
|
||||
#[namespace = "rust"]
|
||||
#[cxx_name = "Utf8CStr"]
|
||||
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
|
||||
#[cxx_name = "ucred"]
|
||||
type UCred = crate::UCred;
|
||||
|
||||
include!("include/daemon.hpp");
|
||||
include!("include/core.hpp");
|
||||
|
||||
#[cxx_name = "get_magisk_tmp_rs"]
|
||||
fn get_magisk_tmp() -> Utf8CStrRef<'static>;
|
||||
#[cxx_name = "resolve_preinit_dir_rs"]
|
||||
fn resolve_preinit_dir(base_dir: Utf8CStrRef) -> String;
|
||||
fn setup_magisk_env() -> bool;
|
||||
fn check_key_combo() -> bool;
|
||||
fn disable_modules();
|
||||
fn exec_common_scripts(stage: Utf8CStrRef);
|
||||
fn exec_module_scripts(state: Utf8CStrRef, modules: &Vec<ModuleInfo>);
|
||||
fn install_apk(apk: Utf8CStrRef);
|
||||
fn uninstall_pkg(apk: Utf8CStrRef);
|
||||
|
||||
fn update_deny_flags(uid: i32, process: &str, flags: &mut u32);
|
||||
fn initialize_denylist();
|
||||
fn restore_zygisk_prop();
|
||||
fn switch_mnt_ns(pid: i32) -> i32;
|
||||
}
|
||||
fn app_request(req: &SuAppRequest) -> i32;
|
||||
fn app_notify(req: &SuAppRequest, policy: SuPolicy);
|
||||
fn app_log(req: &SuAppRequest, policy: SuPolicy, notify: bool);
|
||||
fn exec_root_shell(client: i32, pid: i32, req: &mut SuRequest, mode: MntNsMode);
|
||||
|
||||
enum DbEntryKey {
|
||||
RootAccess,
|
||||
SuMultiuserMode,
|
||||
SuMntNs,
|
||||
DenylistConfig,
|
||||
ZygiskConfig,
|
||||
BootloopCount,
|
||||
SuManager,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum RootAccess {
|
||||
Disabled,
|
||||
AppsOnly,
|
||||
AdbOnly,
|
||||
AppsAndAdb,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum MultiuserMode {
|
||||
OwnerOnly,
|
||||
OwnerManaged,
|
||||
User,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum MntNsMode {
|
||||
Global,
|
||||
Requester,
|
||||
Isolate,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DbSettings {
|
||||
root_access: RootAccess,
|
||||
multiuser_mode: MultiuserMode,
|
||||
mnt_ns: MntNsMode,
|
||||
boot_count: i32,
|
||||
denylist: bool,
|
||||
zygisk: bool,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum SuPolicy {
|
||||
Query,
|
||||
Deny,
|
||||
Allow,
|
||||
}
|
||||
|
||||
struct RootSettings {
|
||||
policy: SuPolicy,
|
||||
log: bool,
|
||||
notify: bool,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("include/sqlite.hpp");
|
||||
|
||||
fn sqlite3_errstr(code: i32) -> *const c_char;
|
||||
|
||||
type sqlite3;
|
||||
fn open_and_init_db() -> *mut sqlite3;
|
||||
|
||||
type DbValues;
|
||||
type DbStatement;
|
||||
|
||||
fn sqlite3_errstr(code: i32) -> *const c_char;
|
||||
fn open_and_init_db() -> *mut sqlite3;
|
||||
fn get_int(self: &DbValues, index: i32) -> i32;
|
||||
#[cxx_name = "get_str"]
|
||||
fn get_text(self: &DbValues, index: i32) -> &str;
|
||||
|
||||
fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32;
|
||||
fn bind_int64(self: Pin<&mut DbStatement>, index: i32, val: i64) -> i32;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
fn rust_test_entry();
|
||||
fn android_logging();
|
||||
fn zygisk_logging();
|
||||
fn zygisk_close_logd();
|
||||
fn zygisk_get_logd() -> i32;
|
||||
fn setup_logfile();
|
||||
fn setup_mounts();
|
||||
fn clean_mounts();
|
||||
fn find_preinit_device() -> String;
|
||||
fn revert_unmount(pid: i32);
|
||||
fn zygisk_should_load_module(flags: u32) -> bool;
|
||||
unsafe fn persist_get_prop(name: Utf8CStrRef, prop_cb: Pin<&mut PropCb>);
|
||||
unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>);
|
||||
unsafe fn persist_delete_prop(name: Utf8CStrRef) -> bool;
|
||||
unsafe fn persist_set_prop(name: Utf8CStrRef, value: Utf8CStrRef) -> bool;
|
||||
fn send_fd(socket: i32, fd: i32) -> bool;
|
||||
fn send_fds(socket: i32, fds: &[i32]) -> bool;
|
||||
fn recv_fd(socket: i32) -> i32;
|
||||
fn recv_fds(socket: i32) -> Vec<i32>;
|
||||
unsafe fn write_to_fd(self: &SuRequest, fd: i32);
|
||||
|
||||
#[namespace = "rust"]
|
||||
fn daemon_entry();
|
||||
}
|
||||
|
||||
// Default constructors
|
||||
extern "Rust" {
|
||||
#[Self = SuRequest]
|
||||
#[cxx_name = "New"]
|
||||
fn default() -> SuRequest;
|
||||
}
|
||||
|
||||
// FFI for MagiskD
|
||||
extern "Rust" {
|
||||
type MagiskD;
|
||||
fn is_recovery(&self) -> bool;
|
||||
fn sdk_int(&self) -> i32;
|
||||
fn zygisk_enabled(&self) -> bool;
|
||||
fn boot_stage_handler(&self, client: i32, code: i32);
|
||||
fn preserve_stub_apk(&self);
|
||||
fn zygisk_handler(&self, client: i32);
|
||||
fn zygisk_reset(&self, restore: bool);
|
||||
fn prune_su_access(&self);
|
||||
fn uid_granted_root(&self, mut uid: i32) -> bool;
|
||||
fn su_daemon_handler(&self, client: i32, cred: &UCred);
|
||||
#[cxx_name = "get_manager"]
|
||||
unsafe fn get_manager_for_cxx(&self, user: i32, ptr: *mut CxxString, install: bool) -> i32;
|
||||
|
||||
#[cxx_name = "get_db_settings"]
|
||||
fn get_db_settings_for_cxx(&self, cfg: &mut DbSettings) -> bool;
|
||||
fn get_db_setting(&self, key: DbEntryKey) -> i32;
|
||||
#[cxx_name = "set_db_setting"]
|
||||
fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool;
|
||||
#[cxx_name = "db_exec"]
|
||||
fn db_exec_for_cxx(&self, client_fd: i32);
|
||||
#[cxx_name = "get_root_settings"]
|
||||
fn get_root_settings_for_cxx(&self, uid: i32, settings: &mut RootSettings) -> bool;
|
||||
|
||||
#[cxx_name = "DbSettings"]
|
||||
fn get_default_db_settings() -> DbSettings;
|
||||
#[cxx_name = "RootSettings"]
|
||||
fn get_default_root_settings() -> RootSettings;
|
||||
#[cxx_name = "MagiskD"]
|
||||
fn get_magiskd() -> &'static MagiskD;
|
||||
#[Self = MagiskD]
|
||||
#[cxx_name = "Get"]
|
||||
fn get() -> &'static MagiskD;
|
||||
}
|
||||
unsafe extern "C++" {
|
||||
#[allow(dead_code)]
|
||||
fn reboot(self: &MagiskD);
|
||||
fn post_fs_data(self: &MagiskD) -> bool;
|
||||
fn late_start(self: &MagiskD);
|
||||
fn boot_complete(self: &MagiskD);
|
||||
fn handle_modules(self: &MagiskD) -> Vec<ModuleInfo>;
|
||||
}
|
||||
}
|
||||
|
||||
fn rust_test_entry() {}
|
||||
#[repr(transparent)]
|
||||
pub struct UCred(pub libc::ucred);
|
||||
|
||||
unsafe impl ExternType for UCred {
|
||||
type Id = type_id!("ucred");
|
||||
type Kind = cxx::kind::Trivial;
|
||||
}
|
||||
|
||||
impl SuRequest {
|
||||
unsafe fn write_to_fd(&self, fd: i32) {
|
||||
unsafe {
|
||||
let mut w = ManuallyDrop::new(File::from_raw_fd(fd));
|
||||
self.encode(w.deref_mut()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_prop(name: &Utf8CStr, persist: bool) -> String {
|
||||
unsafe { ffi::get_prop_rs(name.as_ptr(), persist) }
|
||||
|
||||
@@ -6,8 +6,8 @@ use base::libc::{
|
||||
timespec, tm, O_CLOEXEC, O_RDWR, O_WRONLY, PIPE_BUF, SIGPIPE, SIG_BLOCK, SIG_SETMASK,
|
||||
};
|
||||
use base::{
|
||||
const_format::concatcp, libc, raw_cstr, FsPathBuf, LogLevel, Logger, ReadExt, SharedFd,
|
||||
Utf8CStr, Utf8CStrBuf, Utf8CStrBufArr, Utf8CStrWrite, LOGGER,
|
||||
const_format::concatcp, cstr_buf, libc, raw_cstr, FsPathBuf, LogLevel, Logger, ReadExt,
|
||||
Utf8CStr, Utf8CStrBuf, WriteExt, LOGGER,
|
||||
};
|
||||
use bytemuck::{bytes_of, write_zeroes, Pod, Zeroable};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
@@ -18,10 +18,10 @@ use std::fmt::Write as FmtWrite;
|
||||
use std::fs::File;
|
||||
use std::io::{IoSlice, Read, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::fd::{FromRawFd, OwnedFd, RawFd};
|
||||
use std::os::fd::{FromRawFd, RawFd};
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::{fs, io};
|
||||
|
||||
@@ -42,7 +42,7 @@ enum ALogPriority {
|
||||
|
||||
type ThreadEntry = extern "C" fn(*mut c_void) -> *mut c_void;
|
||||
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
fn __android_log_write(prio: i32, tag: *const c_char, msg: *const c_char);
|
||||
fn strftime(buf: *mut c_char, len: usize, fmt: *const c_char, tm: *const tm) -> usize;
|
||||
fn new_daemon_thread(entry: ThreadEntry, arg: *mut c_void);
|
||||
@@ -114,7 +114,7 @@ struct LogMeta {
|
||||
|
||||
const MAX_MSG_LEN: usize = PIPE_BUF - size_of::<LogMeta>();
|
||||
|
||||
fn write_log_to_pipe(logd: &mut File, prio: i32, msg: &Utf8CStr) -> io::Result<usize> {
|
||||
fn write_log_to_pipe(mut logd: &File, prio: i32, msg: &Utf8CStr) -> io::Result<usize> {
|
||||
// Truncate message if needed
|
||||
let len = min(MAX_MSG_LEN, msg.len());
|
||||
let msg = &msg.as_bytes()[..len];
|
||||
@@ -130,29 +130,28 @@ fn write_log_to_pipe(logd: &mut File, prio: i32, msg: &Utf8CStr) -> io::Result<u
|
||||
let io2 = IoSlice::new(msg);
|
||||
let result = logd.write_vectored(&[io1, io2]);
|
||||
if let Err(ref e) = result {
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
buf.write_fmt(format_args_nl!("Cannot write_log_to_pipe: {}", e))
|
||||
let mut buf = cstr_buf::default();
|
||||
buf.write_fmt(format_args!("Cannot write_log_to_pipe: {}", e))
|
||||
.ok();
|
||||
android_log_write(LogLevel::Error, &buf);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
static MAGISK_LOGD_FD: Mutex<SharedFd> = Mutex::new(SharedFd::new());
|
||||
static MAGISK_LOGD_FD: Mutex<Option<Arc<File>>> = Mutex::new(None);
|
||||
|
||||
fn with_logd_fd<F: FnOnce(&mut File) -> io::Result<()>>(f: F) {
|
||||
fn with_logd_fd<R, F: FnOnce(&File) -> io::Result<R>>(f: F) {
|
||||
let fd = MAGISK_LOGD_FD.lock().unwrap().clone();
|
||||
// SAFETY: writing less than PIPE_BUF bytes is guaranteed to be atomic on Linux
|
||||
if let Some(mut logd) = unsafe { fd.as_file() } {
|
||||
if f(&mut logd).is_err() {
|
||||
if let Some(logd) = fd {
|
||||
if f(&logd).is_err() {
|
||||
// If any error occurs, shut down the logd pipe
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = SharedFd::default();
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn magisk_log_to_pipe(prio: i32, msg: &Utf8CStr) {
|
||||
with_logd_fd(|logd| write_log_to_pipe(logd, prio, msg).map(|_| ()));
|
||||
with_logd_fd(|logd| write_log_to_pipe(logd, prio, msg));
|
||||
}
|
||||
|
||||
// SAFETY: zygisk client code runs single threaded, so no need to prevent data race
|
||||
@@ -181,10 +180,7 @@ pub fn zygisk_get_logd() -> i32 {
|
||||
let mut fd = ZYGISK_LOGD.load(Ordering::Relaxed);
|
||||
if fd < 0 {
|
||||
android_logging();
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let path = FsPathBuf::new(&mut buf)
|
||||
.join(get_magisk_tmp())
|
||||
.join(LOG_PIPE);
|
||||
let path = FsPathBuf::default().join(get_magisk_tmp()).join(LOG_PIPE);
|
||||
// Open as RW as sometimes it may block
|
||||
fd = unsafe { libc::open(path.as_ptr(), O_RDWR | O_CLOEXEC) };
|
||||
if fd >= 0 {
|
||||
@@ -217,8 +213,8 @@ fn zygisk_log_to_pipe(prio: i32, msg: &Utf8CStr) {
|
||||
pthread_sigmask(SIG_BLOCK, &mask, &mut orig_mask);
|
||||
}
|
||||
|
||||
let mut logd = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
|
||||
let result = write_log_to_pipe(&mut logd, prio, msg);
|
||||
let logd = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
|
||||
let result = write_log_to_pipe(&logd, prio, msg);
|
||||
|
||||
// Consume SIGPIPE if exists, then restore mask
|
||||
unsafe {
|
||||
@@ -270,7 +266,7 @@ extern "C" fn logfile_writer(arg: *mut c_void) -> *mut c_void {
|
||||
|
||||
let mut meta = LogMeta::zeroed();
|
||||
let mut msg_buf = [0u8; MAX_MSG_LEN];
|
||||
let mut aux = Utf8CStrBufArr::<64>::new();
|
||||
let mut aux = cstr_buf::new::<64>();
|
||||
|
||||
loop {
|
||||
// Read request
|
||||
@@ -320,12 +316,7 @@ extern "C" fn logfile_writer(arg: *mut c_void) -> *mut c_void {
|
||||
if localtime_r(&secs, &mut tm).is_null() {
|
||||
continue;
|
||||
}
|
||||
let len = strftime(
|
||||
aux.mut_buf().as_mut_ptr().cast(),
|
||||
aux.capacity(),
|
||||
raw_cstr!("%m-%d %T"),
|
||||
&tm,
|
||||
);
|
||||
let len = strftime(aux.as_mut_ptr(), aux.capacity(), raw_cstr!("%m-%d %T"), &tm);
|
||||
aux.set_len(len);
|
||||
aux.write_fmt(format_args!(
|
||||
".{:03} {:5} {:5} {} : ",
|
||||
@@ -347,34 +338,31 @@ extern "C" fn logfile_writer(arg: *mut c_void) -> *mut c_void {
|
||||
|
||||
writer_loop(arg as RawFd).ok();
|
||||
// If any error occurs, shut down the logd pipe
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = SharedFd::default();
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = None;
|
||||
null_mut()
|
||||
}
|
||||
|
||||
pub fn setup_logfile() {
|
||||
with_logd_fd(|logd| {
|
||||
with_logd_fd(|mut logd| {
|
||||
let meta = LogMeta {
|
||||
prio: -1,
|
||||
len: 0,
|
||||
pid: 0,
|
||||
tid: 0,
|
||||
};
|
||||
logd.write_all(bytes_of(&meta))
|
||||
(&mut logd).write_pod(&meta)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn start_log_daemon() {
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let path = FsPathBuf::new(&mut buf)
|
||||
.join(get_magisk_tmp())
|
||||
.join(LOG_PIPE);
|
||||
let path = FsPathBuf::default().join(get_magisk_tmp()).join(LOG_PIPE);
|
||||
|
||||
unsafe {
|
||||
libc::mkfifo(path.as_ptr(), 0o666);
|
||||
libc::chown(path.as_ptr(), 0, 0);
|
||||
let read = libc::open(path.as_ptr(), O_RDWR | O_CLOEXEC);
|
||||
let write = libc::open(path.as_ptr(), O_WRONLY | O_CLOEXEC);
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = SharedFd::from(OwnedFd::from_raw_fd(write));
|
||||
*MAGISK_LOGD_FD.lock().unwrap() = Some(Arc::new(File::from_raw_fd(write)));
|
||||
new_daemon_thread(logfile_writer, read as *mut c_void);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ bool dir_node::prepare() {
|
||||
return upgrade_to_tmpfs;
|
||||
}
|
||||
|
||||
void dir_node::collect_module_files(const char *module, int dfd) {
|
||||
void dir_node::collect_module_files(std::string_view module, int dfd) {
|
||||
auto dir = xopen_dir(xopenat(dfd, name().data(), O_RDONLY | O_CLOEXEC));
|
||||
if (!dir)
|
||||
return;
|
||||
@@ -149,7 +149,9 @@ void module_node::mount() {
|
||||
VLOGD("delete", "null", node_path().data());
|
||||
return;
|
||||
}
|
||||
std::string path = module + (parent()->root()->prefix + node_path());
|
||||
std::string path{module.begin(), module.end()};
|
||||
path += parent()->root()->prefix;
|
||||
path += node_path();
|
||||
string mnt_src = module_mnt + path;
|
||||
{
|
||||
string src = MODULEROOT "/" + path;
|
||||
@@ -231,10 +233,21 @@ private:
|
||||
};
|
||||
|
||||
static void inject_magisk_bins(root_node *system) {
|
||||
auto bin = system->get_child<inter_node>("bin");
|
||||
dir_node* bin = system->get_child<inter_node>("bin");
|
||||
if (!bin) {
|
||||
bin = new inter_node("bin");
|
||||
system->insert(bin);
|
||||
struct stat st{};
|
||||
bin = system;
|
||||
for (auto &item: split(getenv("PATH"), ":")) {
|
||||
item.erase(0, item.starts_with("/system/") ? 8 : 1);
|
||||
auto system_path = "/system/" + item;
|
||||
if (stat(system_path.data(), &st) == 0 && st.st_mode & S_IXOTH) {
|
||||
for (const auto &dir: split(item, "/")) {
|
||||
auto node = bin->get_child<inter_node>(dir);
|
||||
bin = node ? node : bin->emplace<inter_node>(dir, dir.data());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert binaries
|
||||
@@ -267,9 +280,7 @@ static void inject_zygisk_libs(root_node *system) {
|
||||
}
|
||||
}
|
||||
|
||||
vector<module_info> *module_list;
|
||||
|
||||
void load_modules() {
|
||||
static void load_modules(bool zygisk_enabled, const rust::Vec<ModuleInfo> &module_list) {
|
||||
node_entry::module_mnt = get_magisk_tmp() + "/"s MODULEMNT "/";
|
||||
|
||||
auto root = make_unique<root_node>("");
|
||||
@@ -278,14 +289,14 @@ void load_modules() {
|
||||
|
||||
char buf[4096];
|
||||
LOGI("* Loading modules\n");
|
||||
for (const auto &m : *module_list) {
|
||||
const char *module = m.name.data();
|
||||
char *b = buf + ssprintf(buf, sizeof(buf), "%s/" MODULEMNT "/%s/", get_magisk_tmp(), module);
|
||||
for (const auto &m : module_list) {
|
||||
char *b = buf + ssprintf(buf, sizeof(buf), "%s/" MODULEMNT "/%.*s/",
|
||||
get_magisk_tmp(), (int) m.name.size(), m.name.data());
|
||||
|
||||
// Read props
|
||||
strcpy(b, "system.prop");
|
||||
if (access(buf, F_OK) == 0) {
|
||||
LOGI("%s: loading [system.prop]\n", module);
|
||||
LOGI("%.*s: loading [system.prop]\n", (int) m.name.size(), m.name.data());
|
||||
// Do NOT go through property service as it could cause boot lock
|
||||
load_prop_file(buf, true);
|
||||
}
|
||||
@@ -300,10 +311,10 @@ void load_modules() {
|
||||
if (access(buf, F_OK) != 0)
|
||||
continue;
|
||||
|
||||
LOGI("%s: loading mount files\n", module);
|
||||
LOGI("%.*s: loading mount files\n", (int) m.name.size(), m.name.data());
|
||||
b[-1] = '\0';
|
||||
int fd = xopen(buf, O_RDONLY | O_CLOEXEC);
|
||||
system->collect_module_files(module, fd);
|
||||
system->collect_module_files({ m.name.begin(), m.name.end() }, fd);
|
||||
close(fd);
|
||||
}
|
||||
if (get_magisk_tmp() != "/sbin"sv || !str_contains(getenv("PATH") ?: "", "/sbin")) {
|
||||
@@ -341,9 +352,6 @@ void load_modules() {
|
||||
root->prepare();
|
||||
root->mount();
|
||||
}
|
||||
|
||||
// cleanup mounts
|
||||
clean_mounts();
|
||||
}
|
||||
|
||||
/************************
|
||||
@@ -392,8 +400,9 @@ static void foreach_module(Func fn) {
|
||||
}
|
||||
}
|
||||
|
||||
static void collect_modules(bool open_zygisk) {
|
||||
foreach_module([=](int dfd, dirent *entry, int modfd) {
|
||||
static rust::Vec<ModuleInfo> collect_modules(bool zygisk_enabled, bool open_zygisk) {
|
||||
rust::Vec<ModuleInfo> modules;
|
||||
foreach_module([&](int dfd, dirent *entry, int modfd) {
|
||||
if (faccessat(modfd, "remove", F_OK, 0) == 0) {
|
||||
LOGI("%s: remove\n", entry->d_name);
|
||||
auto uninstaller = MODULEROOT + "/"s + entry->d_name + "/uninstall.sh";
|
||||
@@ -407,7 +416,7 @@ static void collect_modules(bool open_zygisk) {
|
||||
if (faccessat(modfd, "disable", F_OK, 0) == 0)
|
||||
return;
|
||||
|
||||
module_info info;
|
||||
ModuleInfo info{{}, -1, -1};
|
||||
if (zygisk_enabled) {
|
||||
// Riru and its modules are not compatible with zygisk
|
||||
if (entry->d_name == "riru-core"sv || faccessat(modfd, "riru", F_OK, 0) == 0) {
|
||||
@@ -417,11 +426,13 @@ static void collect_modules(bool open_zygisk) {
|
||||
if (open_zygisk) {
|
||||
#if defined(__arm__)
|
||||
info.z32 = openat(modfd, "zygisk/armeabi-v7a.so", O_RDONLY | O_CLOEXEC);
|
||||
info.z64 = -1;
|
||||
#elif defined(__aarch64__)
|
||||
info.z32 = openat(modfd, "zygisk/armeabi-v7a.so", O_RDONLY | O_CLOEXEC);
|
||||
info.z64 = openat(modfd, "zygisk/arm64-v8a.so", O_RDONLY | O_CLOEXEC);
|
||||
#elif defined(__i386__)
|
||||
info.z32 = openat(modfd, "zygisk/x86.so", O_RDONLY | O_CLOEXEC);
|
||||
info.z64 = -1;
|
||||
#elif defined(__x86_64__)
|
||||
info.z32 = openat(modfd, "zygisk/x86.so", O_RDONLY | O_CLOEXEC);
|
||||
info.z64 = openat(modfd, "zygisk/x86_64.so", O_RDONLY | O_CLOEXEC);
|
||||
@@ -441,7 +452,7 @@ static void collect_modules(bool open_zygisk) {
|
||||
}
|
||||
}
|
||||
info.name = entry->d_name;
|
||||
module_list->push_back(info);
|
||||
modules.push_back(std::move(info));
|
||||
});
|
||||
if (zygisk_enabled) {
|
||||
bool use_memfd = true;
|
||||
@@ -461,23 +472,22 @@ static void collect_modules(bool open_zygisk) {
|
||||
}
|
||||
return fd;
|
||||
};
|
||||
std::for_each(module_list->begin(), module_list->end(), [&](module_info &info) {
|
||||
std::for_each(modules.begin(),modules.end(), [&](ModuleInfo &info) {
|
||||
info.z32 = convert_to_memfd(info.z32);
|
||||
#if defined(__LP64__)
|
||||
info.z64 = convert_to_memfd(info.z64);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
void handle_modules() {
|
||||
rust::Vec<ModuleInfo> MagiskD::handle_modules() const noexcept {
|
||||
bool zygisk = zygisk_enabled();
|
||||
prepare_modules();
|
||||
collect_modules(false);
|
||||
exec_module_scripts("post-fs-data");
|
||||
|
||||
exec_module_scripts("post-fs-data", collect_modules(zygisk, false));
|
||||
// Recollect modules (module scripts could remove itself)
|
||||
module_list->clear();
|
||||
collect_modules(true);
|
||||
auto list = collect_modules(zygisk, true);
|
||||
load_modules(zygisk, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
static int check_rules_dir(char *buf, size_t sz) {
|
||||
@@ -517,10 +527,3 @@ void remove_modules() {
|
||||
});
|
||||
rm_rf(MODULEROOT);
|
||||
}
|
||||
|
||||
void exec_module_scripts(const char *stage) {
|
||||
vector<string_view> module_names;
|
||||
std::transform(module_list->begin(), module_list->end(), std::back_inserter(module_names),
|
||||
[](const module_info &info) -> string_view { return info.name; });
|
||||
exec_module_scripts(stage, module_names);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ use num_traits::AsPrimitive;
|
||||
|
||||
use base::libc::{c_uint, dev_t};
|
||||
use base::{
|
||||
cstr, debug, info, libc, parse_mount_info, raw_cstr, warn, FsPath, FsPathBuf, LibcReturn,
|
||||
LoggedResult, MountInfo, ResultExt, Utf8CStr, Utf8CStrBufArr,
|
||||
cstr, cstr_buf, debug, info, libc, parse_mount_info, raw_cstr, warn, FsPath, FsPathBuf,
|
||||
LibcReturn, LoggedResult, MountInfo, ResultExt, Utf8CStr,
|
||||
};
|
||||
|
||||
use crate::consts::{MODULEMNT, MODULEROOT, PREINITDEV, PREINITMIRR, WORKERDIR};
|
||||
@@ -20,13 +20,9 @@ pub fn setup_mounts() {
|
||||
info!("* Setup internal mounts");
|
||||
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
|
||||
// Mount preinit directory
|
||||
let mut dev_buf = Utf8CStrBufArr::<64>::new();
|
||||
let dev_path = FsPathBuf::new(&mut dev_buf)
|
||||
.join(magisk_tmp)
|
||||
.join(PREINITDEV);
|
||||
let dev_path = FsPathBuf::<64>::new().join(magisk_tmp).join(PREINITDEV);
|
||||
let mut linked = false;
|
||||
if let Ok(attr) = dev_path.get_attr() {
|
||||
if attr.st.st_mode & libc::S_IFMT as c_uint == libc::S_IFBLK.as_() {
|
||||
@@ -36,7 +32,7 @@ pub fn setup_mounts() {
|
||||
// What we do instead is to scan through the current mountinfo and find a pre-existing
|
||||
// mount point mounting our desired partition, and then bind mount the target folder.
|
||||
let preinit_dev = attr.st.st_rdev;
|
||||
let mnt_path = FsPathBuf::new(&mut buf).join(magisk_tmp).join(PREINITMIRR);
|
||||
let mnt_path = FsPathBuf::default().join(magisk_tmp).join(PREINITMIRR);
|
||||
for info in parse_mount_info("self") {
|
||||
if info.root == "/" && info.device == preinit_dev {
|
||||
if !info.fs_option.split(',').any(|s| s == "rw") {
|
||||
@@ -49,7 +45,7 @@ pub fn setup_mounts() {
|
||||
let preinit_dir = Utf8CStr::from_string(&mut preinit_dir);
|
||||
let r: LoggedResult<()> = try {
|
||||
FsPath::from(preinit_dir).mkdir(0o700)?;
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let mut buf = cstr_buf::default();
|
||||
if mnt_path.parent(&mut buf) {
|
||||
FsPath::from(&buf).mkdirs(0o755)?;
|
||||
}
|
||||
@@ -74,7 +70,7 @@ pub fn setup_mounts() {
|
||||
}
|
||||
|
||||
// Bind remount module root to clear nosuid
|
||||
let module_mnt = FsPathBuf::new(&mut buf).join(magisk_tmp).join(MODULEMNT);
|
||||
let module_mnt = FsPathBuf::default().join(magisk_tmp).join(MODULEMNT);
|
||||
let _: LoggedResult<()> = try {
|
||||
module_mnt.mkdir(0o755)?;
|
||||
unsafe {
|
||||
@@ -101,20 +97,15 @@ pub fn setup_mounts() {
|
||||
pub fn clean_mounts() {
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
|
||||
let module_mnt = FsPathBuf::new(&mut buf).join(magisk_tmp).join(MODULEMNT);
|
||||
let mut module_mnt = FsPathBuf::default().join(magisk_tmp).join(MODULEMNT);
|
||||
let _: LoggedResult<()> = try {
|
||||
unsafe {
|
||||
libc::umount2(
|
||||
module_mnt.as_ptr(),
|
||||
libc::MNT_DETACH,
|
||||
)
|
||||
.as_os_err()?;
|
||||
libc::umount2(module_mnt.as_ptr(), libc::MNT_DETACH).as_os_err()?;
|
||||
}
|
||||
};
|
||||
|
||||
let worker_dir = FsPathBuf::new(&mut buf).join(magisk_tmp).join(WORKERDIR);
|
||||
module_mnt.clear();
|
||||
let worker_dir = module_mnt.join(magisk_tmp).join(WORKERDIR);
|
||||
let _: LoggedResult<()> = try {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
@@ -125,11 +116,7 @@ pub fn clean_mounts() {
|
||||
ptr::null(),
|
||||
)
|
||||
.as_os_err()?;
|
||||
libc::umount2(
|
||||
worker_dir.as_ptr(),
|
||||
libc::MNT_DETACH,
|
||||
)
|
||||
.as_os_err()?;
|
||||
libc::umount2(worker_dir.as_ptr(), libc::MNT_DETACH).as_os_err()?;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -226,12 +213,11 @@ pub fn find_preinit_device() -> String {
|
||||
&& let Ok(tmp) = std::env::var("MAGISKTMP")
|
||||
&& !tmp.is_empty()
|
||||
{
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let mirror_dir = FsPathBuf::new(&mut buf).join(&tmp).join(PREINITMIRR);
|
||||
let mut mirror_dir = FsPathBuf::default().join(&tmp).join(PREINITMIRR);
|
||||
let preinit_dir = FsPath::from(Utf8CStr::from_string(&mut preinit_dir));
|
||||
let _: LoggedResult<()> = try {
|
||||
preinit_dir.mkdirs(0o700)?;
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let mut buf = cstr_buf::default();
|
||||
if mirror_dir.parent(&mut buf) {
|
||||
FsPath::from(&buf).mkdirs(0o755)?;
|
||||
}
|
||||
@@ -244,7 +230,8 @@ pub fn find_preinit_device() -> String {
|
||||
}
|
||||
};
|
||||
if std::env::var_os("MAKEDEV").is_some() {
|
||||
let dev_path = FsPathBuf::new(&mut buf).join(&tmp).join(PREINITDEV);
|
||||
mirror_dir.clear();
|
||||
let dev_path = mirror_dir.join(&tmp).join(PREINITDEV);
|
||||
unsafe {
|
||||
libc::mknod(
|
||||
dev_path.as_ptr(),
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
**************/
|
||||
|
||||
// Traverse through module directories to generate a tree of module files
|
||||
void collect_module_files(const char *module, int dfd);
|
||||
void collect_module_files(std::string_view module, int dfd);
|
||||
|
||||
// Traverse through the real filesystem and prepare the tree for magic mount.
|
||||
// Return true to indicate that this node needs to be upgraded to tmpfs_node.
|
||||
@@ -279,16 +279,16 @@ public:
|
||||
|
||||
class module_node : public node_entry {
|
||||
public:
|
||||
module_node(const char *module, dirent *entry)
|
||||
module_node(std::string_view module, dirent *entry)
|
||||
: node_entry(entry->d_name, entry->d_type, this), module(module) {}
|
||||
|
||||
module_node(node_entry *node, const char *module) : node_entry(this), module(module) {
|
||||
module_node(node_entry *node, std::string_view module) : node_entry(this), module(module) {
|
||||
node_entry::consume(node);
|
||||
}
|
||||
|
||||
void mount() override;
|
||||
private:
|
||||
const char *module;
|
||||
std::string_view module;
|
||||
};
|
||||
|
||||
// Don't create tmpfs_node before prepare
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::consts::{APP_PACKAGE_NAME, MAGISK_VER_CODE};
|
||||
use crate::daemon::{to_app_id, MagiskD, AID_USER_OFFSET};
|
||||
use crate::daemon::{to_app_id, MagiskD, AID_APP_END, AID_APP_START, AID_USER_OFFSET};
|
||||
use crate::ffi::{get_magisk_tmp, install_apk, uninstall_pkg, DbEntryKey};
|
||||
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
|
||||
use base::WalkResult::{Abort, Continue, Skip};
|
||||
use base::{
|
||||
cstr, error, fd_get_attr, open_fd, warn, BufReadExt, Directory, FsPath, FsPathBuf,
|
||||
LoggedResult, ReadExt, ResultExt, Utf8CStrBuf, Utf8CStrBufArr,
|
||||
cstr, cstr_buf, error, fd_get_attr, open_fd, warn, BufReadExt, Directory, FsPath, FsPathBuf,
|
||||
LoggedResult, ReadExt, ResultExt, Utf8CStrBuf,
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
use cxx::CxxString;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
@@ -51,7 +52,7 @@ macro_rules! bad_apk {
|
||||
* within the APK v2 signature block.
|
||||
*/
|
||||
fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
fn inner(apk: &mut File, version: i32) -> io::Result<Vec<u8>> {
|
||||
let res: io::Result<Vec<u8>> = try {
|
||||
let mut u32_val = 0u32;
|
||||
let mut u64_val = 0u64;
|
||||
|
||||
@@ -70,7 +71,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
if i == 0xffff {
|
||||
return Err(bad_apk!("invalid APK format"));
|
||||
Err(bad_apk!("invalid APK format"))?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +98,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
}
|
||||
});
|
||||
if version > apk_ver {
|
||||
return Err(bad_apk!("APK version too low"));
|
||||
Err(bad_apk!("APK version too low"))?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
let mut magic = [0u8; 16];
|
||||
apk.read_exact(&mut magic)?;
|
||||
if magic != APK_SIGNING_BLOCK_MAGIC {
|
||||
return Err(bad_apk!("invalid signing block magic"));
|
||||
Err(bad_apk!("invalid signing block magic"))?;
|
||||
}
|
||||
let mut signing_blk_sz = 0u64;
|
||||
apk.seek(SeekFrom::Current(
|
||||
@@ -115,14 +116,14 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
))?;
|
||||
apk.read_pod(&mut signing_blk_sz)?;
|
||||
if signing_blk_sz != u64_val {
|
||||
return Err(bad_apk!("invalid signing block size"));
|
||||
Err(bad_apk!("invalid signing block size"))?;
|
||||
}
|
||||
|
||||
// Finally, we are now at the beginning of the id-value pair sequence
|
||||
loop {
|
||||
apk.read_pod(&mut u64_val)?; // id-value pair length
|
||||
if u64_val == signing_blk_sz {
|
||||
break;
|
||||
Err(bad_apk!("cannot find certificate"))?;
|
||||
}
|
||||
|
||||
let mut id = 0u32;
|
||||
@@ -139,7 +140,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
|
||||
let mut cert = vec![0; u32_val as usize];
|
||||
apk.read_exact(cert.as_mut())?;
|
||||
return Ok(cert);
|
||||
break cert;
|
||||
} else {
|
||||
// Skip this id-value pair
|
||||
apk.seek(SeekFrom::Current(
|
||||
@@ -147,10 +148,8 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
|
||||
))?;
|
||||
}
|
||||
}
|
||||
|
||||
Err(bad_apk!("cannot find certificate"))
|
||||
}
|
||||
inner(apk, version).log().unwrap_or(vec![])
|
||||
};
|
||||
res.log().unwrap_or(vec![])
|
||||
}
|
||||
|
||||
fn find_apk_path(pkg: &str, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
@@ -158,7 +157,7 @@ fn find_apk_path(pkg: &str, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||
if !e.is_dir() {
|
||||
return Ok(Skip);
|
||||
}
|
||||
let name_bytes = e.d_name().to_bytes();
|
||||
let name_bytes = e.name().to_bytes();
|
||||
if name_bytes.starts_with(pkg.as_bytes()) && name_bytes[pkg.len()] == b'-' {
|
||||
// Found the APK path, we can abort now
|
||||
e.path(buf)?;
|
||||
@@ -240,8 +239,7 @@ impl TrackedFile {
|
||||
|
||||
impl ManagerInfo {
|
||||
fn check_dyn(&mut self, daemon: &MagiskD, user: i32, pkg: &str) -> Status {
|
||||
let mut arr = Utf8CStrBufArr::default();
|
||||
let apk = FsPathBuf::new(&mut arr)
|
||||
let apk = FsPathBuf::default()
|
||||
.join(daemon.app_data_dir())
|
||||
.join_fmt(user)
|
||||
.join(pkg)
|
||||
@@ -275,7 +273,7 @@ impl ManagerInfo {
|
||||
}
|
||||
|
||||
fn check_stub(&mut self, user: i32, pkg: &str) -> Status {
|
||||
let mut arr = Utf8CStrBufArr::default();
|
||||
let mut arr = cstr_buf::default();
|
||||
if find_apk_path(pkg, &mut arr).is_err() {
|
||||
return Status::NotInstalled;
|
||||
}
|
||||
@@ -300,7 +298,7 @@ impl ManagerInfo {
|
||||
}
|
||||
|
||||
fn check_orig(&mut self, user: i32) -> Status {
|
||||
let mut arr = Utf8CStrBufArr::default();
|
||||
let mut arr = cstr_buf::default();
|
||||
if find_apk_path(APP_PACKAGE_NAME, &mut arr).is_err() {
|
||||
return Status::NotInstalled;
|
||||
}
|
||||
@@ -445,8 +443,7 @@ impl ManagerInfo {
|
||||
|
||||
impl MagiskD {
|
||||
fn get_package_uid(&self, user: i32, pkg: &str) -> i32 {
|
||||
let mut arr = Utf8CStrBufArr::default();
|
||||
let path = FsPathBuf::new(&mut arr)
|
||||
let path = FsPathBuf::default()
|
||||
.join(self.app_data_dir())
|
||||
.join_fmt(user)
|
||||
.join(pkg);
|
||||
@@ -458,29 +455,80 @@ impl MagiskD {
|
||||
pub fn preserve_stub_apk(&self) {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
|
||||
let mut arr = Utf8CStrBufArr::default();
|
||||
let apk = FsPathBuf::new(&mut arr)
|
||||
.join(get_magisk_tmp())
|
||||
.join("stub.apk");
|
||||
let apk = FsPathBuf::default().join(get_magisk_tmp()).join("stub.apk");
|
||||
|
||||
if let Ok(mut fd) = apk.open(O_RDONLY | O_CLOEXEC) {
|
||||
info.trusted_cert = read_certificate(&mut fd, MAGISK_VER_CODE);
|
||||
// Seek the fd back to start
|
||||
fd.seek(SeekFrom::Start(0)).log().ok();
|
||||
fd.seek(SeekFrom::Start(0)).log_ok();
|
||||
info.stub_apk_fd = Some(fd);
|
||||
}
|
||||
|
||||
apk.remove().log().ok();
|
||||
apk.remove().log_ok();
|
||||
}
|
||||
|
||||
pub fn get_manager_uid(&self, user: i32) -> i32 {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let (uid, _) = info.get_manager(self, user, false);
|
||||
uid
|
||||
}
|
||||
|
||||
pub fn get_manager(&self, user: i32, install: bool) -> (i32, String) {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let (uid, pkg) = info.get_manager(self, user, install);
|
||||
(uid, pkg.to_string())
|
||||
}
|
||||
|
||||
pub fn ensure_manager(&self) {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let _ = info.get_manager(self, 0, true);
|
||||
}
|
||||
|
||||
pub unsafe fn get_manager_for_cxx(&self, user: i32, ptr: *mut CxxString, install: bool) -> i32 {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let (uid, pkg) = info.get_manager(self, user, install);
|
||||
if let Some(str) = ptr.as_mut() {
|
||||
let mut str = Pin::new_unchecked(str);
|
||||
str.as_mut().clear();
|
||||
str.push_str(pkg);
|
||||
unsafe {
|
||||
let mut info = self.manager_info.lock().unwrap();
|
||||
let (uid, pkg) = info.get_manager(self, user, install);
|
||||
if let Some(str) = ptr.as_mut() {
|
||||
let mut str = Pin::new_unchecked(str);
|
||||
str.as_mut().clear();
|
||||
str.push_str(pkg);
|
||||
}
|
||||
uid
|
||||
}
|
||||
uid
|
||||
}
|
||||
|
||||
// app_id = app_no + AID_APP_START
|
||||
// app_no range: [0, 9999]
|
||||
pub fn get_app_no_list(&self) -> BitSet {
|
||||
let mut list = BitSet::new();
|
||||
let _: LoggedResult<()> = try {
|
||||
let mut app_data_dir = Directory::open(self.app_data_dir())?;
|
||||
// For each user
|
||||
loop {
|
||||
let entry = match app_data_dir.read()? {
|
||||
None => break,
|
||||
Some(e) => e,
|
||||
};
|
||||
let mut user_dir = match entry.open_as_dir() {
|
||||
Err(_) => continue,
|
||||
Ok(dir) => dir,
|
||||
};
|
||||
// For each package
|
||||
loop {
|
||||
match user_dir.read()? {
|
||||
None => break,
|
||||
Some(e) => {
|
||||
let attr = e.get_attr()?;
|
||||
let app_id = to_app_id(attr.st.st_uid as i32);
|
||||
if (AID_APP_START..=AID_APP_END).contains(&app_id) {
|
||||
let app_no = app_id - AID_APP_START;
|
||||
list.insert(app_no as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,28 +9,19 @@ use std::{
|
||||
|
||||
use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer};
|
||||
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use base::{
|
||||
clone_attr, cstr, debug, libc::mkstemp, Directory, FsPath, FsPathBuf, LibcReturn, LoggedResult,
|
||||
MappedFile, SilentResultExt, Utf8CStr, Utf8CStrBufArr, WalkResult,
|
||||
};
|
||||
|
||||
use crate::ffi::{prop_cb_exec, PropCb};
|
||||
use crate::resetprop::proto::persistent_properties::{
|
||||
mod_PersistentProperties::PersistentPropertyRecord, PersistentProperties,
|
||||
};
|
||||
use base::const_format::concatcp;
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use base::{
|
||||
clone_attr, cstr, debug, libc::mkstemp, path, Directory, FsPathBuf, LibcReturn, LoggedResult,
|
||||
MappedFile, SilentResultExt, Utf8CStr, WalkResult,
|
||||
};
|
||||
|
||||
macro_rules! PERSIST_PROP_DIR {
|
||||
() => {
|
||||
"/data/property"
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! PERSIST_PROP {
|
||||
() => {
|
||||
concat!(PERSIST_PROP_DIR!(), "/persistent_properties")
|
||||
};
|
||||
}
|
||||
const PERSIST_PROP_DIR: &str = "/data/property";
|
||||
const PERSIST_PROP: &str = concatcp!(PERSIST_PROP_DIR, "/persistent_properties");
|
||||
|
||||
trait PropCbExec {
|
||||
fn exec(&mut self, name: &Utf8CStr, value: &Utf8CStr);
|
||||
@@ -73,14 +64,11 @@ impl PropExt for PersistentProperties {
|
||||
}
|
||||
|
||||
fn check_proto() -> bool {
|
||||
FsPath::from(cstr!(PERSIST_PROP!())).exists()
|
||||
path!(PERSIST_PROP).exists()
|
||||
}
|
||||
|
||||
fn file_get_prop(name: &Utf8CStr) -> LoggedResult<String> {
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let path = FsPathBuf::new(&mut buf)
|
||||
.join(PERSIST_PROP_DIR!())
|
||||
.join(name);
|
||||
let path = FsPathBuf::default().join(PERSIST_PROP_DIR).join(name);
|
||||
let mut file = path.open(O_RDONLY | O_CLOEXEC).silent()?;
|
||||
debug!("resetprop: read prop from [{}]", path);
|
||||
let mut s = String::new();
|
||||
@@ -89,14 +77,10 @@ fn file_get_prop(name: &Utf8CStr) -> LoggedResult<String> {
|
||||
}
|
||||
|
||||
fn file_set_prop(name: &Utf8CStr, value: Option<&Utf8CStr>) -> LoggedResult<()> {
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let path = FsPathBuf::new(&mut buf)
|
||||
.join(PERSIST_PROP_DIR!())
|
||||
.join(name);
|
||||
let path = FsPathBuf::default().join(PERSIST_PROP_DIR).join(name);
|
||||
if let Some(value) = value {
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let mut tmp = FsPathBuf::new(&mut buf)
|
||||
.join(PERSIST_PROP_DIR!())
|
||||
let mut tmp = FsPathBuf::default()
|
||||
.join(PERSIST_PROP_DIR)
|
||||
.join("prop.XXXXXX");
|
||||
{
|
||||
let mut f = unsafe {
|
||||
@@ -115,8 +99,8 @@ fn file_set_prop(name: &Utf8CStr, value: Option<&Utf8CStr>) -> LoggedResult<()>
|
||||
}
|
||||
|
||||
fn proto_read_props() -> LoggedResult<PersistentProperties> {
|
||||
debug!("resetprop: decode with protobuf [{}]", PERSIST_PROP!());
|
||||
let m = MappedFile::open(cstr!(PERSIST_PROP!()))?;
|
||||
debug!("resetprop: decode with protobuf [{}]", PERSIST_PROP);
|
||||
let m = MappedFile::open(cstr!(PERSIST_PROP))?;
|
||||
let m = m.as_ref();
|
||||
let mut r = BytesReader::from_bytes(m);
|
||||
let mut props = PersistentProperties::from_reader(&mut r, m)?;
|
||||
@@ -126,8 +110,7 @@ fn proto_read_props() -> LoggedResult<PersistentProperties> {
|
||||
}
|
||||
|
||||
fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> {
|
||||
let mut buf = Utf8CStrBufArr::default();
|
||||
let mut tmp = FsPathBuf::new(&mut buf).join(concat!(PERSIST_PROP!(), ".XXXXXX"));
|
||||
let mut tmp = FsPathBuf::default().join(concatcp!(PERSIST_PROP, ".XXXXXX"));
|
||||
{
|
||||
let f = unsafe {
|
||||
let fd = mkstemp(tmp.as_mut_ptr()).check_os_err()?;
|
||||
@@ -136,19 +119,19 @@ fn proto_write_props(props: &PersistentProperties) -> LoggedResult<()> {
|
||||
debug!("resetprop: encode with protobuf [{}]", tmp);
|
||||
props.write_message(&mut Writer::new(BufWriter::new(f)))?;
|
||||
}
|
||||
clone_attr(FsPath::from(cstr!(PERSIST_PROP!())), &tmp)?;
|
||||
tmp.rename_to(cstr!(PERSIST_PROP!()))?;
|
||||
clone_attr(path!(PERSIST_PROP), &tmp)?;
|
||||
tmp.rename_to(cstr!(PERSIST_PROP))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn persist_get_prop(name: &Utf8CStr, prop_cb: Pin<&mut PropCb>) {
|
||||
fn inner(name: &Utf8CStr, mut prop_cb: Pin<&mut PropCb>) -> LoggedResult<()> {
|
||||
pub fn persist_get_prop(name: &Utf8CStr, mut prop_cb: Pin<&mut PropCb>) {
|
||||
let res: LoggedResult<()> = try {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
let prop = props.find(name)?;
|
||||
if let PersistentPropertyRecord {
|
||||
name: Some(ref mut n),
|
||||
value: Some(ref mut v),
|
||||
name: Some(n),
|
||||
value: Some(v),
|
||||
} = prop
|
||||
{
|
||||
prop_cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v));
|
||||
@@ -158,29 +141,28 @@ pub unsafe fn persist_get_prop(name: &Utf8CStr, prop_cb: Pin<&mut PropCb>) {
|
||||
prop_cb.exec(name, Utf8CStr::from_string(&mut value));
|
||||
debug!("resetprop: found prop [{}] = [{}]", name, value);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
inner(name, prop_cb).ok();
|
||||
};
|
||||
res.ok();
|
||||
}
|
||||
|
||||
pub unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>) {
|
||||
fn inner(mut prop_cb: Pin<&mut PropCb>) -> LoggedResult<()> {
|
||||
pub fn persist_get_props(mut prop_cb: Pin<&mut PropCb>) {
|
||||
let res: LoggedResult<()> = try {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
props.iter_mut().for_each(|prop| {
|
||||
if let PersistentPropertyRecord {
|
||||
name: Some(ref mut n),
|
||||
value: Some(ref mut v),
|
||||
name: Some(n),
|
||||
value: Some(v),
|
||||
} = prop
|
||||
{
|
||||
prop_cb.exec(Utf8CStr::from_string(n), Utf8CStr::from_string(v));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR!()))?;
|
||||
let mut dir = Directory::open(cstr!(PERSIST_PROP_DIR))?;
|
||||
dir.pre_order_walk(|e| {
|
||||
if e.is_file() {
|
||||
if let Ok(name) = Utf8CStr::from_cstr(e.d_name()) {
|
||||
if let Ok(name) = Utf8CStr::from_cstr(e.name()) {
|
||||
if let Ok(mut value) = file_get_prop(name) {
|
||||
prop_cb.exec(name, Utf8CStr::from_string(&mut value));
|
||||
}
|
||||
@@ -190,27 +172,26 @@ pub unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>) {
|
||||
Ok(WalkResult::Skip)
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
inner(prop_cb).ok();
|
||||
};
|
||||
res.ok();
|
||||
}
|
||||
|
||||
pub unsafe fn persist_delete_prop(name: &Utf8CStr) -> bool {
|
||||
fn inner(name: &Utf8CStr) -> LoggedResult<()> {
|
||||
pub fn persist_delete_prop(name: &Utf8CStr) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
let idx = props.find_index(name).silent()?;
|
||||
props.remove(idx);
|
||||
proto_write_props(&props)
|
||||
proto_write_props(&props)?;
|
||||
} else {
|
||||
file_set_prop(name, None)
|
||||
file_set_prop(name, None)?;
|
||||
}
|
||||
}
|
||||
inner(name).is_ok()
|
||||
};
|
||||
res.is_ok()
|
||||
}
|
||||
|
||||
pub unsafe fn persist_set_prop(name: &Utf8CStr, value: &Utf8CStr) -> bool {
|
||||
unsafe fn inner(name: &Utf8CStr, value: &Utf8CStr) -> LoggedResult<()> {
|
||||
pub fn persist_set_prop(name: &Utf8CStr, value: &Utf8CStr) -> bool {
|
||||
let res: LoggedResult<()> = try {
|
||||
if check_proto() {
|
||||
let mut props = proto_read_props()?;
|
||||
match props.find_index(name) {
|
||||
@@ -223,10 +204,10 @@ pub unsafe fn persist_set_prop(name: &Utf8CStr, value: &Utf8CStr) -> bool {
|
||||
},
|
||||
),
|
||||
}
|
||||
proto_write_props(&props)
|
||||
proto_write_props(&props)?;
|
||||
} else {
|
||||
file_set_prop(name, Some(value))
|
||||
file_set_prop(name, Some(value))?;
|
||||
}
|
||||
}
|
||||
inner(name, value).is_ok()
|
||||
};
|
||||
res.is_ok()
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ static void set_script_env() {
|
||||
char new_path[4096];
|
||||
ssprintf(new_path, sizeof(new_path), "%s:%s", getenv("PATH"), get_magisk_tmp());
|
||||
setenv("PATH", new_path, 1);
|
||||
if (zygisk_enabled)
|
||||
if (MagiskD::Get().zygisk_enabled())
|
||||
setenv("ZYGISK_ENABLED", "1", 1);
|
||||
};
|
||||
|
||||
@@ -75,10 +75,10 @@ if (pfs) { \
|
||||
exit(0); \
|
||||
}
|
||||
|
||||
void exec_common_scripts(const char *stage) {
|
||||
LOGI("* Running %s.d scripts\n", stage);
|
||||
void exec_common_scripts(rust::Utf8CStr stage) {
|
||||
LOGI("* Running %s.d scripts\n", stage.c_str());
|
||||
char path[4096];
|
||||
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage);
|
||||
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage.c_str());
|
||||
auto dir = xopen_dir(path);
|
||||
if (!dir) return;
|
||||
|
||||
@@ -97,7 +97,7 @@ void exec_common_scripts(const char *stage) {
|
||||
if (entry->d_type == DT_REG) {
|
||||
if (faccessat(dfd, entry->d_name, X_OK, 0) != 0)
|
||||
continue;
|
||||
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
|
||||
LOGI("%s.d: exec [%s]\n", stage.c_str(), entry->d_name);
|
||||
strcpy(name, entry->d_name);
|
||||
exec_t exec {
|
||||
.pre_exec = set_script_env,
|
||||
@@ -117,12 +117,12 @@ static bool operator>(const timespec &a, const timespec &b) {
|
||||
return a.tv_nsec > b.tv_nsec;
|
||||
}
|
||||
|
||||
void exec_module_scripts(const char *stage, const vector<string_view> &modules) {
|
||||
LOGI("* Running module %s scripts\n", stage);
|
||||
if (modules.empty())
|
||||
void exec_module_scripts(rust::Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list) {
|
||||
LOGI("* Running module %s scripts\n", stage.c_str());
|
||||
if (module_list.empty())
|
||||
return;
|
||||
|
||||
bool pfs = stage == "post-fs-data"sv;
|
||||
bool pfs = (string_view) stage == "post-fs-data";
|
||||
if (pfs) {
|
||||
timespec now{};
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
@@ -134,12 +134,11 @@ void exec_module_scripts(const char *stage, const vector<string_view> &modules)
|
||||
PFS_SETUP()
|
||||
|
||||
char path[4096];
|
||||
for (auto &m : modules) {
|
||||
const char *module = m.data();
|
||||
sprintf(path, MODULEROOT "/%s/%s.sh", module, stage);
|
||||
for (auto &m : module_list) {
|
||||
sprintf(path, MODULEROOT "/%.*s/%s.sh", (int) m.name.size(), m.name.data(), stage.c_str());
|
||||
if (access(path, F_OK) == -1)
|
||||
continue;
|
||||
LOGI("%s: exec [%s.sh]\n", module, stage);
|
||||
LOGI("%.*s: exec [%s.sh]\n", (int) m.name.size(), m.name.data(), stage.c_str());
|
||||
exec_t exec {
|
||||
.pre_exec = set_script_env,
|
||||
.fork = pfs ? xfork : fork_dont_care
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <endian.h>
|
||||
|
||||
#include <socket.hpp>
|
||||
#include <base.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool get_client_cred(int fd, sock_cred *cred) {
|
||||
socklen_t len = sizeof(ucred);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &len) != 0)
|
||||
return false;
|
||||
char buf[4096];
|
||||
len = sizeof(buf);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &len) != 0)
|
||||
len = 0;
|
||||
buf[len] = '\0';
|
||||
cred->context = buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int send_fds(int sockfd, void *cmsgbuf, size_t bufsz, const int *fds, int cnt) {
|
||||
iovec iov = {
|
||||
.iov_base = &cnt,
|
||||
.iov_len = sizeof(cnt),
|
||||
};
|
||||
msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
if (cnt) {
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = bufsz;
|
||||
cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * cnt);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
|
||||
memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * cnt);
|
||||
}
|
||||
|
||||
return xsendmsg(sockfd, &msg, 0);
|
||||
}
|
||||
|
||||
int send_fds(int sockfd, const int *fds, int cnt) {
|
||||
if (cnt == 0) {
|
||||
return send_fds(sockfd, nullptr, 0, nullptr, 0);
|
||||
}
|
||||
vector<char> cmsgbuf;
|
||||
cmsgbuf.resize(CMSG_SPACE(sizeof(int) * cnt));
|
||||
return send_fds(sockfd, cmsgbuf.data(), cmsgbuf.size(), fds, cnt);
|
||||
}
|
||||
|
||||
int send_fd(int sockfd, int fd) {
|
||||
if (fd < 0) {
|
||||
return send_fds(sockfd, nullptr, 0, nullptr, 0);
|
||||
}
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
return send_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), &fd, 1);
|
||||
}
|
||||
|
||||
static void *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) {
|
||||
iovec iov = {
|
||||
.iov_base = &cnt,
|
||||
.iov_len = sizeof(cnt),
|
||||
};
|
||||
msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsgbuf,
|
||||
.msg_controllen = bufsz
|
||||
};
|
||||
|
||||
xrecvmsg(sockfd, &msg, MSG_WAITALL);
|
||||
if (msg.msg_controllen != bufsz) {
|
||||
LOGE("recv_fd: msg_flags = %d, msg_controllen(%zu) != %zu\n",
|
||||
msg.msg_flags, msg.msg_controllen, bufsz);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg == nullptr) {
|
||||
LOGE("recv_fd: cmsg == nullptr\n");
|
||||
return nullptr;
|
||||
}
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt)) {
|
||||
LOGE("recv_fd: cmsg_len(%zu) != %zu\n", cmsg->cmsg_len, CMSG_LEN(sizeof(int) * cnt));
|
||||
return nullptr;
|
||||
}
|
||||
if (cmsg->cmsg_level != SOL_SOCKET) {
|
||||
LOGE("recv_fd: cmsg_level != SOL_SOCKET\n");
|
||||
return nullptr;
|
||||
}
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
LOGE("recv_fd: cmsg_type != SCM_RIGHTS\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
vector<int> recv_fds(int sockfd) {
|
||||
vector<int> results;
|
||||
|
||||
// Peek fd count to allocate proper buffer
|
||||
int cnt;
|
||||
recv(sockfd, &cnt, sizeof(cnt), MSG_PEEK);
|
||||
if (cnt == 0) {
|
||||
// Consume data
|
||||
recv(sockfd, &cnt, sizeof(cnt), MSG_WAITALL);
|
||||
return results;
|
||||
}
|
||||
|
||||
vector<char> cmsgbuf;
|
||||
cmsgbuf.resize(CMSG_SPACE(sizeof(int) * cnt));
|
||||
|
||||
void *data = recv_fds(sockfd, cmsgbuf.data(), cmsgbuf.size(), cnt);
|
||||
if (data == nullptr)
|
||||
return results;
|
||||
|
||||
results.resize(cnt);
|
||||
memcpy(results.data(), data, sizeof(int) * cnt);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
int recv_fd(int sockfd) {
|
||||
// Peek fd count
|
||||
int cnt;
|
||||
recv(sockfd, &cnt, sizeof(cnt), MSG_PEEK);
|
||||
if (cnt == 0) {
|
||||
// Consume data
|
||||
recv(sockfd, &cnt, sizeof(cnt), MSG_WAITALL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
void *data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1);
|
||||
if (data == nullptr)
|
||||
return -1;
|
||||
|
||||
int result;
|
||||
memcpy(&result, data, sizeof(int));
|
||||
return result;
|
||||
}
|
||||
|
||||
int read_int(int fd) {
|
||||
int val;
|
||||
if (xxread(fd, &val, sizeof(val)) != sizeof(val))
|
||||
return -1;
|
||||
return val;
|
||||
}
|
||||
|
||||
int read_int_be(int fd) {
|
||||
return ntohl(read_int(fd));
|
||||
}
|
||||
|
||||
void write_int(int fd, int val) {
|
||||
if (fd < 0) return;
|
||||
xwrite(fd, &val, sizeof(val));
|
||||
}
|
||||
|
||||
void write_int_be(int fd, int val) {
|
||||
write_int(fd, htonl(val));
|
||||
}
|
||||
|
||||
bool read_string(int fd, std::string &str) {
|
||||
int len = read_int(fd);
|
||||
str.clear();
|
||||
if (len < 0)
|
||||
return false;
|
||||
str.resize(len);
|
||||
return xxread(fd, str.data(), len) == len;
|
||||
}
|
||||
|
||||
string read_string(int fd) {
|
||||
string str;
|
||||
read_string(fd, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void write_string(int fd, string_view str) {
|
||||
if (fd < 0) return;
|
||||
write_int(fd, str.size());
|
||||
xwrite(fd, str.data(), str.size());
|
||||
}
|
||||
257
native/src/core/socket.rs
Normal file
257
native/src/core/socket.rs
Normal file
@@ -0,0 +1,257 @@
|
||||
use base::{libc, warn, ReadExt, ResultExt, WriteExt};
|
||||
use bytemuck::{bytes_of, bytes_of_mut, Zeroable};
|
||||
use std::io;
|
||||
use std::io::{ErrorKind, IoSlice, IoSliceMut, Read, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
use std::os::unix::net::{AncillaryData, SocketAncillary, UnixStream};
|
||||
|
||||
pub trait Encodable {
|
||||
fn encoded_len(&self) -> usize;
|
||||
fn encode(&self, w: &mut impl Write) -> io::Result<()>;
|
||||
}
|
||||
|
||||
pub trait Decodable: Sized + Encodable {
|
||||
fn decode(r: &mut impl Read) -> io::Result<Self>;
|
||||
}
|
||||
|
||||
macro_rules! impl_pod_encodable {
|
||||
($($t:ty)*) => ($(
|
||||
impl Encodable for $t {
|
||||
#[inline(always)]
|
||||
fn encoded_len(&self) -> usize {
|
||||
size_of::<Self>()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, w: &mut impl Write) -> io::Result<()> {
|
||||
w.write_pod(self)
|
||||
}
|
||||
}
|
||||
impl Decodable for $t {
|
||||
#[inline(always)]
|
||||
fn decode(r: &mut impl Read) -> io::Result<Self> {
|
||||
let mut val = Self::zeroed();
|
||||
r.read_pod(&mut val)?;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
impl_pod_encodable! { u8 u32 i32 usize }
|
||||
|
||||
impl Encodable for bool {
|
||||
#[inline(always)]
|
||||
fn encoded_len(&self) -> usize {
|
||||
size_of::<u8>()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn encode(&self, w: &mut impl Write) -> io::Result<()> {
|
||||
match *self {
|
||||
true => 1u8.encode(w),
|
||||
false => 0u8.encode(w),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for bool {
|
||||
#[inline(always)]
|
||||
fn decode(r: &mut impl Read) -> io::Result<Self> {
|
||||
Ok(u8::decode(r)? != 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decodable> Encodable for Vec<T> {
|
||||
fn encoded_len(&self) -> usize {
|
||||
size_of::<i32>() + size_of::<T>() * self.len()
|
||||
}
|
||||
|
||||
fn encode(&self, w: &mut impl Write) -> io::Result<()> {
|
||||
(self.len() as i32).encode(w)?;
|
||||
self.iter().try_for_each(|e| e.encode(w))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decodable> Decodable for Vec<T> {
|
||||
fn decode(r: &mut impl Read) -> io::Result<Self> {
|
||||
let len = i32::decode(r)?;
|
||||
let mut val = Vec::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
val.push(T::decode(r)?);
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for str {
|
||||
fn encoded_len(&self) -> usize {
|
||||
size_of::<i32>() + self.len()
|
||||
}
|
||||
|
||||
fn encode(&self, w: &mut impl Write) -> io::Result<()> {
|
||||
(self.len() as i32).encode(w)?;
|
||||
w.write_all(self.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for String {
|
||||
fn encoded_len(&self) -> usize {
|
||||
self.as_str().encoded_len()
|
||||
}
|
||||
|
||||
fn encode(&self, w: &mut impl Write) -> io::Result<()> {
|
||||
self.as_str().encode(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for String {
|
||||
fn decode(r: &mut impl Read) -> io::Result<String> {
|
||||
let len = i32::decode(r)?;
|
||||
let mut val = String::with_capacity(len as usize);
|
||||
r.take(len as u64).read_to_string(&mut val)?;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IpcRead {
|
||||
fn read_decodable<E: Decodable>(&mut self) -> io::Result<E>;
|
||||
}
|
||||
|
||||
impl<T: Read> IpcRead for T {
|
||||
#[inline(always)]
|
||||
fn read_decodable<E: Decodable>(&mut self) -> io::Result<E> {
|
||||
E::decode(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IpcWrite {
|
||||
fn write_encodable<E: Encodable + ?Sized>(&mut self, val: &E) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: Write> IpcWrite for T {
|
||||
#[inline(always)]
|
||||
fn write_encodable<E: Encodable + ?Sized>(&mut self, val: &E) -> io::Result<()> {
|
||||
val.encode(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UnixSocketExt {
|
||||
fn send_fds(&mut self, fd: &[RawFd]) -> io::Result<()>;
|
||||
fn recv_fd(&mut self) -> io::Result<Option<OwnedFd>>;
|
||||
fn recv_fds(&mut self) -> io::Result<Vec<OwnedFd>>;
|
||||
}
|
||||
|
||||
impl UnixSocketExt for UnixStream {
|
||||
fn send_fds(&mut self, fds: &[RawFd]) -> io::Result<()> {
|
||||
match fds.len() {
|
||||
0 => self.write_pod(&0)?,
|
||||
len => {
|
||||
// 4k buffer is reasonable enough
|
||||
let mut buf = [0u8; 4096];
|
||||
let mut ancillary = SocketAncillary::new(&mut buf);
|
||||
if !ancillary.add_fds(fds) {
|
||||
return Err(ErrorKind::OutOfMemory.into());
|
||||
}
|
||||
let fd_count = len as i32;
|
||||
let iov = IoSlice::new(bytes_of(&fd_count));
|
||||
self.send_vectored_with_ancillary(&[iov], &mut ancillary)?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv_fd(&mut self) -> io::Result<Option<OwnedFd>> {
|
||||
let mut fd_count = 0;
|
||||
self.peek(bytes_of_mut(&mut fd_count))?;
|
||||
if fd_count < 1 {
|
||||
// Actually consume the data
|
||||
self.read_pod(&mut fd_count)?;
|
||||
return Ok(None);
|
||||
}
|
||||
if fd_count > 1 {
|
||||
warn!(
|
||||
"Received unexpected number of fds: expected=1 actual={}",
|
||||
fd_count
|
||||
);
|
||||
}
|
||||
|
||||
// 4k buffer is reasonable enough
|
||||
let mut buf = [0u8; 4096];
|
||||
let mut ancillary = SocketAncillary::new(&mut buf);
|
||||
let iov = IoSliceMut::new(bytes_of_mut(&mut fd_count));
|
||||
self.recv_vectored_with_ancillary(&mut [iov], &mut ancillary)?;
|
||||
for msg in ancillary.messages().flatten() {
|
||||
if let AncillaryData::ScmRights(mut scm_rights) = msg {
|
||||
// We only want the first one
|
||||
let fd = if let Some(fd) = scm_rights.next() {
|
||||
unsafe { OwnedFd::from_raw_fd(fd) }
|
||||
} else {
|
||||
return Ok(None);
|
||||
};
|
||||
// Close all others
|
||||
for fd in scm_rights {
|
||||
unsafe { libc::close(fd) };
|
||||
}
|
||||
return Ok(Some(fd));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn recv_fds(&mut self) -> io::Result<Vec<OwnedFd>> {
|
||||
let mut fd_count = 0;
|
||||
// 4k buffer is reasonable enough
|
||||
let mut buf = [0u8; 4096];
|
||||
let mut ancillary = SocketAncillary::new(&mut buf);
|
||||
let iov = IoSliceMut::new(bytes_of_mut(&mut fd_count));
|
||||
self.recv_vectored_with_ancillary(&mut [iov], &mut ancillary)?;
|
||||
let mut fds: Vec<OwnedFd> = Vec::new();
|
||||
for msg in ancillary.messages().flatten() {
|
||||
if let AncillaryData::ScmRights(scm_rights) = msg {
|
||||
fds = scm_rights
|
||||
.map(|fd| unsafe { OwnedFd::from_raw_fd(fd) })
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
if fd_count as usize != fds.len() {
|
||||
warn!(
|
||||
"Received unexpected number of fds: expected={} actual={}",
|
||||
fd_count,
|
||||
fds.len()
|
||||
);
|
||||
}
|
||||
Ok(fds)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_fd(socket: RawFd, fd: RawFd) -> bool {
|
||||
let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) });
|
||||
if fd < 0 {
|
||||
socket.send_fds(&[]).log().is_ok()
|
||||
} else {
|
||||
socket.send_fds(&[fd]).log().is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_fds(socket: RawFd, fds: &[RawFd]) -> bool {
|
||||
let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) });
|
||||
socket.send_fds(fds).log().is_ok()
|
||||
}
|
||||
|
||||
pub fn recv_fd(socket: RawFd) -> RawFd {
|
||||
let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) });
|
||||
socket
|
||||
.recv_fd()
|
||||
.log()
|
||||
.unwrap_or(None)
|
||||
.map_or(-1, IntoRawFd::into_raw_fd)
|
||||
}
|
||||
|
||||
pub fn recv_fds(socket: RawFd) -> Vec<RawFd> {
|
||||
let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) });
|
||||
let fds = socket.recv_fds().log().unwrap_or(Vec::new());
|
||||
// SAFETY: OwnedFd and RawFd has the same layout
|
||||
unsafe { std::mem::transmute(fds) }
|
||||
}
|
||||
@@ -165,15 +165,17 @@ int DbStatement::bind_text(int index, rust::Str val) {
|
||||
return sqlite3_bind_text(reinterpret_cast<sqlite3_stmt*>(this), index, val.data(), val.size(), nullptr);
|
||||
}
|
||||
|
||||
#define sql_chk_log(fn, ...) if (int rc = fn(__VA_ARGS__); rc != SQLITE_OK) { \
|
||||
LOGE("sqlite3(line:%d): %s\n", __LINE__, sqlite3_errstr(rc)); \
|
||||
return false; \
|
||||
#define sql_chk_log_ret(ret, fn, ...) if (int rc = fn(__VA_ARGS__); rc != SQLITE_OK) { \
|
||||
LOGE("sqlite3(line:%d): %s\n", __LINE__, sqlite3_errstr(rc)); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
static bool open_and_init_db_impl(sqlite3 **dbOut) {
|
||||
#define sql_chk_log(fn, ...) sql_chk_log_ret(nullptr, fn, __VA_ARGS__)
|
||||
|
||||
sqlite3 *open_and_init_db() {
|
||||
if (!load_sqlite()) {
|
||||
LOGE("sqlite3: Cannot load libsqlite.so\n");
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unique_ptr<sqlite3, decltype(sqlite3_close)> db(nullptr, sqlite3_close);
|
||||
@@ -192,9 +194,10 @@ static bool open_and_init_db_impl(sqlite3 **dbOut) {
|
||||
};
|
||||
sql_chk_log(sql_exec_impl, db.get(), "PRAGMA user_version", nullptr, nullptr, ver_cb, &ver);
|
||||
if (ver > DB_VERSION) {
|
||||
// Don't support downgrading database
|
||||
// Don't support downgrading database, delete and retry
|
||||
LOGE("sqlite3: Downgrading database is not supported\n");
|
||||
return false;
|
||||
unlink(MAGISKDB);
|
||||
return open_and_init_db();
|
||||
}
|
||||
|
||||
auto create_policy = [&] {
|
||||
@@ -298,13 +301,7 @@ static bool open_and_init_db_impl(sqlite3 **dbOut) {
|
||||
sql_chk_log(sql_exec_impl, db.get(), "PRAGMA user_version=" DB_VERSION_STR);
|
||||
}
|
||||
|
||||
*dbOut = db.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
sqlite3 *open_and_init_db() {
|
||||
sqlite3 *db = nullptr;
|
||||
return open_and_init_db_impl(&db) ? db : nullptr;
|
||||
return db.release();
|
||||
}
|
||||
|
||||
// Exported from Rust
|
||||
@@ -332,7 +329,7 @@ bool db_exec(const char *sql, DbArgs args, db_exec_callback exec_fn) {
|
||||
fn->operator()(columns, values);
|
||||
};
|
||||
}
|
||||
sql_chk_log(sql_exec_rs, sql, bind_cb, &bind_fn, exec_cb, &exec_fn);
|
||||
sql_chk_log_ret(false, sql_exec_rs, sql, bind_cb, &bind_fn, exec_cb, &exec_fn);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include <base.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <consts.hpp>
|
||||
|
||||
#include "su.hpp"
|
||||
#include <core.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -21,11 +20,6 @@ using namespace std;
|
||||
// 0x18800020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|
|
||||
// FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|FLAG_INCLUDE_STOPPED_PACKAGES
|
||||
|
||||
#define get_cmd(to) \
|
||||
((to).command.empty() ? \
|
||||
((to).shell.empty() ? DEFAULT_SHELL : (to).shell.data()) : \
|
||||
(to).command.data())
|
||||
|
||||
class Extra {
|
||||
const char *key;
|
||||
enum {
|
||||
@@ -128,14 +122,15 @@ static bool check_no_error(int fd) {
|
||||
}
|
||||
|
||||
static void exec_cmd(const char *action, vector<Extra> &data,
|
||||
const shared_ptr<su_info> &info, bool provider = true) {
|
||||
const SuAppRequest &info, bool provider = true) {
|
||||
char target[128];
|
||||
char user[4];
|
||||
ssprintf(user, sizeof(user), "%d", to_user_id(info->eval_uid));
|
||||
ssprintf(user, sizeof(user), "%d", to_user_id(info.eval_uid));
|
||||
|
||||
// First try content provider call method
|
||||
if (provider) {
|
||||
ssprintf(target, sizeof(target), "content://%s.provider", info->mgr_pkg.data());
|
||||
ssprintf(target, sizeof(target), "content://%.*s.provider",
|
||||
(int) info.mgr_pkg.size(), info.mgr_pkg.data());
|
||||
vector<const char *> args{ CALL_PROVIDER };
|
||||
for (auto &e : data) {
|
||||
e.add_bind(args);
|
||||
@@ -153,7 +148,7 @@ static void exec_cmd(const char *action, vector<Extra> &data,
|
||||
}
|
||||
|
||||
// Then try start activity with package name
|
||||
strscpy(target, info->mgr_pkg.data(), sizeof(target));
|
||||
ssprintf(target, sizeof(target), "%.*s", (int) info.mgr_pkg.size(), info.mgr_pkg.data());
|
||||
vector<const char *> args{ START_ACTIVITY };
|
||||
for (auto &e : data) {
|
||||
e.add_intent(args);
|
||||
@@ -168,53 +163,58 @@ static void exec_cmd(const char *action, vector<Extra> &data,
|
||||
exec_command(exec);
|
||||
}
|
||||
|
||||
void app_log(const su_context &ctx) {
|
||||
void app_log(const SuAppRequest &info, SuPolicy policy, bool notify) {
|
||||
if (fork_dont_care() == 0) {
|
||||
string context = (string) info.request.context;
|
||||
string command = info.request.command.empty()
|
||||
? (string) info.request.shell
|
||||
: (string) info.request.command;
|
||||
|
||||
vector<Extra> extras;
|
||||
extras.reserve(9);
|
||||
extras.emplace_back("from.uid", ctx.info->uid);
|
||||
extras.emplace_back("to.uid", static_cast<int>(ctx.req.uid));
|
||||
extras.emplace_back("pid", ctx.pid);
|
||||
extras.emplace_back("policy", +ctx.info->access.policy);
|
||||
extras.emplace_back("target", ctx.req.target);
|
||||
extras.emplace_back("context", ctx.req.context.data());
|
||||
extras.emplace_back("gids", &ctx.req.gids);
|
||||
extras.emplace_back("command", get_cmd(ctx.req));
|
||||
extras.emplace_back("notify", (bool) ctx.info->access.notify);
|
||||
extras.emplace_back("from.uid", info.uid);
|
||||
extras.emplace_back("to.uid", info.request.target_uid);
|
||||
extras.emplace_back("pid", info.pid);
|
||||
extras.emplace_back("policy", +policy);
|
||||
extras.emplace_back("target", info.request.target_pid);
|
||||
extras.emplace_back("context", context.data());
|
||||
extras.emplace_back("gids", &info.request.gids);
|
||||
extras.emplace_back("command", command.data());
|
||||
extras.emplace_back("notify", notify);
|
||||
|
||||
exec_cmd("log", extras, ctx.info);
|
||||
exec_cmd("log", extras, info);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void app_notify(const su_context &ctx) {
|
||||
void app_notify(const SuAppRequest &info, SuPolicy policy) {
|
||||
if (fork_dont_care() == 0) {
|
||||
vector<Extra> extras;
|
||||
extras.reserve(3);
|
||||
extras.emplace_back("from.uid", ctx.info->uid);
|
||||
extras.emplace_back("pid", ctx.pid);
|
||||
extras.emplace_back("policy", +ctx.info->access.policy);
|
||||
extras.emplace_back("from.uid", info.uid);
|
||||
extras.emplace_back("pid", info.pid);
|
||||
extras.emplace_back("policy", +policy);
|
||||
|
||||
exec_cmd("notify", extras, ctx.info);
|
||||
exec_cmd("notify", extras, info);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
int app_request(const su_context &ctx) {
|
||||
int app_request(const SuAppRequest &info) {
|
||||
// Create FIFO
|
||||
char fifo[64];
|
||||
ssprintf(fifo, sizeof(fifo), "%s/" INTLROOT "/su_request_%d", get_magisk_tmp(), ctx.pid);
|
||||
ssprintf(fifo, sizeof(fifo), "%s/" INTLROOT "/su_request_%d", get_magisk_tmp(), info.pid);
|
||||
mkfifo(fifo, 0600);
|
||||
chown(fifo, ctx.info->mgr_uid, ctx.info->mgr_uid);
|
||||
chown(fifo, info.mgr_uid, info.mgr_uid);
|
||||
setfilecon(fifo, MAGISK_FILE_CON);
|
||||
|
||||
// Send request
|
||||
vector<Extra> extras;
|
||||
extras.reserve(3);
|
||||
extras.emplace_back("fifo", fifo);
|
||||
extras.emplace_back("uid", ctx.info->eval_uid);
|
||||
extras.emplace_back("pid", ctx.pid);
|
||||
exec_cmd("request", extras, ctx.info, false);
|
||||
extras.emplace_back("uid", info.eval_uid);
|
||||
extras.emplace_back("pid", info.pid);
|
||||
exec_cmd("request", extras, info, false);
|
||||
|
||||
// Wait for data input for at most 70 seconds
|
||||
// Open with O_RDWR to prevent FIFO open block
|
||||
|
||||
299
native/src/core/su/daemon.rs
Normal file
299
native/src/core/su/daemon.rs
Normal file
@@ -0,0 +1,299 @@
|
||||
use crate::daemon::{to_app_id, to_user_id, MagiskD, AID_ROOT, AID_SHELL};
|
||||
use crate::db::{DbSettings, MultiuserMode, RootAccess};
|
||||
use crate::ffi::{
|
||||
app_log, app_notify, app_request, exec_root_shell, SuAppRequest, SuPolicy, SuRequest,
|
||||
};
|
||||
use crate::socket::IpcRead;
|
||||
use crate::su::db::RootSettings;
|
||||
use crate::UCred;
|
||||
use base::{debug, error, exit_on_error, libc, warn, LoggedResult, ResultExt, WriteExt};
|
||||
use std::fs::File;
|
||||
use std::os::fd::{FromRawFd, IntoRawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
const DEFAULT_SHELL: &str = "/system/bin/sh";
|
||||
|
||||
impl Default for SuRequest {
|
||||
fn default() -> Self {
|
||||
SuRequest {
|
||||
target_uid: AID_ROOT,
|
||||
target_pid: -1,
|
||||
login: false,
|
||||
keep_env: false,
|
||||
shell: DEFAULT_SHELL.to_string(),
|
||||
command: "".to_string(),
|
||||
context: "".to_string(),
|
||||
gids: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SuInfo {
|
||||
uid: i32,
|
||||
eval_uid: i32,
|
||||
cfg: DbSettings,
|
||||
mgr_pkg: String,
|
||||
mgr_uid: i32,
|
||||
access: Mutex<AccessInfo>,
|
||||
}
|
||||
|
||||
struct AccessInfo {
|
||||
settings: RootSettings,
|
||||
timestamp: Instant,
|
||||
}
|
||||
|
||||
impl Default for SuInfo {
|
||||
fn default() -> Self {
|
||||
SuInfo {
|
||||
uid: -1,
|
||||
eval_uid: -1,
|
||||
cfg: Default::default(),
|
||||
mgr_pkg: Default::default(),
|
||||
mgr_uid: -1,
|
||||
access: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AccessInfo {
|
||||
fn default() -> Self {
|
||||
AccessInfo {
|
||||
settings: Default::default(),
|
||||
timestamp: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SuInfo {
|
||||
fn allow(uid: i32) -> SuInfo {
|
||||
let access = RootSettings {
|
||||
policy: SuPolicy::Allow,
|
||||
log: false,
|
||||
notify: false,
|
||||
};
|
||||
SuInfo {
|
||||
uid,
|
||||
access: Mutex::new(AccessInfo::new(access)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn deny(uid: i32) -> SuInfo {
|
||||
let access = RootSettings {
|
||||
policy: SuPolicy::Deny,
|
||||
log: false,
|
||||
notify: false,
|
||||
};
|
||||
SuInfo {
|
||||
uid,
|
||||
access: Mutex::new(AccessInfo::new(access)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AccessInfo {
|
||||
fn new(settings: RootSettings) -> AccessInfo {
|
||||
AccessInfo {
|
||||
settings,
|
||||
timestamp: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_fresh(&self) -> bool {
|
||||
self.timestamp.elapsed() < Duration::from_secs(3)
|
||||
}
|
||||
|
||||
fn refresh(&mut self) {
|
||||
self.timestamp = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
pub fn su_daemon_handler(&self, client: i32, cred: &UCred) {
|
||||
let cred = cred.0;
|
||||
debug!(
|
||||
"su: request from uid=[{}], pid=[{}], client=[{}]",
|
||||
cred.uid, cred.pid, client
|
||||
);
|
||||
|
||||
let mut client = unsafe { UnixStream::from_raw_fd(client) };
|
||||
|
||||
let mut req = match client.read_decodable::<SuRequest>().log() {
|
||||
Ok(req) => req,
|
||||
Err(_) => {
|
||||
warn!("su: remote process probably died, abort");
|
||||
client.write_pod(&SuPolicy::Deny.repr).ok();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let info = self.get_su_info(cred.uid as i32);
|
||||
let app_req = SuAppRequest {
|
||||
uid: cred.uid as i32,
|
||||
pid: cred.pid,
|
||||
eval_uid: info.eval_uid,
|
||||
mgr_pkg: &info.mgr_pkg,
|
||||
mgr_uid: info.mgr_uid,
|
||||
request: &req,
|
||||
};
|
||||
|
||||
{
|
||||
let mut access = info.access.lock().unwrap();
|
||||
|
||||
if access.settings.policy == SuPolicy::Query {
|
||||
let fd = app_request(&app_req);
|
||||
if fd < 0 {
|
||||
access.settings.policy = SuPolicy::Deny;
|
||||
} else {
|
||||
let mut fd = unsafe { File::from_raw_fd(fd) };
|
||||
access.settings.policy = SuPolicy {
|
||||
repr: fd
|
||||
.read_decodable::<i32>()
|
||||
.log()
|
||||
.map(i32::from_be)
|
||||
.unwrap_or(SuPolicy::Deny.repr),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if access.settings.log {
|
||||
app_log(&app_req, access.settings.policy, access.settings.notify);
|
||||
} else if access.settings.notify {
|
||||
app_notify(&app_req, access.settings.policy);
|
||||
}
|
||||
|
||||
// Before unlocking, refresh the timestamp
|
||||
access.refresh();
|
||||
|
||||
// Fail fast
|
||||
if access.settings.policy == SuPolicy::Deny {
|
||||
warn!("su: request rejected ({})", info.uid);
|
||||
client.write_pod(&SuPolicy::Deny.repr).ok();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the root access is granted.
|
||||
// Fork a child root process and monitor its exit value.
|
||||
let child = unsafe { libc::fork() };
|
||||
if child == 0 {
|
||||
debug!("su: fork handler");
|
||||
|
||||
// Abort upon any error occurred
|
||||
exit_on_error(true);
|
||||
|
||||
// ack
|
||||
client.write_pod(&0).ok();
|
||||
|
||||
exec_root_shell(client.into_raw_fd(), cred.pid, &mut req, info.cfg.mnt_ns);
|
||||
return;
|
||||
}
|
||||
if child < 0 {
|
||||
error!("su: fork failed, abort");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait result
|
||||
debug!("su: waiting child pid=[{}]", child);
|
||||
let mut status = 0;
|
||||
let code = unsafe {
|
||||
if libc::waitpid(child, &mut status, 0) > 0 {
|
||||
libc::WEXITSTATUS(status)
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
};
|
||||
debug!("su: return code=[{}]", code);
|
||||
client.write_pod(&code).ok();
|
||||
}
|
||||
|
||||
fn get_su_info(&self, uid: i32) -> Arc<SuInfo> {
|
||||
if uid == AID_ROOT {
|
||||
return Arc::new(SuInfo::allow(AID_ROOT));
|
||||
}
|
||||
|
||||
let cached = self.cached_su_info.load();
|
||||
if cached.uid == uid && cached.access.lock().unwrap().is_fresh() {
|
||||
return cached;
|
||||
}
|
||||
|
||||
let info = self.build_su_info(uid);
|
||||
self.cached_su_info.store(info.clone());
|
||||
info
|
||||
}
|
||||
|
||||
fn build_su_info(&self, uid: i32) -> Arc<SuInfo> {
|
||||
let result: LoggedResult<Arc<SuInfo>> = try {
|
||||
let cfg = self.get_db_settings()?;
|
||||
|
||||
// Check multiuser settings
|
||||
let eval_uid = match cfg.multiuser_mode {
|
||||
MultiuserMode::OwnerOnly => {
|
||||
if to_user_id(uid) != 0 {
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
}
|
||||
uid
|
||||
}
|
||||
MultiuserMode::OwnerManaged => to_app_id(uid),
|
||||
_ => uid,
|
||||
};
|
||||
|
||||
// Check su access settings
|
||||
match cfg.root_access {
|
||||
RootAccess::Disabled => {
|
||||
warn!("Root access is disabled!");
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
}
|
||||
RootAccess::AdbOnly => {
|
||||
if uid != AID_SHELL {
|
||||
warn!("Root access limited to ADB only!");
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
}
|
||||
}
|
||||
RootAccess::AppsOnly => {
|
||||
if uid == AID_SHELL {
|
||||
warn!("Root access is disabled for ADB!");
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let mut access = RootSettings::default();
|
||||
self.get_root_settings(eval_uid, &mut access)?;
|
||||
|
||||
// We need to talk to the manager, get the app info
|
||||
let (mgr_uid, mgr_pkg) =
|
||||
if access.policy == SuPolicy::Query || access.log || access.notify {
|
||||
self.get_manager(to_user_id(eval_uid), true)
|
||||
} else {
|
||||
(-1, String::new())
|
||||
};
|
||||
|
||||
// If it's the manager, allow it silently
|
||||
if to_app_id(uid) == to_app_id(mgr_uid) {
|
||||
return Arc::new(SuInfo::allow(uid));
|
||||
}
|
||||
|
||||
// If still not determined, check if manager exists
|
||||
if access.policy == SuPolicy::Query && mgr_uid < 0 {
|
||||
return Arc::new(SuInfo::deny(uid));
|
||||
}
|
||||
|
||||
// Finally, the SuInfo
|
||||
Arc::new(SuInfo {
|
||||
uid,
|
||||
eval_uid,
|
||||
cfg,
|
||||
mgr_pkg,
|
||||
mgr_uid,
|
||||
access: Mutex::new(AccessInfo::new(access)),
|
||||
})
|
||||
};
|
||||
|
||||
result.unwrap_or(Arc::new(SuInfo::deny(uid)))
|
||||
}
|
||||
}
|
||||
133
native/src/core/su/db.rs
Normal file
133
native/src/core/su/db.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use crate::daemon::{
|
||||
to_app_id, to_user_id, MagiskD, AID_APP_END, AID_APP_START, AID_ROOT, AID_SHELL,
|
||||
};
|
||||
use crate::db::DbArg::Integer;
|
||||
use crate::db::{MultiuserMode, RootAccess, SqlTable, SqliteResult, SqliteReturn};
|
||||
use crate::ffi::{DbValues, SuPolicy};
|
||||
use base::ResultExt;
|
||||
|
||||
impl Default for SuPolicy {
|
||||
fn default() -> Self {
|
||||
SuPolicy::Query
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RootSettings {
|
||||
pub policy: SuPolicy,
|
||||
pub log: bool,
|
||||
pub notify: bool,
|
||||
}
|
||||
|
||||
impl SqlTable for RootSettings {
|
||||
fn on_row(&mut self, columns: &[String], values: &DbValues) {
|
||||
for (i, column) in columns.iter().enumerate() {
|
||||
let val = values.get_int(i as i32);
|
||||
match column.as_str() {
|
||||
"policy" => self.policy.repr = val,
|
||||
"logging" => self.log = val != 0,
|
||||
"notification" => self.notify = val != 0,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UidList(Vec<i32>);
|
||||
|
||||
impl SqlTable for UidList {
|
||||
fn on_row(&mut self, _: &[String], values: &DbValues) {
|
||||
self.0.push(values.get_int(0));
|
||||
}
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
pub fn get_root_settings(&self, uid: i32, settings: &mut RootSettings) -> SqliteResult<()> {
|
||||
self.db_exec_with_rows(
|
||||
"SELECT policy, logging, notification FROM policies \
|
||||
WHERE uid=? AND (until=0 OR until>strftime('%s', 'now'))",
|
||||
&[Integer(uid as i64)],
|
||||
settings,
|
||||
)
|
||||
.sql_result()
|
||||
}
|
||||
|
||||
pub fn prune_su_access(&self) {
|
||||
let mut list = UidList(Vec::new());
|
||||
if self
|
||||
.db_exec_with_rows("SELECT uid FROM policies", &[], &mut list)
|
||||
.sql_result()
|
||||
.log()
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let app_list = self.get_app_no_list();
|
||||
let mut rm_uids = Vec::new();
|
||||
|
||||
for uid in list.0 {
|
||||
let app_id = to_app_id(uid);
|
||||
if (AID_APP_START..=AID_APP_END).contains(&app_id) {
|
||||
let app_no = app_id - AID_APP_START;
|
||||
if !app_list.contains(app_no as usize) {
|
||||
// The app_id is no longer installed
|
||||
rm_uids.push(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for uid in rm_uids {
|
||||
self.db_exec("DELETE FROM policies WHERE uid=?", &[Integer(uid as i64)]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uid_granted_root(&self, mut uid: i32) -> bool {
|
||||
if uid == AID_ROOT {
|
||||
return true;
|
||||
}
|
||||
|
||||
let cfg = match self.get_db_settings().log() {
|
||||
Ok(cfg) => cfg,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// Check user root access settings
|
||||
match cfg.root_access {
|
||||
RootAccess::Disabled => return false,
|
||||
RootAccess::AppsOnly => {
|
||||
if uid == AID_SHELL {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
RootAccess::AdbOnly => {
|
||||
if uid != AID_SHELL {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Check multiuser settings
|
||||
match cfg.multiuser_mode {
|
||||
MultiuserMode::OwnerOnly => {
|
||||
if to_user_id(uid) != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
MultiuserMode::OwnerManaged => uid = to_app_id(uid),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut granted = false;
|
||||
let mut output_fn =
|
||||
|_: &[String], values: &DbValues| granted = values.get_int(0) == SuPolicy::Allow.repr;
|
||||
self.db_exec_with_rows(
|
||||
"SELECT policy FROM policies WHERE uid=? AND (until=0 OR until>strftime('%s', 'now'))",
|
||||
&[Integer(uid as i64)],
|
||||
&mut output_fn,
|
||||
);
|
||||
|
||||
granted
|
||||
}
|
||||
}
|
||||
@@ -1,145 +1,4 @@
|
||||
use crate::daemon::{
|
||||
to_app_id, to_user_id, MagiskD, AID_APP_END, AID_APP_START, AID_ROOT, AID_SHELL,
|
||||
};
|
||||
use crate::db::DbArg::Integer;
|
||||
use crate::db::{SqlTable, SqliteResult, SqliteReturn};
|
||||
use crate::ffi::{DbValues, MultiuserMode, RootAccess, RootSettings, SuPolicy};
|
||||
use base::ResultExt;
|
||||
mod daemon;
|
||||
mod db;
|
||||
|
||||
impl Default for SuPolicy {
|
||||
fn default() -> Self {
|
||||
SuPolicy::Query
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RootSettings {
|
||||
fn default() -> Self {
|
||||
RootSettings {
|
||||
policy: Default::default(),
|
||||
log: true,
|
||||
notify: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SqlTable for RootSettings {
|
||||
fn on_row(&mut self, columns: &[String], values: &DbValues) {
|
||||
for (i, column) in columns.iter().enumerate() {
|
||||
let val = values.get_int(i as i32);
|
||||
if column == "policy" {
|
||||
self.policy.repr = val;
|
||||
} else if column == "logging" {
|
||||
self.log = val != 0;
|
||||
} else if column == "notify" {
|
||||
self.notify = val != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UidList(Vec<i32>);
|
||||
|
||||
impl SqlTable for UidList {
|
||||
fn on_row(&mut self, _: &[String], values: &DbValues) {
|
||||
self.0.push(values.get_int(0));
|
||||
}
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
fn get_root_settings(&self, uid: i32, settings: &mut RootSettings) -> SqliteResult<()> {
|
||||
self.db_exec_with_rows(
|
||||
"SELECT policy, logging, notification FROM policies \
|
||||
WHERE uid=? AND (until=0 OR until>strftime('%s', 'now'))",
|
||||
&[Integer(uid as i64)],
|
||||
settings,
|
||||
)
|
||||
.sql_result()
|
||||
}
|
||||
|
||||
pub fn get_root_settings_for_cxx(&self, uid: i32, settings: &mut RootSettings) -> bool {
|
||||
self.get_root_settings(uid, settings).log().is_ok()
|
||||
}
|
||||
|
||||
pub fn prune_su_access(&self) {
|
||||
let mut list = UidList(Vec::new());
|
||||
if self
|
||||
.db_exec_with_rows("SELECT uid FROM policies", &[], &mut list)
|
||||
.sql_result()
|
||||
.log()
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let app_list = self.get_app_no_list();
|
||||
let mut rm_uids = Vec::new();
|
||||
|
||||
for uid in list.0 {
|
||||
let app_id = to_app_id(uid);
|
||||
if (AID_APP_START..=AID_APP_END).contains(&app_id) {
|
||||
let app_no = app_id - AID_APP_START;
|
||||
if !app_list.contains(app_no as usize) {
|
||||
// The app_id is no longer installed
|
||||
rm_uids.push(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for uid in rm_uids {
|
||||
self.db_exec("DELETE FROM policies WHERE uid=?", &[Integer(uid as i64)]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uid_granted_root(&self, mut uid: i32) -> bool {
|
||||
if uid == AID_ROOT {
|
||||
return true;
|
||||
}
|
||||
|
||||
let cfg = match self.get_db_settings().log() {
|
||||
Ok(cfg) => cfg,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// Check user root access settings
|
||||
match cfg.root_access {
|
||||
RootAccess::Disabled => return false,
|
||||
RootAccess::AppsOnly => {
|
||||
if uid == AID_SHELL {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
RootAccess::AdbOnly => {
|
||||
if uid != AID_SHELL {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Check multiuser settings
|
||||
match cfg.multiuser_mode {
|
||||
MultiuserMode::OwnerOnly => {
|
||||
if to_user_id(uid) != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
MultiuserMode::OwnerManaged => uid = to_app_id(uid),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut granted = false;
|
||||
let mut output_fn =
|
||||
|_: &[String], values: &DbValues| granted = values.get_int(0) == SuPolicy::Allow.repr;
|
||||
self.db_exec_with_rows(
|
||||
"SELECT policy FROM policies WHERE uid=? AND (until=0 OR until>strftime('%s', 'now'))",
|
||||
&[Integer(uid as i64)],
|
||||
&mut output_fn,
|
||||
);
|
||||
|
||||
granted
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_default_root_settings() -> RootSettings {
|
||||
RootSettings::default()
|
||||
}
|
||||
pub use daemon::SuInfo;
|
||||
|
||||
@@ -13,13 +13,24 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <flags.h>
|
||||
#include <core.hpp>
|
||||
|
||||
#include "su.hpp"
|
||||
#include "pts.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DEFAULT_SHELL "/system/bin/sh"
|
||||
|
||||
// Constants for atty
|
||||
#define ATTY_IN (1 << 0)
|
||||
#define ATTY_OUT (1 << 1)
|
||||
#define ATTY_ERR (1 << 2)
|
||||
|
||||
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
|
||||
|
||||
[[noreturn]] static void usage(int status) {
|
||||
@@ -97,7 +108,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
{ nullptr, 0, nullptr, 0 },
|
||||
};
|
||||
|
||||
su_request su_req;
|
||||
auto req = SuRequest::New();
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
// Replace -cn and -z with -Z for backwards compatibility
|
||||
@@ -110,25 +121,28 @@ int su_client_main(int argc, char *argv[]) {
|
||||
|
||||
while ((c = getopt_long(argc, argv, "c:hlmps:VvuZ:Mt:g:G:", long_opts, nullptr)) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
case 'c': {
|
||||
string command;
|
||||
for (int i = optind - 1; i < argc; ++i) {
|
||||
if (!su_req.command.empty())
|
||||
su_req.command += ' ';
|
||||
su_req.command += argv[i];
|
||||
if (!command.empty())
|
||||
command += ' ';
|
||||
command += argv[i];
|
||||
}
|
||||
req.command = command;
|
||||
optind = argc;
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
usage(EXIT_SUCCESS);
|
||||
case 'l':
|
||||
su_req.login = true;
|
||||
req.login = true;
|
||||
break;
|
||||
case 'm':
|
||||
case 'p':
|
||||
su_req.keepenv = true;
|
||||
req.keep_env = true;
|
||||
break;
|
||||
case 's':
|
||||
su_req.shell = optarg;
|
||||
req.shell = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
printf("%d\n", MAGISK_VER_CODE);
|
||||
@@ -137,33 +151,36 @@ int su_client_main(int argc, char *argv[]) {
|
||||
printf("%s\n", MAGISK_VERSION ":MAGISKSU");
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'Z':
|
||||
su_req.context = optarg;
|
||||
req.context = optarg;
|
||||
break;
|
||||
case 'M':
|
||||
case 't':
|
||||
if (su_req.target != -1) {
|
||||
if (req.target_pid != -1) {
|
||||
fprintf(stderr, "Can't use -M and -t at the same time\n");
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
if (optarg == nullptr) {
|
||||
su_req.target = 0;
|
||||
req.target_pid = 0;
|
||||
} else {
|
||||
su_req.target = parse_int(optarg);
|
||||
if (*optarg == '-' || su_req.target == -1) {
|
||||
req.target_pid = parse_int(optarg);
|
||||
if (*optarg == '-' || req.target_pid == -1) {
|
||||
fprintf(stderr, "Invalid PID: %s\n", optarg);
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
case 'G':
|
||||
case 'G': {
|
||||
vector<gid_t> gids;
|
||||
if (int gid = parse_int(optarg); gid >= 0) {
|
||||
su_req.gids.insert(c == 'g' ? su_req.gids.begin() : su_req.gids.end(), gid);
|
||||
gids.insert(c == 'g' ? gids.begin() : gids.end(), gid);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid GID: %s\n", optarg);
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
std::copy(gids.begin(), gids.end(), std::back_inserter(req.gids));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Bionic getopt_long doesn't terminate its error output by newline */
|
||||
fprintf(stderr, "\n");
|
||||
@@ -172,7 +189,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (optind < argc && strcmp(argv[optind], "-") == 0) {
|
||||
su_req.login = true;
|
||||
req.login = true;
|
||||
optind++;
|
||||
}
|
||||
/* username or uid */
|
||||
@@ -180,9 +197,9 @@ int su_client_main(int argc, char *argv[]) {
|
||||
struct passwd *pw;
|
||||
pw = getpwnam(argv[optind]);
|
||||
if (pw)
|
||||
su_req.uid = pw->pw_uid;
|
||||
req.target_uid = pw->pw_uid;
|
||||
else
|
||||
su_req.uid = parse_int(argv[optind]);
|
||||
req.target_uid = parse_int(argv[optind]);
|
||||
optind++;
|
||||
}
|
||||
|
||||
@@ -191,12 +208,8 @@ int su_client_main(int argc, char *argv[]) {
|
||||
// Connect to client
|
||||
fd = connect_daemon(+RequestCode::SUPERUSER);
|
||||
|
||||
// Send su_request
|
||||
xwrite(fd, &su_req, sizeof(su_req_base));
|
||||
write_string(fd, su_req.shell);
|
||||
write_string(fd, su_req.command);
|
||||
write_string(fd, su_req.context);
|
||||
write_vector(fd, su_req.gids);
|
||||
// Send request
|
||||
req.write_to_fd(fd);
|
||||
|
||||
// Wait for ack from daemon
|
||||
if (read_int(fd)) {
|
||||
@@ -239,3 +252,162 @@ int su_client_main(int argc, char *argv[]) {
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Set effective uid back to root, otherwise setres[ug]id will fail if uid isn't root
|
||||
static void set_identity(int uid, const rust::Vec<gid_t> &groups) {
|
||||
if (seteuid(0)) {
|
||||
PLOGE("seteuid (root)");
|
||||
}
|
||||
gid_t gid;
|
||||
if (!groups.empty()) {
|
||||
if (setgroups(groups.size(), groups.data())) {
|
||||
PLOGE("setgroups");
|
||||
}
|
||||
gid = groups[0];
|
||||
} else {
|
||||
gid = uid;
|
||||
}
|
||||
if (setresgid(gid, gid, gid)) {
|
||||
PLOGE("setresgid (%u)", uid);
|
||||
}
|
||||
if (setresuid(uid, uid, uid)) {
|
||||
PLOGE("setresuid (%u)", uid);
|
||||
}
|
||||
}
|
||||
|
||||
void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode) {
|
||||
// Become session leader
|
||||
xsetsid();
|
||||
|
||||
// The FDs for each of the streams
|
||||
int infd = recv_fd(client);
|
||||
int outfd = recv_fd(client);
|
||||
int errfd = recv_fd(client);
|
||||
|
||||
// App need a PTY
|
||||
if (read_int(client)) {
|
||||
string pts;
|
||||
string ptmx;
|
||||
auto magiskpts = get_magisk_tmp() + "/"s SHELLPTS;
|
||||
if (access(magiskpts.data(), F_OK)) {
|
||||
pts = "/dev/pts";
|
||||
ptmx = "/dev/ptmx";
|
||||
} else {
|
||||
pts = magiskpts;
|
||||
ptmx = magiskpts + "/ptmx";
|
||||
}
|
||||
int ptmx_fd = xopen(ptmx.data(), O_RDWR);
|
||||
grantpt(ptmx_fd);
|
||||
unlockpt(ptmx_fd);
|
||||
int pty_num = get_pty_num(ptmx_fd);
|
||||
if (pty_num < 0) {
|
||||
// Kernel issue? Fallback to /dev/pts
|
||||
close(ptmx_fd);
|
||||
pts = "/dev/pts";
|
||||
ptmx_fd = xopen("/dev/ptmx", O_RDWR);
|
||||
grantpt(ptmx_fd);
|
||||
unlockpt(ptmx_fd);
|
||||
pty_num = get_pty_num(ptmx_fd);
|
||||
}
|
||||
send_fd(client, ptmx_fd);
|
||||
close(ptmx_fd);
|
||||
|
||||
string pts_slave = pts + "/" + to_string(pty_num);
|
||||
LOGD("su: pts_slave=[%s]\n", pts_slave.data());
|
||||
|
||||
// Opening the TTY has to occur after the
|
||||
// fork() and setsid() so that it becomes
|
||||
// our controlling TTY and not the daemon's
|
||||
int ptsfd = xopen(pts_slave.data(), O_RDWR);
|
||||
|
||||
if (infd < 0)
|
||||
infd = ptsfd;
|
||||
if (outfd < 0)
|
||||
outfd = ptsfd;
|
||||
if (errfd < 0)
|
||||
errfd = ptsfd;
|
||||
}
|
||||
|
||||
// Swap out stdin, stdout, stderr
|
||||
xdup2(infd, STDIN_FILENO);
|
||||
xdup2(outfd, STDOUT_FILENO);
|
||||
xdup2(errfd, STDERR_FILENO);
|
||||
|
||||
close(infd);
|
||||
close(outfd);
|
||||
close(errfd);
|
||||
close(client);
|
||||
|
||||
// Handle namespaces
|
||||
if (req.target_pid == -1)
|
||||
req.target_pid = pid;
|
||||
else if (req.target_pid == 0)
|
||||
mode = MntNsMode::Global;
|
||||
else if (mode == MntNsMode::Global)
|
||||
mode = MntNsMode::Requester;
|
||||
|
||||
switch (mode) {
|
||||
case MntNsMode::Global:
|
||||
LOGD("su: use global namespace\n");
|
||||
break;
|
||||
case MntNsMode::Requester:
|
||||
LOGD("su: use namespace of pid=[%d]\n", req.target_pid);
|
||||
switch_mnt_ns(req.target_pid);
|
||||
break;
|
||||
case MntNsMode::Isolate:
|
||||
LOGD("su: use new isolated namespace\n");
|
||||
switch_mnt_ns(req.target_pid);
|
||||
xunshare(CLONE_NEWNS);
|
||||
xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
const char *argv[4] = { nullptr };
|
||||
|
||||
argv[0] = req.login ? "-" : req.shell.c_str();
|
||||
|
||||
if (!req.command.empty()) {
|
||||
argv[1] = "-c";
|
||||
argv[2] = req.command.c_str();
|
||||
}
|
||||
|
||||
// Setup environment
|
||||
umask(022);
|
||||
char path[32];
|
||||
ssprintf(path, sizeof(path), "/proc/%d/cwd", pid);
|
||||
char cwd[4096];
|
||||
if (realpath(path, cwd, sizeof(cwd)) > 0)
|
||||
chdir(cwd);
|
||||
ssprintf(path, sizeof(path), "/proc/%d/environ", pid);
|
||||
auto env = full_read(path);
|
||||
clearenv();
|
||||
for (size_t pos = 0; pos < env.size(); ++pos) {
|
||||
putenv(env.data() + pos);
|
||||
pos = env.find_first_of('\0', pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
if (!req.keep_env) {
|
||||
struct passwd *pw;
|
||||
pw = getpwuid(req.target_uid);
|
||||
if (pw) {
|
||||
setenv("HOME", pw->pw_dir, 1);
|
||||
setenv("USER", pw->pw_name, 1);
|
||||
setenv("LOGNAME", pw->pw_name, 1);
|
||||
setenv("SHELL", req.shell.c_str(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Unblock all signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigprocmask(SIG_SETMASK, &block_set, nullptr);
|
||||
if (!req.context.empty()) {
|
||||
auto f = xopen_file("/proc/self/attr/exec", "we");
|
||||
if (f) fprintf(f.get(), "%s", req.context.c_str());
|
||||
}
|
||||
set_identity(req.target_uid, req.gids);
|
||||
execvp(req.shell.c_str(), (char **) argv);
|
||||
fprintf(stderr, "Cannot execute %s: %s\n", req.shell.c_str(), strerror(errno));
|
||||
PLOGE("exec");
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <memory>
|
||||
|
||||
#include <sqlite.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
#define DEFAULT_SHELL "/system/bin/sh"
|
||||
|
||||
// Constants for atty
|
||||
#define ATTY_IN (1 << 0)
|
||||
#define ATTY_OUT (1 << 1)
|
||||
#define ATTY_ERR (1 << 2)
|
||||
|
||||
#define SILENT_ALLOW { SuPolicy::Allow, false, false }
|
||||
#define SILENT_DENY { SuPolicy::Deny, false, false }
|
||||
|
||||
class su_info {
|
||||
public:
|
||||
// Unique key
|
||||
const int uid;
|
||||
|
||||
// These should be guarded with internal lock
|
||||
int eval_uid; // The effective UID, taking multiuser settings into consideration
|
||||
struct DbSettings cfg;
|
||||
struct RootSettings access;
|
||||
std::string mgr_pkg;
|
||||
int mgr_uid;
|
||||
void check_db();
|
||||
|
||||
// These should be guarded with global cache lock
|
||||
bool is_fresh();
|
||||
void refresh();
|
||||
|
||||
su_info(int uid);
|
||||
~su_info();
|
||||
mutex_guard lock();
|
||||
|
||||
private:
|
||||
long timestamp;
|
||||
// Internal lock
|
||||
pthread_mutex_t _lock;
|
||||
};
|
||||
|
||||
struct su_req_base {
|
||||
uid_t uid = AID_ROOT;
|
||||
bool login = false;
|
||||
bool keepenv = false;
|
||||
pid_t target = -1;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct su_request : public su_req_base {
|
||||
std::string shell = DEFAULT_SHELL;
|
||||
std::string command;
|
||||
std::string context;
|
||||
std::vector<gid_t> gids;
|
||||
};
|
||||
|
||||
struct su_context {
|
||||
std::shared_ptr<su_info> info;
|
||||
su_request req;
|
||||
int pid;
|
||||
};
|
||||
|
||||
void app_log(const su_context &ctx);
|
||||
void app_notify(const su_context &ctx);
|
||||
int app_request(const su_context &ctx);
|
||||
@@ -1,377 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
#include "su.hpp"
|
||||
#include "pts.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static shared_ptr<su_info> cached;
|
||||
|
||||
su_info::su_info(int uid) :
|
||||
uid(uid), eval_uid(-1), cfg(DbSettings()), access(RootSettings()),
|
||||
mgr_uid(-1), timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {}
|
||||
|
||||
su_info::~su_info() {
|
||||
pthread_mutex_destroy(&_lock);
|
||||
}
|
||||
|
||||
mutex_guard su_info::lock() {
|
||||
return mutex_guard(_lock);
|
||||
}
|
||||
|
||||
bool su_info::is_fresh() {
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
long current = ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
|
||||
return current - timestamp < 3000; /* 3 seconds */
|
||||
}
|
||||
|
||||
void su_info::refresh() {
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
timestamp = ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
|
||||
}
|
||||
|
||||
void su_info::check_db() {
|
||||
eval_uid = uid;
|
||||
auto &daemon = MagiskD();
|
||||
daemon.get_db_settings(cfg);
|
||||
|
||||
// Check multiuser settings
|
||||
switch (cfg.multiuser_mode) {
|
||||
case MultiuserMode::OwnerOnly:
|
||||
if (to_user_id(uid) != 0) {
|
||||
eval_uid = -1;
|
||||
access = SILENT_DENY;
|
||||
}
|
||||
break;
|
||||
case MultiuserMode::OwnerManaged:
|
||||
eval_uid = to_app_id(uid);
|
||||
break;
|
||||
case MultiuserMode::User:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (eval_uid > 0) {
|
||||
if (!daemon.get_root_settings(eval_uid, access))
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to check our manager
|
||||
if (access.policy == SuPolicy::Query || access.log || access.notify) {
|
||||
mgr_uid = daemon.get_manager(to_user_id(eval_uid), &mgr_pkg, true);
|
||||
}
|
||||
}
|
||||
|
||||
static shared_ptr<su_info> get_su_info(unsigned uid) {
|
||||
if (uid == AID_ROOT) {
|
||||
auto info = make_shared<su_info>(uid);
|
||||
info->access = SILENT_ALLOW;
|
||||
return info;
|
||||
}
|
||||
|
||||
shared_ptr<su_info> info;
|
||||
{
|
||||
mutex_guard lock(cache_lock);
|
||||
if (!cached || cached->uid != uid || !cached->is_fresh())
|
||||
cached = make_shared<su_info>(uid);
|
||||
cached->refresh();
|
||||
info = cached;
|
||||
}
|
||||
|
||||
mutex_guard lock = info->lock();
|
||||
|
||||
if (info->access.policy == SuPolicy::Query) {
|
||||
// Not cached, get data from database
|
||||
info->check_db();
|
||||
|
||||
// If it's the manager, allow it silently
|
||||
if (to_app_id(info->uid) == to_app_id(info->mgr_uid)) {
|
||||
info->access = SILENT_ALLOW;
|
||||
return info;
|
||||
}
|
||||
|
||||
// Check su access settings
|
||||
switch (info->cfg.root_access) {
|
||||
case RootAccess::Disabled:
|
||||
LOGW("Root access is disabled!\n");
|
||||
info->access = SILENT_DENY;
|
||||
break;
|
||||
case RootAccess::AdbOnly:
|
||||
if (info->uid != AID_SHELL) {
|
||||
LOGW("Root access limited to ADB only!\n");
|
||||
info->access = SILENT_DENY;
|
||||
}
|
||||
break;
|
||||
case RootAccess::AppsOnly:
|
||||
if (info->uid == AID_SHELL) {
|
||||
LOGW("Root access is disabled for ADB!\n");
|
||||
info->access = SILENT_DENY;
|
||||
}
|
||||
break;
|
||||
case RootAccess::AppsAndAdb:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (info->access.policy != SuPolicy::Query)
|
||||
return info;
|
||||
|
||||
// If still not determined, check if manager exists
|
||||
if (info->mgr_uid < 0) {
|
||||
info->access = SILENT_DENY;
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
// Set effective uid back to root, otherwise setres[ug]id will fail if uid isn't root
|
||||
static void set_identity(uid_t uid, const std::vector<uid_t> &groups) {
|
||||
if (seteuid(0)) {
|
||||
PLOGE("seteuid (root)");
|
||||
}
|
||||
gid_t gid;
|
||||
if (groups.size() > 0) {
|
||||
if (setgroups(groups.size(), groups.data())) {
|
||||
PLOGE("setgroups");
|
||||
}
|
||||
gid = groups[0];
|
||||
} else {
|
||||
gid = uid;
|
||||
}
|
||||
if (setresgid(gid, gid, gid)) {
|
||||
PLOGE("setresgid (%u)", uid);
|
||||
}
|
||||
if (setresuid(uid, uid, uid)) {
|
||||
PLOGE("setresuid (%u)", uid);
|
||||
}
|
||||
}
|
||||
|
||||
void su_daemon_handler(int client, const sock_cred *cred) {
|
||||
LOGD("su: request from uid=[%d], pid=[%d], client=[%d]\n", cred->uid, cred->pid, client);
|
||||
|
||||
su_context ctx = {
|
||||
.info = get_su_info(cred->uid),
|
||||
.req = su_request(),
|
||||
.pid = cred->pid
|
||||
};
|
||||
|
||||
// Read su_request
|
||||
if (xxread(client, &ctx.req, sizeof(su_req_base)) < 0
|
||||
|| !read_string(client, ctx.req.shell)
|
||||
|| !read_string(client, ctx.req.command)
|
||||
|| !read_string(client, ctx.req.context)
|
||||
|| !read_vector(client, ctx.req.gids)) {
|
||||
LOGW("su: remote process probably died, abort\n");
|
||||
ctx.info.reset();
|
||||
write_int(client, +SuPolicy::Deny);
|
||||
close(client);
|
||||
return;
|
||||
}
|
||||
|
||||
// If still not determined, ask manager
|
||||
if (ctx.info->access.policy == SuPolicy::Query) {
|
||||
int fd = app_request(ctx);
|
||||
if (fd < 0) {
|
||||
ctx.info->access.policy = SuPolicy::Deny;
|
||||
} else {
|
||||
int ret = read_int_be(fd);
|
||||
ctx.info->access.policy = ret < 0 ? SuPolicy::Deny : static_cast<SuPolicy>(ret);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.info->access.log)
|
||||
app_log(ctx);
|
||||
else if (ctx.info->access.notify)
|
||||
app_notify(ctx);
|
||||
|
||||
// Fail fast
|
||||
if (ctx.info->access.policy == SuPolicy::Deny) {
|
||||
LOGW("su: request rejected (%u)\n", ctx.info->uid);
|
||||
ctx.info.reset();
|
||||
write_int(client, +SuPolicy::Deny);
|
||||
close(client);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fork a child root process
|
||||
//
|
||||
// The child process will need to setsid, open a pseudo-terminal
|
||||
// if needed, and eventually exec shell.
|
||||
// The parent process will wait for the result and
|
||||
// send the return code back to our client.
|
||||
|
||||
if (int child = xfork(); child) {
|
||||
ctx.info.reset();
|
||||
|
||||
// Wait result
|
||||
LOGD("su: waiting child pid=[%d]\n", child);
|
||||
int status, code;
|
||||
|
||||
if (waitpid(child, &status, 0) > 0)
|
||||
code = WEXITSTATUS(status);
|
||||
else
|
||||
code = -1;
|
||||
|
||||
LOGD("su: return code=[%d]\n", code);
|
||||
write(client, &code, sizeof(code));
|
||||
close(client);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD("su: fork handler\n");
|
||||
|
||||
// Abort upon any error occurred
|
||||
exit_on_error(true);
|
||||
|
||||
// ack
|
||||
write_int(client, 0);
|
||||
|
||||
// Become session leader
|
||||
xsetsid();
|
||||
|
||||
// The FDs for each of the streams
|
||||
int infd = recv_fd(client);
|
||||
int outfd = recv_fd(client);
|
||||
int errfd = recv_fd(client);
|
||||
|
||||
// App need a PTY
|
||||
if (read_int(client)) {
|
||||
string pts;
|
||||
string ptmx;
|
||||
auto magiskpts = get_magisk_tmp() + "/"s SHELLPTS;
|
||||
if (access(magiskpts.data(), F_OK)) {
|
||||
pts = "/dev/pts";
|
||||
ptmx = "/dev/ptmx";
|
||||
} else {
|
||||
pts = magiskpts;
|
||||
ptmx = magiskpts + "/ptmx";
|
||||
}
|
||||
int ptmx_fd = xopen(ptmx.data(), O_RDWR);
|
||||
grantpt(ptmx_fd);
|
||||
unlockpt(ptmx_fd);
|
||||
int pty_num = get_pty_num(ptmx_fd);
|
||||
if (pty_num < 0) {
|
||||
// Kernel issue? Fallback to /dev/pts
|
||||
close(ptmx_fd);
|
||||
pts = "/dev/pts";
|
||||
ptmx_fd = xopen("/dev/ptmx", O_RDWR);
|
||||
grantpt(ptmx_fd);
|
||||
unlockpt(ptmx_fd);
|
||||
pty_num = get_pty_num(ptmx_fd);
|
||||
}
|
||||
send_fd(client, ptmx_fd);
|
||||
close(ptmx_fd);
|
||||
|
||||
string pts_slave = pts + "/" + to_string(pty_num);
|
||||
LOGD("su: pts_slave=[%s]\n", pts_slave.data());
|
||||
|
||||
// Opening the TTY has to occur after the
|
||||
// fork() and setsid() so that it becomes
|
||||
// our controlling TTY and not the daemon's
|
||||
int ptsfd = xopen(pts_slave.data(), O_RDWR);
|
||||
|
||||
if (infd < 0)
|
||||
infd = ptsfd;
|
||||
if (outfd < 0)
|
||||
outfd = ptsfd;
|
||||
if (errfd < 0)
|
||||
errfd = ptsfd;
|
||||
}
|
||||
|
||||
// Swap out stdin, stdout, stderr
|
||||
xdup2(infd, STDIN_FILENO);
|
||||
xdup2(outfd, STDOUT_FILENO);
|
||||
xdup2(errfd, STDERR_FILENO);
|
||||
|
||||
close(infd);
|
||||
close(outfd);
|
||||
close(errfd);
|
||||
close(client);
|
||||
|
||||
// Handle namespaces
|
||||
if (ctx.req.target == -1)
|
||||
ctx.req.target = ctx.pid;
|
||||
else if (ctx.req.target == 0)
|
||||
ctx.info->cfg.mnt_ns = MntNsMode::Global;
|
||||
else if (ctx.info->cfg.mnt_ns == MntNsMode::Global)
|
||||
ctx.info->cfg.mnt_ns = MntNsMode::Requester;
|
||||
switch (ctx.info->cfg.mnt_ns) {
|
||||
case MntNsMode::Global:
|
||||
LOGD("su: use global namespace\n");
|
||||
break;
|
||||
case MntNsMode::Requester:
|
||||
LOGD("su: use namespace of pid=[%d]\n", ctx.req.target);
|
||||
if (switch_mnt_ns(ctx.req.target))
|
||||
LOGD("su: setns failed, fallback to global\n");
|
||||
break;
|
||||
case MntNsMode::Isolate:
|
||||
LOGD("su: use new isolated namespace\n");
|
||||
switch_mnt_ns(ctx.req.target);
|
||||
xunshare(CLONE_NEWNS);
|
||||
xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
const char *argv[4] = { nullptr };
|
||||
|
||||
argv[0] = ctx.req.login ? "-" : ctx.req.shell.data();
|
||||
|
||||
if (!ctx.req.command.empty()) {
|
||||
argv[1] = "-c";
|
||||
argv[2] = ctx.req.command.data();
|
||||
}
|
||||
|
||||
// Setup environment
|
||||
umask(022);
|
||||
char path[32];
|
||||
ssprintf(path, sizeof(path), "/proc/%d/cwd", ctx.pid);
|
||||
char cwd[4096];
|
||||
if (realpath(path, cwd, sizeof(cwd)) > 0)
|
||||
chdir(cwd);
|
||||
ssprintf(path, sizeof(path), "/proc/%d/environ", ctx.pid);
|
||||
auto env = full_read(path);
|
||||
clearenv();
|
||||
for (size_t pos = 0; pos < env.size(); ++pos) {
|
||||
putenv(env.data() + pos);
|
||||
pos = env.find_first_of('\0', pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
if (!ctx.req.keepenv) {
|
||||
struct passwd *pw;
|
||||
pw = getpwuid(ctx.req.uid);
|
||||
if (pw) {
|
||||
setenv("HOME", pw->pw_dir, 1);
|
||||
setenv("USER", pw->pw_name, 1);
|
||||
setenv("LOGNAME", pw->pw_name, 1);
|
||||
setenv("SHELL", ctx.req.shell.data(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Unblock all signals
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigprocmask(SIG_SETMASK, &block_set, nullptr);
|
||||
if (!ctx.req.context.empty()) {
|
||||
auto f = xopen_file("/proc/self/attr/exec", "we");
|
||||
if (f) fprintf(f.get(), "%s", ctx.req.context.data());
|
||||
}
|
||||
set_identity(ctx.req.uid, ctx.req.gids);
|
||||
execvp(ctx.req.shell.data(), (char **) argv);
|
||||
fprintf(stderr, "Cannot execute %s: %s\n", ctx.req.shell.data(), strerror(errno));
|
||||
PLOGE("exec");
|
||||
}
|
||||
210
native/src/core/zygisk/daemon.rs
Normal file
210
native/src/core/zygisk/daemon.rs
Normal file
@@ -0,0 +1,210 @@
|
||||
use crate::consts::MODULEROOT;
|
||||
use crate::daemon::{to_user_id, MagiskD};
|
||||
use crate::ffi::{
|
||||
get_magisk_tmp, restore_zygisk_prop, update_deny_flags, ZygiskRequest, ZygiskStateFlags,
|
||||
};
|
||||
use crate::socket::{IpcRead, UnixSocketExt};
|
||||
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY, STDOUT_FILENO};
|
||||
use base::{
|
||||
cstr, cstr_buf, error, fork_dont_care, libc, open_fd, raw_cstr, warn, Directory, FsPathBuf,
|
||||
LoggedError, LoggedResult, ResultExt, WriteExt,
|
||||
};
|
||||
use std::fmt::Write;
|
||||
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
const UNMOUNT_MASK: u32 =
|
||||
ZygiskStateFlags::ProcessOnDenyList.repr | ZygiskStateFlags::DenyListEnforced.repr;
|
||||
|
||||
pub fn zygisk_should_load_module(flags: u32) -> bool {
|
||||
flags & UNMOUNT_MASK != UNMOUNT_MASK && flags & ZygiskStateFlags::ProcessIsMagiskApp.repr == 0
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn exec_zygiskd(is_64_bit: bool, remote: UnixStream) {
|
||||
// This fd has to survive exec
|
||||
unsafe {
|
||||
libc::fcntl(remote.as_raw_fd(), libc::F_SETFD, 0);
|
||||
}
|
||||
|
||||
// Start building the exec arguments
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let magisk = if is_64_bit { "magisk" } else { "magisk32" };
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let magisk = "magisk";
|
||||
|
||||
let exe = FsPathBuf::<64>::new().join(get_magisk_tmp()).join(magisk);
|
||||
|
||||
let mut fd_str = cstr_buf::new::<16>();
|
||||
write!(fd_str, "{}", remote.as_raw_fd()).ok();
|
||||
unsafe {
|
||||
libc::execl(
|
||||
exe.as_ptr(),
|
||||
raw_cstr!(""),
|
||||
raw_cstr!("zygisk"),
|
||||
raw_cstr!("companion"),
|
||||
fd_str.as_ptr(),
|
||||
ptr::null() as *const libc::c_char,
|
||||
);
|
||||
libc::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
pub fn zygisk_handler(&self, client: i32) {
|
||||
let mut client = unsafe { UnixStream::from_raw_fd(client) };
|
||||
let _: LoggedResult<()> = try {
|
||||
let code = ZygiskRequest {
|
||||
repr: client.read_decodable()?,
|
||||
};
|
||||
match code {
|
||||
ZygiskRequest::GetInfo => self.get_process_info(client)?,
|
||||
ZygiskRequest::ConnectCompanion => self.connect_zygiskd(client),
|
||||
ZygiskRequest::GetModDir => self.get_mod_dir(client)?,
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn zygisk_reset(&self, mut restore: bool) {
|
||||
if !self.zygisk_enabled.load(Ordering::Acquire) {
|
||||
return;
|
||||
}
|
||||
|
||||
if restore {
|
||||
self.zygote_start_count.store(1, Ordering::Release);
|
||||
} else {
|
||||
*self.zygiskd_sockets.lock().unwrap() = (None, None);
|
||||
if self.zygote_start_count.fetch_add(1, Ordering::AcqRel) > 3 {
|
||||
warn!("zygote crashes too many times, rolling-back");
|
||||
restore = true;
|
||||
}
|
||||
}
|
||||
|
||||
if restore {
|
||||
restore_zygisk_prop();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_module_fds(&self, is_64_bit: bool) -> Option<Vec<RawFd>> {
|
||||
self.module_list.get().map(|module_list| {
|
||||
module_list
|
||||
.iter()
|
||||
.map(|m| if is_64_bit { m.z64 } else { m.z32 })
|
||||
// All fds passed over sockets have to be valid file descriptors.
|
||||
// To work around this issue, send over STDOUT_FILENO as an indicator of an
|
||||
// invalid fd as it will always be /dev/null in magiskd.
|
||||
.map(|fd| if fd < 0 { STDOUT_FILENO } else { fd })
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn connect_zygiskd(&self, mut client: UnixStream) {
|
||||
let mut zygiskd_sockets = self.zygiskd_sockets.lock().unwrap();
|
||||
let result: LoggedResult<()> = try {
|
||||
let is_64_bit: bool = client.read_decodable()?;
|
||||
let socket = if is_64_bit {
|
||||
&mut zygiskd_sockets.1
|
||||
} else {
|
||||
&mut zygiskd_sockets.0
|
||||
};
|
||||
|
||||
if let Some(fd) = socket {
|
||||
// Make sure the socket is still valid
|
||||
let mut pfd = libc::pollfd {
|
||||
fd: fd.as_raw_fd(),
|
||||
events: 0,
|
||||
revents: 0,
|
||||
};
|
||||
if unsafe { libc::poll(&mut pfd, 1, 0) } != 0 || pfd.revents != 0 {
|
||||
// Any revent means error
|
||||
*socket = None;
|
||||
}
|
||||
}
|
||||
|
||||
let socket = if let Some(fd) = socket {
|
||||
fd
|
||||
} else {
|
||||
// Create a new socket pair and fork zygiskd process
|
||||
let (local, remote) = UnixStream::pair()?;
|
||||
if fork_dont_care() == 0 {
|
||||
exec_zygiskd(is_64_bit, remote);
|
||||
}
|
||||
*socket = Some(local);
|
||||
let local = socket.as_mut().unwrap();
|
||||
if let Some(module_fds) = self.get_module_fds(is_64_bit) {
|
||||
local.send_fds(&module_fds)?;
|
||||
}
|
||||
if local.read_decodable::<i32>()? != 0 {
|
||||
Err(LoggedError::default())?;
|
||||
}
|
||||
local
|
||||
};
|
||||
socket.send_fds(&[client.as_raw_fd()])?;
|
||||
};
|
||||
if result.is_err() {
|
||||
error!("zygiskd startup error");
|
||||
}
|
||||
}
|
||||
|
||||
fn get_process_info(&self, mut client: UnixStream) -> LoggedResult<()> {
|
||||
let uid: i32 = client.read_decodable()?;
|
||||
let process: String = client.read_decodable()?;
|
||||
let is_64_bit: bool = client.read_decodable()?;
|
||||
let mut flags: u32 = 0;
|
||||
update_deny_flags(uid, &process, &mut flags);
|
||||
if self.get_manager_uid(to_user_id(uid)) == uid {
|
||||
flags |= ZygiskStateFlags::ProcessIsMagiskApp.repr
|
||||
}
|
||||
if self.uid_granted_root(uid) {
|
||||
flags |= ZygiskStateFlags::ProcessGrantedRoot.repr
|
||||
}
|
||||
|
||||
// First send flags
|
||||
client.write_pod(&flags)?;
|
||||
|
||||
// Next send modules
|
||||
if zygisk_should_load_module(flags) {
|
||||
if let Some(module_fds) = self.get_module_fds(is_64_bit) {
|
||||
client.send_fds(&module_fds)?;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not in system_server, we are done
|
||||
if uid != 1000 || process != "system_server" {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Read all failed modules
|
||||
let failed_ids: Vec<i32> = client.read_decodable()?;
|
||||
if let Some(module_list) = self.module_list.get() {
|
||||
for id in failed_ids {
|
||||
let path = FsPathBuf::default()
|
||||
.join(MODULEROOT)
|
||||
.join(&module_list[id as usize].name)
|
||||
.join("zygisk");
|
||||
// Create the unloaded marker file
|
||||
if let Ok(dir) = Directory::open(&path) {
|
||||
dir.open_fd(cstr!("unloaded"), O_CREAT | O_RDONLY, 0o644)
|
||||
.log()
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_mod_dir(&self, mut client: UnixStream) -> LoggedResult<()> {
|
||||
let id: i32 = client.read_decodable()?;
|
||||
let module = &self.module_list.get().unwrap()[id as usize];
|
||||
let dir = FsPathBuf::default().join(MODULEROOT).join(&module.name);
|
||||
let fd = open_fd!(&dir, O_RDONLY | O_CLOEXEC)?;
|
||||
client.send_fds(&[fd.as_raw_fd()])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,229 +1,120 @@
|
||||
#include <libgen.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <android/log.h>
|
||||
#include <android/dlext.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
#include "zygisk.hpp"
|
||||
#include "module.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
string native_bridge = "0";
|
||||
|
||||
static bool is_compatible_with(uint32_t) {
|
||||
zygisk_logging();
|
||||
hook_entry();
|
||||
ZLOGD("load success\n");
|
||||
return false;
|
||||
static void zygiskd(int socket) {
|
||||
if (getuid() != 0 || fcntl(socket, F_GETFD) < 0)
|
||||
exit(-1);
|
||||
|
||||
init_thread_pool();
|
||||
|
||||
#if defined(__LP64__)
|
||||
set_nice_name("zygiskd64");
|
||||
LOGI("* Launching zygiskd64\n");
|
||||
#else
|
||||
set_nice_name("zygiskd32");
|
||||
LOGI("* Launching zygiskd32\n");
|
||||
#endif
|
||||
|
||||
// Load modules
|
||||
using comp_entry = void(*)(int);
|
||||
vector<comp_entry> modules;
|
||||
{
|
||||
auto module_fds = recv_fds(socket);
|
||||
for (int fd : module_fds) {
|
||||
comp_entry entry = nullptr;
|
||||
struct stat s{};
|
||||
if (fstat(fd, &s) == 0 && S_ISREG(s.st_mode)) {
|
||||
android_dlextinfo info {
|
||||
.flags = ANDROID_DLEXT_USE_LIBRARY_FD,
|
||||
.library_fd = fd,
|
||||
};
|
||||
if (void *h = android_dlopen_ext("/jit-cache", RTLD_LAZY, &info)) {
|
||||
*(void **) &entry = dlsym(h, "zygisk_companion_entry");
|
||||
} else {
|
||||
LOGW("Failed to dlopen zygisk module: %s\n", dlerror());
|
||||
}
|
||||
}
|
||||
modules.push_back(entry);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
// ack
|
||||
write_int(socket, 0);
|
||||
|
||||
// Start accepting requests
|
||||
pollfd pfd = { socket, POLLIN, 0 };
|
||||
for (;;) {
|
||||
poll(&pfd, 1, -1);
|
||||
if (pfd.revents && !(pfd.revents & POLLIN)) {
|
||||
// Something bad happened in magiskd, terminate zygiskd
|
||||
exit(0);
|
||||
}
|
||||
int client = recv_fd(socket);
|
||||
if (client < 0) {
|
||||
// Something bad happened in magiskd, terminate zygiskd
|
||||
exit(0);
|
||||
}
|
||||
int module_id = read_int(client);
|
||||
if (module_id >= 0 && module_id < modules.size() && modules[module_id]) {
|
||||
exec_task([=, entry = modules[module_id]] {
|
||||
struct stat s1;
|
||||
fstat(client, &s1);
|
||||
entry(client);
|
||||
// Only close client if it is the same file so we don't
|
||||
// accidentally close a re-used file descriptor.
|
||||
// This check is required because the module companion
|
||||
// handler could've closed the file descriptor already.
|
||||
if (struct stat s2; fstat(client, &s2) == 0) {
|
||||
if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
|
||||
close(client);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
close(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entrypoint where we need to re-exec ourselves
|
||||
// This should only ever be called internally
|
||||
int zygisk_main(int argc, char *argv[]) {
|
||||
android_logging();
|
||||
if (argc == 3 && argv[1] == "companion"sv) {
|
||||
zygiskd(parse_int(argv[2]));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Entrypoint of code injection
|
||||
extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf {
|
||||
.version = 2,
|
||||
.padding = {},
|
||||
.isCompatibleWith = &is_compatible_with,
|
||||
.isCompatibleWith = [](auto) {
|
||||
zygisk_logging();
|
||||
hook_entry();
|
||||
ZLOGD("load success\n");
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
// The following code runs in zygote/app process
|
||||
|
||||
static inline bool should_load_modules(uint32_t flags) {
|
||||
return (flags & UNMOUNT_MASK) != UNMOUNT_MASK &&
|
||||
(flags & PROCESS_IS_MAGISK_APP) != PROCESS_IS_MAGISK_APP;
|
||||
}
|
||||
|
||||
int remote_get_info(int uid, const char *process, uint32_t *flags, vector<int> &fds) {
|
||||
if (int fd = zygisk_request(ZygiskRequest::GET_INFO); fd >= 0) {
|
||||
write_int(fd, uid);
|
||||
write_string(fd, process);
|
||||
#ifdef __LP64__
|
||||
write_int(fd, 1);
|
||||
#else
|
||||
write_int(fd, 0);
|
||||
#endif
|
||||
xxread(fd, flags, sizeof(*flags));
|
||||
if (should_load_modules(*flags)) {
|
||||
fds = recv_fds(fd);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The following code runs in magiskd
|
||||
|
||||
static vector<int> get_module_fds(bool is_64_bit) {
|
||||
vector<int> fds;
|
||||
// All fds passed to send_fds have to be valid file descriptors.
|
||||
// To workaround this issue, send over STDOUT_FILENO as an indicator of an
|
||||
// invalid fd as it will always be /dev/null in magiskd
|
||||
#if defined(__LP64__)
|
||||
if (is_64_bit) {
|
||||
std::transform(module_list->begin(), module_list->end(), std::back_inserter(fds),
|
||||
[](const module_info &info) { return info.z64 < 0 ? STDOUT_FILENO : info.z64; });
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
std::transform(module_list->begin(), module_list->end(), std::back_inserter(fds),
|
||||
[](const module_info &info) { return info.z32 < 0 ? STDOUT_FILENO : info.z32; });
|
||||
}
|
||||
return fds;
|
||||
}
|
||||
|
||||
static pthread_mutex_t zygiskd_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int zygiskd_sockets[] = { -1, -1 };
|
||||
#define zygiskd_socket zygiskd_sockets[is_64_bit]
|
||||
|
||||
static void connect_companion(int client, bool is_64_bit) {
|
||||
mutex_guard g(zygiskd_lock);
|
||||
|
||||
if (zygiskd_socket >= 0) {
|
||||
// Make sure the socket is still valid
|
||||
pollfd pfd = { zygiskd_socket, 0, 0 };
|
||||
poll(&pfd, 1, 0);
|
||||
if (pfd.revents) {
|
||||
// Any revent means error
|
||||
close(zygiskd_socket);
|
||||
zygiskd_socket = -1;
|
||||
}
|
||||
}
|
||||
if (zygiskd_socket < 0) {
|
||||
int fds[2];
|
||||
socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
|
||||
zygiskd_socket = fds[0];
|
||||
if (fork_dont_care() == 0) {
|
||||
char exe[64];
|
||||
#if defined(__LP64__)
|
||||
ssprintf(exe, sizeof(exe), "%s/magisk%s", get_magisk_tmp(), (is_64_bit ? "" : "32"));
|
||||
#else
|
||||
ssprintf(exe, sizeof(exe), "%s/magisk", get_magisk_tmp());
|
||||
#endif
|
||||
// This fd has to survive exec
|
||||
fcntl(fds[1], F_SETFD, 0);
|
||||
char buf[16];
|
||||
ssprintf(buf, sizeof(buf), "%d", fds[1]);
|
||||
execl(exe, "", "zygisk", "companion", buf, (char *) nullptr);
|
||||
exit(-1);
|
||||
}
|
||||
close(fds[1]);
|
||||
vector<int> module_fds = get_module_fds(is_64_bit);
|
||||
send_fds(zygiskd_socket, module_fds.data(), module_fds.size());
|
||||
// Wait for ack
|
||||
if (read_int(zygiskd_socket) != 0) {
|
||||
LOGE("zygiskd startup error\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
send_fd(zygiskd_socket, client);
|
||||
}
|
||||
|
||||
static void get_process_info(int client, const sock_cred *cred) {
|
||||
int uid = read_int(client);
|
||||
string process = read_string(client);
|
||||
int arch = read_int(client);
|
||||
auto &daemon = MagiskD();
|
||||
|
||||
uint32_t flags = 0;
|
||||
|
||||
if (is_deny_target(uid, process)) {
|
||||
flags |= PROCESS_ON_DENYLIST;
|
||||
}
|
||||
if (daemon.get_manager(to_user_id(uid), nullptr, false) == uid) {
|
||||
flags |= PROCESS_IS_MAGISK_APP;
|
||||
}
|
||||
if (denylist_enforced) {
|
||||
flags |= DENYLIST_ENFORCING;
|
||||
}
|
||||
if (daemon.uid_granted_root(uid)) {
|
||||
flags |= PROCESS_GRANTED_ROOT;
|
||||
}
|
||||
|
||||
xwrite(client, &flags, sizeof(flags));
|
||||
|
||||
if (should_load_modules(flags)) {
|
||||
vector<int> fds = get_module_fds(arch);
|
||||
send_fds(client, fds.data(), fds.size());
|
||||
}
|
||||
|
||||
if (uid != 1000 || process != "system_server")
|
||||
return;
|
||||
|
||||
// Collect module status from system_server
|
||||
int slots = read_int(client);
|
||||
dynamic_bitset bits;
|
||||
for (int i = 0; i < slots; ++i) {
|
||||
dynamic_bitset::slot_type l = 0;
|
||||
xxread(client, &l, sizeof(l));
|
||||
bits.emplace_back(l);
|
||||
}
|
||||
for (int id = 0; id < module_list->size(); ++id) {
|
||||
if (!as_const(bits)[id]) {
|
||||
// Either not a zygisk module, or incompatible
|
||||
char buf[4096];
|
||||
ssprintf(buf, sizeof(buf), MODULEROOT "/%s/zygisk",
|
||||
module_list->operator[](id).name.data());
|
||||
if (int dirfd = open(buf, O_RDONLY | O_CLOEXEC); dirfd >= 0) {
|
||||
close(xopenat(dirfd, "unloaded", O_CREAT | O_RDONLY, 0644));
|
||||
close(dirfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_moddir(int client) {
|
||||
int id = read_int(client);
|
||||
char buf[4096];
|
||||
ssprintf(buf, sizeof(buf), MODULEROOT "/%s", module_list->operator[](id).name.data());
|
||||
int dfd = xopen(buf, O_RDONLY | O_CLOEXEC);
|
||||
send_fd(client, dfd);
|
||||
close(dfd);
|
||||
}
|
||||
|
||||
void zygisk_handler(int client, const sock_cred *cred) {
|
||||
int code = read_int(client);
|
||||
switch (code) {
|
||||
case ZygiskRequest::GET_INFO:
|
||||
get_process_info(client, cred);
|
||||
break;
|
||||
case ZygiskRequest::CONNECT_COMPANION: {
|
||||
int arch = read_int(client);
|
||||
connect_companion(client, arch);
|
||||
break;
|
||||
}
|
||||
case ZygiskRequest::GET_MODDIR:
|
||||
get_moddir(client);
|
||||
break;
|
||||
default:
|
||||
// Unknown code
|
||||
break;
|
||||
}
|
||||
close(client);
|
||||
}
|
||||
|
||||
void reset_zygisk(bool restore) {
|
||||
if (!zygisk_enabled) return;
|
||||
static atomic_uint zygote_start_count{1};
|
||||
if (!restore) {
|
||||
close(zygiskd_sockets[0]);
|
||||
close(zygiskd_sockets[1]);
|
||||
zygiskd_sockets[0] = zygiskd_sockets[1] = -1;
|
||||
}
|
||||
if (restore) {
|
||||
zygote_start_count = 1;
|
||||
} else if (zygote_start_count.fetch_add(1) > 3) {
|
||||
LOGW("zygote crashes too many times, rolling-back\n");
|
||||
restore = true;
|
||||
}
|
||||
if (restore) {
|
||||
string native_bridge_orig = "0";
|
||||
if (native_bridge.length() > strlen(ZYGISKLDR)) {
|
||||
native_bridge_orig = native_bridge.substr(strlen(ZYGISKLDR));
|
||||
}
|
||||
set_prop(NBPROP, native_bridge_orig.data());
|
||||
} else {
|
||||
set_prop(NBPROP, native_bridge.data());
|
||||
}
|
||||
void restore_zygisk_prop() {
|
||||
string native_bridge_orig = "0";
|
||||
if (native_bridge.length() > strlen(ZYGISKLDR)) {
|
||||
native_bridge_orig = native_bridge.substr(strlen(ZYGISKLDR));
|
||||
}
|
||||
set_prop(NBPROP, native_bridge_orig.data());
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
#include <sys/mount.h>
|
||||
#include <android/dlext.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
#include "zygisk.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void zygiskd(int socket) {
|
||||
if (getuid() != 0 || fcntl(socket, F_GETFD) < 0)
|
||||
exit(-1);
|
||||
|
||||
init_thread_pool();
|
||||
|
||||
#if defined(__LP64__)
|
||||
set_nice_name("zygiskd64");
|
||||
LOGI("* Launching zygiskd64\n");
|
||||
#else
|
||||
set_nice_name("zygiskd32");
|
||||
LOGI("* Launching zygiskd32\n");
|
||||
#endif
|
||||
|
||||
// Load modules
|
||||
using comp_entry = void(*)(int);
|
||||
vector<comp_entry> modules;
|
||||
{
|
||||
vector<int> module_fds = recv_fds(socket);
|
||||
for (int fd : module_fds) {
|
||||
comp_entry entry = nullptr;
|
||||
struct stat s{};
|
||||
if (fstat(fd, &s) == 0 && S_ISREG(s.st_mode)) {
|
||||
android_dlextinfo info {
|
||||
.flags = ANDROID_DLEXT_USE_LIBRARY_FD,
|
||||
.library_fd = fd,
|
||||
};
|
||||
if (void *h = android_dlopen_ext("/jit-cache", RTLD_LAZY, &info)) {
|
||||
*(void **) &entry = dlsym(h, "zygisk_companion_entry");
|
||||
} else {
|
||||
LOGW("Failed to dlopen zygisk module: %s\n", dlerror());
|
||||
}
|
||||
}
|
||||
modules.push_back(entry);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
// ack
|
||||
write_int(socket, 0);
|
||||
|
||||
// Start accepting requests
|
||||
pollfd pfd = { socket, POLLIN, 0 };
|
||||
for (;;) {
|
||||
poll(&pfd, 1, -1);
|
||||
if (pfd.revents && !(pfd.revents & POLLIN)) {
|
||||
// Something bad happened in magiskd, terminate zygiskd
|
||||
exit(0);
|
||||
}
|
||||
int client = recv_fd(socket);
|
||||
if (client < 0) {
|
||||
// Something bad happened in magiskd, terminate zygiskd
|
||||
exit(0);
|
||||
}
|
||||
int module_id = read_int(client);
|
||||
if (module_id >= 0 && module_id < modules.size() && modules[module_id]) {
|
||||
exec_task([=, entry = modules[module_id]] {
|
||||
struct stat s1;
|
||||
fstat(client, &s1);
|
||||
entry(client);
|
||||
// Only close client if it is the same file so we don't
|
||||
// accidentally close a re-used file descriptor.
|
||||
// This check is required because the module companion
|
||||
// handler could've closed the file descriptor already.
|
||||
if (struct stat s2; fstat(client, &s2) == 0) {
|
||||
if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) {
|
||||
close(client);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
close(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entrypoint where we need to re-exec ourselves
|
||||
// This should only ever be called internally
|
||||
int zygisk_main(int argc, char *argv[]) {
|
||||
android_logging();
|
||||
if (argc == 3 && argv[1] == "companion"sv) {
|
||||
zygiskd(parse_int(argv[2]));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
3
native/src/core/zygisk/mod.rs
Normal file
3
native/src/core/zygisk/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod daemon;
|
||||
|
||||
pub use daemon::zygisk_should_load_module;
|
||||
@@ -77,11 +77,11 @@ bool ZygiskModule::valid() const {
|
||||
}
|
||||
|
||||
int ZygiskModule::connectCompanion() const {
|
||||
if (int fd = zygisk_request(ZygiskRequest::CONNECT_COMPANION); fd >= 0) {
|
||||
if (int fd = zygisk_request(+ZygiskRequest::ConnectCompanion); fd >= 0) {
|
||||
#ifdef __LP64__
|
||||
write_int(fd, 1);
|
||||
write_any<bool>(fd, true);
|
||||
#else
|
||||
write_int(fd, 0);
|
||||
write_any<bool>(fd, false);
|
||||
#endif
|
||||
write_int(fd, id);
|
||||
return fd;
|
||||
@@ -90,11 +90,9 @@ int ZygiskModule::connectCompanion() const {
|
||||
}
|
||||
|
||||
int ZygiskModule::getModuleDir() const {
|
||||
if (int fd = zygisk_request(ZygiskRequest::GET_MODDIR); fd >= 0) {
|
||||
if (owned_fd fd = zygisk_request(+ZygiskRequest::GetModDir); fd >= 0) {
|
||||
write_int(fd, id);
|
||||
int dfd = recv_fd(fd);
|
||||
close(fd);
|
||||
return dfd;
|
||||
return recv_fd(fd);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -210,6 +208,24 @@ bool ZygiskContext::plt_hook_commit() {
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
int ZygiskContext::get_module_info(int uid, rust::Vec<int> &fds) {
|
||||
if (int fd = zygisk_request(+ZygiskRequest::GetInfo); fd >= 0) {
|
||||
write_int(fd, uid);
|
||||
write_string(fd, process);
|
||||
#ifdef __LP64__
|
||||
write_any<bool>(fd, true);
|
||||
#else
|
||||
write_any<bool>(fd, false);
|
||||
#endif
|
||||
xxread(fd, &info_flags, sizeof(info_flags));
|
||||
if (zygisk_should_load_module(info_flags)) {
|
||||
fds = recv_fds(fd);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ZygiskContext::sanitize_fds() {
|
||||
zygisk_close_logd();
|
||||
|
||||
@@ -315,16 +331,17 @@ void ZygiskContext::fork_post() {
|
||||
sigmask(SIG_UNBLOCK, SIGCHLD);
|
||||
}
|
||||
|
||||
void ZygiskContext::run_modules_pre(const vector<int> &fds) {
|
||||
void ZygiskContext::run_modules_pre(rust::Vec<int> &fds) {
|
||||
for (int i = 0; i < fds.size(); ++i) {
|
||||
owned_fd fd = fds[i];
|
||||
struct stat s{};
|
||||
if (fstat(fds[i], &s) != 0 || !S_ISREG(s.st_mode)) {
|
||||
close(fds[i]);
|
||||
if (fstat(fd, &s) != 0 || !S_ISREG(s.st_mode)) {
|
||||
fds[i] = -1;
|
||||
continue;
|
||||
}
|
||||
android_dlextinfo info {
|
||||
.flags = ANDROID_DLEXT_USE_LIBRARY_FD,
|
||||
.library_fd = fds[i],
|
||||
.library_fd = fd,
|
||||
};
|
||||
if (void *h = android_dlopen_ext("/jit-cache", RTLD_LAZY, &info)) {
|
||||
if (void *e = dlsym(h, "zygisk_module_entry")) {
|
||||
@@ -332,8 +349,8 @@ void ZygiskContext::run_modules_pre(const vector<int> &fds) {
|
||||
}
|
||||
} else if (flags & SERVER_FORK_AND_SPECIALIZE) {
|
||||
ZLOGW("Failed to dlopen zygisk module: %s\n", dlerror());
|
||||
fds[i] = -1;
|
||||
}
|
||||
close(fds[i]);
|
||||
}
|
||||
|
||||
for (auto it = modules.begin(); it != modules.end();) {
|
||||
@@ -369,20 +386,19 @@ void ZygiskContext::run_modules_post() {
|
||||
void ZygiskContext::app_specialize_pre() {
|
||||
flags |= APP_SPECIALIZE;
|
||||
|
||||
vector<int> module_fds;
|
||||
int fd = remote_get_info(args.app->uid, process, &info_flags, module_fds);
|
||||
rust::Vec<int> module_fds;
|
||||
owned_fd fd = get_module_info(args.app->uid, module_fds);
|
||||
if ((info_flags & UNMOUNT_MASK) == UNMOUNT_MASK) {
|
||||
ZLOGI("[%s] is on the denylist\n", process);
|
||||
flags |= DO_REVERT_UNMOUNT;
|
||||
} else if (fd >= 0) {
|
||||
run_modules_pre(module_fds);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void ZygiskContext::app_specialize_post() {
|
||||
run_modules_post();
|
||||
if (info_flags & PROCESS_IS_MAGISK_APP) {
|
||||
if (info_flags & +ZygiskStateFlags::ProcessIsMagiskApp) {
|
||||
setenv("ZYGISK_ENABLED", "1", 1);
|
||||
}
|
||||
|
||||
@@ -391,25 +407,22 @@ void ZygiskContext::app_specialize_post() {
|
||||
}
|
||||
|
||||
void ZygiskContext::server_specialize_pre() {
|
||||
vector<int> module_fds;
|
||||
int fd = remote_get_info(1000, "system_server", &info_flags, module_fds);
|
||||
if (fd >= 0) {
|
||||
rust::Vec<int> module_fds;
|
||||
if (owned_fd fd = get_module_info(1000, module_fds); fd >= 0) {
|
||||
if (module_fds.empty()) {
|
||||
write_int(fd, 0);
|
||||
} else {
|
||||
run_modules_pre(module_fds);
|
||||
|
||||
// Send the bitset of module status back to magiskd from system_server
|
||||
dynamic_bitset bits;
|
||||
for (const auto &m : modules)
|
||||
bits[m.getId()] = true;
|
||||
write_int(fd, static_cast<int>(bits.slots()));
|
||||
for (int i = 0; i < bits.slots(); ++i) {
|
||||
auto l = bits.get_slot(i);
|
||||
xwrite(fd, &l, sizeof(l));
|
||||
// Find all failed module ids and send it back to magiskd
|
||||
vector<int> failed_ids;
|
||||
for (int i = 0; i < module_fds.size(); ++i) {
|
||||
if (module_fds[i] < 0) {
|
||||
failed_ids.push_back(i);
|
||||
}
|
||||
}
|
||||
write_vector(fd, failed_ids);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,6 +448,7 @@ void ZygiskContext::nativeSpecializeAppProcess_post() {
|
||||
void ZygiskContext::nativeForkSystemServer_pre() {
|
||||
ZLOGV("pre forkSystemServer\n");
|
||||
flags |= SERVER_FORK_AND_SPECIALIZE;
|
||||
process = "system_server";
|
||||
|
||||
fork_pre();
|
||||
if (is_child()) {
|
||||
|
||||
@@ -122,15 +122,13 @@ struct module_abi_v1 {
|
||||
void (*postServerSpecialize)(void *, const void *);
|
||||
};
|
||||
|
||||
// Assert the flag values to be the same as the public API
|
||||
static_assert(+ZygiskStateFlags::ProcessGrantedRoot == zygisk::StateFlag::PROCESS_GRANTED_ROOT);
|
||||
static_assert(+ZygiskStateFlags::ProcessOnDenyList == zygisk::StateFlag::PROCESS_ON_DENYLIST);
|
||||
|
||||
enum : uint32_t {
|
||||
PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT,
|
||||
PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST,
|
||||
|
||||
DENYLIST_ENFORCING = (1u << 30),
|
||||
PROCESS_IS_MAGISK_APP = (1u << 31),
|
||||
|
||||
UNMOUNT_MASK = (PROCESS_ON_DENYLIST | DENYLIST_ENFORCING),
|
||||
PRIVATE_MASK = (DENYLIST_ENFORCING | PROCESS_IS_MAGISK_APP)
|
||||
UNMOUNT_MASK = (+ZygiskStateFlags::ProcessOnDenyList | +ZygiskStateFlags::DenyListEnforced),
|
||||
PRIVATE_MASK = (+ZygiskStateFlags::DenyListEnforced | +ZygiskStateFlags::ProcessIsMagiskApp)
|
||||
};
|
||||
|
||||
struct api_abi_base {
|
||||
@@ -188,7 +186,6 @@ struct ZygiskModule {
|
||||
static uint32_t getFlags();
|
||||
void tryUnload() const;
|
||||
void clearApi() { memset(&api, 0, sizeof(api)); }
|
||||
int getId() const { return id; }
|
||||
|
||||
ZygiskModule(int id, void *handle, void *entry);
|
||||
|
||||
@@ -264,7 +261,7 @@ struct ZygiskContext {
|
||||
ZygiskContext(JNIEnv *env, void *args);
|
||||
~ZygiskContext();
|
||||
|
||||
void run_modules_pre(const std::vector<int> &fds);
|
||||
void run_modules_pre(rust::Vec<int> &fds);
|
||||
void run_modules_post();
|
||||
DCL_PRE_POST(fork)
|
||||
DCL_PRE_POST(app_specialize)
|
||||
@@ -273,6 +270,7 @@ struct ZygiskContext {
|
||||
DCL_PRE_POST(nativeSpecializeAppProcess)
|
||||
DCL_PRE_POST(nativeForkSystemServer)
|
||||
|
||||
int get_module_info(int uid, rust::Vec<int> &fds);
|
||||
void sanitize_fds();
|
||||
bool exempt_fd(int fd);
|
||||
bool can_exempt_fd() const;
|
||||
|
||||
@@ -6,15 +6,6 @@
|
||||
#include <vector>
|
||||
#include <core.hpp>
|
||||
|
||||
namespace ZygiskRequest {
|
||||
enum : int {
|
||||
GET_INFO,
|
||||
CONNECT_COMPANION,
|
||||
GET_MODDIR,
|
||||
END
|
||||
};
|
||||
}
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define ZLOGD(...) LOGD("zygisk64: " __VA_ARGS__)
|
||||
#define ZLOGE(...) LOGE("zygisk64: " __VA_ARGS__)
|
||||
@@ -34,8 +25,6 @@ enum : int {
|
||||
void hook_entry();
|
||||
void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods, int numMethods);
|
||||
|
||||
int remote_get_info(int uid, const char *process, uint32_t *flags, std::vector<int> &fds);
|
||||
|
||||
inline int zygisk_request(int req) {
|
||||
int fd = connect_daemon(+RequestCode::ZYGISK);
|
||||
if (fd < 0) return fd;
|
||||
|
||||
42
native/src/external/Android.mk
vendored
42
native/src/external/Android.mk
vendored
@@ -23,21 +23,6 @@ LOCAL_SRC_FILES := \
|
||||
lz4/lib/xxhash.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# libbz2.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libbz2
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/bzip2
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_SRC_FILES := \
|
||||
bzip2/blocksort.c \
|
||||
bzip2/huffman.c \
|
||||
bzip2/crctable.c \
|
||||
bzip2/randtable.c \
|
||||
bzip2/compress.c \
|
||||
bzip2/decompress.c \
|
||||
bzip2/bzlib.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# liblzma.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := liblzma
|
||||
@@ -233,33 +218,6 @@ LOCAL_SRC_FILES := \
|
||||
lsplt/lsplt/src/main/jni/lsplt.cc
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# libz.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libz
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/zlib
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_CFLAGS := \
|
||||
-DHAVE_HIDDEN -DZLIB_CONST -Wall -Werror \
|
||||
-Wno-unused -Wno-unused-parameter -Wno-deprecated-non-prototype
|
||||
LOCAL_SRC_FILES := \
|
||||
zlib/adler32.c \
|
||||
zlib/compress.c \
|
||||
zlib/cpu_features.c \
|
||||
zlib/crc32.c \
|
||||
zlib/deflate.c \
|
||||
zlib/gzclose.c \
|
||||
zlib/gzlib.c \
|
||||
zlib/gzread.c \
|
||||
zlib/gzwrite.c \
|
||||
zlib/infback.c \
|
||||
zlib/inffast.c \
|
||||
zlib/inflate.c \
|
||||
zlib/inftrees.c \
|
||||
zlib/trees.c \
|
||||
zlib/uncompr.c \
|
||||
zlib/zutil.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# libzopfli.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libzopfli
|
||||
|
||||
1
native/src/external/bzip2
vendored
1
native/src/external/bzip2
vendored
Submodule native/src/external/bzip2 deleted from 67d818584d
2
native/src/external/cxx-rs
vendored
2
native/src/external/cxx-rs
vendored
Submodule native/src/external/cxx-rs updated: 08cb634704...0ea6d507ce
1
native/src/external/zlib
vendored
1
native/src/external/zlib
vendored
Submodule native/src/external/zlib deleted from 3a0aa2b3df
@@ -39,7 +39,7 @@ fn write_if_diff<P: AsRef<Path>>(path: P, bytes: &[u8]) -> io::Result<()> {
|
||||
pub fn gen_cxx_binding(name: &str) {
|
||||
println!("cargo:rerun-if-changed=lib.rs");
|
||||
let opt = Opt::default();
|
||||
let gen = cxx_gen::generate_header_and_cc_with_path("lib.rs", &opt);
|
||||
write_if_diff(format!("{}.cpp", name), gen.implementation.as_slice()).ok_or_exit();
|
||||
write_if_diff(format!("{}.hpp", name), gen.header.as_slice()).ok_or_exit();
|
||||
let code = cxx_gen::generate_header_and_cc_with_path("lib.rs", &opt);
|
||||
write_if_diff(format!("{}.cpp", name), code.implementation.as_slice()).ok_or_exit();
|
||||
write_if_diff(format!("{}.hpp", name), code.header.as_slice()).ok_or_exit();
|
||||
}
|
||||
@@ -13,7 +13,7 @@ pub const APP_PACKAGE_NAME: &str = "com.topjohnwu.magisk";
|
||||
pub const LOGFILE: &str = "/cache/magisk.log";
|
||||
|
||||
// data paths
|
||||
const SECURE_DIR: &str = "/data/adb";
|
||||
pub const SECURE_DIR: &str = "/data/adb";
|
||||
pub const MODULEROOT: &str = concatcp!(SECURE_DIR, "/modules");
|
||||
|
||||
// tmpfs paths
|
||||
@@ -25,3 +25,5 @@ pub const MODULEMNT: &str = concatcp!(INTERNAL_DIR, "/modules");
|
||||
pub const WORKERDIR: &str = concatcp!(INTERNAL_DIR, "/worker");
|
||||
pub const DEVICEDIR: &str = concatcp!(INTERNAL_DIR, "/device");
|
||||
pub const PREINITDEV: &str = concatcp!(DEVICEDIR, "/preinit");
|
||||
pub const ROOTOVL: &str = concatcp!(INTERNAL_DIR, "/rootdir");
|
||||
pub const ROOTMNT: &str = concatcp!(ROOTOVL, "/.mount_list");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "magiskinit"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
@@ -12,5 +12,5 @@ cxx-gen = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
base = { path = "../base" }
|
||||
magiskpolicy = { path = "../sepolicy" }
|
||||
magiskpolicy = { path = "../sepolicy", default-features = false }
|
||||
cxx = { workspace = true }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::gen::gen_cxx_binding;
|
||||
use crate::codegen::gen_cxx_binding;
|
||||
|
||||
#[path = "../include/gen.rs"]
|
||||
mod gen;
|
||||
#[path = "../include/codegen.rs"]
|
||||
mod codegen;
|
||||
|
||||
fn main() {
|
||||
gen_cxx_binding("init-rs");
|
||||
|
||||
@@ -118,7 +118,7 @@ static bool check_key_combo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void BootConfig::set(const kv_pairs &kv) {
|
||||
void BootConfig::set(const kv_pairs &kv) noexcept {
|
||||
for (const auto &[key, value] : kv) {
|
||||
if (key == "androidboot.slot_suffix") {
|
||||
// Many Amlogic devices are A-only but have slot_suffix...
|
||||
@@ -126,10 +126,10 @@ void BootConfig::set(const kv_pairs &kv) {
|
||||
LOGW("Skip invalid androidboot.slot_suffix=[normal]\n");
|
||||
continue;
|
||||
}
|
||||
strscpy(slot, value.data(), sizeof(slot));
|
||||
strscpy(slot.data(), value.data(), slot.size());
|
||||
} else if (key == "androidboot.slot") {
|
||||
slot[0] = '_';
|
||||
strscpy(slot + 1, value.data(), sizeof(slot) - 1);
|
||||
strscpy(slot.data() + 1, value.data(), slot.size() - 1);
|
||||
} else if (key == "skip_initramfs") {
|
||||
skip_initramfs = true;
|
||||
} else if (key == "androidboot.force_normal_boot") {
|
||||
@@ -137,13 +137,13 @@ void BootConfig::set(const kv_pairs &kv) {
|
||||
} else if (key == "rootwait") {
|
||||
rootwait = true;
|
||||
} else if (key == "androidboot.android_dt_dir") {
|
||||
strscpy(dt_dir, value.data(), sizeof(dt_dir));
|
||||
strscpy(dt_dir.data(), value.data(), dt_dir.size());
|
||||
} else if (key == "androidboot.hardware") {
|
||||
strscpy(hardware, value.data(), sizeof(hardware));
|
||||
strscpy(hardware.data(), value.data(), hardware.size());
|
||||
} else if (key == "androidboot.hardware.platform") {
|
||||
strscpy(hardware_plat, value.data(), sizeof(hardware_plat));
|
||||
strscpy(hardware_plat.data(), value.data(), hardware_plat.size());
|
||||
} else if (key == "androidboot.fstab_suffix") {
|
||||
strscpy(fstab_suffix, value.data(), sizeof(fstab_suffix));
|
||||
strscpy(fstab_suffix.data(), value.data(), fstab_suffix.size());
|
||||
} else if (key == "qemu") {
|
||||
emulator = true;
|
||||
} else if (key == "androidboot.partition_map") {
|
||||
@@ -151,34 +151,24 @@ void BootConfig::set(const kv_pairs &kv) {
|
||||
// For example, "androidboot.partition_map=vdb,metadata;vdc,userdata" maps
|
||||
// "vdb" to "metadata", and "vdc" to "userdata".
|
||||
// https://android.googlesource.com/platform/system/core/+/refs/heads/android13-release/init/devices.cpp#191
|
||||
partition_map = parse_partition_map(value);
|
||||
for (const auto &[k, v]: parse_partition_map(value)) {
|
||||
partition_map.emplace_back(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BootConfig::print() {
|
||||
LOGD("skip_initramfs=[%d]\n", skip_initramfs);
|
||||
LOGD("force_normal_boot=[%d]\n", force_normal_boot);
|
||||
LOGD("rootwait=[%d]\n", rootwait);
|
||||
LOGD("slot=[%s]\n", slot);
|
||||
LOGD("dt_dir=[%s]\n", dt_dir);
|
||||
LOGD("fstab_suffix=[%s]\n", fstab_suffix);
|
||||
LOGD("hardware=[%s]\n", hardware);
|
||||
LOGD("hardware.platform=[%s]\n", hardware_plat);
|
||||
LOGD("emulator=[%d]\n", emulator);
|
||||
}
|
||||
|
||||
#define read_dt(name, key) \
|
||||
ssprintf(file_name, sizeof(file_name), "%s/" name, dt_dir); \
|
||||
ssprintf(file_name, sizeof(file_name), "%s/" name, dt_dir.data()); \
|
||||
if (access(file_name, R_OK) == 0) { \
|
||||
string data = full_read(file_name); \
|
||||
if (!data.empty()) { \
|
||||
data.pop_back(); \
|
||||
strscpy(key, data.data(), sizeof(key)); \
|
||||
strscpy(key.data(), data.data(), key.size()); \
|
||||
} \
|
||||
}
|
||||
|
||||
void BootConfig::init() {
|
||||
void BootConfig::init() noexcept {
|
||||
set(parse_cmdline(full_read("/proc/cmdline")));
|
||||
set(parse_bootconfig(full_read("/proc/bootconfig")));
|
||||
|
||||
@@ -191,7 +181,7 @@ void BootConfig::init() {
|
||||
});
|
||||
|
||||
if (dt_dir[0] == '\0')
|
||||
strscpy(dt_dir, DEFAULT_DT_DIR, sizeof(dt_dir));
|
||||
strscpy(dt_dir.data(), DEFAULT_DT_DIR, dt_dir.size());
|
||||
|
||||
char file_name[128];
|
||||
read_dt("fstab_suffix", fstab_suffix)
|
||||
@@ -201,34 +191,3 @@ void BootConfig::init() {
|
||||
LOGD("Device config:\n");
|
||||
print();
|
||||
}
|
||||
|
||||
bool check_two_stage() {
|
||||
if (access("/first_stage_ramdisk", F_OK) == 0)
|
||||
return true;
|
||||
if (access("/second_stage_resources", F_OK) == 0)
|
||||
return true;
|
||||
if (access("/system/bin/init", F_OK) == 0)
|
||||
return true;
|
||||
// Use the apex folder to determine whether 2SI (Android 10+)
|
||||
if (access("/apex", F_OK) == 0)
|
||||
return true;
|
||||
// If we still have no indication, parse the original init and see what's up
|
||||
mmap_data init(backup_init());
|
||||
return init.contains("selinux_setup");
|
||||
}
|
||||
|
||||
static void unxz_init(const char *init_xz, const char *init) {
|
||||
LOGD("unxz %s -> %s\n", init_xz, init);
|
||||
int fd = xopen(init, O_WRONLY | O_CREAT, 0777);
|
||||
fd_stream ch(fd);
|
||||
unxz(ch, mmap_data{init_xz});
|
||||
close(fd);
|
||||
clone_attr(init_xz, init);
|
||||
unlink(init_xz);
|
||||
}
|
||||
|
||||
const char *backup_init() {
|
||||
if (access("/.backup/init.xz", F_OK) == 0)
|
||||
unxz_init("/.backup/init.xz", "/.backup/init");
|
||||
return "/.backup/init";
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user