mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-11 22:32:20 +00:00
Compare commits
200 Commits
v28.0
...
canary-281
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e3edb8883 | ||
|
|
3b8b61bf35 | ||
|
|
6f90456036 | ||
|
|
f56fd4e215 | ||
|
|
aa35aac5d5 | ||
|
|
1f162b819d | ||
|
|
52ef1d1cb2 | ||
|
|
f14e3a89cc | ||
|
|
95d3eac2e0 | ||
|
|
8e73536e02 | ||
|
|
12a0870bc9 | ||
|
|
6ff82c4e86 | ||
|
|
c64de35375 | ||
|
|
ee5283f4e8 | ||
|
|
bd0e954fea | ||
|
|
675471a49e | ||
|
|
c90e73ccec | ||
|
|
a43c1267d8 | ||
|
|
e8958c6b5c | ||
|
|
e8a3bf82c6 | ||
|
|
27fd79176a | ||
|
|
28d86a3454 | ||
|
|
c6c1a17ae6 | ||
|
|
2b47d47215 | ||
|
|
0e82df9e10 | ||
|
|
893821ad88 | ||
|
|
6b80fbfa99 | ||
|
|
8c3c7d0194 | ||
|
|
b94a3d9f2f | ||
|
|
442d0b5ddc | ||
|
|
494615d9a0 | ||
|
|
afbfb81837 | ||
|
|
3ed4e258a3 | ||
|
|
dddd41c95b | ||
|
|
5f2ca81e86 | ||
|
|
c9eac0c438 | ||
|
|
b6b34f7612 | ||
|
|
e55c413261 | ||
|
|
0399cde50a | ||
|
|
019eb03823 | ||
|
|
363410e1c0 | ||
|
|
fc2ef21660 | ||
|
|
18cb659ff3 | ||
|
|
63231d97ce | ||
|
|
9ac81a8a25 | ||
|
|
79af2787ae | ||
|
|
f5f9b285c0 | ||
|
|
6c05f2ae85 | ||
|
|
29043e1684 | ||
|
|
b73d4a7022 | ||
|
|
ad95e8951b | ||
|
|
bf591fca12 | ||
|
|
dcf027884d | ||
|
|
584f3820fe | ||
|
|
3c7c46307a | ||
|
|
4d80361805 | ||
|
|
9a74e19117 | ||
|
|
b1e17706a4 | ||
|
|
caad129d69 | ||
|
|
da58571ce5 | ||
|
|
2aa7f1c094 | ||
|
|
823e31a91b | ||
|
|
fb926ae302 | ||
|
|
e0489eeffd | ||
|
|
dc9d5a4cac | ||
|
|
143743d0b0 | ||
|
|
563f0d5ad5 | ||
|
|
c99f4a591b | ||
|
|
449204e380 | ||
|
|
a85c4c6528 | ||
|
|
d203a6fff6 | ||
|
|
6c612d66d7 | ||
|
|
540253a55b | ||
|
|
15b7c4ccd1 | ||
|
|
442d5335ea | ||
|
|
8a80eea597 | ||
|
|
5e35703091 | ||
|
|
b7ca73f431 | ||
|
|
a14fc90f07 | ||
|
|
c913f7ec74 | ||
|
|
7f6c9e8411 | ||
|
|
bb02ea3a20 | ||
|
|
3981c9665e | ||
|
|
88628fdf3c | ||
|
|
0469817781 | ||
|
|
a786801141 | ||
|
|
ab86732c89 | ||
|
|
59622d1688 | ||
|
|
58a25a3e2b | ||
|
|
15dca29a87 | ||
|
|
46980819c0 | ||
|
|
4fb6a7268c | ||
|
|
c05e963f37 | ||
|
|
7f7f625864 | ||
|
|
b25aa8295a | ||
|
|
15a605765c | ||
|
|
b575c95710 | ||
|
|
a48a9c858a | ||
|
|
0d8d6290a3 | ||
|
|
4dcd733ddd | ||
|
|
b62835cbeb | ||
|
|
6ea740b5ab | ||
|
|
7ab98dd5ac | ||
|
|
fc8b3400fc | ||
|
|
54428ba415 | ||
|
|
95d1e69d8e | ||
|
|
a0f13ab49f | ||
|
|
c3e8405020 | ||
|
|
a93593ea66 | ||
|
|
23eff70883 | ||
|
|
110dd4a8b9 | ||
|
|
d9c2bffc9f | ||
|
|
049db49dc8 | ||
|
|
7c1d2ec61e | ||
|
|
a1b2830c06 | ||
|
|
82d1d19267 | ||
|
|
4d4195c02d | ||
|
|
5637a258fc | ||
|
|
ee6810f417 | ||
|
|
7098248c64 | ||
|
|
0d31d356ef | ||
|
|
b782e7dcb7 | ||
|
|
a4671b4698 | ||
|
|
7edd8be169 | ||
|
|
24650eefe4 | ||
|
|
8e1a44e7eb | ||
|
|
2722875190 | ||
|
|
3ca6d06f69 | ||
|
|
10e47248de | ||
|
|
e73ff679ac | ||
|
|
53e401fa2d | ||
|
|
d2768357da | ||
|
|
a6c2ba7c1e | ||
|
|
aae5b466fb | ||
|
|
2b7be8b949 | ||
|
|
b6511a510d | ||
|
|
704541aef2 | ||
|
|
005560a4c5 | ||
|
|
231a5d1853 | ||
|
|
9e2b59060d | ||
|
|
08ea937f7c | ||
|
|
2baedf74d1 | ||
|
|
32faa4ced6 | ||
|
|
ccdb0b5d13 | ||
|
|
8506b672ad | ||
|
|
ce2e33bb20 | ||
|
|
6707b72260 | ||
|
|
5885b8c20d | ||
|
|
820710c086 | ||
|
|
51cf196bf7 | ||
|
|
dadba44cf9 | ||
|
|
2ce4a5543b | ||
|
|
9112a3a4f5 | ||
|
|
24615afda1 | ||
|
|
c5778f398b | ||
|
|
4eb4097b9b | ||
|
|
c512496847 | ||
|
|
506961a10d | ||
|
|
3414415907 | ||
|
|
dc2ae7cfd1 | ||
|
|
2e86d21c29 | ||
|
|
2654382c43 | ||
|
|
9e26b73813 | ||
|
|
10cd13bf80 | ||
|
|
f10ee5f887 | ||
|
|
47cc532d96 | ||
|
|
218327f92b | ||
|
|
4eae66a1a7 | ||
|
|
b09ceeb43c | ||
|
|
4fb539c110 | ||
|
|
849b284da5 | ||
|
|
895b5f6cbf | ||
|
|
cb3d4ea514 | ||
|
|
0d89a2a97d | ||
|
|
3ca5913055 | ||
|
|
df6b808f49 | ||
|
|
09c7ac754b | ||
|
|
805da67c23 | ||
|
|
3c6889505b | ||
|
|
c8e9ce7627 | ||
|
|
837c679a31 | ||
|
|
06616659b8 | ||
|
|
a34c04f999 | ||
|
|
da43ac89a0 | ||
|
|
830fc758b9 | ||
|
|
0f3cfef278 | ||
|
|
b32d7bfafd | ||
|
|
c0899f2939 | ||
|
|
082330808f | ||
|
|
024da05888 | ||
|
|
377b6d0cc2 | ||
|
|
c661009b31 | ||
|
|
613f2d31c5 | ||
|
|
7dbb973db5 | ||
|
|
f4502f8be8 | ||
|
|
455b13b83c | ||
|
|
8b98709743 | ||
|
|
1b12f45f39 | ||
|
|
a5cad532ff | ||
|
|
070719db50 |
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -12,13 +12,10 @@
|
|||||||
|
|
||||||
# Denote all files that are truly binary and should not be modified.
|
# Denote all files that are truly binary and should not be modified.
|
||||||
tools/** binary
|
tools/** binary
|
||||||
|
tools/rustup_wrapper/** -binary
|
||||||
*.jar binary
|
*.jar binary
|
||||||
*.exe binary
|
*.exe binary
|
||||||
*.apk binary
|
*.apk binary
|
||||||
*.png binary
|
*.png binary
|
||||||
*.jpg binary
|
*.jpg binary
|
||||||
*.ttf binary
|
*.ttf binary
|
||||||
|
|
||||||
# Help GitHub detect languages
|
|
||||||
native/jni/external/** linguist-vendored
|
|
||||||
native/jni/systemproperties/** linguist-language=C++
|
|
||||||
|
|||||||
25
.github/actions/setup/action.yml
vendored
25
.github/actions/setup/action.yml
vendored
@@ -6,7 +6,7 @@ inputs:
|
|||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
@@ -26,6 +26,15 @@ runs:
|
|||||||
|
|
||||||
- name: Cache sccache
|
- name: Cache sccache
|
||||||
uses: actions/cache@v4
|
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:
|
with:
|
||||||
path: .sccache
|
path: .sccache
|
||||||
key: sccache-${{ runner.os }}-${{ github.sha }}
|
key: sccache-${{ runner.os }}-${{ github.sha }}
|
||||||
@@ -55,7 +64,7 @@ runs:
|
|||||||
|
|
||||||
- name: Cache Gradle dependencies
|
- name: Cache Gradle dependencies
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
if: inputs.is-asset-build == 'true'
|
if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }}
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
.gradle/caches
|
.gradle/caches
|
||||||
@@ -66,7 +75,7 @@ runs:
|
|||||||
|
|
||||||
- name: Restore Gradle dependencies
|
- name: Restore Gradle dependencies
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
if: inputs.is-asset-build == 'false'
|
if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }}
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
.gradle/caches
|
.gradle/caches
|
||||||
@@ -78,19 +87,17 @@ runs:
|
|||||||
|
|
||||||
- name: Cache Gradle build cache
|
- name: Cache Gradle build cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
if: inputs.is-asset-build == 'true'
|
if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }}
|
||||||
with:
|
with:
|
||||||
path: |
|
path: .gradle/caches/build-cache-*
|
||||||
.gradle/caches/build-cache-*
|
|
||||||
key: gradle-build-cache-${{ github.sha }}
|
key: gradle-build-cache-${{ github.sha }}
|
||||||
restore-keys: gradle-build-cache-
|
restore-keys: gradle-build-cache-
|
||||||
|
|
||||||
- name: Restore Gradle build cache
|
- name: Restore Gradle build cache
|
||||||
uses: actions/cache/restore@v4
|
uses: actions/cache/restore@v4
|
||||||
if: inputs.is-asset-build == 'false'
|
if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }}
|
||||||
with:
|
with:
|
||||||
path: |
|
path: .gradle/caches/build-cache-*
|
||||||
.gradle/caches/build-cache-*
|
|
||||||
key: gradle-build-cache-${{ github.sha }}
|
key: gradle-build-cache-${{ github.sha }}
|
||||||
restore-keys: gradle-build-cache-
|
restore-keys: gradle-build-cache-
|
||||||
enableCrossOsArchive: true
|
enableCrossOsArchive: true
|
||||||
|
|||||||
22
.github/workflows/build.yml
vendored
22
.github/workflows/build.yml
vendored
@@ -9,6 +9,7 @@ on:
|
|||||||
- "buildSrc/**"
|
- "buildSrc/**"
|
||||||
- "build.py"
|
- "build.py"
|
||||||
- "gradle.properties"
|
- "gradle.properties"
|
||||||
|
- "gradle/libs.versions.toml"
|
||||||
- ".github/workflows/build.yml"
|
- ".github/workflows/build.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
@@ -17,7 +18,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build Magisk artifacts
|
name: Build Magisk artifacts
|
||||||
runs-on: macos-14
|
runs-on: macos-15
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
@@ -60,7 +61,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [windows-latest, ubuntu-latest]
|
os: [windows-2025, ubuntu-24.04]
|
||||||
steps:
|
steps:
|
||||||
- name: Check out
|
- name: Check out
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -78,16 +79,19 @@ jobs:
|
|||||||
|
|
||||||
avd-test:
|
avd-test:
|
||||||
name: Test API ${{ matrix.version }} (x86_64)
|
name: Test API ${{ matrix.version }} (x86_64)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs: build
|
needs: build
|
||||||
|
if: ${{ github.event_name != 'push' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]
|
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]
|
||||||
type: [""]
|
type: [""]
|
||||||
include:
|
include:
|
||||||
- version: 35
|
- version: "Baklava"
|
||||||
type: "google_apis"
|
type: "google_apis"
|
||||||
|
- version: "Baklava"
|
||||||
|
type: "google_apis_ps16k"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out
|
- name: Check out
|
||||||
@@ -122,8 +126,9 @@ jobs:
|
|||||||
|
|
||||||
avd-test-32:
|
avd-test-32:
|
||||||
name: Test API ${{ matrix.version }} (x86)
|
name: Test API ${{ matrix.version }} (x86)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs: build
|
needs: build
|
||||||
|
if: ${{ github.event_name != 'push' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -161,10 +166,11 @@ jobs:
|
|||||||
kernel.log
|
kernel.log
|
||||||
logcat.log
|
logcat.log
|
||||||
|
|
||||||
cf_test:
|
cf-test:
|
||||||
name: Test ${{ matrix.device }}
|
name: Test ${{ matrix.device }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: build
|
needs: build
|
||||||
|
if: ${{ github.event_name != 'push' }}
|
||||||
env:
|
env:
|
||||||
CF_HOME: /home/runner/aosp_cf_phone
|
CF_HOME: /home/runner/aosp_cf_phone
|
||||||
strategy:
|
strategy:
|
||||||
@@ -173,8 +179,6 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- branch: "aosp-main"
|
- branch: "aosp-main"
|
||||||
device: "aosp_cf_x86_64_phone"
|
device: "aosp_cf_x86_64_phone"
|
||||||
- branch: "aosp-main-throttled"
|
|
||||||
device: "aosp_cf_x86_64_phone_pgagnostic"
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out
|
- name: Check out
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,6 +14,7 @@ native/out
|
|||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
.idea
|
.idea
|
||||||
|
.kotlin
|
||||||
/local.properties
|
/local.properties
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
|
|||||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -4,18 +4,12 @@
|
|||||||
[submodule "lz4"]
|
[submodule "lz4"]
|
||||||
path = native/src/external/lz4
|
path = native/src/external/lz4
|
||||||
url = https://github.com/lz4/lz4.git
|
url = https://github.com/lz4/lz4.git
|
||||||
[submodule "bzip2"]
|
|
||||||
path = native/src/external/bzip2
|
|
||||||
url = https://github.com/nemequ/bzip2.git
|
|
||||||
[submodule "xz"]
|
[submodule "xz"]
|
||||||
path = native/src/external/xz
|
path = native/src/external/xz
|
||||||
url = https://github.com/xz-mirror/xz.git
|
url = https://github.com/xz-mirror/xz.git
|
||||||
[submodule "libcxx"]
|
[submodule "libcxx"]
|
||||||
path = native/src/external/libcxx
|
path = native/src/external/libcxx
|
||||||
url = https://github.com/topjohnwu/libcxx.git
|
url = https://github.com/topjohnwu/libcxx.git
|
||||||
[submodule "zlib"]
|
|
||||||
path = native/src/external/zlib
|
|
||||||
url = https://android.googlesource.com/platform/external/zlib
|
|
||||||
[submodule "zopfli"]
|
[submodule "zopfli"]
|
||||||
path = native/src/external/zopfli
|
path = native/src/external/zopfli
|
||||||
url = https://github.com/google/zopfli.git
|
url = https://github.com/google/zopfli.git
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ Some highlight features:
|
|||||||
|
|
||||||
Click the icon below to download Magisk apk.
|
Click the icon below to download Magisk apk.
|
||||||
|
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v27.0)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.0)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/v28.1)
|
||||||
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-27008)
|
[](https://github.com/topjohnwu/Magisk/releases/tag/canary-28103)
|
||||||
|
|
||||||
## Useful Links
|
## Useful Links
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ plugins {
|
|||||||
id("androidx.navigation.safeargs.kotlin")
|
id("androidx.navigation.safeargs.kotlin")
|
||||||
}
|
}
|
||||||
|
|
||||||
setupAppCommon()
|
setupMainApk()
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
correctErrorTypes = true
|
correctErrorTypes = true
|
||||||
@@ -18,27 +18,6 @@ kapt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.topjohnwu.magisk"
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId = "com.topjohnwu.magisk"
|
|
||||||
vectorDrawables.useSupportLibrary = true
|
|
||||||
versionName = Config.version
|
|
||||||
versionCode = Config.versionCode
|
|
||||||
ndk {
|
|
||||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64", "riscv64")
|
|
||||||
debugSymbolLevel = "FULL"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
isMinifyEnabled = true
|
|
||||||
isShrinkResources = true
|
|
||||||
proguardFiles("proguard-rules.pro")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
dataBinding = true
|
dataBinding = true
|
||||||
}
|
}
|
||||||
@@ -46,6 +25,13 @@ android {
|
|||||||
compileOptions {
|
compileOptions {
|
||||||
isCoreLibraryDesugaringEnabled = true
|
isCoreLibraryDesugaringEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>(), MenuProvider {
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
defaultOrientation = activity?.requestedOrientation ?: -1
|
defaultOrientation = activity?.requestedOrientation ?: -1
|
||||||
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
viewModel.startFlashing()
|
viewModel.startFlashing()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ object RebootMenu {
|
|||||||
activity.getSystemService<PowerManager>()?.isRebootingUserspaceSupported == true) {
|
activity.getSystemService<PowerManager>()?.isRebootingUserspaceSupported == true) {
|
||||||
menu.menu.findItem(R.id.action_reboot_userspace).isVisible = 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
|
menu.menu.findItem(R.id.action_reboot_safe_mode).isChecked = Config.bootloop >= 2
|
||||||
} else {
|
} else {
|
||||||
menu.menu.findItem(R.id.action_reboot_safe_mode).isVisible = false
|
menu.menu.findItem(R.id.action_reboot_safe_mode).isVisible = false
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.view.Menu
|
|||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewTreeObserver
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.view.MenuProvider
|
import androidx.core.view.MenuProvider
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
@@ -16,8 +17,6 @@ import com.topjohnwu.magisk.arch.BaseFragment
|
|||||||
import com.topjohnwu.magisk.arch.viewModel
|
import com.topjohnwu.magisk.arch.viewModel
|
||||||
import com.topjohnwu.magisk.core.ktx.toast
|
import com.topjohnwu.magisk.core.ktx.toast
|
||||||
import com.topjohnwu.magisk.databinding.FragmentActionMd2Binding
|
import com.topjohnwu.magisk.databinding.FragmentActionMd2Binding
|
||||||
import com.topjohnwu.magisk.ui.flash.FlashViewModel
|
|
||||||
import timber.log.Timber
|
|
||||||
import com.topjohnwu.magisk.core.R as CoreR
|
import com.topjohnwu.magisk.core.R as CoreR
|
||||||
|
|
||||||
class ActionFragment : BaseFragment<FragmentActionMd2Binding>(), MenuProvider {
|
class ActionFragment : BaseFragment<FragmentActionMd2Binding>(), MenuProvider {
|
||||||
@@ -37,39 +36,32 @@ class ActionFragment : BaseFragment<FragmentActionMd2Binding>(), MenuProvider {
|
|||||||
super.onStart()
|
super.onStart()
|
||||||
activity?.setTitle(viewModel.args.name)
|
activity?.setTitle(viewModel.args.name)
|
||||||
binding.closeBtn.setOnClickListener {
|
binding.closeBtn.setOnClickListener {
|
||||||
activity?.onBackPressed();
|
activity?.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.state.observe(this) {
|
viewModel.state.observe(this) {
|
||||||
activity?.supportActionBar?.setSubtitle(
|
if (it != ActionViewModel.State.RUNNING) {
|
||||||
when (it) {
|
binding.closeBtn.apply {
|
||||||
ActionViewModel.State.RUNNING -> CoreR.string.running
|
if (!this.isVisible) this.show()
|
||||||
ActionViewModel.State.SUCCESS -> CoreR.string.done
|
if (!this.isFocused) this.requestFocus()
|
||||||
ActionViewModel.State.FAILED -> CoreR.string.failure
|
}
|
||||||
|
}
|
||||||
|
if (it != ActionViewModel.State.SUCCESS) return@observe
|
||||||
|
view?.viewTreeObserver?.addOnWindowFocusChangeListener(
|
||||||
|
object : ViewTreeObserver.OnWindowFocusChangeListener {
|
||||||
|
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||||
|
if (hasFocus) return
|
||||||
|
view?.viewTreeObserver?.removeOnWindowFocusChangeListener(this)
|
||||||
|
view?.context?.apply {
|
||||||
|
toast(
|
||||||
|
getString(CoreR.string.done_action, viewModel.args.name),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
viewModel.back()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
when (it) {
|
|
||||||
ActionViewModel.State.SUCCESS -> {
|
|
||||||
activity?.apply {
|
|
||||||
toast(
|
|
||||||
getString(
|
|
||||||
com.topjohnwu.magisk.core.R.string.done_action,
|
|
||||||
this@ActionFragment.viewModel.args.name
|
|
||||||
), Toast.LENGTH_LONG
|
|
||||||
)
|
|
||||||
onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ActionViewModel.State.FAILED -> {
|
|
||||||
binding.closeBtn.apply {
|
|
||||||
if (!this.isVisible) this.show()
|
|
||||||
if (!this.isFocused) this.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +77,7 @@ class ActionFragment : BaseFragment<FragmentActionMd2Binding>(), MenuProvider {
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
defaultOrientation = activity?.requestedOrientation ?: -1
|
defaultOrientation = activity?.requestedOrientation ?: -1
|
||||||
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
|
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
viewModel.startRunAction()
|
viewModel.startRunAction()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
package com.topjohnwu.magisk.ui.module
|
package com.topjohnwu.magisk.ui.module
|
||||||
|
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.databinding.Bindable
|
|
||||||
import androidx.databinding.ObservableArrayList
|
import androidx.databinding.ObservableArrayList
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.map
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.topjohnwu.magisk.BR
|
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||||
import com.topjohnwu.magisk.core.Info
|
|
||||||
import com.topjohnwu.magisk.core.ktx.synchronized
|
import com.topjohnwu.magisk.core.ktx.synchronized
|
||||||
import com.topjohnwu.magisk.core.ktx.timeFormatStandard
|
import com.topjohnwu.magisk.core.ktx.timeFormatStandard
|
||||||
import com.topjohnwu.magisk.core.ktx.toTime
|
import com.topjohnwu.magisk.core.ktx.toTime
|
||||||
import com.topjohnwu.magisk.core.tasks.RunAction
|
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||||
import com.topjohnwu.magisk.databinding.set
|
|
||||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||||
import com.topjohnwu.magisk.ui.flash.ConsoleItem
|
import com.topjohnwu.magisk.ui.flash.ConsoleItem
|
||||||
import com.topjohnwu.superuser.CallbackList
|
import com.topjohnwu.superuser.CallbackList
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
class ActionViewModel : BaseViewModel() {
|
class ActionViewModel : BaseViewModel() {
|
||||||
|
|
||||||
@@ -32,7 +30,6 @@ class ActionViewModel : BaseViewModel() {
|
|||||||
|
|
||||||
private val _state = MutableLiveData(State.RUNNING)
|
private val _state = MutableLiveData(State.RUNNING)
|
||||||
val state: LiveData<State> get() = _state
|
val state: LiveData<State> get() = _state
|
||||||
val running = state.map { it == State.RUNNING }
|
|
||||||
|
|
||||||
val items = ObservableArrayList<ConsoleItem>()
|
val items = ObservableArrayList<ConsoleItem>()
|
||||||
lateinit var args: ActionFragmentArgs
|
lateinit var args: ActionFragmentArgs
|
||||||
@@ -46,10 +43,17 @@ class ActionViewModel : BaseViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startRunAction() {
|
fun startRunAction() = viewModelScope.launch {
|
||||||
viewModelScope.launch {
|
onResult(withContext(Dispatchers.IO) {
|
||||||
onResult(RunAction(args.id, outItems, logItems).exec())
|
try {
|
||||||
}
|
Shell.cmd("run_action \'${args.id}\'")
|
||||||
|
.to(outItems, logItems)
|
||||||
|
.exec().isSuccess
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Timber.e(e)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onResult(success: Boolean) {
|
private fun onResult(success: Boolean) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import com.topjohnwu.magisk.BR
|
|||||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||||
import com.topjohnwu.magisk.core.AppContext
|
import com.topjohnwu.magisk.core.AppContext
|
||||||
import com.topjohnwu.magisk.core.BuildConfig
|
import com.topjohnwu.magisk.core.BuildConfig
|
||||||
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.R
|
import com.topjohnwu.magisk.core.R
|
||||||
@@ -92,7 +93,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
|||||||
DownloadPath -> withExternalRW(doAction)
|
DownloadPath -> withExternalRW(doAction)
|
||||||
UpdateChecker -> withPostNotificationPermission(doAction)
|
UpdateChecker -> withPostNotificationPermission(doAction)
|
||||||
Authentication -> AuthEvent(doAction).publish()
|
Authentication -> AuthEvent(doAction).publish()
|
||||||
Hide, Restore -> withInstallPermission(doAction)
|
AutomaticResponse -> if (Config.suAuth) AuthEvent(doAction).publish() else doAction()
|
||||||
else -> doAction()
|
else -> doAction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ android {
|
|||||||
buildConfigField("int", "APP_VERSION_CODE", "${Config.versionCode}")
|
buildConfigField("int", "APP_VERSION_CODE", "${Config.versionCode}")
|
||||||
buildConfigField("String", "APP_VERSION_NAME", "\"${Config.version}\"")
|
buildConfigField("String", "APP_VERSION_NAME", "\"${Config.version}\"")
|
||||||
buildConfigField("int", "STUB_VERSION", Config.stubVersion)
|
buildConfigField("int", "STUB_VERSION", Config.stubVersion)
|
||||||
|
consumerProguardFile("proguard-rules.pro")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
@@ -59,5 +60,10 @@ dependencies {
|
|||||||
implementation(libs.activity)
|
implementation(libs.activity)
|
||||||
implementation(libs.collection.ktx)
|
implementation(libs.collection.ktx)
|
||||||
implementation(libs.profileinstaller)
|
implementation(libs.profileinstaller)
|
||||||
implementation(libs.lifecycle.process)
|
|
||||||
|
// We also implement all our tests in this module.
|
||||||
|
// However, we don't want to bundle test dependencies.
|
||||||
|
// That's why we make it compileOnly.
|
||||||
|
compileOnly(libs.test.junit)
|
||||||
|
compileOnly(libs.test.uiautomator)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,42 +22,19 @@
|
|||||||
int mActivityHandlesConfigFlags;
|
int mActivityHandlesConfigFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
# main
|
|
||||||
-keep,allowoptimization public class com.topjohnwu.magisk.signing.SignBoot {
|
|
||||||
public static void main(java.lang.String[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Strip Timber verbose and debug logging
|
# Strip Timber verbose and debug logging
|
||||||
-assumenosideeffects class timber.log.Timber$Tree {
|
-assumenosideeffects class timber.log.Timber$Tree {
|
||||||
public void v(**);
|
public void v(**);
|
||||||
public void d(**);
|
public void d(**);
|
||||||
}
|
}
|
||||||
|
|
||||||
# https://github.com/square/retrofit/issues/3751#issuecomment-1192043644
|
|
||||||
# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
|
|
||||||
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
|
|
||||||
-keep,allowobfuscation,allowshrinking class retrofit2.Response
|
|
||||||
|
|
||||||
# With R8 full mode generic signatures are stripped for classes that are not
|
# With R8 full mode generic signatures are stripped for classes that are not
|
||||||
# kept. Suspend functions are wrapped in continuations where the type argument
|
# kept. Suspend functions are wrapped in continuations where the type argument
|
||||||
# is used.
|
# is used.
|
||||||
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
|
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
|
||||||
|
|
||||||
|
|
||||||
# Excessive obfuscation
|
# Excessive obfuscation
|
||||||
-repackageclasses 'a'
|
-flattenpackagehierarchy
|
||||||
-allowaccessmodification
|
-allowaccessmodification
|
||||||
|
|
||||||
-obfuscationdictionary ../dict.txt
|
-dontwarn org.junit.**
|
||||||
-classobfuscationdictionary ../dict.txt
|
|
||||||
-packageobfuscationdictionary ../dict.txt
|
|
||||||
|
|
||||||
-dontwarn org.bouncycastle.jsse.BCSSLParameters
|
|
||||||
-dontwarn org.bouncycastle.jsse.BCSSLSocket
|
|
||||||
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
|
|
||||||
-dontwarn org.commonmark.ext.gfm.strikethrough.Strikethrough
|
|
||||||
-dontwarn org.conscrypt.Conscrypt*
|
|
||||||
-dontwarn org.conscrypt.ConscryptHostnameVerifier
|
|
||||||
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
|
|
||||||
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
|
|
||||||
-dontwarn org.openjsse.net.ssl.OpenJSSE
|
|
||||||
@@ -16,7 +16,6 @@ import com.topjohnwu.magisk.StubApk
|
|||||||
import com.topjohnwu.magisk.core.base.UntrackedActivity
|
import com.topjohnwu.magisk.core.base.UntrackedActivity
|
||||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||||
import com.topjohnwu.magisk.core.utils.NetworkObserver
|
import com.topjohnwu.magisk.core.utils.NetworkObserver
|
||||||
import com.topjohnwu.magisk.core.utils.ProcessLifecycle
|
|
||||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||||
import com.topjohnwu.magisk.core.utils.ShellInit
|
import com.topjohnwu.magisk.core.utils.ShellInit
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
@@ -40,6 +39,7 @@ object AppContext : ContextWrapper(null),
|
|||||||
|
|
||||||
private var ref = WeakReference<Activity>(null)
|
private var ref = WeakReference<Activity>(null)
|
||||||
private lateinit var application: Application
|
private lateinit var application: Application
|
||||||
|
private lateinit var networkObserver: NetworkObserver
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Always log full stack trace with Timber
|
// Always log full stack trace with Timber
|
||||||
@@ -56,6 +56,10 @@ object AppContext : ContextWrapper(null),
|
|||||||
LocaleSetting.instance.updateResource(resources)
|
LocaleSetting.instance.updateResource(resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onActivityStarted(activity: Activity) {
|
||||||
|
networkObserver.postCurrentState()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onActivityResumed(activity: Activity) {
|
override fun onActivityResumed(activity: Activity) {
|
||||||
if (activity is UntrackedActivity) return
|
if (activity is UntrackedActivity) return
|
||||||
ref = WeakReference(activity)
|
ref = WeakReference(activity)
|
||||||
@@ -102,8 +106,7 @@ object AppContext : ContextWrapper(null),
|
|||||||
val lm = getSystemService(LocaleManager::class.java)
|
val lm = getSystemService(LocaleManager::class.java)
|
||||||
lm.overrideLocaleConfig = LocaleSetting.localeConfig
|
lm.overrideLocaleConfig = LocaleSetting.localeConfig
|
||||||
}
|
}
|
||||||
ProcessLifecycle.init(this)
|
networkObserver = NetworkObserver.init(this)
|
||||||
NetworkObserver.init(this)
|
|
||||||
if (!BuildConfig.DEBUG && !isRunningAsStub) {
|
if (!BuildConfig.DEBUG && !isRunningAsStub) {
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
ProfileInstaller.writeProfile(this@AppContext)
|
ProfileInstaller.writeProfile(this@AppContext)
|
||||||
@@ -120,7 +123,6 @@ object AppContext : ContextWrapper(null),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
|
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
|
||||||
override fun onActivityStarted(activity: Activity) {}
|
|
||||||
override fun onActivityStopped(activity: Activity) {}
|
override fun onActivityStopped(activity: Activity) {}
|
||||||
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
|
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
|
||||||
override fun onActivityDestroyed(activity: Activity) {}
|
override fun onActivityDestroyed(activity: Activity) {}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ object Config : PreferenceConfig, DBConfig {
|
|||||||
const val SU_AUTO_ALLOW = 2
|
const val SU_AUTO_ALLOW = 2
|
||||||
|
|
||||||
// su timeout
|
// su timeout
|
||||||
val TIMEOUT_LIST = intArrayOf(0, -1, 10, 20, 30, 60)
|
val TIMEOUT_LIST = longArrayOf(0, -1, 10, 20, 30, 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val defaultChannel =
|
private val defaultChannel =
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ object Const {
|
|||||||
else Build.SUPPORTED_32_BIT_ABIS.firstOrNull()
|
else Build.SUPPORTED_32_BIT_ABIS.firstOrNull()
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
const val MAGISK_PATH = "/data/adb/modules"
|
const val MODULE_PATH = "/data/adb/modules"
|
||||||
const val TMPDIR = "/dev/tmp"
|
const val TMPDIR = "/dev/tmp"
|
||||||
const val MAGISK_LOG = "/cache/magisk.log"
|
const val MAGISK_LOG = "/cache/magisk.log"
|
||||||
|
|
||||||
@@ -28,6 +28,7 @@ object Const {
|
|||||||
|
|
||||||
fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
|
fun atLeast_24_0() = Info.env.versionCode >= 24000 || isCanary()
|
||||||
fun atLeast_25_0() = Info.env.versionCode >= 25000 || 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() = isCanary(Info.env.versionCode)
|
||||||
|
|
||||||
fun isCanary(ver: Int) = ver > 0 && ver % 100 != 0
|
fun isCanary(ver: Int) = ver > 0 && ver % 100 != 0
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ object Info {
|
|||||||
private set
|
private set
|
||||||
private var crypto = ""
|
private var crypto = ""
|
||||||
|
|
||||||
var hasGMS = true
|
|
||||||
val isEmulator =
|
val isEmulator =
|
||||||
Build.DEVICE.contains("vsoc")
|
Build.DEVICE.contains("vsoc")
|
||||||
|| getProperty("ro.kernel.qemu", "0") == "1"
|
|| getProperty("ro.kernel.qemu", "0") == "1"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.core.content.getSystemService
|
|||||||
import com.topjohnwu.magisk.core.base.BaseJobService
|
import com.topjohnwu.magisk.core.base.BaseJobService
|
||||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||||
|
import com.topjohnwu.magisk.core.download.DownloadSession
|
||||||
import com.topjohnwu.magisk.core.download.Subject
|
import com.topjohnwu.magisk.core.download.Subject
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -25,7 +26,7 @@ class JobService : BaseJobService() {
|
|||||||
@TargetApi(value = 34)
|
@TargetApi(value = 34)
|
||||||
inner class Session(
|
inner class Session(
|
||||||
private var params: JobParameters
|
private var params: JobParameters
|
||||||
) : DownloadEngine.Session {
|
) : DownloadSession {
|
||||||
|
|
||||||
override val context get() = this@JobService
|
override val context get() = this@JobService
|
||||||
val engine = DownloadEngine(this)
|
val engine = DownloadEngine(this)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.core
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.topjohnwu.magisk.core.base.BaseProvider
|
import com.topjohnwu.magisk.core.base.BaseProvider
|
||||||
import com.topjohnwu.magisk.core.su.SuCallbackHandler
|
import com.topjohnwu.magisk.core.su.SuCallbackHandler
|
||||||
import com.topjohnwu.magisk.core.su.TestHandler
|
|
||||||
|
|
||||||
class Provider : BaseProvider() {
|
class Provider : BaseProvider() {
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ class Provider : BaseProvider() {
|
|||||||
SuCallbackHandler.run(context!!, method, extras)
|
SuCallbackHandler.run(context!!, method, extras)
|
||||||
Bundle.EMPTY
|
Bundle.EMPTY
|
||||||
}
|
}
|
||||||
else -> TestHandler.run(method)
|
else -> Bundle.EMPTY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import androidx.core.app.ServiceCompat
|
|||||||
import androidx.core.content.IntentCompat
|
import androidx.core.content.IntentCompat
|
||||||
import com.topjohnwu.magisk.core.base.BaseService
|
import com.topjohnwu.magisk.core.base.BaseService
|
||||||
import com.topjohnwu.magisk.core.download.DownloadEngine
|
import com.topjohnwu.magisk.core.download.DownloadEngine
|
||||||
|
import com.topjohnwu.magisk.core.download.DownloadSession
|
||||||
import com.topjohnwu.magisk.core.download.Subject
|
import com.topjohnwu.magisk.core.download.Subject
|
||||||
|
|
||||||
class Service : BaseService(), DownloadEngine.Session {
|
class Service : BaseService(), DownloadSession {
|
||||||
|
|
||||||
private var mEngine: DownloadEngine? = null
|
private var mEngine: DownloadEngine? = null
|
||||||
override val context get() = this
|
override val context get() = this
|
||||||
|
|||||||
@@ -7,9 +7,13 @@ import kotlinx.coroutines.withContext
|
|||||||
|
|
||||||
open class MagiskDB {
|
open class MagiskDB {
|
||||||
|
|
||||||
suspend fun <R> exec(
|
class Literal(
|
||||||
|
val str: String
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend inline fun <R> exec(
|
||||||
query: String,
|
query: String,
|
||||||
mapper: suspend (Map<String, String>) -> R
|
crossinline mapper: (Map<String, String>) -> R
|
||||||
): List<R> {
|
): List<R> {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
val out = Shell.cmd("magisk --sqlite '$query'").await().out
|
val out = Shell.cmd("magisk --sqlite '$query'").await().out
|
||||||
@@ -18,13 +22,15 @@ open class MagiskDB {
|
|||||||
.map { it.split("=", limit = 2) }
|
.map { it.split("=", limit = 2) }
|
||||||
.filter { it.size == 2 }
|
.filter { it.size == 2 }
|
||||||
.associate { it[0] to it[1] }
|
.associate { it[0] to it[1] }
|
||||||
.let { mapper(it) }
|
.let(mapper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun exec(query: String) {
|
suspend fun exec(query: String) {
|
||||||
exec(query) {}
|
withContext(Dispatchers.IO) {
|
||||||
|
Shell.cmd("magisk --sqlite '$query'").await()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Map<String, Any>.toQuery(): String {
|
fun Map<String, Any>.toQuery(): String {
|
||||||
@@ -33,6 +39,7 @@ open class MagiskDB {
|
|||||||
when (it) {
|
when (it) {
|
||||||
is Boolean -> if (it) "1" else "0"
|
is Boolean -> if (it) "1" else "0"
|
||||||
is Number -> it.toString()
|
is Number -> it.toString()
|
||||||
|
is Literal -> it.str
|
||||||
else -> "\"$it\""
|
else -> "\"$it\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,24 @@ package com.topjohnwu.magisk.core.data.magiskdb
|
|||||||
import com.topjohnwu.magisk.core.AppContext
|
import com.topjohnwu.magisk.core.AppContext
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
private const val SELECT_QUERY = "SELECT (until - strftime(\"%s\", \"now\")) AS remain, *"
|
||||||
|
|
||||||
class PolicyDao : MagiskDB() {
|
class PolicyDao : MagiskDB() {
|
||||||
|
|
||||||
suspend fun deleteOutdated() {
|
suspend fun deleteOutdated() {
|
||||||
val nowSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
|
|
||||||
val query = "DELETE FROM ${Table.POLICY} WHERE " +
|
val query = "DELETE FROM ${Table.POLICY} WHERE " +
|
||||||
"(until > 0 AND until < $nowSeconds) OR until < 0"
|
"(until > 0 AND until < strftime(\"%s\", \"now\")) OR until < 0"
|
||||||
exec(query)
|
exec(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun delete(uid: Int) {
|
suspend fun delete(uid: Int) {
|
||||||
val query = "DELETE FROM ${Table.POLICY} WHERE uid == $uid"
|
val query = "DELETE FROM ${Table.POLICY} WHERE uid=$uid"
|
||||||
exec(query)
|
exec(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetch(uid: Int): SuPolicy? {
|
suspend fun fetch(uid: Int): SuPolicy? {
|
||||||
val query = "SELECT * FROM ${Table.POLICY} WHERE uid == $uid LIMIT = 1"
|
val query = "$SELECT_QUERY FROM ${Table.POLICY} WHERE uid=$uid LIMIT 1"
|
||||||
return exec(query, ::toPolicy).firstOrNull()
|
return exec(query, ::toPolicy).firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ class PolicyDao : MagiskDB() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchAll(): List<SuPolicy> {
|
suspend fun fetchAll(): List<SuPolicy> {
|
||||||
val query = "SELECT * FROM ${Table.POLICY} WHERE uid/100000 == ${Const.USER_ID}"
|
val query = "$SELECT_QUERY FROM ${Table.POLICY} WHERE uid/100000=${Const.USER_ID}"
|
||||||
return exec(query, ::toPolicy).filterNotNull()
|
return exec(query, ::toPolicy).filterNotNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,8 +43,15 @@ class PolicyDao : MagiskDB() {
|
|||||||
val uid = map["uid"]?.toInt() ?: return null
|
val uid = map["uid"]?.toInt() ?: return null
|
||||||
val policy = SuPolicy(uid)
|
val policy = SuPolicy(uid)
|
||||||
|
|
||||||
|
map["until"]?.toLong()?.let { until ->
|
||||||
|
if (until <= 0) {
|
||||||
|
policy.remain = until
|
||||||
|
} else {
|
||||||
|
map["remain"]?.toLong()?.let { policy.remain = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
map["policy"]?.toInt()?.let { policy.policy = it }
|
map["policy"]?.toInt()?.let { policy.policy = it }
|
||||||
map["until"]?.toLong()?.let { policy.until = it }
|
|
||||||
map["logging"]?.toInt()?.let { policy.logging = it != 0 }
|
map["logging"]?.toInt()?.let { policy.logging = it != 0 }
|
||||||
map["notification"]?.toInt()?.let { policy.notification = it != 0 }
|
map["notification"]?.toInt()?.let { policy.notification = it != 0 }
|
||||||
return policy
|
return policy
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core.data.magiskdb
|
|||||||
class SettingsDao : MagiskDB() {
|
class SettingsDao : MagiskDB() {
|
||||||
|
|
||||||
suspend fun delete(key: String) {
|
suspend fun delete(key: String) {
|
||||||
val query = "DELETE FROM ${Table.SETTINGS} WHERE key == \"$key\""
|
val query = "DELETE FROM ${Table.SETTINGS} WHERE key=\"$key\""
|
||||||
exec(query)
|
exec(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ class SettingsDao : MagiskDB() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetch(key: String, default: Int = -1): Int {
|
suspend fun fetch(key: String, default: Int = -1): Int {
|
||||||
val query = "SELECT value FROM ${Table.SETTINGS} WHERE key == \"$key\" LIMIT 1"
|
val query = "SELECT value FROM ${Table.SETTINGS} WHERE key=\"$key\" LIMIT 1"
|
||||||
return exec(query) { it["value"]?.toInt() }.firstOrNull() ?: default
|
return exec(query) { it["value"]?.toInt() }.firstOrNull() ?: default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.core.data.magiskdb
|
|||||||
class StringDao : MagiskDB() {
|
class StringDao : MagiskDB() {
|
||||||
|
|
||||||
suspend fun delete(key: String) {
|
suspend fun delete(key: String) {
|
||||||
val query = "DELETE FROM ${Table.STRINGS} WHERE key == \"$key\""
|
val query = "DELETE FROM ${Table.STRINGS} WHERE key=\"$key\""
|
||||||
exec(query)
|
exec(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ class StringDao : MagiskDB() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetch(key: String, default: String = ""): String {
|
suspend fun fetch(key: String, default: String = ""): String {
|
||||||
val query = "SELECT value FROM ${Table.STRINGS} WHERE key == \"$key\" LIMIT 1"
|
val query = "SELECT value FROM ${Table.STRINGS} WHERE key=\"$key\" LIMIT 1"
|
||||||
return exec(query) { it["value"] }.firstOrNull() ?: default
|
return exec(query) { it["value"] }.firstOrNull() ?: default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import com.squareup.moshi.Moshi
|
|||||||
import com.topjohnwu.magisk.ProviderInstaller
|
import com.topjohnwu.magisk.ProviderInstaller
|
||||||
import com.topjohnwu.magisk.core.BuildConfig
|
import com.topjohnwu.magisk.core.BuildConfig
|
||||||
import com.topjohnwu.magisk.core.Config
|
import com.topjohnwu.magisk.core.Config
|
||||||
import com.topjohnwu.magisk.core.Info
|
|
||||||
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
import com.topjohnwu.magisk.core.utils.LocaleSetting
|
||||||
import okhttp3.Cache
|
import okhttp3.Cache
|
||||||
import okhttp3.ConnectionSpec
|
import okhttp3.ConnectionSpec
|
||||||
@@ -72,9 +71,7 @@ fun createOkHttpClient(context: Context): OkHttpClient {
|
|||||||
chain.proceed(request.build())
|
chain.proceed(request.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ProviderInstaller.install(context)) {
|
ProviderInstaller.install(context)
|
||||||
Info.hasGMS = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import android.app.PendingIntent
|
|||||||
import android.app.job.JobInfo
|
import android.app.job.JobInfo
|
||||||
import android.app.job.JobScheduler
|
import android.app.job.JobScheduler
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
@@ -16,7 +15,6 @@ import androidx.collection.isNotEmpty
|
|||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.topjohnwu.magisk.StubApk
|
|
||||||
import com.topjohnwu.magisk.core.AppContext
|
import com.topjohnwu.magisk.core.AppContext
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
import com.topjohnwu.magisk.core.JobService
|
import com.topjohnwu.magisk.core.JobService
|
||||||
@@ -25,18 +23,8 @@ import com.topjohnwu.magisk.core.base.IActivityExtension
|
|||||||
import com.topjohnwu.magisk.core.cmp
|
import com.topjohnwu.magisk.core.cmp
|
||||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.core.intent
|
import com.topjohnwu.magisk.core.intent
|
||||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
|
||||||
import com.topjohnwu.magisk.core.ktx.cachedFile
|
|
||||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
|
||||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
|
||||||
import com.topjohnwu.magisk.core.ktx.forEach
|
|
||||||
import com.topjohnwu.magisk.core.ktx.set
|
import com.topjohnwu.magisk.core.ktx.set
|
||||||
import com.topjohnwu.magisk.core.ktx.withStreams
|
|
||||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
|
||||||
import com.topjohnwu.magisk.core.tasks.AppMigration
|
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
|
||||||
import com.topjohnwu.magisk.core.utils.ProgressInputStream
|
import com.topjohnwu.magisk.core.utils.ProgressInputStream
|
||||||
import com.topjohnwu.magisk.utils.APKInstall
|
|
||||||
import com.topjohnwu.magisk.view.Notifications
|
import com.topjohnwu.magisk.view.Notifications
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -44,13 +32,7 @@ import kotlinx.coroutines.Job
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipFile
|
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
import java.util.zip.ZipOutputStream
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class drives the execution of file downloads and notification management.
|
* This class drives the execution of file downloads and notification management.
|
||||||
@@ -69,16 +51,7 @@ import java.util.zip.ZipOutputStream
|
|||||||
* For API 23 - 33, we use a foreground service as a session.
|
* For API 23 - 33, we use a foreground service as a session.
|
||||||
* For API 34 and higher, we use user-initiated job services as a session.
|
* For API 34 and higher, we use user-initiated job services as a session.
|
||||||
*/
|
*/
|
||||||
class DownloadEngine(
|
class DownloadEngine(session: DownloadSession) : DownloadSession by session, DownloadNotifier {
|
||||||
private val session: Session
|
|
||||||
) {
|
|
||||||
|
|
||||||
interface Session {
|
|
||||||
val context: Context
|
|
||||||
|
|
||||||
fun attachNotification(id: Int, builder: Notification.Builder)
|
|
||||||
fun onDownloadComplete()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val ACTION = "com.topjohnwu.magisk.DOWNLOAD"
|
const val ACTION = "com.topjohnwu.magisk.DOWNLOAD"
|
||||||
@@ -99,33 +72,35 @@ class DownloadEngine(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createIntent(context: Context, subject: Subject) =
|
private fun createBroadcastIntent(context: Context, subject: Subject) =
|
||||||
if (Build.VERSION.SDK_INT >= 34) {
|
context.intent<com.topjohnwu.magisk.core.Receiver>()
|
||||||
context.intent<com.topjohnwu.magisk.core.Receiver>()
|
.setAction(ACTION)
|
||||||
.setAction(ACTION)
|
.putExtra(SUBJECT_KEY, subject)
|
||||||
.putExtra(SUBJECT_KEY, subject)
|
|
||||||
} else {
|
private fun createServiceIntent(context: Context, subject: Subject) =
|
||||||
context.intent<com.topjohnwu.magisk.core.Service>()
|
context.intent<com.topjohnwu.magisk.core.Service>()
|
||||||
.setAction(ACTION)
|
.setAction(ACTION)
|
||||||
.putExtra(SUBJECT_KEY, subject)
|
.putExtra(SUBJECT_KEY, subject)
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
fun getPendingIntent(context: Context, subject: Subject): PendingIntent {
|
fun getPendingIntent(context: Context, subject: Subject): PendingIntent {
|
||||||
val flag = PendingIntent.FLAG_IMMUTABLE or
|
val flag = PendingIntent.FLAG_IMMUTABLE or
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or
|
PendingIntent.FLAG_UPDATE_CURRENT or
|
||||||
PendingIntent.FLAG_ONE_SHOT
|
PendingIntent.FLAG_ONE_SHOT
|
||||||
val intent = createIntent(context, subject)
|
|
||||||
return if (Build.VERSION.SDK_INT >= 34) {
|
return if (Build.VERSION.SDK_INT >= 34) {
|
||||||
// On API 34+, download tasks are handled with a user-initiated job.
|
// On API 34+, download tasks are handled with a user-initiated job.
|
||||||
// However, there is no way to schedule a new job directly with a pending intent.
|
// However, there is no way to schedule a new job directly with a pending intent.
|
||||||
// As a workaround, we send the subject to a broadcast receiver and have it
|
// As a workaround, we send the subject to a broadcast receiver and have it
|
||||||
// schedule the job for us.
|
// schedule the job for us.
|
||||||
|
val intent = createBroadcastIntent(context, subject)
|
||||||
PendingIntent.getBroadcast(context, REQUEST_CODE, intent, flag)
|
PendingIntent.getBroadcast(context, REQUEST_CODE, intent, flag)
|
||||||
} else if (Build.VERSION.SDK_INT >= 26) {
|
|
||||||
PendingIntent.getForegroundService(context, REQUEST_CODE, intent, flag)
|
|
||||||
} else {
|
} else {
|
||||||
PendingIntent.getService(context, REQUEST_CODE, intent, flag)
|
val intent = createServiceIntent(context, subject)
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
PendingIntent.getForegroundService(context, REQUEST_CODE, intent, flag)
|
||||||
|
} else {
|
||||||
|
PendingIntent.getService(context, REQUEST_CODE, intent, flag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +115,7 @@ class DownloadEngine(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
fun start(context: Context, subject: Subject) {
|
fun start(context: Context, subject: Subject) {
|
||||||
if (Build.VERSION.SDK_INT >= 34) {
|
if (Build.VERSION.SDK_INT >= 34) {
|
||||||
val scheduler = context.getSystemService<JobScheduler>()!!
|
val scheduler = context.getSystemService<JobScheduler>()!!
|
||||||
@@ -152,24 +128,29 @@ class DownloadEngine(
|
|||||||
.setTransientExtras(extras)
|
.setTransientExtras(extras)
|
||||||
.build()
|
.build()
|
||||||
scheduler.schedule(info)
|
scheduler.schedule(info)
|
||||||
} else if (Build.VERSION.SDK_INT >= 26) {
|
|
||||||
context.startForegroundService(createIntent(context, subject))
|
|
||||||
} else {
|
} else {
|
||||||
context.startService(createIntent(context, subject))
|
val intent = createServiceIntent(context, subject)
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
context.startForegroundService(intent)
|
||||||
|
} else {
|
||||||
|
context.startService(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val notifications = SparseArrayCompat<Notification.Builder>()
|
||||||
|
private var attachedId = -1
|
||||||
|
private val job = Job()
|
||||||
|
private val processor = DownloadProcessor(this)
|
||||||
|
private val network get() = ServiceLocator.networkService
|
||||||
|
|
||||||
fun download(subject: Subject) {
|
fun download(subject: Subject) {
|
||||||
notifyUpdate(subject.notifyId)
|
notifyUpdate(subject.notifyId)
|
||||||
CoroutineScope(job + Dispatchers.IO).launch {
|
CoroutineScope(job + Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
val stream = network.fetchFile(subject.url).toProgressStream(subject)
|
val stream = network.fetchFile(subject.url).toProgressStream(subject)
|
||||||
when (subject) {
|
processor.handle(stream, subject)
|
||||||
is Subject.App -> handleApp(stream, subject)
|
|
||||||
is Subject.Module -> handleModule(stream, subject.file)
|
|
||||||
else -> stream.copyAndClose(subject.file.outputStream())
|
|
||||||
}
|
|
||||||
val activity = AppContext.foregroundActivity
|
val activity = AppContext.foregroundActivity
|
||||||
if (activity != null && subject.autoLaunch) {
|
if (activity != null && subject.autoLaunch) {
|
||||||
notifyRemove(subject.notifyId)
|
notifyRemove(subject.notifyId)
|
||||||
@@ -187,16 +168,13 @@ class DownloadEngine(
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
fun reattach() {
|
fun reattach() {
|
||||||
val builder = notifications[attachedId] ?: return
|
val builder = notifications[attachedId] ?: return
|
||||||
session.attachNotification(attachedId, builder)
|
attachNotification(attachedId, builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val notifications = SparseArrayCompat<Notification.Builder>()
|
private fun attach(id: Int, notification: Notification.Builder) {
|
||||||
private var attachedId = -1
|
attachedId = id
|
||||||
|
attachNotification(id, notification)
|
||||||
private val job = Job()
|
}
|
||||||
|
|
||||||
private val context get() = session.context
|
|
||||||
private val network get() = ServiceLocator.networkService
|
|
||||||
|
|
||||||
private fun finalNotify(id: Int, editor: (Notification.Builder) -> Unit): Int {
|
private fun finalNotify(id: Int, editor: (Notification.Builder) -> Unit): Int {
|
||||||
val notification = notifyRemove(id)?.also(editor) ?: return -1
|
val notification = notifyRemove(id)?.also(editor) ?: return -1
|
||||||
@@ -223,19 +201,14 @@ class DownloadEngine(
|
|||||||
subject.pendingIntent(context)?.let { intent -> it.setContentIntent(intent) }
|
subject.pendingIntent(context)?.let { intent -> it.setContentIntent(intent) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun attachNotification(id: Int, notification: Notification.Builder) {
|
|
||||||
attachedId = id
|
|
||||||
session.attachNotification(id, notification)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit = {}) {
|
override fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit) {
|
||||||
val notification = (notifications[id] ?: Notifications.startProgress("").also {
|
val notification = (notifications[id] ?: Notifications.startProgress("").also {
|
||||||
notifications[id] = it
|
notifications[id] = it
|
||||||
}).apply(editor)
|
}).apply(editor)
|
||||||
|
|
||||||
if (attachedId < 0)
|
if (attachedId < 0)
|
||||||
attachNotification(id, notification)
|
attach(id, notification)
|
||||||
else
|
else
|
||||||
Notifications.mgr.notify(id, notification.build())
|
Notifications.mgr.notify(id, notification.build())
|
||||||
}
|
}
|
||||||
@@ -255,11 +228,11 @@ class DownloadEngine(
|
|||||||
// There are still remaining notifications, pick one and attach to the session
|
// There are still remaining notifications, pick one and attach to the session
|
||||||
val anotherId = notifications.keyAt(0)
|
val anotherId = notifications.keyAt(0)
|
||||||
val notification = notifications.valueAt(0)
|
val notification = notifications.valueAt(0)
|
||||||
attachNotification(anotherId, notification)
|
attach(anotherId, notification)
|
||||||
} else {
|
} else {
|
||||||
// No more notifications left, terminate the session
|
// No more notifications left, terminate the session
|
||||||
attachedId = -1
|
attachedId = -1
|
||||||
session.onDownloadComplete()
|
onDownloadComplete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,90 +241,6 @@ class DownloadEngine(
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleApp(stream: InputStream, subject: Subject.App) {
|
|
||||||
val external = subject.file.outputStream()
|
|
||||||
|
|
||||||
if (isRunningAsStub) {
|
|
||||||
val updateApk = StubApk.update(context)
|
|
||||||
try {
|
|
||||||
// Download full APK to stub update path
|
|
||||||
stream.copyAndClose(TeeOutputStream(external, updateApk.outputStream()))
|
|
||||||
|
|
||||||
// Also upgrade stub
|
|
||||||
notifyUpdate(subject.notifyId) {
|
|
||||||
it.setProgress(0, 0, true)
|
|
||||||
.setContentTitle(context.getString(R.string.hide_app_title))
|
|
||||||
.setContentText("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract stub
|
|
||||||
val zf = ZipFile(updateApk)
|
|
||||||
val apk = context.cachedFile("stub.apk")
|
|
||||||
apk.delete()
|
|
||||||
zf.getInputStream(zf.getEntry("assets/stub.apk")).writeTo(apk)
|
|
||||||
zf.close()
|
|
||||||
|
|
||||||
// Patch and install
|
|
||||||
subject.intent = AppMigration.upgradeStub(context, apk)
|
|
||||||
?: throw IOException("HideAPK patch error")
|
|
||||||
apk.delete()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// If any error occurred, do not let stub load the new APK
|
|
||||||
updateApk.delete()
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val session = APKInstall.startSession(context)
|
|
||||||
stream.copyAndClose(TeeOutputStream(external, session.openStream(context)))
|
|
||||||
subject.intent = session.waitIntent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun handleModule(src: InputStream, file: Uri) {
|
|
||||||
val input = ZipInputStream(src)
|
|
||||||
val output = ZipOutputStream(file.outputStream())
|
|
||||||
|
|
||||||
withStreams(input, output) { zin, zout ->
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/"))
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/"))
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/google/"))
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/"))
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/update-binary"))
|
|
||||||
context.assets.open("module_installer.sh").use { it.copyAll(zout) }
|
|
||||||
|
|
||||||
zout.putNextEntry(ZipEntry("META-INF/com/google/android/updater-script"))
|
|
||||||
zout.write("#MAGISK\n".toByteArray())
|
|
||||||
|
|
||||||
zin.forEach { entry ->
|
|
||||||
val path = entry.name
|
|
||||||
if (path.isNotEmpty() && !path.startsWith("META-INF")) {
|
|
||||||
zout.putNextEntry(ZipEntry(path))
|
|
||||||
if (!entry.isDirectory) {
|
|
||||||
zin.copyAll(zout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TeeOutputStream(
|
|
||||||
private val o1: OutputStream,
|
|
||||||
private val o2: OutputStream
|
|
||||||
) : OutputStream() {
|
|
||||||
override fun write(b: Int) {
|
|
||||||
o1.write(b)
|
|
||||||
o2.write(b)
|
|
||||||
}
|
|
||||||
override fun write(b: ByteArray?, off: Int, len: Int) {
|
|
||||||
o1.write(b, off, len)
|
|
||||||
o2.write(b, off, len)
|
|
||||||
}
|
|
||||||
override fun close() {
|
|
||||||
o1.close()
|
|
||||||
o2.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ResponseBody.toProgressStream(subject: Subject): InputStream {
|
private fun ResponseBody.toProgressStream(subject: Subject): InputStream {
|
||||||
val max = contentLength()
|
val max = contentLength()
|
||||||
val total = max.toFloat() / 1048576
|
val total = max.toFloat() / 1048576
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package com.topjohnwu.magisk.core.download
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import com.topjohnwu.magisk.StubApk
|
||||||
|
import com.topjohnwu.magisk.core.R
|
||||||
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
|
import com.topjohnwu.magisk.core.ktx.cachedFile
|
||||||
|
import com.topjohnwu.magisk.core.ktx.copyAll
|
||||||
|
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
||||||
|
import com.topjohnwu.magisk.core.ktx.withInOut
|
||||||
|
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||||
|
import com.topjohnwu.magisk.core.tasks.AppMigration
|
||||||
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream
|
||||||
|
import com.topjohnwu.magisk.utils.APKInstall
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipFile
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
class DownloadProcessor(notifier: DownloadNotifier) : DownloadNotifier by notifier {
|
||||||
|
|
||||||
|
suspend fun handle(stream: InputStream, subject: Subject) {
|
||||||
|
when (subject) {
|
||||||
|
is Subject.App -> handleApp(stream, subject)
|
||||||
|
is Subject.Module -> handleModule(stream, subject.file)
|
||||||
|
else -> stream.copyAndClose(subject.file.outputStream())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun handleApp(stream: InputStream, subject: Subject.App) {
|
||||||
|
val external = subject.file.outputStream()
|
||||||
|
|
||||||
|
if (isRunningAsStub) {
|
||||||
|
val updateApk = StubApk.update(context)
|
||||||
|
try {
|
||||||
|
// Download full APK to stub update path
|
||||||
|
stream.copyAndClose(TeeOutputStream(external, updateApk.outputStream()))
|
||||||
|
|
||||||
|
// Also upgrade stub
|
||||||
|
notifyUpdate(subject.notifyId) {
|
||||||
|
it.setProgress(0, 0, true)
|
||||||
|
.setContentTitle(context.getString(R.string.hide_app_title))
|
||||||
|
.setContentText("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract stub
|
||||||
|
val apk = context.cachedFile("stub.apk")
|
||||||
|
ZipFile.Builder().setFile(updateApk).get().use { zf ->
|
||||||
|
apk.delete()
|
||||||
|
zf.getInputStream(zf.getEntry("assets/stub.apk")).writeTo(apk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch and install
|
||||||
|
subject.intent = AppMigration.upgradeStub(context, apk)
|
||||||
|
?: throw IOException("HideAPK patch error")
|
||||||
|
apk.delete()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// If any error occurred, do not let stub load the new APK
|
||||||
|
updateApk.delete()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val session = APKInstall.startSession(context)
|
||||||
|
stream.copyAndClose(TeeOutputStream(external, session.openStream(context)))
|
||||||
|
subject.intent = session.waitIntent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun handleModule(src: InputStream, file: Uri) {
|
||||||
|
val tmp = context.cachedFile("module.zip")
|
||||||
|
try {
|
||||||
|
// First download the entire zip into cache so we can process it
|
||||||
|
src.writeTo(tmp)
|
||||||
|
|
||||||
|
val input = ZipFile.Builder().setFile(tmp).get()
|
||||||
|
val output = ZipArchiveOutputStream(file.outputStream())
|
||||||
|
withInOut(input, output) { zin, zout ->
|
||||||
|
zout.putArchiveEntry(ZipArchiveEntry("META-INF/"))
|
||||||
|
zout.closeArchiveEntry()
|
||||||
|
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/"))
|
||||||
|
zout.closeArchiveEntry()
|
||||||
|
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/"))
|
||||||
|
zout.closeArchiveEntry()
|
||||||
|
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/"))
|
||||||
|
zout.closeArchiveEntry()
|
||||||
|
|
||||||
|
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/update-binary"))
|
||||||
|
context.assets.open("module_installer.sh").use { it.copyAll(zout) }
|
||||||
|
zout.closeArchiveEntry()
|
||||||
|
|
||||||
|
zout.putArchiveEntry(ZipArchiveEntry("META-INF/com/google/android/updater-script"))
|
||||||
|
zout.write("#MAGISK\n".toByteArray())
|
||||||
|
zout.closeArchiveEntry()
|
||||||
|
|
||||||
|
// Then simply copy all entries to output
|
||||||
|
zin.copyRawEntries(zout) { entry -> !entry.name.startsWith("META-INF") }
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
tmp.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TeeOutputStream(
|
||||||
|
private val o1: OutputStream,
|
||||||
|
private val o2: OutputStream
|
||||||
|
) : OutputStream() {
|
||||||
|
override fun write(b: Int) {
|
||||||
|
o1.write(b)
|
||||||
|
o2.write(b)
|
||||||
|
}
|
||||||
|
override fun write(b: ByteArray?, off: Int, len: Int) {
|
||||||
|
o1.write(b, off, len)
|
||||||
|
o2.write(b, off, len)
|
||||||
|
}
|
||||||
|
override fun close() {
|
||||||
|
o1.close()
|
||||||
|
o2.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.topjohnwu.magisk.core.download
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
interface DownloadSession {
|
||||||
|
val context: Context
|
||||||
|
fun attachNotification(id: Int, builder: Notification.Builder)
|
||||||
|
fun onDownloadComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DownloadNotifier {
|
||||||
|
val context: Context
|
||||||
|
fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit = {})
|
||||||
|
}
|
||||||
@@ -2,7 +2,11 @@ package com.topjohnwu.magisk.core.ktx
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.*
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@@ -23,7 +27,6 @@ import com.topjohnwu.magisk.core.utils.RootUtils
|
|||||||
import com.topjohnwu.magisk.utils.APKInstall
|
import com.topjohnwu.magisk.utils.APKInstall
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.String
|
|
||||||
|
|
||||||
fun Context.getBitmap(id: Int): Bitmap {
|
fun Context.getBitmap(id: Int): Bitmap {
|
||||||
var drawable = getDrawable(id)!!
|
var drawable = getDrawable(id)!!
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.flatMapMerge
|
|||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@@ -17,24 +18,14 @@ import java.text.DateFormat
|
|||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.zip.ZipEntry
|
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
|
|
||||||
inline fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) {
|
inline fun <In : Closeable, Out : Closeable> withInOut(
|
||||||
var entry: ZipEntry? = nextEntry
|
input: In,
|
||||||
while (entry != null) {
|
output: Out,
|
||||||
callback(entry)
|
|
||||||
entry = nextEntry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <In : InputStream, Out : OutputStream> withStreams(
|
|
||||||
inStream: In,
|
|
||||||
outStream: Out,
|
|
||||||
withBoth: (In, Out) -> Unit
|
withBoth: (In, Out) -> Unit
|
||||||
) {
|
) {
|
||||||
inStream.use { reader ->
|
input.use { reader ->
|
||||||
outStream.use { writer ->
|
output.use { writer ->
|
||||||
withBoth(reader, writer)
|
withBoth(reader, writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +55,7 @@ suspend inline fun InputStream.copyAndClose(
|
|||||||
out: OutputStream,
|
out: OutputStream,
|
||||||
bufferSize: Int = DEFAULT_BUFFER_SIZE,
|
bufferSize: Int = DEFAULT_BUFFER_SIZE,
|
||||||
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
) = withStreams(this, out) { i, o -> i.copyAll(o, bufferSize, dispatcher) }
|
) = withInOut(this, out) { i, o -> i.copyAll(o, bufferSize, dispatcher) }
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
suspend inline fun InputStream.writeTo(
|
suspend inline fun InputStream.writeTo(
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import com.topjohnwu.magisk.core.Const
|
|||||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import com.topjohnwu.superuser.nio.ExtendedFile
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
|
|
||||||
data class LocalModule(
|
data class LocalModule(
|
||||||
private val path: String,
|
private val base: ExtendedFile,
|
||||||
) : Module() {
|
) : Module() {
|
||||||
private val svc get() = ServiceLocator.networkService
|
private val svc get() = ServiceLocator.networkService
|
||||||
|
|
||||||
@@ -24,20 +25,18 @@ data class LocalModule(
|
|||||||
var description: String = ""
|
var description: String = ""
|
||||||
var updateInfo: OnlineModule? = null
|
var updateInfo: OnlineModule? = null
|
||||||
var outdated = false
|
var outdated = false
|
||||||
|
|
||||||
private var updateUrl: String = ""
|
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()
|
private val removeFile = base.getChildFile("remove")
|
||||||
val isRiru: Boolean get() = (id == "riru-core") || riruFolder.exists()
|
private val disableFile = base.getChildFile("disable")
|
||||||
val isZygisk: Boolean get() = zygiskFolder.exists()
|
private val updateFile = base.getChildFile("update")
|
||||||
val zygiskUnloaded: Boolean get() = unloaded.exists()
|
val zygiskFolder = base.getChildFile("zygisk")
|
||||||
val hasAction: Boolean;
|
|
||||||
|
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
|
var enable: Boolean
|
||||||
get() = !disableFile.exists()
|
get() = !disableFile.exists()
|
||||||
@@ -90,19 +89,16 @@ data class LocalModule(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
runCatching {
|
runCatching {
|
||||||
parseProps(Shell.cmd("dos2unix < $path/module.prop").exec().out)
|
parseProps(Shell.cmd("dos2unix < $base/module.prop").exec().out)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id.isEmpty()) {
|
if (id.isEmpty()) {
|
||||||
val sep = path.lastIndexOf('/')
|
id = base.name
|
||||||
id = path.substring(sep + 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
name = id
|
name = id
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAction = RootUtils.fs.getFile(path, "action.sh").exists()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetch(): Boolean {
|
suspend fun fetch(): Boolean {
|
||||||
@@ -125,14 +121,14 @@ data class LocalModule(
|
|||||||
|
|
||||||
companion object {
|
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) {
|
suspend fun installed() = withContext(Dispatchers.IO) {
|
||||||
RootUtils.fs.getFile(Const.MAGISK_PATH)
|
RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||||
.listFiles()
|
.listFiles()
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
.filter { !it.isFile && !it.isHidden }
|
.filter { !it.isFile && !it.isHidden }
|
||||||
.map { LocalModule("${Const.MAGISK_PATH}/${it.name}") }
|
.map { LocalModule(it) }
|
||||||
.sortedBy { it.name.lowercase(Locale.ROOT) }
|
.sortedBy { it.name.lowercase(Locale.ROOT) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,32 @@
|
|||||||
package com.topjohnwu.magisk.core.model.su
|
package com.topjohnwu.magisk.core.model.su
|
||||||
|
|
||||||
class SuPolicy(val uid: Int) {
|
import com.topjohnwu.magisk.core.data.magiskdb.MagiskDB
|
||||||
|
|
||||||
|
class SuPolicy(
|
||||||
|
val uid: Int,
|
||||||
|
var policy: Int = INTERACTIVE,
|
||||||
|
var remain: Long = -1L,
|
||||||
|
var logging: Boolean = true,
|
||||||
|
var notification: Boolean = true,
|
||||||
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val INTERACTIVE = 0
|
const val INTERACTIVE = 0
|
||||||
const val DENY = 1
|
const val DENY = 1
|
||||||
const val ALLOW = 2
|
const val ALLOW = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
var policy: Int = INTERACTIVE
|
fun toMap(): MutableMap<String, Any> {
|
||||||
var until: Long = -1L
|
val until = if (remain <= 0) {
|
||||||
var logging: Boolean = true
|
remain
|
||||||
var notification: Boolean = true
|
} else {
|
||||||
|
MagiskDB.Literal("(strftime(\"%s\", \"now\") + $remain)")
|
||||||
fun toMap(): MutableMap<String, Any> = mutableMapOf(
|
}
|
||||||
"uid" to uid,
|
return mutableMapOf(
|
||||||
"policy" to policy,
|
"uid" to uid,
|
||||||
"until" to until,
|
"policy" to policy,
|
||||||
"logging" to logging,
|
"until" to until,
|
||||||
"notification" to notification
|
"logging" to logging,
|
||||||
)
|
"notification" to notification
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
public class ByteArrayStream extends ByteArrayOutputStream {
|
public class ByteArrayStream extends ByteArrayOutputStream {
|
||||||
|
|
||||||
@@ -27,4 +28,8 @@ public class ByteArrayStream extends ByteArrayOutputStream {
|
|||||||
public ByteArrayInputStream getInputStream() {
|
public ByteArrayInputStream getInputStream() {
|
||||||
return new ByteArrayInputStream(buf, 0, count);
|
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;
|
privateKey[0] = key;
|
||||||
|
|
||||||
// Generate, in memory, an APK signed using standard JAR Signature Scheme.
|
// Generate, in memory, an APK signed using standard JAR Signature Scheme.
|
||||||
ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream();
|
ByteArrayStream v1SignedApkBuf = new ByteArrayStream();
|
||||||
JarOutputStream outputJar = new JarOutputStream(v1SignedApkBuf);
|
JarOutputStream outputJar = new JarOutputStream(v1SignedApkBuf);
|
||||||
// Use maximum compression for compressed entries because the APK lives forever on
|
// Use maximum compression for compressed entries because the APK lives forever on
|
||||||
// the system partition.
|
// the system partition.
|
||||||
@@ -519,8 +519,7 @@ public class SignApk {
|
|||||||
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
|
copyFiles(manifest, inputJar, outputJar, timestamp, alignment);
|
||||||
signFile(manifest, publicKey, privateKey, timestamp, outputJar);
|
signFile(manifest, publicKey, privateKey, timestamp, outputJar);
|
||||||
outputJar.close();
|
outputJar.close();
|
||||||
ByteBuffer v1SignedApk = ByteBuffer.wrap(v1SignedApkBuf.toByteArray());
|
ByteBuffer v1SignedApk = v1SignedApkBuf.toByteBuffer();
|
||||||
v1SignedApkBuf.reset();
|
|
||||||
|
|
||||||
ByteBuffer[] outputChunks;
|
ByteBuffer[] outputChunks;
|
||||||
List<ApkSignerV2.SignerConfig> signerConfigs = createV2SignerConfigs(privateKey, publicKey,
|
List<ApkSignerV2.SignerConfig> signerConfigs = createV2SignerConfigs(privateKey, publicKey,
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class SuRequestHandler(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
output = File(fifo)
|
output = File(fifo)
|
||||||
policy = SuPolicy(uid)
|
policy = policyDB.fetch(uid) ?: SuPolicy(uid)
|
||||||
try {
|
try {
|
||||||
pkgInfo = pm.getPackageInfo(uid, pid) ?: PackageInfo().apply {
|
pkgInfo = pm.getPackageInfo(uid, pid) ?: PackageInfo().apply {
|
||||||
val name = pm.getNameForUid(uid) ?: throw PackageManager.NameNotFoundException()
|
val name = pm.getNameForUid(uid) ?: throw PackageManager.NameNotFoundException()
|
||||||
@@ -81,15 +81,13 @@ class SuRequestHandler(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun respond(action: Int, time: Int) {
|
suspend fun respond(action: Int, time: Long) {
|
||||||
val until = if (time > 0)
|
|
||||||
TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) +
|
|
||||||
TimeUnit.MINUTES.toSeconds(time.toLong())
|
|
||||||
else
|
|
||||||
time.toLong()
|
|
||||||
|
|
||||||
policy.policy = action
|
policy.policy = action
|
||||||
policy.until = until
|
if (time >= 0) {
|
||||||
|
policy.remain = TimeUnit.MINUTES.toSeconds(time)
|
||||||
|
} else {
|
||||||
|
policy.remain = time
|
||||||
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
@@ -100,7 +98,7 @@ class SuRequestHandler(
|
|||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
if (until >= 0) {
|
if (time >= 0) {
|
||||||
policyDB.update(policy)
|
policyDB.update(policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.core.su
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import com.topjohnwu.magisk.core.Config
|
|
||||||
import com.topjohnwu.magisk.core.Info
|
|
||||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
|
||||||
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 kotlinx.coroutines.Runnable
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
object TestHandler {
|
|
||||||
|
|
||||||
object LogList : CallbackList<String>(Runnable::run) {
|
|
||||||
override fun onAddElement(e: String) {
|
|
||||||
Timber.i(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun run(method: String): Bundle {
|
|
||||||
var reason: String? = null
|
|
||||||
|
|
||||||
fun prerequisite(): Boolean {
|
|
||||||
// Make sure the Magisk app can get root
|
|
||||||
val shell = Shell.getShell()
|
|
||||||
if (!shell.isRoot) {
|
|
||||||
reason = "shell not root"
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the root service is running
|
|
||||||
RootUtils.Connection.await()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setup(): Boolean {
|
|
||||||
return runBlocking {
|
|
||||||
MagiskInstaller.Emulator(LogList, LogList).exec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun test(): Boolean {
|
|
||||||
// Make sure Zygisk works correctly
|
|
||||||
if (!Info.isZygiskEnabled) {
|
|
||||||
reason = "zygisk not enabled"
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear existing grant for ADB shell
|
|
||||||
runBlocking {
|
|
||||||
ServiceLocator.policyDB.delete(2000)
|
|
||||||
Config.suAutoResponse = Config.Value.SU_AUTO_ALLOW
|
|
||||||
Config.prefs.edit().commit()
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = prerequisite() && runCatching {
|
|
||||||
when (method) {
|
|
||||||
"setup" -> setup()
|
|
||||||
"test" -> test()
|
|
||||||
else -> {
|
|
||||||
reason = "unknown method"
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.getOrElse {
|
|
||||||
reason = it.stackTraceToString()
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
return Bundle().apply {
|
|
||||||
putBoolean("result", result)
|
|
||||||
if (reason != null) putString("reason", reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ import android.app.Activity
|
|||||||
import android.app.ActivityOptions
|
import android.app.ActivityOptions
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.topjohnwu.magisk.StubApk
|
import com.topjohnwu.magisk.StubApk
|
||||||
@@ -13,7 +14,6 @@ import com.topjohnwu.magisk.core.Config
|
|||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
import com.topjohnwu.magisk.core.R
|
import com.topjohnwu.magisk.core.R
|
||||||
import com.topjohnwu.magisk.core.ktx.await
|
import com.topjohnwu.magisk.core.ktx.await
|
||||||
import com.topjohnwu.magisk.core.ktx.copyAndClose
|
|
||||||
import com.topjohnwu.magisk.core.ktx.toast
|
import com.topjohnwu.magisk.core.ktx.toast
|
||||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||||
import com.topjohnwu.magisk.core.signing.JarMap
|
import com.topjohnwu.magisk.core.signing.JarMap
|
||||||
@@ -23,11 +23,9 @@ import com.topjohnwu.magisk.core.utils.Keygen
|
|||||||
import com.topjohnwu.magisk.utils.APKInstall
|
import com.topjohnwu.magisk.utils.APKInstall
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Runnable
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
@@ -38,6 +36,7 @@ object AppMigration {
|
|||||||
private const val ALPHA = "abcdefghijklmnopqrstuvwxyz"
|
private const val ALPHA = "abcdefghijklmnopqrstuvwxyz"
|
||||||
private const val ALPHADOTS = "$ALPHA....."
|
private const val ALPHADOTS = "$ALPHA....."
|
||||||
private const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
private const val ANDROID_MANIFEST = "AndroidManifest.xml"
|
||||||
|
private const val TEST_PKG_NAME = "$APP_PACKAGE_NAME.test"
|
||||||
|
|
||||||
// Some arbitrary limit
|
// Some arbitrary limit
|
||||||
const val MAX_LABEL_LENGTH = 32
|
const val MAX_LABEL_LENGTH = 32
|
||||||
@@ -133,21 +132,15 @@ object AppMigration {
|
|||||||
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
||||||
val xml = AXML(jar.getRawData(je))
|
val xml = AXML(jar.getRawData(je))
|
||||||
val generator = classNameGenerator()
|
val generator = classNameGenerator()
|
||||||
|
val p = xml.patchStrings {
|
||||||
if (!xml.patchStrings {
|
when {
|
||||||
for (i in it.indices) {
|
it.contains(APP_PACKAGE_NAME) -> it.replace(APP_PACKAGE_NAME, pkg)
|
||||||
val s = it[i]
|
it.contains(PLACEHOLDER) -> generator.next()
|
||||||
if (s.contains(APP_PACKAGE_NAME)) {
|
it == origLabel -> label.toString()
|
||||||
it[i] = s.replace(APP_PACKAGE_NAME, pkg)
|
else -> it
|
||||||
} else if (s.contains(PLACEHOLDER)) {
|
|
||||||
it[i] = generator.next()
|
|
||||||
} else if (s == origLabel) {
|
|
||||||
it[i] = label.toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}) {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
if (!p) return false
|
||||||
|
|
||||||
// Write apk changes
|
// Write apk changes
|
||||||
jar.getOutputStream(je).use { it.write(xml.bytes) }
|
jar.getOutputStream(je).use { it.write(xml.bytes) }
|
||||||
@@ -161,52 +154,87 @@ object AppMigration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchApp(activity: Activity, pkg: String) {
|
private fun patchTest(apk: File, out: File, pkg: String): Boolean {
|
||||||
val intent = activity.packageManager.getLaunchIntentForPackage(pkg) ?: return
|
try {
|
||||||
|
JarMap.open(apk, true).use { jar ->
|
||||||
|
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
||||||
|
val xml = AXML(jar.getRawData(je))
|
||||||
|
val p = xml.patchStrings {
|
||||||
|
when (it) {
|
||||||
|
APP_PACKAGE_NAME -> pkg
|
||||||
|
TEST_PKG_NAME -> "$pkg.test"
|
||||||
|
else -> it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!p) return false
|
||||||
|
|
||||||
|
// Write apk changes
|
||||||
|
jar.getOutputStream(je).use { it.write(xml.bytes) }
|
||||||
|
val keys = Keygen()
|
||||||
|
out.outputStream().use { SignApk.sign(keys.cert, keys.key, jar, it) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun launchApp(context: Context, pkg: String) {
|
||||||
|
val intent = context.packageManager.getLaunchIntentForPackage(pkg) ?: return
|
||||||
intent.putExtra(Const.Key.PREV_CONFIG, Config.toBundle())
|
intent.putExtra(Const.Key.PREV_CONFIG, Config.toBundle())
|
||||||
val options = ActivityOptions.makeBasic()
|
val options = ActivityOptions.makeBasic()
|
||||||
if (Build.VERSION.SDK_INT >= 34) {
|
if (Build.VERSION.SDK_INT >= 34) {
|
||||||
options.setShareIdentityEnabled(true)
|
options.setShareIdentityEnabled(true)
|
||||||
}
|
}
|
||||||
activity.startActivity(intent, options.toBundle())
|
context.startActivity(intent, options.toBundle())
|
||||||
activity.finish()
|
if (context is Activity) {
|
||||||
|
context.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun patchAndHide(activity: Activity, label: String, onFailure: Runnable): Boolean {
|
suspend fun patchAndHide(context: Context, label: String, pkg: String? = null): Boolean {
|
||||||
val stub = File(activity.cacheDir, "stub.apk")
|
val stub = File(context.cacheDir, "stub.apk")
|
||||||
try {
|
try {
|
||||||
activity.assets.open("stub.apk").writeTo(stub)
|
context.assets.open("stub.apk").writeTo(stub)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new random package name and signature
|
// Generate a new random signature and package name if needed
|
||||||
val repack = File(activity.cacheDir, "patched.apk")
|
val pkg = pkg ?: genPackageName()
|
||||||
val pkg = genPackageName()
|
|
||||||
Config.keyStoreRaw = ""
|
Config.keyStoreRaw = ""
|
||||||
|
|
||||||
if (!patch(activity, stub, FileOutputStream(repack), pkg, label))
|
// Check and patch the test APK
|
||||||
return false
|
try {
|
||||||
|
val info = context.packageManager.getApplicationInfo(TEST_PKG_NAME, 0)
|
||||||
|
val testApk = File(info.sourceDir)
|
||||||
|
val testRepack = File(context.cacheDir, "test.apk")
|
||||||
|
if (!patchTest(testApk, testRepack, pkg))
|
||||||
|
return false
|
||||||
|
val cmd = "adb_pm_install $testRepack $pkg.test"
|
||||||
|
if (!Shell.cmd(cmd).exec().isSuccess)
|
||||||
|
return false
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
val repack = File(context.cacheDir, "patched.apk")
|
||||||
|
repack.outputStream().use {
|
||||||
|
if (!patch(context, stub, it, pkg, label))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Install and auto launch app
|
// Install and auto launch app
|
||||||
val session = APKInstall.startSession(activity, pkg, onFailure) {
|
val cmd = "adb_pm_install $repack $pkg"
|
||||||
|
if (Shell.cmd(cmd).exec().isSuccess) {
|
||||||
Config.suManager = pkg
|
Config.suManager = pkg
|
||||||
Shell.cmd("touch $AppApkPath").exec()
|
Shell.cmd("touch $AppApkPath").exec()
|
||||||
launchApp(activity, pkg)
|
launchApp(context, pkg)
|
||||||
}
|
return true
|
||||||
|
} else {
|
||||||
val cmd = "adb_pm_install $repack $pkg"
|
|
||||||
if (Shell.cmd(cmd).exec().isSuccess) return true
|
|
||||||
|
|
||||||
try {
|
|
||||||
repack.inputStream().copyAndClose(session.openStream(activity))
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Timber.e(e)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
session.waitIntent()?.let { activity.startActivity(it) } ?: return false
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@@ -217,14 +245,25 @@ object AppMigration {
|
|||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
val onFailure = Runnable {
|
val success = withContext(Dispatchers.IO) {
|
||||||
|
patchAndHide(activity, label)
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
activity.toast(R.string.failure, Toast.LENGTH_LONG)
|
activity.toast(R.string.failure, Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
val success = withContext(Dispatchers.IO) {
|
}
|
||||||
patchAndHide(activity, label, onFailure)
|
|
||||||
|
suspend fun restoreApp(context: Context): Boolean {
|
||||||
|
val apk = StubApk.current(context)
|
||||||
|
val cmd = "adb_pm_install $apk $APP_PACKAGE_NAME"
|
||||||
|
if (Shell.cmd(cmd).await().isSuccess) {
|
||||||
|
Config.suManager = ""
|
||||||
|
Shell.cmd("touch $AppApkPath").exec()
|
||||||
|
launchApp(context, APP_PACKAGE_NAME)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
if (!success) onFailure.run()
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@@ -235,30 +274,10 @@ object AppMigration {
|
|||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
val onFailure = Runnable {
|
if (!restoreApp(activity)) {
|
||||||
dialog.dismiss()
|
|
||||||
activity.toast(R.string.failure, Toast.LENGTH_LONG)
|
activity.toast(R.string.failure, Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
val apk = StubApk.current(activity)
|
dialog.dismiss()
|
||||||
val session = APKInstall.startSession(activity, APP_PACKAGE_NAME, onFailure) {
|
|
||||||
Config.suManager = ""
|
|
||||||
Shell.cmd("touch $AppApkPath").exec()
|
|
||||||
launchApp(activity, APP_PACKAGE_NAME)
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
val cmd = "adb_pm_install $apk $APP_PACKAGE_NAME"
|
|
||||||
if (Shell.cmd(cmd).await().isSuccess) return
|
|
||||||
val success = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
apk.inputStream().copyAndClose(session.openStream(activity))
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Timber.e(e)
|
|
||||||
return@withContext false
|
|
||||||
}
|
|
||||||
session.waitIntent()?.let { activity.startActivity(it) } ?: return@withContext false
|
|
||||||
return@withContext true
|
|
||||||
}
|
|
||||||
if (!success) onFailure.run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun upgradeStub(context: Context, apk: File): Intent? {
|
suspend fun upgradeStub(context: Context, apk: File): Intent? {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import com.topjohnwu.magisk.core.Const
|
|||||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
import com.topjohnwu.magisk.core.ktx.writeTo
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
||||||
import com.topjohnwu.magisk.core.utils.unzip
|
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -47,20 +46,14 @@ open class FlashZip(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isValid = try {
|
try {
|
||||||
zipFile.unzip(installDir, "META-INF/com/google/android", true)
|
val binary = File(installDir, "update-binary")
|
||||||
val script = File(installDir, "updater-script")
|
AppContext.assets.open("module_installer.sh").use { it.writeTo(binary) }
|
||||||
script.readText().contains("#MAGISK")
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
console.add("! Unzip error")
|
console.add("! Unzip error")
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValid) {
|
|
||||||
console.add("! This zip is not a Magisk module!")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
console.add("- Installing ${mUri.displayName}")
|
console.add("- Installing ${mUri.displayName}")
|
||||||
|
|
||||||
return Shell.cmd("sh $installDir/update-binary dummy 1 \'$zipFile\'")
|
return Shell.cmd("sh $installDir/update-binary dummy 1 \'$zipFile\'")
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import com.topjohnwu.magisk.core.Info
|
|||||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
import com.topjohnwu.magisk.core.ktx.copyAll
|
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.ktx.writeTo
|
||||||
import com.topjohnwu.magisk.core.utils.DummyList
|
import com.topjohnwu.magisk.core.utils.DummyList
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
|
||||||
@@ -47,7 +46,6 @@ import java.io.OutputStream
|
|||||||
import java.io.PushbackInputStream
|
import java.io.PushbackInputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.Arrays
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
@@ -191,7 +189,8 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
return true
|
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) {
|
private class NoAvailableStream(s: InputStream) : FilterInputStream(s) {
|
||||||
// Make sure available is never called on the actual stream and always return 0
|
// Make sure available is never called on the actual stream and always return 0
|
||||||
@@ -225,8 +224,8 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
console.add("- Processing tar file")
|
console.add("- Processing tar file")
|
||||||
var entry: TarArchiveEntry? = tarIn.nextEntry
|
var entry: TarArchiveEntry? = tarIn.nextEntry
|
||||||
|
|
||||||
fun TarArchiveEntry.decompressedStream(): InputStream {
|
fun decompressedStream(): InputStream {
|
||||||
val stream = if (name.endsWith(".lz4"))
|
val stream = if (tarIn.currentEntry.name.endsWith(".lz4"))
|
||||||
FramedLZ4CompressorInputStream(tarIn, true) else tarIn
|
FramedLZ4CompressorInputStream(tarIn, true) else tarIn
|
||||||
return NoAvailableStream(stream)
|
return NoAvailableStream(stream)
|
||||||
}
|
}
|
||||||
@@ -252,9 +251,9 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
|
|
||||||
if (bootItem != null) {
|
if (bootItem != null) {
|
||||||
console.add("-- Extracting: ${bootItem.name}")
|
console.add("-- Extracting: ${bootItem.name}")
|
||||||
entry.decompressedStream().copyAndCloseOut(bootItem.file.newOutputStream())
|
decompressedStream().copyAndCloseOut(bootItem.file.newOutputStream())
|
||||||
} else if (entry.name.contains("vbmeta.img")) {
|
} 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
|
// Valid vbmeta.img should be at least 256 bytes
|
||||||
if (rawData.size < 256)
|
if (rawData.size < 256)
|
||||||
continue
|
continue
|
||||||
@@ -287,7 +286,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
} else {
|
} else {
|
||||||
console.add("-- Copying : ${entry.name}")
|
console.add("-- Copying : ${entry.name}")
|
||||||
tarOut.putArchiveEntry(entry)
|
tarOut.putArchiveEntry(entry)
|
||||||
tarIn.copyAll(tarOut, bufferSize = 1024 * 1024)
|
tarIn.copyAll(tarOut)
|
||||||
tarOut.closeArchiveEntry()
|
tarOut.closeArchiveEntry()
|
||||||
}
|
}
|
||||||
entry = tarIn.nextEntry ?: break
|
entry = tarIn.nextEntry ?: break
|
||||||
@@ -429,7 +428,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
|
|
||||||
// Process input file
|
// Process input file
|
||||||
try {
|
try {
|
||||||
PushbackInputStream(uri.inputStream(), 512).use { src ->
|
PushbackInputStream(uri.inputStream().buffered(1024 * 1024), 512).use { src ->
|
||||||
val head = ByteArray(512)
|
val head = ByteArray(512)
|
||||||
if (src.read(head) != head.size) {
|
if (src.read(head) != head.size) {
|
||||||
console.add("! Invalid input file")
|
console.add("! Invalid input file")
|
||||||
@@ -438,12 +437,13 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
src.unread(head)
|
src.unread(head)
|
||||||
|
|
||||||
val magic = head.copyOf(4)
|
val magic = head.copyOf(4)
|
||||||
val tarMagic = Arrays.copyOfRange(head, 257, 262)
|
val tarMagic = head.copyOfRange(257, 262)
|
||||||
|
|
||||||
srcBoot = if (tarMagic.contentEquals("ustar".toByteArray())) {
|
srcBoot = if (tarMagic.contentEquals("ustar".toByteArray())) {
|
||||||
// tar file
|
// tar file
|
||||||
outFile = MediaStoreUtils.getFile("$destName.tar")
|
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.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
||||||
it.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
it.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
||||||
}
|
}
|
||||||
@@ -500,7 +500,7 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
bootItem.file = newBoot
|
bootItem.file = newBoot
|
||||||
bootItem.copyTo(outStream as TarArchiveOutputStream)
|
bootItem.copyTo(outStream as TarArchiveOutputStream)
|
||||||
} else {
|
} else {
|
||||||
newBoot.newInputStream().copyAndClose(outStream)
|
newBoot.newInputStream().use { it.copyAll(outStream, 1024 * 1024) }
|
||||||
}
|
}
|
||||||
newBoot.delete()
|
newBoot.delete()
|
||||||
|
|
||||||
@@ -514,6 +514,8 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
outFile.delete()
|
outFile.delete()
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
return false
|
return false
|
||||||
|
} finally {
|
||||||
|
outStream.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix up binaries
|
// Fix up binaries
|
||||||
@@ -597,7 +599,9 @@ abstract class MagiskInstallImpl protected constructor(
|
|||||||
if (result)
|
if (result)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
Shell.cmd("rm -rf $installDir").submit()
|
// Not every operation initializes installDir
|
||||||
|
if (::installDir.isInitialized)
|
||||||
|
Shell.cmd("rm -rf $installDir").submit()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.core.tasks
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.core.net.toFile
|
|
||||||
import com.topjohnwu.magisk.core.AppContext
|
|
||||||
import com.topjohnwu.magisk.core.Const
|
|
||||||
import com.topjohnwu.magisk.core.ktx.writeTo
|
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
|
|
||||||
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.inputStream
|
|
||||||
import com.topjohnwu.magisk.core.utils.unzip
|
|
||||||
import com.topjohnwu.superuser.Shell
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileNotFoundException
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
open class RunAction(
|
|
||||||
private val module: String,
|
|
||||||
private val console: MutableList<String>,
|
|
||||||
private val logs: MutableList<String>
|
|
||||||
) {
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private suspend fun run(): Boolean {
|
|
||||||
return Shell.cmd("run_action \'$module\'").to(console, logs).exec().isSuccess
|
|
||||||
}
|
|
||||||
|
|
||||||
open suspend fun exec() = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
if (!run()) {
|
|
||||||
console.add("! Run action failed")
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Timber.e(e)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,7 +29,7 @@ class AXML(b: ByteArray) {
|
|||||||
* Followed by an array of uint32_t with size = number of strings
|
* Followed by an array of uint32_t with size = number of strings
|
||||||
* Each entry points to an offset into the string data
|
* Each entry points to an offset into the string data
|
||||||
*/
|
*/
|
||||||
fun patchStrings(patchFn: (Array<String>) -> Unit): Boolean {
|
fun patchStrings(mapFn: (String) -> String): Boolean {
|
||||||
val buffer = ByteBuffer.wrap(bytes).order(LITTLE_ENDIAN)
|
val buffer = ByteBuffer.wrap(bytes).order(LITTLE_ENDIAN)
|
||||||
|
|
||||||
fun findStringPool(): Int {
|
fun findStringPool(): Int {
|
||||||
@@ -65,7 +65,9 @@ class AXML(b: ByteArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val strArr = strList.toTypedArray()
|
val strArr = strList.toTypedArray()
|
||||||
patchFn(strArr)
|
for (i in strArr.indices) {
|
||||||
|
strArr[i] = mapFn(strArr[i])
|
||||||
|
}
|
||||||
|
|
||||||
// Write everything before string data, will patch values later
|
// Write everything before string data, will patch values later
|
||||||
val baos = RawByteStream()
|
val baos = RawByteStream()
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.topjohnwu.magisk.core.utils;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipUtil;
|
||||||
|
|
||||||
|
import java.nio.file.attribute.FileTime;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
|
public class Desugar {
|
||||||
|
public static FileTime getLastModifiedTime(ZipEntry entry) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
return entry.getLastModifiedTime();
|
||||||
|
} else {
|
||||||
|
return FileTime.fromMillis(entry.getTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileTime getLastAccessTime(ZipEntry entry) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
return entry.getLastAccessTime();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileTime getCreationTime(ZipEntry entry) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
return entry.getCreationTime();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Within {@link ZipArchiveOutputStream#copyFromZipInputStream}, we redirect the method call
|
||||||
|
* {@link ZipUtil#checkRequestedFeatures} to this method. This is safe because the only usage
|
||||||
|
* of copyFromZipInputStream is in {@link ZipArchiveOutputStream#addRawArchiveEntry},
|
||||||
|
* which does not need to actually understand the content of the zip entry. By removing
|
||||||
|
* this feature check, we can modify zip files using unsupported compression methods.
|
||||||
|
*/
|
||||||
|
public static void checkRequestedFeatures(final ZipArchiveEntry ze) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,13 +11,10 @@ import android.net.NetworkRequest
|
|||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import androidx.collection.ArraySet
|
import androidx.collection.ArraySet
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
import com.topjohnwu.magisk.core.ktx.registerRuntimeReceiver
|
import com.topjohnwu.magisk.core.ktx.registerRuntimeReceiver
|
||||||
|
|
||||||
class NetworkObserver(context: Context): DefaultLifecycleObserver {
|
class NetworkObserver(context: Context) {
|
||||||
private val manager = context.getSystemService<ConnectivityManager>()!!
|
private val manager = context.getSystemService<ConnectivityManager>()!!
|
||||||
|
|
||||||
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
|
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||||
@@ -55,16 +52,13 @@ class NetworkObserver(context: Context): DefaultLifecycleObserver {
|
|||||||
manager.registerNetworkCallback(request, networkCallback)
|
manager.registerNetworkCallback(request, networkCallback)
|
||||||
val filter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
|
val filter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
|
||||||
context.applicationContext.registerRuntimeReceiver(receiver, filter)
|
context.applicationContext.registerRuntimeReceiver(receiver, filter)
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart(owner: LifecycleOwner) {
|
fun postCurrentState() {
|
||||||
postCurrentState()
|
postValue(
|
||||||
}
|
manager.getNetworkCapabilities(manager.activeNetwork)
|
||||||
|
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) == true
|
||||||
private fun postCurrentState() {
|
)
|
||||||
postValue(manager.getNetworkCapabilities(manager.activeNetwork)
|
|
||||||
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ?: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postValue(b: Boolean) {
|
private fun postValue(b: Boolean) {
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.core.utils;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.lifecycle.LifecycleDispatcher;
|
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
|
||||||
|
|
||||||
// Use Java to bypass Kotlin internal visibility modifier
|
|
||||||
public class ProcessLifecycle {
|
|
||||||
public static void init(@NonNull Context context) {
|
|
||||||
LifecycleDispatcher.init(context);
|
|
||||||
ProcessLifecycleOwner.init$lifecycle_process_release(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package com.topjohnwu.magisk.test
|
||||||
|
|
||||||
|
import android.os.ParcelFileDescriptor.AutoCloseInputStream
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.uiautomator.By
|
||||||
|
import androidx.test.uiautomator.Until
|
||||||
|
import com.topjohnwu.magisk.core.model.module.LocalModule
|
||||||
|
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Assume.assumeTrue
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class AdditionalTest : BaseTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
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
|
||||||
|
fun teardown() {
|
||||||
|
device.pressHome()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModuleCount() {
|
||||||
|
var expected = 0
|
||||||
|
if (Environment.testModules()) expected +=2
|
||||||
|
if (Environment.lsposed()) expected++
|
||||||
|
if (Environment.shamiko()) expected++
|
||||||
|
assertEquals("Module count incorrect", expected, modules.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLsposed() {
|
||||||
|
assumeTrue(Environment.lsposed())
|
||||||
|
|
||||||
|
val module = modules.find { it.id == "zygisk_lsposed" }
|
||||||
|
assertNotNull("zygisk_lsposed is not installed", module)
|
||||||
|
module!!
|
||||||
|
assertFalse("zygisk_lsposed is not enabled", module.zygiskUnloaded)
|
||||||
|
|
||||||
|
// Launch lsposed manager to ensure the module is active
|
||||||
|
uiAutomation.executeShellCommand(
|
||||||
|
"am start -c $LSPOSED_CATEGORY $SHELL_PKG/.BugreportWarningActivity"
|
||||||
|
).let { pfd -> AutoCloseInputStream(pfd).use { it.readBytes() } }
|
||||||
|
|
||||||
|
val pattern = Pattern.compile("$LSPOSED_PKG:id/.*")
|
||||||
|
assertNotNull(
|
||||||
|
"LSPosed manager launch failed",
|
||||||
|
device.wait(Until.hasObject(By.res(pattern)), TimeUnit.SECONDS.toMillis(10))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModule01() {
|
||||||
|
assumeTrue(Environment.testModules())
|
||||||
|
|
||||||
|
val module = modules.find { it.id == "test_01" }
|
||||||
|
assertNotNull("test_01 is not installed", module)
|
||||||
|
assertTrue(
|
||||||
|
"/system/etc/newfile should exist",
|
||||||
|
RootUtils.fs.getFile("/system/etc/newfile").exists()
|
||||||
|
)
|
||||||
|
assertFalse(
|
||||||
|
"/system/bin/screenrecord should not exist",
|
||||||
|
RootUtils.fs.getFile("/system/bin/screenrecord").exists()
|
||||||
|
)
|
||||||
|
module!!
|
||||||
|
assertTrue("test_01 should be zygisk unloaded", module.zygiskUnloaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModule02() {
|
||||||
|
assumeTrue(Environment.testModules())
|
||||||
|
|
||||||
|
val module = modules.find { it.id == "test_02" }
|
||||||
|
assertNotNull("test_02 is not installed", module)
|
||||||
|
module!!
|
||||||
|
assertTrue("test_02 should be zygisk unloaded", module.zygiskUnloaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModule03() {
|
||||||
|
assumeTrue(Environment.testModules())
|
||||||
|
|
||||||
|
assertNull("test_03 should be removed", modules.find { it.id == "test_03" })
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/core/src/main/java/com/topjohnwu/magisk/test/BaseTest.kt
Normal file
27
app/core/src/main/java/com/topjohnwu/magisk/test/BaseTest.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package com.topjohnwu.magisk.test
|
||||||
|
|
||||||
|
import android.app.Instrumentation
|
||||||
|
import android.app.UiAutomation
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.uiautomator.UiDevice
|
||||||
|
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
|
||||||
|
interface BaseTest {
|
||||||
|
val instrumentation: Instrumentation
|
||||||
|
get() = InstrumentationRegistry.getInstrumentation()
|
||||||
|
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)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun prerequisite() {
|
||||||
|
assertTrue("Should have root access", Shell.getShell().isRoot)
|
||||||
|
// Make sure the root service is running
|
||||||
|
RootUtils.Connection.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
209
app/core/src/main/java/com/topjohnwu/magisk/test/Environment.kt
Normal file
209
app/core/src/main/java/com/topjohnwu/magisk/test/Environment.kt
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
package com.topjohnwu.magisk.test
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.os.Build
|
||||||
|
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.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.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class Environment : BaseTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun before() = BaseTest.prerequisite()
|
||||||
|
|
||||||
|
// The kernel running on emulators < API 26 does not play well with
|
||||||
|
// magic mount. Skip module tests on those legacy platforms.
|
||||||
|
fun testModules(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= 26
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lsposed(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT in 27..34
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shamiko(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= 27
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val MODULE_ERROR = "Module zip processing incorrect"
|
||||||
|
}
|
||||||
|
|
||||||
|
object TimberLog : CallbackList<String>(Runnable::run) {
|
||||||
|
override fun onAddElement(e: String) {
|
||||||
|
Timber.i(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkModuleZip(file: File) {
|
||||||
|
// Make sure module processing is correct
|
||||||
|
ZipFile.Builder().setFile(file).get().use { zip ->
|
||||||
|
val meta = zip.entries
|
||||||
|
.asSequence()
|
||||||
|
.filter { it.name.startsWith("META-INF") }
|
||||||
|
.toMutableList()
|
||||||
|
assertEquals(MODULE_ERROR, 6, meta.size)
|
||||||
|
|
||||||
|
val binary = zip.getInputStream(
|
||||||
|
zip.getEntry("META-INF/com/google/android/update-binary")
|
||||||
|
).use { it.readBytes() }
|
||||||
|
val ref = appContext.assets.open("module_installer.sh").use { it.readBytes() }
|
||||||
|
assertArrayEquals(MODULE_ERROR, ref, binary)
|
||||||
|
|
||||||
|
val script = zip.getInputStream(
|
||||||
|
zip.getEntry("META-INF/com/google/android/updater-script")
|
||||||
|
).use { it.readBytes() }
|
||||||
|
assertArrayEquals(MODULE_ERROR, "#MAGISK\n".toByteArray(), script)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupModule01(root: ExtendedFile) {
|
||||||
|
val error = "test_01 setup failed"
|
||||||
|
val path = root.getChildFile("test_01")
|
||||||
|
|
||||||
|
// Create /system/etc/newfile
|
||||||
|
val etc = path.getChildFile("system").getChildFile("etc")
|
||||||
|
assertTrue(error, etc.mkdirs())
|
||||||
|
assertTrue(error, etc.getChildFile("newfile").createNewFile())
|
||||||
|
|
||||||
|
// Delete /system/bin/screenrecord
|
||||||
|
val bin = path.getChildFile("system").getChildFile("bin")
|
||||||
|
assertTrue(error, bin.mkdirs())
|
||||||
|
assertTrue(error, Shell.cmd("mknod $bin/screenrecord c 0 0").exec().isSuccess)
|
||||||
|
|
||||||
|
// Create an empty zygisk folder
|
||||||
|
val module = LocalModule(path)
|
||||||
|
assertTrue(error, module.zygiskFolder.mkdir())
|
||||||
|
|
||||||
|
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupModule02(root: ExtendedFile) {
|
||||||
|
val error = "test_02 setup failed"
|
||||||
|
val path = root.getChildFile("test_02")
|
||||||
|
|
||||||
|
// Create invalid zygisk libraries
|
||||||
|
val module = LocalModule(path)
|
||||||
|
assertTrue(error, module.zygiskFolder.mkdirs())
|
||||||
|
assertTrue(error, module.zygiskFolder.getChildFile("armeabi-v7a.so").createNewFile())
|
||||||
|
assertTrue(error, module.zygiskFolder.getChildFile("arm64-v8a.so").createNewFile())
|
||||||
|
assertTrue(error, module.zygiskFolder.getChildFile("x86.so").createNewFile())
|
||||||
|
assertTrue(error, module.zygiskFolder.getChildFile("x86_64.so").createNewFile())
|
||||||
|
|
||||||
|
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupModule03(root: ExtendedFile) {
|
||||||
|
val error = "test_03 setup failed"
|
||||||
|
val path = root.getChildFile("test_03")
|
||||||
|
|
||||||
|
// Create a new module but mark is as "remove"
|
||||||
|
val module = LocalModule(path)
|
||||||
|
assertTrue(error, path.mkdirs())
|
||||||
|
assertTrue(error, path.getChildFile("service.sh").createNewFile())
|
||||||
|
module.remove = true
|
||||||
|
|
||||||
|
assertTrue(error, Shell.cmd("set_default_perm $path").exec().isSuccess)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setupEnvironment() {
|
||||||
|
runBlocking {
|
||||||
|
assertTrue(
|
||||||
|
"Magisk setup failed",
|
||||||
|
MagiskInstaller.Emulator(TimberLog, TimberLog).exec()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val notify = object : DownloadNotifier {
|
||||||
|
override val context = appContext
|
||||||
|
override fun notifyUpdate(id: Int, editor: (Notification.Builder) -> Unit) {}
|
||||||
|
}
|
||||||
|
val processor = DownloadProcessor(notify)
|
||||||
|
|
||||||
|
val shamiko = appContext.cachedFile("shamiko.zip")
|
||||||
|
runBlocking {
|
||||||
|
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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val lsp = appContext.cachedFile("lsposed.zip")
|
||||||
|
runBlocking {
|
||||||
|
testContext.assets.open("lsposed.zip").use {
|
||||||
|
processor.handleModule(it, lsp.toUri())
|
||||||
|
}
|
||||||
|
checkModuleZip(lsp)
|
||||||
|
if (lsposed()) {
|
||||||
|
assertTrue(
|
||||||
|
"LSPosed installation failed",
|
||||||
|
FlashZip(lsp.toUri(), TimberLog, TimberLog).exec()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testModules()) {
|
||||||
|
val root = RootUtils.fs.getFile(Const.MODULE_PATH)
|
||||||
|
setupModule01(root)
|
||||||
|
setupModule02(root)
|
||||||
|
setupModule03(root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setupAppHide() {
|
||||||
|
runBlocking {
|
||||||
|
assertTrue(
|
||||||
|
"App hiding failed",
|
||||||
|
AppMigration.patchAndHide(
|
||||||
|
context = appContext,
|
||||||
|
label = "Settings",
|
||||||
|
pkg = "repackaged.$APP_PACKAGE_NAME"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setupAppRestore() {
|
||||||
|
runBlocking {
|
||||||
|
assertTrue(
|
||||||
|
"App restoration failed",
|
||||||
|
AppMigration.restoreApp(appContext)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.topjohnwu.magisk.test
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.ParcelFileDescriptor.AutoCloseInputStream
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.topjohnwu.magisk.core.Config
|
||||||
|
import com.topjohnwu.magisk.core.Info
|
||||||
|
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||||
|
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class MagiskAppTest : BaseTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun before() = BaseTest.prerequisite()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testZygisk() {
|
||||||
|
assertTrue("Zygisk should be enabled", Info.isZygiskEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSuRequest() {
|
||||||
|
// Bypass the need to actually show a dialog
|
||||||
|
Config.suAutoResponse = Config.Value.SU_AUTO_ALLOW
|
||||||
|
Config.prefs.edit().commit()
|
||||||
|
|
||||||
|
// Inject an undetermined + mute logging policy for ADB shell
|
||||||
|
val policy = SuPolicy(
|
||||||
|
uid = 2000,
|
||||||
|
logging = false,
|
||||||
|
notification = false,
|
||||||
|
remain = 0L
|
||||||
|
)
|
||||||
|
runBlocking {
|
||||||
|
ServiceLocator.policyDB.update(policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
val filter = IntentFilter(Intent.ACTION_VIEW)
|
||||||
|
filter.addCategory(Intent.CATEGORY_DEFAULT)
|
||||||
|
val monitor = instrumentation.addMonitor(filter, null, false)
|
||||||
|
|
||||||
|
// Try to call su from ADB shell
|
||||||
|
val cmd = if (Build.VERSION.SDK_INT < 24) {
|
||||||
|
// API 23 runs executeShellCommand as root
|
||||||
|
"/system/xbin/su 2000 su -c id"
|
||||||
|
} else {
|
||||||
|
"su -c id"
|
||||||
|
}
|
||||||
|
val pfd = uiAutomation.executeShellCommand(cmd)
|
||||||
|
|
||||||
|
// Make sure SuRequestActivity is launched
|
||||||
|
val suRequest = monitor.waitForActivityWithTimeout(TimeUnit.SECONDS.toMillis(10))
|
||||||
|
assertNotNull("SuRequestActivity is not launched", suRequest)
|
||||||
|
|
||||||
|
// Check that the request went through
|
||||||
|
AutoCloseInputStream(pfd).reader().use {
|
||||||
|
assertTrue(
|
||||||
|
"Cannot grant root permission from shell",
|
||||||
|
it.readText().contains("uid=0")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the database is updated
|
||||||
|
runBlocking {
|
||||||
|
val policy = ServiceLocator.policyDB.fetch(2000)
|
||||||
|
?: throw AssertionError("PolicyDB is invalid")
|
||||||
|
assertEquals("Policy for shell is incorrect", SuPolicy.ALLOW, policy.policy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<string name="modules">Módulos</string>
|
<string name="modules">Módulos</string>
|
||||||
<string name="superuser">Superusuariu</string>
|
<string name="superuser">Superusuariu</string>
|
||||||
<string name="logs">Rexistru</string>
|
<string name="logs">Rexistru</string>
|
||||||
<string name="settings">Axustes</string>
|
<string name="settings">Configuración</string>
|
||||||
<string name="install">Instalar</string>
|
<string name="install">Instalar</string>
|
||||||
<string name="section_home">Aniciu</string>
|
<string name="section_home">Aniciu</string>
|
||||||
<string name="section_theme">Estilos</string>
|
<string name="section_theme">Estilos</string>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<string name="loading">Cargando…</string>
|
<string name="loading">Cargando…</string>
|
||||||
<string name="update">Anovar</string>
|
<string name="update">Anovar</string>
|
||||||
<string name="not_available">N/D</string>
|
<string name="not_available">N/D</string>
|
||||||
<string name="hide">Anubrir</string>
|
<string name="hide">Esconder</string>
|
||||||
<string name="home_package">Paquete</string>
|
<string name="home_package">Paquete</string>
|
||||||
<string name="home_app_title">Aplicación</string>
|
<string name="home_app_title">Aplicación</string>
|
||||||
<string name="home_notice_content">Baxa Magisk NAMÁS dende la páxina oficial de GitHub. ¡Los ficheros de fontes desconocíes puen ser maliciosos!</string>
|
<string name="home_notice_content">Baxa Magisk NAMÁS dende la páxina oficial de GitHub. ¡Los ficheros de fontes desconocíes puen ser maliciosos!</string>
|
||||||
@@ -39,10 +39,10 @@
|
|||||||
<string name="manager_download_install">Primi equí pa baxalu ya instalalu</string>
|
<string name="manager_download_install">Primi equí pa baxalu ya instalalu</string>
|
||||||
<string name="direct_install">Instalación direuta (aconséyase)</string>
|
<string name="direct_install">Instalación direuta (aconséyase)</string>
|
||||||
<string name="install_inactive_slot">Instalar na ralura inactiva (darréu del OTA)</string>
|
<string name="install_inactive_slot">Instalar na ralura inactiva (darréu del OTA)</string>
|
||||||
<string name="install_inactive_slot_msg">¡El preséu va arrincar OBLIGATORIAMENTE na ralura inactiva darréu de reaniciar!\nUsa esta opción namás dempués d\'acabar l\'anovamientu per OTA.\n¿Quies siguir?</string>
|
<string name="install_inactive_slot_msg">¡El preséu va arrincar OBLIGATORIAMENTE na ralura inactiva dempués de reaniciar!\nUsa esta opción namás dempués d\'acabar l\'anovamientu per OTA.\n¿Quies siguir?</string>
|
||||||
<string name="setup_title">Configuración adicional</string>
|
<string name="setup_title">Configuración adicional</string>
|
||||||
<string name="select_patch_file">Seleicionar y parchiar un ficheru</string>
|
<string name="select_patch_file">Seleicionar y parchiar un ficheru</string>
|
||||||
<string name="patch_file_msg">Seleiciona una imaxe en bruto (*.img) o un archivu d\'ODIN (*.tar)</string>
|
<string name="patch_file_msg">Seleiciona una imaxe en bruto (*.img), un archivu d\'ODIN (*.tar) o un ficheru payload.bin (*.bin)</string>
|
||||||
<string name="reboot_delay_toast">Reaniciando en 5 segundos…</string>
|
<string name="reboot_delay_toast">Reaniciando en 5 segundos…</string>
|
||||||
<string name="flash_screen_title">Instalación</string>
|
<string name="flash_screen_title">Instalación</string>
|
||||||
<!--Superuser-->
|
<!--Superuser-->
|
||||||
@@ -81,6 +81,9 @@
|
|||||||
<string name="logs_cleared">El rexistru borróse correutamente</string>
|
<string name="logs_cleared">El rexistru borróse correutamente</string>
|
||||||
<string name="pid">PID: %1$d</string>
|
<string name="pid">PID: %1$d</string>
|
||||||
<string name="target_uid">UID de destín: %1$d</string>
|
<string name="target_uid">UID de destín: %1$d</string>
|
||||||
|
<string name="target_pid">Mount ns target PID: %s</string>
|
||||||
|
<string name="selinux_context">Contestu de SELinux: %s</string>
|
||||||
|
<string name="supp_group">Grupu suplementariu: %s</string>
|
||||||
<!--SafetyNet-->
|
<!--SafetyNet-->
|
||||||
<!--MagiskHide-->
|
<!--MagiskHide-->
|
||||||
<string name="show_system_app">Aplicaciones del sistema</string>
|
<string name="show_system_app">Aplicaciones del sistema</string>
|
||||||
@@ -94,15 +97,19 @@
|
|||||||
<string name="reboot_bootloader">Reaniciar al cargador d\'arrinque</string>
|
<string name="reboot_bootloader">Reaniciar al cargador d\'arrinque</string>
|
||||||
<string name="reboot_download">Reaniciar al mou de descarga</string>
|
<string name="reboot_download">Reaniciar al mou de descarga</string>
|
||||||
<string name="reboot_edl">Reaniciar al mou EDL</string>
|
<string name="reboot_edl">Reaniciar al mou EDL</string>
|
||||||
|
<string name="reboot_safe_mode">Mou seguru</string>
|
||||||
<string name="module_version_author">%1$s por %2$s</string>
|
<string name="module_version_author">%1$s por %2$s</string>
|
||||||
<string name="module_state_remove">Quitar</string>
|
<string name="module_state_remove">Quitar</string>
|
||||||
|
<string name="module_action">Aición</string>
|
||||||
<string name="module_state_restore">Restaurar</string>
|
<string name="module_state_restore">Restaurar</string>
|
||||||
<string name="module_action_install_external">Instalar dende l\'almacenamientu</string>
|
<string name="module_action_install_external">Instalar dende l\'almacenamientu</string>
|
||||||
<string name="update_available">Hai un anovamientu disponible</string>
|
<string name="update_available">Hai un anovamientu disponible</string>
|
||||||
<string name="suspend_text_riru">Suspendióse\'l módulu porque s\'activó «%1$s»</string>
|
<string name="suspend_text_riru">Suspendióse\'l módulu porque s\'activó «%1$s»</string>
|
||||||
<string name="suspend_text_zygisk">Suspendióse\'l módulu porque nun s\'activó «%1$s»</string>
|
<string name="suspend_text_zygisk">Suspendióse\'l módulu porque nun s\'activó «%1$s»</string>
|
||||||
<string name="zygisk_module_unloaded">El módulu de Zygisk nun cargó pola mor d\'haber incompatibilidaes</string>
|
<string name="zygisk_module_unloaded">El módulu de Zygisk nun cargó por haber incompatibilidaes</string>
|
||||||
<string name="module_empty">Nun hai nengún módulu instaláu</string>
|
<string name="module_empty">Nun hai nengún módulu instaláu</string>
|
||||||
|
<string name="confirm_install">¿Quies instalar el módulu «%1$s»?</string>
|
||||||
|
<string name="confirm_install_title">Confirmación de la instalación</string>
|
||||||
<!--Settings-->
|
<!--Settings-->
|
||||||
<string name="settings_dark_mode_title">Mou del estilu</string>
|
<string name="settings_dark_mode_title">Mou del estilu</string>
|
||||||
<string name="settings_dark_mode_message">¡Seleiciona\'l mou que meyor s\'adaute al to estilu!</string>
|
<string name="settings_dark_mode_message">¡Seleiciona\'l mou que meyor s\'adaute al to estilu!</string>
|
||||||
@@ -111,7 +118,7 @@
|
|||||||
<string name="settings_dark_mode_dark">Escuridá</string>
|
<string name="settings_dark_mode_dark">Escuridá</string>
|
||||||
<string name="settings_download_path_title">Camín de les descargues</string>
|
<string name="settings_download_path_title">Camín de les descargues</string>
|
||||||
<string name="settings_download_path_message">Los ficheros van guardase en «%1$s»</string>
|
<string name="settings_download_path_message">Los ficheros van guardase en «%1$s»</string>
|
||||||
<string name="settings_hide_app_title">Anubrir Magisk</string>
|
<string name="settings_hide_app_title">Esconder Magisk</string>
|
||||||
<string name="settings_hide_app_summary">Instala una aplicación intermedia con una ID y una etiqueta al debalu</string>
|
<string name="settings_hide_app_summary">Instala una aplicación intermedia con una ID y una etiqueta al debalu</string>
|
||||||
<string name="settings_restore_app_title">Restaurar el mou visible</string>
|
<string name="settings_restore_app_title">Restaurar el mou visible</string>
|
||||||
<string name="settings_restore_app_summary">Fai que l\'aplicación orixinal vuelva ser visible</string>
|
<string name="settings_restore_app_summary">Fai que l\'aplicación orixinal vuelva ser visible</string>
|
||||||
@@ -149,14 +156,19 @@
|
|||||||
<string name="auto_response">Rempuesta automática</string>
|
<string name="auto_response">Rempuesta automática</string>
|
||||||
<string name="request_timeout">Tiempu d\'espera de les solicitúes</string>
|
<string name="request_timeout">Tiempu d\'espera de les solicitúes</string>
|
||||||
<string name="superuser_notification">Avisu de superusuariu</string>
|
<string name="superuser_notification">Avisu de superusuariu</string>
|
||||||
<string name="settings_su_reauth_title">Volver autenticar darréu d\'anovar</string>
|
<string name="settings_su_reauth_title">Volver autenticar dempués d\'anovar</string>
|
||||||
<string name="settings_su_reauth_summary">Vuelve pidir los permisos de superusuariu dempués d\'anovar les aplicaciones</string>
|
<string name="settings_su_reauth_summary">Vuelve pidir los permisos de superusuariu dempués d\'anovar les aplicaciones</string>
|
||||||
<string name="settings_su_tapjack_title">Proteición escontra\'l tapjacking</string>
|
<string name="settings_su_tapjack_title">Proteición escontra\'l tapjacking</string>
|
||||||
<string name="settings_su_tapjack_summary">El diálogu de concesión de permisos de superusuariu nun respuende a la entrada mentanto lu torgue otra ventana o superposición</string>
|
<string name="settings_su_tapjack_summary">El diálogu de concesión de permisos de superusuariu nun respuende a la entrada mentanto lu torgue otra ventana o superposición</string>
|
||||||
|
<string name="settings_su_auth_title">Autenticación d\'usuariu</string>
|
||||||
|
<string name="settings_su_auth_summary">Pide l\'autenticación demientres les solicitúes de superusuariu</string>
|
||||||
|
<string name="settings_su_auth_insecure">Nun se configuró nengún métodu d\'autenticación nel preséu</string>
|
||||||
<string name="settings_customization">Personalización</string>
|
<string name="settings_customization">Personalización</string>
|
||||||
<string name="setting_add_shortcut_summary">Amiesta un atayu a la pantalla d\'aniciu en casu de que\'l nome y l\'iconu seyan difíciles de reconocer darréu d\'anubrir l\'aplicación</string>
|
<string name="setting_add_shortcut_summary">Amiesta un atayu a la pantalla d\'aniciu en casu de que\'l nome y l\'iconu seyan difíciles de reconocer dempués d\'esconder l\'aplicación</string>
|
||||||
<string name="settings_doh_title">DNS per HTTPS</string>
|
<string name="settings_doh_title">DNS per HTTPS</string>
|
||||||
<string name="settings_doh_description">Una igua alternativa pal envelenamientu de DNS en dalgunos países</string>
|
<string name="settings_doh_description">Una igua alternativa pal envelenamientu de DNS en dalgunos países</string>
|
||||||
|
<string name="settings_random_name_title">Nome de la salida aleatoriu</string>
|
||||||
|
<string name="settings_random_name_description">Fai que\'l nome de ficheru de la salida de les imáxenes parchiaes y los ficheros tar seya aleatoriu pa impidir la deteición</string>
|
||||||
<string name="multiuser_mode">Mou de multiusuariu</string>
|
<string name="multiuser_mode">Mou de multiusuariu</string>
|
||||||
<string name="settings_owner_only">Namás el propietariu del preséu</string>
|
<string name="settings_owner_only">Namás el propietariu del preséu</string>
|
||||||
<string name="settings_owner_manage">El propietariu xestionáu del preséu</string>
|
<string name="settings_owner_manage">El propietariu xestionáu del preséu</string>
|
||||||
@@ -186,11 +198,14 @@
|
|||||||
<string name="repo_install_title">Instalación de: %1$s %2$s (%3$d)</string>
|
<string name="repo_install_title">Instalación de: %1$s %2$s (%3$d)</string>
|
||||||
<string name="download">Baxar</string>
|
<string name="download">Baxar</string>
|
||||||
<string name="reboot">Reaniciar</string>
|
<string name="reboot">Reaniciar</string>
|
||||||
|
<string name="close">Zarrar</string>
|
||||||
<string name="release_notes">Notes de la versión</string>
|
<string name="release_notes">Notes de la versión</string>
|
||||||
<string name="flashing">Flaxando…</string>
|
<string name="flashing">Flaxando…</string>
|
||||||
|
<string name="running">Executando…</string>
|
||||||
<string name="done">¡Fecho!</string>
|
<string name="done">¡Fecho!</string>
|
||||||
|
<string name="done_action">Completóse l\'aición de: %1$s</string>
|
||||||
<string name="failure">¡Falló!</string>
|
<string name="failure">¡Falló!</string>
|
||||||
<string name="hide_app_title">Anubriendo l\'aplicación Magisk…</string>
|
<string name="hide_app_title">Escondiendo l\'aplicación Magisk…</string>
|
||||||
<string name="open_link_failed_toast">Nun s\'atopó nenguna aplicación p\'abrir l\'enllaz</string>
|
<string name="open_link_failed_toast">Nun s\'atopó nenguna aplicación p\'abrir l\'enllaz</string>
|
||||||
<string name="complete_uninstall">Desinstalar dafechu</string>
|
<string name="complete_uninstall">Desinstalar dafechu</string>
|
||||||
<string name="restore_img">Restaurar les imáxenes</string>
|
<string name="restore_img">Restaurar les imáxenes</string>
|
||||||
@@ -200,6 +215,7 @@
|
|||||||
<string name="setup_fail">La configuración falló</string>
|
<string name="setup_fail">La configuración falló</string>
|
||||||
<string name="env_fix_title">Configuración adicional</string>
|
<string name="env_fix_title">Configuración adicional</string>
|
||||||
<string name="env_fix_msg">El preséu precisa una configuración adicional pa que Magisk funcione afayadizamente. ¿Quies siguir y reaniciar?</string>
|
<string name="env_fix_msg">El preséu precisa una configuración adicional pa que Magisk funcione afayadizamente. ¿Quies siguir y reaniciar?</string>
|
||||||
|
<string name="env_full_fix_msg">El preséu precisa volver flaxar Magisk pa que funcione afayadizamente. Volvi instalar Magisk dientro de l\'aplicación porque\'l mou de recuperación nun pue consiguir la información correuta del preséu.</string>
|
||||||
<string name="setup_msg">Executando la configuración del entornu…</string>
|
<string name="setup_msg">Executando la configuración del entornu…</string>
|
||||||
<string name="unsupport_magisk_title">Versión non compatible</string>
|
<string name="unsupport_magisk_title">Versión non compatible</string>
|
||||||
<string name="unsupport_magisk_msg">Esta versión de l\'aplicación nun ye compatible coles versiones de Magisk anteriores a la %1$s.\n\nL\'aplicación va comportase como si Magisk nun tuviere instaláu, anueva Magisk namás que puedas.</string>
|
<string name="unsupport_magisk_msg">Esta versión de l\'aplicación nun ye compatible coles versiones de Magisk anteriores a la %1$s.\n\nL\'aplicación va comportase como si Magisk nun tuviere instaláu, anueva Magisk namás que puedas.</string>
|
||||||
@@ -207,12 +223,13 @@
|
|||||||
<string name="unsupport_system_app_msg">Esta aplicación nun se pue executar nel espaciu del sistema. Volvi instalala mas nel espaciu del usuariu.</string>
|
<string name="unsupport_system_app_msg">Esta aplicación nun se pue executar nel espaciu del sistema. Volvi instalala mas nel espaciu del usuariu.</string>
|
||||||
<string name="unsupport_other_su_msg">Detectóse un binariu «su» que nun ye de Magisk. Quita cualesquier solución de root y/o volvi instalar Magisk.</string>
|
<string name="unsupport_other_su_msg">Detectóse un binariu «su» que nun ye de Magisk. Quita cualesquier solución de root y/o volvi instalar Magisk.</string>
|
||||||
<string name="unsupport_external_storage_msg">Magisk ta instaláu nel almacenamientu esternu. Movi l\'aplicación al almacenamientu internu, por favor.</string>
|
<string name="unsupport_external_storage_msg">Magisk ta instaláu nel almacenamientu esternu. Movi l\'aplicación al almacenamientu internu, por favor.</string>
|
||||||
<string name="unsupport_nonroot_stub_msg">Magisk nun pue siguir funcionando nel mou anubríu darréu que se perdió\'l root. Restaura\'l mou visible de l\'aplicación.</string>
|
<string name="unsupport_nonroot_stub_msg">Magisk nun pue siguir funcionando nel mou escondíu darréu que se perdió\'l root. Restaura\'l mou visible de l\'aplicación.</string>
|
||||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||||
<string name="external_rw_permission_denied">Concede\'l permisu d\'almacenamientu p\'activar esta funcionalidá</string>
|
<string name="external_rw_permission_denied">Concede\'l permisu d\'almacenamientu p\'activar esta funcionalidá</string>
|
||||||
|
<string name="post_notifications_denied">Concede\'l permisu de los avisos p\'activar esta función</string>
|
||||||
<string name="install_unknown_denied">Permite la instalación d\'aplicaciones desconocíes p\'activar esta funcionalidá</string>
|
<string name="install_unknown_denied">Permite la instalación d\'aplicaciones desconocíes p\'activar esta funcionalidá</string>
|
||||||
<string name="add_shortcut_title">Amestar un atayu a la pantalla d\'aniciu</string>
|
<string name="add_shortcut_title">Amestar un atayu a la pantalla d\'aniciu</string>
|
||||||
<string name="add_shortcut_msg">Darréu d\'anubrir esta aplicación, el so nome ya iconu van ser difíciles de reconocer. ¿Quies amestar un atayu a la pantalla d\'aniciu?</string>
|
<string name="add_shortcut_msg">Dempués d\'esconder esta aplicación, el so nome ya iconu van ser difíciles de reconocer. ¿Quies amestar un atayu a la pantalla d\'aniciu?</string>
|
||||||
<string name="app_not_found">Nun s\'atopó nenguna aplicación pa remanar esta aición</string>
|
<string name="app_not_found">Nun s\'atopó nenguna aplicación pa remanar esta aición</string>
|
||||||
<string name="reboot_apply_change">Reanicia p\'aplicar los cambeos</string>
|
<string name="reboot_apply_change">Reanicia p\'aplicar los cambeos</string>
|
||||||
<string name="restore_app_confirmation">Esta aición va restaurar l\'aplicación orixinal y desanicia la intermedia. ¿De xuru que quies facelo?</string>
|
<string name="restore_app_confirmation">Esta aición va restaurar l\'aplicación orixinal y desanicia la intermedia. ¿De xuru que quies facelo?</string>
|
||||||
|
|||||||
@@ -10,10 +10,9 @@
|
|||||||
<string name="section_theme">עיצוב</string>
|
<string name="section_theme">עיצוב</string>
|
||||||
<string name="denylist">רשימת דחייה</string>
|
<string name="denylist">רשימת דחייה</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Home-->
|
<!--Home-->
|
||||||
<string name="no_connection">אין חיבור זמין</string>
|
<string name="no_connection">אין חיבור זמין</string>
|
||||||
<string name="app_changelog">רשימת שינויים</string>
|
<string name="app_changelog">יומן שינויים</string>
|
||||||
<string name="loading">טוען…</string>
|
<string name="loading">טוען…</string>
|
||||||
<string name="update">עדכון</string>
|
<string name="update">עדכון</string>
|
||||||
<string name="not_available">ל/ז</string>
|
<string name="not_available">ל/ז</string>
|
||||||
@@ -45,16 +44,16 @@
|
|||||||
<string name="install_inactive_slot_msg">ההתקן שלך ייאלץ אתחול לחריץ הלא פעיל הנוכחי שלך לאחר הפעלה מחדש!\nיש להשתמש באפשרות זו רק לאחר ביצוע OTA בלבד.\nלהמשיך?</string>
|
<string name="install_inactive_slot_msg">ההתקן שלך ייאלץ אתחול לחריץ הלא פעיל הנוכחי שלך לאחר הפעלה מחדש!\nיש להשתמש באפשרות זו רק לאחר ביצוע OTA בלבד.\nלהמשיך?</string>
|
||||||
<string name="setup_title">התקנה נוספת</string>
|
<string name="setup_title">התקנה נוספת</string>
|
||||||
<string name="select_patch_file">בחירה והתקנת קובץ</string>
|
<string name="select_patch_file">בחירה והתקנת קובץ</string>
|
||||||
<string name="patch_file_msg">בחירת תמונה גולמית (*.img) או ODIN קובץ tar (*.tar)</string>
|
<string name="patch_file_msg">בחירת תמונה גולמית (*.img) או ODIN tarfile (*.tar) או payload.bin (*.bin)</string>
|
||||||
<string name="reboot_delay_toast">מאתחל בעוד 5 שניות…</string>
|
<string name="reboot_delay_toast">מאתחל בעוד 5 שניות…</string>
|
||||||
<string name="flash_screen_title">התקנה</string>
|
<string name="flash_screen_title">התקנה</string>
|
||||||
|
|
||||||
<!--Superuser-->
|
<!--Superuser-->
|
||||||
<string name="su_request_title">בקשות משתמש על</string>
|
<string name="su_request_title">בקשות משתמש על</string>
|
||||||
<string name="touch_filtered_warning">מכיוון שיישום מסתיר בקשה של משתמש על, Magisk לא יכול לאמת את תגובתך</string>
|
<string name="touch_filtered_warning">מכיוון שיישום מסתיר בקשה של משתמש על, Magisk לא יכול לאמת את תגובתך</string>
|
||||||
<string name="deny">דחה</string>
|
<string name="deny">דחייה</string>
|
||||||
<string name="prompt">מיידי</string>
|
<string name="prompt">מיידי</string>
|
||||||
<string name="grant">הענק</string>
|
<string name="grant">הענקה</string>
|
||||||
<string name="su_warning">מעניק גישה מלאה להתקן שלך.\nיש לדחות באי וודאות!</string>
|
<string name="su_warning">מעניק גישה מלאה להתקן שלך.\nיש לדחות באי וודאות!</string>
|
||||||
<string name="forever">לצמיתות</string>
|
<string name="forever">לצמיתות</string>
|
||||||
<string name="once">פעם אחת</string>
|
<string name="once">פעם אחת</string>
|
||||||
@@ -62,24 +61,24 @@
|
|||||||
<string name="twentymin">20 דקות</string>
|
<string name="twentymin">20 דקות</string>
|
||||||
<string name="thirtymin">חצי שעה</string>
|
<string name="thirtymin">חצי שעה</string>
|
||||||
<string name="sixtymin">שעה</string>
|
<string name="sixtymin">שעה</string>
|
||||||
<string name="su_allow_toast">%1$s הוענקו הרשאות משתמש עבור</string>
|
<string name="su_allow_toast">%1$s קיבל הרשאות משתמש על</string>
|
||||||
<string name="su_deny_toast">%1$s נשללו הרשאות משתמש עבור</string>
|
<string name="su_deny_toast">%1$s נשללו הרשאות משתמש על</string>
|
||||||
<string name="su_snack_grant">הרשאות משתמש על עבור %1$s הוענקו</string>
|
<string name="su_snack_grant">הרשאות משתמש על עבור %1$s הוענקו</string>
|
||||||
<string name="su_snack_deny">הרשאות משתמש על עבור %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_on">התראות של %1$s מופעלות</string>
|
||||||
<string name="su_snack_notif_off">התראות עבור %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_on">יומני רישום עבור %1$s פועלות</string>
|
||||||
<string name="su_snack_log_off">יומני רישום עבור %1$s כבויות</string>
|
<string name="su_snack_log_off">יומני רישום עבור %1$s מושבתות</string>
|
||||||
<string name="su_revoke_title">להסיר?</string>
|
<string name="su_revoke_title">להסיר?</string>
|
||||||
<string name="su_revoke_msg">נא לאשר שלילת הרשאות עבור %1$s?</string>
|
<string name="su_revoke_msg">נא לאשר שלילת הרשאות עבור %1$s?</string>
|
||||||
<string name="toast">הרמת כוסית</string>
|
<string name="toast">הרמת כוסית</string>
|
||||||
<string name="none">ללא</string>
|
<string name="none">ללא</string>
|
||||||
<string name="superuser_toggle_notification">התראות</string>
|
<string name="superuser_toggle_notification">התראות</string>
|
||||||
<string name="superuser_toggle_revoke">הסרה</string>
|
<string name="superuser_toggle_revoke">הסרה</string>
|
||||||
<string name="superuser_policy_none">לא נתבקשו הרשאות משתמש על על ידי שום יישום</string>
|
<string name="superuser_policy_none">טרם נתבקשו הרשאות משתמש על על ידי יישומים</string>
|
||||||
|
|
||||||
<!--Logs-->
|
<!--Logs-->
|
||||||
<string name="log_data_none">הינך ללא יומן רישום, יש לנסות להשתמש ביישומים מותאמים יותר למשתמש העל שלך</string>
|
<string name="log_data_none">הינך ללא יומן, יש לנסות להשתמש יותר ביישומי השורש שלך</string>
|
||||||
<string name="log_data_magisk_none">יומני רישום Magisk ריקים, זה מוזר</string>
|
<string name="log_data_magisk_none">יומני רישום Magisk ריקים, זה מוזר</string>
|
||||||
<string name="menuSaveLog">שמירת יומן רישום</string>
|
<string name="menuSaveLog">שמירת יומן רישום</string>
|
||||||
<string name="menuClearLog">ניקוי יומן רישום כעת</string>
|
<string name="menuClearLog">ניקוי יומן רישום כעת</string>
|
||||||
@@ -92,7 +91,7 @@
|
|||||||
|
|
||||||
<!--SafetyNet-->
|
<!--SafetyNet-->
|
||||||
|
|
||||||
<!-- MagiskHide -->
|
<!--MagiskHide-->
|
||||||
<string name="show_system_app">הצגת יישומי מערכת</string>
|
<string name="show_system_app">הצגת יישומי מערכת</string>
|
||||||
<string name="show_os_app">הצגת יישומי מערכת הפעלה</string>
|
<string name="show_os_app">הצגת יישומי מערכת הפעלה</string>
|
||||||
<string name="hide_filter_hint">סינון לפי שם</string>
|
<string name="hide_filter_hint">סינון לפי שם</string>
|
||||||
@@ -100,14 +99,16 @@
|
|||||||
|
|
||||||
<!--Module-->
|
<!--Module-->
|
||||||
<string name="no_info_provided">(לא סופק מידע)</string>
|
<string name="no_info_provided">(לא סופק מידע)</string>
|
||||||
<string name="reboot_userspace">אתחול מהיר</string>
|
<string name="reboot_userspace">אתחול רך</string>
|
||||||
<string name="reboot_recovery">אתחול למצב שחזור</string>
|
<string name="reboot_recovery">אתחול למצב שחזור</string>
|
||||||
<string name="reboot_bootloader">אתחול מצב מנהל האתחול</string>
|
<string name="reboot_bootloader">אתחול לטוען האתחול</string>
|
||||||
<string name="reboot_download">אתחול מצב הורדה</string>
|
<string name="reboot_download">אתחול למצב הורדה</string>
|
||||||
<string name="reboot_edl">אתחול למצב EDL</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_version_author">%1$s מאת %2$s</string>
|
||||||
<string name="module_state_remove">הסרה</string>
|
<string name="module_state_remove">הסרה</string>
|
||||||
<string name="module_state_restore">שיחזור</string>
|
<string name="module_action">פעולה</string>
|
||||||
|
<string name="module_state_restore">שחזור</string>
|
||||||
<string name="module_action_install_external">התקנה מהאחסון</string>
|
<string name="module_action_install_external">התקנה מהאחסון</string>
|
||||||
<string name="update_available">עדכונים זמינים</string>
|
<string name="update_available">עדכונים זמינים</string>
|
||||||
<string name="suspend_text_riru">מודול מושעה כי %1$s מופעל</string>
|
<string name="suspend_text_riru">מודול מושעה כי %1$s מופעל</string>
|
||||||
@@ -117,7 +118,7 @@
|
|||||||
<string name="confirm_install">להתקין מודול %1$s?</string>
|
<string name="confirm_install">להתקין מודול %1$s?</string>
|
||||||
<string name="confirm_install_title">אישור התקנה</string>
|
<string name="confirm_install_title">אישור התקנה</string>
|
||||||
|
|
||||||
<!--Settings -->
|
<!--Settings-->
|
||||||
<string name="settings_dark_mode_title">מצב עיצוב</string>
|
<string name="settings_dark_mode_title">מצב עיצוב</string>
|
||||||
<string name="settings_dark_mode_message">נא לבחור מצב המתאים ביותר לסגנון שלך!</string>
|
<string name="settings_dark_mode_message">נא לבחור מצב המתאים ביותר לסגנון שלך!</string>
|
||||||
<string name="settings_dark_mode_light">תמיד בהיר</string>
|
<string name="settings_dark_mode_light">תמיד בהיר</string>
|
||||||
@@ -128,11 +129,11 @@
|
|||||||
<string name="settings_hide_app_title">הסתרת היישום Magisk</string>
|
<string name="settings_hide_app_title">הסתרת היישום Magisk</string>
|
||||||
<string name="settings_hide_app_summary">התקנת יישום מתווך עם מזהה חבילה אקראי ותווית שם מותאמת אישית</string>
|
<string name="settings_hide_app_summary">התקנת יישום מתווך עם מזהה חבילה אקראי ותווית שם מותאמת אישית</string>
|
||||||
<string name="settings_restore_app_title">שיחזור היישום Magisk</string>
|
<string name="settings_restore_app_title">שיחזור היישום Magisk</string>
|
||||||
<string name="settings_restore_app_summary">יש לבטל את הסתרת היישום ולשחזור אותו ל-APK המקורי</string>
|
<string name="settings_restore_app_summary">ביטול הסתרת היישום ושחזור אל ה-APK המקורי</string>
|
||||||
<string name="language">שפה</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_title">בדיקת עדכונים</string>
|
||||||
<string name="settings_check_update_summary">בדוק מעת לעת ברקע אם יש עדכונים</string>
|
<string name="settings_check_update_summary">בדיקה מעת לעת ברקע אם יש עדכונים</string>
|
||||||
<string name="settings_update_channel_title">ערוץ עדכון</string>
|
<string name="settings_update_channel_title">ערוץ עדכון</string>
|
||||||
<string name="settings_update_stable">יציב</string>
|
<string name="settings_update_stable">יציב</string>
|
||||||
<string name="settings_update_beta">בטא</string>
|
<string name="settings_update_beta">בטא</string>
|
||||||
@@ -161,12 +162,12 @@
|
|||||||
<string name="settings_su_request_60">60 שניות</string>
|
<string name="settings_su_request_60">60 שניות</string>
|
||||||
<string name="superuser_access">גישת משתמש על</string>
|
<string name="superuser_access">גישת משתמש על</string>
|
||||||
<string name="auto_response">תגובה אוטומטית</string>
|
<string name="auto_response">תגובה אוטומטית</string>
|
||||||
<string name="request_timeout">בקש פסק זמן</string>
|
<string name="request_timeout">בקשת פסק זמן</string>
|
||||||
<string name="superuser_notification">התראות משתמש על</string>
|
<string name="superuser_notification">התראות משתמש על</string>
|
||||||
<string name="settings_su_reauth_title">אימות מחדש לאחר שדרוג</string>
|
<string name="settings_su_reauth_title">אימות מחדש לאחר שדרוג</string>
|
||||||
<string name="settings_su_reauth_summary">אימות מחדש הרשאות של משתמש על לאחר שדרוג יישום</string>
|
<string name="settings_su_reauth_summary">אימות מחדש הרשאות של משתמש על לאחר שדרוג יישום</string>
|
||||||
<string name="settings_su_tapjack_title">הפעלת הגנת Tapjacking</string>
|
<string name="settings_su_tapjack_title">הגנת Tapjacking</string>
|
||||||
<string name="settings_su_tapjack_summary">תיבת הדו שיח של משתמש העל לא תגיב לקלט כשהיא מוסתרת על ידי חלון או כיסוי אחר</string>
|
<string name="settings_su_tapjack_summary">תיבת הדו שיח של משתמש העל לא תגיב לקלט כשהיא מוסתרת על ידי חלון או שכבת על אחרת</string>
|
||||||
<string name="settings_su_auth_title">אימות משתמש</string>
|
<string name="settings_su_auth_title">אימות משתמש</string>
|
||||||
<string name="settings_su_auth_summary">בקשת אימות משתמש במהלך בקשות משתמש על</string>
|
<string name="settings_su_auth_summary">בקשת אימות משתמש במהלך בקשות משתמש על</string>
|
||||||
<string name="settings_su_auth_insecure">לא מוגדרת שיטת אימות בהתקן</string>
|
<string name="settings_su_auth_insecure">לא מוגדרת שיטת אימות בהתקן</string>
|
||||||
@@ -174,6 +175,8 @@
|
|||||||
<string name="setting_add_shortcut_summary">הוספת קיצור דרך יפה במסך הבית למקרה שקשה לזהות את השם ואת הסמל לאחר הסתרת היישום</string>
|
<string name="setting_add_shortcut_summary">הוספת קיצור דרך יפה במסך הבית למקרה שקשה לזהות את השם ואת הסמל לאחר הסתרת היישום</string>
|
||||||
<string name="settings_doh_title">DNS על HTTPS</string>
|
<string name="settings_doh_title">DNS על HTTPS</string>
|
||||||
<string name="settings_doh_description">עקיפת DNS מורעל במדינות מסוימות</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="multiuser_mode">מצב מרובה משתמשים</string>
|
||||||
<string name="settings_owner_only">בעל ההתקן בלבד</string>
|
<string name="settings_owner_only">בעל ההתקן בלבד</string>
|
||||||
<string name="settings_owner_manage">אחראי ניהול ההתקן</string>
|
<string name="settings_owner_manage">אחראי ניהול ההתקן</string>
|
||||||
@@ -205,10 +208,13 @@
|
|||||||
<string name="repo_install_title">מתקין %1$s %2$s(%3$d)</string>
|
<string name="repo_install_title">מתקין %1$s %2$s(%3$d)</string>
|
||||||
<string name="download">הורדה</string>
|
<string name="download">הורדה</string>
|
||||||
<string name="reboot">הפעלה מחדש</string>
|
<string name="reboot">הפעלה מחדש</string>
|
||||||
|
<string name="close">סגירה</string>
|
||||||
<string name="release_notes">הערות שחרור</string>
|
<string name="release_notes">הערות שחרור</string>
|
||||||
<string name="flashing">צורב…</string>
|
<string name="flashing">צורב…</string>
|
||||||
|
<string name="running">רץ…</string>
|
||||||
<string name="done">בוצע!</string>
|
<string name="done">בוצע!</string>
|
||||||
<string name="failure">נכשל</string>
|
<string name="done_action">בוצעה ריצת פעולה של %1$s</string>
|
||||||
|
<string name="failure">נכשל!</string>
|
||||||
<string name="hide_app_title">מסתיר את יישום Magisk…</string>
|
<string name="hide_app_title">מסתיר את יישום Magisk…</string>
|
||||||
<string name="open_link_failed_toast">לא נמצאו יישומים לפתיחת קישור זה</string>
|
<string name="open_link_failed_toast">לא נמצאו יישומים לפתיחת קישור זה</string>
|
||||||
<string name="complete_uninstall">הסרה מלאה</string>
|
<string name="complete_uninstall">הסרה מלאה</string>
|
||||||
@@ -237,4 +243,5 @@
|
|||||||
<string name="app_not_found">לא נמצא יישום לטיפול בפעולה זו</string>
|
<string name="app_not_found">לא נמצא יישום לטיפול בפעולה זו</string>
|
||||||
<string name="reboot_apply_change">ייש להפעיל מחדש כדי להחיל שינויים</string>
|
<string name="reboot_apply_change">ייש להפעיל מחדש כדי להחיל שינויים</string>
|
||||||
<string name="restore_app_confirmation">פעולה זו תשחזר את היישום המוסתר חזרה ליישום המקורי. האם בוודאות ברצונך לעשות את זה?</string>
|
<string name="restore_app_confirmation">פעולה זו תשחזר את היישום המוסתר חזרה ליישום המקורי. האם בוודאות ברצונך לעשות את זה?</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
252
app/core/src/main/res/values-ku/strings.xml
Normal file
252
app/core/src/main/res/values-ku/strings.xml
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<!--Sections-->
|
||||||
|
<string name="modules">زیادکراوەکان</string>
|
||||||
|
<string name="superuser">سوپەر یوسەر</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>
|
||||||
|
|
||||||
|
<!--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="hide">شاردنەوە</string>
|
||||||
|
<string name="home_package">پاکێج</string>
|
||||||
|
<string name="home_app_title">ئەپ</string>
|
||||||
|
|
||||||
|
<string name="home_notice_content">تەنها لە گیتهەبی فەرمی ماجیسک دابگرە، لە شوێنی تر لەوانەیە زیانبەخش بێت</string>
|
||||||
|
<string name="home_support_title">پشتگیریمان بکە</string>
|
||||||
|
<string name="home_follow_title">شوێنمان بکەوە</string>
|
||||||
|
<string name="home_item_source">سەرچاوە</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">سڕینەوەی ماجیسک</string>
|
||||||
|
<string name="uninstall_magisk_msg">هەموو زیادکراوەکان دەسڕێنەوە، ڕۆت دەسڕێتەوە، و هەر ڕەمزێنراوێک بەهۆی ماجیسک کرابێ لادەچێت!</string>
|
||||||
|
|
||||||
|
<!--Install-->
|
||||||
|
<string name="keep_force_encryption">ڕەمزاندنی بەزۆر بهێڵەوە</string>
|
||||||
|
<string name="keep_dm_verity">بهێڵەوە AVB 2.0/dm-verity</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="direct_install">داگرتنی ڕاستەوخۆ(پێشنیارکراوە)</string>
|
||||||
|
<string name="install_inactive_slot">دایبگرە بۆ خانەی ناچالاک(پاش OTA)</string>
|
||||||
|
<string name="install_inactive_slot_msg">ئێستا ئامێرەکەت دەچێتە خانە ناچالاکەکە، ئەمە بەکاربهێنە تەنها دوای ئەپدەیت کردن لە ڕێگەی OTA، بەردەوام دەبیت؟</string>
|
||||||
|
<string name="setup_title">ڕێکخستنی زیاتر</string>
|
||||||
|
<string name="select_patch_file">فایلێک هەڵبژێرە و پینەی بکە</string>
|
||||||
|
<string name="patch_file_msg">تکایە فایلێکی Tar یان img یان payload.bin هەڵبژێرە</string>
|
||||||
|
<string name="reboot_delay_toast">ڕێستارت کردنەوە لە ماوەی ٥ چرکە…</string>
|
||||||
|
<string name="flash_screen_title">ڕێکخستن</string>
|
||||||
|
|
||||||
|
<!--Superuser-->
|
||||||
|
<string name="su_request_title">داواکاری سوپەریوسەر</string>
|
||||||
|
<string name="touch_filtered_warning">ئەپێک لە سەر شاشەکەیە، ناتوانین دڵنیا بینەوە</string>
|
||||||
|
<string name="deny">ڕەتکردنەوە</string>
|
||||||
|
<string name="prompt">داواکاری</string>
|
||||||
|
<string name="grant">ڕێگەپێدان</string>
|
||||||
|
<string name="su_warning">ڕێگەپێدان بۆ تەواوی ئامێرەکەت، گەر دڵنیا نیت ڕەتی بکەوە</string>
|
||||||
|
<string name="forever">بۆ هەمیشە</string>
|
||||||
|
<string name="once">بۆ یەکجار</string>
|
||||||
|
<string name="tenmin">بۆ ١٠ خولەک</string>
|
||||||
|
<string name="twentymin">بۆ ٢٠ خولەک</string>
|
||||||
|
<string name="thirtymin">بۆ ٣٠ خولەک</string>
|
||||||
|
<string name="sixtymin">بۆ ٦٠ خولەک</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_revoke_title">لابردن؟</string>
|
||||||
|
<string name="su_revoke_msg">دڵنیابەوە بۆ لابردنی سوپەریوسەر بۆ %1$s </string>
|
||||||
|
<string name="toast">هێنانەسەر</string>
|
||||||
|
<string name="none">هیچ</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">تۆمارەکانی ماجیسک بەتاڵن، باشە بۆ؟</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 target PID: %s</string>
|
||||||
|
<string name="selinux_context">SELinux context: %s</string>
|
||||||
|
<string name="supp_group">Supplementary 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">چوونە ناو بووتلۆدەر</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 by %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">شاردنەوەی ئەپی ماجیسک</string>
|
||||||
|
<string name="settings_hide_app_summary">داگرتنی ماجیسک بە ناوی جیاوە</string>
|
||||||
|
<string name="settings_restore_app_title">ئەپە ڕەسەنەکە بهێنەوە</string>
|
||||||
|
<string name="settings_restore_app_summary">ئەپەکە دەربخەوە و ڕەسەنڵ</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">بەستەرێکی تایبەت دابنێ</string>
|
||||||
|
<string name="settings_zygisk_summary">کارپێکردنی بەشێکی ماجیسک لە zygote daemon</string>
|
||||||
|
<string name="settings_denylist_title">پێڕستی نەرێنی کراوەکان</string>
|
||||||
|
<string name="settings_denylist_summary">هەر ئەپێک لە پێڕستی نەرێنییەکان کاریگەریەکانی ماجیسکی لەسەر نییە</string>
|
||||||
|
<string name="settings_denylist_config_title">دەستکاریکردنی پێڕستی نەرێنیکراوەکان</string>
|
||||||
|
<string name="settings_denylist_config_summary">ئەو ئەپە هەڵبژێرە کە دەتەوێت نەرێنیی بکەیت</string>
|
||||||
|
<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_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">پارێزگاری کردن لە ئەگەری دەستلێدانی تر</string>
|
||||||
|
<string name="settings_su_tapjack_summary"> کاتێک ئەپێکی تر بەسەر شاشەکەوەیە، سوپەر یوسەر وەڵام ناداتەوە لە کردنی هەر بژاردەیەک لەبەر پارێزراوی</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">Workaround DNS خراپە لە هەندێک شوێن</string>
|
||||||
|
<string name="settings_random_name_title">ناوێک لەخۆیەوە</string>
|
||||||
|
<string name="settings_random_name_description">دانانی ناوێک لەخۆوە تاوەکوو ئاشکرا نەبێت</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>
|
||||||
|
|
||||||
|
<!--Notifications-->
|
||||||
|
<string name="update_channel">نوێکردنەوەکانی ماجیسک</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">وەشانی نوێی ماجیسک ئامادەیە!</string>
|
||||||
|
<string name="updated_title">ماجیسک نوێکراوە!</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">نێبینییەکان</string>
|
||||||
|
<string name="flashing">فلاش کردن</string>
|
||||||
|
<string name="running">کار کردن...</string>
|
||||||
|
<string name="done">تەواو!</string>
|
||||||
|
<string name="done_action">کارکردنی %1$s تەواو بوو</string>
|
||||||
|
<string name="failure">Failed!</string>
|
||||||
|
<string name="hide_app_title">شاردنەوەی ئەپی ماجیسک…</string>
|
||||||
|
<string name="open_link_failed_toast">هیچ ئەپێک تییە تا لینکەکەی پێ بکرێتەوە</string>
|
||||||
|
<string name="complete_uninstall">سڕینەوەی تەواوی</string>
|
||||||
|
<string name="restore_img">هێنانەوەی 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">مۆبایلەکەت پێویستی بە ڕێکخستنی زیاترە، ئایا ئەتەوێت بەردەوام بیت و ڕێستارتی بکەیتەوە؟</string>
|
||||||
|
<string name="env_full_fix_msg"> پێویستە دوبارە ماجیسک دابگریتەوە بۆ ئەوەی بەباشی کاربکات تکایە ماجیسک دابگرەوە لەناو ئەپەکە خۆی چونکە لە ڕیکەڤەرییەوە ناتوانرێ زانیاری تەواو لەسەر ئامێرەکە دەستبخرێت </string>
|
||||||
|
<string name="setup_msg">دەستپێکردن....</string>
|
||||||
|
<string name="unsupport_magisk_title">وەشانی ماجیسک پاڵپشتینەکراوە</string>
|
||||||
|
<string name="unsupport_magisk_msg">وەشانی ماجیسکەکەت زۆر کۆنە وەک ئەوە وایە هەر نەبێت، تکایە نوێی بکەوە بە زوترین کات</string>
|
||||||
|
<string name="unsupport_general_title">باری نائاسایی</string>
|
||||||
|
<string name="unsupport_system_app_msg">ئەم ئەپە وەکو ئەپی سیستەم کارناکات، تکایە بیگۆڕەوە بۆ ئەپی ئاسایی</string>
|
||||||
|
<string name="unsupport_other_su_msg"> \"su\" binary یەکی بێگانە دۆزرایەوە، تکایە جگە لە ماجیسک ئەپی تر بەکارمەهێنە بۆ ڕۆت کردن </string>
|
||||||
|
<string name="unsupport_external_storage_msg">ماجیسک لە بیرگەی دەرەکی داگیراوە، تکایە بیبەوە بۆ ناوەکی</string>
|
||||||
|
<string name="unsupport_nonroot_stub_msg">ئەپە شاراوەکە کار ناکات چونکە ڕۆتەکە نەماوە، تکایە ئەپە ڕەسەنەکە بگەڕێنەوە</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">ڕەزامەندی "install unknown apps" تاوەکوو کار بکات</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>
|
||||||
|
|
||||||
@@ -87,6 +87,9 @@
|
|||||||
<string name="logs_cleared">Логи успешно очищены</string>
|
<string name="logs_cleared">Логи успешно очищены</string>
|
||||||
<string name="pid">PID: %1$d</string>
|
<string name="pid">PID: %1$d</string>
|
||||||
<string name="target_uid">Целевой UID: %1$d</string>
|
<string name="target_uid">Целевой UID: %1$d</string>
|
||||||
|
<string name="target_pid">Целевой PID пространства имён: %s</string>
|
||||||
|
<string name="selinux_context">Контекст SELinux: %s</string>
|
||||||
|
<string name="supp_group">Дополнительная группа: %s</string>
|
||||||
|
|
||||||
<!--SafetyNet-->
|
<!--SafetyNet-->
|
||||||
|
|
||||||
@@ -164,11 +167,16 @@
|
|||||||
<string name="settings_su_reauth_title">Повторная аутентификация</string>
|
<string name="settings_su_reauth_title">Повторная аутентификация</string>
|
||||||
<string name="settings_su_reauth_summary">Повторный запрос прав суперпользователя после обновления приложений</string>
|
<string name="settings_su_reauth_summary">Повторный запрос прав суперпользователя после обновления приложений</string>
|
||||||
<string name="settings_su_tapjack_title">Защита от перехвата нажатий</string>
|
<string name="settings_su_tapjack_title">Защита от перехвата нажатий</string>
|
||||||
|
<string name="settings_su_auth_title">Аутентификация пользователя</string>
|
||||||
|
<string name="settings_su_auth_summary">Требовать аутентификацию пользователя при запросах Superuser</string>
|
||||||
|
<string name="settings_su_auth_insecure">На устройстве не настроен метод аутентификации</string>
|
||||||
<string name="settings_su_tapjack_summary">Окно запроса прав суперпользователя будет неактивно пока активированы наложения экрана</string>
|
<string name="settings_su_tapjack_summary">Окно запроса прав суперпользователя будет неактивно пока активированы наложения экрана</string>
|
||||||
<string name="settings_customization">Персонализация</string>
|
<string name="settings_customization">Персонализация</string>
|
||||||
<string name="setting_add_shortcut_summary">Добавить ярлык на рабочий стол для удобного восприятия приложения после его скрытия</string>
|
<string name="setting_add_shortcut_summary">Добавить ярлык на рабочий стол для удобного восприятия приложения после его скрытия</string>
|
||||||
<string name="settings_doh_title">DNS поверх HTTPS</string>
|
<string name="settings_doh_title">DNS поверх HTTPS</string>
|
||||||
<string name="settings_doh_description">Активировать DoH (используйте при проблемах с подключением к сети)</string>
|
<string name="settings_doh_description">Активировать DoH (используйте при проблемах с подключением к сети)</string>
|
||||||
|
<string name="settings_random_name_title">Случайное имя образа</string>
|
||||||
|
<string name="settings_random_name_description">Генерировать случайные имена для патченных образов и tar-файлов для предотвращения обнаружения</string>
|
||||||
|
|
||||||
<string name="multiuser_mode">Многопользовательский режим</string>
|
<string name="multiuser_mode">Многопользовательский режим</string>
|
||||||
<string name="settings_owner_only">Только администратор</string>
|
<string name="settings_owner_only">Только администратор</string>
|
||||||
|
|||||||
@@ -130,8 +130,8 @@
|
|||||||
<string name="settings_update_custom">Власний</string>
|
<string name="settings_update_custom">Власний</string>
|
||||||
<string name="settings_update_custom_msg">Вставте власний URL</string>
|
<string name="settings_update_custom_msg">Вставте власний URL</string>
|
||||||
<string name="settings_zygisk_summary">Запускати частини Magisk в сервісі zygote</string>
|
<string name="settings_zygisk_summary">Запускати частини Magisk в сервісі zygote</string>
|
||||||
<string name="settings_denylist_title">Enforce DenyList</string>
|
<string name="settings_denylist_title">Увімкнути DenyList</string>
|
||||||
<string name="settings_denylist_summary">Processes on the denylist will have all Magisk modifications reverted</string>
|
<string name="settings_denylist_summary">Всі зміни, внесені Magisk, будуть приховані від процесів, позначених у DenyList</string>
|
||||||
<string name="settings_denylist_config_title">Налаштувати DenyList</string>
|
<string name="settings_denylist_config_title">Налаштувати DenyList</string>
|
||||||
<string name="settings_denylist_config_summary">Вибрати процеси, які будуть додані до denylist</string>
|
<string name="settings_denylist_config_summary">Вибрати процеси, які будуть додані до denylist</string>
|
||||||
<string name="settings_hosts_title">Позасистемні хости</string>
|
<string name="settings_hosts_title">Позасистемні хости</string>
|
||||||
@@ -156,7 +156,7 @@
|
|||||||
<string name="superuser_notification">Сповіщення суперкористувача</string>
|
<string name="superuser_notification">Сповіщення суперкористувача</string>
|
||||||
<string name="settings_su_reauth_title">Повторна автентифікація</string>
|
<string name="settings_su_reauth_title">Повторна автентифікація</string>
|
||||||
<string name="settings_su_reauth_summary">Перевидача прав суперкористувача після оновлення застосунку</string>
|
<string name="settings_su_reauth_summary">Перевидача прав суперкористувача після оновлення застосунку</string>
|
||||||
<string name="settings_su_tapjack_title">Увімкнути захист від Tapjack</string>
|
<string name="settings_su_tapjack_title">Увімкнути захист від підміни натискань</string>
|
||||||
<string name="settings_su_tapjack_summary">Діалогове вікно суперкористувача не буде отримувати ввід від користувача, коли вікно перекрито іншим застосунком чи вікном</string>
|
<string name="settings_su_tapjack_summary">Діалогове вікно суперкористувача не буде отримувати ввід від користувача, коли вікно перекрито іншим застосунком чи вікном</string>
|
||||||
<string name="settings_customization">Оформлення</string>
|
<string name="settings_customization">Оформлення</string>
|
||||||
<string name="setting_add_shortcut_summary">Додати ярлик на домашній екран для зручного сприйняття застосунку після його приховування</string>
|
<string name="setting_add_shortcut_summary">Додати ярлик на домашній екран для зручного сприйняття застосунку після його приховування</string>
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ public class ProviderInstaller {
|
|||||||
|
|
||||||
private static final String GMS_PACKAGE_NAME = "com.google.android.gms";
|
private static final String GMS_PACKAGE_NAME = "com.google.android.gms";
|
||||||
|
|
||||||
public static boolean install(Context context) {
|
public static void install(Context context) {
|
||||||
try {
|
try {
|
||||||
// Check if gms is a system app
|
// Check if gms is a system app
|
||||||
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(GMS_PACKAGE_NAME, 0);
|
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(GMS_PACKAGE_NAME, 0);
|
||||||
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try installing new SSL provider from Google Play Service
|
// Try installing new SSL provider from Google Play Service
|
||||||
@@ -22,9 +22,7 @@ public class ProviderInstaller {
|
|||||||
.loadClass("com.google.android.gms.common.security.ProviderInstallerImpl")
|
.loadClass("com.google.android.gms.common.security.ProviderInstallerImpl")
|
||||||
.getMethod("insertProvider", Context.class)
|
.getMethod("insertProvider", Context.class)
|
||||||
.invoke(null, gms);
|
.invoke(null, gms);
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) {
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import dalvik.system.BaseDexClassLoader;
|
|||||||
public class DynamicClassLoader extends BaseDexClassLoader {
|
public class DynamicClassLoader extends BaseDexClassLoader {
|
||||||
|
|
||||||
public DynamicClassLoader(File apk) {
|
public DynamicClassLoader(File apk) {
|
||||||
this(apk, getSystemClassLoader());
|
this(apk, DynamicClassLoader.class.getClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicClassLoader(File apk, ClassLoader parent) {
|
public DynamicClassLoader(File apk, ClassLoader parent) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ android {
|
|||||||
val canary = !Config.version.contains(".")
|
val canary = !Config.version.contains(".")
|
||||||
|
|
||||||
val url = if (canary) null
|
val url = if (canary) null
|
||||||
else "https://cdn.jsdelivr.net/gh/topjohnwu/magisk-files@${Config.version}/app-release.apk"
|
else "https://github.com/topjohnwu/Magisk/releases/download/v${Config.version}/Magisk-v${Config.version}.apk"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.topjohnwu.magisk"
|
applicationId = "com.topjohnwu.magisk"
|
||||||
@@ -27,9 +27,9 @@ android {
|
|||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
proguardFiles("proguard-rules.pro")
|
||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
isShrinkResources = false
|
isShrinkResources = false
|
||||||
proguardFiles("proguard-rules.pro")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setupStub()
|
setupStubApk()
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":app:shared"))
|
implementation(project(":app:shared"))
|
||||||
|
|||||||
7
app/stub/src/main/res/values-ku/strings.xml
Normal file
7
app/stub/src/main/res/values-ku/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="upgrade_msg">ماجیسکەکەت بەرزبکەوە بۆ وەشانی تەواوەتی، دەتەوێت دایبگریت و ڕێکیبخەیت؟</string>
|
||||||
|
<string name="no_internet_msg">تکایە پەیوەست ببە بە ئینتەرنێتەوە، پێویستە ماجیسکەکەت ڕێک بخەیت.</string>
|
||||||
|
<string name="dling">داگرتن</string>
|
||||||
|
<string name="relaunch_app">تکایە دووبارە ئەپەکە بکەوە</string>
|
||||||
|
</resources>
|
||||||
1
app/test/.gitignore
vendored
Normal file
1
app/test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
30
app/test/build.gradle.kts
Normal file
30
app/test/build.gradle.kts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
kotlin("android")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.topjohnwu.magisk.test"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.topjohnwu.magisk.test"
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
proguardFile("proguard-rules.pro")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTestApk()
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.test.runner)
|
||||||
|
implementation(libs.test.rules)
|
||||||
|
implementation(libs.test.junit)
|
||||||
|
implementation(libs.test.uiautomator)
|
||||||
|
}
|
||||||
13
app/test/proguard-rules.pro
vendored
Normal file
13
app/test/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Keep all test dependencies
|
||||||
|
-keep class org.junit.** { *; }
|
||||||
|
-keep class androidx.test.** { *; }
|
||||||
|
|
||||||
|
# Make sure the classloader constructor is kept
|
||||||
|
-keepclassmembers class com.topjohnwu.magisk.test.TestClassLoader { <init>(); }
|
||||||
|
|
||||||
|
# Repackage dependencies
|
||||||
|
-repackageclasses 'deps'
|
||||||
|
-allowaccessmodification
|
||||||
|
|
||||||
|
# Keep attributes for stacktrace
|
||||||
|
-keepattributes *
|
||||||
23
app/test/src/main/AndroidManifest.xml
Normal file
23
app/test/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
|
||||||
|
<queries tools:node="removeAll" />
|
||||||
|
|
||||||
|
<application tools:node="replace">
|
||||||
|
<uses-library android:name="android.test.runner" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
<instrumentation
|
||||||
|
android:name="com.topjohnwu.magisk.test.AppTestRunner"
|
||||||
|
android:targetPackage="com.topjohnwu.magisk" />
|
||||||
|
|
||||||
|
<instrumentation
|
||||||
|
android:name="com.topjohnwu.magisk.test.TestRunner"
|
||||||
|
android:targetPackage="com.topjohnwu.magisk.test" />
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.topjohnwu.magisk.test
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.os.ParcelFileDescriptor.AutoCloseInputStream
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class AppMigrationTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val APP_PKG = "com.topjohnwu.magisk"
|
||||||
|
private const val STUB_PKG = "repackaged.$APP_PKG"
|
||||||
|
private const val RECEIVER_TIMEOUT = 20L
|
||||||
|
}
|
||||||
|
|
||||||
|
private val instrumentation get() = InstrumentationRegistry.getInstrumentation()
|
||||||
|
private val context get() = instrumentation.context
|
||||||
|
private val uiAutomation get() = instrumentation.uiAutomation
|
||||||
|
private val registeredReceivers = mutableListOf<BroadcastReceiver>()
|
||||||
|
|
||||||
|
class PackageRemoveMonitor(
|
||||||
|
context: Context,
|
||||||
|
private val packageName: String
|
||||||
|
) : BroadcastReceiver() {
|
||||||
|
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
|
||||||
|
init {
|
||||||
|
val filter = IntentFilter(Intent.ACTION_PACKAGE_REMOVED)
|
||||||
|
filter.addDataScheme("package")
|
||||||
|
context.registerReceiver(this, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
if (intent.action != Intent.ACTION_PACKAGE_REMOVED)
|
||||||
|
return
|
||||||
|
val data = intent.data ?: return
|
||||||
|
val pkg = data.schemeSpecificPart
|
||||||
|
if (pkg == packageName) latch.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
registeredReceivers.forEach(context::unregisterReceiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testAppMigration(pkg: String, method: String) {
|
||||||
|
val receiver = PackageRemoveMonitor(context, pkg)
|
||||||
|
registeredReceivers.add(receiver)
|
||||||
|
|
||||||
|
// Trigger the test to run migration
|
||||||
|
val pfd = uiAutomation.executeShellCommand(
|
||||||
|
"am instrument -w --user 0 -e class .Environment#$method " +
|
||||||
|
"$pkg.test/${AppTestRunner::class.java.name}"
|
||||||
|
)
|
||||||
|
val output = AutoCloseInputStream(pfd).reader().use { it.readText() }
|
||||||
|
assertTrue("$method failed, inst out: $output", output.contains("OK ("))
|
||||||
|
|
||||||
|
// Wait for migration to complete
|
||||||
|
assertTrue(
|
||||||
|
"$pkg uninstallation failed",
|
||||||
|
receiver.latch.await(RECEIVER_TIMEOUT, TimeUnit.SECONDS)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAppHide() {
|
||||||
|
testAppMigration(APP_PKG, "setupAppHide")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAppRestore() {
|
||||||
|
testAppMigration(STUB_PKG, "setupAppRestore")
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/test/src/main/java/com/topjohnwu/magisk/test/Runners.kt
Normal file
35
app/test/src/main/java/com/topjohnwu/magisk/test/Runners.kt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package com.topjohnwu.magisk.test
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.runner.AndroidJUnitRunner
|
||||||
|
|
||||||
|
open class TestRunner : AndroidJUnitRunner() {
|
||||||
|
override fun onCreate(arguments: Bundle) {
|
||||||
|
// Support short-hand ".ClassName"
|
||||||
|
arguments.getString("class")?.let {
|
||||||
|
val classArg = it.split(",").joinToString(separator = ",") { clz ->
|
||||||
|
if (clz.startsWith(".")) {
|
||||||
|
"com.topjohnwu.magisk.test$clz"
|
||||||
|
} else {
|
||||||
|
clz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arguments.putString("class", classArg)
|
||||||
|
}
|
||||||
|
super.onCreate(arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppTestRunner : TestRunner() {
|
||||||
|
override fun onCreate(arguments: Bundle) {
|
||||||
|
// Force using the target context's classloader to run tests
|
||||||
|
arguments.putString("classLoader", TestClassLoader::class.java.name)
|
||||||
|
super.onCreate(arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val targetClassLoader inline get() =
|
||||||
|
InstrumentationRegistry.getInstrumentation().targetContext.classLoader
|
||||||
|
|
||||||
|
class TestClassLoader : ClassLoader(targetClassLoader)
|
||||||
242
build.py
242
build.py
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
|
import copy
|
||||||
import glob
|
import glob
|
||||||
import lzma
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
@@ -56,14 +56,6 @@ if is_windows:
|
|||||||
if not sys.version_info >= (3, 8):
|
if not sys.version_info >= (3, 8):
|
||||||
error("Requires Python 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()
|
cpu_count = multiprocessing.cpu_count()
|
||||||
os_name = platform.system().lower()
|
os_name = platform.system().lower()
|
||||||
|
|
||||||
@@ -79,17 +71,6 @@ default_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
|||||||
support_targets = default_targets | {"resetprop"}
|
support_targets = default_targets | {"resetprop"}
|
||||||
rust_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
rust_targets = {"magisk", "magiskinit", "magiskboot", "magiskpolicy"}
|
||||||
|
|
||||||
# Common paths
|
|
||||||
ndk_root = sdk_path / "ndk"
|
|
||||||
ndk_path = ndk_root / "magisk"
|
|
||||||
ndk_build = ndk_path / "ndk-build"
|
|
||||||
rust_bin = ndk_path / "toolchains" / "rust" / "bin"
|
|
||||||
llvm_bin = ndk_path / "toolchains" / "llvm" / "prebuilt" / f"{os_name}-x86_64" / "bin"
|
|
||||||
cargo = rust_bin / "cargo"
|
|
||||||
gradlew = Path.cwd() / "gradlew"
|
|
||||||
adb_path = sdk_path / "platform-tools" / "adb"
|
|
||||||
native_gen_path = Path("native", "out", "generated").resolve()
|
|
||||||
|
|
||||||
# Global vars
|
# Global vars
|
||||||
config = {}
|
config = {}
|
||||||
args = {}
|
args = {}
|
||||||
@@ -161,10 +142,6 @@ def cmd_out(cmds: list):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def xz(data):
|
|
||||||
return lzma.compress(data, preset=9, check=lzma.CHECK_NONE)
|
|
||||||
|
|
||||||
|
|
||||||
###############
|
###############
|
||||||
# Build Native
|
# Build Native
|
||||||
###############
|
###############
|
||||||
@@ -218,8 +195,6 @@ def run_ndk_build(cmds: list):
|
|||||||
|
|
||||||
|
|
||||||
def build_cpp_src(targets: set):
|
def build_cpp_src(targets: set):
|
||||||
dump_flag_header()
|
|
||||||
|
|
||||||
cmds = []
|
cmds = []
|
||||||
clean = False
|
clean = False
|
||||||
|
|
||||||
@@ -257,6 +232,7 @@ def build_cpp_src(targets: set):
|
|||||||
|
|
||||||
|
|
||||||
def run_cargo(cmds):
|
def run_cargo(cmds):
|
||||||
|
ensure_paths()
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["PATH"] = f'{rust_bin}{os.pathsep}{env["PATH"]}'
|
env["PATH"] = f'{rust_bin}{os.pathsep}{env["PATH"]}'
|
||||||
env["CARGO_BUILD_RUSTC"] = str(rust_bin / f"rustc{EXE_EXT}")
|
env["CARGO_BUILD_RUSTC"] = str(rust_bin / f"rustc{EXE_EXT}")
|
||||||
@@ -334,11 +310,18 @@ def dump_flag_header():
|
|||||||
flag_txt += f'#define MAGISK_VER_CODE {config["versionCode"]}\n'
|
flag_txt += f'#define MAGISK_VER_CODE {config["versionCode"]}\n'
|
||||||
flag_txt += f"#define MAGISK_DEBUG {0 if args.release else 1}\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)
|
native_gen_path.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||||
write_if_diff(Path(native_gen_path, "flags.h"), flag_txt)
|
write_if_diff(native_gen_path / "flags.h", flag_txt)
|
||||||
|
|
||||||
|
rust_flag_txt = f'pub const MAGISK_VERSION: &str = "{config["version"]}";\n'
|
||||||
|
rust_flag_txt += f'pub const MAGISK_VER_CODE: i32 = {config["versionCode"]};\n'
|
||||||
|
write_if_diff(native_gen_path / "flags.rs", rust_flag_txt)
|
||||||
|
|
||||||
|
|
||||||
def build_native():
|
def build_native():
|
||||||
|
ensure_paths()
|
||||||
|
|
||||||
# Verify NDK install
|
# Verify NDK install
|
||||||
try:
|
try:
|
||||||
with open(Path(ndk_path, "ONDK_VERSION"), "r") as ondk_ver:
|
with open(Path(ndk_path, "ONDK_VERSION"), "r") as ondk_ver:
|
||||||
@@ -362,6 +345,7 @@ def build_native():
|
|||||||
if ccache := shutil.which("ccache"):
|
if ccache := shutil.which("ccache"):
|
||||||
os.environ["NDK_CCACHE"] = ccache
|
os.environ["NDK_CCACHE"] = ccache
|
||||||
|
|
||||||
|
dump_flag_header()
|
||||||
build_rust_src(targets)
|
build_rust_src(targets)
|
||||||
build_cpp_src(targets)
|
build_cpp_src(targets)
|
||||||
|
|
||||||
@@ -397,13 +381,14 @@ def find_jdk():
|
|||||||
if no_jdk:
|
if no_jdk:
|
||||||
error(
|
error(
|
||||||
"Please set Android Studio's path to environment variable ANDROID_STUDIO,\n"
|
"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
|
return env
|
||||||
|
|
||||||
|
|
||||||
def build_apk(module: str):
|
def build_apk(module: str):
|
||||||
|
ensure_paths()
|
||||||
env = find_jdk()
|
env = find_jdk()
|
||||||
|
|
||||||
build_type = "Release" if args.release else "Debug"
|
build_type = "Release" if args.release else "Debug"
|
||||||
@@ -426,19 +411,20 @@ def build_apk(module: str):
|
|||||||
source = Path(*paths, "build", "outputs", "apk", build_type, apk)
|
source = Path(*paths, "build", "outputs", "apk", build_type, apk)
|
||||||
target = config["outdir"] / apk
|
target = config["outdir"] / apk
|
||||||
mv(source, target)
|
mv(source, target)
|
||||||
header(f"Output: {target}")
|
return target
|
||||||
|
|
||||||
|
|
||||||
def build_app():
|
def build_app():
|
||||||
header("* Building the Magisk app")
|
header("* Building the Magisk app")
|
||||||
build_apk(":app:apk")
|
apk = build_apk(":app:apk")
|
||||||
|
|
||||||
build_type = "release" if args.release else "debug"
|
build_type = "release" if args.release else "debug"
|
||||||
|
|
||||||
# Rename apk-variant.apk to app-variant.apk
|
# Rename apk-variant.apk to app-variant.apk
|
||||||
source = config["outdir"] / f"apk-{build_type}.apk"
|
source = apk
|
||||||
target = config["outdir"] / f"app-{build_type}.apk"
|
target = apk.parent / apk.name.replace("apk-", "app-")
|
||||||
mv(source, target)
|
mv(source, target)
|
||||||
|
header(f"Output: {target}")
|
||||||
|
|
||||||
# Stub building is directly integrated into the main app
|
# Stub building is directly integrated into the main app
|
||||||
# build process. Copy the stub APK into output directory.
|
# build process. Copy the stub APK into output directory.
|
||||||
@@ -449,7 +435,23 @@ def build_app():
|
|||||||
|
|
||||||
def build_stub():
|
def build_stub():
|
||||||
header("* Building the stub app")
|
header("* Building the stub app")
|
||||||
build_apk(":app:stub")
|
apk = build_apk(":app:stub")
|
||||||
|
header(f"Output: {apk}")
|
||||||
|
|
||||||
|
|
||||||
|
def build_test():
|
||||||
|
global args
|
||||||
|
args_bak = copy.copy(args)
|
||||||
|
# Test APK has to be built as release to prevent classname clash
|
||||||
|
args.release = True
|
||||||
|
try:
|
||||||
|
header("* Building the test app")
|
||||||
|
source = build_apk(":app:test")
|
||||||
|
target = source.parent / "test.apk"
|
||||||
|
mv(source, target)
|
||||||
|
header(f"Output: {target}")
|
||||||
|
finally:
|
||||||
|
args = args_bak
|
||||||
|
|
||||||
|
|
||||||
################
|
################
|
||||||
@@ -458,6 +460,7 @@ def build_stub():
|
|||||||
|
|
||||||
|
|
||||||
def cleanup():
|
def cleanup():
|
||||||
|
ensure_paths()
|
||||||
support_targets = {"native", "cpp", "rust", "app"}
|
support_targets = {"native", "cpp", "rust", "app"}
|
||||||
if args.targets:
|
if args.targets:
|
||||||
targets = set(args.targets) & support_targets
|
targets = set(args.targets) & support_targets
|
||||||
@@ -491,6 +494,7 @@ def cleanup():
|
|||||||
def build_all():
|
def build_all():
|
||||||
build_native()
|
build_native()
|
||||||
build_app()
|
build_app()
|
||||||
|
build_test()
|
||||||
|
|
||||||
|
|
||||||
############
|
############
|
||||||
@@ -498,6 +502,17 @@ def build_all():
|
|||||||
############
|
############
|
||||||
|
|
||||||
|
|
||||||
|
def clippy_cli():
|
||||||
|
args.force_out = True
|
||||||
|
os.chdir(Path("native", "src"))
|
||||||
|
cmds = ["clippy", "--no-deps", "--target", ""]
|
||||||
|
for triple in build_abis.values():
|
||||||
|
cmds[3] = triple
|
||||||
|
run_cargo(cmds)
|
||||||
|
run_cargo(cmds + ["--release"])
|
||||||
|
os.chdir(Path("..", ".."))
|
||||||
|
|
||||||
|
|
||||||
def cargo_cli():
|
def cargo_cli():
|
||||||
args.force_out = True
|
args.force_out = True
|
||||||
if len(args.commands) >= 1 and args.commands[0] == "--":
|
if len(args.commands) >= 1 and args.commands[0] == "--":
|
||||||
@@ -508,6 +523,7 @@ def cargo_cli():
|
|||||||
|
|
||||||
|
|
||||||
def setup_ndk():
|
def setup_ndk():
|
||||||
|
ensure_paths()
|
||||||
ndk_ver = config["ondkVersion"]
|
ndk_ver = config["ondkVersion"]
|
||||||
url = f"https://github.com/topjohnwu/ondk/releases/download/{ndk_ver}/ondk-{ndk_ver}-{os_name}.tar.xz"
|
url = f"https://github.com/topjohnwu/ondk/releases/download/{ndk_ver}/ondk-{ndk_ver}-{os_name}.tar.xz"
|
||||||
ndk_archive = url.split("/")[-1]
|
ndk_archive = url.split("/")[-1]
|
||||||
@@ -526,14 +542,54 @@ def setup_ndk():
|
|||||||
mv(ondk_path, ndk_path)
|
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):
|
def push_files(script):
|
||||||
|
if args.build:
|
||||||
|
build_all()
|
||||||
|
ensure_adb()
|
||||||
|
|
||||||
abi = cmd_out([adb_path, "shell", "getprop", "ro.product.cpu.abi"])
|
abi = cmd_out([adb_path, "shell", "getprop", "ro.product.cpu.abi"])
|
||||||
if not abi:
|
if not abi:
|
||||||
error("Cannot detect emulator ABI")
|
error("Cannot detect emulator ABI")
|
||||||
|
|
||||||
apk = Path(
|
if args.apk:
|
||||||
config["outdir"], ("app-release.apk" if args.release else "app-debug.apk")
|
apk = Path(args.apk)
|
||||||
)
|
else:
|
||||||
|
apk = Path(
|
||||||
|
config["outdir"], ("app-release.apk" if args.release else "app-debug.apk")
|
||||||
|
)
|
||||||
|
|
||||||
# Extract busybox from APK
|
# Extract busybox from APK
|
||||||
busybox = Path(config["outdir"], "busybox")
|
busybox = Path(config["outdir"], "busybox")
|
||||||
@@ -555,9 +611,6 @@ def push_files(script):
|
|||||||
|
|
||||||
|
|
||||||
def setup_avd():
|
def setup_avd():
|
||||||
if not args.skip:
|
|
||||||
build_all()
|
|
||||||
|
|
||||||
header("* Setting up emulator")
|
header("* Setting up emulator")
|
||||||
|
|
||||||
push_files(Path("scripts", "avd_magisk.sh"))
|
push_files(Path("scripts", "avd_magisk.sh"))
|
||||||
@@ -568,17 +621,8 @@ def setup_avd():
|
|||||||
|
|
||||||
|
|
||||||
def patch_avd_file():
|
def patch_avd_file():
|
||||||
if not args.skip:
|
|
||||||
build_all()
|
|
||||||
|
|
||||||
input = Path(args.image)
|
input = Path(args.image)
|
||||||
if args.output:
|
output = Path(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"
|
|
||||||
|
|
||||||
header(f"* Patching {input.name}")
|
header(f"* Patching {input.name}")
|
||||||
|
|
||||||
@@ -588,6 +632,9 @@ def patch_avd_file():
|
|||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
error("adb push failed!")
|
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])
|
proc = execv([adb_path, "shell", "sh", "/data/local/tmp/avd_patch.sh", src_file])
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
error("avd_patch.sh failed!")
|
error("avd_patch.sh failed!")
|
||||||
@@ -599,37 +646,48 @@ def patch_avd_file():
|
|||||||
header(f"Output: {output}")
|
header(f"Output: {output}")
|
||||||
|
|
||||||
|
|
||||||
def setup_rustup():
|
##########################
|
||||||
wrapper_dir = Path(args.wrapper_dir)
|
# Config, paths, argparse
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
##################
|
def ensure_paths():
|
||||||
# Config and args
|
global sdk_path, ndk_root, ndk_path, ndk_build, rust_bin
|
||||||
##################
|
global llvm_bin, cargo, gradlew, adb_path, native_gen_path
|
||||||
|
|
||||||
|
# Skip if already initialized
|
||||||
|
if "sdk_path" in globals():
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
sdk_path = Path(os.environ["ANDROID_HOME"])
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
sdk_path = Path(os.environ["ANDROID_SDK_ROOT"])
|
||||||
|
except KeyError:
|
||||||
|
error("Please set Android SDK path to environment variable ANDROID_HOME")
|
||||||
|
|
||||||
|
ndk_root = sdk_path / "ndk"
|
||||||
|
ndk_path = ndk_root / "magisk"
|
||||||
|
ndk_build = ndk_path / "ndk-build"
|
||||||
|
rust_bin = ndk_path / "toolchains" / "rust" / "bin"
|
||||||
|
llvm_bin = (
|
||||||
|
ndk_path / "toolchains" / "llvm" / "prebuilt" / f"{os_name}-x86_64" / "bin"
|
||||||
|
)
|
||||||
|
cargo = rust_bin / "cargo"
|
||||||
|
adb_path = sdk_path / "platform-tools" / "adb"
|
||||||
|
gradlew = Path.cwd() / "gradlew"
|
||||||
|
|
||||||
|
|
||||||
|
# We allow using several functionality with only ADB
|
||||||
|
def ensure_adb():
|
||||||
|
global adb_path
|
||||||
|
if "adb_path" not in globals():
|
||||||
|
adb_path = shutil.which("adb")
|
||||||
|
if not adb_path:
|
||||||
|
error("Command 'adb' cannot be found in PATH")
|
||||||
|
else:
|
||||||
|
adb_path = Path(adb_path)
|
||||||
|
|
||||||
|
|
||||||
def parse_props(file):
|
def parse_props(file):
|
||||||
@@ -719,6 +777,8 @@ def parse_args():
|
|||||||
|
|
||||||
stub_parser = subparsers.add_parser("stub", help="build the stub app")
|
stub_parser = subparsers.add_parser("stub", help="build the stub app")
|
||||||
|
|
||||||
|
test_parser = subparsers.add_parser("test", help="build the test app")
|
||||||
|
|
||||||
clean_parser = subparsers.add_parser("clean", help="cleanup")
|
clean_parser = subparsers.add_parser("clean", help="cleanup")
|
||||||
clean_parser.add_argument(
|
clean_parser.add_argument(
|
||||||
"targets", nargs="*", help="native, cpp, rust, java, or empty to clean all"
|
"targets", nargs="*", help="native, cpp, rust, java, or empty to clean all"
|
||||||
@@ -727,17 +787,19 @@ def parse_args():
|
|||||||
ndk_parser = subparsers.add_parser("ndk", help="setup Magisk NDK")
|
ndk_parser = subparsers.add_parser("ndk", help="setup Magisk NDK")
|
||||||
|
|
||||||
emu_parser = subparsers.add_parser("emulator", help="setup AVD for development")
|
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(
|
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_parser = subparsers.add_parser(
|
||||||
"avd_patch", help="patch AVD ramdisk.img or init_boot.img"
|
"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("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(
|
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(
|
cargo_parser = subparsers.add_parser(
|
||||||
@@ -745,6 +807,8 @@ def parse_args():
|
|||||||
)
|
)
|
||||||
cargo_parser.add_argument("commands", nargs=argparse.REMAINDER)
|
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 = subparsers.add_parser("rustup", help="setup rustup wrapper")
|
||||||
rustup_parser.add_argument(
|
rustup_parser.add_argument(
|
||||||
"wrapper_dir", help="path to setup rustup wrapper binaries"
|
"wrapper_dir", help="path to setup rustup wrapper binaries"
|
||||||
@@ -754,9 +818,11 @@ def parse_args():
|
|||||||
all_parser.set_defaults(func=build_all)
|
all_parser.set_defaults(func=build_all)
|
||||||
native_parser.set_defaults(func=build_native)
|
native_parser.set_defaults(func=build_native)
|
||||||
cargo_parser.set_defaults(func=cargo_cli)
|
cargo_parser.set_defaults(func=cargo_cli)
|
||||||
|
clippy_parser.set_defaults(func=clippy_cli)
|
||||||
rustup_parser.set_defaults(func=setup_rustup)
|
rustup_parser.set_defaults(func=setup_rustup)
|
||||||
app_parser.set_defaults(func=build_app)
|
app_parser.set_defaults(func=build_app)
|
||||||
stub_parser.set_defaults(func=build_stub)
|
stub_parser.set_defaults(func=build_stub)
|
||||||
|
test_parser.set_defaults(func=build_test)
|
||||||
emu_parser.set_defaults(func=setup_avd)
|
emu_parser.set_defaults(func=setup_avd)
|
||||||
avd_patch_parser.set_defaults(func=patch_avd_file)
|
avd_patch_parser.set_defaults(func=patch_avd_file)
|
||||||
clean_parser.set_defaults(func=cleanup)
|
clean_parser.set_defaults(func=cleanup)
|
||||||
@@ -769,7 +835,13 @@ def parse_args():
|
|||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
args = parse_args()
|
def main():
|
||||||
load_config()
|
global args
|
||||||
vars(args)["force_out"] = False
|
args = parse_args()
|
||||||
args.func()
|
load_config()
|
||||||
|
vars(args)["force_out"] = False
|
||||||
|
args.func()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`kotlin-dsl`
|
`kotlin-dsl`
|
||||||
@@ -18,9 +18,9 @@ gradlePlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<KotlinCompile>().configureEach {
|
kotlin {
|
||||||
kotlinOptions {
|
compilerOptions {
|
||||||
languageVersion = "2.0"
|
languageVersion = KotlinVersion.KOTLIN_2_0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
122
buildSrc/src/main/java/DesugarClassVisitorFactory.kt
Normal file
122
buildSrc/src/main/java/DesugarClassVisitorFactory.kt
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import com.android.build.api.instrumentation.AsmClassVisitorFactory
|
||||||
|
import com.android.build.api.instrumentation.ClassContext
|
||||||
|
import com.android.build.api.instrumentation.ClassData
|
||||||
|
import com.android.build.api.instrumentation.InstrumentationParameters
|
||||||
|
import org.objectweb.asm.ClassVisitor
|
||||||
|
import org.objectweb.asm.MethodVisitor
|
||||||
|
import org.objectweb.asm.Opcodes
|
||||||
|
import org.objectweb.asm.Opcodes.ASM9
|
||||||
|
|
||||||
|
private const val DESUGAR_CLASS_NAME = "com.topjohnwu.magisk.core.utils.Desugar"
|
||||||
|
private const val ZIP_ENTRY_CLASS_NAME = "java.util.zip.ZipEntry"
|
||||||
|
private const val ZIP_OUT_STREAM_CLASS_NAME = "org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream"
|
||||||
|
private const val ZIP_UTIL_CLASS_NAME = "org/apache/commons/compress/archivers/zip/ZipUtil"
|
||||||
|
private const val ZIP_ENTRY_GET_TIME_DESC = "()Ljava/nio/file/attribute/FileTime;"
|
||||||
|
private const val DESUGAR_GET_TIME_DESC =
|
||||||
|
"(Ljava/util/zip/ZipEntry;)Ljava/nio/file/attribute/FileTime;"
|
||||||
|
|
||||||
|
private fun ClassData.isTypeOf(name: String) = className == name || superClasses.contains(name)
|
||||||
|
|
||||||
|
abstract class DesugarClassVisitorFactory : AsmClassVisitorFactory<InstrumentationParameters.None> {
|
||||||
|
override fun createClassVisitor(
|
||||||
|
classContext: ClassContext,
|
||||||
|
nextClassVisitor: ClassVisitor
|
||||||
|
): ClassVisitor {
|
||||||
|
return if (classContext.currentClassData.className == ZIP_OUT_STREAM_CLASS_NAME) {
|
||||||
|
ZipEntryPatcher(classContext, ZipOutputStreamPatcher(nextClassVisitor))
|
||||||
|
} else {
|
||||||
|
ZipEntryPatcher(classContext, nextClassVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isInstrumentable(classData: ClassData) = classData.className != DESUGAR_CLASS_NAME
|
||||||
|
|
||||||
|
// Patch ALL references to ZipEntry#getXXXTime
|
||||||
|
class ZipEntryPatcher(
|
||||||
|
private val classContext: ClassContext,
|
||||||
|
cv: ClassVisitor
|
||||||
|
) : ClassVisitor(ASM9, cv) {
|
||||||
|
override fun visitMethod(
|
||||||
|
access: Int,
|
||||||
|
name: String?,
|
||||||
|
descriptor: String?,
|
||||||
|
signature: String?,
|
||||||
|
exceptions: Array<out String>?
|
||||||
|
) = MethodPatcher(super.visitMethod(access, name, descriptor, signature, exceptions))
|
||||||
|
|
||||||
|
inner class MethodPatcher(mv: MethodVisitor?) : MethodVisitor(ASM9, mv) {
|
||||||
|
override fun visitMethodInsn(
|
||||||
|
opcode: Int,
|
||||||
|
owner: String,
|
||||||
|
name: String,
|
||||||
|
descriptor: String,
|
||||||
|
isInterface: Boolean
|
||||||
|
) {
|
||||||
|
if (!process(owner, name, descriptor)) {
|
||||||
|
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun process(owner: String, name: String, descriptor: String): Boolean {
|
||||||
|
val classData = classContext.loadClassData(owner.replace("/", ".")) ?: return false
|
||||||
|
if (!classData.isTypeOf(ZIP_ENTRY_CLASS_NAME))
|
||||||
|
return false
|
||||||
|
if (descriptor != ZIP_ENTRY_GET_TIME_DESC)
|
||||||
|
return false
|
||||||
|
return when (name) {
|
||||||
|
"getLastModifiedTime", "getLastAccessTime", "getCreationTime" -> {
|
||||||
|
mv.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESTATIC,
|
||||||
|
DESUGAR_CLASS_NAME.replace('.', '/'),
|
||||||
|
name,
|
||||||
|
DESUGAR_GET_TIME_DESC,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch ZipArchiveOutputStream#copyFromZipInputStream
|
||||||
|
class ZipOutputStreamPatcher(cv: ClassVisitor) : ClassVisitor(ASM9, cv) {
|
||||||
|
override fun visitMethod(
|
||||||
|
access: Int,
|
||||||
|
name: String,
|
||||||
|
descriptor: String,
|
||||||
|
signature: String?,
|
||||||
|
exceptions: Array<out String?>?
|
||||||
|
): MethodVisitor? {
|
||||||
|
return if (name == "copyFromZipInputStream") {
|
||||||
|
MethodPatcher(super.visitMethod(access, name, descriptor, signature, exceptions))
|
||||||
|
} else {
|
||||||
|
super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MethodPatcher(mv: MethodVisitor?) : MethodVisitor(ASM9, mv) {
|
||||||
|
override fun visitMethodInsn(
|
||||||
|
opcode: Int,
|
||||||
|
owner: String,
|
||||||
|
name: String,
|
||||||
|
descriptor: String?,
|
||||||
|
isInterface: Boolean
|
||||||
|
) {
|
||||||
|
if (owner == ZIP_UTIL_CLASS_NAME && name == "checkRequestedFeatures") {
|
||||||
|
// Redirect
|
||||||
|
mv.visitMethodInsn(
|
||||||
|
Opcodes.INVOKESTATIC,
|
||||||
|
DESUGAR_CLASS_NAME.replace('.', '/'),
|
||||||
|
name,
|
||||||
|
descriptor,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import com.android.build.api.artifact.ArtifactTransformationRequest
|
import com.android.build.api.artifact.ArtifactTransformationRequest
|
||||||
import com.android.build.api.artifact.SingleArtifact
|
import com.android.build.api.artifact.SingleArtifact
|
||||||
import com.android.build.api.dsl.ApkSigningConfig
|
import com.android.build.api.dsl.ApkSigningConfig
|
||||||
|
import com.android.build.api.instrumentation.FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
|
||||||
|
import com.android.build.api.instrumentation.InstrumentationScope
|
||||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||||
import com.android.build.gradle.BaseExtension
|
import com.android.build.gradle.BaseExtension
|
||||||
import com.android.build.gradle.LibraryExtension
|
import com.android.build.gradle.LibraryExtension
|
||||||
@@ -70,9 +72,9 @@ private val Project.androidComponents
|
|||||||
fun Project.setupCommon() {
|
fun Project.setupCommon() {
|
||||||
androidBase {
|
androidBase {
|
||||||
compileSdkVersion(35)
|
compileSdkVersion(35)
|
||||||
buildToolsVersion = "34.0.0"
|
buildToolsVersion = "35.0.1"
|
||||||
ndkPath = "$sdkDirectory/ndk/magisk"
|
ndkPath = "$sdkDirectory/ndk/magisk"
|
||||||
ndkVersion = "27.0.12077973"
|
ndkVersion = "29.0.13113456"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
@@ -113,6 +115,27 @@ fun Project.setupCommon() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Project.downloadFile(url: String, checksum: String): File {
|
||||||
|
val file = layout.buildDirectory.file(checksum).get().asFile
|
||||||
|
if (file.exists()) {
|
||||||
|
val md = MessageDigest.getInstance("SHA-256")
|
||||||
|
file.inputStream().use { md.update(it.readAllBytes()) }
|
||||||
|
val hash = HexFormat.of().formatHex(md.digest())
|
||||||
|
if (hash != checksum) {
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.parentFile.mkdirs()
|
||||||
|
URI(url).toURL().openStream().use { dl ->
|
||||||
|
file.outputStream().use {
|
||||||
|
dl.copyTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
const val BUSYBOX_DOWNLOAD_URL =
|
const val BUSYBOX_DOWNLOAD_URL =
|
||||||
"https://github.com/topjohnwu/magisk-files/releases/download/files/busybox-1.36.1.1.zip"
|
"https://github.com/topjohnwu/magisk-files/releases/download/files/busybox-1.36.1.1.zip"
|
||||||
const val BUSYBOX_ZIP_CHECKSUM =
|
const val BUSYBOX_ZIP_CHECKSUM =
|
||||||
@@ -142,24 +165,7 @@ fun Project.setupCoreLib() {
|
|||||||
|
|
||||||
val downloadBusybox by tasks.registering(Copy::class) {
|
val downloadBusybox by tasks.registering(Copy::class) {
|
||||||
dependsOn(syncLibs)
|
dependsOn(syncLibs)
|
||||||
val bb = layout.buildDirectory.file(BUSYBOX_ZIP_CHECKSUM).get().asFile
|
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
|
||||||
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))
|
|
||||||
include(abiList.map { "$it/libbusybox.so" })
|
include(abiList.map { "$it/libbusybox.so" })
|
||||||
into("src/main/jniLibs")
|
into("src/main/jniLibs")
|
||||||
}
|
}
|
||||||
@@ -297,6 +303,9 @@ fun Project.setupAppCommon() {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -348,7 +357,34 @@ fun Project.setupAppCommon() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Project.setupStub() {
|
fun Project.setupMainApk() {
|
||||||
|
setupAppCommon()
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.topjohnwu.magisk"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.topjohnwu.magisk"
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
versionName = Config.version
|
||||||
|
versionCode = Config.versionCode
|
||||||
|
ndk {
|
||||||
|
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64", "riscv64")
|
||||||
|
debugSymbolLevel = "FULL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
androidComponents.onVariants { variant ->
|
||||||
|
variant.instrumentation.apply {
|
||||||
|
setAsmFramesComputationMode(COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS)
|
||||||
|
transformClassesWith(
|
||||||
|
DesugarClassVisitorFactory::class.java, InstrumentationScope.ALL) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Project.setupStubApk() {
|
||||||
setupAppCommon()
|
setupAppCommon()
|
||||||
|
|
||||||
androidComponents.onVariants { variant ->
|
androidComponents.onVariants { variant ->
|
||||||
@@ -430,3 +466,31 @@ fun Project.setupStub() {
|
|||||||
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# Magisk Changelog
|
# Magisk Changelog
|
||||||
|
|
||||||
|
### v28.1
|
||||||
|
|
||||||
|
- [App] Fix stub APK download link
|
||||||
|
- [App] Fix support for Android lower than 8.0
|
||||||
|
- [General] Fix support for MTK Samsung devices
|
||||||
|
- [MagiskInit] Fix a regression for 2SI devices
|
||||||
|
- [MagiskPolicy] Fix a regression causing `overlay.d` replaced files to be not accessible
|
||||||
|
|
||||||
### v28.0
|
### v28.0
|
||||||
|
|
||||||
- [General] Support 16k page size
|
- [General] Support 16k page size
|
||||||
|
|||||||
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.)
|
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:
|
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.
|
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.
|
||||||
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.
|
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?
|
### 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 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.
|
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.
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ If you place a file named `.replace` in any of the folders, instead of merging i
|
|||||||
|
|
||||||
If you want to replace files in `/vendor`, `/product`, or `/system_ext`, please place them under `system/vendor`, `system/product`, and `system/system_ext` respectively. Magisk will transparently handle whether these partitions are in a separate partition or not.
|
If you want to replace files in `/vendor`, `/product`, or `/system_ext`, please place them under `system/vendor`, `system/product`, and `system/system_ext` respectively. Magisk will transparently handle whether these partitions are in a separate partition or not.
|
||||||
|
|
||||||
|
If you want to remove a specific file or folder, please place a dummy character device with major number 0 and minor number 0 in the same path. For example, if you want to remove `/system/app/GoogleCamera`, you can `mknod GoogleCamera c 0 0` in `$MODDIR/system/app`.
|
||||||
|
|
||||||
#### Zygisk
|
#### Zygisk
|
||||||
|
|
||||||
Zygisk is a feature of Magisk that allows advanced module developers to run code directly in every Android applications' processes before they are specialized and running. For more details about the Zygisk API and building a Zygisk module, please checkout the [Zygisk Module Sample](https://github.com/topjohnwu/zygisk-module-sample) project.
|
Zygisk is a feature of Magisk that allows advanced module developers to run code directly in every Android applications' processes before they are specialized and running. For more details about the Zygisk API and building a Zygisk module, please checkout the [Zygisk Module Sample](https://github.com/topjohnwu/zygisk-module-sample) project.
|
||||||
@@ -138,7 +140,7 @@ If your module requires some additional sepolicy patches, please add those rules
|
|||||||
|
|
||||||
## Magisk Module Installer
|
## Magisk Module Installer
|
||||||
|
|
||||||
A Magisk module installer is a Magisk module packaged in a zip file that can be flashed in the Magisk app or custom recoveries such as TWRP. The simplest Magisk module installer is just a Magisk module packed as a zip file, in addition to the following files:
|
A Magisk module installer is a Magisk module packaged in a zip file that can be flashed in the Magisk app or custom recoveries such as TWRP. The simplest Magisk module installer is just a Magisk module packed as a zip file, in addition to the following files only if the module supports flashing in recovery:
|
||||||
|
|
||||||
- `update-binary`: Download the latest [module_installer.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and rename/copy that script as `update-binary`
|
- `update-binary`: Download the latest [module_installer.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and rename/copy that script as `update-binary`
|
||||||
- `updater-script`: This file should only contain the string `#MAGISK`
|
- `updater-script`: This file should only contain the string `#MAGISK`
|
||||||
@@ -148,7 +150,7 @@ The module installer script will setup the environment, extract the module files
|
|||||||
```
|
```
|
||||||
module.zip
|
module.zip
|
||||||
│
|
│
|
||||||
├── META-INF
|
├── META-INF <--- Only needed for flashing in recovery
|
||||||
│ └── com
|
│ └── com
|
||||||
│ └── google
|
│ └── google
|
||||||
│ └── android
|
│ └── android
|
||||||
@@ -212,7 +214,7 @@ set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission>
|
|||||||
|
|
||||||
For convenience, you can also declare a list of folders you want to replace in the variable name `REPLACE`. The module installer script will create the `.replace` file into the folders listed in `REPLACE`. For example:
|
For convenience, you can also declare a list of folders you want to replace in the variable name `REPLACE`. The module installer script will create the `.replace` file into the folders listed in `REPLACE`. For example:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
REPLACE="
|
REPLACE="
|
||||||
/system/app/YouTube
|
/system/app/YouTube
|
||||||
/system/app/Bloatware
|
/system/app/Bloatware
|
||||||
@@ -221,6 +223,17 @@ REPLACE="
|
|||||||
|
|
||||||
The list above will result in the following files being created: `$MODPATH/system/app/YouTube/.replace` and `$MODPATH/system/app/Bloatware/.replace`.
|
The list above will result in the following files being created: `$MODPATH/system/app/YouTube/.replace` and `$MODPATH/system/app/Bloatware/.replace`.
|
||||||
|
|
||||||
|
For convenience, you can also declare a list of files/folders you want to remove in the variable name `REMOVE`. The module installer script will create the corresponding dummy devices. For example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
REMOVE="
|
||||||
|
/system/app/YouTube
|
||||||
|
/system/fonts/Roboto.ttf
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
The list above will result in the following dummy devices being created: `$MODPATH/system/app/YouTube` and `$MODPATH/system/fonts/Roboto.ttf`.
|
||||||
|
|
||||||
#### Notes
|
#### Notes
|
||||||
|
|
||||||
- When your module is downloaded with the Magisk app, `update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh). **DO NOT** try to add any custom logic in `update-binary`.
|
- When your module is downloaded with the Magisk app, `update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh). **DO NOT** try to add any custom logic in `update-binary`.
|
||||||
|
|||||||
33
docs/releases/28100.md
Normal file
33
docs/releases/28100.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
## 2024.12.6 Magisk v28.1
|
||||||
|
|
||||||
|
- [App] Fix stub APK download link
|
||||||
|
- [App] Fix support for Android lower than 8.0
|
||||||
|
- [General] Fix support for MTK Samsung devices
|
||||||
|
- [MagiskInit] Fix a regression for 2SI devices
|
||||||
|
- [MagiskPolicy] Fix a regression causing `overlay.d` replaced files to be not accessible
|
||||||
|
|
||||||
|
## Magisk v28.0 Changes
|
||||||
|
|
||||||
|
- [General] Support 16k page size
|
||||||
|
- [General] Add basic support for RISC-V (not built in releases)
|
||||||
|
- [General] Use a minimal libc to build static executables (`magiskinit` and `magiskboot`) for smaller sizes
|
||||||
|
- [Core] Remove unnecessary mirror for magic mount
|
||||||
|
- [Core] Update boot image detection logic to support more devices
|
||||||
|
- [MagiskInit] Rewrite 2SI logic for injecting `magiskinit` as `init`
|
||||||
|
- [MagiskInit] Update preinit partition detection
|
||||||
|
- [Zygisk] Update internal JNI hooking implementation
|
||||||
|
- [MagiskPolicy] Preserve sepolicy config flag after patching
|
||||||
|
- [MagiskPolicy] Optimize patching rules to reduce the amount of new rules being injected
|
||||||
|
- [DenyList] Support enforcing denylist when Zygisk is disabled
|
||||||
|
- [Resetprop] Improve implementation to workaround several property modification detections
|
||||||
|
- [Resetprop] Update to properly work with property overlays
|
||||||
|
- [App] Major internal code refactoring
|
||||||
|
- [App] Support patching Samsung firmware with images larger than 8GiB
|
||||||
|
- [App] Use user-initiated job instead of foreground services on Android 14
|
||||||
|
- [App] Support Android 13+ built-in per-app language preferences
|
||||||
|
- [App] Add `action.sh` support to allow modules to define an action triggered from UI
|
||||||
|
- [MagiskBoot] Support spliting kernel images without decompression
|
||||||
|
- [MagiskBoot] Properly support vendor boot images
|
||||||
|
- [MagiskBoot] Disable Samsung PROCA from kernel image
|
||||||
|
|
||||||
|
### Full Changelog: [here](https://topjohnwu.github.io/Magisk/changes.html)
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
|
- [v28.1](28100.md)
|
||||||
- [v28.0](28000.md)
|
- [v28.0](28000.md)
|
||||||
- [v27.0](27000.md)
|
- [v27.0](27000.md)
|
||||||
- [v26.4](26400.md)
|
- [v26.4](26400.md)
|
||||||
|
|||||||
@@ -30,5 +30,5 @@ android.nonFinalResIds=false
|
|||||||
|
|
||||||
# Magisk
|
# Magisk
|
||||||
magisk.stubVersion=40
|
magisk.stubVersion=40
|
||||||
magisk.versionCode=28000
|
magisk.versionCode=28103
|
||||||
magisk.ondkVersion=r27.4
|
magisk.ondkVersion=r29.0
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin = "2.0.20"
|
kotlin = "2.1.10"
|
||||||
android = "8.7.0"
|
android = "8.9.0"
|
||||||
ksp = "2.0.20-1.0.25"
|
ksp = "2.1.10-1.0.31"
|
||||||
rikka = "1.3.0"
|
rikka = "1.3.0"
|
||||||
navigation = "2.8.2"
|
navigation = "2.8.4"
|
||||||
libsu = "6.0.0"
|
libsu = "6.0.0"
|
||||||
moshi = "1.15.1"
|
moshi = "1.15.1"
|
||||||
okhttp = "4.12.0"
|
okhttp = "4.12.0"
|
||||||
@@ -11,7 +11,7 @@ retrofit = "2.11.0"
|
|||||||
room = "2.6.1"
|
room = "2.6.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.78.1" }
|
bcpkix = { module = "org.bouncycastle:bcpkix-jdk18on", version = "1.80" }
|
||||||
commons-compress = { module = "org.apache.commons:commons-compress", version = "1.27.1" }
|
commons-compress = { module = "org.apache.commons:commons-compress", version = "1.27.1" }
|
||||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||||
retrofit-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
|
retrofit-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
|
||||||
@@ -23,28 +23,31 @@ okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.
|
|||||||
moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
|
moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" }
|
||||||
moshi-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
|
moshi-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
|
||||||
timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
|
timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
|
||||||
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version = "6.10.0.202406032230-r" }
|
jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version = "7.1.0.202411261347-r" }
|
||||||
|
|
||||||
# AndroidX
|
# AndroidX
|
||||||
activity = { module = "androidx.activity:activity", version = "1.9.2" }
|
activity = { module = "androidx.activity:activity", version = "1.10.0" }
|
||||||
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.0" }
|
appcompat = { module = "androidx.appcompat:appcompat", version = "1.7.0" }
|
||||||
core-ktx = { module = "androidx.core:core-ktx", version = "1.13.1" }
|
core-ktx = { module = "androidx.core:core-ktx", version = "1.15.0" }
|
||||||
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
|
core-splashscreen = { module = "androidx.core:core-splashscreen", version = "1.0.1" }
|
||||||
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.1.4" }
|
constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.2.0" }
|
||||||
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.4" }
|
fragment-ktx = { module = "androidx.fragment:fragment-ktx", version = "1.8.5" }
|
||||||
navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" }
|
navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigation" }
|
||||||
navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-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" }
|
profileinstaller = { module = "androidx.profileinstaller:profileinstaller", version = "1.4.1" }
|
||||||
recyclerview = { module = "androidx.recyclerview:recyclerview", version = "1.3.2" }
|
recyclerview = { module = "androidx.recyclerview:recyclerview", version = "1.4.0" }
|
||||||
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||||
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||||
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||||
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.1.0" }
|
swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version = "1.1.0" }
|
||||||
transition = { module = "androidx.transition:transition", version = "1.5.1" }
|
transition = { module = "androidx.transition:transition", version = "1.5.1" }
|
||||||
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.4.4" }
|
collection-ktx = { module = "androidx.collection:collection-ktx", version = "1.4.5" }
|
||||||
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version = "2.8.6" }
|
|
||||||
material = { module = "com.google.android.material:material", version = "1.12.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.2" }
|
jdk-libs = { module = "com.android.tools:desugar_jdk_libs_nio", version = "2.1.3" }
|
||||||
|
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" }
|
||||||
|
test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version = "2.3.0" }
|
||||||
|
|
||||||
# topjohnwu
|
# topjohnwu
|
||||||
indeterminate-checkbox = { module = "com.github.topjohnwu:indeterminate-checkbox", version = "1.0.7" }
|
indeterminate-checkbox = { module = "com.github.topjohnwu:indeterminate-checkbox", version = "1.0.7" }
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
3
gradlew
vendored
3
gradlew
vendored
@@ -86,8 +86,7 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
' "$PWD" ) || exit
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ target-dir = "../out/rust"
|
|||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
build-std = ["std", "panic_abort"]
|
build-std = ["std", "panic_abort"]
|
||||||
build-std-features = ["panic_immediate_abort"]
|
build-std-features = ["panic_immediate_abort", "optimize_for_size"]
|
||||||
profile-rustflags = true
|
profile-rustflags = true
|
||||||
|
|||||||
@@ -4,35 +4,43 @@ LOCAL_PATH := $(call my-dir)
|
|||||||
# Rust compilation outputs
|
# Rust compilation outputs
|
||||||
###########################
|
###########################
|
||||||
|
|
||||||
LIBRARY_PATH = ../out/$(TARGET_ARCH_ABI)/libmagisk-rs.a
|
|
||||||
ifneq (,$(wildcard $(LOCAL_PATH)/$(LIBRARY_PATH)))
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := magisk-rs
|
LOCAL_MODULE := magisk-rs
|
||||||
LOCAL_EXPORT_C_INCLUDES := src/core/include
|
LOCAL_EXPORT_C_INCLUDES := src/core/include
|
||||||
LOCAL_SRC_FILES := $(LIBRARY_PATH)
|
LOCAL_LIB = ../out/$(TARGET_ARCH_ABI)/libmagisk-rs.a
|
||||||
|
ifneq (,$(wildcard $(LOCAL_PATH)/$(LOCAL_LIB)))
|
||||||
|
LOCAL_SRC_FILES := $(LOCAL_LIB)
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
else
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIBRARY_PATH = ../out/$(TARGET_ARCH_ABI)/libmagiskboot-rs.a
|
|
||||||
ifneq (,$(wildcard $(LOCAL_PATH)/$(LIBRARY_PATH)))
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := boot-rs
|
LOCAL_MODULE := boot-rs
|
||||||
LOCAL_SRC_FILES := $(LIBRARY_PATH)
|
LOCAL_LIB = ../out/$(TARGET_ARCH_ABI)/libmagiskboot-rs.a
|
||||||
|
ifneq (,$(wildcard $(LOCAL_PATH)/$(LOCAL_LIB)))
|
||||||
|
LOCAL_SRC_FILES := $(LOCAL_LIB)
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
else
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIBRARY_PATH = ../out/$(TARGET_ARCH_ABI)/libmagiskinit-rs.a
|
|
||||||
ifneq (,$(wildcard $(LOCAL_PATH)/$(LIBRARY_PATH)))
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := init-rs
|
LOCAL_MODULE := init-rs
|
||||||
LOCAL_SRC_FILES := $(LIBRARY_PATH)
|
LOCAL_LIB = ../out/$(TARGET_ARCH_ABI)/libmagiskinit-rs.a
|
||||||
|
ifneq (,$(wildcard $(LOCAL_PATH)/$(LOCAL_LIB)))
|
||||||
|
LOCAL_SRC_FILES := $(LOCAL_LIB)
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
else
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIBRARY_PATH = ../out/$(TARGET_ARCH_ABI)/libmagiskpolicy-rs.a
|
|
||||||
ifneq (,$(wildcard $(LOCAL_PATH)/$(LIBRARY_PATH)))
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := policy-rs
|
LOCAL_MODULE := policy-rs
|
||||||
LOCAL_SRC_FILES := $(LIBRARY_PATH)
|
LOCAL_LIB = ../out/$(TARGET_ARCH_ABI)/libmagiskpolicy-rs.a
|
||||||
|
ifneq (,$(wildcard $(LOCAL_PATH)/$(LOCAL_LIB)))
|
||||||
|
LOCAL_SRC_FILES := $(LOCAL_LIB)
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
include $(PREBUILT_STATIC_LIBRARY)
|
||||||
|
else
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -18,12 +18,9 @@ LOCAL_SRC_FILES := \
|
|||||||
core/applets.cpp \
|
core/applets.cpp \
|
||||||
core/magisk.cpp \
|
core/magisk.cpp \
|
||||||
core/daemon.cpp \
|
core/daemon.cpp \
|
||||||
core/bootstages.cpp \
|
|
||||||
core/socket.cpp \
|
|
||||||
core/db.cpp \
|
|
||||||
core/package.cpp \
|
|
||||||
core/scripting.cpp \
|
core/scripting.cpp \
|
||||||
core/selinux.cpp \
|
core/selinux.cpp \
|
||||||
|
core/sqlite.cpp \
|
||||||
core/module.cpp \
|
core/module.cpp \
|
||||||
core/thread.cpp \
|
core/thread.cpp \
|
||||||
core/core-rs.cpp \
|
core/core-rs.cpp \
|
||||||
@@ -31,9 +28,7 @@ LOCAL_SRC_FILES := \
|
|||||||
core/su/su.cpp \
|
core/su/su.cpp \
|
||||||
core/su/connect.cpp \
|
core/su/connect.cpp \
|
||||||
core/su/pts.cpp \
|
core/su/pts.cpp \
|
||||||
core/su/su_daemon.cpp \
|
|
||||||
core/zygisk/entry.cpp \
|
core/zygisk/entry.cpp \
|
||||||
core/zygisk/main.cpp \
|
|
||||||
core/zygisk/module.cpp \
|
core/zygisk/module.cpp \
|
||||||
core/zygisk/hook.cpp \
|
core/zygisk/hook.cpp \
|
||||||
core/deny/cli.cpp \
|
core/deny/cli.cpp \
|
||||||
@@ -68,11 +63,9 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libinit-rs
|
libinit-rs
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
init/init.cpp \
|
|
||||||
init/mount.cpp \
|
init/mount.cpp \
|
||||||
init/rootdir.cpp \
|
init/rootdir.cpp \
|
||||||
init/getinfo.cpp \
|
init/getinfo.cpp \
|
||||||
init/twostage.cpp \
|
|
||||||
init/selinux.cpp \
|
init/selinux.cpp \
|
||||||
init/init-rs.cpp
|
init/init-rs.cpp
|
||||||
|
|
||||||
@@ -80,6 +73,7 @@ LOCAL_LDFLAGS := -static
|
|||||||
|
|
||||||
ifdef B_CRT0
|
ifdef B_CRT0
|
||||||
LOCAL_STATIC_LIBRARIES += crt0
|
LOCAL_STATIC_LIBRARIES += crt0
|
||||||
|
LOCAL_LDFLAGS += -Wl,--defsym=vfprintf=tiny_vfprintf
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
@@ -94,8 +88,6 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libbase \
|
libbase \
|
||||||
liblzma \
|
liblzma \
|
||||||
liblz4 \
|
liblz4 \
|
||||||
libbz2 \
|
|
||||||
libz \
|
|
||||||
libzopfli \
|
libzopfli \
|
||||||
libboot-rs
|
libboot-rs
|
||||||
|
|
||||||
@@ -126,8 +118,6 @@ LOCAL_STATIC_LIBRARIES := \
|
|||||||
libpolicy \
|
libpolicy \
|
||||||
libpolicy-rs
|
libpolicy-rs
|
||||||
|
|
||||||
LOCAL_SRC_FILES := sepolicy/main.cpp
|
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
@@ -169,6 +159,7 @@ LOCAL_SRC_FILES := \
|
|||||||
sepolicy/policy-rs.cpp
|
sepolicy/policy-rs.cpp
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
include src/Android-rs.mk
|
CWD := $(LOCAL_PATH)
|
||||||
include src/base/Android.mk
|
include $(CWD)/Android-rs.mk
|
||||||
include src/external/Android.mk
|
include $(CWD)/base/Android.mk
|
||||||
|
include $(CWD)/external/Android.mk
|
||||||
|
|||||||
476
native/src/Cargo.lock
generated
476
native/src/Cargo.lock
generated
@@ -1,20 +1,29 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argh"
|
name = "argh"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
source = "git+https://github.com/google/argh.git?rev=1c632b046d084e7bde86b82dfc969b30b4647c8c#1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34ff18325c8a36b82f992e533ece1ec9f9a9db446bd1c14d4f936bac88fcd240"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argh_derive",
|
"argh_derive",
|
||||||
"argh_shared",
|
"argh_shared",
|
||||||
|
"rust-fuzzy-search",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argh_derive"
|
name = "argh_derive"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
source = "git+https://github.com/google/argh.git?rev=1c632b046d084e7bde86b82dfc969b30b4647c8c#1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adb7b2b83a50d329d5d8ccc620f5c7064028828538bdf5646acd60dc1f767803"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argh_shared",
|
"argh_shared",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -24,17 +33,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argh_shared"
|
name = "argh_shared"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
source = "git+https://github.com/google/argh.git?rev=1c632b046d084e7bde86b82dfc969b30b4647c8c#1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
checksum = "a464143cc82dedcdc3928737445362466b7674b5db4e2eb8e869846d6d84f4f6"
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base"
|
name = "base"
|
||||||
@@ -65,28 +72,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "bit-set"
|
||||||
version = "0.11.0-rc.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17092d478f4fadfb35a7e082f62e49f0907fdf048801d9d706277e34f9df8a78"
|
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-common",
|
"bit-vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.11.0-rc.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fd016a0ddc7cb13661bf5576073ce07330a693f8608a1320b4e20561cc12cdc"
|
||||||
|
dependencies = [
|
||||||
|
"hybrid-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.16.3"
|
version = "1.22.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83"
|
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck_derive",
|
"bytemuck_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck_derive"
|
name = "bytemuck_derive"
|
||||||
version = "1.7.0"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b"
|
checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -101,9 +123,12 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.7"
|
version = "1.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
|
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -111,6 +136,32 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@@ -123,24 +174,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.10.0-rc.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9adcf94f05e094fca3005698822ec791cb4433ced416afda1c5ca3b8dfc05a2f"
|
checksum = "1cb3c4a0d3776f7535c32793be81d6d5fec0d48ac70955d9834e643aa249a52f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_format"
|
name = "const_format"
|
||||||
version = "0.2.32"
|
version = "0.2.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673"
|
checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const_format_proc_macros",
|
"const_format_proc_macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_format_proc_macros"
|
name = "const_format_proc_macros"
|
||||||
version = "0.2.32"
|
version = "0.2.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500"
|
checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -149,49 +200,62 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.12"
|
version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-bigint"
|
name = "crypto-bigint"
|
||||||
version = "0.6.0-rc.2"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e43027691f1c055da3da4f7d96af09fcec420d435d5616e51f29afd0811c56a7"
|
checksum = "96272c2ff28b807e09250b180ad1fb7889a3258f7455759b5c3c58b719467130"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
|
"serdect",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.2.0-rc.0"
|
version = "0.2.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c070b79a496dccd931229780ad5bbedd535ceff6c3565605a8e440e18e1aa2b"
|
checksum = "b0b8ce8218c97789f16356e7896b3714f26c2ee1079b79c0b7ae7064bb9089fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-primes"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76db6c35962f23f58ab08c156831c55caf69fe85a63ae3780d4fdb24c38b32b6"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-bigint",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx"
|
name = "cxx"
|
||||||
version = "1.0.124"
|
version = "1.0.137"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
|
"cxxbridge-cmd",
|
||||||
"cxxbridge-flags",
|
"cxxbridge-flags",
|
||||||
"cxxbridge-macro",
|
"cxxbridge-macro",
|
||||||
|
"foldhash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx-gen"
|
name = "cxx-gen"
|
||||||
version = "0.7.124"
|
version = "0.7.137"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -200,23 +264,35 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-flags"
|
name = "cxxbridge-cmd"
|
||||||
version = "1.0.124"
|
version = "1.0.137"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cxxbridge-macro"
|
|
||||||
version = "1.0.124"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"codespan-reporting",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxxbridge-flags"
|
||||||
|
version = "1.0.137"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxxbridge-macro"
|
||||||
|
version = "1.0.137"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der"
|
name = "der"
|
||||||
version = "0.8.0-rc.0"
|
version = "0.8.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05d9c07d3bd80cf0935ce478d07edf7e7a5b158446757f988f3e62082227b700"
|
checksum = "82db698b33305f0134faf590b9d1259dc171b5481ac41d5c8146c3b3ee7d4319"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid",
|
"const-oid",
|
||||||
"der_derive",
|
"der_derive",
|
||||||
@@ -227,9 +303,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "der_derive"
|
name = "der_derive"
|
||||||
version = "0.8.0-rc.0"
|
version = "0.8.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c46c0d3c8dba679a95cc7caf42fe6220338e28c9d7c09e0a458e6f6156c06e7"
|
checksum = "211bea8bb45f5f61bc857104606913ef8ac8b5ec698143aa2aa96a7ffdc94991"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive"
|
||||||
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -250,9 +335,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecdsa"
|
name = "ecdsa"
|
||||||
version = "0.17.0-pre.7"
|
version = "0.17.0-pre.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fad051af2b2d2f356d716138c76775929be913deb5b4ea217cd2613535936bef"
|
checksum = "7e62f2041a28c40b8884b79fbd19bc7457d76c6397767831e9ff4029fc0473a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"der",
|
"der",
|
||||||
"digest",
|
"digest",
|
||||||
@@ -264,9 +349,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "elliptic-curve"
|
name = "elliptic-curve"
|
||||||
version = "0.14.0-pre.6"
|
version = "0.14.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ed8e96bb573517f42470775f8ef1b9cd7595de52ba7a8e19c48325a92c8fe4f"
|
checksum = "cc43715037532dc2d061e5c97e81b684c28993d52a4fa4eb7d2ce2826d78f2f2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base16ct",
|
"base16ct",
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
@@ -305,6 +390,12 @@ version = "0.4.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
|
checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
@@ -347,53 +438,59 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hybrid-array"
|
name = "hybrid-array"
|
||||||
version = "0.2.0-rc.9"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d306b679262030ad8813a82d4915fc04efff97776e4db7f8eb5137039d56400"
|
checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "libbz2-rs-sys"
|
||||||
version = "1.5.0"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "0864a00c8d019e36216b69c2c4ce50b83b7bd966add3cf5ba554ec44f8bebcf5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"spin",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.155"
|
version = "0.2.170"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libz-rs-sys"
|
||||||
version = "0.2.8"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
checksum = "902bc563b5d65ad9bba616b490842ef0651066a1a1dc3ce1087113ffcb873c8d"
|
||||||
|
dependencies = [
|
||||||
|
"zlib-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.22"
|
version = "0.4.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "magisk"
|
name = "magisk"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base",
|
"base",
|
||||||
|
"bit-set",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cxx",
|
"cxx",
|
||||||
"cxx-gen",
|
"cxx-gen",
|
||||||
|
"derive",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"pb-rs",
|
"pb-rs",
|
||||||
"quick-protobuf",
|
"quick-protobuf",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -402,6 +499,7 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
"base",
|
"base",
|
||||||
|
"block-buffer",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cxx",
|
"cxx",
|
||||||
@@ -409,6 +507,8 @@ dependencies = [
|
|||||||
"der",
|
"der",
|
||||||
"digest",
|
"digest",
|
||||||
"fdt",
|
"fdt",
|
||||||
|
"libbz2-rs-sys",
|
||||||
|
"libz-rs-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"p256",
|
"p256",
|
||||||
"p384",
|
"p384",
|
||||||
@@ -416,6 +516,7 @@ dependencies = [
|
|||||||
"pb-rs",
|
"pb-rs",
|
||||||
"quick-protobuf",
|
"quick-protobuf",
|
||||||
"rsa",
|
"rsa",
|
||||||
|
"sec1",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2",
|
||||||
"size",
|
"size",
|
||||||
@@ -436,6 +537,7 @@ dependencies = [
|
|||||||
name = "magiskpolicy"
|
name = "magiskpolicy"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"argh",
|
||||||
"base",
|
"base",
|
||||||
"cxx",
|
"cxx",
|
||||||
"cxx-gen",
|
"cxx-gen",
|
||||||
@@ -463,23 +565,6 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"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]]
|
[[package]]
|
||||||
name = "num-derive"
|
name = "num-derive"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
@@ -491,26 +576,6 @@ dependencies = [
|
|||||||
"syn",
|
"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]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -518,14 +583,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"libm",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "p256"
|
name = "p256"
|
||||||
version = "0.14.0-pre.1"
|
version = "0.14.0-pre.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c32c18a74d9dda1314d2f945fb3e274848822f63f264a9e4d3f783e29b3bc1f"
|
checksum = "71f3fd64a9cad9c26ed7f734b152196d5e56376b9957c832bcca0de48a708080"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
@@ -535,9 +599,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "p384"
|
name = "p384"
|
||||||
version = "0.14.0-pre.1"
|
version = "0.14.0-pre.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99acc40dbfad9cc3dc102828f5678c8ca14f0cbf3a1f56f74c2875b5a84427af"
|
checksum = "1e19554fe6ee269c860a0f231cbba714e5cbef26a927c75d8e30ac9040a4b32e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
@@ -547,9 +611,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "p521"
|
name = "p521"
|
||||||
version = "0.14.0-pre.1"
|
version = "0.14.0-pre.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ec5d919bea930a34a522bb1c95a89f559925deab255db2c2ffa174fc48df664"
|
checksum = "957df9b5e6a7542f6430ec5187a4cb66d8498946c38b23fd14562bce6e32e6de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base16ct",
|
"base16ct",
|
||||||
"ecdsa",
|
"ecdsa",
|
||||||
@@ -571,18 +635,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem-rfc7468"
|
name = "pem-rfc7468"
|
||||||
version = "1.0.0-rc.1"
|
version = "1.0.0-rc.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c1cde4770761bf6bd336f947b9ac1fe700b0a4ec5867cf66cf08597fe89e8c"
|
checksum = "c2dfbfa5c6f0906884269722c5478e72fd4d6c0e24fe600332c6d62359567ce1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64ct",
|
"base64ct",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkcs1"
|
name = "pkcs1"
|
||||||
version = "0.8.0-rc.0"
|
version = "0.8.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d2f4c73d459a85331915baebd5082dce5ee8ef16fd9a1ca75559ac91e66a9ee"
|
checksum = "226eb25e2c46c166ce498ac0f606ac623142d640064879ff445938accddff1e2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"der",
|
"der",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
@@ -591,23 +655,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkcs8"
|
name = "pkcs8"
|
||||||
version = "0.11.0-rc.0"
|
version = "0.11.0-rc.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66180445f1dce533620a7743467ef85fe1c5e80cdaf7c7053609d7a2fbcdae20"
|
checksum = "f22636de7c995e997ed3d8d2949b7414d4faba3efa7312a6c0e75d875a14bdd4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"der",
|
"der",
|
||||||
"spki",
|
"spki",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ppv-lite86"
|
|
||||||
version = "0.2.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "primefield"
|
name = "primefield"
|
||||||
version = "0.14.0-pre.0"
|
version = "0.14.0-pre.0"
|
||||||
@@ -616,18 +671,18 @@ checksum = "d3f2ce0fa9cccdaf216230d151ce51a15298aef50ad76081a830128ecbc6428a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "primeorder"
|
name = "primeorder"
|
||||||
version = "0.14.0-pre.1"
|
version = "0.14.0-pre.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9bed0c431186675ad845922b903d28c7faa2b634a6d130fb7b50bb289f5a4d52"
|
checksum = "b794117b388378d55629f78f61e64e182baa200bf59c1a8205e0c46508ce5873"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"elliptic-curve",
|
"elliptic-curve",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.86"
|
version = "1.0.94"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -642,33 +697,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.36"
|
version = "1.0.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
@@ -690,15 +725,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.10.0-pre.2"
|
version = "0.10.0-pre.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57e864e43f5d003321ab452feea6450f9611d7be6726489b4ec051da34774c62"
|
checksum = "e82e90f434676d49758cab5b3ff2cc1fd8f12bf7d79b70c6088bc5a4e7c63270"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid",
|
"const-oid",
|
||||||
|
"crypto-bigint",
|
||||||
|
"crypto-primes",
|
||||||
"digest",
|
"digest",
|
||||||
"num-bigint-dig",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
"pkcs1",
|
"pkcs1",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
@@ -710,10 +744,22 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sec1"
|
name = "rust-fuzzy-search"
|
||||||
version = "0.8.0-rc.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32c98827dc6ed0ea1707286a3d14b4ad4e25e2643169cbf111568a46ff5b09f5"
|
checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sec1"
|
||||||
|
version = "0.8.0-rc.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1988446eff153796413a73669dfaa4caa3f5ce8b25fac89e3821a39c611772e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base16ct",
|
"base16ct",
|
||||||
"der",
|
"der",
|
||||||
@@ -725,24 +771,34 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.204"
|
version = "1.0.218"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.204"
|
version = "1.0.218"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serdect"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f42f67da2385b51a5f9652db9c93d78aeaf7610bf5ec366080b6de810604af53"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.11.0-pre.4"
|
version = "0.11.0-pre.4"
|
||||||
@@ -765,6 +821,12 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signature"
|
name = "signature"
|
||||||
version = "2.3.0-pre.4"
|
version = "2.3.0-pre.4"
|
||||||
@@ -777,32 +839,26 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "size"
|
name = "size"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fed904c7fb2856d868b92464fc8fa597fce366edea1a9cbfaa8cb5fe080bd6d"
|
checksum = "1b6709c7b6754dca1311b3c73e79fcce40dd414c782c66d88e8823030093b02b"
|
||||||
|
|
||||||
[[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"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spki"
|
name = "spki"
|
||||||
version = "0.8.0-rc.0"
|
version = "0.8.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ee3fb1c675852398475928637b3ebbdd7e1d0cc24d27b3bbc81788b4eb51e310"
|
checksum = "37ac66481418fd7afdc584adcf3be9aa572cf6c2858814494dc2a01755f050bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64ct",
|
"base64ct",
|
||||||
"der",
|
"der",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@@ -811,9 +867,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.72"
|
version = "2.0.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -831,18 +887,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "2.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.63"
|
version = "2.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -851,9 +907,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tls_codec"
|
name = "tls_codec"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a"
|
checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tls_codec_derive",
|
"tls_codec_derive",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -861,9 +917,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tls_codec_derive"
|
name = "tls_codec_derive"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c"
|
checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -872,27 +928,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.17.0"
|
version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.13"
|
version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.4"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
@@ -984,8 +1040,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x509-cert"
|
name = "x509-cert"
|
||||||
version = "0.3.0-pre"
|
version = "0.3.0-pre.0"
|
||||||
source = "git+https://github.com/RustCrypto/formats.git?rev=9c0e851c6db9c2c8a2601840d46375afde2663fb#9c0e851c6db9c2c8a2601840d46375afde2663fb"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2db382aa43c1fb5c419a960f72c3847ab0f383f635fc2e25f0bd6c5fb94371d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid",
|
"const-oid",
|
||||||
"der",
|
"der",
|
||||||
@@ -993,27 +1050,6 @@ dependencies = [
|
|||||||
"tls_codec",
|
"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]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
@@ -1033,3 +1069,9 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zlib-rs"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b20717f0917c908dc63de2e44e97f1e6b126ca58d0e391cee86d504eb8fbd05"
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
exclude = ["external"]
|
exclude = ["external"]
|
||||||
members = ["base", "boot", "core", "init", "sepolicy"]
|
members = ["base", "boot", "core", "core/derive", "init", "sepolicy"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
version = "0.0.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
cxx = { path = "external/cxx-rs" }
|
cxx = { path = "external/cxx-rs" }
|
||||||
cxx-gen = { path = "external/cxx-rs/gen/lib" }
|
cxx-gen = { path = "external/cxx-rs/gen/lib" }
|
||||||
@@ -10,26 +14,32 @@ libc = "0.2"
|
|||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
num-derive = "0.4"
|
num-derive = "0.4"
|
||||||
thiserror = "1.0"
|
thiserror = "2.0"
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
size = "0.4"
|
size = "0.5"
|
||||||
sha1 = "0.11.0-pre.4"
|
sha1 = "0.11.0-pre.4"
|
||||||
sha2 = "=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.1"
|
p256 = "0.14.0-pre.2"
|
||||||
p384 = "0.14.0-pre.1"
|
p384 = "0.14.0-pre.2"
|
||||||
p521 = "0.14.0-pre.1"
|
p521 = "0.14.0-pre.2"
|
||||||
rsa = "0.10.0-pre.2"
|
rsa = "0.10.0-pre.4"
|
||||||
#x509-cert = "0.3"
|
x509-cert = "0.3.0-pre.0"
|
||||||
der = "0.8.0-rc.0"
|
der = "0.8.0-rc.1"
|
||||||
bytemuck = "1.16"
|
bytemuck = "1.16"
|
||||||
fdt = "0.1"
|
fdt = "0.1"
|
||||||
const_format = "0.2"
|
const_format = "0.2"
|
||||||
|
bit-set = "0.8"
|
||||||
|
syn = "2"
|
||||||
|
quote = "1"
|
||||||
|
proc-macro2 = "1"
|
||||||
|
argh = { version = "0.1.13", default-features = false }
|
||||||
|
libz-rs-sys = { version = "0.4.2", default-features = false, features=["c-allocator"] }
|
||||||
|
libbz2-rs-sys = { version = "0.1.3", default-features = false, features = ["c-allocator"] }
|
||||||
|
|
||||||
[workspace.dependencies.argh]
|
# Pin version to prevent cargo update break builds
|
||||||
git = "https://github.com/google/argh.git"
|
block-buffer = "=0.11.0-rc.3"
|
||||||
rev = "1c632b046d084e7bde86b82dfc969b30b4647c8c"
|
sec1 = "=0.8.0-rc.3"
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[workspace.dependencies.pb-rs]
|
[workspace.dependencies.pb-rs]
|
||||||
git = "https://github.com/tafia/quick-protobuf.git"
|
git = "https://github.com/tafia/quick-protobuf.git"
|
||||||
@@ -40,10 +50,6 @@ default-features = false
|
|||||||
git = "https://github.com/tafia/quick-protobuf.git"
|
git = "https://github.com/tafia/quick-protobuf.git"
|
||||||
rev = "2f37d5a65504de7d716b5b28fd82219501a901a9"
|
rev = "2f37d5a65504de7d716b5b28fd82219501a901a9"
|
||||||
|
|
||||||
[workspace.dependencies.x509-cert]
|
|
||||||
git = "https://github.com/RustCrypto/formats.git"
|
|
||||||
rev = "9c0e851c6db9c2c8a2601840d46375afde2663fb"
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
lto = "thin"
|
lto = "thin"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "base"
|
name = "base"
|
||||||
version = "0.0.0"
|
version.workspace = true
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::gen::gen_cxx_binding;
|
use crate::codegen::gen_cxx_binding;
|
||||||
|
|
||||||
#[path = "../include/gen.rs"]
|
#[path = "../include/codegen.rs"]
|
||||||
mod gen;
|
mod codegen;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
gen_cxx_binding("base-rs");
|
gen_cxx_binding("base-rs");
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
|
use cxx::{type_id, ExternType};
|
||||||
|
use libc::c_char;
|
||||||
|
use std::borrow::Borrow;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::ffi::{CStr, FromBytesWithNulError, OsStr};
|
use std::ffi::{CStr, FromBytesWithNulError, OsStr};
|
||||||
use std::fmt::{Arguments, Debug, Display, Formatter, Write};
|
use std::fmt::{Debug, Display, Formatter, Write};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
use std::{fmt, mem, slice, str};
|
use std::{fmt, mem, slice, str};
|
||||||
|
|
||||||
use cxx::{type_id, ExternType};
|
|
||||||
use libc::c_char;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::slice_from_ptr_mut;
|
use crate::slice_from_ptr_mut;
|
||||||
@@ -18,23 +18,114 @@ use crate::slice_from_ptr_mut;
|
|||||||
// Several Utf8CStr types:
|
// Several Utf8CStr types:
|
||||||
//
|
//
|
||||||
// Utf8CStr: can only exist as reference, similar to &str
|
// 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
|
// Utf8CStrBufRef: reference to a fixed sized buffer
|
||||||
// Utf8CStrBufArr<N>: fixed sized buffer allocated on the stack
|
// 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
|
// In most cases, these are the types being used
|
||||||
//
|
//
|
||||||
// &Utf8CStr: whenever a printable null terminated string is needed
|
// &Utf8CStr: whenever a printable null terminated string is needed
|
||||||
// &mut dyn Utf8CStrWrite: whenever we need a buffer that only needs to support appending
|
// &mut dyn Utf8CStrBuf: whenever we need a buffer that needs to support appending
|
||||||
// strings to the end, and has to be null terminated
|
// 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
|
// &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
|
// in the result, and has to be null terminated
|
||||||
//
|
//
|
||||||
// All types dereferences to &Utf8CStr.
|
// All types dereferences to &Utf8CStr.
|
||||||
// Utf8CString, Utf8CStrBufRef, and Utf8CStrBufArr<N> implements Utf8CStrWrite.
|
// Utf8CString, Utf8CStrBufRef, and Utf8CStrBufArr<N> implements Utf8CStrBuf.
|
||||||
// Utf8CStrBufRef and Utf8CStrBufArr<N> implements Utf8CStrBuf.
|
|
||||||
|
|
||||||
fn utf8_cstr_buf_append(buf: &mut dyn Utf8CStrBuf, s: &[u8]) -> usize {
|
// Public helper functions
|
||||||
|
|
||||||
|
pub mod cstr_buf {
|
||||||
|
use super::{Utf8CStrBufArr, Utf8CStrBufRef, Utf8CString};
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn with_capacity(capacity: usize) -> Utf8CString {
|
||||||
|
Utf8CString::with_capacity(capacity)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn default() -> Utf8CStrBufArr<4096> {
|
||||||
|
Utf8CStrBufArr::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new<const N: usize>() -> Utf8CStrBufArr<N> {
|
||||||
|
Utf8CStrBufArr::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn wrap(buf: &mut [u8]) -> Utf8CStrBufRef {
|
||||||
|
Utf8CStrBufRef::from(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn wrap_ptr<'a>(buf: *mut u8, len: usize) -> Utf8CStrBufRef<'a> {
|
||||||
|
unsafe { Utf8CStrBufRef::from_ptr(buf, len) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trait definitions
|
||||||
|
|
||||||
|
pub trait Utf8CStrBuf:
|
||||||
|
Write + AsRef<Utf8CStr> + AsMut<Utf8CStr> + Deref<Target = Utf8CStr> + DerefMut
|
||||||
|
{
|
||||||
|
// The length of the string without the terminating null character.
|
||||||
|
// assert_true(len <= capacity - 1)
|
||||||
|
fn len(&self) -> usize;
|
||||||
|
// Set the length of the string
|
||||||
|
//
|
||||||
|
// It is your responsibility to:
|
||||||
|
// 1. Null terminate the string by setting the next byte after len to null
|
||||||
|
// 2. Ensure len <= capacity - 1
|
||||||
|
// 3. All bytes from 0 to len is valid UTF-8 and does not contain null
|
||||||
|
unsafe fn set_len(&mut self, len: usize);
|
||||||
|
fn push_str(&mut self, s: &str) -> usize;
|
||||||
|
fn push_lossy(&mut self, s: &[u8]) -> usize;
|
||||||
|
// The capacity of the internal buffer. The maximum string length this buffer can contain
|
||||||
|
// is capacity - 1, because the last byte is reserved for the terminating null character.
|
||||||
|
fn capacity(&self) -> usize;
|
||||||
|
fn clear(&mut self);
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Utf8CStrBufWithSlice: Utf8CStrBuf {
|
||||||
|
fn buf(&self) -> &[u8];
|
||||||
|
unsafe fn mut_buf(&mut self) -> &mut [u8];
|
||||||
|
}
|
||||||
|
|
||||||
|
trait AsUtf8CStr {
|
||||||
|
fn as_utf8_cstr(&self) -> &Utf8CStr;
|
||||||
|
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Utf8CStrBufWithSlice> AsUtf8CStr for T {
|
||||||
|
#[inline(always)]
|
||||||
|
fn as_utf8_cstr(&self) -> &Utf8CStr {
|
||||||
|
// SAFETY: the internal buffer is always UTF-8 checked
|
||||||
|
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||||
|
unsafe { Utf8CStr::from_bytes_unchecked(self.buf().get_unchecked(..(self.len() + 1))) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
||||||
|
// SAFETY: the internal buffer is always UTF-8 checked
|
||||||
|
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
||||||
|
unsafe {
|
||||||
|
let len = self.len() + 1;
|
||||||
|
Utf8CStr::from_bytes_unchecked_mut(self.mut_buf().get_unchecked_mut(..len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation for Utf8CString
|
||||||
|
|
||||||
|
fn utf8_cstr_buf_append(buf: &mut dyn Utf8CStrBufWithSlice, s: &[u8]) -> usize {
|
||||||
let mut used = buf.len();
|
let mut used = buf.len();
|
||||||
if used >= buf.capacity() - 1 {
|
if used >= buf.capacity() - 1 {
|
||||||
// Truncate
|
// Truncate
|
||||||
@@ -51,7 +142,7 @@ fn utf8_cstr_buf_append(buf: &mut dyn Utf8CStrBuf, s: &[u8]) -> usize {
|
|||||||
len
|
len
|
||||||
}
|
}
|
||||||
|
|
||||||
fn utf8_cstr_append_lossy(buf: &mut dyn Utf8CStrWrite, s: &[u8]) -> usize {
|
fn utf8_cstr_append_lossy(buf: &mut dyn Utf8CStrBuf, s: &[u8]) -> usize {
|
||||||
let mut len = 0_usize;
|
let mut len = 0_usize;
|
||||||
for chunk in s.utf8_chunks() {
|
for chunk in s.utf8_chunks() {
|
||||||
len += buf.push_str(chunk.valid());
|
len += buf.push_str(chunk.valid());
|
||||||
@@ -62,44 +153,6 @@ fn utf8_cstr_append_lossy(buf: &mut dyn Utf8CStrWrite, s: &[u8]) -> usize {
|
|||||||
len
|
len
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trait definitions
|
|
||||||
|
|
||||||
pub trait Utf8CStrWrite:
|
|
||||||
Write + AsRef<Utf8CStr> + AsMut<Utf8CStr> + Deref<Target = Utf8CStr> + DerefMut
|
|
||||||
{
|
|
||||||
fn len(&self) -> usize;
|
|
||||||
#[inline(always)]
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.len() == 0
|
|
||||||
}
|
|
||||||
fn push_str(&mut self, s: &str) -> usize;
|
|
||||||
fn push_lossy(&mut self, s: &[u8]) -> usize;
|
|
||||||
fn clear(&mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Utf8CStrBuf: Utf8CStrWrite {
|
|
||||||
fn buf(&self) -> &[u8];
|
|
||||||
|
|
||||||
// Modifying the underlying buffer or length is unsafe because it can either:
|
|
||||||
// 1. Break null termination
|
|
||||||
// 2. Break UTF-8 validation
|
|
||||||
// 3. Introduce inner null byte in the string
|
|
||||||
unsafe fn mut_buf(&mut self) -> &mut [u8];
|
|
||||||
unsafe fn set_len(&mut self, len: usize);
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn capacity(&self) -> usize {
|
|
||||||
self.buf().len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait AsUtf8CStr {
|
|
||||||
fn as_utf8_cstr(&self) -> &Utf8CStr;
|
|
||||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation for Utf8CString
|
|
||||||
|
|
||||||
pub trait StringExt {
|
pub trait StringExt {
|
||||||
fn nul_terminate(&mut self) -> &mut [u8];
|
fn nul_terminate(&mut self) -> &mut [u8];
|
||||||
}
|
}
|
||||||
@@ -132,13 +185,24 @@ impl StringExt for PathBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Utf8CString(String);
|
pub struct Utf8CString(String);
|
||||||
|
|
||||||
|
impl Default for Utf8CString {
|
||||||
|
fn default() -> Self {
|
||||||
|
Utf8CString::with_capacity(256)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Utf8CString {
|
impl Utf8CString {
|
||||||
#[inline(always)]
|
pub fn with_capacity(capacity: usize) -> Utf8CString {
|
||||||
pub fn new() -> Self {
|
Utf8CString::from(String::with_capacity(capacity))
|
||||||
Self::default()
|
}
|
||||||
|
|
||||||
|
pub fn ensure_capacity(&mut self, capacity: usize) {
|
||||||
|
if self.capacity() >= capacity {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.0.reserve(capacity - self.0.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,16 +215,28 @@ impl AsUtf8CStr for Utf8CString {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
||||||
Utf8CStr::from_string(&mut self.0)
|
// SAFETY: the internal string is always null terminated
|
||||||
|
unsafe {
|
||||||
|
mem::transmute(slice::from_raw_parts_mut(
|
||||||
|
self.0.as_mut_ptr(),
|
||||||
|
self.0.len() + 1,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Utf8CStrWrite for Utf8CString {
|
impl Utf8CStrBuf for Utf8CString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.0.len()
|
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 {
|
fn push_str(&mut self, s: &str) -> usize {
|
||||||
self.0.push_str(s);
|
self.0.push_str(s);
|
||||||
self.0.nul_terminate();
|
self.0.nul_terminate();
|
||||||
@@ -172,6 +248,10 @@ impl Utf8CStrWrite for Utf8CString {
|
|||||||
utf8_cstr_append_lossy(self, s)
|
utf8_cstr_append_lossy(self, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn capacity(&self) -> usize {
|
||||||
|
self.0.capacity()
|
||||||
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
self.0.clear();
|
self.0.clear();
|
||||||
self.0.nul_terminate();
|
self.0.nul_terminate();
|
||||||
@@ -185,24 +265,9 @@ impl From<String> for Utf8CString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementations for Utf8CStrBuf
|
impl Borrow<Utf8CStr> for Utf8CString {
|
||||||
|
fn borrow(&self) -> &Utf8CStr {
|
||||||
impl<T: Utf8CStrBuf> AsUtf8CStr for T {
|
self.deref()
|
||||||
#[inline(always)]
|
|
||||||
fn as_utf8_cstr(&self) -> &Utf8CStr {
|
|
||||||
// SAFETY: the internal buffer is always UTF-8 checked
|
|
||||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
|
||||||
unsafe { Utf8CStr::from_bytes_unchecked(self.buf().get_unchecked(..(self.len() + 1))) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn as_utf8_cstr_mut(&mut self) -> &mut Utf8CStr {
|
|
||||||
// SAFETY: the internal buffer is always UTF-8 checked
|
|
||||||
// SAFETY: self.used is guaranteed to always <= SIZE - 1
|
|
||||||
unsafe {
|
|
||||||
let len = self.len() + 1;
|
|
||||||
Utf8CStr::from_bytes_unchecked_mut(self.mut_buf().get_unchecked_mut(..len))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +279,7 @@ pub struct Utf8CStrBufRef<'a> {
|
|||||||
|
|
||||||
impl<'a> Utf8CStrBufRef<'a> {
|
impl<'a> Utf8CStrBufRef<'a> {
|
||||||
pub unsafe fn from_ptr(buf: *mut u8, len: usize) -> Utf8CStrBufRef<'a> {
|
pub unsafe fn from_ptr(buf: *mut u8, len: usize) -> Utf8CStrBufRef<'a> {
|
||||||
Self::from(slice_from_ptr_mut(buf, len))
|
unsafe { Self::from(slice_from_ptr_mut(buf, len)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +290,7 @@ impl<'a> From<&'a mut [u8]> for Utf8CStrBufRef<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Utf8CStrBuf for Utf8CStrBufRef<'_> {
|
impl Utf8CStrBufWithSlice for Utf8CStrBufRef<'_> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn buf(&self) -> &[u8] {
|
fn buf(&self) -> &[u8] {
|
||||||
self.buf
|
self.buf
|
||||||
@@ -235,11 +300,6 @@ impl Utf8CStrBuf for Utf8CStrBufRef<'_> {
|
|||||||
unsafe fn mut_buf(&mut self) -> &mut [u8] {
|
unsafe fn mut_buf(&mut self) -> &mut [u8] {
|
||||||
self.buf
|
self.buf
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn set_len(&mut self, len: usize) {
|
|
||||||
self.used = len;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UTF-8 validated + null terminated buffer on the stack
|
// UTF-8 validated + null terminated buffer on the stack
|
||||||
@@ -257,7 +317,7 @@ impl<const N: usize> Utf8CStrBufArr<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> Utf8CStrBuf for Utf8CStrBufArr<N> {
|
impl<const N: usize> Utf8CStrBufWithSlice for Utf8CStrBufArr<N> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn buf(&self) -> &[u8] {
|
fn buf(&self) -> &[u8] {
|
||||||
&self.buf
|
&self.buf
|
||||||
@@ -267,16 +327,6 @@ impl<const N: usize> Utf8CStrBuf for Utf8CStrBufArr<N> {
|
|||||||
unsafe fn mut_buf(&mut self) -> &mut [u8] {
|
unsafe fn mut_buf(&mut self) -> &mut [u8] {
|
||||||
&mut self.buf
|
&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> {
|
impl Default for Utf8CStrBufArr<4096> {
|
||||||
@@ -310,13 +360,6 @@ impl Utf8CStr {
|
|||||||
Self::from_cstr(CStr::from_bytes_with_nul(buf)?)
|
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) -> &mut Utf8CStr {
|
||||||
let buf = s.nul_terminate();
|
let buf = s.nul_terminate();
|
||||||
// SAFETY: the null byte is explicitly added to the buffer
|
// SAFETY: the null byte is explicitly added to the buffer
|
||||||
@@ -324,13 +367,13 @@ impl Utf8CStr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn from_bytes_unchecked(buf: &[u8]) -> &Utf8CStr {
|
pub const unsafe fn from_bytes_unchecked(buf: &[u8]) -> &Utf8CStr {
|
||||||
mem::transmute(buf)
|
unsafe { mem::transmute(buf) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn from_bytes_unchecked_mut(buf: &mut [u8]) -> &mut Utf8CStr {
|
unsafe fn from_bytes_unchecked_mut(buf: &mut [u8]) -> &mut Utf8CStr {
|
||||||
mem::transmute(buf)
|
unsafe { mem::transmute(buf) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> Result<&'a Utf8CStr, StrErr> {
|
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> Result<&'a Utf8CStr, StrErr> {
|
||||||
@@ -340,6 +383,13 @@ impl Utf8CStr {
|
|||||||
Self::from_cstr(unsafe { CStr::from_ptr(ptr) })
|
Self::from_cstr(unsafe { CStr::from_ptr(ptr) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub unsafe fn from_ptr_unchecked<'a>(ptr: *const c_char) -> &'a Utf8CStr {
|
||||||
|
unsafe {
|
||||||
|
let cstr = CStr::from_ptr(ptr);
|
||||||
|
Self::from_bytes_unchecked(cstr.to_bytes_with_nul())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||||
&self.0
|
&self.0
|
||||||
@@ -392,6 +442,16 @@ impl DerefMut for Utf8CStr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToOwned for Utf8CStr {
|
||||||
|
type Owned = Utf8CString;
|
||||||
|
|
||||||
|
fn to_owned(&self) -> Utf8CString {
|
||||||
|
let mut s = Utf8CString::with_capacity(self.len() + 1);
|
||||||
|
s.push_str(self.as_str());
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Notice that we only implement ExternType on Utf8CStr *reference*
|
// Notice that we only implement ExternType on Utf8CStr *reference*
|
||||||
unsafe impl ExternType for &Utf8CStr {
|
unsafe impl ExternType for &Utf8CStr {
|
||||||
type Id = type_id!("rust::Utf8CStr");
|
type Id = type_id!("rust::Utf8CStr");
|
||||||
@@ -405,8 +465,8 @@ macro_rules! const_assert_eq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assert ABI layout
|
// Assert ABI layout
|
||||||
const_assert_eq!(mem::size_of::<&Utf8CStr>(), mem::size_of::<[usize; 2]>());
|
const_assert_eq!(size_of::<&Utf8CStr>(), size_of::<[usize; 2]>());
|
||||||
const_assert_eq!(mem::align_of::<&Utf8CStr>(), mem::align_of::<[usize; 2]>());
|
const_assert_eq!(align_of::<&Utf8CStr>(), align_of::<[usize; 2]>());
|
||||||
|
|
||||||
// File system path extensions types
|
// File system path extensions types
|
||||||
|
|
||||||
@@ -441,47 +501,106 @@ impl DerefMut for FsPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FsPathBuf<'a>(&'a mut dyn Utf8CStrWrite);
|
#[repr(transparent)]
|
||||||
|
pub struct FsPathFollow(Utf8CStr);
|
||||||
|
|
||||||
impl<'a> FsPathBuf<'a> {
|
impl Deref for FsPathFollow {
|
||||||
pub fn new(value: &'a mut dyn Utf8CStrWrite) -> Self {
|
type Target = Utf8CStr;
|
||||||
value.clear();
|
|
||||||
FsPathBuf(value)
|
#[inline(always)]
|
||||||
|
fn deref(&self) -> &Utf8CStr {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for FsPathFollow {
|
||||||
|
#[inline(always)]
|
||||||
|
fn deref_mut(&mut self) -> &mut Utf8CStr {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Utf8CStrBufOwned<const N: usize> {
|
||||||
|
Dynamic(Utf8CString),
|
||||||
|
Fixed(Utf8CStrBufArr<N>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Deref for Utf8CStrBufOwned<N> {
|
||||||
|
type Target = dyn Utf8CStrBuf;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match self {
|
||||||
|
Utf8CStrBufOwned::Dynamic(s) => s,
|
||||||
|
Utf8CStrBufOwned::Fixed(arr) => arr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> DerefMut for Utf8CStrBufOwned<N> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
match self {
|
||||||
|
Utf8CStrBufOwned::Dynamic(s) => s,
|
||||||
|
Utf8CStrBufOwned::Fixed(arr) => arr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FsPathBuf<const N: usize>(Utf8CStrBufOwned<N>);
|
||||||
|
|
||||||
|
impl FsPathBuf<0> {
|
||||||
|
pub fn new_dynamic(capacity: usize) -> Self {
|
||||||
|
FsPathBuf(Utf8CStrBufOwned::Dynamic(Utf8CString::with_capacity(
|
||||||
|
capacity,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FsPathBuf<4096> {
|
||||||
|
fn default() -> Self {
|
||||||
|
FsPathBuf(Utf8CStrBufOwned::Fixed(cstr_buf::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> FsPathBuf<N> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
FsPathBuf(Utf8CStrBufOwned::Fixed(cstr_buf::new::<N>()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn join<T: AsRef<str>>(self, path: T) -> Self {
|
pub fn clear(&mut self) {
|
||||||
fn inner(buf: &mut dyn Utf8CStrWrite, path: &str) {
|
self.0.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join<T: AsRef<str>>(mut self, path: T) -> Self {
|
||||||
|
fn inner(buf: &mut dyn Utf8CStrBuf, path: &str) {
|
||||||
if path.starts_with('/') {
|
if path.starts_with('/') {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
} else {
|
}
|
||||||
|
if !buf.is_empty() && !buf.ends_with('/') {
|
||||||
buf.push_str("/");
|
buf.push_str("/");
|
||||||
}
|
}
|
||||||
buf.push_str(path);
|
buf.push_str(path);
|
||||||
}
|
}
|
||||||
inner(self.0, path.as_ref());
|
inner(self.0.deref_mut(), path.as_ref());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn join_fmt<T: Display>(self, name: T) -> Self {
|
pub fn join_fmt<T: Display>(mut self, name: T) -> Self {
|
||||||
fn inner(buf: &mut dyn Utf8CStrWrite, path: Arguments) {
|
self.0.write_fmt(format_args!("/{}", name)).ok();
|
||||||
buf.write_fmt(path).ok();
|
|
||||||
}
|
|
||||||
inner(self.0, format_args!("/{}", name));
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Deref for FsPathBuf<'a> {
|
impl<const N: usize> Deref for FsPathBuf<N> {
|
||||||
type Target = FsPath;
|
type Target = FsPath;
|
||||||
|
|
||||||
fn deref(&self) -> &FsPath {
|
fn deref(&self) -> &FsPath {
|
||||||
FsPath::from(&self.0)
|
FsPath::from(self.0.deref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DerefMut for FsPathBuf<'a> {
|
impl<const N: usize> DerefMut for FsPathBuf<N> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut FsPath {
|
||||||
FsPath::from_mut(&mut self.0)
|
FsPath::from_mut(self.0.deref_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,13 +686,14 @@ macro_rules! impl_str {
|
|||||||
impl_str!(
|
impl_str!(
|
||||||
(Utf8CStr,)
|
(Utf8CStr,)
|
||||||
(FsPath,)
|
(FsPath,)
|
||||||
(FsPathBuf<'_>,)
|
(FsPathFollow,)
|
||||||
|
(FsPathBuf<N>, const N: usize)
|
||||||
(Utf8CStrBufRef<'_>,)
|
(Utf8CStrBufRef<'_>,)
|
||||||
(Utf8CStrBufArr<N>, const N: usize)
|
(Utf8CStrBufArr<N>, const N: usize)
|
||||||
(Utf8CString,)
|
(Utf8CString,)
|
||||||
);
|
);
|
||||||
|
|
||||||
macro_rules! impl_str_write {
|
macro_rules! impl_str_buf {
|
||||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||||
impl<$($g)*> Write for $t {
|
impl<$($g)*> Write for $t {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -605,20 +725,24 @@ macro_rules! impl_str_write {
|
|||||||
)*}
|
)*}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_str_write!(
|
impl_str_buf!(
|
||||||
(Utf8CStrBufRef<'_>,)
|
(Utf8CStrBufRef<'_>,)
|
||||||
(Utf8CStrBufArr<N>, const N: usize)
|
(Utf8CStrBufArr<N>, const N: usize)
|
||||||
(Utf8CString,)
|
(Utf8CString,)
|
||||||
);
|
);
|
||||||
|
|
||||||
macro_rules! impl_str_buf {
|
macro_rules! impl_str_buf_with_slice {
|
||||||
($( ($t:ty, $($g:tt)*) )*) => {$(
|
($( ($t:ty, $($g:tt)*) )*) => {$(
|
||||||
impl<$($g)*> Utf8CStrWrite for $t {
|
impl<$($g)*> Utf8CStrBuf for $t {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.used
|
self.used
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
unsafe fn set_len(&mut self, len: usize) {
|
||||||
|
self.used = len;
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
fn push_str(&mut self, s: &str) -> usize {
|
fn push_str(&mut self, s: &str) -> usize {
|
||||||
utf8_cstr_buf_append(self, s.as_bytes())
|
utf8_cstr_buf_append(self, s.as_bytes())
|
||||||
}
|
}
|
||||||
@@ -627,6 +751,10 @@ macro_rules! impl_str_buf {
|
|||||||
utf8_cstr_append_lossy(self, s)
|
utf8_cstr_append_lossy(self, s)
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
fn capacity(&self) -> usize {
|
||||||
|
self.buf.len()
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
self.buf[0] = b'\0';
|
self.buf[0] = b'\0';
|
||||||
self.used = 0;
|
self.used = 0;
|
||||||
@@ -635,31 +763,32 @@ macro_rules! impl_str_buf {
|
|||||||
)*}
|
)*}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_str_buf!(
|
impl_str_buf_with_slice!(
|
||||||
(Utf8CStrBufRef<'_>,)
|
(Utf8CStrBufRef<'_>,)
|
||||||
(Utf8CStrBufArr<N>, const N: usize)
|
(Utf8CStrBufArr<N>, const N: usize)
|
||||||
);
|
);
|
||||||
|
|
||||||
// The cstr! macro is copied from https://github.com/bytecodealliance/rustix/blob/main/src/cstr.rs
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! cstr {
|
macro_rules! cstr {
|
||||||
($($str:tt)*) => {{
|
($str:expr) => {{
|
||||||
assert!(
|
const NULL_STR: &str = $crate::const_format::concatcp!($str, "\0");
|
||||||
!($($str)*).bytes().any(|b| b == b'\0'),
|
|
||||||
"cstr argument contains embedded NUL bytes",
|
|
||||||
);
|
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
unsafe {
|
unsafe {
|
||||||
$crate::Utf8CStr::from_bytes_unchecked($crate::const_format::concatcp!($($str)*, "\0")
|
$crate::Utf8CStr::from_bytes_unchecked(NULL_STR.as_bytes())
|
||||||
.as_bytes())
|
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! raw_cstr {
|
macro_rules! raw_cstr {
|
||||||
($($str:tt)*) => {{
|
($str:expr) => {{
|
||||||
$crate::cstr!($($str)*).as_ptr()
|
$crate::cstr!($str).as_ptr()
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! path {
|
||||||
|
($str:expr) => {{
|
||||||
|
$crate::FsPath::from($crate::cstr!($str))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,47 +9,53 @@ use libc::{c_char, mode_t};
|
|||||||
use crate::files::map_file_at;
|
use crate::files::map_file_at;
|
||||||
pub(crate) use crate::xwrap::*;
|
pub(crate) use crate::xwrap::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
clone_attr, cstr, fclone_attr, fd_path, map_fd, map_file, slice_from_ptr, CxxResultExt,
|
clone_attr, cstr, cstr_buf, fclone_attr, fd_path, map_fd, map_file, slice_from_ptr,
|
||||||
Directory, FsPath, Utf8CStr, Utf8CStrBufRef,
|
CxxResultExt, Directory, FsPath, Utf8CStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize {
|
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)
|
fd_path(fd, &mut buf)
|
||||||
.log_cxx_with_msg(|w| w.write_str("fd_path failed"))
|
.log_cxx_with_msg(|w| w.write_str("fd_path failed"))
|
||||||
.map_or(-1_isize, |_| buf.len() as isize)
|
.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 {
|
unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||||
match Utf8CStr::from_ptr(path) {
|
unsafe {
|
||||||
Ok(p) => {
|
match Utf8CStr::from_ptr(path) {
|
||||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
Ok(p) => {
|
||||||
FsPath::from(p)
|
let mut buf = cstr_buf::wrap_ptr(buf, bufsz);
|
||||||
.realpath(&mut buf)
|
FsPath::from(p)
|
||||||
.map_or(-1, |_| buf.len() as isize)
|
.realpath(&mut buf)
|
||||||
|
.map_or(-1, |_| buf.len() as isize)
|
||||||
|
}
|
||||||
|
Err(_) => -1,
|
||||||
}
|
}
|
||||||
Err(_) => -1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export_name = "mkdirs"]
|
#[unsafe(export_name = "mkdirs")]
|
||||||
unsafe extern "C" fn mkdirs_for_cxx(path: *const c_char, mode: mode_t) -> i32 {
|
unsafe extern "C" fn mkdirs_for_cxx(path: *const c_char, mode: mode_t) -> i32 {
|
||||||
match Utf8CStr::from_ptr(path) {
|
unsafe {
|
||||||
Ok(p) => FsPath::from(p).mkdirs(mode).map_or(-1, |_| 0),
|
match Utf8CStr::from_ptr(path) {
|
||||||
Err(_) => -1,
|
Ok(p) => FsPath::from(p).mkdirs(mode).map_or(-1, |_| 0),
|
||||||
|
Err(_) => -1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export_name = "rm_rf"]
|
#[unsafe(export_name = "rm_rf")]
|
||||||
unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
|
unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool {
|
||||||
match Utf8CStr::from_ptr(path) {
|
unsafe {
|
||||||
Ok(p) => FsPath::from(p).remove_all().is_ok(),
|
match Utf8CStr::from_ptr(path) {
|
||||||
Err(_) => false,
|
Ok(p) => FsPath::from(p).remove_all().is_ok(),
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
|
unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool {
|
||||||
fn inner(fd: OwnedFd) -> io::Result<()> {
|
fn inner(fd: OwnedFd) -> io::Result<()> {
|
||||||
Directory::try_from(fd)?.remove_all()
|
Directory::try_from(fd)?.remove_all()
|
||||||
@@ -83,110 +89,122 @@ pub(crate) unsafe fn readlinkat_for_cxx(
|
|||||||
buf: *mut u8,
|
buf: *mut u8,
|
||||||
bufsz: usize,
|
bufsz: usize,
|
||||||
) -> isize {
|
) -> isize {
|
||||||
// readlinkat() may fail on x86 platform, returning random value
|
unsafe {
|
||||||
// instead of number of bytes placed in buf (length of link)
|
// readlinkat() may fail on x86 platform, returning random value
|
||||||
cfg_if! {
|
// instead of number of bytes placed in buf (length of link)
|
||||||
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
|
cfg_if! {
|
||||||
libc::memset(buf.cast(), 0, bufsz);
|
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
|
||||||
let mut r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
|
libc::memset(buf.cast(), 0, bufsz);
|
||||||
if r > 0 {
|
let mut r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
|
||||||
r = libc::strlen(buf.cast()) as isize;
|
if r > 0 {
|
||||||
}
|
r = libc::strlen(buf.cast()) as isize;
|
||||||
} else {
|
}
|
||||||
let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
|
} else {
|
||||||
if r >= 0 {
|
let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1);
|
||||||
*buf.offset(r) = b'\0';
|
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 {
|
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) {
|
unsafe {
|
||||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||||
let src = FsPath::from(src);
|
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||||
let dest = FsPath::from(dest);
|
let src = FsPath::from(src);
|
||||||
return src
|
let dest = FsPath::from(dest);
|
||||||
.copy_to(dest)
|
return src
|
||||||
.log_cxx_with_msg(|w| {
|
.copy_to(dest)
|
||||||
w.write_fmt(format_args!("cp_afc {} -> {} failed", src, dest))
|
.log_cxx_with_msg(|w| {
|
||||||
})
|
w.write_fmt(format_args!("cp_afc {} -> {} failed", src, dest))
|
||||||
.is_ok();
|
})
|
||||||
|
.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 {
|
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) {
|
unsafe {
|
||||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||||
let src = FsPath::from(src);
|
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||||
let dest = FsPath::from(dest);
|
let src = FsPath::from(src);
|
||||||
return src
|
let dest = FsPath::from(dest);
|
||||||
.move_to(dest)
|
return src
|
||||||
.log_cxx_with_msg(|w| {
|
.move_to(dest)
|
||||||
w.write_fmt(format_args!("mv_path {} -> {} failed", src, dest))
|
.log_cxx_with_msg(|w| {
|
||||||
})
|
w.write_fmt(format_args!("mv_path {} -> {} failed", src, dest))
|
||||||
.is_ok();
|
})
|
||||||
|
.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 {
|
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) {
|
unsafe {
|
||||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||||
let src = FsPath::from(src);
|
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||||
let dest = FsPath::from(dest);
|
let src = FsPath::from(src);
|
||||||
return src
|
let dest = FsPath::from(dest);
|
||||||
.link_to(dest)
|
return src
|
||||||
.log_cxx_with_msg(|w| {
|
.link_to(dest)
|
||||||
w.write_fmt(format_args!("link_path {} -> {} failed", src, dest))
|
.log_cxx_with_msg(|w| {
|
||||||
})
|
w.write_fmt(format_args!("link_path {} -> {} failed", src, dest))
|
||||||
.is_ok();
|
})
|
||||||
|
.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 {
|
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) {
|
unsafe {
|
||||||
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
if let Ok(src) = Utf8CStr::from_ptr(src) {
|
||||||
let src = FsPath::from(src);
|
if let Ok(dest) = Utf8CStr::from_ptr(dest) {
|
||||||
let dest = FsPath::from(dest);
|
let src = FsPath::from(src);
|
||||||
return clone_attr(src, dest)
|
let dest = FsPath::from(dest);
|
||||||
.log_cxx_with_msg(|w| {
|
return clone_attr(src, dest)
|
||||||
w.write_fmt(format_args!("clone_attr {} -> {} failed", src, dest))
|
.log_cxx_with_msg(|w| {
|
||||||
})
|
w.write_fmt(format_args!("clone_attr {} -> {} failed", src, dest))
|
||||||
.is_ok();
|
})
|
||||||
|
.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 {
|
unsafe extern "C" fn fclone_attr_for_cxx(a: RawFd, b: RawFd) -> bool {
|
||||||
fclone_attr(a, b)
|
fclone_attr(a, b)
|
||||||
.log_cxx_with_msg(|w| w.write_str("fclone_attr failed"))
|
.log_cxx_with_msg(|w| w.write_str("fclone_attr failed"))
|
||||||
.is_ok()
|
.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) {
|
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 {
|
unsafe extern "C" fn str_ptr(this: &&Utf8CStr) -> *const u8 {
|
||||||
this.as_ptr().cast()
|
this.as_ptr().cast()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[export_name = "cxx$utf8str$len"]
|
#[unsafe(export_name = "cxx$utf8str$len")]
|
||||||
unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize {
|
unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize {
|
||||||
this.len()
|
this.len()
|
||||||
}
|
}
|
||||||
|
|||||||
420
native/src/base/dir.rs
Normal file
420
native/src/base/dir.rs
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
use crate::cxx_extern::readlinkat_for_cxx;
|
||||||
|
use crate::{
|
||||||
|
cstr, cstr_buf, errno, fd_path, fd_set_attr, FileAttr, FsPath, LibcReturn, Utf8CStr,
|
||||||
|
Utf8CStrBuf,
|
||||||
|
};
|
||||||
|
use libc::{dirent, O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY};
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||||
|
use std::{io, mem, slice};
|
||||||
|
|
||||||
|
pub struct DirEntry<'a> {
|
||||||
|
dir: &'a Directory,
|
||||||
|
entry: &'a dirent,
|
||||||
|
d_name_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirEntry<'_> {
|
||||||
|
pub fn name(&self) -> &CStr {
|
||||||
|
unsafe {
|
||||||
|
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
|
||||||
|
self.d_name.as_ptr().cast(),
|
||||||
|
self.d_name_len,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
self.dir.path(buf)?;
|
||||||
|
buf.push_str("/");
|
||||||
|
buf.push_lossy(self.name().to_bytes());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_dir(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_DIR
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_file(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_REG
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_symlink(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_LNK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_block_device(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_BLK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_char_device(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_CHR
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_fifo(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_FIFO
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_socket(&self) -> bool {
|
||||||
|
self.d_type == libc::DT_SOCK
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlink(&self) -> io::Result<()> {
|
||||||
|
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
||||||
|
unsafe {
|
||||||
|
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
buf.clear();
|
||||||
|
unsafe {
|
||||||
|
let r = readlinkat_for_cxx(
|
||||||
|
self.dir.as_raw_fd(),
|
||||||
|
self.d_name.as_ptr(),
|
||||||
|
buf.as_mut_ptr().cast(),
|
||||||
|
buf.capacity(),
|
||||||
|
)
|
||||||
|
.check_os_err()? as usize;
|
||||||
|
buf.set_len(r);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn open_fd(&self, flags: i32) -> io::Result<RawFd> {
|
||||||
|
unsafe { self.dir.open_raw_fd(self.name(), flags, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
||||||
|
if !self.is_dir() {
|
||||||
|
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
||||||
|
}
|
||||||
|
unsafe { Directory::try_from(OwnedFd::from_raw_fd(self.open_fd(O_RDONLY)?)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
||||||
|
if self.is_dir() {
|
||||||
|
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
||||||
|
}
|
||||||
|
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
self.path(&mut path)?;
|
||||||
|
FsPath::from(&path).get_attr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
self.path(&mut path)?;
|
||||||
|
FsPath::from(&path).set_attr(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
self.path(&mut path)?;
|
||||||
|
FsPath::from(&path).get_secontext(con)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
self.path(&mut path)?;
|
||||||
|
FsPath::from(&path).set_secontext(con)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for DirEntry<'_> {
|
||||||
|
type Target = dirent;
|
||||||
|
|
||||||
|
fn deref(&self) -> &dirent {
|
||||||
|
self.entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Directory {
|
||||||
|
dirp: *mut libc::DIR,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum WalkResult {
|
||||||
|
Continue,
|
||||||
|
Abort,
|
||||||
|
Skip,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn open(path: &Utf8CStr) -> io::Result<Directory> {
|
||||||
|
let dirp = unsafe { libc::opendir(path.as_ptr()) }.check_os_err()?;
|
||||||
|
Ok(Directory { dirp })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) -> io::Result<Option<DirEntry<'_>>> {
|
||||||
|
*errno() = 0;
|
||||||
|
let e = unsafe { libc::readdir(self.dirp) };
|
||||||
|
if e.is_null() {
|
||||||
|
return if *errno() != 0 {
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Skip both "." and ".."
|
||||||
|
unsafe {
|
||||||
|
let entry = &*e;
|
||||||
|
let d_name = CStr::from_ptr(entry.d_name.as_ptr());
|
||||||
|
if d_name == cstr!(".") || d_name == cstr!("..") {
|
||||||
|
self.read()
|
||||||
|
} else {
|
||||||
|
let e = DirEntry {
|
||||||
|
dir: self,
|
||||||
|
entry,
|
||||||
|
d_name_len: d_name.to_bytes_with_nul().len(),
|
||||||
|
};
|
||||||
|
Ok(Some(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rewind(&mut self) {
|
||||||
|
unsafe { libc::rewinddir(self.dirp) }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn open_raw_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
|
||||||
|
unsafe {
|
||||||
|
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_fd(&self, name: &Utf8CStr, flags: i32, mode: i32) -> io::Result<OwnedFd> {
|
||||||
|
unsafe {
|
||||||
|
self.open_raw_fd(name.as_cstr(), flags, mode)
|
||||||
|
.map(|fd| OwnedFd::from_raw_fd(fd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_path(&self, path: &CStr) -> bool {
|
||||||
|
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
||||||
|
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
||||||
|
// Use fstatat to check the existence of a file instead.
|
||||||
|
unsafe {
|
||||||
|
let mut st: libc::stat = mem::zeroed();
|
||||||
|
libc::fstatat(
|
||||||
|
self.as_raw_fd(),
|
||||||
|
path.as_ptr(),
|
||||||
|
&mut st,
|
||||||
|
libc::AT_SYMLINK_NOFOLLOW,
|
||||||
|
) == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
fd_path(self.as_raw_fd(), buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||||
|
&mut self,
|
||||||
|
mut f: F,
|
||||||
|
) -> io::Result<WalkResult> {
|
||||||
|
self.post_order_walk_impl(&mut f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pre_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||||
|
&mut self,
|
||||||
|
mut f: F,
|
||||||
|
) -> io::Result<WalkResult> {
|
||||||
|
self.pre_order_walk_impl(&mut f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_all(&mut self) -> io::Result<()> {
|
||||||
|
self.post_order_walk(|e| {
|
||||||
|
e.unlink()?;
|
||||||
|
Ok(WalkResult::Continue)
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn copy_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||||
|
while let Some(ref e) = self.read()? {
|
||||||
|
let attr = e.get_attr()?;
|
||||||
|
let new_entry = DirEntry {
|
||||||
|
dir,
|
||||||
|
entry: e.entry,
|
||||||
|
d_name_len: e.d_name_len,
|
||||||
|
};
|
||||||
|
if e.is_dir() {
|
||||||
|
unsafe {
|
||||||
|
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||||
|
}
|
||||||
|
let mut src = e.open_as_dir()?;
|
||||||
|
let dest = new_entry.open_as_dir()?;
|
||||||
|
src.copy_into(&dest)?;
|
||||||
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||||
|
} else if e.is_file() {
|
||||||
|
let mut src = e.open_as_file(O_RDONLY)?;
|
||||||
|
let mut dest = unsafe {
|
||||||
|
File::from_raw_fd(dir.open_raw_fd(
|
||||||
|
e.name(),
|
||||||
|
O_WRONLY | O_CREAT | O_TRUNC,
|
||||||
|
0o777,
|
||||||
|
)?)
|
||||||
|
};
|
||||||
|
std::io::copy(&mut src, &mut dest)?;
|
||||||
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||||
|
} else if e.is_symlink() {
|
||||||
|
let mut path = cstr_buf::default();
|
||||||
|
e.read_link(&mut path)?;
|
||||||
|
unsafe {
|
||||||
|
libc::symlinkat(path.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
||||||
|
.as_os_err()?;
|
||||||
|
}
|
||||||
|
new_entry.set_attr(&attr)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||||
|
let dir_fd = self.as_raw_fd();
|
||||||
|
while let Some(ref e) = self.read()? {
|
||||||
|
if e.is_dir() && dir.contains_path(e.name()) {
|
||||||
|
// Destination folder exists, needs recursive move
|
||||||
|
let mut src = e.open_as_dir()?;
|
||||||
|
let new_entry = DirEntry {
|
||||||
|
dir,
|
||||||
|
entry: e.entry,
|
||||||
|
d_name_len: e.d_name_len,
|
||||||
|
};
|
||||||
|
let dest = new_entry.open_as_dir()?;
|
||||||
|
src.move_into(&dest)?;
|
||||||
|
return e.unlink();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libc::renameat(
|
||||||
|
dir_fd,
|
||||||
|
e.d_name.as_ptr(),
|
||||||
|
dir.as_raw_fd(),
|
||||||
|
e.d_name.as_ptr(),
|
||||||
|
)
|
||||||
|
.as_os_err()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn link_into(&mut self, dir: &Directory) -> io::Result<()> {
|
||||||
|
let dir_fd = self.as_raw_fd();
|
||||||
|
while let Some(ref e) = self.read()? {
|
||||||
|
if e.is_dir() {
|
||||||
|
unsafe {
|
||||||
|
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
||||||
|
}
|
||||||
|
let attr = e.get_attr()?;
|
||||||
|
let new_entry = DirEntry {
|
||||||
|
dir,
|
||||||
|
entry: e.entry,
|
||||||
|
d_name_len: e.d_name_len,
|
||||||
|
};
|
||||||
|
let mut src = e.open_as_dir()?;
|
||||||
|
let dest = new_entry.open_as_dir()?;
|
||||||
|
src.link_into(&dest)?;
|
||||||
|
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
libc::linkat(
|
||||||
|
dir_fd,
|
||||||
|
e.d_name.as_ptr(),
|
||||||
|
dir.as_raw_fd(),
|
||||||
|
e.d_name.as_ptr(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.as_os_err()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
fn post_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||||
|
&mut self,
|
||||||
|
f: &mut F,
|
||||||
|
) -> io::Result<WalkResult> {
|
||||||
|
use WalkResult::*;
|
||||||
|
loop {
|
||||||
|
match self.read()? {
|
||||||
|
None => return Ok(Continue),
|
||||||
|
Some(ref e) => {
|
||||||
|
if e.is_dir() {
|
||||||
|
let mut dir = e.open_as_dir()?;
|
||||||
|
if let Abort = dir.post_order_walk_impl(f)? {
|
||||||
|
return Ok(Abort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match f(e)? {
|
||||||
|
Abort => return Ok(Abort),
|
||||||
|
Skip => return Ok(Continue),
|
||||||
|
Continue => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
||||||
|
&mut self,
|
||||||
|
f: &mut F,
|
||||||
|
) -> io::Result<WalkResult> {
|
||||||
|
use WalkResult::*;
|
||||||
|
loop {
|
||||||
|
match self.read()? {
|
||||||
|
None => return Ok(Continue),
|
||||||
|
Some(ref e) => match f(e)? {
|
||||||
|
Abort => return Ok(Abort),
|
||||||
|
Skip => continue,
|
||||||
|
Continue => {
|
||||||
|
if e.is_dir() {
|
||||||
|
let mut dir = e.open_as_dir()?;
|
||||||
|
if let Abort = dir.pre_order_walk_impl(f)? {
|
||||||
|
return Ok(Abort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<OwnedFd> for Directory {
|
||||||
|
type Error = io::Error;
|
||||||
|
|
||||||
|
fn try_from(fd: OwnedFd) -> io::Result<Self> {
|
||||||
|
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) }.check_os_err()?;
|
||||||
|
Ok(Directory { dirp })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Directory {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
unsafe { libc::dirfd(self.dirp) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsFd for Directory {
|
||||||
|
fn as_fd(&self) -> BorrowedFd {
|
||||||
|
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Directory {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
libc::closedir(self.dirp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -144,6 +144,8 @@ string resolve_preinit_dir(const char *base_dir) {
|
|||||||
dir += "/unencrypted/magisk";
|
dir += "/unencrypted/magisk";
|
||||||
} else if (access((dir + "/adb").data(), F_OK) == 0) {
|
} else if (access((dir + "/adb").data(), F_OK) == 0) {
|
||||||
dir += "/adb/modules";
|
dir += "/adb/modules";
|
||||||
|
} else if (access((dir + "/watchdog").data(), F_OK) == 0) {
|
||||||
|
dir += "/watchdog/magisk";
|
||||||
} else {
|
} else {
|
||||||
dir += "/magisk";
|
dir += "/magisk";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,24 @@
|
|||||||
|
use crate::{
|
||||||
|
cstr_buf, errno, error, Directory, FsPath, FsPathBuf, FsPathFollow, LibcReturn, Utf8CStr,
|
||||||
|
Utf8CStrBuf,
|
||||||
|
};
|
||||||
|
use bytemuck::{bytes_of, bytes_of_mut, Pod};
|
||||||
|
use libc::{
|
||||||
|
c_uint, makedev, mode_t, stat, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY,
|
||||||
|
O_RDWR, O_TRUNC, O_WRONLY,
|
||||||
|
};
|
||||||
use mem::MaybeUninit;
|
use mem::MaybeUninit;
|
||||||
|
use num_traits::AsPrimitive;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
||||||
use std::ops::Deref;
|
use std::os::fd::{AsFd, BorrowedFd};
|
||||||
use std::os::fd::{AsFd, BorrowedFd, IntoRawFd};
|
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{io, mem, ptr, slice};
|
use std::{io, mem, ptr, slice};
|
||||||
|
|
||||||
use bytemuck::{bytes_of_mut, Pod};
|
|
||||||
use libc::{
|
|
||||||
c_uint, dirent, makedev, mode_t, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY,
|
|
||||||
O_RDWR, O_TRUNC, O_WRONLY,
|
|
||||||
};
|
|
||||||
use num_traits::AsPrimitive;
|
|
||||||
|
|
||||||
use crate::cxx_extern::readlinkat_for_cxx;
|
|
||||||
use crate::{
|
|
||||||
cstr, errno, error, FsPath, FsPathBuf, LibcReturn, Utf8CStr, Utf8CStrBuf, Utf8CStrBufArr,
|
|
||||||
Utf8CStrWrite,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn __open_fd_impl(path: &Utf8CStr, flags: i32, mode: mode_t) -> io::Result<OwnedFd> {
|
pub fn __open_fd_impl(path: &Utf8CStr, flags: i32, mode: mode_t) -> io::Result<OwnedFd> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).check_os_err()?;
|
let fd = libc::open(path.as_ptr(), flags, mode as c_uint).check_os_err()?;
|
||||||
@@ -41,8 +37,7 @@ macro_rules! open_fd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
pub fn fd_path(fd: RawFd, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
let mut arr = Utf8CStrBufArr::<40>::new();
|
let path = FsPathBuf::default().join("/proc/self/fd").join_fmt(fd);
|
||||||
let path = FsPathBuf::new(&mut arr).join("/proc/self/fd").join_fmt(fd);
|
|
||||||
path.read_link(buf)
|
path.read_link(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +118,7 @@ impl<T: BufRead> BufReadExt for T {
|
|||||||
|
|
||||||
pub trait WriteExt {
|
pub trait WriteExt {
|
||||||
fn write_zeros(&mut self, len: usize) -> io::Result<()>;
|
fn write_zeros(&mut self, len: usize) -> io::Result<()>;
|
||||||
|
fn write_pod<F: Pod>(&mut self, data: &F) -> io::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Write> WriteExt for T {
|
impl<T: Write> WriteExt for T {
|
||||||
@@ -135,12 +131,16 @@ impl<T: Write> WriteExt for T {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_pod<F: Pod>(&mut self, data: &F) -> io::Result<()> {
|
||||||
|
self.write_all(bytes_of(data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FileAttr {
|
pub struct FileAttr {
|
||||||
pub st: libc::stat,
|
pub st: libc::stat,
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
pub con: Utf8CStrBufArr<128>,
|
pub con: crate::Utf8CStrBufArr<128>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileAttr {
|
impl FileAttr {
|
||||||
@@ -148,7 +148,7 @@ impl FileAttr {
|
|||||||
FileAttr {
|
FileAttr {
|
||||||
st: unsafe { mem::zeroed() },
|
st: unsafe { mem::zeroed() },
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
con: Utf8CStrBufArr::new(),
|
con: crate::Utf8CStrBufArr::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,398 +187,13 @@ impl FileAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "selinux")]
|
const XATTR_NAME_SELINUX: &CStr = c"security.selinux";
|
||||||
const XATTR_NAME_SELINUX: &[u8] = b"security.selinux\0";
|
|
||||||
|
|
||||||
pub struct DirEntry<'a> {
|
|
||||||
dir: &'a Directory,
|
|
||||||
entry: &'a dirent,
|
|
||||||
d_name_len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirEntry<'_> {
|
|
||||||
pub fn d_name(&self) -> &CStr {
|
|
||||||
unsafe {
|
|
||||||
CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
|
|
||||||
self.d_name.as_ptr().cast(),
|
|
||||||
self.d_name_len,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
|
||||||
self.dir.path(buf)?;
|
|
||||||
buf.push_str("/");
|
|
||||||
buf.push_lossy(self.d_name().to_bytes());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_dir(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_DIR
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_file(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_REG
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_symlink(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_LNK
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_block_device(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_BLK
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_char_device(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_CHR
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_fifo(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_FIFO
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_socket(&self) -> bool {
|
|
||||||
self.d_type == libc::DT_SOCK
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unlink(&self) -> io::Result<()> {
|
|
||||||
let flag = if self.is_dir() { libc::AT_REMOVEDIR } else { 0 };
|
|
||||||
unsafe {
|
|
||||||
libc::unlinkat(self.dir.as_raw_fd(), self.d_name.as_ptr(), flag).check_os_err()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
|
||||||
buf.clear();
|
|
||||||
unsafe {
|
|
||||||
let r = readlinkat_for_cxx(
|
|
||||||
self.dir.as_raw_fd(),
|
|
||||||
self.d_name.as_ptr(),
|
|
||||||
buf.as_mut_ptr().cast(),
|
|
||||||
buf.capacity(),
|
|
||||||
)
|
|
||||||
.check_os_err()? as usize;
|
|
||||||
buf.set_len(r);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn open_fd(&self, flags: i32) -> io::Result<RawFd> {
|
|
||||||
self.dir.open_fd(self.d_name(), flags, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_as_dir(&self) -> io::Result<Directory> {
|
|
||||||
if !self.is_dir() {
|
|
||||||
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
|
||||||
}
|
|
||||||
unsafe { Directory::try_from(OwnedFd::from_raw_fd(self.open_fd(O_RDONLY)?)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_as_file(&self, flags: i32) -> io::Result<File> {
|
|
||||||
if self.is_dir() {
|
|
||||||
return Err(io::Error::from(io::ErrorKind::IsADirectory));
|
|
||||||
}
|
|
||||||
unsafe { Ok(File::from_raw_fd(self.open_fd(flags)?)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
|
||||||
let mut path = Utf8CStrBufArr::default();
|
|
||||||
self.path(&mut path)?;
|
|
||||||
FsPath::from(&path).get_attr()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
|
||||||
let mut path = Utf8CStrBufArr::default();
|
|
||||||
self.path(&mut path)?;
|
|
||||||
FsPath::from(&path).set_attr(attr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for DirEntry<'_> {
|
|
||||||
type Target = dirent;
|
|
||||||
|
|
||||||
fn deref(&self) -> &dirent {
|
|
||||||
self.entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Directory {
|
|
||||||
dirp: *mut libc::DIR,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum WalkResult {
|
|
||||||
Continue,
|
|
||||||
Abort,
|
|
||||||
Skip,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Directory {
|
|
||||||
pub fn open(path: &Utf8CStr) -> io::Result<Directory> {
|
|
||||||
let dirp = unsafe { libc::opendir(path.as_ptr()) }.check_os_err()?;
|
|
||||||
Ok(Directory { dirp })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self) -> io::Result<Option<DirEntry<'_>>> {
|
|
||||||
*errno() = 0;
|
|
||||||
let e = unsafe { libc::readdir(self.dirp) };
|
|
||||||
if e.is_null() {
|
|
||||||
return if *errno() != 0 {
|
|
||||||
Err(io::Error::last_os_error())
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Skip both "." and ".."
|
|
||||||
unsafe {
|
|
||||||
let entry = &*e;
|
|
||||||
let d_name = CStr::from_ptr(entry.d_name.as_ptr());
|
|
||||||
return if d_name == cstr!(".") || d_name == cstr!("..") {
|
|
||||||
self.read()
|
|
||||||
} else {
|
|
||||||
let e = DirEntry {
|
|
||||||
dir: self,
|
|
||||||
entry,
|
|
||||||
d_name_len: d_name.to_bytes_with_nul().len(),
|
|
||||||
};
|
|
||||||
Ok(Some(e))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rewind(&mut self) {
|
|
||||||
unsafe { libc::rewinddir(self.dirp) }
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn open_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result<RawFd> {
|
|
||||||
libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_path(&self, path: &CStr) -> bool {
|
|
||||||
// WARNING: Using faccessat is incorrect, because the raw linux kernel syscall
|
|
||||||
// does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2.
|
|
||||||
// Use fstatat to check the existence of a file instead.
|
|
||||||
unsafe {
|
|
||||||
let mut st: libc::stat = mem::zeroed();
|
|
||||||
libc::fstatat(
|
|
||||||
self.as_raw_fd(),
|
|
||||||
path.as_ptr(),
|
|
||||||
&mut st,
|
|
||||||
libc::AT_SYMLINK_NOFOLLOW,
|
|
||||||
) == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
|
||||||
fd_path(self.as_raw_fd(), buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn post_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
|
||||||
&mut self,
|
|
||||||
mut f: F,
|
|
||||||
) -> io::Result<WalkResult> {
|
|
||||||
self.post_order_walk_impl(&mut f)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pre_order_walk<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
|
||||||
&mut self,
|
|
||||||
mut f: F,
|
|
||||||
) -> io::Result<WalkResult> {
|
|
||||||
self.pre_order_walk_impl(&mut f)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_all(&mut self) -> io::Result<()> {
|
|
||||||
self.post_order_walk(|e| {
|
|
||||||
e.unlink()?;
|
|
||||||
Ok(WalkResult::Continue)
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_into(&mut self, dir: &Directory) -> io::Result<()> {
|
|
||||||
while let Some(ref e) = self.read()? {
|
|
||||||
let attr = e.get_attr()?;
|
|
||||||
let new_entry = DirEntry {
|
|
||||||
dir,
|
|
||||||
entry: e.entry,
|
|
||||||
d_name_len: e.d_name_len,
|
|
||||||
};
|
|
||||||
if e.is_dir() {
|
|
||||||
unsafe {
|
|
||||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
|
||||||
}
|
|
||||||
let mut src = e.open_as_dir()?;
|
|
||||||
let dest = new_entry.open_as_dir()?;
|
|
||||||
src.copy_into(&dest)?;
|
|
||||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
||||||
} else if e.is_file() {
|
|
||||||
let mut src = e.open_as_file(O_RDONLY)?;
|
|
||||||
let mut dest = unsafe {
|
|
||||||
File::from_raw_fd(dir.open_fd(
|
|
||||||
e.d_name(),
|
|
||||||
O_WRONLY | O_CREAT | O_TRUNC,
|
|
||||||
0o777,
|
|
||||||
)?)
|
|
||||||
};
|
|
||||||
std::io::copy(&mut src, &mut dest)?;
|
|
||||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
||||||
} else if e.is_symlink() {
|
|
||||||
let mut path = Utf8CStrBufArr::default();
|
|
||||||
e.read_link(&mut path)?;
|
|
||||||
unsafe {
|
|
||||||
libc::symlinkat(path.as_ptr(), dir.as_raw_fd(), e.d_name.as_ptr())
|
|
||||||
.as_os_err()?;
|
|
||||||
}
|
|
||||||
new_entry.set_attr(&attr)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn move_into(&mut self, dir: &Directory) -> io::Result<()> {
|
|
||||||
let dir_fd = self.as_raw_fd();
|
|
||||||
while let Some(ref e) = self.read()? {
|
|
||||||
if e.is_dir() && dir.contains_path(e.d_name()) {
|
|
||||||
// Destination folder exists, needs recursive move
|
|
||||||
let mut src = e.open_as_dir()?;
|
|
||||||
let new_entry = DirEntry {
|
|
||||||
dir,
|
|
||||||
entry: e.entry,
|
|
||||||
d_name_len: e.d_name_len,
|
|
||||||
};
|
|
||||||
let dest = new_entry.open_as_dir()?;
|
|
||||||
src.move_into(&dest)?;
|
|
||||||
return e.unlink();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
libc::renameat(
|
|
||||||
dir_fd,
|
|
||||||
e.d_name.as_ptr(),
|
|
||||||
dir.as_raw_fd(),
|
|
||||||
e.d_name.as_ptr(),
|
|
||||||
)
|
|
||||||
.as_os_err()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn link_into(&mut self, dir: &Directory) -> io::Result<()> {
|
|
||||||
let dir_fd = self.as_raw_fd();
|
|
||||||
while let Some(ref e) = self.read()? {
|
|
||||||
if e.is_dir() {
|
|
||||||
unsafe {
|
|
||||||
libc::mkdirat(dir.as_raw_fd(), e.d_name.as_ptr(), 0o777).as_os_err()?;
|
|
||||||
}
|
|
||||||
let attr = e.get_attr()?;
|
|
||||||
let new_entry = DirEntry {
|
|
||||||
dir,
|
|
||||||
entry: e.entry,
|
|
||||||
d_name_len: e.d_name_len,
|
|
||||||
};
|
|
||||||
let mut src = e.open_as_dir()?;
|
|
||||||
let dest = new_entry.open_as_dir()?;
|
|
||||||
src.link_into(&dest)?;
|
|
||||||
fd_set_attr(dest.as_raw_fd(), &attr)?;
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
libc::linkat(
|
|
||||||
dir_fd,
|
|
||||||
e.d_name.as_ptr(),
|
|
||||||
dir.as_raw_fd(),
|
|
||||||
e.d_name.as_ptr(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.as_os_err()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Directory {
|
|
||||||
fn post_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
|
||||||
&mut self,
|
|
||||||
f: &mut F,
|
|
||||||
) -> io::Result<WalkResult> {
|
|
||||||
use WalkResult::*;
|
|
||||||
loop {
|
|
||||||
match self.read()? {
|
|
||||||
None => return Ok(Continue),
|
|
||||||
Some(ref e) => {
|
|
||||||
if e.is_dir() {
|
|
||||||
let mut dir = e.open_as_dir()?;
|
|
||||||
if let Abort = dir.post_order_walk_impl(f)? {
|
|
||||||
return Ok(Abort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match f(e)? {
|
|
||||||
Abort => return Ok(Abort),
|
|
||||||
Skip => return Ok(Continue),
|
|
||||||
Continue => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_order_walk_impl<F: FnMut(&DirEntry) -> io::Result<WalkResult>>(
|
|
||||||
&mut self,
|
|
||||||
f: &mut F,
|
|
||||||
) -> io::Result<WalkResult> {
|
|
||||||
use WalkResult::*;
|
|
||||||
loop {
|
|
||||||
match self.read()? {
|
|
||||||
None => return Ok(Continue),
|
|
||||||
Some(ref e) => match f(e)? {
|
|
||||||
Abort => return Ok(Abort),
|
|
||||||
Skip => continue,
|
|
||||||
Continue => {
|
|
||||||
if e.is_dir() {
|
|
||||||
let mut dir = e.open_as_dir()?;
|
|
||||||
if let Abort = dir.pre_order_walk_impl(f)? {
|
|
||||||
return Ok(Abort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<OwnedFd> for Directory {
|
|
||||||
type Error = io::Error;
|
|
||||||
|
|
||||||
fn try_from(fd: OwnedFd) -> io::Result<Self> {
|
|
||||||
let dirp = unsafe { libc::fdopendir(fd.into_raw_fd()) }.check_os_err()?;
|
|
||||||
Ok(Directory { dirp })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRawFd for Directory {
|
|
||||||
fn as_raw_fd(&self) -> RawFd {
|
|
||||||
unsafe { libc::dirfd(self.dirp) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsFd for Directory {
|
|
||||||
fn as_fd(&self) -> BorrowedFd {
|
|
||||||
unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Directory {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
libc::closedir(self.dirp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FsPath {
|
impl FsPath {
|
||||||
|
pub fn follow_link(&self) -> &FsPathFollow {
|
||||||
|
unsafe { mem::transmute(self) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn open(&self, flags: i32) -> io::Result<File> {
|
pub fn open(&self, flags: i32) -> io::Result<File> {
|
||||||
Ok(File::from(open_fd!(self, flags)?))
|
Ok(File::from(open_fd!(self, flags)?))
|
||||||
}
|
}
|
||||||
@@ -588,7 +203,10 @@ impl FsPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn exists(&self) -> bool {
|
pub fn exists(&self) -> bool {
|
||||||
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
unsafe {
|
||||||
|
let mut st: stat = mem::zeroed();
|
||||||
|
libc::lstat(self.as_ptr(), &mut st) == 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename_to<T: AsRef<Utf8CStr>>(&self, name: T) -> io::Result<()> {
|
pub fn rename_to<T: AsRef<Utf8CStr>>(&self, name: T) -> io::Result<()> {
|
||||||
@@ -608,13 +226,14 @@ impl FsPath {
|
|||||||
self.remove()
|
self.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_cast)]
|
||||||
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
pub fn read_link(&self, buf: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr().cast(), buf.capacity() - 1)
|
let r = libc::readlink(self.as_ptr(), buf.as_mut_ptr(), buf.capacity() - 1)
|
||||||
.check_os_err()? as usize;
|
.check_os_err()? as isize;
|
||||||
*buf.mut_buf().get_unchecked_mut(r) = b'\0';
|
*(buf.as_mut_ptr().offset(r) as *mut u8) = b'\0';
|
||||||
buf.set_len(r);
|
buf.set_len(r as usize);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -636,7 +255,7 @@ impl FsPath {
|
|||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut arr = Utf8CStrBufArr::default();
|
let mut arr = cstr_buf::default();
|
||||||
arr.push_str(self);
|
arr.push_str(self);
|
||||||
let mut off = 1;
|
let mut off = 1;
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -688,16 +307,7 @@ impl FsPath {
|
|||||||
libc::lstat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
libc::lstat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
||||||
|
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
{
|
self.get_secontext(&mut attr.con)?;
|
||||||
let sz = libc::lgetxattr(
|
|
||||||
self.as_ptr(),
|
|
||||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
|
||||||
attr.con.as_mut_ptr().cast(),
|
|
||||||
attr.con.capacity(),
|
|
||||||
)
|
|
||||||
.check_os_err()?;
|
|
||||||
attr.con.set_len((sz - 1) as usize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(attr)
|
Ok(attr)
|
||||||
}
|
}
|
||||||
@@ -711,19 +321,45 @@ impl FsPath {
|
|||||||
|
|
||||||
#[cfg(feature = "selinux")]
|
#[cfg(feature = "selinux")]
|
||||||
if !attr.con.is_empty() {
|
if !attr.con.is_empty() {
|
||||||
libc::lsetxattr(
|
self.set_secontext(&attr.con)?;
|
||||||
self.as_ptr(),
|
|
||||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
|
||||||
attr.con.as_ptr().cast(),
|
|
||||||
attr.con.len() + 1,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.as_os_err()?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let sz = libc::lgetxattr(
|
||||||
|
self.as_ptr(),
|
||||||
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
|
con.as_mut_ptr().cast(),
|
||||||
|
con.capacity(),
|
||||||
|
);
|
||||||
|
if sz < 1 {
|
||||||
|
con.clear();
|
||||||
|
if *errno() != libc::ENODATA {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
con.set_len((sz - 1) as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
libc::lsetxattr(
|
||||||
|
self.as_ptr(),
|
||||||
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
|
con.as_ptr().cast(),
|
||||||
|
con.len() + 1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.as_os_err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn copy_to(&self, path: &FsPath) -> io::Result<()> {
|
pub fn copy_to(&self, path: &FsPath) -> io::Result<()> {
|
||||||
let attr = self.get_attr()?;
|
let attr = self.get_attr()?;
|
||||||
if attr.is_dir() {
|
if attr.is_dir() {
|
||||||
@@ -739,7 +375,7 @@ impl FsPath {
|
|||||||
let mut dest = path.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o777)?;
|
let mut dest = path.create(O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0o777)?;
|
||||||
std::io::copy(&mut src, &mut dest)?;
|
std::io::copy(&mut src, &mut dest)?;
|
||||||
} else if attr.is_symlink() {
|
} else if attr.is_symlink() {
|
||||||
let mut buf = Utf8CStrBufArr::default();
|
let mut buf = cstr_buf::default();
|
||||||
self.read_link(&mut buf)?;
|
self.read_link(&mut buf)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::symlink(buf.as_ptr(), path.as_ptr()).as_os_err()?;
|
libc::symlink(buf.as_ptr(), path.as_ptr()).as_os_err()?;
|
||||||
@@ -781,7 +417,7 @@ impl FsPath {
|
|||||||
unsafe { libc::symlink(self.as_ptr(), path.as_ptr()).as_os_err() }
|
unsafe { libc::symlink(self.as_ptr(), path.as_ptr()).as_os_err() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parent(&self, buf: &mut dyn Utf8CStrWrite) -> bool {
|
pub fn parent(&self, buf: &mut dyn Utf8CStrBuf) -> bool {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
if let Some(parent) = Path::new(self.as_str()).parent() {
|
if let Some(parent) = Path::new(self.as_str()).parent() {
|
||||||
let bytes = parent.as_os_str().as_bytes();
|
let bytes = parent.as_os_str().as_bytes();
|
||||||
@@ -795,6 +431,69 @@ impl FsPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FsPathFollow {
|
||||||
|
pub fn exists(&self) -> bool {
|
||||||
|
unsafe { libc::access(self.as_ptr(), F_OK) == 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_attr(&self) -> io::Result<FileAttr> {
|
||||||
|
let mut attr = FileAttr::new();
|
||||||
|
unsafe {
|
||||||
|
libc::stat(self.as_ptr(), &mut attr.st).as_os_err()?;
|
||||||
|
|
||||||
|
#[cfg(feature = "selinux")]
|
||||||
|
self.get_secontext(&mut attr.con)?;
|
||||||
|
}
|
||||||
|
Ok(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_attr(&self, attr: &FileAttr) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
libc::chmod(self.as_ptr(), (attr.st.st_mode & 0o777).as_()).as_os_err()?;
|
||||||
|
libc::chown(self.as_ptr(), attr.st.st_uid, attr.st.st_gid).as_os_err()?;
|
||||||
|
|
||||||
|
#[cfg(feature = "selinux")]
|
||||||
|
if !attr.con.is_empty() {
|
||||||
|
self.set_secontext(&attr.con)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_secontext(&self, con: &mut dyn Utf8CStrBuf) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let sz = libc::getxattr(
|
||||||
|
self.as_ptr(),
|
||||||
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
|
con.as_mut_ptr().cast(),
|
||||||
|
con.capacity(),
|
||||||
|
);
|
||||||
|
if sz < 1 {
|
||||||
|
con.clear();
|
||||||
|
if *errno() != libc::ENODATA {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
con.set_len((sz - 1) as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_secontext(&self, con: &Utf8CStr) -> io::Result<()> {
|
||||||
|
unsafe {
|
||||||
|
libc::setxattr(
|
||||||
|
self.as_ptr(),
|
||||||
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
|
con.as_ptr().cast(),
|
||||||
|
con.len() + 1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.as_os_err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
||||||
let mut attr = FileAttr::new();
|
let mut attr = FileAttr::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -804,12 +503,17 @@ pub fn fd_get_attr(fd: RawFd) -> io::Result<FileAttr> {
|
|||||||
{
|
{
|
||||||
let sz = libc::fgetxattr(
|
let sz = libc::fgetxattr(
|
||||||
fd,
|
fd,
|
||||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
attr.con.as_mut_ptr().cast(),
|
attr.con.as_mut_ptr().cast(),
|
||||||
attr.con.capacity(),
|
attr.con.capacity(),
|
||||||
)
|
);
|
||||||
.check_os_err()?;
|
if sz < 1 {
|
||||||
attr.con.set_len((sz - 1) as usize);
|
if *errno() != libc::ENODATA {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attr.con.set_len((sz - 1) as usize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(attr)
|
Ok(attr)
|
||||||
@@ -824,7 +528,7 @@ pub fn fd_set_attr(fd: RawFd, attr: &FileAttr) -> io::Result<()> {
|
|||||||
if !attr.con.is_empty() {
|
if !attr.con.is_empty() {
|
||||||
libc::fsetxattr(
|
libc::fsetxattr(
|
||||||
fd,
|
fd,
|
||||||
XATTR_NAME_SELINUX.as_ptr().cast(),
|
XATTR_NAME_SELINUX.as_ptr(),
|
||||||
attr.con.as_ptr().cast(),
|
attr.con.as_ptr().cast(),
|
||||||
attr.con.len() + 1,
|
attr.con.len() + 1,
|
||||||
0,
|
0,
|
||||||
@@ -889,7 +593,7 @@ impl Drop for MappedFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
unsafe extern "C" {
|
||||||
// Don't use the declaration from the libc crate as request should be u32 not i32
|
// Don't use the declaration from the libc crate as request should be u32 not i32
|
||||||
fn ioctl(fd: RawFd, request: u32, ...) -> i32;
|
fn ioctl(fd: RawFd, request: u32, ...) -> i32;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,5 @@
|
|||||||
#include "../base-rs.hpp"
|
#include "../base-rs.hpp"
|
||||||
|
|
||||||
using rust::xpipe2;
|
using rust::xpipe2;
|
||||||
using rust::fd_path;
|
using rust::fd_path;
|
||||||
|
using kv_pairs = std::vector<std::pair<std::string, std::string>>;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use num_traits::FromPrimitive;
|
|||||||
|
|
||||||
pub use cstr::*;
|
pub use cstr::*;
|
||||||
use cxx_extern::*;
|
use cxx_extern::*;
|
||||||
|
pub use dir::*;
|
||||||
|
pub use ffi::fork_dont_care;
|
||||||
pub use files::*;
|
pub use files::*;
|
||||||
pub use logging::*;
|
pub use logging::*;
|
||||||
pub use misc::*;
|
pub use misc::*;
|
||||||
@@ -15,6 +17,7 @@ pub use result::*;
|
|||||||
|
|
||||||
mod cstr;
|
mod cstr;
|
||||||
mod cxx_extern;
|
mod cxx_extern;
|
||||||
|
mod dir;
|
||||||
mod files;
|
mod files;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod misc;
|
mod misc;
|
||||||
@@ -42,6 +45,7 @@ pub mod ffi {
|
|||||||
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
|
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
|
||||||
|
|
||||||
fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>;
|
fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>;
|
||||||
|
fn fork_dont_care() -> i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use num_derive::{FromPrimitive, ToPrimitive};
|
|||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
use crate::ffi::LogLevelCxx;
|
use crate::ffi::LogLevelCxx;
|
||||||
use crate::{Utf8CStr, Utf8CStrBufArr};
|
use crate::{cstr_buf, Utf8CStr};
|
||||||
|
|
||||||
// Ugly hack to avoid using enum
|
// Ugly hack to avoid using enum
|
||||||
#[allow(non_snake_case, non_upper_case_globals)]
|
#[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);
|
f(logger.write);
|
||||||
if matches!(level, LogLevel::ErrorCxx) && (logger.flags & LogFlag::ExitOnError) != 0 {
|
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) {
|
pub fn log_with_formatter<F: FnOnce(Formatter) -> fmt::Result>(level: LogLevel, f: F) {
|
||||||
log_with_writer(level, |write| {
|
log_with_writer(level, |write| {
|
||||||
let mut buf = Utf8CStrBufArr::default();
|
let mut buf = cstr_buf::default();
|
||||||
f(&mut buf).ok();
|
f(&mut buf).ok();
|
||||||
write(level, &buf);
|
write(level, &buf);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
use std::fmt::Arguments;
|
use crate::{ffi, StrErr, Utf8CStr};
|
||||||
use std::io::Write;
|
|
||||||
use std::process::exit;
|
|
||||||
use std::{fmt, io, slice, str};
|
|
||||||
|
|
||||||
use argh::EarlyExit;
|
use argh::EarlyExit;
|
||||||
use libc::c_char;
|
use libc::c_char;
|
||||||
|
use std::fmt::Arguments;
|
||||||
use crate::{ffi, StrErr, Utf8CStr};
|
use std::io::Write;
|
||||||
|
use std::mem::ManuallyDrop;
|
||||||
|
use std::process::exit;
|
||||||
|
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::{fmt, io, slice, str};
|
||||||
|
|
||||||
pub fn errno() -> &'static mut i32 {
|
pub fn errno() -> &'static mut i32 {
|
||||||
unsafe { &mut *libc::__errno() }
|
unsafe { &mut *libc::__errno() }
|
||||||
@@ -15,20 +16,24 @@ pub fn errno() -> &'static mut i32 {
|
|||||||
// When len is 0, don't care whether buf is null or not
|
// When len is 0, don't care whether buf is null or not
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn slice_from_ptr<'a, T>(buf: *const T, len: usize) -> &'a [T] {
|
pub unsafe fn slice_from_ptr<'a, T>(buf: *const T, len: usize) -> &'a [T] {
|
||||||
if len == 0 {
|
unsafe {
|
||||||
&[]
|
if len == 0 {
|
||||||
} else {
|
&[]
|
||||||
slice::from_raw_parts(buf, len)
|
} else {
|
||||||
|
slice::from_raw_parts(buf, len)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When len is 0, don't care whether buf is null or not
|
// When len is 0, don't care whether buf is null or not
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn slice_from_ptr_mut<'a, T>(buf: *mut T, len: usize) -> &'a mut [T] {
|
pub unsafe fn slice_from_ptr_mut<'a, T>(buf: *mut T, len: usize) -> &'a mut [T] {
|
||||||
if len == 0 {
|
unsafe {
|
||||||
&mut []
|
if len == 0 {
|
||||||
} else {
|
&mut []
|
||||||
slice::from_raw_parts_mut(buf, len)
|
} else {
|
||||||
|
slice::from_raw_parts_mut(buf, len)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,3 +166,52 @@ impl<T: Write> fmt::Write for FmtAdaptor<'_, T> {
|
|||||||
self.0.write_fmt(args).map_err(|_| fmt::Error)
|
self.0.write_fmt(args).map_err(|_| fmt::Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AtomicArc<T> {
|
||||||
|
ptr: AtomicPtr<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AtomicArc<T> {
|
||||||
|
pub fn new(arc: Arc<T>) -> AtomicArc<T> {
|
||||||
|
let raw = Arc::into_raw(arc);
|
||||||
|
Self {
|
||||||
|
ptr: AtomicPtr::new(raw as *mut _),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&self) -> Arc<T> {
|
||||||
|
let raw = self.ptr.load(Ordering::Acquire);
|
||||||
|
// SAFETY: the raw pointer is always created from Arc::into_raw
|
||||||
|
let arc = ManuallyDrop::new(unsafe { Arc::from_raw(raw) });
|
||||||
|
ManuallyDrop::into_inner(arc.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swap_ptr(&self, raw: *const T) -> Arc<T> {
|
||||||
|
let prev = self.ptr.swap(raw as *mut _, Ordering::AcqRel);
|
||||||
|
// SAFETY: the raw pointer is always created from Arc::into_raw
|
||||||
|
unsafe { Arc::from_raw(prev) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap(&self, arc: Arc<T>) -> Arc<T> {
|
||||||
|
let raw = Arc::into_raw(arc);
|
||||||
|
self.swap_ptr(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store(&self, arc: Arc<T>) {
|
||||||
|
// Drop the previous value
|
||||||
|
let _ = self.swap(arc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for AtomicArc<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Drop the internal value
|
||||||
|
let _ = self.swap_ptr(std::ptr::null());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> Default for AtomicArc<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ impl<T> SilentResultExt<T> for Option<T> {
|
|||||||
pub trait ResultExt<T> {
|
pub trait ResultExt<T> {
|
||||||
fn log(self) -> LoggedResult<T>;
|
fn log(self) -> LoggedResult<T>;
|
||||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> 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
|
// Internal C++ bridging logging routines
|
||||||
@@ -105,6 +106,17 @@ impl<T, R: Loggable<T>> ResultExt<T> for R {
|
|||||||
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
|
||||||
self.do_log_msg(LogLevel::Error, Some(Location::caller()), f)
|
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> {
|
impl<T> Loggable<T> for LoggedResult<T> {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ int xopenat(int dirfd, const char *pathname, int flags, mode_t mode = 0);
|
|||||||
ssize_t xwrite(int fd, const void *buf, size_t count);
|
ssize_t xwrite(int fd, const void *buf, size_t count);
|
||||||
ssize_t xread(int fd, 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);
|
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 xsetns(int fd, int nstype);
|
||||||
int xunshare(int flags);
|
int xunshare(int flags);
|
||||||
DIR *xopendir(const char *name);
|
DIR *xopendir(const char *name);
|
||||||
@@ -26,33 +25,22 @@ int xsocket(int domain, int type, int protocol);
|
|||||||
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||||
int xlisten(int sockfd, int backlog);
|
int xlisten(int sockfd, int backlog);
|
||||||
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
|
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 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 xstat(const char *pathname, struct stat *buf);
|
||||||
int xlstat(const char *pathname, struct stat *buf);
|
|
||||||
int xfstat(int fd, 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 xdup(int fd);
|
||||||
int xdup2(int oldfd, int newfd);
|
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 xreadlink(const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
||||||
ssize_t xreadlinkat(
|
ssize_t xreadlinkat(
|
||||||
int dirfd, const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
int dirfd, const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz);
|
||||||
int xsymlink(const char *target, const char *linkpath);
|
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,
|
int xmount(const char *source, const char *target,
|
||||||
const char *filesystemtype, unsigned long mountflags,
|
const char *filesystemtype, unsigned long mountflags,
|
||||||
const void *data);
|
const void *data);
|
||||||
int xumount(const char *target);
|
|
||||||
int xumount2(const char *target, int flags);
|
int xumount2(const char *target, int flags);
|
||||||
int xrename(const char *oldpath, const char *newpath);
|
int xrename(const char *oldpath, const char *newpath);
|
||||||
int xmkdir(const char *pathname, mode_t mode);
|
int xmkdir(const char *pathname, mode_t mode);
|
||||||
int xmkdirs(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);
|
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||||
pid_t xfork();
|
pid_t xfork();
|
||||||
int xpoll(pollfd *fds, nfds_t nfds, int timeout);
|
int xpoll(pollfd *fds, nfds_t nfds, int timeout);
|
||||||
|
|||||||
@@ -2,15 +2,14 @@
|
|||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
use libc::{
|
use libc::{
|
||||||
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
|
c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t,
|
||||||
ssize_t, SYS_dup3,
|
ssize_t,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::cxx_extern::readlinkat_for_cxx;
|
use crate::cxx_extern::readlinkat_for_cxx;
|
||||||
use crate::{errno, raw_cstr, CxxResultExt, FsPath, Utf8CStr, Utf8CStrBufRef};
|
use crate::{CxxResultExt, FsPath, Utf8CStr, Utf8CStrBufRef, errno, raw_cstr};
|
||||||
|
|
||||||
fn ptr_to_str<'a, T>(ptr: *const T) -> &'a str {
|
fn ptr_to_str<'a, T>(ptr: *const T) -> &'a str {
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
@@ -50,93 +49,107 @@ mod c_export {
|
|||||||
|
|
||||||
use crate::{slice_from_ptr, slice_from_ptr_mut};
|
use crate::{slice_from_ptr, slice_from_ptr_mut};
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize {
|
||||||
super::xwrite(fd, slice_from_ptr(buf, bufsz))
|
unsafe { super::xwrite(fd, slice_from_ptr(buf, bufsz)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xxread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize {
|
unsafe extern "C" fn xxread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize {
|
||||||
super::xxread(fd, slice_from_ptr_mut(buf, bufsz))
|
unsafe { super::xxread(fd, slice_from_ptr_mut(buf, bufsz)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||||
match Utf8CStr::from_ptr(path) {
|
unsafe {
|
||||||
Ok(p) => {
|
match Utf8CStr::from_ptr(path) {
|
||||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
Ok(p) => {
|
||||||
FsPath::from(p)
|
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||||
.realpath(&mut buf)
|
FsPath::from(p)
|
||||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("realpath {} failed", p)))
|
.realpath(&mut buf)
|
||||||
.map_or(-1, |_| buf.len() as isize)
|
.log_cxx_with_msg(|w| w.write_fmt(format_args!("realpath {} failed", p)))
|
||||||
|
.map_or(-1, |_| buf.len() as isize)
|
||||||
|
}
|
||||||
|
Err(_) => -1,
|
||||||
}
|
}
|
||||||
Err(_) => -1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize {
|
||||||
match Utf8CStr::from_ptr(path) {
|
unsafe {
|
||||||
Ok(p) => {
|
match Utf8CStr::from_ptr(path) {
|
||||||
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
Ok(p) => {
|
||||||
FsPath::from(p)
|
let mut buf = Utf8CStrBufRef::from_ptr(buf, bufsz);
|
||||||
.read_link(&mut buf)
|
FsPath::from(p)
|
||||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("readlink {} failed", p)))
|
.read_link(&mut buf)
|
||||||
.map_or(-1, |_| buf.len() as isize)
|
.log_cxx_with_msg(|w| w.write_fmt(format_args!("readlink {} failed", p)))
|
||||||
|
.map_or(-1, |_| buf.len() as isize)
|
||||||
|
}
|
||||||
|
Err(_) => -1,
|
||||||
}
|
}
|
||||||
Err(_) => -1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xreadlinkat(
|
unsafe extern "C" fn xreadlinkat(
|
||||||
dirfd: RawFd,
|
dirfd: RawFd,
|
||||||
path: *const c_char,
|
path: *const c_char,
|
||||||
buf: *mut u8,
|
buf: *mut u8,
|
||||||
bufsz: usize,
|
bufsz: usize,
|
||||||
) -> isize {
|
) -> isize {
|
||||||
let r = readlinkat_for_cxx(dirfd, path, buf, bufsz);
|
unsafe {
|
||||||
if r < 0 {
|
let r = readlinkat_for_cxx(dirfd, path, buf, bufsz);
|
||||||
perror!("readlinkat {}", ptr_to_str(path))
|
if r < 0 {
|
||||||
|
perror!("readlinkat {}", ptr_to_str(path))
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xfopen(path: *const c_char, mode: *const c_char) -> *mut libc::FILE {
|
unsafe extern "C" fn xfopen(path: *const c_char, mode: *const c_char) -> *mut libc::FILE {
|
||||||
let fp = libc::fopen(path, mode);
|
unsafe {
|
||||||
if fp.is_null() {
|
let fp = libc::fopen(path, mode);
|
||||||
perror!("fopen {}", ptr_to_str(path));
|
if fp.is_null() {
|
||||||
|
perror!("fopen {}", ptr_to_str(path));
|
||||||
|
}
|
||||||
|
fp
|
||||||
}
|
}
|
||||||
fp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xfdopen(fd: RawFd, mode: *const c_char) -> *mut libc::FILE {
|
unsafe extern "C" fn xfdopen(fd: RawFd, mode: *const c_char) -> *mut libc::FILE {
|
||||||
let fp = libc::fdopen(fd, mode);
|
unsafe {
|
||||||
if fp.is_null() {
|
let fp = libc::fdopen(fd, mode);
|
||||||
perror!("fdopen");
|
if fp.is_null() {
|
||||||
|
perror!("fdopen");
|
||||||
|
}
|
||||||
|
fp
|
||||||
}
|
}
|
||||||
fp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xopen(path: *const c_char, flags: i32, mode: mode_t) -> RawFd {
|
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);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::open(path, flags, mode as c_uint);
|
||||||
perror!("open {}", ptr_to_str(path));
|
if r < 0 {
|
||||||
|
perror!("open {}", ptr_to_str(path));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xopenat(dirfd: RawFd, path: *const c_char, flags: i32, mode: mode_t) -> RawFd {
|
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);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::openat(dirfd, path, flags, mode as c_uint);
|
||||||
perror!("openat {}", ptr_to_str(path));
|
if r < 0 {
|
||||||
|
perror!("openat {}", ptr_to_str(path));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fully write data slice
|
// Fully write data slice
|
||||||
@@ -168,7 +181,7 @@ fn xwrite(fd: RawFd, data: &[u8]) -> isize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xread(fd: RawFd, buf: *mut c_void, bufsz: usize) -> isize {
|
unsafe extern "C" fn xread(fd: RawFd, buf: *mut c_void, bufsz: usize) -> isize {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::read(fd, buf, bufsz);
|
let r = libc::read(fd, buf, bufsz);
|
||||||
@@ -208,17 +221,6 @@ fn xxread(fd: RawFd, data: &mut [u8]) -> isize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
extern "C" fn xlseek64(fd: RawFd, offset: i64, whence: i32) -> i64 {
|
|
||||||
unsafe {
|
|
||||||
let r = libc::lseek64(fd, offset, whence);
|
|
||||||
if r < 0 {
|
|
||||||
perror!("lseek64");
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32 {
|
pub(crate) fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::pipe2(fds.as_mut_ptr(), flags);
|
let r = libc::pipe2(fds.as_mut_ptr(), flags);
|
||||||
@@ -229,7 +231,7 @@ pub(crate) fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn xsetns(fd: RawFd, nstype: i32) -> i32 {
|
extern "C" fn xsetns(fd: RawFd, nstype: i32) -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::setns(fd, nstype);
|
let r = libc::setns(fd, nstype);
|
||||||
@@ -240,7 +242,7 @@ extern "C" fn xsetns(fd: RawFd, nstype: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn xunshare(flags: i32) -> i32 {
|
extern "C" fn xunshare(flags: i32) -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::unshare(flags);
|
let r = libc::unshare(flags);
|
||||||
@@ -251,16 +253,18 @@ extern "C" fn xunshare(flags: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xopendir(path: *const c_char) -> *mut libc::DIR {
|
unsafe extern "C" fn xopendir(path: *const c_char) -> *mut libc::DIR {
|
||||||
let dp = libc::opendir(path);
|
unsafe {
|
||||||
if dp.is_null() {
|
let dp = libc::opendir(path);
|
||||||
perror!("opendir {}", ptr_to_str(path));
|
if dp.is_null() {
|
||||||
|
perror!("opendir {}", ptr_to_str(path));
|
||||||
|
}
|
||||||
|
dp
|
||||||
}
|
}
|
||||||
dp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR {
|
extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR {
|
||||||
unsafe {
|
unsafe {
|
||||||
let dp = libc::fdopendir(fd);
|
let dp = libc::fdopendir(fd);
|
||||||
@@ -271,27 +275,29 @@ extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xreaddir(dirp: *mut libc::DIR) -> *mut libc::dirent {
|
unsafe extern "C" fn xreaddir(dirp: *mut libc::DIR) -> *mut libc::dirent {
|
||||||
*errno() = 0;
|
unsafe {
|
||||||
loop {
|
*errno() = 0;
|
||||||
let e = libc::readdir(dirp);
|
loop {
|
||||||
if e.is_null() {
|
let e = libc::readdir(dirp);
|
||||||
if *errno() != 0 {
|
if e.is_null() {
|
||||||
perror!("readdir")
|
if *errno() != 0 {
|
||||||
}
|
perror!("readdir")
|
||||||
} else {
|
}
|
||||||
// Filter out . and ..
|
} else {
|
||||||
let s = (*e).d_name.as_ptr();
|
// Filter out . and ..
|
||||||
if libc::strcmp(s, raw_cstr!(".")) == 0 || libc::strcmp(s, raw_cstr!("..")) == 0 {
|
let s = (*e).d_name.as_ptr();
|
||||||
continue;
|
if libc::strcmp(s, raw_cstr!(".")) == 0 || libc::strcmp(s, raw_cstr!("..")) == 0 {
|
||||||
}
|
continue;
|
||||||
};
|
}
|
||||||
return e;
|
};
|
||||||
|
return e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn xsetsid() -> i32 {
|
extern "C" fn xsetsid() -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::setsid();
|
let r = libc::setsid();
|
||||||
@@ -302,7 +308,7 @@ extern "C" fn xsetsid() -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn xsocket(domain: i32, ty: i32, protocol: i32) -> RawFd {
|
extern "C" fn xsocket(domain: i32, ty: i32, protocol: i32) -> RawFd {
|
||||||
unsafe {
|
unsafe {
|
||||||
let fd = libc::socket(domain, ty, protocol);
|
let fd = libc::socket(domain, ty, protocol);
|
||||||
@@ -313,16 +319,18 @@ extern "C" fn xsocket(domain: i32, ty: i32, protocol: i32) -> RawFd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xbind(socket: i32, address: *const sockaddr, len: socklen_t) -> i32 {
|
unsafe extern "C" fn xbind(socket: i32, address: *const sockaddr, len: socklen_t) -> i32 {
|
||||||
let r = libc::bind(socket, address, len);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::bind(socket, address, len);
|
||||||
perror!("bind");
|
if r < 0 {
|
||||||
|
perror!("bind");
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn xlisten(socket: i32, backlog: i32) -> i32 {
|
extern "C" fn xlisten(socket: i32, backlog: i32) -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::listen(socket, backlog);
|
let r = libc::listen(socket, backlog);
|
||||||
@@ -333,103 +341,56 @@ extern "C" fn xlisten(socket: i32, backlog: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xaccept4(
|
unsafe extern "C" fn xaccept4(
|
||||||
sockfd: RawFd,
|
sockfd: RawFd,
|
||||||
addr: *mut sockaddr,
|
addr: *mut sockaddr,
|
||||||
len: *mut socklen_t,
|
len: *mut socklen_t,
|
||||||
flg: i32,
|
flg: i32,
|
||||||
) -> RawFd {
|
) -> RawFd {
|
||||||
let fd = libc::accept4(sockfd, addr, len, flg);
|
unsafe {
|
||||||
if fd < 0 {
|
let fd = libc::accept4(sockfd, addr, len, flg);
|
||||||
perror!("accept4");
|
if fd < 0 {
|
||||||
|
perror!("accept4");
|
||||||
|
}
|
||||||
|
fd
|
||||||
}
|
}
|
||||||
fd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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 {
|
unsafe extern "C" fn xaccess(path: *const c_char, mode: i32) -> i32 {
|
||||||
let r = libc::access(path, mode);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::access(path, mode);
|
||||||
perror!("access {}", ptr_to_str(path));
|
if r < 0 {
|
||||||
|
perror!("access {}", ptr_to_str(path));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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 extern "C" fn xstat(path: *const c_char, buf: *mut libc::stat) -> i32 {
|
unsafe extern "C" fn xstat(path: *const c_char, buf: *mut libc::stat) -> i32 {
|
||||||
let r = libc::stat(path, buf);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::stat(path, buf);
|
||||||
perror!("stat {}", ptr_to_str(path));
|
if r < 0 {
|
||||||
|
perror!("stat {}", ptr_to_str(path));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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 extern "C" fn xfstat(fd: RawFd, buf: *mut libc::stat) -> i32 {
|
unsafe extern "C" fn xfstat(fd: RawFd, buf: *mut libc::stat) -> i32 {
|
||||||
let r = libc::fstat(fd, buf);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::fstat(fd, buf);
|
||||||
perror!("fstat");
|
if r < 0 {
|
||||||
|
perror!("fstat");
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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]
|
|
||||||
extern "C" fn xdup(oldfd: RawFd) -> RawFd {
|
extern "C" fn xdup(oldfd: RawFd) -> RawFd {
|
||||||
unsafe {
|
unsafe {
|
||||||
let fd = libc::dup(oldfd);
|
let fd = libc::dup(oldfd);
|
||||||
@@ -440,7 +401,7 @@ extern "C" fn xdup(oldfd: RawFd) -> RawFd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
|
extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
|
||||||
unsafe {
|
unsafe {
|
||||||
let fd = libc::dup2(oldfd, newfd);
|
let fd = libc::dup2(oldfd, newfd);
|
||||||
@@ -451,59 +412,18 @@ extern "C" fn xdup2(oldfd: RawFd, newfd: RawFd) -> RawFd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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 extern "C" fn xsymlink(target: *const c_char, linkpath: *const c_char) -> i32 {
|
unsafe extern "C" fn xsymlink(target: *const c_char, linkpath: *const c_char) -> i32 {
|
||||||
let r = libc::symlink(target, linkpath);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::symlink(target, linkpath);
|
||||||
perror!("symlink {} -> {}", ptr_to_str(target), ptr_to_str(linkpath));
|
if r < 0 {
|
||||||
|
perror!("symlink {} -> {}", ptr_to_str(target), ptr_to_str(linkpath));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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 extern "C" fn xmount(
|
unsafe extern "C" fn xmount(
|
||||||
src: *const c_char,
|
src: *const c_char,
|
||||||
target: *const c_char,
|
target: *const c_char,
|
||||||
@@ -511,101 +431,78 @@ unsafe extern "C" fn xmount(
|
|||||||
flags: c_ulong,
|
flags: c_ulong,
|
||||||
data: *const c_void,
|
data: *const c_void,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
let r = libc::mount(src, target, fstype, flags, data);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::mount(src, target, fstype, flags, data);
|
||||||
perror!("mount {} -> {}", ptr_to_str(src), ptr_to_str(target));
|
if r < 0 {
|
||||||
|
perror!("mount {} -> {}", ptr_to_str(src), ptr_to_str(target));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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 extern "C" fn xumount2(target: *const c_char, flags: i32) -> i32 {
|
unsafe extern "C" fn xumount2(target: *const c_char, flags: i32) -> i32 {
|
||||||
let r = libc::umount2(target, flags);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::umount2(target, flags);
|
||||||
perror!("umount2 {}", ptr_to_str(target));
|
if r < 0 {
|
||||||
|
perror!("umount2 {}", ptr_to_str(target));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xrename(oldname: *const c_char, newname: *const c_char) -> i32 {
|
unsafe extern "C" fn xrename(oldname: *const c_char, newname: *const c_char) -> i32 {
|
||||||
let r = libc::rename(oldname, newname);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::rename(oldname, newname);
|
||||||
perror!("rename {} -> {}", ptr_to_str(oldname), ptr_to_str(newname));
|
if r < 0 {
|
||||||
|
perror!("rename {} -> {}", ptr_to_str(oldname), ptr_to_str(newname));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
|
unsafe extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 {
|
||||||
let r = libc::mkdir(path, mode);
|
unsafe {
|
||||||
if r < 0 && *errno() != libc::EEXIST {
|
let r = libc::mkdir(path, mode);
|
||||||
perror!("mkdir {}", ptr_to_str(path));
|
if r < 0 && *errno() != libc::EEXIST {
|
||||||
|
perror!("mkdir {}", ptr_to_str(path));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
unsafe extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 {
|
||||||
match Utf8CStr::from_ptr(path) {
|
unsafe {
|
||||||
Ok(p) => FsPath::from(p)
|
match Utf8CStr::from_ptr(path) {
|
||||||
.mkdirs(mode)
|
Ok(p) => FsPath::from(p)
|
||||||
.log_cxx_with_msg(|w| w.write_fmt(format_args!("mkdirs {} failed", p)))
|
.mkdirs(mode)
|
||||||
.map_or(-1, |_| 0),
|
.log_cxx_with_msg(|w| w.write_fmt(format_args!("mkdirs {} failed", p)))
|
||||||
Err(_) => -1,
|
.map_or(-1, |_| 0),
|
||||||
|
Err(_) => -1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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 extern "C" fn xsendfile(
|
unsafe extern "C" fn xsendfile(
|
||||||
out_fd: RawFd,
|
out_fd: RawFd,
|
||||||
in_fd: RawFd,
|
in_fd: RawFd,
|
||||||
offset: *mut off_t,
|
offset: *mut off_t,
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> isize {
|
) -> isize {
|
||||||
let r = libc::sendfile(out_fd, in_fd, offset, count);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::sendfile(out_fd, in_fd, offset, count);
|
||||||
perror!("sendfile");
|
if r < 0 {
|
||||||
|
perror!("sendfile");
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(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]
|
|
||||||
extern "C" fn xfork() -> i32 {
|
extern "C" fn xfork() -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let r = libc::fork();
|
let r = libc::fork();
|
||||||
@@ -616,20 +513,24 @@ extern "C" fn xfork() -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xpoll(fds: *mut pollfd, nfds: nfds_t, timeout: i32) -> i32 {
|
unsafe extern "C" fn xpoll(fds: *mut pollfd, nfds: nfds_t, timeout: i32) -> i32 {
|
||||||
let r = libc::poll(fds, nfds, timeout);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::poll(fds, nfds, timeout);
|
||||||
perror!("poll");
|
if r < 0 {
|
||||||
|
perror!("poll");
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
|
unsafe extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 {
|
||||||
let r = libc::mknod(pathname, mode, dev);
|
unsafe {
|
||||||
if r < 0 {
|
let r = libc::mknod(pathname, mode, dev);
|
||||||
perror!("mknod {}", ptr_to_str(pathname));
|
if r < 0 {
|
||||||
|
perror!("mknod {}", ptr_to_str(pathname));
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magiskboot"
|
name = "magiskboot"
|
||||||
version = "0.0.0"
|
version.workspace = true
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
@@ -25,8 +25,12 @@ p256 = { workspace = true }
|
|||||||
p384 = { workspace = true }
|
p384 = { workspace = true }
|
||||||
p521 = { workspace = true }
|
p521 = { workspace = true }
|
||||||
rsa = { workspace = true, features = ["sha2"] }
|
rsa = { workspace = true, features = ["sha2"] }
|
||||||
|
block-buffer = { workspace = true }
|
||||||
|
sec1 = { workspace = true }
|
||||||
x509-cert = { workspace = true }
|
x509-cert = { workspace = true }
|
||||||
der = { workspace = true, features = ["derive", "pem"] }
|
der = { workspace = true, features = ["derive", "pem"] }
|
||||||
fdt = { workspace = true }
|
fdt = { workspace = true }
|
||||||
bytemuck = { workspace = true, features = ["derive", "min_const_generics"] }
|
bytemuck = { workspace = true, features = ["derive", "min_const_generics"] }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
|
libz-rs-sys = { workspace = true }
|
||||||
|
libbz2-rs-sys = { workspace = true }
|
||||||
|
|||||||
@@ -431,9 +431,20 @@ bool boot_img::parse_image(const uint8_t *p, format_t type) {
|
|||||||
}
|
}
|
||||||
if (k_fmt == ZIMAGE) {
|
if (k_fmt == ZIMAGE) {
|
||||||
z_hdr = reinterpret_cast<const zimage_hdr *>(kernel);
|
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");
|
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
|
// Find end of piggy
|
||||||
uint32_t zImage_size = z_hdr->end - z_hdr->start;
|
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());
|
k_fmt = check_fmt_lg(kernel, hdr->kernel_size());
|
||||||
}
|
}
|
||||||
} else {
|
} 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]);
|
fprintf(stderr, "%-*s [%s]\n", PADDING, "KERNEL_FMT", fmt2name[k_fmt]);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use pb_rs::{types::FileDescriptor, ConfigBuilder};
|
use pb_rs::{types::FileDescriptor, ConfigBuilder};
|
||||||
|
|
||||||
use crate::gen::gen_cxx_binding;
|
use crate::codegen::gen_cxx_binding;
|
||||||
|
|
||||||
#[path = "../include/gen.rs"]
|
#[path = "../include/codegen.rs"]
|
||||||
mod gen;
|
mod codegen;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rerun-if-changed=proto/update_metadata.proto");
|
println!("cargo:rerun-if-changed=proto/update_metadata.proto");
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user