mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-12 01:11:50 +00:00
Compare commits
181 Commits
canary-281
...
v29.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62b1310d97 | ||
|
|
0a86916d3a | ||
|
|
9907ce57aa | ||
|
|
b92626cacc | ||
|
|
5a762f0a8e | ||
|
|
5dd7a7d804 | ||
|
|
7831f40691 | ||
|
|
4f4b1ff885 | ||
|
|
97901979dd | ||
|
|
287316842c | ||
|
|
608786e8f3 | ||
|
|
9684a35cab | ||
|
|
e3e4202954 | ||
|
|
23c2054d46 | ||
|
|
a20a2a8fa0 | ||
|
|
a2896be4a6 | ||
|
|
e9220a28d9 | ||
|
|
cf12087e21 | ||
|
|
00c1b36837 | ||
|
|
03e034795d | ||
|
|
79c0fafe43 | ||
|
|
d499819ba0 | ||
|
|
86da917174 | ||
|
|
30bd7d6555 | ||
|
|
e5a12f0f5f | ||
|
|
c85a8434c6 | ||
|
|
427a1ca4e5 | ||
|
|
22884e173a | ||
|
|
d1829308e9 | ||
|
|
73840f8721 | ||
|
|
c7d1af9805 | ||
|
|
4ad26d3dfb | ||
|
|
0c70b7670c | ||
|
|
f44d044095 | ||
|
|
5c1cb13472 | ||
|
|
3327fc668e | ||
|
|
610945ac54 | ||
|
|
ddf5474917 | ||
|
|
6ba1685ade | ||
|
|
e02b5f7868 | ||
|
|
ab2e5d1e7e | ||
|
|
f3fef7bfe4 | ||
|
|
c34c7838bb | ||
|
|
c8a16b0e0c | ||
|
|
14f9ed91a1 | ||
|
|
7a207d4ccf | ||
|
|
92a42d901f | ||
|
|
084d89fcce | ||
|
|
55b036c071 | ||
|
|
30e79310ab | ||
|
|
f063fa5054 | ||
|
|
7bd901273c | ||
|
|
c1e061603b | ||
|
|
cb08504fe5 | ||
|
|
c0a1fb77be | ||
|
|
4864c1112a | ||
|
|
9ddeab034b | ||
|
|
c4847ed288 | ||
|
|
b8f1523fb2 | ||
|
|
fb7fa8a6b3 | ||
|
|
9c7d359093 | ||
|
|
eb54bc1fd7 | ||
|
|
d4a0286e13 | ||
|
|
83e66767ff | ||
|
|
7dc010749b | ||
|
|
8e8d013b1b | ||
|
|
bba0373808 | ||
|
|
1fa318dc8c | ||
|
|
6edc5e2037 | ||
|
|
1523ed9f78 | ||
|
|
8e604d2ab8 | ||
|
|
2aba7247a9 | ||
|
|
e66fe8533e | ||
|
|
b03fbb3917 | ||
|
|
c2ece62e4c | ||
|
|
8c972dcf34 | ||
|
|
50af14f2a3 | ||
|
|
e0a356b319 | ||
|
|
c09a792958 | ||
|
|
0bbfe7f44d | ||
|
|
a396abf565 | ||
|
|
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 |
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -12,13 +12,11 @@
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
tools/** binary
|
||||
tools/rustup-wrapper/** -binary
|
||||
tools/elf-cleaner/** -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
|
||||
|
||||
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
@@ -1,6 +1,13 @@
|
||||
name: Magisk Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- "app/**"
|
||||
- "native/**"
|
||||
- "build.py"
|
||||
- ".github/workflows/build.yml"
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
@@ -29,7 +36,7 @@ jobs:
|
||||
run: ./build.py -v all
|
||||
|
||||
- name: Stop gradle daemon
|
||||
run: ./gradlew --stop
|
||||
run: ./app/gradlew --stop
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -65,21 +72,22 @@ jobs:
|
||||
run: python build.py -v -c .github/ci.prop all
|
||||
|
||||
- name: Stop gradle daemon
|
||||
run: ./gradlew --stop
|
||||
run: ./app/gradlew --stop
|
||||
|
||||
avd-test:
|
||||
name: Test API ${{ matrix.version }} (x86_64)
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
|
||||
type: [""]
|
||||
include:
|
||||
- version: "Baklava"
|
||||
- version: 36
|
||||
type: "google_apis"
|
||||
- version: "Baklava"
|
||||
- version: 36
|
||||
type: "google_apis_ps16k"
|
||||
|
||||
steps:
|
||||
@@ -117,6 +125,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,14 +167,15 @@ 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:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- branch: "aosp-main"
|
||||
device: "aosp_cf_x86_64_phone"
|
||||
- branch: "aosp-android-latest-release"
|
||||
device: "aosp_cf_x86_64_only_phone"
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
@@ -184,7 +194,7 @@ jobs:
|
||||
|
||||
- name: Run Cuttlefish test
|
||||
timeout-minutes: 10
|
||||
run: su $USER -c 'scripts/cuttlefish.sh test'
|
||||
run: sudo -E -u $USER scripts/cuttlefish.sh test
|
||||
|
||||
- name: Upload logs on error
|
||||
if: ${{ failure() }}
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,19 +2,13 @@ out
|
||||
*.zip
|
||||
*.jks
|
||||
*.apk
|
||||
*.log
|
||||
/config.prop
|
||||
/notes.md
|
||||
/update.sh
|
||||
/app/dict.txt
|
||||
|
||||
# Built binaries
|
||||
native/out
|
||||
|
||||
# Android Studio / Gradle
|
||||
# Android Studio
|
||||
*.iml
|
||||
.gradle
|
||||
.idea
|
||||
.kotlin
|
||||
/local.properties
|
||||
/build
|
||||
/captures
|
||||
|
||||
9
.gitmodules
vendored
9
.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
|
||||
@@ -31,6 +25,3 @@
|
||||
[submodule "crt0"]
|
||||
path = native/src/external/crt0
|
||||
url = https://github.com/topjohnwu/crt0.git
|
||||
[submodule "termux-elf-cleaner"]
|
||||
path = tools/termux-elf-cleaner
|
||||
url = https://github.com/termux/termux-elf-cleaner.git
|
||||
|
||||
@@ -21,8 +21,8 @@ Some highlight features:
|
||||
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/v29.0)
|
||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28104)
|
||||
|
||||
## Useful Links
|
||||
|
||||
|
||||
7
app/.gitignore
vendored
Normal file
7
app/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/dict.txt
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
.kotlin
|
||||
/local.properties
|
||||
/build
|
||||
@@ -35,7 +35,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app:core"))
|
||||
implementation(project(":core"))
|
||||
coreLibraryDesugaring(libs.jdk.libs)
|
||||
|
||||
implementation(libs.indeterminate.checkbox)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.view.KeyEvent
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.navOptions
|
||||
import com.topjohnwu.magisk.utils.AccessibilityUtils
|
||||
|
||||
abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Binding>() {
|
||||
|
||||
@@ -31,7 +34,17 @@ abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Bindin
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun navigate(directions: NavDirections, navigation: NavController, cr: ContentResolver) {
|
||||
if (AccessibilityUtils.isAnimationEnabled(cr)) {
|
||||
navigation.navigate(directions)
|
||||
} else {
|
||||
navigation.navigate(directions, navOptions {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun NavDirections.navigate() {
|
||||
navigation.navigate(this)
|
||||
navigate(this, navigation, contentResolver)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import com.topjohnwu.magisk.core.Info
|
||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||
import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
import androidx.navigation.findNavController
|
||||
import com.topjohnwu.magisk.arch.NavigationActivity
|
||||
|
||||
class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
|
||||
|
||||
@@ -68,7 +70,13 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
|
||||
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_settings ->
|
||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
|
||||
activity?.let {
|
||||
NavigationActivity.navigate(
|
||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment(),
|
||||
it.findNavController(R.id.main_nav_host),
|
||||
it.contentResolver,
|
||||
)
|
||||
}
|
||||
R.id.action_reboot -> activity?.let { RebootMenu.inflate(it).show() }
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.HorizontalScrollView
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.isVisible
|
||||
import com.topjohnwu.magisk.R
|
||||
@@ -12,6 +13,7 @@ import com.topjohnwu.magisk.arch.BaseFragment
|
||||
import com.topjohnwu.magisk.arch.viewModel
|
||||
import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding
|
||||
import com.topjohnwu.magisk.ui.MainActivity
|
||||
import com.topjohnwu.magisk.utils.AccessibilityUtils
|
||||
import com.topjohnwu.magisk.utils.MotionRevealHelper
|
||||
import rikka.recyclerview.addEdgeSpacing
|
||||
import rikka.recyclerview.addItemSpacing
|
||||
@@ -56,6 +58,11 @@ class LogFragment : BaseFragment<FragmentLogMd2Binding>(), MenuProvider {
|
||||
addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1)
|
||||
fixEdgeEffect()
|
||||
}
|
||||
|
||||
if (!AccessibilityUtils.isAnimationEnabled(requireContext().contentResolver)) {
|
||||
val scrollView = view.findViewById<HorizontalScrollView>(R.id.log_scroll_magisk)
|
||||
scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.topjohnwu.magisk.utils
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.provider.Settings
|
||||
|
||||
class AccessibilityUtils {
|
||||
companion object {
|
||||
fun isAnimationEnabled(cr: ContentResolver): Boolean {
|
||||
return !(Settings.Global.getFloat(cr, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0.0f
|
||||
&& Settings.Global.getFloat(cr, Settings.Global.TRANSITION_ANIMATION_SCALE, 1.0f) == 0.0f
|
||||
&& Settings.Global.getFloat(cr, Settings.Global.WINDOW_ANIMATION_SCALE, 1.0f) == 0.0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/log_scroll_magisk"
|
||||
gone="@{viewModel.loading}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
tasks.register("clean") {
|
||||
plugins {
|
||||
id("MagiskPlugin")
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete::class) {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
|
||||
subprojects.forEach {
|
||||
dependsOn(":app:${it.name}:clean")
|
||||
dependsOn(":${it.name}:clean")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ private val defaultAbis = setOf("armeabi-v7a", "x86", "arm64-v8a", "x86_64")
|
||||
object Config {
|
||||
operator fun get(key: String): String? {
|
||||
val v = props[key] as? String ?: return null
|
||||
return if (v.isBlank()) null else v
|
||||
return v.ifBlank { null }
|
||||
}
|
||||
|
||||
fun contains(key: String) = get(key) != null
|
||||
@@ -28,19 +28,21 @@ object Config {
|
||||
}
|
||||
}
|
||||
|
||||
val Project.baseDir: File get() = rootProject.file("..")
|
||||
|
||||
class MagiskPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) = project.applyPlugin()
|
||||
|
||||
private fun Project.applyPlugin() {
|
||||
initRandom(rootProject.file("app/dict.txt"))
|
||||
initRandom(rootProject.file("dict.txt"))
|
||||
props.clear()
|
||||
rootProject.file("gradle.properties").inputStream().use { props.load(it) }
|
||||
val configPath: String? by this
|
||||
val config = configPath?.let { File(it) } ?: rootProject.file("config.prop")
|
||||
val config = configPath?.let { File(it) } ?: File(baseDir, "config.prop")
|
||||
if (config.exists())
|
||||
config.inputStream().use { props.load(it) }
|
||||
|
||||
val repo = FileRepository(rootProject.file(".git"))
|
||||
val repo = FileRepository(File(baseDir, ".git"))
|
||||
val refId = repo.refDatabase.exactRef("HEAD").objectId
|
||||
commitHash = repo.newObjectReader().abbreviate(refId, 8).name()
|
||||
}
|
||||
@@ -71,10 +71,10 @@ private val Project.androidComponents
|
||||
|
||||
fun Project.setupCommon() {
|
||||
androidBase {
|
||||
compileSdkVersion(35)
|
||||
buildToolsVersion = "35.0.1"
|
||||
compileSdkVersion(36)
|
||||
buildToolsVersion = "36.0.0"
|
||||
ndkPath = "$sdkDirectory/ndk/magisk"
|
||||
ndkVersion = "28.0.12674087"
|
||||
ndkVersion = "29.0.13113456"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 23
|
||||
@@ -89,6 +89,7 @@ fun Project.setupCommon() {
|
||||
resources {
|
||||
excludes += arrayOf(
|
||||
"/META-INF/*",
|
||||
"/META-INF/androidx/**",
|
||||
"/META-INF/versions/**",
|
||||
"/org/bouncycastle/**",
|
||||
"/org/apache/commons/**",
|
||||
@@ -115,6 +116,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 =
|
||||
@@ -129,7 +151,7 @@ fun Project.setupCoreLib() {
|
||||
into("src/main/jniLibs")
|
||||
for (abi in abiList) {
|
||||
into(abi) {
|
||||
from(rootProject.file("native/out/$abi")) {
|
||||
from(File(baseDir, "native/out/$abi")) {
|
||||
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
|
||||
rename { if (it.endsWith(".so")) it else "lib$it.so" }
|
||||
}
|
||||
@@ -144,34 +166,17 @@ 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")
|
||||
}
|
||||
|
||||
val syncResources by tasks.registering(Sync::class) {
|
||||
into("src/main/resources/META-INF/com/google/android")
|
||||
from(rootProject.file("scripts/update_binary.sh")) {
|
||||
from(File(baseDir, "scripts/update_binary.sh")) {
|
||||
rename { "update-binary" }
|
||||
}
|
||||
from(rootProject.file("scripts/flash_script.sh")) {
|
||||
from(File(baseDir, "scripts/flash_script.sh")) {
|
||||
rename { "updater-script" }
|
||||
}
|
||||
}
|
||||
@@ -182,7 +187,7 @@ fun Project.setupCoreLib() {
|
||||
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(downloadBusybox)
|
||||
processJavaResourcesProvider.configure { dependsOn(syncResources) }
|
||||
|
||||
val stubTask = tasks.getByPath(":app:stub:comment$variantCapped")
|
||||
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
|
||||
val stubApk = stubTask.outputs.files.asFileTree.filter {
|
||||
it.name.endsWith(".apk")
|
||||
}
|
||||
@@ -192,14 +197,14 @@ fun Project.setupCoreLib() {
|
||||
inputs.property("version", Config.version)
|
||||
inputs.property("versionCode", Config.versionCode)
|
||||
into("src/${this@all.name}/assets")
|
||||
from(rootProject.file("scripts")) {
|
||||
from(File(baseDir, "scripts")) {
|
||||
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
|
||||
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
|
||||
}
|
||||
from(rootProject.file("tools/bootctl"))
|
||||
from(File(baseDir, "tools/bootctl"))
|
||||
into("chromeos") {
|
||||
from(rootProject.file("tools/futility"))
|
||||
from(rootProject.file("tools/keys")) {
|
||||
from(File(baseDir, "tools/futility"))
|
||||
from(File(baseDir, "tools/keys")) {
|
||||
include("kernel_data_key.vbprivk", "kernel.keyblock")
|
||||
}
|
||||
}
|
||||
@@ -289,7 +294,7 @@ fun Project.setupAppCommon() {
|
||||
signingConfigs {
|
||||
create("config") {
|
||||
Config["keyStore"]?.also {
|
||||
storeFile = rootProject.file(it)
|
||||
storeFile = File(baseDir, it)
|
||||
storePassword = Config["keyStorePass"]
|
||||
keyAlias = Config["keyAlias"]
|
||||
keyPassword = Config["keyPass"]
|
||||
@@ -298,7 +303,7 @@ fun Project.setupAppCommon() {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
targetSdk = 35
|
||||
targetSdk = 36
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt")
|
||||
)
|
||||
@@ -462,3 +467,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) }
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":app:shared"))
|
||||
api(project(":shared"))
|
||||
|
||||
api(libs.timber)
|
||||
api(libs.markwon.core)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<application
|
||||
android:name=".App"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:multiArch="true"
|
||||
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"
|
||||
tools:remove="android:appComponentFactory">
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -134,7 +132,6 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
if (entry != null) {
|
||||
val magisk32 = File(installDir, "magisk32")
|
||||
zf.getInputStream(entry).writeTo(magisk32)
|
||||
magisk32.setExecutable(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,11 +146,15 @@ abstract class MagiskInstallImpl protected constructor(
|
||||
Os.symlink(lib.path, "$installDir/$name")
|
||||
}
|
||||
|
||||
// Also symlink magisk32 on 64-bit devices that supports 32-bit
|
||||
val lib32 = info.javaClass.getDeclaredField("secondaryNativeLibraryDir")
|
||||
.get(info) as String?
|
||||
if (lib32 != null) {
|
||||
Os.symlink("$lib32/libmagisk.so", "$installDir/magisk32");
|
||||
// Also extract magisk32 on 64-bit devices that supports 32-bit
|
||||
val abi32 = Const.CPU_ABI_32
|
||||
if (Process.is64Bit() && abi32 != null) {
|
||||
val name = "lib/$abi32/libmagisk.so"
|
||||
val entry = javaClass.classLoader!!.getResourceAsStream(name)
|
||||
if (entry != null) {
|
||||
val magisk32 = File(installDir, "magisk32")
|
||||
entry.writeTo(magisk32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +192,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 +227,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 +254,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 +289,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 +431,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 +440,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 +503,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 +517,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,23 @@ 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 com.topjohnwu.magisk.test.Environment.Companion.EMPTY_ZYGISK
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.INVALID_ZYGISK
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.MOUNT_TEST
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.REMOVE_TEST
|
||||
import com.topjohnwu.magisk.test.Environment.Companion.SEPOLICY_RULE
|
||||
import com.topjohnwu.superuser.Shell
|
||||
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 +35,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 +54,25 @@ class AdditionalTest : BaseTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLaunchLsposedManager() {
|
||||
fun testModuleCount() {
|
||||
var expected = 2
|
||||
if (Environment.mount()) expected++
|
||||
if (Environment.preinit()) expected++
|
||||
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 +83,56 @@ class AdditionalTest : BaseTest {
|
||||
device.wait(Until.hasObject(By.res(pattern)), TimeUnit.SECONDS.toMillis(10))
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModuleMount() {
|
||||
assumeTrue(Environment.mount())
|
||||
|
||||
assertNotNull("$MOUNT_TEST is not installed", modules.find { it.id == MOUNT_TEST })
|
||||
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()
|
||||
)
|
||||
val egg = RootUtils.fs.getFile("/system/app/EasterEgg").list() ?: arrayOf()
|
||||
assertTrue(
|
||||
"/system/app/EasterEgg should be empty",
|
||||
egg.isEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSepolicyRule() {
|
||||
assumeTrue(Environment.preinit())
|
||||
|
||||
assertNotNull("$SEPOLICY_RULE is not installed", modules.find { it.id == SEPOLICY_RULE })
|
||||
assertTrue(
|
||||
"Module sepolicy.rule is not applied",
|
||||
Shell.cmd("magiskpolicy --print-rules | grep -q magisk_test").exec().isSuccess
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmptyZygiskModule() {
|
||||
val module = modules.find { it.id == EMPTY_ZYGISK }
|
||||
assertNotNull("$EMPTY_ZYGISK is not installed", module)
|
||||
module!!
|
||||
assertTrue("$EMPTY_ZYGISK should be zygisk unloaded", module.zygiskUnloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInvalidZygiskModule() {
|
||||
val module = modules.find { it.id == INVALID_ZYGISK }
|
||||
assertNotNull("$INVALID_ZYGISK is not installed", module)
|
||||
module!!
|
||||
assertTrue("$INVALID_ZYGISK should be zygisk unloaded", module.zygiskUnloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveModule() {
|
||||
assertNull("$REMOVE_TEST should be removed", modules.find { it.id == REMOVE_TEST })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,29 @@ 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
|
||||
import java.io.PrintStream
|
||||
|
||||
@Keep
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@@ -31,12 +39,31 @@ 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 mount_test on those legacy platforms.
|
||||
fun mount(): 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"
|
||||
// It is possible that there are no suitable preinit partition to use
|
||||
fun preinit(): Boolean {
|
||||
return Shell.cmd("magisk --preinit-device").exec().isSuccess
|
||||
}
|
||||
|
||||
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"
|
||||
const val MOUNT_TEST = "mount_test"
|
||||
const val SEPOLICY_RULE = "sepolicy_rule"
|
||||
const val INVALID_ZYGISK = "invalid_zygisk"
|
||||
const val REMOVE_TEST = "remove_test"
|
||||
const val EMPTY_ZYGISK = "empty_zygisk"
|
||||
}
|
||||
|
||||
object TimberLog : CallbackList<String>(Runnable::run) {
|
||||
@@ -45,35 +72,152 @@ 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 setupMountTest(root: ExtendedFile) {
|
||||
val error = "$MOUNT_TEST setup failed"
|
||||
val path = root.getChildFile(MOUNT_TEST)
|
||||
|
||||
// Create /system/etc/newfile
|
||||
val etc = path.getChildFile("system").getChildFile("etc")
|
||||
assertTrue(error, etc.mkdirs())
|
||||
assertTrue(error, etc.getChildFile("newfile").createNewFile())
|
||||
|
||||
// Create /system/app/EasterEgg/.replace
|
||||
val egg = path.getChildFile("system").getChildFile("app").getChildFile("EasterEgg")
|
||||
assertTrue(error, egg.mkdirs())
|
||||
assertTrue(error, egg.getChildFile(".replace").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)
|
||||
|
||||
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupSepolicyRuleModule(root: ExtendedFile) {
|
||||
val error = "$SEPOLICY_RULE setup failed"
|
||||
val path = root.getChildFile(SEPOLICY_RULE)
|
||||
assertTrue(error, path.mkdirs())
|
||||
|
||||
// Add sepolicy patch
|
||||
PrintStream(path.getChildFile("sepolicy.rule").newOutputStream()).use {
|
||||
it.println("type magisk_test domain")
|
||||
}
|
||||
|
||||
assertTrue(error, Shell.cmd(
|
||||
"set_default_perm $path",
|
||||
"copy_preinit_files"
|
||||
).exec().isSuccess)
|
||||
}
|
||||
|
||||
private fun setupEmptyZygiskModule(root: ExtendedFile) {
|
||||
val error = "$EMPTY_ZYGISK setup failed"
|
||||
val path = root.getChildFile(EMPTY_ZYGISK)
|
||||
|
||||
// Create an empty zygisk folder
|
||||
val module = LocalModule(path)
|
||||
assertTrue(error, module.zygiskFolder.mkdirs())
|
||||
}
|
||||
|
||||
private fun setupInvalidZygiskModule(root: ExtendedFile) {
|
||||
val error = "$INVALID_ZYGISK setup failed"
|
||||
val path = root.getChildFile(INVALID_ZYGISK)
|
||||
|
||||
// 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 setupRemoveModule(root: ExtendedFile) {
|
||||
val error = "$REMOVE_TEST setup failed"
|
||||
val path = root.getChildFile(REMOVE_TEST)
|
||||
|
||||
// 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())
|
||||
}
|
||||
checkModuleZip(shamiko)
|
||||
if (shamiko()) {
|
||||
assertTrue(
|
||||
"Shamiko installation failed",
|
||||
FlashZip(shamiko.toUri(), TimberLog, TimberLog).exec()
|
||||
)
|
||||
}
|
||||
assertTrue(
|
||||
"LSPosed installation failed",
|
||||
FlashZip(zip.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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||
if (mount()) { setupMountTest(root) }
|
||||
if (preinit()) { setupSepolicyRuleModule(root) }
|
||||
setupEmptyZygiskModule(root)
|
||||
setupInvalidZygiskModule(root)
|
||||
setupRemoveModule(root)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,7 +226,7 @@ class Environment : BaseTest {
|
||||
assertTrue(
|
||||
"App hiding failed",
|
||||
AppMigration.patchAndHide(
|
||||
context = context,
|
||||
context = appContext,
|
||||
label = "Settings",
|
||||
pkg = "repackaged.$APP_PACKAGE_NAME"
|
||||
)
|
||||
@@ -95,7 +239,7 @@ class Environment : BaseTest {
|
||||
runBlocking {
|
||||
assertTrue(
|
||||
"App restoration failed",
|
||||
AppMigration.restoreApp(context)
|
||||
AppMigration.restoreApp(appContext)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,25 +6,25 @@
|
||||
<string name="logs">السجلات</string>
|
||||
<string name="settings">الإعدادات</string>
|
||||
<string name="install">تثبيت</string>
|
||||
<string name="section_home">الأساسي</string>
|
||||
<string name="section_theme">السِمات</string>
|
||||
<string name="section_home">الصفحة الرئيسية</string>
|
||||
<string name="section_theme">المظهر</string>
|
||||
|
||||
<!--Home-->
|
||||
<string name="no_connection">لا يوجد إتصال</string>
|
||||
<string name="app_changelog">تفاصيل التحديث</string>
|
||||
<string name="loading">جارٍ التحميل...</string>
|
||||
<string name="update">تحديث</string>
|
||||
<string name="not_available">غير/متوفر</string>
|
||||
<string name="not_available">غير متوفر</string>
|
||||
<string name="hide">إخفاء</string>
|
||||
<string name="home_package">الحزمة</string>
|
||||
|
||||
<string name="home_support_title">تبرع لنا</string>
|
||||
<string name="home_item_source">الكود المصدري للتطبيق</string>
|
||||
<string name="home_support_content">مـاجـيسك هي، وستظل دوماً، مجانيةً و مفتوحة المصدر، اظهر اهتمامك لنا لكي نبقيها هكذا بدعم مالي صغير</string>
|
||||
<string name="home_support_content">ماجيسك هو، وسيظل دوماً، مجانياّ و مفتوح المصدر، اظهر اهتمامك لنا لكي نبقيه هكذا بدعم مالي صغير</string>
|
||||
<string name="home_installed_version">تم التثبيت</string>
|
||||
<string name="home_latest_version">آخر إصدار</string>
|
||||
<string name="invalid_update_channel">مصدر التحديث غير صالح</string>
|
||||
<string name="uninstall_magisk_title">إلغاء تثبيت Magisk</string>
|
||||
<string name="uninstall_magisk_title">إلغاء تثبيت ماجيسك</string>
|
||||
<string name="uninstall_magisk_msg">ستُعطل/ستُحذف جميع الإضافات. سيُحذف الروت، وربما ستشفر بياناتك إذا لم تكن غير مشفرة حالياً.</string>
|
||||
|
||||
<!--Install-->
|
||||
@@ -34,7 +34,7 @@
|
||||
<string name="install_options_title">الخيارات</string>
|
||||
<string name="install_method_title">الطريقة</string>
|
||||
<string name="install_next">التالي</string>
|
||||
<string name="install_start">هيا، بنا</string>
|
||||
<string name="install_start">هيا بنا</string>
|
||||
<string name="manager_download_install">اضغط للتنزيل و التثبيت</string>
|
||||
<string name="direct_install">تثبيت مباشر (موصى بها)</string>
|
||||
<string name="install_inactive_slot">التثبيت على المنطقة الغير نشطة (بعد OTA)</string>
|
||||
@@ -87,15 +87,15 @@
|
||||
|
||||
<!-- MagiskHide -->
|
||||
<string name="show_system_app">إظهار برامج النظام</string>
|
||||
<string name="hide_filter_hint">البحث بالإسم</string>
|
||||
<string name="hide_filter_hint">البحث بالاسم</string>
|
||||
<string name="hide_search">ابحث</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(لم توفر معلومات)</string>
|
||||
<string name="no_info_provided">(لا تتوفر معلومات)</string>
|
||||
<string name="reboot_recovery">إعادة التشغيل إلى Recovery</string>
|
||||
<string name="reboot_bootloader">إعادة التشغيل إلى Bootloader</string>
|
||||
<string name="reboot_download">إعادة التشغيل إلى وضـع Odin</string>
|
||||
<string name="reboot_edl">إعادة التشغيل إلى وضـعية EDL</string>
|
||||
<string name="reboot_edl">إعادة التشغيل إلى EDL</string>
|
||||
<string name="module_version_author">%1$sبواسطة%2$s</string>
|
||||
<string name="module_state_remove">إزالة </string>
|
||||
<string name="module_state_restore">إسترجاع</string>
|
||||
@@ -104,15 +104,15 @@
|
||||
<string name="external_rw_permission_denied">امنحني إذن الولوج للذاكرة الداخلية</string>
|
||||
|
||||
<!--Settings -->
|
||||
<string name="settings_dark_mode_title">وضـعية الِسمات</string>
|
||||
<string name="settings_dark_mode_message">حدد الوضـع الذي يناسب ذوقك</string>
|
||||
<string name="settings_dark_mode_title">المظهر</string>
|
||||
<string name="settings_dark_mode_message">حدد المظهر الذي يناسب ذوقك</string>
|
||||
<string name="settings_dark_mode_light">الوضـع المضيء</string>
|
||||
<string name="settings_dark_mode_system">اتبّع النظام</string>
|
||||
<string name="settings_dark_mode_dark">وضـع الظلام</string>
|
||||
<string name="settings_dark_mode_dark">الوضع المظلم</string>
|
||||
<string name="settings_download_path_title">مسار التحميل</string>
|
||||
<string name="settings_download_path_message">ستحمل الملفات إلى %1$s</string>
|
||||
<string name="language">اللغة</string>
|
||||
<string name="system_default">(الأفتراضي)</string>
|
||||
<string name="system_default">(الإفتراضي)</string>
|
||||
<string name="settings_check_update_title">تحقق من التحديثات</string>
|
||||
<string name="settings_check_update_summary">التحقق من التحديثات في الخلفية بشكل دوري</string>
|
||||
<string name="settings_update_channel_title">مصدر التحديثات</string>
|
||||
@@ -123,8 +123,8 @@
|
||||
<string name="settings_hosts_title">موانع الاعلانات</string>
|
||||
<string name="settings_hosts_summary">حجب الاعلانات دون تعديل النظام</string>
|
||||
<string name="settings_hosts_toast">تم تمكين خاصية حجب الاعلانات</string>
|
||||
<string name="settings_app_name_hint">الإسم الجديد</string>
|
||||
<string name="settings_app_name_helper">التطبيق الجديد سوف يملك هذا الإسم</string>
|
||||
<string name="settings_app_name_hint">الاسم الجديد</string>
|
||||
<string name="settings_app_name_helper">التطبيق الجديد سوف يملك هذا الاسم</string>
|
||||
<string name="settings_app_name_error">الصيغة غير مقبولة</string>
|
||||
<string name="settings_su_app_adb">التطبيقات و ADB</string>
|
||||
<string name="settings_su_app">التطبيقات فقط</string>
|
||||
@@ -140,51 +140,51 @@
|
||||
<string name="auto_response">الفعل التلقائي</string>
|
||||
<string name="request_timeout">المهلة قبل الفعل التلقائي</string>
|
||||
<string name="superuser_notification">إشعارات طلبات الروت</string>
|
||||
<string name="settings_su_reauth_title">إعادة المصادقة بعد الترقية</string>
|
||||
<string name="settings_su_reauth_summary">أعد مصادقة صلاحيات الروت بعد إجراء ترقيات للتطبيق</string>
|
||||
<string name="settings_su_reauth_title">إعادة المصادقة بعد التحديث</string>
|
||||
<string name="settings_su_reauth_summary">أعد مصادقة صلاحيات الروت بعد تحديث التطبيق</string>
|
||||
<string name="settings_customization">تخصيص</string>
|
||||
|
||||
<string name="multiuser_mode">نمط مستخدمين متعددين</string>
|
||||
<string name="multiuser_mode">نمط المستخدم المزدوج</string>
|
||||
<string name="settings_owner_only">مالك الجهاز فقط</string>
|
||||
<string name="settings_owner_manage">المالك هو من يحدد</string>
|
||||
<string name="settings_user_independent">مستقل </string>
|
||||
<string name="settings_user_independent">مستقل</string>
|
||||
<string name="owner_only_summary">للمالك فقط له صلاحيات الروت</string>
|
||||
<string name="owner_manage_summary">فقط المالك من يرفض و يمنح صلاحيات الروت</string>
|
||||
<string name="user_independent_summary">كل مستخدم له قواعد روت خاصة به</string>
|
||||
|
||||
<string name="mount_namespace_mode">نمط Mount Namespace</string>
|
||||
<string name="settings_ns_global">نمط Namespace العام</string>
|
||||
<string name="settings_ns_requester">نمط NameSpace المتوارث</string>
|
||||
<string name="settings_ns_isolate">نمط NameSpace المعزول</string>
|
||||
<string name="settings_ns_global">نمط Namespace عام</string>
|
||||
<string name="settings_ns_requester">نمط NameSpace متوارث</string>
|
||||
<string name="settings_ns_isolate">نمط NameSpace معزول</string>
|
||||
<string name="global_summary">جميع الجلسات الروت تستخدم NameSpace العام</string>
|
||||
<string name="requester_summary">جميع الجلسات الروت تستخدم NameSpace المتوارث</string>
|
||||
<string name="isolate_summary">جميع الجلسات الروت تستخدم NameSpace المعزول</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">تحديثات Magisk</string>
|
||||
<string name="update_channel">تحديثات ماجيسك</string>
|
||||
<string name="progress_channel">إشعارات التقدم</string>
|
||||
<string name="download_complete">اكتمل التنزيل</string>
|
||||
<string name="download_file_error">فشل تنزيل الملف</string>
|
||||
<string name="magisk_update_title">تحديث مـاجـيسك متوفر!</string>
|
||||
<string name="magisk_update_title">تحديث ماجيسك متوفر!</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">نعم</string>
|
||||
<string name="no">لا</string>
|
||||
<string name="download">تنزيل</string>
|
||||
<string name="reboot">إعادة التشغيل</string>
|
||||
<string name="release_notes">معلومات الأصدار الجديد</string>
|
||||
<string name="flashing">يتم الحرق...</string>
|
||||
<string name="release_notes">معلومات الإصدار الجديد</string>
|
||||
<string name="flashing">يتم التثبيت...</string>
|
||||
<string name="done">تم!</string>
|
||||
<string name="failure">فشل!</string>
|
||||
<string name="open_link_failed_toast">لم يُعثر على تطبيق لفتح الرابط …</string>
|
||||
<string name="complete_uninstall">إلغاء التثبيت بالكامل</string>
|
||||
<string name="restore_img">استعادة الصور</string>
|
||||
<string name="restore_img_msg">جار الأستعادة…</string>
|
||||
<string name="restore_done">تم الأستعادة</string>
|
||||
<string name="restore_fail">النسخة الاحتياطية الأصلية غير موجودة!</string>
|
||||
<string name="setup_fail">فشل التضبيط</string>
|
||||
<string name="env_fix_title"> الإعداد الأضافي مطلوب</string>
|
||||
<string name="setup_msg">جار تضبيت البيئة</string>
|
||||
<string name="unsupport_magisk_title">إصدار مـاجـيسك غير مدعوم</string>
|
||||
<string name="restore_img_msg">جار الإستعادة…</string>
|
||||
<string name="restore_done">تم الإستعادة</string>
|
||||
<string name="restore_fail">النسخة الإحتياطية الأصلية غير موجودة!</string>
|
||||
<string name="setup_fail">فشل الإعداد</string>
|
||||
<string name="env_fix_title">الإعداد الإضافي مطلوب</string>
|
||||
<string name="setup_msg">جار إعداد البيئة</string>
|
||||
<string name="unsupport_magisk_title">إصدار ماجيسك غير مدعوم</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,99 +1,57 @@
|
||||
<resources>
|
||||
<!--Universal-->
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<!--Sections-->
|
||||
<string name="modules">Модули</string>
|
||||
<string name="superuser">Супер-корисник</string>
|
||||
<string name="logs">Дневник</string>
|
||||
<string name="logs">Логови</string>
|
||||
<string name="settings">Подешавања</string>
|
||||
<string name="install">Инсталација</string>
|
||||
<string name="section_home">Почетно</string>
|
||||
<string name="section_theme">Теме</string>
|
||||
<string name="denylist">Листа забрана</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="invalid_update_channel">Непостојећи Канал Ажурирања</string>
|
||||
<!--Home-->
|
||||
<string name="no_connection">Недоступна конекција</string>
|
||||
<string name="app_changelog">Промене у апликацији</string>
|
||||
<string name="loading">Учитавање…</string>
|
||||
<string name="update">Ажурирање</string>
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="hide">Сакриј</string>
|
||||
<string name="home_package">Пакет</string>
|
||||
<string name="home_app_title">Апл.</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="home_notice_content">Преузмите Magisk САМО са званичне GitHub странице. Фајлови из непознатих извора могу бити малициозни!</string>
|
||||
<string name="home_support_title">Подржите нас</string>
|
||||
<string name="home_follow_title">Запратите нас</string>
|
||||
<string name="home_item_source">Извор</string>
|
||||
<string name="home_support_content">Magisk јесте и увек ће бити бесплатан и open source. Можете показати да вам је стало својом донацијом.</string>
|
||||
<string name="home_installed_version">Инсталирано</string>
|
||||
<string name="home_latest_version">Најновије</string>
|
||||
<string name="invalid_update_channel">Невалидан канал ажурирања</string>
|
||||
<string name="uninstall_magisk_title">Деинсталирај Magisk</string>
|
||||
<string name="uninstall_magisk_msg">Сви модули ће бити онемогућени/уклоњени!\nКорен ће бити уклоњен!\nСвако неенкриптовано интерно складиште ће употребом Magisk-а бити поново енкриптовано!</string>
|
||||
|
||||
<!--Install-->
|
||||
<string name="keep_force_encryption">Задржи форсирану енкрипцију</string>
|
||||
<string name="keep_dm_verity">Задржи AVB 2.0/dm-verity</string>
|
||||
<string name="uninstall_magisk_title">Унинсталирај Магиск</string>
|
||||
<string name="uninstall_magisk_msg">Сви модули ће бити онеспособљени/уклоњени. Корен ће бити уклоњен, и потенцијално енкриптовати твоје податке уколико већ нису енкриптовани</string>
|
||||
<string name="update">Ажурирање</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Без информација)</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Ажурирање доступно</string>
|
||||
<string name="home_installed_version">Инсталирано</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveLog">Сачувај дневник</string>
|
||||
<string name="menuClearLog">Обриши дневник</string>
|
||||
<string name="logs_cleared">Дневник успешно креиран</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="app_changelog">Дневник промена апликације</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="repo_install_title">Инсталирај %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Преузми</string>
|
||||
<string name="reboot">Рестартуј</string>
|
||||
<string name="magisk_update_title">Нови Адбејт Магиска Доступан!</string>
|
||||
<string name="release_notes">Белешке обљављивања</string>
|
||||
<string name="recovery_mode">Режим опоравка</string>
|
||||
<string name="install_options_title">Опције</string>
|
||||
<string name="install_method_title">Метод</string>
|
||||
<string name="install_next">Наредно</string>
|
||||
<string name="install_start">Почнимо</string>
|
||||
<string name="manager_download_install">Притисни да преузмеш и инсталираш</string>
|
||||
<string name="update_channel">Магиск Ажурирање</string>
|
||||
<string name="flashing">Флешовање</string>
|
||||
<string name="direct_install">Директна Инсталација (Препоручено)</string>
|
||||
<string name="complete_uninstall">Комплетна Унинсталација</string>
|
||||
<string name="restore_done">Повратак успешан!</string>
|
||||
<string name="restore_fail">Фабрички бекап не постоји!</string>
|
||||
<string name="download_file_error">Грешка при преузимању фајла</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="language">Језик</string>
|
||||
<string name="system_default">(Фабрички Система)</string>
|
||||
<string name="settings_update_channel_title">Канал Ажурирања</string>
|
||||
<string name="settings_update_stable">Стабилан</string>
|
||||
<string name="settings_update_beta">Бета</string>
|
||||
<string name="settings_update_custom">По наруџби</string>
|
||||
<string name="settings_update_custom_msg">Унеси наруџбени УРЛ</string>
|
||||
<string name="settings_hosts_title">Без-системски домаћини (hosts)</string>
|
||||
<string name="settings_hosts_summary">Подршка без-системских домаћина за апликације за блокирање реклама</string>
|
||||
|
||||
<string name="settings_su_app_adb">Апликације и АДБ</string>
|
||||
<string name="settings_su_app">Само Апликације</string>
|
||||
<string name="settings_su_adb">Само АДБ</string>
|
||||
<string name="settings_su_disable">Онемогућено</string>
|
||||
<string name="settings_su_request_10">10 секунди</string>
|
||||
<string name="settings_su_request_15">15 секунди</string>
|
||||
<string name="settings_su_request_20">20 секунди</string>
|
||||
<string name="settings_su_request_30">30 секунди</string>
|
||||
<string name="settings_su_request_45">45 секунди</string>
|
||||
<string name="settings_su_request_60">60 секунди</string>
|
||||
<string name="superuser_access">Приступ Супер-кориснику</string>
|
||||
<string name="auto_response">Аутоматски одговор</string>
|
||||
<string name="request_timeout">Истек захтева</string>
|
||||
<string name="superuser_notification">Нотификације Супер-корисника</string>
|
||||
<string name="settings_su_reauth_title">Поново одобри после ажурирања</string>
|
||||
<string name="settings_su_reauth_summary">Поново одобри дозволу Супер-корисника после ажурирања апликације</string>
|
||||
|
||||
<string name="multiuser_mode">Више-кориснички режим</string>
|
||||
<string name="settings_owner_only">Власник уређаја само</string>
|
||||
<string name="settings_owner_manage">Одређено од стране власника</string>
|
||||
<string name="settings_user_independent">Независно од корисника</string>
|
||||
<string name="owner_only_summary">Само власник има приступ корену</string>
|
||||
<string name="owner_manage_summary">Само власник може да приступа корену и да прими захтеве за њега</string>
|
||||
<string name="user_independent_summary">Сваки корисник има своја правила корена</string>
|
||||
|
||||
<string name="mount_namespace_mode">Постоље режима имена простора</string>
|
||||
<string name="settings_ns_global">Глобално име простора</string>
|
||||
<string name="settings_ns_requester">Наслеђено име простора</string>
|
||||
<string name="settings_ns_isolate">Ограђено име простора</string>
|
||||
<string name="global_summary">Све коренске сесије користе глобално име простора</string>
|
||||
<string name="requester_summary">Коренске сесије ће наследити свој простор именовања</string>
|
||||
<string name="isolate_summary">Свака коренска сесија ће имати свој изоловани простор именовања</string>
|
||||
<string name="direct_install">Директна инсталација (Препоручено)</string>
|
||||
<string name="install_inactive_slot">Инсталација на неактиван слот (Након OTA)</string>
|
||||
<string name="install_inactive_slot_msg">Ваш уређај ће бити ФОРСИРАН да се покрене на тренутно неактивном слоту након поновног покретања!\nКористите опцију само кад се OTA заврши.\nНастави?</string>
|
||||
<string name="setup_title">Додатне поставке</string>
|
||||
<string name="select_patch_file">Изаберите фајл</string>
|
||||
<string name="patch_file_msg">Изаберите слику (*.img) или ODIN tarfile (*.tar) или payload.bin (*.bin)</string>
|
||||
<string name="reboot_delay_toast">Поново покретање за 5 секунди…</string>
|
||||
<string name="flash_screen_title">Инсталација</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Супер-кориснички захтев</string>
|
||||
<string name="touch_filtered_warning">Magisk не може да верификује ваш одговор, јер апликација прикрива супер-кориснички захтев</string>
|
||||
<string name="deny">Забрани</string>
|
||||
<string name="prompt">Захтев</string>
|
||||
<string name="grant">Дозволи</string>
|
||||
@@ -104,20 +62,190 @@
|
||||
<string name="twentymin">20 мин</string>
|
||||
<string name="thirtymin">30 мин</string>
|
||||
<string name="sixtymin">60 мин</string>
|
||||
<string name="su_allow_toast">%1$s је добио права на Супер-корисника</string>
|
||||
<string name="su_deny_toast">%1$s није добио права на Супер-корисника</string>
|
||||
<string name="su_allow_toast">%1$s је добио права на супер-корисника</string>
|
||||
<string name="su_deny_toast">%1$s није добио права на супер-корисника</string>
|
||||
<string name="su_snack_grant">Супер-корисничка права од %1$s су пружена</string>
|
||||
<string name="su_snack_deny">Супер-корисничка права од %1$s су одбијена</string>
|
||||
<string name="su_snack_notif_on">Нотификације од %1$s су омогућене</string>
|
||||
<string name="su_snack_notif_off">Нотификације од %1$s су онемогућене</string>
|
||||
<string name="su_snack_log_on">Записивање у дневник за %1$s је омогућено</string>
|
||||
<string name="su_snack_log_off">Записивање у дневник за %1$s је онемогућено</string>
|
||||
<string name="su_snack_log_on">Логовање за %1$s је омогућено</string>
|
||||
<string name="su_snack_log_off">Логовање за %1$s је онемогућено</string>
|
||||
<string name="su_revoke_title">Опозови?</string>
|
||||
<string name="su_revoke_msg">Потврди да опозовеш права од %1$s?</string>
|
||||
<string name="toast">Тост</string>
|
||||
<string name="none">Ниједан</string>
|
||||
<string name="su_revoke_msg">Потврди да опозовеш права на супер-корисника од %1$s?</string>
|
||||
<string name="toast">Toast</string>
|
||||
<string name="none">Ништа</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="target_uid">Циљани УИД: %1$d</string>
|
||||
<string name="superuser_toggle_notification">Нотификације</string>
|
||||
<string name="superuser_toggle_revoke">Опозови</string>
|
||||
<string name="superuser_policy_none">Ниједна апликација није тражила пермисије за супер-корисника још увек.</string>
|
||||
|
||||
<!--Logs-->
|
||||
<string name="log_data_none">Немате логова, покушајте користити коренске апликације више</string>
|
||||
<string name="log_data_magisk_none">Magisk логови су празни, то је чудно</string>
|
||||
<string name="menuSaveLog">Сачувај лог</string>
|
||||
<string name="menuClearLog">Уклони лог</string>
|
||||
<string name="logs_cleared">Лог успешно уклоњен</string>
|
||||
<string name="pid">PID: %1$d</string>
|
||||
<string name="target_uid">Циљани UID: %1$d</string>
|
||||
<string name="target_pid">Mount ns цињани PID: %s</string>
|
||||
<string name="selinux_context">SELinux контекст: %s</string>
|
||||
<string name="supp_group">Допунска група: %s</string>
|
||||
|
||||
<!--SafetyNet-->
|
||||
|
||||
<!--MagiskHide-->
|
||||
<string name="show_system_app">Прикажи системске апл.</string>
|
||||
<string name="show_os_app">Прикажи апл. ОС-а</string>
|
||||
<string name="hide_filter_hint">Филтрирај по имену</string>
|
||||
<string name="hide_search">Претрага</string>
|
||||
|
||||
<!--Module-->
|
||||
<string name="no_info_provided">(Без информација)</string>
|
||||
<string name="reboot_userspace">Лако поново покретање</string>
|
||||
<string name="reboot_recovery">Поново покрени за опоравак</string>
|
||||
<string name="reboot_bootloader">Поново покрени за bootloader</string>
|
||||
<string name="reboot_download">Поново покрени за преузимање</string>
|
||||
<string name="reboot_edl">Поново покрени за EDL</string>
|
||||
<string name="reboot_safe_mode">Сигуран мод</string>
|
||||
<string name="module_version_author">%1$s од %2$s</string>
|
||||
<string name="module_state_remove">Уклони</string>
|
||||
<string name="module_action">Акција</string>
|
||||
<string name="module_state_restore">Поврати</string>
|
||||
<string name="module_action_install_external">Инсталирај из складишта</string>
|
||||
<string name="update_available">Ажурирање доступно</string>
|
||||
<string name="suspend_text_riru">Модул је суспендован јер је %1$s омогућено</string>
|
||||
<string name="suspend_text_zygisk">Модул је суспендован јер %1$s није омогућено</string>
|
||||
<string name="zygisk_module_unloaded">Zygisk модул није учитан због некомпатибилности</string>
|
||||
<string name="module_empty">Ниједан модул није инсталиран</string>
|
||||
<string name="confirm_install">Инсталирај модул %1$s?</string>
|
||||
<string name="confirm_install_title">Потврда инсталације</string>
|
||||
|
||||
<!--Settings-->
|
||||
<string name="settings_dark_mode_title">Тема</string>
|
||||
<string name="settings_dark_mode_message">Изаберите тему која вам највише одговара!</string>
|
||||
<string name="settings_dark_mode_light">Увек светло</string>
|
||||
<string name="settings_dark_mode_system">Прати систем</string>
|
||||
<string name="settings_dark_mode_dark">Увек тамно</string>
|
||||
<string name="settings_download_path_title">Путања за преузимање</string>
|
||||
<string name="settings_download_path_message">Фајлови ће бити сачувани на %1$s</string>
|
||||
<string name="settings_hide_app_title">Сакриј Magisk апл.</string>
|
||||
<string name="settings_hide_app_summary">Инсталирај proxy апликацију са насумичним ID-јем пакета и прилагођеном лабелом</string>
|
||||
<string name="settings_restore_app_title">Поврати Magisk апл.</string>
|
||||
<string name="settings_restore_app_summary">Откриј апл. и поврати оригинални APK</string>
|
||||
<string name="language">Језик</string>
|
||||
<string name="system_default">(Подразумевано системски)</string>
|
||||
<string name="settings_check_update_title">Провери ажурирања</string>
|
||||
<string name="settings_check_update_summary">Периодично провери ажурирања у позадини</string>
|
||||
<string name="settings_update_channel_title">Канал ажурирања</string>
|
||||
<string name="settings_update_stable">Стабилно</string>
|
||||
<string name="settings_update_beta">Бета</string>
|
||||
<string name="settings_update_custom">Прилагођено</string>
|
||||
<string name="settings_update_custom_msg">Унеси прилагођени URL канала</string>
|
||||
<string name="settings_zygisk_summary">Покрени делове Magisk-а у zygote daemon-у</string>
|
||||
<string name="settings_denylist_title">Спроведи листу забрана</string>
|
||||
<string name="settings_denylist_summary">Процеси на листи забрана ће повратити све Magisk измене</string>
|
||||
<string name="settings_denylist_config_title">Конфигуриши листу забрана</string>
|
||||
<string name="settings_denylist_config_summary">Изабери процесе који ће бити на листи забрана</string>
|
||||
<string name="settings_hosts_title">Безсистемски домаћини (hosts)</string>
|
||||
<string name="settings_hosts_summary">Подршка безсистемских домаћина за апликације блокирања реклама</string>
|
||||
<string name="settings_hosts_toast">Модул безсистемских домаћина додат</string>
|
||||
<string name="settings_app_name_hint">Ново име</string>
|
||||
<string name="settings_app_name_helper">Апл. ће бити спакована под овим именом</string>
|
||||
<string name="settings_app_name_error">Невалидан формат</string>
|
||||
<string name="settings_su_app_adb">Апликације и ADB</string>
|
||||
<string name="settings_su_app">Само апликације</string>
|
||||
<string name="settings_su_adb">Само ADB</string>
|
||||
<string name="settings_su_disable">Онемогућено</string>
|
||||
<string name="settings_su_request_10">10 секунди</string>
|
||||
<string name="settings_su_request_15">15 секунди</string>
|
||||
<string name="settings_su_request_20">20 секунди</string>
|
||||
<string name="settings_su_request_30">30 секунди</string>
|
||||
<string name="settings_su_request_45">45 секунди</string>
|
||||
<string name="settings_su_request_60">60 секунди</string>
|
||||
<string name="superuser_access">Приступ супер-корисника</string>
|
||||
<string name="auto_response">Аутоматски одговор</string>
|
||||
<string name="request_timeout">Истек захтева</string>
|
||||
<string name="superuser_notification">Нотификације супер-корисника</string>
|
||||
<string name="settings_su_reauth_title">Поново одобри након ажурирања</string>
|
||||
<string name="settings_su_reauth_summary">Поново тражи пермисије супер-корисника након ажурирања апликација</string>
|
||||
<string name="settings_su_tapjack_title">Заштита од tapjacking-а</string>
|
||||
<string name="settings_su_tapjack_summary">Prompt дијалог супер-корисника неће реаговати док је прикривен другим прозором или overlay-ем</string>
|
||||
<string name="settings_su_auth_title">Аутентификација корисника</string>
|
||||
<string name="settings_su_auth_summary">Тражи аутентификацију корисника током захтева супер-корисника</string>
|
||||
<string name="settings_su_auth_insecure">Ниједан метод аутентификације није подешен на уређају</string>
|
||||
<string name="settings_customization">Прилагођавање</string>
|
||||
<string name="setting_add_shortcut_summary">Додај лепу пречицу на почетни екран у случају да се име и иконица не препознају лако након скривања апликације</string>
|
||||
<string name="settings_doh_title">DNS преко HTTPS-а</string>
|
||||
<string name="settings_doh_description">Заобилазно решење DNS тровања у неким нацијама</string>
|
||||
<string name="settings_random_name_title">Насумично име на излазу</string>
|
||||
<string name="settings_random_name_description">Насумично име излазног фајла слика и tar фајлова ради спречавања детекције</string>
|
||||
|
||||
<string name="multiuser_mode">Вишекориснички режим</string>
|
||||
<string name="settings_owner_only">Само власник уређаја</string>
|
||||
<string name="settings_owner_manage">Одређено од стране власника</string>
|
||||
<string name="settings_user_independent">Независно од корисника</string>
|
||||
<string name="owner_only_summary">Само власник има приступ корену</string>
|
||||
<string name="owner_manage_summary">Само власник може да приступа корену и да прима захтеве за њега</string>
|
||||
<string name="user_independent_summary">Сваки корисник има своја правила корена</string>
|
||||
|
||||
<string name="mount_namespace_mode">Mount режим namespace-а</string>
|
||||
<string name="settings_ns_global">Глобални namespace</string>
|
||||
<string name="settings_ns_requester">Наслеђени namespace</string>
|
||||
<string name="settings_ns_isolate">Изоловани namespace</string>
|
||||
<string name="global_summary">Све коренске сесије користе глобални mount namespace</string>
|
||||
<string name="requester_summary">Коренске сесије ће наследити namespace од подносиоца захтева</string>
|
||||
<string name="isolate_summary">Свака коренска сесија ће имати свој изоловани namespace</string>
|
||||
|
||||
<!--Notifications-->
|
||||
<string name="update_channel">Ажурирања Magisk-а</string>
|
||||
<string name="progress_channel">Нотификације о прогресу</string>
|
||||
<string name="updated_channel">Ажурирање завршено</string>
|
||||
<string name="download_complete">Преузимање завршено</string>
|
||||
<string name="download_file_error">Грешка при преузимању фајла</string>
|
||||
<string name="magisk_update_title">Ажурирање Magisk-а доступно!</string>
|
||||
<string name="updated_title">Magisk је ажуриран</string>
|
||||
<string name="updated_text">Кликни да отвориш апликацију</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="yes">Да</string>
|
||||
<string name="no">Не</string>
|
||||
<string name="repo_install_title">Инсталирај %1$s %2$s(%3$d)</string>
|
||||
<string name="download">Преузми</string>
|
||||
<string name="reboot">Поново покрени</string>
|
||||
<string name="close">Затвори</string>
|
||||
<string name="release_notes">Release notes</string>
|
||||
<string name="flashing">Флешовање…</string>
|
||||
<string name="running">Покретање…</string>
|
||||
<string name="done">Завршено!</string>
|
||||
<string name="done_action">Покретање акције %1$s завршено</string>
|
||||
<string name="failure">Неуспешно!</string>
|
||||
<string name="hide_app_title">Скривање Magisk апликације…</string>
|
||||
<string name="open_link_failed_toast">Није пронађена апликација за отварање линка</string>
|
||||
<string name="complete_uninstall">Комплетна деинсталација</string>
|
||||
<string name="restore_img">Поврати слике</string>
|
||||
<string name="restore_img_msg">Повратак…</string>
|
||||
<string name="restore_done">Повратак успешан!</string>
|
||||
<string name="restore_fail">Фабрички бекап не постоји!</string>
|
||||
<string name="setup_fail">Неуспешна поставка</string>
|
||||
<string name="env_fix_title">Потребно додатно подешавање</string>
|
||||
<string name="env_fix_msg">Ваш уређај захтева додатно подешавање да би Magisk радио како треба. Да ли желите наставити и покренути поново?</string>
|
||||
<string name="env_full_fix_msg">Ваш уређај захтева поновно флешовање да би Magisk радио како треба. Реинсталирајте Magisk кроз апликацију, режим опоравка не може добити тачне информације о уређају.</string>
|
||||
<string name="setup_msg">Покретање подешавања окружења…</string>
|
||||
<string name="unsupport_magisk_title">Неподржана верзија Magisk-а</string>
|
||||
<string name="unsupport_magisk_msg">Ова верзија апликације не подржава Magisk верзије мање од %1$s.\n\nАпликација ће се понашати као да Magisk није инсталиран, молимо ажурирајте Magisk што пре.</string>
|
||||
<string name="unsupport_general_title">Ненормално стање</string>
|
||||
<string name="unsupport_system_app_msg">Покретање апликације као системске није подржано. Молимо поставите апликацију да буде корисничка.</string>
|
||||
<string name="unsupport_other_su_msg">Детектован \"su\" binary који није Magisk-ов. Молимо уклоните конкурентно коренско решење и/или реинсталирајте Magisk.</string>
|
||||
<string name="unsupport_external_storage_msg">Magisk је инсталиран на екстерно складиште. Молимо померите апл. у интерно складиште.</string>
|
||||
<string name="unsupport_nonroot_stub_msg">Скривена Magisk апликација не може наставити са радом јер је корен изгубљен. Молимо повратите оригинални APK.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Дозволите пермисију за складиште да бисте омогућили ову функционалност</string>
|
||||
<string name="post_notifications_denied">Дозволите пермисију за нотификације да бисте омогућили ову функционалност</string>
|
||||
<string name="install_unknown_denied">Дозволите "инсталирање непознатих апликација" да бисте омогућили ову функционалност</string>
|
||||
<string name="add_shortcut_title">Додај пречицу на почетни екран</string>
|
||||
<string name="add_shortcut_msg">Након скривања апликације, њено име и иконицу ћете тешко препознати. Желите ли додати лепу пречицу на почетни екран?</string>
|
||||
<string name="app_not_found">Није пронађена апликација за ову акцију</string>
|
||||
<string name="reboot_apply_change">Поново покрени да примениш измене</string>
|
||||
<string name="restore_app_confirmation">Ово ће вратити скривену апликацију на оригиналну. Да ли стварно то желите?</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -30,5 +30,4 @@ android.nonFinalResIds=false
|
||||
|
||||
# Magisk
|
||||
magisk.stubVersion=40
|
||||
magisk.versionCode=28102
|
||||
magisk.ondkVersion=r28.2
|
||||
magisk.versionCode=29000
|
||||
@@ -1,14 +1,14 @@
|
||||
[versions]
|
||||
kotlin = "2.1.0"
|
||||
android = "8.8.0"
|
||||
ksp = "2.1.0-1.0.29"
|
||||
kotlin = "2.1.20"
|
||||
android = "8.9.2"
|
||||
ksp = "2.1.20-1.0.31"
|
||||
rikka = "1.3.0"
|
||||
navigation = "2.8.4"
|
||||
navigation = "2.8.9"
|
||||
libsu = "6.0.0"
|
||||
moshi = "1.15.1"
|
||||
moshi = "1.15.2"
|
||||
okhttp = "4.12.0"
|
||||
retrofit = "2.11.0"
|
||||
room = "2.6.1"
|
||||
room = "2.7.1"
|
||||
|
||||
[libraries]
|
||||
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.80" }
|
||||
@@ -26,12 +26,12 @@ timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
|
||||
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version = "7.1.0.202411261347-r" }
|
||||
|
||||
# AndroidX
|
||||
activity = { module = "androidx.activity:activity", version = "1.10.0" }
|
||||
activity = { module = "androidx.activity:activity", version = "1.10.1" }
|
||||
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.0" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.15.0" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.16.0" }
|
||||
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.0" }
|
||||
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.5" }
|
||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.1" }
|
||||
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.6" }
|
||||
navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" }
|
||||
navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigation" }
|
||||
profileinstaller = { module = "androidx.profileinstaller:profileinstaller", version = "1.4.1" }
|
||||
@@ -40,10 +40,10 @@ room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.1.0" }
|
||||
transition = { module = "androidx.transition:transition", version = "1.5.1" }
|
||||
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.4.5" }
|
||||
transition = { module = "androidx.transition:transition", version = "1.6.0" }
|
||||
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.5.0" }
|
||||
material = { module = "com.google.android.material:material", version = "1.12.0" }
|
||||
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.3" }
|
||||
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.5" }
|
||||
test-runner = { module = "androidx.test:runner", version = "1.6.2" }
|
||||
test-rules = { module = "androidx.test:rules", version = "1.6.1" }
|
||||
test-junit = { module = "androidx.test.ext:junit", version = "1.2.1" }
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
0
gradlew → app/gradlew
vendored
0
gradlew → app/gradlew
vendored
0
gradlew.bat → app/gradlew.bat
vendored
0
gradlew.bat → app/gradlew.bat
vendored
@@ -8,4 +8,4 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
rootProject.name = "Magisk"
|
||||
include(":app:apk", ":app:core", ":app:shared", ":app:stub", ":app:test", ":native")
|
||||
include(":apk", ":core", ":shared", ":stub", ":test")
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:enableOnBackInvokedCallback="false"
|
||||
android:label="Magisk"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,5 +41,5 @@ android {
|
||||
setupStubApk()
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app:shared"))
|
||||
implementation(project(":shared"))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">عليك الترقية Magisk لإكمال تهيئة التطبيق. هل تريد التنزيل والتثبيت؟</string>
|
||||
<string name="no_internet_msg">يرجى اللإتصال بالإنترنت! ترقية Magisk مطلوبة.</string>
|
||||
<string name="upgrade_msg">عليك بتحديث ماجيسك لإكمال تهيئة التطبيق. هل تريد التنزيل والتثبيت؟</string>
|
||||
<string name="no_internet_msg">يرجى الإتصال بالإنترنت! تحديث ماجيسك مطلوب.</string>
|
||||
<string name="dling">جارٍ التنزيل</string>
|
||||
<string name="relaunch_app">يرجى إعادة تشغيل التطبيق يدوياً</string>
|
||||
</resources>
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
<resources></resources>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="upgrade_msg">Ажурирајте Magisk да бисте завршили постављање. Преузми и инсталирај?</string>
|
||||
<string name="no_internet_msg">Молимо повежите се на интернет! Неопходно је ажурирање Magisk-а.</string>
|
||||
<string name="dling">Преузимање</string>
|
||||
<string name="relaunch_app">Молимо покрените апликацију поново</string>
|
||||
</resources>
|
||||
|
||||
@@ -20,7 +20,7 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
setupAppCommon()
|
||||
setupTestApk()
|
||||
|
||||
dependencies {
|
||||
implementation(libs.test.runner)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
id("MagiskPlugin")
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete::class) {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
}
|
||||
329
build.py
329
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()
|
||||
|
||||
@@ -76,20 +67,11 @@ support_abis = {
|
||||
"x86_64": "x86_64-linux-android",
|
||||
"riscv64": "riscv64-linux-android",
|
||||
}
|
||||
default_archs = {"armeabi-v7a", "x86", "arm64-v8a", "x86_64"}
|
||||
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()
|
||||
ondk_version = "r29.1"
|
||||
|
||||
# Global vars
|
||||
config = {}
|
||||
@@ -162,37 +144,31 @@ def cmd_out(cmds: list):
|
||||
)
|
||||
|
||||
|
||||
def xz(data):
|
||||
return lzma.compress(data, preset=9, check=lzma.CHECK_NONE)
|
||||
|
||||
|
||||
###############
|
||||
# Build Native
|
||||
###############
|
||||
|
||||
|
||||
def clean_elf():
|
||||
if is_windows:
|
||||
elf_cleaner = Path("tools", "elf-cleaner.exe")
|
||||
else:
|
||||
elf_cleaner = Path("native", "out", "elf-cleaner")
|
||||
if not elf_cleaner.exists():
|
||||
execv(
|
||||
[
|
||||
"gcc",
|
||||
'-DPACKAGE_NAME="termux-elf-cleaner"',
|
||||
'-DPACKAGE_VERSION="2.1.1"',
|
||||
'-DCOPYRIGHT="Copyright (C) 2022 Termux."',
|
||||
"tools/termux-elf-cleaner/elf-cleaner.cpp",
|
||||
"tools/termux-elf-cleaner/arghandling.c",
|
||||
"-o",
|
||||
elf_cleaner,
|
||||
]
|
||||
)
|
||||
cmds = [elf_cleaner, "--api-level", "23"]
|
||||
cargo_toml = Path("tools", "elf-cleaner", "Cargo.toml")
|
||||
cmds = ["run", "--release", "--manifest-path", cargo_toml]
|
||||
if args.verbose == 0:
|
||||
cmds.append("-q")
|
||||
elif args.verbose > 1:
|
||||
cmds.append("--verbose")
|
||||
cmds.append("--")
|
||||
cmds.extend(glob.glob("native/out/*/magisk"))
|
||||
cmds.extend(glob.glob("native/out/*/magiskpolicy"))
|
||||
execv(cmds)
|
||||
run_cargo(cmds)
|
||||
|
||||
|
||||
def collect_ndk_build():
|
||||
for arch in build_abis.keys():
|
||||
arch_dir = Path("native", "libs", arch)
|
||||
out_dir = Path("native", "out", arch)
|
||||
for source in arch_dir.iterdir():
|
||||
target = out_dir / source.name
|
||||
mv(source, target)
|
||||
|
||||
|
||||
def run_ndk_build(cmds: list):
|
||||
@@ -210,13 +186,6 @@ def run_ndk_build(cmds: list):
|
||||
error("Build binary failed!")
|
||||
os.chdir("..")
|
||||
|
||||
for arch in build_abis.keys():
|
||||
arch_dir = Path("native", "libs", arch)
|
||||
out_dir = Path("native", "out", arch)
|
||||
for source in arch_dir.iterdir():
|
||||
target = out_dir / source.name
|
||||
mv(source, target)
|
||||
|
||||
|
||||
def build_cpp_src(targets: set):
|
||||
cmds = []
|
||||
@@ -238,6 +207,7 @@ def build_cpp_src(targets: set):
|
||||
|
||||
if cmds:
|
||||
run_ndk_build(cmds)
|
||||
collect_ndk_build()
|
||||
|
||||
cmds.clear()
|
||||
|
||||
@@ -250,17 +220,18 @@ def build_cpp_src(targets: set):
|
||||
if cmds:
|
||||
cmds.append("B_CRT0=1")
|
||||
run_ndk_build(cmds)
|
||||
collect_ndk_build()
|
||||
|
||||
if clean:
|
||||
clean_elf()
|
||||
|
||||
|
||||
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}")
|
||||
env["RUSTUP_TOOLCHAIN"] = str(rust_sysroot)
|
||||
env["CARGO_BUILD_RUSTFLAGS"] = f"-Z threads={min(8, cpu_count)}"
|
||||
return execv([cargo, *cmds], env)
|
||||
return execv(["cargo", *cmds], env)
|
||||
|
||||
|
||||
def build_rust_src(targets: set):
|
||||
@@ -333,6 +304,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,10 +314,12 @@ def dump_flag_header():
|
||||
|
||||
|
||||
def build_native():
|
||||
ensure_paths()
|
||||
|
||||
# Verify NDK install
|
||||
try:
|
||||
with open(Path(ndk_path, "ONDK_VERSION"), "r") as ondk_ver:
|
||||
assert ondk_ver.read().strip(" \t\r\n") == config["ondkVersion"]
|
||||
assert ondk_ver.read().strip(" \t\r\n") == ondk_version
|
||||
except:
|
||||
error('Unmatched NDK. Please install/upgrade NDK with "build.py ndk"')
|
||||
|
||||
@@ -401,24 +375,28 @@ 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()
|
||||
props = args.config.resolve()
|
||||
|
||||
os.chdir("app")
|
||||
build_type = "Release" if args.release else "Debug"
|
||||
proc = execv(
|
||||
[
|
||||
gradlew,
|
||||
f"{module}:assemble{build_type}",
|
||||
f"-PconfigPath={args.config.resolve()}",
|
||||
f"-PconfigPath={props}",
|
||||
],
|
||||
env=env,
|
||||
)
|
||||
os.chdir("..")
|
||||
if proc.returncode != 0:
|
||||
error(f"Build {module} failed!")
|
||||
|
||||
@@ -427,7 +405,7 @@ def build_apk(module: str):
|
||||
paths = module.split(":")
|
||||
|
||||
apk = f"{paths[-1]}-{build_type}.apk"
|
||||
source = Path(*paths, "build", "outputs", "apk", build_type, apk)
|
||||
source = Path("app", *paths, "build", "outputs", "apk", build_type, apk)
|
||||
target = config["outdir"] / apk
|
||||
mv(source, target)
|
||||
return target
|
||||
@@ -435,7 +413,7 @@ def build_apk(module: str):
|
||||
|
||||
def build_app():
|
||||
header("* Building the Magisk app")
|
||||
apk = build_apk(":app:apk")
|
||||
apk = build_apk(":apk")
|
||||
|
||||
build_type = "release" if args.release else "debug"
|
||||
|
||||
@@ -454,7 +432,7 @@ def build_app():
|
||||
|
||||
def build_stub():
|
||||
header("* Building the stub app")
|
||||
apk = build_apk(":app:stub")
|
||||
apk = build_apk(":stub")
|
||||
header(f"Output: {apk}")
|
||||
|
||||
|
||||
@@ -465,7 +443,7 @@ def build_test():
|
||||
args.release = True
|
||||
try:
|
||||
header("* Building the test app")
|
||||
source = build_apk(":app:test")
|
||||
source = build_apk(":test")
|
||||
target = source.parent / "test.apk"
|
||||
mv(source, target)
|
||||
header(f"Output: {target}")
|
||||
@@ -479,6 +457,7 @@ def build_test():
|
||||
|
||||
|
||||
def cleanup():
|
||||
ensure_paths()
|
||||
support_targets = {"native", "cpp", "rust", "app"}
|
||||
if args.targets:
|
||||
targets = set(args.targets) & support_targets
|
||||
@@ -502,11 +481,15 @@ def cleanup():
|
||||
rm(rs_gen)
|
||||
|
||||
if "native" in targets:
|
||||
header("* Cleaning native")
|
||||
rm_rf(Path("native", "out"))
|
||||
rm_rf(Path("tools", "elf-cleaner", "target"))
|
||||
|
||||
if "app" in targets:
|
||||
header("* Cleaning app")
|
||||
execv([gradlew, ":app:clean"], env=find_jdk())
|
||||
os.chdir("app")
|
||||
execv([gradlew, ":clean"], env=find_jdk())
|
||||
os.chdir("..")
|
||||
|
||||
|
||||
def build_all():
|
||||
@@ -520,6 +503,46 @@ def build_all():
|
||||
############
|
||||
|
||||
|
||||
def gen_ide():
|
||||
ensure_paths()
|
||||
set_archs({args.abi})
|
||||
|
||||
# Dump flags for both C++ and Rust code
|
||||
dump_flag_header()
|
||||
|
||||
# Run build.rs to generate Rust/C++ FFI bindings
|
||||
os.chdir(Path("native", "src"))
|
||||
run_cargo(["check"])
|
||||
os.chdir(Path("..", ".."))
|
||||
|
||||
# Generate compilation database
|
||||
rm_rf(Path("native", "compile_commands.json"))
|
||||
run_ndk_build(
|
||||
[
|
||||
"B_MAGISK=1",
|
||||
"B_INIT=1",
|
||||
"B_BOOT=1",
|
||||
"B_POLICY=1",
|
||||
"B_PRELOAD=1",
|
||||
"B_PROP=1",
|
||||
"B_CRT0=1",
|
||||
"compile_commands.json",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def clippy_cli():
|
||||
args.force_out = True
|
||||
set_archs(default_archs)
|
||||
|
||||
os.chdir(Path("native", "src"))
|
||||
cmds = ["clippy", "--no-deps", "--target"]
|
||||
for triple in build_abis.values():
|
||||
run_cargo(cmds + [triple])
|
||||
run_cargo(cmds + [triple, "--release"])
|
||||
os.chdir(Path("..", ".."))
|
||||
|
||||
|
||||
def cargo_cli():
|
||||
args.force_out = True
|
||||
if len(args.commands) >= 1 and args.commands[0] == "--":
|
||||
@@ -530,10 +553,10 @@ def cargo_cli():
|
||||
|
||||
|
||||
def setup_ndk():
|
||||
ndk_ver = config["ondkVersion"]
|
||||
url = f"https://github.com/topjohnwu/ondk/releases/download/{ndk_ver}/ondk-{ndk_ver}-{os_name}.tar.xz"
|
||||
ensure_paths()
|
||||
url = f"https://github.com/topjohnwu/ondk/releases/download/{ondk_version}/ondk-{ondk_version}-{os_name}.tar.xz"
|
||||
ndk_archive = url.split("/")[-1]
|
||||
ondk_path = Path(ndk_root, f"ondk-{ndk_ver}")
|
||||
ondk_path = Path(ndk_root, f"ondk-{ondk_version}")
|
||||
|
||||
header(f"* Downloading and extracting {ndk_archive}")
|
||||
rm_rf(ondk_path)
|
||||
@@ -548,14 +571,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 +640,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 +650,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 +661,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 +675,47 @@ 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_sysroot
|
||||
global llvm_bin, 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_sysroot = ndk_path / "toolchains" / "rust"
|
||||
llvm_bin = (
|
||||
ndk_path / "toolchains" / "llvm" / "prebuilt" / f"{os_name}-x86_64" / "bin"
|
||||
)
|
||||
adb_path = sdk_path / "platform-tools" / "adb"
|
||||
gradlew = Path.cwd() / "app" / "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):
|
||||
@@ -671,6 +735,12 @@ def parse_props(file):
|
||||
return props
|
||||
|
||||
|
||||
def set_archs(archs: set):
|
||||
triples = map(support_abis.get, archs)
|
||||
global build_abis
|
||||
build_abis = dict(zip(archs, triples))
|
||||
|
||||
|
||||
def load_config():
|
||||
commit_hash = cmd_out(["git", "rev-parse", "--short=8", "HEAD"])
|
||||
|
||||
@@ -685,8 +755,9 @@ def load_config():
|
||||
if args.config.exists():
|
||||
config.update(parse_props(args.config))
|
||||
|
||||
if Path("gradle.properties").exists():
|
||||
for key, value in parse_props("gradle.properties").items():
|
||||
gradle_props = Path("app", "gradle.properties")
|
||||
if gradle_props.exists():
|
||||
for key, value in parse_props(gradle_props).items():
|
||||
if key.startswith("magisk."):
|
||||
config[key[7:]] = value
|
||||
|
||||
@@ -702,12 +773,9 @@ def load_config():
|
||||
abiList = re.split("\\s*,\\s*", config["abiList"])
|
||||
archs = set(abiList) & support_abis.keys()
|
||||
else:
|
||||
archs = {"armeabi-v7a", "x86", "arm64-v8a", "x86_64"}
|
||||
archs = default_archs
|
||||
|
||||
triples = map(support_abis.get, archs)
|
||||
|
||||
global build_abis
|
||||
build_abis = dict(zip(archs, triples))
|
||||
set_archs(archs)
|
||||
|
||||
|
||||
def parse_args():
|
||||
@@ -751,17 +819,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,16 +839,23 @@ 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"
|
||||
)
|
||||
|
||||
gen_parser = subparsers.add_parser("gen", help="generate files for IDE")
|
||||
gen_parser.add_argument("--abi", default="arm64-v8a", help="target ABI to generate")
|
||||
|
||||
# Set callbacks
|
||||
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)
|
||||
gen_parser.set_defaults(func=gen_ide)
|
||||
app_parser.set_defaults(func=build_app)
|
||||
stub_parser.set_defaults(func=build_stub)
|
||||
test_parser.set_defaults(func=build_test)
|
||||
@@ -794,7 +871,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()
|
||||
|
||||
@@ -40,12 +40,9 @@
|
||||
|
||||
### Developing Rust
|
||||
|
||||
The Magisk NDK package [ONDK](https://github.com/topjohnwu/ondk) (the one installed with `./build.py ndk`) bundles a complete Rust toolchain, so _building_ the Magisk project itself does not require any further configuration. However, if you'd like to work on the Rust codebase with proper support, you'd need some setup as most development tools are built around `rustup`.
|
||||
First, install [rustup](https://www.rust-lang.org/tools/install), the official Rust toolchain manager. The Magisk NDK package [ONDK](https://github.com/topjohnwu/ondk) (the one installed with `./build.py ndk`) bundles a complete Rust toolchain, so _building_ the Magisk project itself does not require any further configuration.
|
||||
|
||||
Let's first setup `rustup` to use our custom ONDK Rust toolchain by default:
|
||||
|
||||
- Install [rustup](https://rustup.rs/), the official Rust toolchain manager
|
||||
- Link the ONDK Rust toolchain and set it as default:
|
||||
However, if you'd like to work on the Rust codebase, it'll be easier if you link ONDK's Rust toolchain in `rustup` and set it as default so several development tools and IDEs will work properly:
|
||||
|
||||
```bash
|
||||
# Link the ONDK toolchain with the name "magisk"
|
||||
@@ -54,7 +51,7 @@ rustup toolchain link magisk "$ANDROID_HOME/ndk/magisk/toolchains/rust"
|
||||
rustup default magisk
|
||||
```
|
||||
|
||||
If you plan to use VSCode, you can then install the [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) plugin and everything should be good to go. If you plan to use Jetbrain IDEs (e.g. [Rustrover](https://www.jetbrains.com/rust/), or its Rust Plugin), due to its poor support with custom toolchains, we need some additional setup:
|
||||
If you plan to use VSCode, you can then install the [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) plugin and everything should be good to go. If you plan to use Jetbrain IDEs (e.g. [Rustrover](https://www.jetbrains.com/rust/), or its Rust Plugin), we need some additional setup:
|
||||
|
||||
- Install the official nightly toolchain and add some components. We won't actually use the nightly toolchain for anything other than tricking the IDE to cooperate; the magic happens in the wrapper we setup in the next step.
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Magisk Changelog
|
||||
|
||||
### v29.0
|
||||
|
||||
- [General] Massive internal refactoring and code migration
|
||||
- [App] Support downloading module zip files with XZ compression
|
||||
- [App] Disable app animations when system animations are disabled
|
||||
- [MagiskMount] Support systemlessly deleting files with modules using blank file nodes
|
||||
- [MagiskInit] Redesign sepolicy patching and injection logic
|
||||
- [MagiskSU] Better TTY/PTY support
|
||||
|
||||
### v28.1
|
||||
|
||||
- [App] Fix stub APK download link
|
||||
|
||||
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.
|
||||
|
||||
@@ -76,7 +76,7 @@ As a summary, after installing Magisk in recovery **(starting from power off)**:
|
||||
Before proceeding, please acknowledge that:
|
||||
|
||||
- Installing Magisk **WILL** trip your Knox Warranty Bit, this action is not reversible in any way.
|
||||
- Installing Magisk for the first time **REQUIRES** a full data wipe (this is **NOT** counting the data wipe when unlocking bootloader). Please make a backup your data.
|
||||
- Installing Magisk for the first time **REQUIRES** a full data wipe (this is **NOT** counting the data wipe when unlocking bootloader). Please make a backup of your data.
|
||||
|
||||
### Flashing Tools
|
||||
|
||||
@@ -86,7 +86,7 @@ Before proceeding, please acknowledge that:
|
||||
|
||||
### Requirements
|
||||
|
||||
To verify whether or not Magisk can be installed in your Samsung device, you first must check the OEM Lock and KnoxGuard (RMM) status, to do so boot your device in Download mode with its key combo.
|
||||
To verify whether or not Magisk can be installed in your Samsung device, you first must check the OEM Lock and KnoxGuard (RMM) status. To do so, boot your device in Download mode with its key combo.
|
||||
|
||||
Possible OEM Lock values are the following:
|
||||
- **ON (L)**: fully locked.
|
||||
|
||||
16
docs/releases/29000.md
Normal file
16
docs/releases/29000.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## 2025.5.14 Magisk v29.0
|
||||
|
||||
This release looks minor at the surface, however, the entire codebase has gone through significant refactoring and migration. The native code in Magisk used to be mainly C++, but several contributors and I have been steadily rewriting parts of the code in Rust since April 2022. After years of effort, the Rust-ification of the project slowly began picking up steam, and at the moment of this release, over 40% of the native code has been rewritten in Rust, with several major subsystem rewrites in the PR queue, planned to be merged for the next release.
|
||||
|
||||
Many might wonder, why introduce a new language to the project? My reason is actually not to reduce memory safety issues (although it is a nice side benefit), but to be able to develop Magisk using a more modern programming language. After using Rust for a while, it's clear to me that using Rust allows me to write more correct code and makes me happier compared to dealing with C++. People share the [same sentiment as I do](https://threadreaderapp.com/thread/1577667445719912450.html).
|
||||
|
||||
## Changelog
|
||||
|
||||
- [General] Massive internal refactoring and code migration
|
||||
- [App] Support downloading module zip files with XZ compression
|
||||
- [App] Disable app animations when system animations are disabled
|
||||
- [MagiskMount] Support systemlessly deleting files with modules using blank file nodes
|
||||
- [MagiskInit] Redesign sepolicy patching and injection logic
|
||||
- [MagiskSU] Better TTY/PTY support
|
||||
|
||||
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)
|
||||
@@ -1,5 +1,6 @@
|
||||
# Release Notes
|
||||
|
||||
- [v29.0](29000.md)
|
||||
- [v28.1](28100.md)
|
||||
- [v28.0](28000.md)
|
||||
- [v27.0](27000.md)
|
||||
|
||||
1
native/.gitignore
vendored
1
native/.gitignore
vendored
@@ -5,3 +5,4 @@ libs
|
||||
/.cxx
|
||||
*-rs.cpp
|
||||
*-rs.hpp
|
||||
/compile_commands.json
|
||||
|
||||
@@ -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,10 +18,7 @@ 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 \
|
||||
core/module.cpp \
|
||||
core/thread.cpp \
|
||||
@@ -29,10 +26,7 @@ LOCAL_SRC_FILES := \
|
||||
core/resetprop/resetprop.cpp \
|
||||
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,18 +61,16 @@ 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
|
||||
|
||||
LOCAL_LDFLAGS := -static
|
||||
|
||||
ifdef B_CRT0
|
||||
LOCAL_STATIC_LIBRARIES += crt0
|
||||
LOCAL_LDFLAGS += -Wl,--defsym=vfprintf=tiny_vfprintf
|
||||
endif
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@@ -93,8 +85,6 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libbase \
|
||||
liblzma \
|
||||
liblz4 \
|
||||
libbz2 \
|
||||
libz \
|
||||
libzopfli \
|
||||
libboot-rs
|
||||
|
||||
@@ -125,8 +115,6 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libpolicy \
|
||||
libpolicy-rs
|
||||
|
||||
LOCAL_SRC_FILES := sepolicy/main.cpp
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
|
||||
@@ -5,7 +5,6 @@ APP_STL := none
|
||||
APP_PLATFORM := android-23
|
||||
APP_THIN_ARCHIVE := true
|
||||
APP_STRIP_MODE := none
|
||||
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
|
||||
|
||||
ifdef MAGISK_DEBUG
|
||||
|
||||
|
||||
321
native/src/Cargo.lock
generated
321
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"
|
||||
@@ -66,9 +67,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
@@ -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.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
|
||||
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -122,9 +123,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.3"
|
||||
version = "1.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d"
|
||||
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -137,18 +138,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.23"
|
||||
version = "4.5.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.23"
|
||||
version = "4.5.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -173,9 +174,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.0-rc.3"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ff6be19477a1bd5441f382916a89bc2a0b2c35db6d41e0f6e8538bf6d6463f"
|
||||
checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e"
|
||||
|
||||
[[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.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acbaf157961745008b5a80ee1cc974150691304fe9177edf69747142bfd9878"
|
||||
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"
|
||||
@@ -355,9 +376,9 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"subtle",
|
||||
@@ -365,15 +386,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flagset"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
|
||||
checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[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.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
name = "libz-rs-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[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]]
|
||||
@@ -635,7 +627,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "pb-rs"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/tafia/quick-protobuf.git?rev=2f37d5a65504de7d716b5b28fd82219501a901a9#2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||
source = "git+https://github.com/tafia/quick-protobuf.git#54e7d6c5d981c6f7cec2e9a2167c10ed0f9392b4"
|
||||
dependencies = [
|
||||
"log",
|
||||
"nom",
|
||||
@@ -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",
|
||||
]
|
||||
@@ -707,40 +690,20 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "quick-protobuf"
|
||||
version = "0.8.1"
|
||||
source = "git+https://github.com/tafia/quick-protobuf.git?rev=2f37d5a65504de7d716b5b28fd82219501a901a9#2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||
source = "git+https://github.com/tafia/quick-protobuf.git#54e7d6c5d981c6f7cec2e9a2167c10ed0f9392b4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
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.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
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.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
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.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8"
|
||||
|
||||
@@ -1,45 +1,53 @@
|
||||
[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" }
|
||||
libc = "0.2"
|
||||
cfg-if = "1.0"
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.4"
|
||||
thiserror = "2.0"
|
||||
byteorder = "1"
|
||||
size = "0.4"
|
||||
sha1 = "0.11.0-pre.4"
|
||||
libc = "0.2.171"
|
||||
cfg-if = "1.0.0"
|
||||
num-traits = "0.2.19"
|
||||
num-derive = "0.4.2"
|
||||
thiserror = "2.0.12"
|
||||
byteorder = "1.5.0"
|
||||
size = "0.5.0"
|
||||
bytemuck = "1.22.0"
|
||||
fdt = "0.1.5"
|
||||
const_format = "0.2.34"
|
||||
bit-set = "0.8.0"
|
||||
syn = "2.0.100"
|
||||
quote = "1.0.40"
|
||||
proc-macro2 = "1.0.94"
|
||||
argh = { version = "0.1.13", default-features = false }
|
||||
libz-rs-sys = { version = "0.5.0", features = ["export-symbols"] }
|
||||
libbz2-rs-sys = { version = "0.1.3" }
|
||||
pb-rs = { version = "0.10.0", default-features = false }
|
||||
quick-protobuf = "0.8.1"
|
||||
|
||||
# Rust crypto crates are tied together
|
||||
sha1 = "=0.11.0-pre.4"
|
||||
sha2 = "=0.11.0-pre.4"
|
||||
digest = "0.11.0-pre.9"
|
||||
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"
|
||||
|
||||
[workspace.dependencies.argh]
|
||||
git = "https://github.com/google/argh.git"
|
||||
rev = "1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
||||
default-features = false
|
||||
# Pin version to prevent cargo update breaking 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"
|
||||
rev = "2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.quick-protobuf]
|
||||
git = "https://github.com/tafia/quick-protobuf.git"
|
||||
rev = "2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||
[patch.crates-io]
|
||||
pb-rs = { git = "https://github.com/tafia/quick-protobuf.git" }
|
||||
quick-protobuf = { git = "https://github.com/tafia/quick-protobuf.git" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = "z"
|
||||
@@ -51,3 +59,4 @@ opt-level = "z"
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = true
|
||||
|
||||
@@ -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 std::cmp::min;
|
||||
use cxx::{ExternType, type_id};
|
||||
use libc::c_char;
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::{Ordering, min};
|
||||
use std::ffi::{CStr, FromBytesWithNulError, OsStr};
|
||||
use std::fmt::{Arguments, Debug, Display, Formatter, Write};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::fmt::{Debug, Display, Formatter, Write};
|
||||
use std::ops::Deref;
|
||||
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,88 +18,81 @@ 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 {
|
||||
let mut used = buf.len();
|
||||
if used >= buf.capacity() - 1 {
|
||||
// Truncate
|
||||
return 0;
|
||||
}
|
||||
let dest = unsafe { &mut buf.mut_buf()[used..] };
|
||||
let len = min(s.len(), dest.len() - 1);
|
||||
if len > 0 {
|
||||
dest[..len].copy_from_slice(&s[..len]);
|
||||
}
|
||||
dest[len] = b'\0';
|
||||
used += len;
|
||||
unsafe { buf.set_len(used) };
|
||||
len
|
||||
}
|
||||
// Public helper functions
|
||||
|
||||
fn utf8_cstr_append_lossy(buf: &mut dyn Utf8CStrWrite, s: &[u8]) -> usize {
|
||||
let mut len = 0_usize;
|
||||
for chunk in s.utf8_chunks() {
|
||||
len += buf.push_str(chunk.valid());
|
||||
if !chunk.invalid().is_empty() {
|
||||
len += buf.push_str(char::REPLACEMENT_CHARACTER.encode_utf8(&mut [0; 4]));
|
||||
}
|
||||
pub mod buf {
|
||||
use super::{Utf8CStrBufArr, Utf8CStrBufRef, Utf8CString};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn dynamic(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) }
|
||||
}
|
||||
len
|
||||
}
|
||||
|
||||
// Trait definitions
|
||||
|
||||
pub trait Utf8CStrWrite:
|
||||
Write + AsRef<Utf8CStr> + AsMut<Utf8CStr> + Deref<Target = Utf8CStr> + DerefMut
|
||||
{
|
||||
pub trait Utf8CStrBuf: Write + AsRef<Utf8CStr> + Deref<Target = Utf8CStr> {
|
||||
// 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;
|
||||
// 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);
|
||||
fn as_mut_ptr(&mut self) -> *mut c_char;
|
||||
fn truncate(&mut self, new_len: 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,50 +125,70 @@ impl StringExt for PathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Utf8CString(String);
|
||||
|
||||
impl Utf8CString {
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
impl Default for Utf8CString {
|
||||
fn default() -> Self {
|
||||
Utf8CString::with_capacity(256)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsUtf8CStr for Utf8CString {
|
||||
impl Utf8CString {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Utf8CStr> for Utf8CString {
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr(&self) -> &Utf8CStr {
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
// SAFETY: the internal string is always null terminated
|
||||
unsafe { mem::transmute(slice::from_raw_parts(self.0.as_ptr(), self.0.len() + 1)) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
||||
Utf8CStr::from_string(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
s.len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push_lossy(&mut self, s: &[u8]) -> usize {
|
||||
utf8_cstr_append_lossy(self, s)
|
||||
fn capacity(&self) -> usize {
|
||||
self.0.capacity()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
self.0.nul_terminate();
|
||||
}
|
||||
|
||||
fn as_mut_ptr(&mut self) -> *mut c_char {
|
||||
self.0.as_mut_ptr().cast()
|
||||
}
|
||||
|
||||
fn truncate(&mut self, new_len: usize) {
|
||||
self.0.truncate(new_len);
|
||||
self.0.nul_terminate();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Utf8CString {
|
||||
@@ -185,24 +198,15 @@ 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))) }
|
||||
impl From<&str> for Utf8CString {
|
||||
fn from(value: &str) -> Self {
|
||||
value.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[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 +218,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,23 +229,6 @@ impl<'a> From<&'a mut [u8]> for Utf8CStrBufRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Utf8CStrBuf for Utf8CStrBufRef<'_> {
|
||||
#[inline(always)]
|
||||
fn buf(&self) -> &[u8] {
|
||||
self.buf
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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
|
||||
pub struct Utf8CStrBufArr<const N: usize> {
|
||||
used: usize,
|
||||
@@ -257,28 +244,6 @@ impl<const N: usize> Utf8CStrBufArr<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Utf8CStrBuf for Utf8CStrBufArr<N> {
|
||||
#[inline(always)]
|
||||
fn buf(&self) -> &[u8] {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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> {
|
||||
fn default() -> Self {
|
||||
Utf8CStrBufArr::<4096>::new()
|
||||
@@ -310,27 +275,15 @@ 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 {
|
||||
pub fn from_string(s: &mut String) -> &Utf8CStr {
|
||||
let buf = s.nul_terminate();
|
||||
// SAFETY: the null byte is explicitly added to the buffer
|
||||
unsafe { mem::transmute(buf) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_bytes_unchecked(buf: &[u8]) -> &Utf8CStr {
|
||||
mem::transmute(buf)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_bytes_unchecked_mut(buf: &mut [u8]) -> &mut Utf8CStr {
|
||||
mem::transmute(buf)
|
||||
pub const unsafe fn from_bytes_unchecked(buf: &[u8]) -> &Utf8CStr {
|
||||
unsafe { mem::transmute(buf) }
|
||||
}
|
||||
|
||||
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> Result<&'a Utf8CStr, StrErr> {
|
||||
@@ -341,8 +294,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)]
|
||||
@@ -355,11 +310,6 @@ impl Utf8CStr {
|
||||
self.0.as_ptr().cast()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_mut_ptr(&mut self) -> *mut c_char {
|
||||
self.0.as_mut_ptr().cast()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_cstr(&self) -> &CStr {
|
||||
// SAFETY: Already validated as null terminated during construction
|
||||
@@ -372,13 +322,6 @@ impl Utf8CStr {
|
||||
// SAFETY: The length of the slice is at least 1 due to null termination check
|
||||
unsafe { str::from_utf8_unchecked(self.0.get_unchecked(..self.0.len() - 1)) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_str_mut(&mut self) -> &mut str {
|
||||
// SAFETY: Already UTF-8 validated during construction
|
||||
// SAFETY: The length of the slice is at least 1 due to null termination check
|
||||
unsafe { str::from_utf8_unchecked_mut(self.0.get_unchecked_mut(..self.0.len() - 1)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Utf8CStr {
|
||||
@@ -390,10 +333,19 @@ impl Deref for Utf8CStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Utf8CStr {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_str_mut()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Utf8CStr> for Utf8CStr {
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,90 +368,39 @@ const_assert_eq!(align_of::<&Utf8CStr>(), align_of::<[usize; 2]>());
|
||||
// File system path extensions types
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct FsPath(Utf8CStr);
|
||||
pub struct FsPathFollow(Utf8CStr);
|
||||
|
||||
impl FsPath {
|
||||
impl AsRef<Utf8CStr> for FsPathFollow {
|
||||
#[inline(always)]
|
||||
pub fn from<T: AsRef<Utf8CStr> + ?Sized>(value: &T) -> &FsPath {
|
||||
unsafe { mem::transmute(value.as_ref()) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_mut<T: AsMut<Utf8CStr> + ?Sized>(value: &mut T) -> &mut FsPath {
|
||||
unsafe { mem::transmute(value.as_mut()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for FsPath {
|
||||
type Target = Utf8CStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Utf8CStr {
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FsPath {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Utf8CStr {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FsPathBuf<'a>(&'a mut dyn Utf8CStrWrite);
|
||||
|
||||
impl<'a> FsPathBuf<'a> {
|
||||
pub fn new(value: &'a mut dyn Utf8CStrWrite) -> Self {
|
||||
value.clear();
|
||||
FsPathBuf(value)
|
||||
}
|
||||
|
||||
pub fn join<T: AsRef<str>>(self, path: T) -> Self {
|
||||
fn inner(buf: &mut dyn Utf8CStrWrite, path: &str) {
|
||||
if path.starts_with('/') {
|
||||
buf.clear();
|
||||
} else {
|
||||
buf.push_str("/");
|
||||
}
|
||||
buf.push_str(path);
|
||||
}
|
||||
inner(self.0, 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));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for FsPathBuf<'_> {
|
||||
type Target = FsPath;
|
||||
|
||||
fn deref(&self) -> &FsPath {
|
||||
FsPath::from(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FsPathBuf<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
FsPath::from_mut(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Boilerplate trait implementations
|
||||
|
||||
macro_rules! impl_str {
|
||||
// impl<T: AsRef<Utf8CStr>> Deref<Target = Utf8CStr> for T { ... }
|
||||
macro_rules! impl_cstr_deref {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> AsRef<Utf8CStr> for $t {
|
||||
impl<$($g)*> Deref for $t {
|
||||
type Target = Utf8CStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &Utf8CStr {
|
||||
self
|
||||
fn deref(&self) -> &Utf8CStr {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_cstr_deref!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
(FsPathFollow,)
|
||||
);
|
||||
|
||||
// impl<T: Deref<Target = Utf8CStr>> BoilerPlate for T { ... }
|
||||
macro_rules! impl_cstr_misc {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> AsRef<str> for $t {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
@@ -560,25 +461,107 @@ macro_rules! impl_str {
|
||||
self == other.as_cstr()
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<Utf8CStr>, $($g)*> PartialEq<T> for $t {
|
||||
impl<T: AsRef<Utf8CStr> + ?Sized, $($g)*> PartialEq<T> for $t {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.as_bytes_with_nul() == other.as_ref().as_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
impl<$($g)*> Eq for $t {}
|
||||
impl<$($g)*> PartialOrd for $t {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl<$($g)*> Ord for $t {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_str!(
|
||||
impl_cstr_misc!(
|
||||
(Utf8CStr,)
|
||||
(FsPath,)
|
||||
(FsPathBuf<'_>,)
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
(FsPathFollow,)
|
||||
);
|
||||
|
||||
macro_rules! impl_str_write {
|
||||
fn copy_cstr_truncate(dest: &mut [u8], src: &[u8]) -> usize {
|
||||
if dest.len() <= 1 {
|
||||
// Truncate
|
||||
return 0;
|
||||
}
|
||||
let len = min(src.len(), dest.len() - 1);
|
||||
if len > 0 {
|
||||
dest[..len].copy_from_slice(&src[..len]);
|
||||
}
|
||||
dest[len] = b'\0';
|
||||
len
|
||||
}
|
||||
|
||||
// impl<T> AsRef<Utf8CStr> for T { ... }
|
||||
// impl<T> Utf8CStrBuf for T { ... }
|
||||
macro_rules! impl_cstr_buf {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> AsRef<Utf8CStr> for $t {
|
||||
#[inline(always)]
|
||||
fn as_ref(&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.used + 1))) }
|
||||
}
|
||||
}
|
||||
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 {
|
||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||
let dest = unsafe { self.buf.get_unchecked_mut(self.used..) };
|
||||
let len = copy_cstr_truncate(dest, s.as_bytes());
|
||||
self.used += len;
|
||||
len
|
||||
}
|
||||
#[inline(always)]
|
||||
fn capacity(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn clear(&mut self) {
|
||||
self.buf[0] = b'\0';
|
||||
self.used = 0;
|
||||
}
|
||||
#[inline(always)]
|
||||
fn as_mut_ptr(&mut self) -> *mut c_char {
|
||||
self.buf.as_mut_ptr().cast()
|
||||
}
|
||||
fn truncate(&mut self, new_len: usize) {
|
||||
if self.used <= new_len {
|
||||
return;
|
||||
}
|
||||
self.buf[new_len] = b'\0';
|
||||
self.used = new_len;
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_cstr_buf!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
);
|
||||
|
||||
// impl<T: Utf8CStrBuf> Write for T { ... }
|
||||
macro_rules! impl_cstr_buf_write {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> Write for $t {
|
||||
#[inline(always)]
|
||||
@@ -587,84 +570,27 @@ macro_rules! impl_str_write {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl<$($g)*> Deref for $t {
|
||||
type Target = Utf8CStr;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Utf8CStr {
|
||||
self.as_utf8_cstr()
|
||||
}
|
||||
}
|
||||
impl<$($g)*> DerefMut for $t {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Utf8CStr {
|
||||
self.as_utf8_cstr_mut()
|
||||
}
|
||||
}
|
||||
impl<$($g)*> AsMut<Utf8CStr> for $t {
|
||||
#[inline(always)]
|
||||
fn as_mut(&mut self) -> &mut Utf8CStr {
|
||||
self.as_utf8_cstr_mut()
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_str_write!(
|
||||
impl_cstr_buf_write!(
|
||||
(Utf8CStrBufRef<'_>,)
|
||||
(Utf8CStrBufArr<N>, const N: usize)
|
||||
(Utf8CString,)
|
||||
);
|
||||
|
||||
macro_rules! impl_str_buf {
|
||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||
impl<$($g)*> Utf8CStrWrite for $t {
|
||||
#[inline(always)]
|
||||
fn len(&self) -> usize {
|
||||
self.used
|
||||
}
|
||||
#[inline(always)]
|
||||
fn push_str(&mut self, s: &str) -> usize {
|
||||
utf8_cstr_buf_append(self, s.as_bytes())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn push_lossy(&mut self, s: &[u8]) -> usize {
|
||||
utf8_cstr_append_lossy(self, s)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn clear(&mut self) {
|
||||
self.buf[0] = b'\0';
|
||||
self.used = 0;
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
impl_str_buf!(
|
||||
(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() }};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Functions in this file are only for exporting to C++, DO NOT USE IN RUST
|
||||
|
||||
use std::io;
|
||||
use std::os::fd::{BorrowedFd, OwnedFd, RawFd};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
@@ -9,49 +8,55 @@ 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,
|
||||
CxxResultExt, Directory, OsResultStatic, Utf8CStr, clone_attr, cstr, fclone_attr, fd_path,
|
||||
map_fd, map_file, slice_from_ptr,
|
||||
};
|
||||
|
||||
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"))
|
||||
.log_cxx()
|
||||
.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(path) => {
|
||||
let mut buf = cstr::buf::wrap_ptr(buf, bufsz);
|
||||
path.realpath(&mut buf)
|
||||
.log_cxx()
|
||||
.map_or(-1_isize, |_| 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(path) => path.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(path) => path.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<()> {
|
||||
fn inner(fd: OwnedFd) -> OsResultStatic<()> {
|
||||
Directory::try_from(fd)?.remove_all()
|
||||
}
|
||||
inner(fd).is_ok()
|
||||
@@ -77,116 +82,99 @@ pub(crate) fn map_fd_for_cxx(fd: RawFd, sz: usize, rw: bool) -> &'static mut [u8
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn readlinkat_for_cxx(
|
||||
pub(crate) unsafe fn readlinkat(
|
||||
dirfd: RawFd,
|
||||
path: *const c_char,
|
||||
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) {
|
||||
return src.copy_to(dest).log_cxx().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) {
|
||||
return src.move_to(dest).log_cxx().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) {
|
||||
return src.link_to(dest).log_cxx().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) {
|
||||
return clone_attr(src, dest).log_cxx().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()
|
||||
fclone_attr(a, b).log_cxx().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()
|
||||
}
|
||||
|
||||
495
native/src/base/dir.rs
Normal file
495
native/src/base/dir.rs
Normal file
@@ -0,0 +1,495 @@
|
||||
use crate::cxx_extern::readlinkat;
|
||||
use crate::{
|
||||
FsPathBuilder, LibcReturn, OsError, OsResult, OsResultStatic, Utf8CStr, Utf8CStrBuf, cstr,
|
||||
errno, fd_path, fd_set_attr,
|
||||
};
|
||||
use libc::{EEXIST, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, dirent, mode_t};
|
||||
use std::fs::File;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||
use std::ptr::NonNull;
|
||||
use std::{mem, slice};
|
||||
|
||||
pub struct DirEntry<'a> {
|
||||
dir: BorrowedDirectory<'a>,
|
||||
entry: NonNull<dirent>,
|
||||
d_name_len: usize,
|
||||
}
|
||||
|
||||
impl DirEntry<'_> {
|
||||
pub fn as_ptr(&self) -> *mut dirent {
|
||||
self.entry.as_ptr()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Utf8CStr {
|
||||
unsafe {
|
||||
Utf8CStr::from_bytes_unchecked(slice::from_raw_parts(
|
||||
self.d_name.as_ptr().cast(),
|
||||
self.d_name_len,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||
self.dir.path_at(self.name(), buf)
|
||||
}
|
||||
|
||||
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) -> OsResult<()> {
|
||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||
self.dir.unlink_at(self.name(), flag)
|
||||
}
|
||||
|
||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<()> {
|
||||
self.dir.read_link_at(self.name(), buf)
|
||||
}
|
||||
|
||||
pub fn open_as_dir(&self) -> OsResult<Directory> {
|
||||
if !self.is_dir() {
|
||||
return Err(OsError::with_os_error(
|
||||
libc::ENOTDIR,
|
||||
"fdopendir",
|
||||
Some(self.name()),
|
||||
None,
|
||||
));
|
||||
}
|
||||
self.dir.open_as_dir_at(self.name())
|
||||
}
|
||||
|
||||
pub fn open_as_file(&self, flags: i32) -> OsResult<File> {
|
||||
if self.is_dir() {
|
||||
return Err(OsError::with_os_error(
|
||||
libc::EISDIR,
|
||||
"open_as_file",
|
||||
Some(self.name()),
|
||||
None,
|
||||
));
|
||||
}
|
||||
self.dir.open_as_file_at(self.name(), flags, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DirEntry<'_> {
|
||||
type Target = dirent;
|
||||
|
||||
fn deref(&self) -> &dirent {
|
||||
unsafe { self.entry.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Directory {
|
||||
inner: NonNull<libc::DIR>,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct BorrowedDirectory<'a> {
|
||||
inner: NonNull<libc::DIR>,
|
||||
phantom: PhantomData<&'a Directory>,
|
||||
}
|
||||
|
||||
impl Deref for BorrowedDirectory<'_> {
|
||||
type Target = Directory;
|
||||
|
||||
fn deref(&self) -> &Directory {
|
||||
// SAFETY: layout of NonNull<libc::DIR> is the same as Directory
|
||||
// SAFETY: the lifetime of the raw pointer is tracked in the PhantomData
|
||||
unsafe { mem::transmute(&self.inner) }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for BorrowedDirectory<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Directory {
|
||||
// SAFETY: layout of NonNull<libc::DIR> is the same as Directory
|
||||
// SAFETY: the lifetime of the raw pointer is tracked in the PhantomData
|
||||
unsafe { mem::transmute(&mut self.inner) }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum WalkResult {
|
||||
Continue,
|
||||
Abort,
|
||||
Skip,
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
fn borrow(&self) -> BorrowedDirectory {
|
||||
BorrowedDirectory {
|
||||
inner: self.inner,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn openat<'a>(&self, name: &'a Utf8CStr, flags: i32, mode: u32) -> OsResult<'a, OwnedFd> {
|
||||
unsafe {
|
||||
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode)
|
||||
.as_os_result("openat", Some(name), None)
|
||||
.map(|fd| OwnedFd::from_raw_fd(fd))
|
||||
}
|
||||
}
|
||||
|
||||
fn path_at(&self, name: &Utf8CStr, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||
self.resolve_path(buf)?;
|
||||
buf.append_path(name);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
pub fn open(path: &Utf8CStr) -> OsResult<Directory> {
|
||||
let dirp = unsafe { libc::opendir(path.as_ptr()) };
|
||||
let dirp = dirp.as_os_result("opendir", Some(path), None)?;
|
||||
Ok(Directory { inner: dirp })
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> OsResult<'static, Option<DirEntry>> {
|
||||
*errno() = 0;
|
||||
let e = unsafe { libc::readdir(self.inner.as_ptr()) };
|
||||
if e.is_null() {
|
||||
return if *errno() != 0 {
|
||||
Err(OsError::last_os_error("readdir", None, None))
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
}
|
||||
// Skip non UTF-8 entries, ".", and ".."
|
||||
unsafe {
|
||||
let entry = &*e;
|
||||
|
||||
let Ok(name) = Utf8CStr::from_ptr(entry.d_name.as_ptr()) else {
|
||||
return self.read();
|
||||
};
|
||||
|
||||
if name == "." || name == ".." {
|
||||
self.read()
|
||||
} else {
|
||||
let e = DirEntry {
|
||||
dir: self.borrow(),
|
||||
entry: NonNull::from(entry),
|
||||
d_name_len: name.as_bytes_with_nul().len(),
|
||||
};
|
||||
Ok(Some(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewind(&mut self) {
|
||||
unsafe { libc::rewinddir(self.inner.as_ptr()) };
|
||||
}
|
||||
|
||||
pub fn open_as_dir_at<'a>(&self, name: &'a Utf8CStr) -> OsResult<'a, Directory> {
|
||||
let fd = self.openat(name, O_RDONLY, 0)?;
|
||||
Directory::try_from(fd).map_err(|e| e.set_args(Some(name), None))
|
||||
}
|
||||
|
||||
pub fn open_as_file_at<'a>(
|
||||
&self,
|
||||
name: &'a Utf8CStr,
|
||||
flags: i32,
|
||||
mode: u32,
|
||||
) -> OsResult<'a, File> {
|
||||
let fd = self.openat(name, flags, mode)?;
|
||||
Ok(File::from(fd))
|
||||
}
|
||||
|
||||
pub fn read_link_at<'a>(
|
||||
&self,
|
||||
name: &'a Utf8CStr,
|
||||
buf: &mut dyn Utf8CStrBuf,
|
||||
) -> OsResult<'a, ()> {
|
||||
buf.clear();
|
||||
unsafe {
|
||||
let r = readlinkat(
|
||||
self.as_raw_fd(),
|
||||
name.as_ptr(),
|
||||
buf.as_mut_ptr().cast(),
|
||||
buf.capacity(),
|
||||
)
|
||||
.as_os_result("readlinkat", Some(name), None)? as usize;
|
||||
buf.set_len(r);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mkdir_at<'a>(&self, name: &'a Utf8CStr, mode: mode_t) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
if libc::mkdirat(self.as_raw_fd(), name.as_ptr(), mode as mode_t) < 0
|
||||
&& *errno() != EEXIST
|
||||
{
|
||||
return Err(OsError::last_os_error("mkdirat", Some(name), None));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ln -s target self/name
|
||||
pub fn create_symlink_at<'a>(
|
||||
&self,
|
||||
name: &'a Utf8CStr,
|
||||
target: &'a Utf8CStr,
|
||||
) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::symlinkat(target.as_ptr(), self.as_raw_fd(), name.as_ptr()).check_os_err(
|
||||
"symlinkat",
|
||||
Some(target),
|
||||
Some(name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlink_at<'a>(&self, name: &'a Utf8CStr, flag: i32) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::unlinkat(self.as_raw_fd(), name.as_ptr(), flag).check_os_err(
|
||||
"unlinkat",
|
||||
Some(name),
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn contains_path(&self, path: &Utf8CStr) -> 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 resolve_path(&self, buf: &mut dyn Utf8CStrBuf) -> OsResult<'static, ()> {
|
||||
fd_path(self.as_raw_fd(), buf)
|
||||
}
|
||||
|
||||
pub fn post_order_walk<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> OsResultStatic<WalkResult> {
|
||||
self.post_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn pre_order_walk<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||
&mut self,
|
||||
mut f: F,
|
||||
) -> OsResultStatic<WalkResult> {
|
||||
self.pre_order_walk_impl(&mut f)
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self) -> OsResultStatic<()> {
|
||||
self.post_order_walk(|e| {
|
||||
e.unlink()?;
|
||||
Ok(WalkResult::Continue)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||
let mut buf = cstr::buf::default();
|
||||
self.copy_into_impl(dir, &mut buf)
|
||||
}
|
||||
|
||||
pub fn move_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||
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 dest = dir.open_as_dir_at(e.name())?;
|
||||
src.move_into(&dest)?;
|
||||
return Ok(e.unlink()?);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
libc::renameat(
|
||||
dir_fd,
|
||||
e.d_name.as_ptr(),
|
||||
dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
)
|
||||
.check_os_err("renameat", Some(e.name()), None)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn link_into(&mut self, dir: &Directory) -> OsResultStatic<()> {
|
||||
let mut buf = cstr::buf::default();
|
||||
self.link_into_impl(dir, &mut buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
fn post_order_walk_impl<F: FnMut(&DirEntry) -> OsResultStatic<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> OsResultStatic<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) -> OsResultStatic<WalkResult>>(
|
||||
&mut self,
|
||||
f: &mut F,
|
||||
) -> OsResultStatic<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_into_impl(
|
||||
&mut self,
|
||||
dest_dir: &Directory,
|
||||
buf: &mut dyn Utf8CStrBuf,
|
||||
) -> OsResultStatic<()> {
|
||||
while let Some(ref e) = self.read()? {
|
||||
e.resolve_path(buf)?;
|
||||
let attr = buf.get_attr()?;
|
||||
if e.is_dir() {
|
||||
dest_dir.mkdir_at(e.name(), 0o777)?;
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = dest_dir.open_as_dir_at(e.name())?;
|
||||
src.copy_into_impl(&dest, buf)?;
|
||||
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 =
|
||||
dest_dir.open_as_file_at(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() {
|
||||
e.read_link(buf)?;
|
||||
dest_dir.create_symlink_at(e.name(), buf)?;
|
||||
dest_dir.path_at(e.name(), buf)?;
|
||||
buf.set_attr(&attr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn link_into_impl(
|
||||
&mut self,
|
||||
dest_dir: &Directory,
|
||||
buf: &mut dyn Utf8CStrBuf,
|
||||
) -> OsResultStatic<()> {
|
||||
let dir_fd = self.as_raw_fd();
|
||||
while let Some(ref e) = self.read()? {
|
||||
if e.is_dir() {
|
||||
dest_dir.mkdir_at(e.name(), 0o777)?;
|
||||
e.resolve_path(buf)?;
|
||||
let attr = buf.get_attr()?;
|
||||
let mut src = e.open_as_dir()?;
|
||||
let dest = dest_dir.open_as_dir_at(e.name())?;
|
||||
src.link_into_impl(&dest, buf)?;
|
||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||
} else {
|
||||
unsafe {
|
||||
libc::linkat(
|
||||
dir_fd,
|
||||
e.d_name.as_ptr(),
|
||||
dest_dir.as_raw_fd(),
|
||||
e.d_name.as_ptr(),
|
||||
0,
|
||||
)
|
||||
.check_os_err("linkat", Some(e.name()), None)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<OwnedFd> for Directory {
|
||||
type Error = OsError<'static>;
|
||||
|
||||
fn try_from(fd: OwnedFd) -> OsResult<'static, Self> {
|
||||
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) };
|
||||
let dirp = dirp.as_os_result("fdopendir", None, None)?;
|
||||
Ok(Directory { inner: dirp })
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Directory {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
unsafe { libc::dirfd(self.inner.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
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.inner.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ string resolve_preinit_dir(const char *base_dir) {
|
||||
if (access((dir + "/unencrypted").data(), F_OK) == 0) {
|
||||
dir += "/unencrypted/magisk";
|
||||
} else if (access((dir + "/adb").data(), F_OK) == 0) {
|
||||
dir += "/adb/modules";
|
||||
dir += "/adb";
|
||||
} else if (access((dir + "/watchdog").data(), F_OK) == 0) {
|
||||
dir += "/watchdog/magisk";
|
||||
} else {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>>;
|
||||
|
||||
@@ -6,18 +6,24 @@ pub use const_format;
|
||||
pub use libc;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
pub use cstr::*;
|
||||
pub use cstr::{
|
||||
FsPathFollow, StrErr, Utf8CStr, Utf8CStrBuf, Utf8CStrBufArr, Utf8CStrBufRef, Utf8CString,
|
||||
};
|
||||
use cxx_extern::*;
|
||||
pub use dir::*;
|
||||
pub use ffi::fork_dont_care;
|
||||
pub use files::*;
|
||||
pub use logging::*;
|
||||
pub use misc::*;
|
||||
pub use result::*;
|
||||
|
||||
mod cstr;
|
||||
pub mod cstr;
|
||||
mod cxx_extern;
|
||||
mod dir;
|
||||
mod files;
|
||||
mod logging;
|
||||
mod misc;
|
||||
mod mount;
|
||||
mod result;
|
||||
mod xwrap;
|
||||
|
||||
@@ -42,6 +48,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" {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Arguments;
|
||||
use std::io::{stderr, stdout, Write};
|
||||
use std::io::{Write, stderr, stdout};
|
||||
use std::process::exit;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::ffi::LogLevelCxx;
|
||||
use crate::{Utf8CStr, Utf8CStrBufArr};
|
||||
use crate::{Utf8CStr, cstr};
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
@@ -186,27 +186,6 @@ int parse_int(string_view s) {
|
||||
return parse_num<int, 10>(s);
|
||||
}
|
||||
|
||||
uint64_t parse_uint64_hex(string_view s) {
|
||||
return parse_num<uint64_t, 16>(s);
|
||||
}
|
||||
|
||||
uint32_t binary_gcd(uint32_t u, uint32_t v) {
|
||||
if (u == 0) return v;
|
||||
if (v == 0) return u;
|
||||
auto shift = __builtin_ctz(u | v);
|
||||
u >>= __builtin_ctz(u);
|
||||
do {
|
||||
v >>= __builtin_ctz(v);
|
||||
if (u > v) {
|
||||
auto t = v;
|
||||
v = u;
|
||||
u = t;
|
||||
}
|
||||
v -= u;
|
||||
} while (v != 0);
|
||||
return u << shift;
|
||||
}
|
||||
|
||||
int switch_mnt_ns(int pid) {
|
||||
int ret = -1;
|
||||
int fd = syscall(__NR_pidfd_open, pid, 0);
|
||||
@@ -255,10 +234,6 @@ vector<string> split(string_view s, string_view delims) {
|
||||
return split_impl<string>(s, delims);
|
||||
}
|
||||
|
||||
vector<string_view> split_view(string_view s, string_view delims) {
|
||||
return split_impl<string_view>(s, delims);
|
||||
}
|
||||
|
||||
#undef vsnprintf
|
||||
int vssprintf(char *dest, size_t size, const char *fmt, va_list ap) {
|
||||
if (size > 0) {
|
||||
|
||||
@@ -71,57 +71,6 @@ static inline void default_new(T *&p) { p = new T(); }
|
||||
template<class T>
|
||||
static inline void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
|
||||
|
||||
template<typename T, typename Impl>
|
||||
class stateless_allocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
T *allocate(size_t num) { return static_cast<T*>(Impl::allocate(sizeof(T) * num)); }
|
||||
void deallocate(T *ptr, size_t num) { Impl::deallocate(ptr, sizeof(T) * num); }
|
||||
stateless_allocator() = default;
|
||||
stateless_allocator(const stateless_allocator&) = default;
|
||||
stateless_allocator(stateless_allocator&&) = default;
|
||||
template <typename U>
|
||||
stateless_allocator(const stateless_allocator<U, Impl>&) {}
|
||||
bool operator==(const stateless_allocator&) { return true; }
|
||||
bool operator!=(const stateless_allocator&) { return false; }
|
||||
};
|
||||
|
||||
class dynamic_bitset_impl {
|
||||
public:
|
||||
using slot_type = unsigned long;
|
||||
constexpr static int slot_size = sizeof(slot_type) * 8;
|
||||
using slot_bits = std::bitset<slot_size>;
|
||||
|
||||
size_t slots() const { return slot_list.size(); }
|
||||
slot_type get_slot(size_t slot) const {
|
||||
return slot_list.size() > slot ? slot_list[slot].to_ulong() : 0ul;
|
||||
}
|
||||
void emplace_back(slot_type l) {
|
||||
slot_list.emplace_back(l);
|
||||
}
|
||||
protected:
|
||||
slot_bits::reference get(size_t pos) {
|
||||
size_t slot = pos / slot_size;
|
||||
size_t index = pos % slot_size;
|
||||
if (slot_list.size() <= slot) {
|
||||
slot_list.resize(slot + 1);
|
||||
}
|
||||
return slot_list[slot][index];
|
||||
}
|
||||
bool get(size_t pos) const {
|
||||
size_t slot = pos / slot_size;
|
||||
size_t index = pos % slot_size;
|
||||
return slot_list.size() > slot && slot_list[slot][index];
|
||||
}
|
||||
private:
|
||||
std::vector<slot_bits> slot_list;
|
||||
};
|
||||
|
||||
struct dynamic_bitset : public dynamic_bitset_impl {
|
||||
slot_bits::reference operator[] (size_t pos) { return get(pos); }
|
||||
bool operator[] (size_t pos) const { return get(pos); }
|
||||
};
|
||||
|
||||
struct StringCmp {
|
||||
using is_transparent = void;
|
||||
bool operator()(std::string_view a, std::string_view b) const { return a < b; }
|
||||
@@ -198,13 +147,6 @@ struct byte_data : public byte_view {
|
||||
rust::Vec<size_t> patch(byte_view from, byte_view to);
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct byte_array : public byte_data {
|
||||
byte_array() : byte_data(arr, N), arr{0} {}
|
||||
private:
|
||||
uint8_t arr[N];
|
||||
};
|
||||
|
||||
class byte_stream;
|
||||
|
||||
struct heap_data : public byte_data {
|
||||
@@ -238,7 +180,6 @@ rust::Vec<size_t> mut_u8_patch(
|
||||
rust::Slice<const uint8_t> from,
|
||||
rust::Slice<const uint8_t> to);
|
||||
|
||||
uint64_t parse_uint64_hex(std::string_view s);
|
||||
int parse_int(std::string_view s);
|
||||
|
||||
using thread_entry = void *(*)(void *);
|
||||
@@ -270,11 +211,9 @@ int fork_dont_care();
|
||||
int fork_no_orphan();
|
||||
void init_argv0(int argc, char **argv);
|
||||
void set_nice_name(const char *name);
|
||||
uint32_t binary_gcd(uint32_t u, uint32_t v);
|
||||
int switch_mnt_ns(int pid);
|
||||
std::string &replace_all(std::string &str, std::string_view from, std::string_view to);
|
||||
std::vector<std::string> split(std::string_view s, std::string_view delims);
|
||||
std::vector<std::string_view> split_view(std::string_view, std::string_view delims);
|
||||
|
||||
// Similar to vsnprintf, but the return value is the written number of bytes
|
||||
__printflike(3, 0) int vssprintf(char *dest, size_t size, const char *fmt, va_list ap);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::fmt::Arguments;
|
||||
use std::io::Write;
|
||||
use std::process::exit;
|
||||
use std::{fmt, io, slice, str};
|
||||
|
||||
use crate::{StrErr, Utf8CStr, ffi};
|
||||
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::Arc;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::{fmt, slice, str};
|
||||
|
||||
pub fn errno() -> &'static mut i32 {
|
||||
unsafe { &mut *libc::__errno() }
|
||||
@@ -15,67 +16,25 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Check libc return value and map to Result
|
||||
pub trait LibcReturn
|
||||
where
|
||||
Self: Copy,
|
||||
{
|
||||
fn is_error(&self) -> bool;
|
||||
fn check_os_err(self) -> io::Result<Self> {
|
||||
if self.is_error() {
|
||||
Err(io::Error::last_os_error())
|
||||
unsafe {
|
||||
if len == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
Ok(self)
|
||||
slice::from_raw_parts_mut(buf, len)
|
||||
}
|
||||
}
|
||||
fn as_os_err(self) -> io::Result<()> {
|
||||
self.check_os_err()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_libc_return {
|
||||
($($t:ty)*) => ($(
|
||||
impl LibcReturn for $t {
|
||||
#[inline]
|
||||
fn is_error(&self) -> bool {
|
||||
*self < 0
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
impl_libc_return! { i8 i16 i32 i64 isize }
|
||||
|
||||
impl<T> LibcReturn for *const T {
|
||||
#[inline]
|
||||
fn is_error(&self) -> bool {
|
||||
self.is_null()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LibcReturn for *mut T {
|
||||
#[inline]
|
||||
fn is_error(&self) -> bool {
|
||||
self.is_null()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BytesExt {
|
||||
@@ -161,3 +120,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())
|
||||
}
|
||||
}
|
||||
|
||||
90
native/src/base/mount.rs
Normal file
90
native/src/base/mount.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use crate::{LibcReturn, OsResult, Utf8CStr};
|
||||
use libc::c_ulong;
|
||||
use std::ptr;
|
||||
|
||||
impl Utf8CStr {
|
||||
pub fn bind_mount_to<'a>(&'a self, path: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
self.as_ptr(),
|
||||
path.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_BIND,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("bind_mount", Some(self), Some(path))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_mount_point_flags(&self, flags: c_ulong) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
self.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_BIND | libc::MS_REMOUNT | flags,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("remount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_mount_flags(&self, flags: c_ulong) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
self.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_REMOUNT | flags,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("remount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remount_with_data(&self, data: &Utf8CStr) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
self.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_REMOUNT,
|
||||
data.as_ptr().cast(),
|
||||
)
|
||||
.check_os_err("remount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_mount_to<'a>(&'a self, path: &'a Utf8CStr) -> OsResult<'a, ()> {
|
||||
unsafe {
|
||||
libc::mount(
|
||||
self.as_ptr(),
|
||||
path.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_MOVE,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("move_mount", Some(self), Some(path))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmount(&self) -> OsResult<()> {
|
||||
unsafe {
|
||||
libc::umount2(self.as_ptr(), libc::MNT_DETACH).check_os_err("unmount", Some(self), None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_mount_private(&self, recursive: bool) -> OsResult<()> {
|
||||
let flag = if recursive { libc::MS_REC } else { 0 };
|
||||
unsafe {
|
||||
libc::mount(
|
||||
ptr::null(),
|
||||
self.as_ptr(),
|
||||
ptr::null(),
|
||||
libc::MS_PRIVATE | flag,
|
||||
ptr::null(),
|
||||
)
|
||||
.check_os_err("set_mount_private", Some(self), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
use std::fmt;
|
||||
use crate::logging::Formatter;
|
||||
use crate::{LogLevel, errno, log_with_args, log_with_formatter};
|
||||
use std::fmt::Display;
|
||||
use std::panic::Location;
|
||||
|
||||
use crate::logging::Formatter;
|
||||
use crate::{log_with_args, log_with_formatter, LogLevel};
|
||||
use std::ptr::NonNull;
|
||||
use std::{fmt, io};
|
||||
use thiserror::Error;
|
||||
|
||||
// Error handling throughout the Rust codebase in Magisk:
|
||||
//
|
||||
@@ -55,12 +56,12 @@ 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
|
||||
pub(crate) trait CxxResultExt<T> {
|
||||
fn log_cxx(self) -> LoggedResult<T>;
|
||||
fn log_cxx_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
|
||||
}
|
||||
|
||||
trait Loggable<T> {
|
||||
@@ -77,10 +78,6 @@ impl<T, R: Loggable<T>> CxxResultExt<T> for R {
|
||||
fn log_cxx(self) -> LoggedResult<T> {
|
||||
self.do_log(LogLevel::ErrorCxx, None)
|
||||
}
|
||||
|
||||
fn log_cxx_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||
self.do_log_msg(LogLevel::ErrorCxx, None, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R: Loggable<T>> ResultExt<T> for R {
|
||||
@@ -105,6 +102,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> {
|
||||
@@ -193,3 +201,205 @@ impl<T: Display> From<T> for LoggedError {
|
||||
LoggedError::default()
|
||||
}
|
||||
}
|
||||
|
||||
// Check libc return value and map to Result
|
||||
pub trait LibcReturn
|
||||
where
|
||||
Self: Copy,
|
||||
{
|
||||
type Value;
|
||||
|
||||
fn is_error(&self) -> bool;
|
||||
fn map_val(self) -> Self::Value;
|
||||
|
||||
fn as_os_result<'a>(
|
||||
self,
|
||||
name: &'static str,
|
||||
arg1: Option<&'a str>,
|
||||
arg2: Option<&'a str>,
|
||||
) -> OsResult<'a, Self::Value> {
|
||||
if self.is_error() {
|
||||
Err(OsError::last_os_error(name, arg1, arg2))
|
||||
} else {
|
||||
Ok(self.map_val())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_os_err<'a>(
|
||||
self,
|
||||
name: &'static str,
|
||||
arg1: Option<&'a str>,
|
||||
arg2: Option<&'a str>,
|
||||
) -> OsResult<'a, ()> {
|
||||
if self.is_error() {
|
||||
Err(OsError::last_os_error(name, arg1, arg2))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_io_err(self) -> io::Result<()> {
|
||||
if self.is_error() {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_libc_return {
|
||||
($($t:ty)*) => ($(
|
||||
impl LibcReturn for $t {
|
||||
type Value = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_error(&self) -> bool {
|
||||
*self < 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn map_val(self) -> Self::Value {
|
||||
self
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
impl_libc_return! { i8 i16 i32 i64 isize }
|
||||
|
||||
impl<T> LibcReturn for *mut T {
|
||||
type Value = NonNull<T>;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_error(&self) -> bool {
|
||||
self.is_null()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn map_val(self) -> NonNull<T> {
|
||||
// SAFETY: pointer is null checked by is_error
|
||||
unsafe { NonNull::new_unchecked(self.cast()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum OwnableStr<'a> {
|
||||
None,
|
||||
Borrowed(&'a str),
|
||||
Owned(Box<str>),
|
||||
}
|
||||
|
||||
impl OwnableStr<'_> {
|
||||
fn into_owned(self) -> OwnableStr<'static> {
|
||||
match self {
|
||||
OwnableStr::None => OwnableStr::None,
|
||||
OwnableStr::Borrowed(s) => OwnableStr::Owned(Box::from(s)),
|
||||
OwnableStr::Owned(s) => OwnableStr::Owned(s),
|
||||
}
|
||||
}
|
||||
|
||||
fn ok(&self) -> Option<&str> {
|
||||
match self {
|
||||
OwnableStr::None => None,
|
||||
OwnableStr::Borrowed(s) => Some(*s),
|
||||
OwnableStr::Owned(s) => Some(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Option<&'a str>> for OwnableStr<'a> {
|
||||
fn from(value: Option<&'a str>) -> Self {
|
||||
value.map(OwnableStr::Borrowed).unwrap_or(OwnableStr::None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OsError<'a> {
|
||||
code: i32,
|
||||
name: &'static str,
|
||||
arg1: OwnableStr<'a>,
|
||||
arg2: OwnableStr<'a>,
|
||||
}
|
||||
|
||||
impl OsError<'_> {
|
||||
pub fn with_os_error<'a>(
|
||||
code: i32,
|
||||
name: &'static str,
|
||||
arg1: Option<&'a str>,
|
||||
arg2: Option<&'a str>,
|
||||
) -> OsError<'a> {
|
||||
OsError {
|
||||
code,
|
||||
name,
|
||||
arg1: OwnableStr::from(arg1),
|
||||
arg2: OwnableStr::from(arg2),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_os_error<'a>(
|
||||
name: &'static str,
|
||||
arg1: Option<&'a str>,
|
||||
arg2: Option<&'a str>,
|
||||
) -> OsError<'a> {
|
||||
Self::with_os_error(*errno(), name, arg1, arg2)
|
||||
}
|
||||
|
||||
pub fn set_args<'a>(self, arg1: Option<&'a str>, arg2: Option<&'a str>) -> OsError<'a> {
|
||||
Self::with_os_error(self.code, self.name, arg1, arg2)
|
||||
}
|
||||
|
||||
pub fn into_owned(self) -> OsError<'static> {
|
||||
OsError {
|
||||
code: *errno(),
|
||||
name: self.name,
|
||||
arg1: self.arg1.into_owned(),
|
||||
arg2: self.arg2.into_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_io_error(&self) -> io::Error {
|
||||
io::Error::from_raw_os_error(self.code)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for OsError<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let error = self.as_io_error();
|
||||
if self.name.is_empty() {
|
||||
write!(f, "{:#}", error)
|
||||
} else {
|
||||
match (self.arg1.ok(), self.arg2.ok()) {
|
||||
(Some(arg1), Some(arg2)) => {
|
||||
write!(f, "{} '{}' '{}': {:#}", self.name, arg1, arg2, error)
|
||||
}
|
||||
(Some(arg1), None) => {
|
||||
write!(f, "{} '{}': {:#}", self.name, arg1, error)
|
||||
}
|
||||
_ => {
|
||||
write!(f, "{}: {:#}", self.name, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for OsError<'_> {}
|
||||
|
||||
pub type OsResult<'a, T> = Result<T, OsError<'a>>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum OsErrorStatic {
|
||||
#[error(transparent)]
|
||||
Os(OsError<'static>),
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
}
|
||||
|
||||
// Convert non-static OsError to static
|
||||
impl<'a> From<OsError<'a>> for OsErrorStatic {
|
||||
fn from(value: OsError<'a>) -> Self {
|
||||
OsErrorStatic::Os(value.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
pub type OsResultStatic<T> = Result<T, OsErrorStatic>;
|
||||
|
||||
@@ -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,21 @@ 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);
|
||||
|
||||
@@ -1,509 +1,306 @@
|
||||
// Functions in this file are only for exporting to C++, DO NOT USE IN RUST
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
|
||||
use crate::cxx_extern::readlinkat;
|
||||
use crate::{
|
||||
BorrowedDirectory, CxxResultExt, LibcReturn, Utf8CStr, cstr, slice_from_ptr, slice_from_ptr_mut,
|
||||
};
|
||||
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,
|
||||
};
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::os::fd::FromRawFd;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use crate::cxx_extern::readlinkat_for_cxx;
|
||||
use crate::{errno, raw_cstr, CxxResultExt, FsPath, Utf8CStr, Utf8CStrBufRef};
|
||||
|
||||
fn ptr_to_str<'a, T>(ptr: *const T) -> &'a str {
|
||||
fn ptr_to_str<'a>(ptr: *const c_char) -> Option<&'a str> {
|
||||
if ptr.is_null() {
|
||||
"(null)"
|
||||
None
|
||||
} else {
|
||||
unsafe { CStr::from_ptr(ptr.cast()) }.to_str().unwrap_or("")
|
||||
unsafe { CStr::from_ptr(ptr) }.to_str().ok()
|
||||
}
|
||||
}
|
||||
|
||||
fn error_str() -> &'static str {
|
||||
unsafe { ptr_to_str(libc::strerror(*errno())) }
|
||||
}
|
||||
|
||||
macro_rules! error_cxx {
|
||||
($($args:tt)+) => {
|
||||
($crate::log_with_args($crate::LogLevel::ErrorCxx, format_args_nl!($($args)+)))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! perror {
|
||||
($fmt:expr) => {
|
||||
$crate::log_with_formatter($crate::LogLevel::ErrorCxx, |w| {
|
||||
w.write_str($fmt)?;
|
||||
w.write_fmt(format_args_nl!(" failed with {}: {}", $crate::errno(), error_str()))
|
||||
})
|
||||
};
|
||||
($fmt:expr, $($args:tt)*) => {
|
||||
$crate::log_with_formatter($crate::LogLevel::ErrorCxx, |w| {
|
||||
w.write_fmt(format_args!($fmt, $($args)*))?;
|
||||
w.write_fmt(format_args_nl!(" failed with {}: {}", $crate::errno(), error_str()))
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
mod c_export {
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use crate::{slice_from_ptr, slice_from_ptr_mut};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
||||
super::xwrite(fd, slice_from_ptr(buf, bufsz))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn xxread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize {
|
||||
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(path) => {
|
||||
let mut buf = cstr::buf::wrap_ptr(buf, bufsz);
|
||||
path.realpath(&mut buf)
|
||||
.log_cxx()
|
||||
.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(path) => {
|
||||
let mut buf = cstr::buf::wrap_ptr(buf, bufsz);
|
||||
path.read_link(&mut buf)
|
||||
.log_cxx()
|
||||
.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))
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[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));
|
||||
}
|
||||
fp
|
||||
}
|
||||
|
||||
#[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");
|
||||
}
|
||||
fp
|
||||
}
|
||||
|
||||
#[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));
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[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));
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
// Fully write data slice
|
||||
fn xwrite(fd: RawFd, data: &[u8]) -> isize {
|
||||
unsafe {
|
||||
let mut write_sz: usize = 0;
|
||||
let mut r: ssize_t;
|
||||
let mut remain: &[u8] = data;
|
||||
loop {
|
||||
r = libc::write(fd, remain.as_ptr().cast(), remain.len());
|
||||
if r < 0 {
|
||||
if *errno() == libc::EINTR {
|
||||
continue;
|
||||
}
|
||||
perror!("write");
|
||||
return r;
|
||||
}
|
||||
let r = r as usize;
|
||||
write_sz += r;
|
||||
remain = &remain[r..];
|
||||
if r == 0 || remain.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !remain.is_empty() {
|
||||
error_cxx!("write ({} != {})", write_sz, data.len())
|
||||
}
|
||||
write_sz as isize
|
||||
readlinkat(dirfd, path, buf, bufsz)
|
||||
.as_os_result("readlinkat", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfopen(path: *const c_char, mode: *const c_char) -> *mut libc::FILE {
|
||||
unsafe {
|
||||
libc::fopen(path, mode)
|
||||
.as_os_result("fopen", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.map_or(ptr::null_mut(), NonNull::as_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xfdopen(fd: RawFd, mode: *const c_char) -> *mut libc::FILE {
|
||||
unsafe {
|
||||
libc::fdopen(fd, mode)
|
||||
.as_os_result("fdopen", None, None)
|
||||
.log_cxx()
|
||||
.map_or(ptr::null_mut(), NonNull::as_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xopen(path: *const c_char, flags: i32, mode: mode_t) -> RawFd {
|
||||
unsafe {
|
||||
libc::open(path, flags, mode as c_uint)
|
||||
.as_os_result("open", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xopenat(dirfd: RawFd, path: *const c_char, flags: i32, mode: mode_t) -> RawFd {
|
||||
unsafe {
|
||||
libc::openat(dirfd, path, flags, mode as c_uint)
|
||||
.as_os_result("openat", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
||||
let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
|
||||
let data = unsafe { slice_from_ptr(buf, bufsz) };
|
||||
file.write_all(data)
|
||||
.log_cxx()
|
||||
.map_or(-1, |_| data.len() as isize)
|
||||
}
|
||||
|
||||
#[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);
|
||||
if r < 0 {
|
||||
perror!("read");
|
||||
}
|
||||
r
|
||||
libc::read(fd, buf, bufsz)
|
||||
.as_os_result("read", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
// Fully read size of data slice
|
||||
fn xxread(fd: RawFd, data: &mut [u8]) -> isize {
|
||||
unsafe {
|
||||
let mut read_sz: usize = 0;
|
||||
let mut r: ssize_t;
|
||||
let mut remain: &mut [u8] = data;
|
||||
loop {
|
||||
r = libc::read(fd, remain.as_mut_ptr().cast(), remain.len());
|
||||
if r < 0 {
|
||||
if *errno() == libc::EINTR {
|
||||
continue;
|
||||
}
|
||||
perror!("read");
|
||||
return r;
|
||||
}
|
||||
let r = r as usize;
|
||||
read_sz += r;
|
||||
remain = &mut remain[r..];
|
||||
if r == 0 || remain.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !remain.is_empty() {
|
||||
error_cxx!("read ({} != {})", read_sz, data.len())
|
||||
}
|
||||
read_sz as 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
|
||||
}
|
||||
#[unsafe(no_mangle)]
|
||||
unsafe extern "C" fn xxread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize {
|
||||
let mut file = unsafe { ManuallyDrop::new(File::from_raw_fd(fd)) };
|
||||
let data = unsafe { slice_from_ptr_mut(buf, bufsz) };
|
||||
file.read_exact(data)
|
||||
.log_cxx()
|
||||
.map_or(-1, |_| data.len() as isize)
|
||||
}
|
||||
|
||||
pub(crate) fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::pipe2(fds.as_mut_ptr(), flags);
|
||||
if r < 0 {
|
||||
perror!("pipe2");
|
||||
}
|
||||
r
|
||||
libc::pipe2(fds.as_mut_ptr(), flags)
|
||||
.as_os_result("pipe2", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsetns(fd: RawFd, nstype: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::setns(fd, nstype);
|
||||
if r < 0 {
|
||||
perror!("setns");
|
||||
}
|
||||
r
|
||||
libc::setns(fd, nstype)
|
||||
.as_os_result("setns", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xunshare(flags: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::unshare(flags);
|
||||
if r < 0 {
|
||||
perror!("unshare");
|
||||
}
|
||||
r
|
||||
libc::unshare(flags)
|
||||
.as_os_result("unshare", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
libc::opendir(path)
|
||||
.as_os_result("opendir", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.map_or(ptr::null_mut(), NonNull::as_ptr)
|
||||
}
|
||||
dp
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR {
|
||||
unsafe {
|
||||
let dp = libc::fdopendir(fd);
|
||||
if dp.is_null() {
|
||||
perror!("fdopendir");
|
||||
}
|
||||
dp
|
||||
libc::fdopendir(fd)
|
||||
.as_os_result("fdopendir", None, None)
|
||||
.log_cxx()
|
||||
.map_or(ptr::null_mut(), NonNull::as_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[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(no_mangle)]
|
||||
unsafe extern "C" fn xreaddir(mut dir: BorrowedDirectory) -> *mut libc::dirent {
|
||||
dir.read()
|
||||
.log_cxx()
|
||||
.ok()
|
||||
.flatten()
|
||||
.map_or(ptr::null_mut(), |entry| entry.as_ptr())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsetsid() -> i32 {
|
||||
unsafe {
|
||||
let r = libc::setsid();
|
||||
if r < 0 {
|
||||
perror!("setsid");
|
||||
}
|
||||
r
|
||||
libc::setsid()
|
||||
.as_os_result("setsid", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xsocket(domain: i32, ty: i32, protocol: i32) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::socket(domain, ty, protocol);
|
||||
if fd < 0 {
|
||||
perror!("socket");
|
||||
}
|
||||
fd
|
||||
libc::socket(domain, ty, protocol)
|
||||
.as_os_result("socket", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
libc::bind(socket, address, len)
|
||||
.as_os_result("bind", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xlisten(socket: i32, backlog: i32) -> i32 {
|
||||
unsafe {
|
||||
let r = libc::listen(socket, backlog);
|
||||
if r < 0 {
|
||||
perror!("listen");
|
||||
}
|
||||
r
|
||||
libc::listen(socket, backlog)
|
||||
.as_os_result("listen", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
libc::accept4(sockfd, addr, len, flg)
|
||||
.as_os_result("accept4", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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 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));
|
||||
}
|
||||
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 {
|
||||
libc::stat(path, buf)
|
||||
.as_os_result("stat", ptr_to_str(path), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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 {
|
||||
libc::fstat(fd, buf)
|
||||
.as_os_result("fstat", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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);
|
||||
if fd < 0 {
|
||||
perror!("dup");
|
||||
}
|
||||
fd
|
||||
libc::dup(oldfd)
|
||||
.as_os_result("dup", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
|
||||
unsafe {
|
||||
let fd = libc::dup2(oldfd, newfd);
|
||||
if fd < 0 {
|
||||
perror!("dup2");
|
||||
}
|
||||
fd
|
||||
libc::dup2(oldfd, newfd)
|
||||
.as_os_result("dup2", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
libc::symlink(target, linkpath)
|
||||
.as_os_result("symlink", ptr_to_str(target), ptr_to_str(linkpath))
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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,125 +308,95 @@ 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 {
|
||||
libc::mount(src, target, fstype, flags, data)
|
||||
.as_os_result("mount", ptr_to_str(src), ptr_to_str(target))
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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 {
|
||||
libc::umount2(target, flags)
|
||||
.as_os_result("umount2", ptr_to_str(target), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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 {
|
||||
libc::rename(oldname, newname)
|
||||
.as_os_result("rename", ptr_to_str(oldname), ptr_to_str(newname))
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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 {
|
||||
match Utf8CStr::from_ptr(path) {
|
||||
Ok(path) => path.mkdir(mode).log_cxx().map_or(-1, |_| 0),
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
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(path) => path.mkdirs(mode).log_cxx().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 {
|
||||
libc::sendfile(out_fd, in_fd, offset, count)
|
||||
.as_os_result("sendfile", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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();
|
||||
if r < 0 {
|
||||
perror!("fork");
|
||||
}
|
||||
r
|
||||
libc::fork()
|
||||
.as_os_result("fork", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
libc::poll(fds, nfds, timeout)
|
||||
.as_os_result("poll", None, None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
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 {
|
||||
libc::mknod(pathname, mode, dev)
|
||||
.as_os_result("mknod", ptr_to_str(pathname), None)
|
||||
.log_cxx()
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "magiskboot"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
@@ -13,7 +13,7 @@ pb-rs = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
base = { path = "../base" }
|
||||
cxx = { path = "../external/cxx-rs" }
|
||||
cxx = { workspace = true }
|
||||
byteorder = { workspace = true }
|
||||
size = { workspace = true }
|
||||
quick-protobuf = { workspace = true }
|
||||
@@ -30,3 +30,9 @@ 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 }
|
||||
|
||||
# Pin version to prevent cargo update break builds
|
||||
block-buffer = { workspace = true }
|
||||
sec1 = { 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 pb_rs::{ConfigBuilder, types::FileDescriptor};
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -10,18 +10,18 @@ use std::process::exit;
|
||||
use std::str;
|
||||
|
||||
use argh::FromArgs;
|
||||
use bytemuck::{from_bytes, Pod, Zeroable};
|
||||
use bytemuck::{Pod, Zeroable, from_bytes};
|
||||
use num_traits::cast::AsPrimitive;
|
||||
use size::{Base, Size, Style};
|
||||
|
||||
use base::libc::{
|
||||
c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t, O_CLOEXEC, O_CREAT,
|
||||
O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRGRP,
|
||||
S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
||||
O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT,
|
||||
S_IFREG, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
|
||||
c_char, dev_t, gid_t, major, makedev, minor, mknod, mode_t, uid_t,
|
||||
};
|
||||
use base::{
|
||||
log_err, map_args, BytesExt, EarlyExitExt, FsPath, LoggedResult, MappedFile, ResultExt,
|
||||
Utf8CStr, Utf8CStrBufArr, Utf8CStrWrite, WriteExt,
|
||||
BytesExt, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, Utf8CStrBuf, WriteExt,
|
||||
cstr, log_err, map_args,
|
||||
};
|
||||
|
||||
use crate::check_env;
|
||||
@@ -342,13 +342,13 @@ impl Cpio {
|
||||
eprintln!("Extracting entry [{}] to [{}]", path, out);
|
||||
|
||||
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) {
|
||||
FsPath::from(&buf).mkdirs(0o755)?;
|
||||
if let Some(dir) = out.parent_dir() {
|
||||
buf.push_str(dir);
|
||||
buf.mkdirs(0o755)?;
|
||||
}
|
||||
|
||||
let mode: mode_t = (entry.mode & 0o777).into();
|
||||
@@ -362,7 +362,7 @@ impl Cpio {
|
||||
S_IFLNK => {
|
||||
buf.clear();
|
||||
buf.push_str(str::from_utf8(entry.data.as_slice())?);
|
||||
FsPath::from(&buf).symlink_to(out)?;
|
||||
out.create_symlink_to(&buf)?;
|
||||
}
|
||||
S_IFBLK | S_IFCHR => {
|
||||
let dev = makedev(entry.rdevmajor.try_into()?, entry.rdevminor.try_into()?);
|
||||
@@ -399,7 +399,6 @@ impl Cpio {
|
||||
return Err(log_err!("path cannot end with / for add"));
|
||||
}
|
||||
let file = Utf8CStr::from_string(file);
|
||||
let file = FsPath::from(&file);
|
||||
let attr = file.get_attr()?;
|
||||
|
||||
let mut content = Vec::<u8>::new();
|
||||
@@ -413,8 +412,8 @@ impl Cpio {
|
||||
file.open(O_RDONLY | O_CLOEXEC)?.read_to_end(&mut content)?;
|
||||
mode | S_IFREG
|
||||
} else {
|
||||
rdevmajor = unsafe { major(attr.st.st_rdev.as_()) }.as_();
|
||||
rdevminor = unsafe { minor(attr.st.st_rdev.as_()) }.as_();
|
||||
rdevmajor = major(attr.st.st_rdev.as_()).as_();
|
||||
rdevminor = minor(attr.st.st_rdev.as_()).as_();
|
||||
if attr.is_block_device() {
|
||||
mode | S_IFBLK
|
||||
} else if attr.is_char_device() {
|
||||
@@ -746,8 +745,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 +753,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)?;
|
||||
@@ -766,7 +764,7 @@ pub fn cpio_commands(argc: i32, argv: *const *const c_char) -> bool {
|
||||
CpioCli::from_args(&["magiskboot", "cpio"], &cmds).on_early_exit(print_cpio_usage);
|
||||
|
||||
let file = Utf8CStr::from_string(&mut cli.file);
|
||||
let mut cpio = if FsPath::from(file).exists() {
|
||||
let mut cpio = if file.exists() {
|
||||
Cpio::load_from_file(file)?
|
||||
} else {
|
||||
Cpio::new()
|
||||
@@ -807,7 +805,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 +817,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()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ use std::{cell::UnsafeCell, process::exit};
|
||||
|
||||
use argh::FromArgs;
|
||||
use fdt::{
|
||||
Fdt, FdtError,
|
||||
node::{FdtNode, NodeProperty},
|
||||
Fdt,
|
||||
};
|
||||
|
||||
use base::{
|
||||
libc::c_char, log_err, map_args, EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr,
|
||||
EarlyExitExt, LoggedResult, MappedFile, ResultExt, Utf8CStr, libc::c_char, log_err, map_args,
|
||||
};
|
||||
|
||||
use crate::{check_env, patch::patch_verity};
|
||||
@@ -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,13 +1,16 @@
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(btree_extract_if)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(try_blocks)]
|
||||
|
||||
pub use base;
|
||||
use cpio::cpio_commands;
|
||||
use dtb::dtb_commands;
|
||||
pub use libbz2_rs_sys::*;
|
||||
pub use libz_rs_sys::*;
|
||||
use patch::hexpatch;
|
||||
use payload::extract_boot_from_payload;
|
||||
use sign::{get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image, SHA};
|
||||
use sign::{SHA, get_sha, sha1_hash, sha256_hash, sign_boot_image, verify_boot_image};
|
||||
use std::env;
|
||||
|
||||
mod cpio;
|
||||
@@ -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::{DeltaArchiveManifest, mod_InstallOperation::Type},
|
||||
};
|
||||
use base::{
|
||||
LoggedError, LoggedResult, ReadSeekExt, ResultExt, Utf8CStr, WriteExt, error, ffi::Utf8CStrRef,
|
||||
};
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -15,18 +15,18 @@ use rsa::pkcs1v15::{
|
||||
Signature as RsaSignature, SigningKey as RsaSigningKey, VerifyingKey as RsaVerifyingKey,
|
||||
};
|
||||
use rsa::pkcs8::SubjectPublicKeyInfoRef;
|
||||
use rsa::signature::hazmat::{PrehashSigner, PrehashVerifier};
|
||||
use rsa::signature::SignatureEncoding;
|
||||
use rsa::signature::hazmat::{PrehashSigner, PrehashVerifier};
|
||||
use rsa::{RsaPrivateKey, RsaPublicKey};
|
||||
use sha1::Sha1;
|
||||
use sha2::{Sha256, Sha384, Sha512};
|
||||
use x509_cert::der::asn1::{OctetString, PrintableString};
|
||||
use x509_cert::der::Any;
|
||||
use x509_cert::spki::AlgorithmIdentifier;
|
||||
use x509_cert::Certificate;
|
||||
use x509_cert::der::Any;
|
||||
use x509_cert::der::asn1::{OctetString, PrintableString};
|
||||
use x509_cert::spki::AlgorithmIdentifier;
|
||||
|
||||
use base::libc::c_char;
|
||||
use base::{log_err, LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr};
|
||||
use base::{LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr, log_err};
|
||||
|
||||
use crate::ffi::BootImage;
|
||||
|
||||
@@ -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,7 +1,6 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <base.hpp>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <base.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -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 pb_rs::{ConfigBuilder, types::FileDescriptor};
|
||||
|
||||
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,11 +2,12 @@
|
||||
#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>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <flags.h>
|
||||
|
||||
using namespace std;
|
||||
@@ -127,30 +128,57 @@ static void poll_ctrl_handler(pollfd *pfd) {
|
||||
}
|
||||
}
|
||||
|
||||
void MagiskD::reboot() const noexcept {
|
||||
if (is_recovery())
|
||||
exec_command_sync("/system/bin/reboot", "recovery");
|
||||
else
|
||||
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 +186,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,24 +313,11 @@ 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void switch_cgroup(const char *cgroup, int pid) {
|
||||
char buf[32];
|
||||
ssprintf(buf, sizeof(buf), "%s/cgroup.procs", cgroup);
|
||||
if (access(buf, F_OK) != 0)
|
||||
return;
|
||||
int fd = xopen(buf, O_WRONLY | O_APPEND | O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return;
|
||||
ssprintf(buf, sizeof(buf), "%d\n", pid);
|
||||
xwrite(fd, buf, strlen(buf));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void daemon_entry() {
|
||||
android_logging();
|
||||
|
||||
@@ -324,51 +339,15 @@ static void daemon_entry() {
|
||||
if (fd > STDERR_FILENO)
|
||||
close(fd);
|
||||
|
||||
setsid();
|
||||
setcon(MAGISK_PROC_CON);
|
||||
|
||||
rust::daemon_entry();
|
||||
SDK_INT = MagiskD().sdk_int();
|
||||
|
||||
// Escape from cgroup
|
||||
int pid = getpid();
|
||||
switch_cgroup("/acct", pid);
|
||||
switch_cgroup("/dev/cg2_bpf", pid);
|
||||
switch_cgroup("/sys/fs/cgroup", pid);
|
||||
if (get_prop("ro.config.per_app_memcg") != "false") {
|
||||
switch_cgroup("/dev/memcg/apps", pid);
|
||||
}
|
||||
SDK_INT = MagiskD::Get().sdk_int();
|
||||
|
||||
// Get self stat
|
||||
xstat("/proc/self/exe", &self_st);
|
||||
|
||||
// Samsung workaround #7887
|
||||
if (access("/system_ext/app/mediatek-res/mediatek-res.apk", F_OK) == 0) {
|
||||
set_prop("ro.vendor.mtk_model", "0");
|
||||
}
|
||||
|
||||
restore_tmpcon();
|
||||
|
||||
// Cleanups
|
||||
const char *tmp = get_magisk_tmp();
|
||||
char path[64];
|
||||
ssprintf(path, sizeof(path), "%s/" ROOTMNT, tmp);
|
||||
if (access(path, F_OK) == 0) {
|
||||
file_readline(true, path, [](string_view line) -> bool {
|
||||
umount2(line.data(), MNT_DETACH);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (getenv("REMOUNT_ROOT")) {
|
||||
xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr);
|
||||
unsetenv("REMOUNT_ROOT");
|
||||
}
|
||||
ssprintf(path, sizeof(path), "%s/" ROOTOVL, tmp);
|
||||
rm_rf(path);
|
||||
|
||||
fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
sockaddr_un addr = {.sun_family = AF_LOCAL};
|
||||
ssprintf(addr.sun_path, sizeof(addr.sun_path), "%s/" MAIN_SOCKET, tmp);
|
||||
ssprintf(addr.sun_path, sizeof(addr.sun_path), "%s/" MAIN_SOCKET, get_magisk_tmp());
|
||||
unlink(addr.sun_path);
|
||||
if (xbind(fd, (sockaddr *) &addr, sizeof(addr)))
|
||||
exit(1);
|
||||
@@ -378,7 +357,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 +433,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,24 @@
|
||||
use crate::consts::{MAGISK_FULL_VER, MAIN_CONFIG};
|
||||
use crate::consts::{MAGISK_FULL_VER, MAGISK_PROC_CON, MAIN_CONFIG, ROOTMNT, ROOTOVL, SECURE_DIR};
|
||||
use crate::db::Sqlite3;
|
||||
use crate::ffi::{get_magisk_tmp, RequestCode};
|
||||
use crate::get_prop;
|
||||
use crate::logging::{magisk_logging, start_log_daemon};
|
||||
use crate::package::ManagerInfo;
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use base::{
|
||||
cstr, info, libc, open_fd, BufReadExt, Directory, FsPath, FsPathBuf, LoggedResult, ReadExt,
|
||||
Utf8CStr, Utf8CStrBufArr,
|
||||
use crate::ffi::{
|
||||
DbEntryKey, ModuleInfo, RequestCode, check_key_combo, disable_modules, exec_common_scripts,
|
||||
exec_module_scripts, get_magisk_tmp, initialize_denylist, setup_magisk_env,
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
use bytemuck::bytes_of;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{BufReader, Read, Write};
|
||||
use crate::logging::{magisk_logging, setup_logfile, start_log_daemon};
|
||||
use crate::mount::{clean_mounts, setup_module_mount, setup_preinit_dir};
|
||||
use crate::package::ManagerInfo;
|
||||
use crate::selinux::restore_tmpcon;
|
||||
use crate::su::SuInfo;
|
||||
use crate::{get_prop, set_prop};
|
||||
use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY};
|
||||
use base::{
|
||||
AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, error, info, libc,
|
||||
};
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::io::{BufReader, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
// Global magiskd singleton
|
||||
@@ -60,14 +65,23 @@ 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 is_recovery(&self) -> bool {
|
||||
self.is_recovery
|
||||
pub fn get() -> &'static MagiskD {
|
||||
unsafe { MAGISKD.get().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
pub fn zygisk_enabled(&self) -> bool {
|
||||
self.zygisk_enabled.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn sdk_int(&self) -> i32 {
|
||||
@@ -82,39 +96,88 @@ 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 = cstr!(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_module_mount();
|
||||
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 = cstr!(SECURE_DIR);
|
||||
if !secure_dir.exists() {
|
||||
secure_dir.mkdir(0o700).log_ok();
|
||||
}
|
||||
|
||||
setup_preinit_dir();
|
||||
self.ensure_manager();
|
||||
self.zygisk_reset(true)
|
||||
}
|
||||
|
||||
pub fn boot_stage_handler(&self, client: i32, code: i32) {
|
||||
@@ -152,9 +215,26 @@ impl MagiskD {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reboot(&self) {
|
||||
if self.is_recovery {
|
||||
Command::new("/system/bin/reboot").arg("recovery").status()
|
||||
} else {
|
||||
Command::new("/system/bin/reboot").status()
|
||||
}
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn daemon_entry() {
|
||||
unsafe { libc::setsid() };
|
||||
|
||||
// Make sure the current context is magisk
|
||||
if let Ok(mut current) = cstr!("/proc/self/attr/current").open(O_WRONLY | O_CLOEXEC) {
|
||||
let con = cstr!(MAGISK_PROC_CON);
|
||||
current.write_all(con.as_bytes_with_nul()).log_ok();
|
||||
}
|
||||
|
||||
start_log_daemon();
|
||||
magisk_logging();
|
||||
info!("Magisk {} daemon started", MAGISK_FULL_VER);
|
||||
@@ -164,14 +244,13 @@ 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)
|
||||
.join(get_magisk_tmp())
|
||||
.join(MAIN_CONFIG);
|
||||
let magisk_tmp = get_magisk_tmp();
|
||||
let mut tmp_path = cstr::buf::new::<64>()
|
||||
.join_path(magisk_tmp)
|
||||
.join_path(MAIN_CONFIG);
|
||||
let mut is_recovery = false;
|
||||
if let Ok(file) = path.open(O_RDONLY | O_CLOEXEC) {
|
||||
let mut file = BufReader::new(file);
|
||||
file.foreach_props(|key, val| {
|
||||
if let Ok(main_config) = tmp_path.open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(main_config).foreach_props(|key, val| {
|
||||
if key == "RECOVERYMODE" {
|
||||
is_recovery = val == "true";
|
||||
return false;
|
||||
@@ -179,11 +258,11 @@ pub fn daemon_entry() {
|
||||
true
|
||||
});
|
||||
}
|
||||
tmp_path.truncate(magisk_tmp.len());
|
||||
|
||||
let mut sdk_int = -1;
|
||||
if let Ok(file) = FsPath::from(cstr!("/system/build.prop")).open(O_RDONLY | O_CLOEXEC) {
|
||||
let mut file = BufReader::new(file);
|
||||
file.foreach_props(|key, val| {
|
||||
if let Ok(build_prop) = cstr!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(build_prop).foreach_props(|key, val| {
|
||||
if key == "ro.build.version.sdk" {
|
||||
sdk_int = val.parse::<i32>().unwrap_or(-1);
|
||||
return false;
|
||||
@@ -199,18 +278,71 @@ pub fn daemon_entry() {
|
||||
}
|
||||
info!("* Device API level: {}", sdk_int);
|
||||
|
||||
restore_tmpcon().log_ok();
|
||||
|
||||
// Escape from cgroup
|
||||
let pid = unsafe { libc::getpid() };
|
||||
switch_cgroup("/acct", pid);
|
||||
switch_cgroup("/dev/cg2_bpf", pid);
|
||||
switch_cgroup("/sys/fs/cgroup", pid);
|
||||
if get_prop(cstr!("ro.config.per_app_memcg"), false) != "false" {
|
||||
switch_cgroup("/dev/memcg/apps", pid);
|
||||
}
|
||||
|
||||
// Samsung workaround #7887
|
||||
if cstr!("/system_ext/app/mediatek-res/mediatek-res.apk").exists() {
|
||||
set_prop(cstr!("ro.vendor.mtk_model"), cstr!("0"), false);
|
||||
}
|
||||
|
||||
// Cleanup pre-init mounts
|
||||
tmp_path.append_path(ROOTMNT);
|
||||
if let Ok(mount_list) = tmp_path.open(O_RDONLY | O_CLOEXEC) {
|
||||
BufReader::new(mount_list).foreach_lines(|line| {
|
||||
line.truncate(line.trim_end().len());
|
||||
let item = Utf8CStr::from_string(line);
|
||||
item.unmount().log_ok();
|
||||
true
|
||||
})
|
||||
}
|
||||
tmp_path.truncate(magisk_tmp.len());
|
||||
|
||||
// Remount rootfs as read-only if requested
|
||||
if std::env::var_os("REMOUNT_ROOT").is_some() {
|
||||
cstr!("/").remount_mount_flags(libc::MS_RDONLY).log_ok();
|
||||
unsafe { std::env::remove_var("REMOUNT_ROOT") };
|
||||
}
|
||||
|
||||
// Remove all pre-init overlay files to free-up memory
|
||||
tmp_path.append_path(ROOTOVL);
|
||||
tmp_path.remove_all().log_ok();
|
||||
tmp_path.truncate(magisk_tmp.len());
|
||||
|
||||
let magiskd = MagiskD {
|
||||
sdk_int,
|
||||
is_emulator,
|
||||
is_recovery,
|
||||
zygote_start_count: AtomicU32::new(1),
|
||||
..Default::default()
|
||||
};
|
||||
MAGISKD.set(magiskd).ok();
|
||||
}
|
||||
|
||||
fn switch_cgroup(cgroup: &str, pid: i32) {
|
||||
let mut buf = cstr::buf::new::<64>()
|
||||
.join_path(cgroup)
|
||||
.join_path("cgroup.procs");
|
||||
if !buf.exists() {
|
||||
return;
|
||||
}
|
||||
if let Ok(mut file) = buf.open(O_WRONLY | O_APPEND | O_CLOEXEC) {
|
||||
buf.clear();
|
||||
buf.write_fmt(format_args!("{}", pid)).ok();
|
||||
file.write_all(buf.as_bytes()).log_ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_data() -> bool {
|
||||
if let Ok(fd) = open_fd!(cstr!("/proc/mounts"), O_RDONLY | O_CLOEXEC) {
|
||||
let file = File::from(fd);
|
||||
if let Ok(file) = cstr!("/proc/mounts").open(O_RDONLY | O_CLOEXEC) {
|
||||
let mut mnt = false;
|
||||
BufReader::new(file).foreach_lines(|line| {
|
||||
if line.contains(" /data ") && !line.contains("tmpfs") {
|
||||
@@ -238,43 +370,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())
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user