You've already forked KernelSU
mirror of
https://github.com/tiann/KernelSU.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
147 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3ba483b81 | ||
|
|
e309a03515 | ||
|
|
82a304e054 | ||
|
|
b76d973f3a | ||
|
|
237e477876 | ||
|
|
47bcccdce3 | ||
|
|
ba8ca1f9f2 | ||
|
|
4837f2101c | ||
|
|
37d2914611 | ||
|
|
849164e4de | ||
|
|
683ba112aa | ||
|
|
e743722449 | ||
|
|
a5ee2ef93b | ||
|
|
3e66f5e8cf | ||
|
|
83b0aed52a | ||
|
|
498763505a | ||
|
|
8cc4ad4d80 | ||
|
|
6ec0c25173 | ||
|
|
93bcd78f89 | ||
|
|
ebf6a52237 | ||
|
|
a2906093ec | ||
|
|
a161c318a1 | ||
|
|
3f1ee2f784 | ||
|
|
60de2e4a6e | ||
|
|
d5bb79edd5 | ||
|
|
7264a00813 | ||
|
|
cf21767975 | ||
|
|
8fbdd996de | ||
|
|
2c3dcae117 | ||
|
|
b024b5d006 | ||
|
|
d5bab2317e | ||
|
|
0c8b4a48de | ||
|
|
f9b3218ded | ||
|
|
acc37fb387 | ||
|
|
f50b4dfe34 | ||
|
|
9ce7351aaa | ||
|
|
c691a1adb2 | ||
|
|
9a2a21ec5d | ||
|
|
a9fd0aa132 | ||
|
|
198674d889 | ||
|
|
203a5683ac | ||
|
|
eeb8cda175 | ||
|
|
b268971323 | ||
|
|
051fc53a4f | ||
|
|
55602f1f16 | ||
|
|
42428345ff | ||
|
|
ca950d909b | ||
|
|
20ff530962 | ||
|
|
a5dbbf4881 | ||
|
|
e91b1fc89a | ||
|
|
4e35e4ae86 | ||
|
|
6d15cb7e33 | ||
|
|
095acad8a6 | ||
|
|
c187d1ad8a | ||
|
|
f6967d2cfb | ||
|
|
388d2b0b59 | ||
|
|
d9aecbcbca | ||
|
|
eabdf3e78c | ||
|
|
a1fb7c5fdf | ||
|
|
89394245b1 | ||
|
|
91f1eb2d6a | ||
|
|
ab5f6db54b | ||
|
|
626642af76 | ||
|
|
473f02396f | ||
|
|
aa4b1bf9d8 | ||
|
|
8e5a72fc35 | ||
|
|
7302653879 | ||
|
|
b2b563547c | ||
|
|
cc29ad151a | ||
|
|
a4a93d8945 | ||
|
|
3389cd0aea | ||
|
|
d26956ff72 | ||
|
|
66284bfbe3 | ||
|
|
14be75629b | ||
|
|
d77ab8dbff | ||
|
|
c2ac548ac7 | ||
|
|
ad4d8e939e | ||
|
|
aa7a00b299 | ||
|
|
1ff421365e | ||
|
|
0a12d0139d | ||
|
|
181ab4f545 | ||
|
|
3e29e98f2c | ||
|
|
a66c1de660 | ||
|
|
0c322a33bc | ||
|
|
cd33a6dd07 | ||
|
|
c1dceaf11f | ||
|
|
3181dd17bc | ||
|
|
c93fa1af59 | ||
|
|
9f4a8d3dfc | ||
|
|
4da829792f | ||
|
|
1a073224c3 | ||
|
|
175de861bf | ||
|
|
aa73c34db2 | ||
|
|
ed2176af8c | ||
|
|
49f7d56fdd | ||
|
|
71cc166f72 | ||
|
|
8ee00839dc | ||
|
|
6239662a7f | ||
|
|
216c2aa2cd | ||
|
|
70b8b43b48 | ||
|
|
430c2e709f | ||
|
|
b975950b07 | ||
|
|
ed42cf42d2 | ||
|
|
d80c282644 | ||
|
|
a05edb3872 | ||
|
|
43b8987b4b | ||
|
|
219ea1c458 | ||
|
|
bea93f6ad7 | ||
|
|
e4267848f0 | ||
|
|
bc5953b510 | ||
|
|
e41b7cd117 | ||
|
|
cab78e7893 | ||
|
|
199f5cc223 | ||
|
|
962649f7ca | ||
|
|
7b32c0e37b | ||
|
|
12f353a1ae | ||
|
|
c26e170c87 | ||
|
|
6a706de09e | ||
|
|
2a4675e25b | ||
|
|
0bc36b3299 | ||
|
|
dc902b16d4 | ||
|
|
86998a032e | ||
|
|
1727ec41c4 | ||
|
|
3eb812be5b | ||
|
|
64c2f6ba5c | ||
|
|
2db7da0766 | ||
|
|
d1e7bad18f | ||
|
|
d7cef25665 | ||
|
|
23f41145b0 | ||
|
|
a969af8159 | ||
|
|
001fa00355 | ||
|
|
a6dddd32b3 | ||
|
|
cd825e34da | ||
|
|
2bba088319 | ||
|
|
4905bd9bb8 | ||
|
|
3f625a000b | ||
|
|
3bfee10a0d | ||
|
|
d5a05da5b8 | ||
|
|
d8042a36c3 | ||
|
|
85bf01eb65 | ||
|
|
4f2b8b7077 | ||
|
|
417ff8a6c5 | ||
|
|
681c4a3f0d | ||
|
|
619dd1ace1 | ||
|
|
1cd18a643d | ||
|
|
3519d61636 | ||
|
|
46913671a8 |
65
.github/scripts/build_a12.sh
vendored
65
.github/scripts/build_a12.sh
vendored
@@ -1,51 +1,56 @@
|
||||
build_from_image(){
|
||||
export TITLE=kernel-aarch64-$(echo $1 | sed 's/Image-//g')
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
build_from_image() {
|
||||
export TITLE
|
||||
TITLE=kernel-aarch64-${1//Image-/}
|
||||
echo "[+] title: $TITLE"
|
||||
|
||||
export PATCH_LEVEL=$(echo $1 | awk -F_ '{ print $2}')
|
||||
export PATCH_LEVEL
|
||||
PATCH_LEVEL=$(echo "$1" | awk -F_ '{ print $2}')
|
||||
echo "[+] patch level: $PATCH_LEVEL"
|
||||
|
||||
echo "[+] Download prebuilt ramdisk"
|
||||
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-${PATCH_LEVEL}_r1.zip
|
||||
echo '[+] Download prebuilt ramdisk'
|
||||
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-android12-5.10-"${PATCH_LEVEL}"_r1.zip
|
||||
unzip gki-kernel.zip && rm gki-kernel.zip
|
||||
|
||||
echo "[+] Unpack prebuilt boot.img"
|
||||
echo '[+] Unpack prebuilt boot.img'
|
||||
BOOT_IMG=$(find . -maxdepth 1 -name "boot*.img")
|
||||
$UNPACK_BOOTIMG --boot_img=$BOOT_IMG
|
||||
rm $BOOT_IMG
|
||||
$UNPACK_BOOTIMG --boot_img="$BOOT_IMG"
|
||||
rm "$BOOT_IMG"
|
||||
|
||||
echo "[+] Building Image.gz"
|
||||
cat Image | $GZIP -n -f -9 > Image.gz
|
||||
echo '[+] Building Image.gz'
|
||||
$GZIP -n -k -f -9 Image >Image.gz
|
||||
|
||||
echo "[+] Building boot.img"
|
||||
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level ${PATCH_LEVEL}
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
echo '[+] Building boot.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo "[+] Building boot-gz.img"
|
||||
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level ${PATCH_LEVEL}
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
echo '[+] Building boot-gz.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo "[+] Building boot-lz4.img"
|
||||
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level ${PATCH_LEVEL}
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
echo '[+] Building boot-lz4.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img --ramdisk out/ramdisk --os_version 12.0.0 --os_patch_level "${PATCH_LEVEL}"
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo "[+] Compress images"
|
||||
for image in boot*.img; do
|
||||
$GZIP -n -f -9 $image
|
||||
mv $image.gz ksu-$VERSION-$image.gz
|
||||
done
|
||||
echo '[+] Compress images'
|
||||
for image in boot*.img; do
|
||||
$GZIP -n -f -9 "$image"
|
||||
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz
|
||||
done
|
||||
|
||||
echo "[+] Images to upload"
|
||||
find . -type f -name "*.gz"
|
||||
echo "[+] Images to upload"
|
||||
find . -type f -name "*.gz"
|
||||
|
||||
find . -type f -name "*.gz" | xargs python3 $GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py
|
||||
find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
||||
}
|
||||
|
||||
for dir in Image*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "----- Building $dir -----"
|
||||
cd $dir
|
||||
build_from_image $dir
|
||||
cd "$dir"
|
||||
build_from_image "$dir"
|
||||
cd ..
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
47
.github/scripts/build_a13.sh
vendored
47
.github/scripts/build_a13.sh
vendored
@@ -1,38 +1,43 @@
|
||||
build_from_image(){
|
||||
export TITLE=kernel-aarch64-$(echo $1 | sed 's/Image-//g')
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
build_from_image() {
|
||||
export TITLE
|
||||
TITLE=kernel-aarch64-${1//Image-/}
|
||||
|
||||
echo "[+] title: $TITLE"
|
||||
echo "[+] Building Image.gz"
|
||||
cat Image | $GZIP -n -f -9 > Image.gz
|
||||
echo '[+] Building Image.gz'
|
||||
$GZIP -n -k -f -9 Image >Image.gz
|
||||
|
||||
echo "[+] Building boot.img"
|
||||
echo '[+] Building boot.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image --output boot.img
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo "[+] Building boot-gz.img"
|
||||
echo '[+] Building boot-gz.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image.gz --output boot-gz.img
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-gz.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo "[+] Building boot-lz4.img"
|
||||
echo '[+] Building boot-lz4.img'
|
||||
$MKBOOTIMG --header_version 4 --kernel Image.lz4 --output boot-lz4.img
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
$AVBTOOL add_hash_footer --partition_name boot --partition_size $((64 * 1024 * 1024)) --image boot-lz4.img --algorithm SHA256_RSA2048 --key ../kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
|
||||
|
||||
echo "[+] Compress images"
|
||||
for image in boot*.img; do
|
||||
$GZIP -n -f -9 $image
|
||||
mv $image.gz ksu-$VERSION-$image.gz
|
||||
done
|
||||
echo '[+] Compress images'
|
||||
for image in boot*.img; do
|
||||
$GZIP -n -f -9 "$image"
|
||||
mv "$image".gz ksu-"$VERSION"-"$1"-"$image".gz
|
||||
done
|
||||
|
||||
echo "[+] Images to upload"
|
||||
find . -type f -name "*.gz"
|
||||
echo '[+] Images to upload'
|
||||
find . -type f -name "*.gz"
|
||||
|
||||
find . -type f -name "*.gz" | xargs python3 $GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py
|
||||
find . -type f -name "*.gz" -exec python3 "$GITHUB_WORKSPACE"/KernelSU/scripts/ksubot.py {} +
|
||||
}
|
||||
|
||||
for dir in Image*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "----- Building $dir -----"
|
||||
cd $dir
|
||||
build_from_image $dir
|
||||
cd "$dir"
|
||||
build_from_image "$dir"
|
||||
cd ..
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
@@ -9,6 +9,7 @@ on:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'kernel/**'
|
||||
workflow_call:
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
|
||||
16
.github/workflows/build-kernel-a12.yml
vendored
16
.github/workflows/build-kernel-a12.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build_a12.sh"
|
||||
- "kernel/**"
|
||||
- "userspace/ksud/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
@@ -15,7 +14,7 @@ on:
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build-a12.sh"
|
||||
- "kernel/**"
|
||||
- "userspace/ksud/**"
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request'
|
||||
@@ -43,7 +42,7 @@ jobs:
|
||||
upload-artifacts:
|
||||
needs: build-kernel
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
|
||||
@@ -93,11 +92,20 @@ jobs:
|
||||
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
|
||||
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
export VERSION=$(git rev-list --count HEAD)
|
||||
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
cd -
|
||||
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a12.sh
|
||||
|
||||
- name: Display structure of boot files
|
||||
run: ls -R
|
||||
|
||||
- name: Upload images artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: boot-images-android12
|
||||
path: Image-android12*/*.img.gz
|
||||
|
||||
check-build-kernel:
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
|
||||
16
.github/workflows/build-kernel-a13.yml
vendored
16
.github/workflows/build-kernel-a13.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build_a13.sh"
|
||||
- "kernel/**"
|
||||
- "userspace/ksud/**"
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
@@ -15,7 +14,7 @@ on:
|
||||
- ".github/workflows/gki-kernel.yml"
|
||||
- ".github/scripts/build-a13.sh"
|
||||
- "kernel/**"
|
||||
- "userspace/ksud/**"
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-kernel:
|
||||
if: github.event_name != 'pull_request'
|
||||
@@ -44,7 +43,7 @@ jobs:
|
||||
upload-artifacts:
|
||||
needs: build-kernel
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
|
||||
env:
|
||||
CHAT_ID: ${{ secrets.CHAT_ID }}
|
||||
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
|
||||
@@ -94,10 +93,19 @@ jobs:
|
||||
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
|
||||
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
export VERSION=$(git rev-list --count HEAD)
|
||||
export VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
cd -
|
||||
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
|
||||
|
||||
- name: Display structure of boot files
|
||||
run: ls -R
|
||||
|
||||
- name: Upload images artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: boot-images-android13
|
||||
path: Image-android13*/*.img.gz
|
||||
|
||||
check-build-kernel:
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
1
.github/workflows/build-ksud.yml
vendored
1
.github/workflows/build-ksud.yml
vendored
@@ -19,6 +19,7 @@ jobs:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
- target: x86_64-linux-android
|
||||
- target: x86_64-pc-windows-gnu # only for build
|
||||
uses: ./.github/workflows/ksud.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
1
.github/workflows/build-manager.yml
vendored
1
.github/workflows/build-manager.yml
vendored
@@ -10,6 +10,7 @@ on:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'manager/**'
|
||||
workflow_call:
|
||||
jobs:
|
||||
build-ksud:
|
||||
strategy:
|
||||
|
||||
22
.github/workflows/clippy-pr.yml
vendored
Normal file
22
.github/workflows/clippy-pr.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Clippy check for pull request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/clippy-pr.yml'
|
||||
- 'userspace/ksud/**'
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
workdir: userspace/ksud
|
||||
30
.github/workflows/clippy.yml
vendored
Normal file
30
.github/workflows/clippy.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Clippy check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/clippy.yml'
|
||||
- 'userspace/ksud/**'
|
||||
|
||||
env:
|
||||
RUSTFLAGS: '-Dwarnings'
|
||||
|
||||
jobs:
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud
|
||||
|
||||
- name: Install cross
|
||||
run: cargo install cross
|
||||
|
||||
- name: Run clippy
|
||||
run: |
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android
|
||||
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android
|
||||
23
.github/workflows/ksud.yml
vendored
23
.github/workflows/ksud.yml
vendored
@@ -14,19 +14,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up cargo cache
|
||||
if: inputs.use_cache == true
|
||||
uses: actions/cache@v3
|
||||
continue-on-error: false
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: ${{ runner.os }}-cargo-
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: userspace/ksud
|
||||
cache-targets: false
|
||||
|
||||
- name: Install cross
|
||||
run: cargo install cross
|
||||
|
||||
- name: Build ksud
|
||||
run: cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml
|
||||
@@ -35,5 +32,5 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ksud-${{ inputs.target }}
|
||||
path: ./userspace/ksud/target/**/release/ksud
|
||||
path: userspace/ksud/target/**/release/ksud
|
||||
|
||||
|
||||
56
.github/workflows/release.yml
vendored
Normal file
56
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-manager:
|
||||
uses: ./.github/workflows/build-manager.yml
|
||||
secrets: inherit
|
||||
build-a12-kernel:
|
||||
uses: ./.github/workflows/build-kernel-a12.yml
|
||||
build-a13-kernel:
|
||||
uses: ./.github/workflows/build-kernel-a13.yml
|
||||
build-wsa-kernel:
|
||||
uses: ./.github/workflows/build-WSA-5.10.117-kernel.yml
|
||||
release:
|
||||
needs:
|
||||
- build-manager
|
||||
- build-a12-kernel
|
||||
- build-a13-kernel
|
||||
- build-wsa-kernel
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Zip AnyKernel3
|
||||
run: |
|
||||
for dir in AnyKernel3-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "----- Zip $dir -----"
|
||||
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Zip WSA kernel
|
||||
run: |
|
||||
for dir in kernel-WSA-*; do
|
||||
if [ -d "$dir" ]; then
|
||||
echo "------ Zip $dir ----------"
|
||||
(cd $dir && zip -r9 "$dir".zip ./* -x .git .gitignore ./*.zip && mv *.zip ..)
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
manager/*.apk
|
||||
AnyKernel3-*.zip
|
||||
boot-images-*/Image-*/*.img.gz
|
||||
kernel-WSA*.zip
|
||||
33
.github/workflows/rustfmt.yml
vendored
Normal file
33
.github/workflows/rustfmt.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Rustfmt check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/rustfmt.yml'
|
||||
- 'userspace/ksud/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/rustfmt.yml'
|
||||
- 'userspace/ksud/**'
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rustfmt
|
||||
|
||||
- uses: LoliGothick/rustfmt-check@v0.2
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
options: --manifest-path userspace/ksud/Cargo.toml
|
||||
27
.github/workflows/shellcheck.yml
vendored
Normal file
27
.github/workflows/shellcheck.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: ShellCheck
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/shellcheck.yml'
|
||||
- '**/*.sh'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- '.github/workflows/shellcheck.yml'
|
||||
- '**/*.sh'
|
||||
|
||||
jobs:
|
||||
shellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@2.0.0
|
||||
with:
|
||||
ignore_names: gradlew
|
||||
ignore_paths: ./userspace/ksud/src/installer.sh
|
||||
@@ -31,7 +31,8 @@ And the current supported ABIs are : `arm64-v8a` and `x86_64`
|
||||
|
||||
## License
|
||||
|
||||
[GPL-3](http://www.gnu.org/copyleft/gpl.html)
|
||||
- Files under `kernel` directory are [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- All other parts except `kernel` directory are [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## Credits
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ WSA 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
|
||||
|
||||
## 许可证
|
||||
|
||||
[GPL-3](http://www.gnu.org/copyleft/gpl.html)
|
||||
- 目录 `kernel` 下所有文件为 [GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
- 除 `kernel` 目录的其他部分均为 [GPL-3](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
|
||||
339
kernel/LICENSE
Normal file
339
kernel/LICENSE
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -9,8 +9,15 @@ obj-y += manager.o
|
||||
obj-y += core_hook.o
|
||||
obj-y += ksud.o
|
||||
obj-y += embed_ksud.o
|
||||
obj-y += kernel_compat.o
|
||||
|
||||
obj-y += selinux/
|
||||
# .git is a text file while the module is imported by 'git submodule add'.
|
||||
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||
# We must use the absolute path to git, otherwise the build will fail if git is not in the PATH
|
||||
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src);/usr/bin/git rev-list --count HEAD)
|
||||
ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION)
|
||||
endif
|
||||
|
||||
ifndef EXPECTED_SIZE
|
||||
EXPECTED_SIZE := 0x033b
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
#include "linux/list.h"
|
||||
#include "linux/printk.h"
|
||||
#include "linux/slab.h"
|
||||
|
||||
#include "linux/version.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "selinux/selinux.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
||||
#define FILE_FORMAT_VERSION 1 // u32
|
||||
@@ -21,7 +22,7 @@ struct perm_data {
|
||||
|
||||
static struct list_head allow_list;
|
||||
|
||||
#define KERNEL_SU_ALLOWLIST "/data/adb/.ksu_allowlist"
|
||||
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
||||
|
||||
static struct work_struct ksu_save_work;
|
||||
static struct work_struct ksu_load_work;
|
||||
@@ -118,7 +119,7 @@ void do_persistent_allow_list(struct work_struct *work)
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
loff_t off = 0;
|
||||
|
||||
KWORKER_INSTALL_KEYRING();
|
||||
struct file *fp =
|
||||
filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644);
|
||||
|
||||
@@ -128,12 +129,12 @@ void do_persistent_allow_list(struct work_struct *work)
|
||||
}
|
||||
|
||||
// store magic and version
|
||||
if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||
pr_err("save_allow_list write magic failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (kernel_write(fp, &version, sizeof(version), &off) !=
|
||||
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
|
||||
sizeof(version)) {
|
||||
pr_err("save_allow_list write version failed.\n");
|
||||
goto exit;
|
||||
@@ -143,8 +144,8 @@ void do_persistent_allow_list(struct work_struct *work)
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("save allow list uid :%d, allow: %d\n", p->uid,
|
||||
p->allow);
|
||||
kernel_write(fp, &p->uid, sizeof(p->uid), &off);
|
||||
kernel_write(fp, &p->allow, sizeof(p->allow), &off);
|
||||
ksu_kernel_write_compat(fp, &p->uid, sizeof(p->uid), &off);
|
||||
ksu_kernel_write_compat(fp, &p->allow, sizeof(p->allow), &off);
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -158,8 +159,8 @@ void do_load_allow_list(struct work_struct *work)
|
||||
struct file *fp = NULL;
|
||||
u32 magic;
|
||||
u32 version;
|
||||
|
||||
fp = filp_open("/data/adb/", O_RDONLY, 0);
|
||||
KWORKER_INSTALL_KEYRING();
|
||||
fp = filp_open("/data/adb", O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
int errno = PTR_ERR(fp);
|
||||
pr_err("load_allow_list open '/data/adb': %d\n", PTR_ERR(fp));
|
||||
@@ -194,13 +195,13 @@ void do_load_allow_list(struct work_struct *work)
|
||||
}
|
||||
|
||||
// verify magic
|
||||
if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||
magic != FILE_MAGIC) {
|
||||
pr_err("allowlist file invalid: %d!\n", magic);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (kernel_read(fp, &version, sizeof(version), &off) !=
|
||||
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
|
||||
sizeof(version)) {
|
||||
pr_err("allowlist read version: %d failed\n", version);
|
||||
goto exit;
|
||||
@@ -211,12 +212,12 @@ void do_load_allow_list(struct work_struct *work)
|
||||
while (true) {
|
||||
u32 uid;
|
||||
bool allow = false;
|
||||
ret = kernel_read(fp, &uid, sizeof(uid), &off);
|
||||
ret = ksu_kernel_read_compat(fp, &uid, sizeof(uid), &off);
|
||||
if (ret <= 0) {
|
||||
pr_info("load_allow_list read err: %d\n", ret);
|
||||
break;
|
||||
}
|
||||
ret = kernel_read(fp, &allow, sizeof(allow), &off);
|
||||
ret = ksu_kernel_read_compat(fp, &allow, sizeof(allow), &off);
|
||||
|
||||
pr_info("load_allow_uid: %d, allow: %d\n", uid, allow);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "apk_sign.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h"
|
||||
|
||||
static __always_inline int
|
||||
check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
|
||||
@@ -14,21 +15,25 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
|
||||
loff_t pos;
|
||||
|
||||
int sign = -1;
|
||||
int i;
|
||||
struct file *fp = filp_open(path, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("open %s error.", path);
|
||||
return PTR_ERR(fp);
|
||||
}
|
||||
|
||||
// disable inotify for this file
|
||||
fp->f_mode |= FMODE_NONOTIFY;
|
||||
|
||||
sign = 1;
|
||||
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
||||
for (int i = 0;; ++i) {
|
||||
for (i = 0;; ++i) {
|
||||
unsigned short n;
|
||||
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
||||
kernel_read(fp, &n, 2, &pos);
|
||||
ksu_kernel_read_compat(fp, &n, 2, &pos);
|
||||
if (n == i) {
|
||||
pos -= 22;
|
||||
kernel_read(fp, &size4, 4, &pos);
|
||||
ksu_kernel_read_compat(fp, &size4, 4, &pos);
|
||||
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
||||
break;
|
||||
}
|
||||
@@ -41,17 +46,17 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
|
||||
|
||||
pos += 12;
|
||||
// offset
|
||||
kernel_read(fp, &size4, 0x4, &pos);
|
||||
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
|
||||
pos = size4 - 0x18;
|
||||
|
||||
kernel_read(fp, &size8, 0x8, &pos);
|
||||
kernel_read(fp, buffer, 0x10, &pos);
|
||||
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
|
||||
ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
|
||||
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
pos = size4 - (size8 + 0x8);
|
||||
kernel_read(fp, &size_of_block, 0x8, &pos);
|
||||
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
|
||||
if (size_of_block != size8) {
|
||||
goto clean;
|
||||
}
|
||||
@@ -59,37 +64,37 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
|
||||
for (;;) {
|
||||
uint32_t id;
|
||||
uint32_t offset;
|
||||
kernel_read(fp, &size8, 0x8, &pos); // sequence length
|
||||
ksu_kernel_read_compat(fp, &size8, 0x8, &pos); // sequence length
|
||||
if (size8 == size_of_block) {
|
||||
break;
|
||||
}
|
||||
kernel_read(fp, &id, 0x4, &pos); // id
|
||||
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
||||
offset = 4;
|
||||
pr_info("id: 0x%08x\n", id);
|
||||
if ((id ^ 0xdeadbeefu) == 0xafa439f5u ||
|
||||
(id ^ 0xdeadbeefu) == 0x2efed62f) {
|
||||
kernel_read(fp, &size4, 0x4,
|
||||
ksu_kernel_read_compat(fp, &size4, 0x4,
|
||||
&pos); // signer-sequence length
|
||||
kernel_read(fp, &size4, 0x4, &pos); // signer length
|
||||
kernel_read(fp, &size4, 0x4,
|
||||
ksu_kernel_read_compat(fp, &size4, 0x4, &pos); // signer length
|
||||
ksu_kernel_read_compat(fp, &size4, 0x4,
|
||||
&pos); // signed data length
|
||||
offset += 0x4 * 3;
|
||||
|
||||
kernel_read(fp, &size4, 0x4,
|
||||
ksu_kernel_read_compat(fp, &size4, 0x4,
|
||||
&pos); // digests-sequence length
|
||||
pos += size4;
|
||||
offset += 0x4 + size4;
|
||||
|
||||
kernel_read(fp, &size4, 0x4,
|
||||
ksu_kernel_read_compat(fp, &size4, 0x4,
|
||||
&pos); // certificates length
|
||||
kernel_read(fp, &size4, 0x4,
|
||||
ksu_kernel_read_compat(fp, &size4, 0x4,
|
||||
&pos); // certificate length
|
||||
offset += 0x4 * 2;
|
||||
#if 0
|
||||
int hash = 1;
|
||||
signed char c;
|
||||
for (unsigned i = 0; i < size4; ++i) {
|
||||
kernel_read(fp, &c, 0x1, &pos);
|
||||
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
|
||||
hash = 31 * hash + c;
|
||||
}
|
||||
offset += size4;
|
||||
@@ -99,7 +104,7 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
|
||||
int hash = 1;
|
||||
signed char c;
|
||||
for (unsigned i = 0; i < size4; ++i) {
|
||||
kernel_read(fp, &c, 0x1, &pos);
|
||||
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
|
||||
hash = 31 * hash + c;
|
||||
}
|
||||
offset += size4;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "manager.h"
|
||||
#include "selinux/selinux.h"
|
||||
#include "uid_observer.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||
|
||||
@@ -242,6 +243,39 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_CHECK_SAFEMODE) {
|
||||
if (ksu_is_safe_mode()) {
|
||||
pr_warn("safemode enabled!\n");
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("safemode: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
|
||||
if (is_manager() || 0 == current_uid().val) {
|
||||
u32 array[128];
|
||||
u32 array_length;
|
||||
bool success = ksu_get_allow_list(array, &array_length,
|
||||
arg2 == CMD_GET_ALLOW_LIST);
|
||||
if (success) {
|
||||
if (!copy_to_user(arg4, &array_length,
|
||||
sizeof(array_length)) &&
|
||||
!copy_to_user(arg3, array,
|
||||
sizeof(u32) * array_length)) {
|
||||
if (copy_to_user(result, &reply_ok,
|
||||
sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %d\n",
|
||||
arg2);
|
||||
}
|
||||
} else {
|
||||
pr_err("prctl copy allowlist error\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all other cmds are for 'root manager'
|
||||
if (!is_manager()) {
|
||||
pr_info("Only manager can do cmd: %d\n", arg2);
|
||||
@@ -260,25 +294,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
}
|
||||
}
|
||||
ksu_show_allow_list();
|
||||
} else if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
|
||||
u32 array[128];
|
||||
u32 array_length;
|
||||
bool success = ksu_get_allow_list(array, &array_length,
|
||||
arg2 == CMD_GET_ALLOW_LIST);
|
||||
if (success) {
|
||||
if (!copy_to_user(arg4, &array_length,
|
||||
sizeof(array_length)) &&
|
||||
!copy_to_user(arg3, array,
|
||||
sizeof(u32) * array_length)) {
|
||||
if (copy_to_user(result, &reply_ok,
|
||||
sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %d\n",
|
||||
arg2);
|
||||
}
|
||||
} else {
|
||||
pr_err("prctl copy allowlist error\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -356,7 +371,26 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
// kernel 4.4 and 4.9
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||
static int ksu_key_permission(key_ref_t key_ref,
|
||||
const struct cred *cred,
|
||||
unsigned perm)
|
||||
{
|
||||
if (init_session_keyring != NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(current->comm, "init"))
|
||||
{
|
||||
// we are only interested in `init` process
|
||||
return 0;
|
||||
}
|
||||
init_session_keyring = cred->session_keyring;
|
||||
pr_info("kernel_compat: got init_session_keyring");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||
struct inode *new_inode, struct dentry *new_dentry)
|
||||
{
|
||||
@@ -366,6 +400,9 @@ static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||
static struct security_hook_list ksu_hooks[] = {
|
||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||
LSM_HOOK_INIT(key_permission, ksu_key_permission)
|
||||
#endif
|
||||
};
|
||||
|
||||
void __init ksu_lsm_hook_init(void)
|
||||
|
||||
34
kernel/kernel_compat.c
Normal file
34
kernel/kernel_compat.c
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "linux/version.h"
|
||||
#include "linux/fs.h"
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||
#include "linux/key.h"
|
||||
#include "linux/errno.h"
|
||||
struct key *init_session_keyring = NULL;
|
||||
#endif
|
||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos){
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
return kernel_read(p, buf, count, pos);
|
||||
#else
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
ssize_t result = kernel_read(p, offset, (char *)buf, count);
|
||||
if (pos && result > 0)
|
||||
{
|
||||
*pos = offset + result;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos){
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
return kernel_write(p, buf, count, pos);
|
||||
#else
|
||||
loff_t offset = pos ? *pos : 0;
|
||||
ssize_t result = kernel_write(p, buf, count, offset);
|
||||
if (pos && result > 0)
|
||||
{
|
||||
*pos = offset + result;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
42
kernel/kernel_compat.h
Normal file
42
kernel/kernel_compat.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef __KSU_H_KERNEL_COMPAT
|
||||
#define __KSU_H_KERNEL_COMPAT
|
||||
|
||||
#include "linux/fs.h"
|
||||
#include "linux/key.h"
|
||||
#include "linux/version.h"
|
||||
|
||||
extern struct key *init_session_keyring;
|
||||
|
||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos);
|
||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||
static inline int install_session_keyring(struct key *keyring)
|
||||
{
|
||||
struct cred *new;
|
||||
int ret;
|
||||
|
||||
new = prepare_creds();
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = install_session_keyring_to_cred(new, keyring);
|
||||
if (ret < 0) {
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return commit_creds(new);
|
||||
}
|
||||
#define KWORKER_INSTALL_KEYRING() \
|
||||
static bool keyring_installed = false; \
|
||||
if (init_session_keyring != NULL && !keyring_installed) \
|
||||
{ \
|
||||
install_session_keyring(init_session_keyring); \
|
||||
keyring_installed = true; \
|
||||
}
|
||||
#else
|
||||
#define KWORKER_INSTALL_KEYRING()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -3,7 +3,12 @@
|
||||
|
||||
#include "linux/workqueue.h"
|
||||
|
||||
#define KERNEL_SU_VERSION 14
|
||||
#ifndef KSU_GIT_VERSION
|
||||
#warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!"
|
||||
#define KERNEL_SU_VERSION (16)
|
||||
#else
|
||||
#define KERNEL_SU_VERSION (10000 + KSU_GIT_VERSION + 200) // major * 10000 + git version + 200 for historical reasons
|
||||
#endif
|
||||
|
||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||
|
||||
@@ -16,6 +21,7 @@
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
#define CMD_REPORT_EVENT 7
|
||||
#define CMD_SET_SEPOLICY 8
|
||||
#define CMD_CHECK_SAFEMODE 9
|
||||
|
||||
#define EVENT_POST_FS_DATA 1
|
||||
#define EVENT_BOOT_COMPLETED 2
|
||||
|
||||
111
kernel/ksud.c
111
kernel/ksud.c
@@ -3,16 +3,19 @@
|
||||
#include "linux/dcache.h"
|
||||
#include "linux/err.h"
|
||||
#include "linux/fs.h"
|
||||
#include "linux/input-event-codes.h"
|
||||
#include "linux/kprobes.h"
|
||||
#include "linux/printk.h"
|
||||
#include "linux/types.h"
|
||||
#include "linux/uaccess.h"
|
||||
#include "linux/version.h"
|
||||
#include "linux/workqueue.h"
|
||||
#include "linux/input.h"
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksud.h"
|
||||
#include "selinux/selinux.h"
|
||||
|
||||
static const char KERNEL_SU_RC[] =
|
||||
@@ -20,32 +23,35 @@ static const char KERNEL_SU_RC[] =
|
||||
|
||||
"on post-fs-data\n"
|
||||
// We should wait for the post-fs-data finish
|
||||
" exec u:r:su:s0 root -- /data/adb/ksud post-fs-data\n"
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
|
||||
"\n"
|
||||
|
||||
"on nonencrypted\n"
|
||||
" exec u:r:su:s0 root -- /data/adb/ksud services\n"
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " services\n"
|
||||
"\n"
|
||||
|
||||
"on property:vold.decrypt=trigger_restart_framework\n"
|
||||
" exec u:r:su:s0 root -- /data/adb/ksud services\n"
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " services\n"
|
||||
"\n"
|
||||
|
||||
"on property:sys.boot_completed=1\n"
|
||||
" exec u:r:su:s0 root -- /data/adb/ksud boot-completed\n"
|
||||
" exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n"
|
||||
"\n"
|
||||
|
||||
"\n";
|
||||
|
||||
static void stop_vfs_read_hook();
|
||||
static void stop_execve_hook();
|
||||
static void stop_input_hook();
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
static struct work_struct stop_vfs_read_work;
|
||||
static struct work_struct stop_execve_hook_work;
|
||||
static struct work_struct stop_input_hook_work;
|
||||
#else
|
||||
static bool vfs_read_hook = true;
|
||||
static bool execveat_hook = true;
|
||||
static bool input_hook = true;
|
||||
#endif
|
||||
|
||||
void on_post_fs_data(void)
|
||||
@@ -58,6 +64,8 @@ void on_post_fs_data(void)
|
||||
done = true;
|
||||
pr_info("ksu_load_allow_list");
|
||||
ksu_load_allow_list();
|
||||
// sanity check, this may influence the performance
|
||||
stop_input_hook();
|
||||
}
|
||||
|
||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
@@ -183,6 +191,57 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int volumedown_pressed_count = 0;
|
||||
|
||||
static bool is_volumedown_enough(unsigned int count) {
|
||||
return count >= 3;
|
||||
}
|
||||
|
||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||
int *value)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
if (!input_hook) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) {
|
||||
int val = *value;
|
||||
pr_info("KEY_VOLUMEDOWN val: %d\n", val);
|
||||
if (val) {
|
||||
// key pressed, count it
|
||||
volumedown_pressed_count += 1;
|
||||
if (is_volumedown_enough(volumedown_pressed_count)) {
|
||||
stop_input_hook();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ksu_is_safe_mode() {
|
||||
|
||||
static bool safe_mode = false;
|
||||
if (safe_mode) {
|
||||
// don't need to check again, userspace may call multiple times
|
||||
return true;
|
||||
}
|
||||
|
||||
// stop hook first!
|
||||
stop_input_hook();
|
||||
|
||||
pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count);
|
||||
if (is_volumedown_enough(volumedown_pressed_count)) {
|
||||
// pressed over 3 times
|
||||
pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n");
|
||||
safe_mode = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
|
||||
// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864
|
||||
@@ -208,14 +267,21 @@ static int read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
|
||||
}
|
||||
|
||||
static int input_handle_event_handler_pre(struct kprobe *p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs);
|
||||
unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs);
|
||||
int *value = (int *)&PT_REGS_PARM4(regs);
|
||||
return ksu_handle_input_handle_event(type, code, value);
|
||||
}
|
||||
|
||||
static struct kprobe execve_kp = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
.symbol_name = "do_execveat_common",
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && \
|
||||
LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||
.symbol_name = "__do_execve_file",
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) && \
|
||||
LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||
.symbol_name = "do_execveat_common",
|
||||
#endif
|
||||
.pre_handler = execve_handler_pre,
|
||||
@@ -226,6 +292,11 @@ static struct kprobe vfs_read_kp = {
|
||||
.pre_handler = read_handler_pre,
|
||||
};
|
||||
|
||||
static struct kprobe input_handle_event_kp = {
|
||||
.symbol_name = "input_handle_event",
|
||||
.pre_handler = input_handle_event_handler_pre,
|
||||
};
|
||||
|
||||
static void do_stop_vfs_read_hook(struct work_struct *work)
|
||||
{
|
||||
unregister_kprobe(&vfs_read_kp);
|
||||
@@ -235,6 +306,11 @@ static void do_stop_execve_hook(struct work_struct *work)
|
||||
{
|
||||
unregister_kprobe(&execve_kp);
|
||||
}
|
||||
|
||||
static void do_stop_input_hook(struct work_struct *work)
|
||||
{
|
||||
unregister_kprobe(&input_handle_event_kp);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void stop_vfs_read_hook()
|
||||
@@ -257,6 +333,21 @@ static void stop_execve_hook()
|
||||
#endif
|
||||
}
|
||||
|
||||
static void stop_input_hook()
|
||||
{
|
||||
static bool input_hook_stopped = false;
|
||||
if (input_hook_stopped) {
|
||||
return;
|
||||
}
|
||||
input_hook_stopped = true;
|
||||
#ifdef CONFIG_KPROBES
|
||||
bool ret = schedule_work(&stop_input_hook_work);
|
||||
pr_info("unregister input kprobe: %d!\n", ret);
|
||||
#else
|
||||
input_hook = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ksud: module support
|
||||
void ksu_enable_ksud()
|
||||
{
|
||||
@@ -269,7 +360,11 @@ void ksu_enable_ksud()
|
||||
ret = register_kprobe(&vfs_read_kp);
|
||||
pr_info("ksud: vfs_read_kp: %d\n", ret);
|
||||
|
||||
ret = register_kprobe(&input_handle_event_kp);
|
||||
pr_info("ksud: input_handle_event_kp: %d\n", ret);
|
||||
|
||||
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
|
||||
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
|
||||
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#ifndef __KSU_H_KSUD
|
||||
#define __KSU_H_KSUD
|
||||
|
||||
#define KSUD_PATH "/data/adb/ksud"
|
||||
|
||||
void on_post_fs_data(void);
|
||||
|
||||
bool ksu_is_safe_mode(void);
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "selinux.h"
|
||||
#include "sepolicy.h"
|
||||
#include "ss/services.h"
|
||||
#include "linux/lsm_audit.h"
|
||||
#include "xfrm.h"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||
@@ -19,12 +21,17 @@
|
||||
static struct policydb *get_policydb(void)
|
||||
{
|
||||
struct policydb *db;
|
||||
// selinux_state does not exists before 4.19
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 212)
|
||||
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
|
||||
db = &policy->policydb;
|
||||
#else
|
||||
struct selinux_ss *ss = rcu_dereference(selinux_state.ss);
|
||||
db = &ss->policydb;
|
||||
#endif
|
||||
#else
|
||||
db = &policydb;
|
||||
#endif
|
||||
return db;
|
||||
}
|
||||
@@ -59,7 +66,7 @@ void apply_kernelsu_rules()
|
||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
||||
}
|
||||
|
||||
// we need to save allowlist in /data/adb
|
||||
// we need to save allowlist in /data/adb/ksu
|
||||
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
||||
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
||||
// we may need to do mount on shell
|
||||
@@ -69,12 +76,14 @@ void apply_kernelsu_rules()
|
||||
// Android 10+:
|
||||
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
|
||||
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
|
||||
// Kernel 4.4
|
||||
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
|
||||
// Android 9-:
|
||||
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
|
||||
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
|
||||
|
||||
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
|
||||
// our ksud triggered by init
|
||||
ksu_allow(db, "init", "adb_data_file", "file", "execute");
|
||||
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
||||
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||
|
||||
// copied from Magisk rules
|
||||
@@ -159,6 +168,21 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||
static void reset_avc_cache() {
|
||||
#if ((KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 163))) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 212))
|
||||
avc_ss_reset(0);
|
||||
selnl_notify_policyload(0);
|
||||
selinux_status_update_policyload(0);
|
||||
#else
|
||||
struct selinux_avc *avc = selinux_state.avc;
|
||||
avc_ss_reset(avc, 0);
|
||||
selnl_notify_policyload(0);
|
||||
selinux_status_update_policyload(&selinux_state, 0);
|
||||
#endif
|
||||
selinux_xfrm_notify_policyload();
|
||||
}
|
||||
|
||||
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
{
|
||||
if (!arg4) {
|
||||
@@ -167,7 +191,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
|
||||
if (!getenforce()) {
|
||||
pr_info("SELinux permissive or disabled, don't apply policies.");
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sepol_data data;
|
||||
@@ -427,11 +451,15 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
}
|
||||
ret = 0;
|
||||
} else {
|
||||
pr_err("sepol: unknown cmd: %d");
|
||||
pr_err("sepol: unknown cmd: %d\n", cmd);
|
||||
}
|
||||
|
||||
exit:
|
||||
rcu_read_unlock();
|
||||
|
||||
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
||||
// we are in atomic context. so we just reset it every time.
|
||||
reset_avc_cache();
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "selinux.h"
|
||||
#include "objsec.h"
|
||||
|
||||
#include "linux/version.h"
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
#if ((KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 163))) || (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 212))
|
||||
#include "avc.h"
|
||||
#endif
|
||||
|
||||
#define KERNEL_SU_DOMAIN "u:r:su:s0"
|
||||
|
||||
@@ -54,26 +57,50 @@ if (!is_domain_permissive) {
|
||||
void setenforce(bool enforce)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 212)))
|
||||
selinux_state.enforcing = enforce;
|
||||
#else
|
||||
selinux_enforcing = enforce;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool getenforce()
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 212)))
|
||||
if (selinux_state.disabled) {
|
||||
#else
|
||||
if (selinux_disabled) {
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163)) || ((KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 212)))
|
||||
return selinux_state.enforcing;
|
||||
#else
|
||||
return false;
|
||||
return selinux_enforcing;
|
||||
#endif
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 212)
|
||||
/*
|
||||
* get the subjective security ID of the current task
|
||||
*/
|
||||
static inline u32 current_sid(void)
|
||||
{
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
|
||||
return tsec->sid;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool is_ksu_domain()
|
||||
{
|
||||
return ksu_sid && current_sid() == ksu_sid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
#include "ss/symtab.h"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||
// TODO: backport to lower kernel
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) || LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)
|
||||
// 5.4 is not tested
|
||||
#define KSU_SUPPORT_ADD_TYPE
|
||||
#endif
|
||||
|
||||
@@ -82,6 +82,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
|
||||
// https://elixir.bootlin.com/linux/v5.9-rc1/source/security/selinux/ss/symtab.h
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
|
||||
#define symtab_search(s, name) hashtab_search((s)->table, name)
|
||||
#define symtab_insert(s, name, datum) hashtab_insert((s)->table, name, datum)
|
||||
#endif
|
||||
|
||||
#define avtab_for_each(avtab, cur) \
|
||||
@@ -128,14 +129,13 @@ static struct avtab_node *get_avtab_node(struct policydb *db,
|
||||
/* this is used to get the node - insertion is actually unique */
|
||||
node = avtab_insert_nonunique(&db->te_avtab, key, &avdatum);
|
||||
|
||||
int grow_size = sizeof(u16) * 4;
|
||||
int grow_size = sizeof(struct avtab_key);
|
||||
grow_size += sizeof(struct avtab_datum);
|
||||
if (key->specified & AVTAB_XPERMS) {
|
||||
grow_size += sizeof(u8);
|
||||
grow_size += sizeof(u8);
|
||||
grow_size += sizeof(u32) *
|
||||
ARRAY_SIZE(avdatum.u.xperms->perms.p);
|
||||
} else {
|
||||
grow_size += sizeof(u32) * 1;
|
||||
}
|
||||
db->len += grow_size;
|
||||
}
|
||||
@@ -456,7 +456,9 @@ static bool add_type_rule(struct policydb *db, const char *s, const char *t,
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef KSU_SUPPORT_ADD_TYPE
|
||||
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void *datum, struct hashtab_key_params key_params)
|
||||
// 5.8.0: int hashtab_insert(struct hashtab *h, void *k, void *d);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
static u32 filenametr_hash(const void *k)
|
||||
{
|
||||
const struct filename_trans_key *ft = k;
|
||||
@@ -500,7 +502,6 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
const char *t, const char *c, const char *d,
|
||||
const char *o)
|
||||
{
|
||||
#ifdef KSU_SUPPORT_ADD_TYPE
|
||||
struct type_datum *src, *tgt, *def;
|
||||
struct class_datum *cls;
|
||||
|
||||
@@ -525,14 +526,21 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
return false;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
|
||||
struct filename_trans_key key;
|
||||
key.ttype = tgt->value;
|
||||
key.tclass = cls->value;
|
||||
key.name = (char *)o;
|
||||
|
||||
struct filename_trans_datum *last = NULL;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
struct filename_trans_datum *trans =
|
||||
policydb_filenametr_search(db, &key);
|
||||
#else
|
||||
struct filename_trans_datum *trans =
|
||||
hashtab_search(&db->filename_trans, &key);
|
||||
#endif
|
||||
while (trans) {
|
||||
if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
|
||||
// Duplicate, overwrite existing data and return
|
||||
@@ -559,8 +567,35 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
||||
|
||||
db->compat_filename_trans_count++;
|
||||
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
|
||||
#else
|
||||
return false;
|
||||
#else // < 5.7.0, has no filename_trans_key, but struct filename_trans
|
||||
|
||||
struct filename_trans key;
|
||||
key.ttype = tgt->value;
|
||||
key.tclass = cls->value;
|
||||
key.name = (char *)o;
|
||||
|
||||
struct filename_trans_datum *trans =
|
||||
hashtab_search(db->filename_trans, &key);
|
||||
|
||||
if (trans == NULL) {
|
||||
trans = (struct filename_trans_datum*) kcalloc(sizeof(*trans), 1, GFP_ATOMIC);
|
||||
if (!trans) {
|
||||
pr_err("add_filename_trans: Failed to alloc datum");
|
||||
return false;
|
||||
}
|
||||
struct filename_trans *new_key =
|
||||
(struct filename_trans*) kmalloc(sizeof(*new_key), GFP_ATOMIC);
|
||||
if (!new_key) {
|
||||
pr_err("add_filename_trans: Failed to alloc new_key");
|
||||
return false;
|
||||
}
|
||||
*new_key = key;
|
||||
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
||||
trans->otype = def->value;
|
||||
hashtab_insert(db->filename_trans, new_key, trans);
|
||||
}
|
||||
|
||||
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -580,7 +615,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
}
|
||||
|
||||
u32 value = ++db->p_types.nprim;
|
||||
type = (struct type_datum *)kmalloc(sizeof(struct type_datum),
|
||||
type = (struct type_datum *)kzalloc(sizeof(struct type_datum),
|
||||
GFP_ATOMIC);
|
||||
if (!type) {
|
||||
pr_err("add_type: alloc type_datum failed.\n");
|
||||
@@ -589,6 +624,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
|
||||
type->primary = 1;
|
||||
type->value = value;
|
||||
type->attribute = attr;
|
||||
|
||||
char *key = kstrdup(type_name, GFP_ATOMIC);
|
||||
if (!key) {
|
||||
@@ -601,6 +637,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||
size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim;
|
||||
struct ebitmap *new_type_attr_map_array =
|
||||
(krealloc(db->type_attr_map_array, new_size, GFP_ATOMIC));
|
||||
@@ -646,6 +683,112 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
// flex_array is not extensible, we need to create a new bigger one instead
|
||||
struct flex_array *new_type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap),
|
||||
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
struct flex_array *new_type_val_to_struct = flex_array_alloc(sizeof(struct type_datum *),
|
||||
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
struct flex_array *new_val_to_name_types = flex_array_alloc(sizeof(char *),
|
||||
db->symtab[SYM_TYPES].nprim, GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
if (!new_type_attr_map_array) {
|
||||
pr_err("add_type: alloc type_attr_map_array failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new_type_val_to_struct) {
|
||||
pr_err("add_type: alloc type_val_to_struct failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new_val_to_name_types) {
|
||||
pr_err("add_type: alloc val_to_name failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// preallocate so we don't have to worry about the put ever failing
|
||||
if (flex_array_prealloc(new_type_attr_map_array, 0,
|
||||
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc type_attr_map_array failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flex_array_prealloc(new_type_val_to_struct, 0,
|
||||
db->p_types.nprim, GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc type_val_to_struct_array failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flex_array_prealloc(new_val_to_name_types, 0,
|
||||
db->symtab[SYM_TYPES].nprim, GFP_ATOMIC | __GFP_ZERO)) {
|
||||
pr_err("add_type: prealloc val_to_name_types failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int j;
|
||||
void *old_elem;
|
||||
// copy the old data or pointers to new flex arrays
|
||||
for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) {
|
||||
old_elem = flex_array_get(db->type_attr_map_array, j);
|
||||
if (old_elem)
|
||||
flex_array_put(new_type_attr_map_array, j,
|
||||
old_elem, GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
|
||||
old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j);
|
||||
if (old_elem)
|
||||
flex_array_put_ptr(new_type_val_to_struct, j,
|
||||
old_elem, GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) {
|
||||
old_elem = flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j);
|
||||
if (old_elem)
|
||||
flex_array_put_ptr(new_val_to_name_types, j,
|
||||
old_elem, GFP_ATOMIC | __GFP_ZERO);
|
||||
}
|
||||
|
||||
// store the pointer of old flex arrays first, when assigning new ones we should free it
|
||||
struct flex_array *old_fa;
|
||||
|
||||
old_fa = db->type_attr_map_array;
|
||||
db->type_attr_map_array = new_type_attr_map_array;
|
||||
if (old_fa) {
|
||||
flex_array_free(old_fa);
|
||||
}
|
||||
|
||||
ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1));
|
||||
ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1),
|
||||
value - 1, 1);
|
||||
|
||||
old_fa = db->type_val_to_struct_array;
|
||||
db->type_val_to_struct_array = new_type_val_to_struct;
|
||||
if (old_fa) {
|
||||
flex_array_free(old_fa);
|
||||
}
|
||||
flex_array_put_ptr(db->type_val_to_struct_array, value - 1,
|
||||
type, GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
old_fa = db->sym_val_to_name[SYM_TYPES];
|
||||
db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
|
||||
if (old_fa) {
|
||||
flex_array_free(old_fa);
|
||||
}
|
||||
flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1,
|
||||
key, GFP_ATOMIC | __GFP_ZERO);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < db->p_roles.nprim; ++i) {
|
||||
ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
|
||||
0);
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#! /bin/bash
|
||||
|
||||
set -x
|
||||
#!/bin/sh
|
||||
set -eux
|
||||
|
||||
GKI_ROOT=$(pwd)
|
||||
|
||||
@@ -11,8 +10,8 @@ if test -d "$GKI_ROOT/common/drivers"; then
|
||||
elif test -d "$GKI_ROOT/drivers"; then
|
||||
DRIVER_DIR="$GKI_ROOT/drivers"
|
||||
else
|
||||
echo "[ERROR] "drivers/" directory is not found."
|
||||
echo "[+] You should modify this scrpit by yourself."
|
||||
echo '[ERROR] "drivers/" directory is not found.'
|
||||
echo '[+] You should modify this scrpit by yourself.'
|
||||
exit 127
|
||||
fi
|
||||
|
||||
@@ -26,9 +25,9 @@ echo "[+] Copy kernel su driver to $DRIVER_DIR"
|
||||
|
||||
test -e "$DRIVER_DIR/kernelsu" || ln -sf "$GKI_ROOT/KernelSU/kernel" "$DRIVER_DIR/kernelsu"
|
||||
|
||||
echo "[+] Add kernel su driver to Makefile"
|
||||
echo '[+] Add kernel su driver to Makefile'
|
||||
|
||||
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
|
||||
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || echo "obj-y += kernelsu/" >>"$DRIVER_MAKEFILE"
|
||||
|
||||
echo "[+] Done."
|
||||
echo '[+] Done.'
|
||||
|
||||
@@ -134,9 +134,15 @@ static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
|
||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
int *dfd = (int *)PT_REGS_PARM1(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||
const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
// static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask)
|
||||
int *flags = (int *)&PT_REGS_PARM3(regs);
|
||||
#else
|
||||
// int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag)
|
||||
int *flags = (int *)&PT_REGS_PARM4(regs);
|
||||
#endif
|
||||
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
@@ -165,18 +171,20 @@ static struct kprobe faccessat_kp = {
|
||||
};
|
||||
|
||||
static struct kprobe newfstatat_kp = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
.symbol_name = "vfs_statx",
|
||||
#else
|
||||
.symbol_name = "vfs_fstatat",
|
||||
#endif
|
||||
.pre_handler = newfstatat_handler_pre,
|
||||
};
|
||||
|
||||
static struct kprobe execve_kp = {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||
.symbol_name = "do_execveat_common",
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && \
|
||||
LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||
.symbol_name = "__do_execve_file",
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) && \
|
||||
LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||
.symbol_name = "do_execveat_common",
|
||||
#endif
|
||||
.pre_handler = execve_handler_pre,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "ksu.h"
|
||||
#include "manager.h"
|
||||
#include "uid_observer.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list"
|
||||
static struct work_struct ksu_update_uid_work;
|
||||
@@ -38,6 +39,7 @@ static bool is_uid_exist(uid_t uid, void *data)
|
||||
|
||||
static void do_update_uid(struct work_struct *work)
|
||||
{
|
||||
KWORKER_INSTALL_KEYRING();
|
||||
struct file *fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH
|
||||
@@ -54,13 +56,13 @@ static void do_update_uid(struct work_struct *work)
|
||||
loff_t line_start = 0;
|
||||
char buf[128];
|
||||
for (;;) {
|
||||
ssize_t count = kernel_read(fp, &chr, sizeof(chr), &pos);
|
||||
ssize_t count = ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
||||
if (count != sizeof(chr))
|
||||
break;
|
||||
if (chr != '\n')
|
||||
continue;
|
||||
|
||||
count = kernel_read(fp, buf, sizeof(buf), &line_start);
|
||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf), &line_start);
|
||||
|
||||
struct uid_data *data =
|
||||
kmalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||
|
||||
@@ -32,6 +32,16 @@
|
||||
android:name="android.app.lib_name"
|
||||
android:value="" />
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -61,3 +61,9 @@ Java_me_weishu_kernelsu_Natives_allowRoot(JNIEnv *env, jclass clazz, jint uid, j
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_me_weishu_kernelsu_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
|
||||
return is_safe_mode();
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
#define CMD_DENY_SU 4
|
||||
#define CMD_GET_ALLOW_LIST 5
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
#define CMD_CHECK_SAFEMODE 9
|
||||
|
||||
static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||
int32_t result = 0;
|
||||
@@ -51,4 +52,8 @@ bool get_allow_list(int *uids, int *size) {
|
||||
|
||||
bool get_deny_list(int *uids, int *size) {
|
||||
return ksuctl(CMD_GET_DENY_LIST, uids, size);
|
||||
}
|
||||
|
||||
bool is_safe_mode() {
|
||||
return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr);
|
||||
}
|
||||
@@ -15,4 +15,6 @@ bool get_allow_list(int *uids, int *size);
|
||||
|
||||
bool get_deny_list(int *uids, int *size);
|
||||
|
||||
bool is_safe_mode();
|
||||
|
||||
#endif //KERNELSU_KSU_H
|
||||
|
||||
@@ -21,4 +21,6 @@ public final class Natives {
|
||||
public static native int[] getDenyList();
|
||||
|
||||
public static native boolean allowRoot(int uid, boolean allow);
|
||||
|
||||
public static native boolean isSafeMode();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ import androidx.activity.compose.setContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -52,31 +53,35 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(navController: NavHostController) {
|
||||
val currentDestination: Destination = navController.appCurrentDestinationAsState().value
|
||||
val topDestination: Destination = navController.appCurrentDestinationAsState().value
|
||||
?: NavGraphs.root.startAppDestination
|
||||
var topDestination by rememberSaveable { mutableStateOf(currentDestination.route) }
|
||||
LaunchedEffect(currentDestination) {
|
||||
val queue = navController.backQueue
|
||||
if (queue.size == 2) topDestination = queue[1].destination.route!!
|
||||
else if (queue.size > 2) topDestination = queue[2].destination.route!!
|
||||
val bottomBarRoutes = remember {
|
||||
BottomBarDestination.values().map { it.direction.route }
|
||||
}
|
||||
|
||||
NavigationBar(tonalElevation = 8.dp) {
|
||||
BottomBarDestination.values().forEach { destination ->
|
||||
NavigationBarItem(
|
||||
selected = topDestination == destination.direction.route,
|
||||
selected = topDestination.route == destination.direction.route,
|
||||
onClick = {
|
||||
val firstRoute = navController.backQueue.reversed().first {
|
||||
it.destination.route in bottomBarRoutes
|
||||
}.destination.route
|
||||
|
||||
navController.navigate(destination.direction.route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
saveState = firstRoute != destination.direction.route
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
if (topDestination == destination.direction.route) Icon(destination.iconSelected, stringResource(destination.label))
|
||||
else Icon(destination.iconNotSelected, stringResource(destination.label))
|
||||
if (topDestination.route == destination.direction.route) {
|
||||
Icon(destination.iconSelected, stringResource(destination.label))
|
||||
} else {
|
||||
Icon(destination.iconNotSelected, stringResource(destination.label))
|
||||
}
|
||||
},
|
||||
label = { Text(stringResource(destination.label)) },
|
||||
alwaysShowLabel = false
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package me.weishu.kernelsu.ui.component
|
||||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.*
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import me.weishu.kernelsu.ui.util.LocalDialogHost
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
sealed interface DialogResult {
|
||||
object Confirmed : DialogResult
|
||||
object Dismissed : DialogResult
|
||||
}
|
||||
|
||||
interface DialogVisuals {
|
||||
val title: String
|
||||
val content: String
|
||||
val confirm: String?
|
||||
val dismiss: String?
|
||||
}
|
||||
|
||||
interface DialogData {
|
||||
val visuals: DialogVisuals
|
||||
|
||||
fun confirm()
|
||||
|
||||
fun dismiss()
|
||||
}
|
||||
|
||||
class DialogHostState {
|
||||
|
||||
private data class DialogVisualsImpl(
|
||||
override val title: String,
|
||||
override val content: String,
|
||||
override val confirm: String?,
|
||||
override val dismiss: String?
|
||||
) : DialogVisuals
|
||||
|
||||
private data class DialogDataImpl(
|
||||
override val visuals: DialogVisuals,
|
||||
val continuation: CancellableContinuation<DialogResult>
|
||||
) : DialogData {
|
||||
|
||||
override fun confirm() {
|
||||
if (continuation.isActive) continuation.resume(DialogResult.Confirmed)
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
if (continuation.isActive) continuation.resume(DialogResult.Dismissed)
|
||||
}
|
||||
}
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
||||
var currentDialogData by mutableStateOf<DialogData?>(null)
|
||||
private set
|
||||
|
||||
suspend fun showDialog(
|
||||
title: String,
|
||||
content: String,
|
||||
confirm: String? = null,
|
||||
dismiss: String? = null
|
||||
): DialogResult = mutex.withLock {
|
||||
try {
|
||||
return@withLock suspendCancellableCoroutine { continuation ->
|
||||
currentDialogData = DialogDataImpl(
|
||||
visuals = DialogVisualsImpl(title, content, confirm, dismiss),
|
||||
continuation = continuation
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
currentDialogData = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberDialogHostState(): DialogHostState {
|
||||
return remember {
|
||||
DialogHostState()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BaseDialog(
|
||||
state: DialogHostState = LocalDialogHost.current,
|
||||
title: @Composable (String) -> Unit,
|
||||
confirmButton: @Composable (String?, () -> Unit) -> Unit,
|
||||
dismissButton: @Composable (String?, () -> Unit) -> Unit,
|
||||
content: @Composable (String) -> Unit = { Text(text = it) },
|
||||
) {
|
||||
val currentDialogData = state.currentDialogData ?: return
|
||||
val visuals = currentDialogData.visuals
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
currentDialogData.dismiss()
|
||||
},
|
||||
title = {
|
||||
title(visuals.title)
|
||||
},
|
||||
text = {
|
||||
content(visuals.content)
|
||||
},
|
||||
confirmButton = {
|
||||
confirmButton(visuals.confirm, currentDialogData::confirm)
|
||||
},
|
||||
dismissButton = {
|
||||
dismissButton(visuals.dismiss, currentDialogData::dismiss)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SimpleDialog(
|
||||
state: DialogHostState = LocalDialogHost.current,
|
||||
content: @Composable (String) -> Unit
|
||||
) {
|
||||
BaseDialog(
|
||||
state = state,
|
||||
title = {
|
||||
Text(text = it)
|
||||
},
|
||||
confirmButton = { text, confirm ->
|
||||
text?.let {
|
||||
TextButton(onClick = confirm) {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissButton = { text, dismiss ->
|
||||
text?.let {
|
||||
TextButton(onClick = dismiss) {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmDialog(state: DialogHostState = LocalDialogHost.current) {
|
||||
BaseDialog(
|
||||
state = state,
|
||||
title = {
|
||||
Text(text = it)
|
||||
},
|
||||
confirmButton = { text, confirm ->
|
||||
text?.let {
|
||||
TextButton(onClick = confirm) {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissButton = { text, dismiss ->
|
||||
text?.let {
|
||||
TextButton(onClick = dismiss) {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -10,8 +10,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
@@ -22,9 +21,11 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import me.weishu.kernelsu.R
|
||||
|
||||
private const val TAG = "SearchBar"
|
||||
|
||||
@@ -36,7 +37,8 @@ fun SearchAppBar(
|
||||
onSearchTextChange: (String) -> Unit,
|
||||
onClearClick: () -> Unit,
|
||||
onBackClick: (() -> Unit)? = null,
|
||||
onConfirm: (() -> Unit)? = null
|
||||
onConfirm: (() -> Unit)? = null,
|
||||
dropdownContent: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
@@ -116,6 +118,11 @@ fun SearchAppBar(
|
||||
content = { Icon(Icons.Filled.Search, null) }
|
||||
)
|
||||
}
|
||||
|
||||
if (dropdownContent != null) {
|
||||
dropdownContent()
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -130,21 +131,29 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
else MaterialTheme.colorScheme.errorContainer
|
||||
})
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
// TODO: Install kernel
|
||||
if (kernelVersion.isGKI() && ksuVersion == null) {
|
||||
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
|
||||
}
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
when {
|
||||
ksuVersion != null -> {
|
||||
val appendText = if (Natives.isSafeMode()) {
|
||||
" [${stringResource(id = R.string.safe_mode)}]"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working))
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.home_working),
|
||||
text = stringResource(R.string.home_working) + appendText,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
@@ -213,10 +222,7 @@ private fun InfoCard() {
|
||||
InfoCardItem(stringResource(R.string.home_kernel), uname.release)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_arch), uname.machine)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_version), uname.version)
|
||||
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
InfoCardItem(stringResource(R.string.home_api), Build.VERSION.SDK_INT.toString())
|
||||
@@ -247,6 +253,11 @@ private fun InfoCard() {
|
||||
}
|
||||
}
|
||||
|
||||
fun getManagerVersion(context: Context) : String {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
return "${packageInfo.versionName} (${packageInfo.versionCode})"
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun StatusCardPreview() {
|
||||
|
||||
@@ -30,11 +30,11 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.ConfirmDialog
|
||||
import me.weishu.kernelsu.ui.component.DialogResult
|
||||
import me.weishu.kernelsu.ui.component.rememberDialogHostState
|
||||
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.util.overlayFsAvailable
|
||||
import me.weishu.kernelsu.ui.util.toggleModule
|
||||
import me.weishu.kernelsu.ui.util.uninstallModule
|
||||
import me.weishu.kernelsu.ui.util.*
|
||||
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -52,11 +52,14 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar()
|
||||
},
|
||||
floatingActionButton = {
|
||||
val isSafeMode = Natives.isSafeMode()
|
||||
|
||||
Scaffold(topBar = {
|
||||
TopBar()
|
||||
}, floatingActionButton = if (isSafeMode) {
|
||||
{ /* Empty */ }
|
||||
} else {
|
||||
{
|
||||
val moduleInstall = stringResource(id = R.string.module_install)
|
||||
val selectZipLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
@@ -83,7 +86,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
text = { Text(text = moduleInstall) },
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
}) { innerPadding ->
|
||||
|
||||
val dialogState = rememberDialogHostState()
|
||||
ConfirmDialog(dialogState)
|
||||
|
||||
val failedEnable = stringResource(R.string.module_failed_to_enable)
|
||||
val failedDisable = stringResource(R.string.module_failed_to_disable)
|
||||
val failedUninstall = stringResource(R.string.module_uninstall_failed)
|
||||
@@ -97,11 +104,9 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
return@Scaffold
|
||||
}
|
||||
SwipeRefresh(
|
||||
state = swipeState,
|
||||
onRefresh = {
|
||||
state = swipeState, onRefresh = {
|
||||
scope.launch { viewModel.fetchModuleList() }
|
||||
},
|
||||
modifier = Modifier
|
||||
}, modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.padding(16.dp)
|
||||
.fillMaxSize()
|
||||
@@ -121,42 +126,69 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
Text(stringResource(R.string.module_empty))
|
||||
}
|
||||
} else {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(15.dp),
|
||||
contentPadding = remember { PaddingValues(bottom = 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */ ) }
|
||||
) {
|
||||
LazyColumn(verticalArrangement = Arrangement.spacedBy(15.dp),
|
||||
contentPadding = remember { PaddingValues(bottom = 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */) }) {
|
||||
items(viewModel.moduleList) { module ->
|
||||
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
|
||||
ModuleItem(module,
|
||||
isChecked,
|
||||
onUninstall = {
|
||||
scope.launch {
|
||||
val result = uninstallModule(module.id)
|
||||
if (result) {
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
snackBarHost.showSnackbar(
|
||||
if (result) {
|
||||
successUninstall.format(module.name)
|
||||
} else {
|
||||
failedUninstall.format(module.name)
|
||||
}
|
||||
)
|
||||
val reboot = stringResource(id = R.string.reboot)
|
||||
val rebootToApply = stringResource(id = R.string.reboot_to_apply)
|
||||
val moduleStr = stringResource(id = R.string.module)
|
||||
val uninstall = stringResource(id = R.string.uninstall)
|
||||
val cancel = stringResource(id = android.R.string.cancel)
|
||||
val moduleUninstallConfirm =
|
||||
stringResource(id = R.string.module_uninstall_confirm)
|
||||
ModuleItem(module, isChecked, onUninstall = {
|
||||
scope.launch {
|
||||
val dialogResult = dialogState.showDialog(
|
||||
moduleStr,
|
||||
content = moduleUninstallConfirm.format(module.name),
|
||||
confirm = uninstall,
|
||||
dismiss = cancel
|
||||
)
|
||||
if (dialogResult != DialogResult.Confirmed) {
|
||||
return@launch
|
||||
}
|
||||
},
|
||||
onCheckChanged = {
|
||||
val success = toggleModule(module.id, !isChecked)
|
||||
|
||||
val success = uninstallModule(module.id)
|
||||
if (success) {
|
||||
isChecked = it
|
||||
scope.launch {
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
} else scope.launch {
|
||||
val message = if (isChecked) failedDisable else failedEnable
|
||||
snackBarHost.showSnackbar(message.format(module.name))
|
||||
viewModel.fetchModuleList()
|
||||
}
|
||||
val message = if (success) {
|
||||
successUninstall.format(module.name)
|
||||
} else {
|
||||
failedUninstall.format(module.name)
|
||||
}
|
||||
val actionLabel = if (success) {
|
||||
reboot
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val result = snackBarHost.showSnackbar(
|
||||
message, actionLabel = actionLabel
|
||||
)
|
||||
if (result == SnackbarResult.ActionPerformed) {
|
||||
reboot()
|
||||
}
|
||||
}
|
||||
)
|
||||
}, onCheckChanged = {
|
||||
val success = toggleModule(module.id, !isChecked)
|
||||
if (success) {
|
||||
isChecked = it
|
||||
scope.launch {
|
||||
viewModel.fetchModuleList()
|
||||
|
||||
val result = snackBarHost.showSnackbar(
|
||||
rebootToApply, actionLabel = reboot
|
||||
)
|
||||
if (result == SnackbarResult.ActionPerformed) {
|
||||
reboot()
|
||||
}
|
||||
}
|
||||
} else scope.launch {
|
||||
val message = if (isChecked) failedDisable else failedEnable
|
||||
snackBarHost.showSnackbar(message.format(module.name))
|
||||
}
|
||||
})
|
||||
// fix last item shadow incomplete in LazyColumn
|
||||
Spacer(Modifier.height(1.dp))
|
||||
}
|
||||
@@ -169,9 +201,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar() {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.module)) }
|
||||
)
|
||||
TopAppBar(title = { Text(stringResource(R.string.module)) })
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.FileProvider
|
||||
import com.alorma.compose.settings.ui.*
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
import me.weishu.kernelsu.ui.util.getBugreportFile
|
||||
import me.weishu.kernelsu.BuildConfig
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SimpleDialog
|
||||
import me.weishu.kernelsu.ui.component.rememberDialogHostState
|
||||
import me.weishu.kernelsu.ui.util.LinkifyText
|
||||
|
||||
|
||||
/**
|
||||
* @author weishu
|
||||
* @date 2023/1/1.
|
||||
@@ -34,46 +42,57 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
) { paddingValues ->
|
||||
|
||||
var openDialog by remember { mutableStateOf(false) }
|
||||
val dialogState = rememberDialogHostState()
|
||||
|
||||
if (openDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
openDialog = false
|
||||
},
|
||||
title = {
|
||||
Text(text = stringResource(id = R.string.about))
|
||||
},
|
||||
text = {
|
||||
SupportCard()
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
openDialog = false
|
||||
}
|
||||
) {
|
||||
Text(stringResource(id = android.R.string.ok))
|
||||
}
|
||||
},
|
||||
)
|
||||
SimpleDialog(dialogState) {
|
||||
SupportCard()
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(paddingValues)) {
|
||||
|
||||
val context = LocalContext.current
|
||||
SettingsSwitch(
|
||||
title = {
|
||||
Text(stringResource(id = R.string.settings_system_rw))
|
||||
},
|
||||
subtitle = {
|
||||
Text(stringResource(id = R.string.settings_system_rw_summary))
|
||||
},
|
||||
onCheckedChange = {
|
||||
Toast.makeText(context, "coming soon", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
)
|
||||
SettingsMenuLink(title = {
|
||||
Text(stringResource(id = R.string.about))
|
||||
Text(stringResource(id = R.string.send_log))
|
||||
},
|
||||
onClick = {
|
||||
openDialog = true
|
||||
val bugreport = getBugreportFile(context)
|
||||
val uri: Uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", bugreport)
|
||||
|
||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
|
||||
shareIntent.setDataAndType(uri, "application/zip")
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
context.startActivity(
|
||||
Intent.createChooser(
|
||||
shareIntent,
|
||||
context.getString(R.string.send_log)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
val about = stringResource(id = R.string.about)
|
||||
val ok = stringResource(id = android.R.string.ok)
|
||||
val scope = rememberCoroutineScope()
|
||||
SettingsMenuLink(title = {
|
||||
Text(about)
|
||||
},
|
||||
onClick = {
|
||||
scope.launch {
|
||||
dialogState.showDialog(about, content = "unused", confirm = ok)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package me.weishu.kernelsu.ui.screen
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@@ -17,17 +13,8 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.ImageLoader
|
||||
import coil.compose.AsyncImage
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import coil.compose.rememberImagePainter
|
||||
import coil.decode.DataSource
|
||||
import coil.fetch.DrawableResult
|
||||
import coil.fetch.FetchResult
|
||||
import coil.fetch.Fetcher
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.Options
|
||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
@@ -37,7 +24,6 @@ import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.component.SearchAppBar
|
||||
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
|
||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
|
||||
import java.util.*
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -60,7 +46,44 @@ fun SuperUserScreen() {
|
||||
title = { Text(stringResource(R.string.superuser)) },
|
||||
searchText = viewModel.search,
|
||||
onSearchTextChange = { viewModel.search = it },
|
||||
onClearClick = { viewModel.search = "" }
|
||||
onClearClick = { viewModel.search = "" },
|
||||
dropdownContent = {
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(
|
||||
onClick = { showDropdown = true },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.MoreVert,
|
||||
contentDescription = stringResource(id = R.string.settings)
|
||||
)
|
||||
|
||||
DropdownMenu(expanded = showDropdown, onDismissRequest = {
|
||||
showDropdown = false
|
||||
}) {
|
||||
DropdownMenuItem(text = {
|
||||
Text(stringResource(R.string.refresh))
|
||||
}, onClick = {
|
||||
scope.launch {
|
||||
viewModel.fetchAppList()
|
||||
}
|
||||
showDropdown = false
|
||||
})
|
||||
DropdownMenuItem(text = {
|
||||
Text(
|
||||
if (viewModel.showSystemApps) {
|
||||
stringResource(R.string.hide_system_apps)
|
||||
} else {
|
||||
stringResource(R.string.show_system_apps)
|
||||
}
|
||||
)
|
||||
}, onClick = {
|
||||
viewModel.showSystemApps = !viewModel.showSystemApps
|
||||
showDropdown = false
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
@@ -77,7 +100,7 @@ fun SuperUserScreen() {
|
||||
.fillMaxSize()
|
||||
) {
|
||||
LazyColumn {
|
||||
items(viewModel.appList) { app ->
|
||||
items(viewModel.appList, key = { it.packageName }) { app ->
|
||||
var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) }
|
||||
AppItem(app, isChecked) { checked ->
|
||||
val success = Natives.allowRoot(app.uid, checked)
|
||||
|
||||
@@ -2,7 +2,12 @@ package me.weishu.kernelsu.ui.util
|
||||
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import me.weishu.kernelsu.ui.component.DialogHostState
|
||||
|
||||
val LocalSnackbarHost = compositionLocalOf<SnackbarHostState> {
|
||||
error("CompositionLocal LocalSnackbarController not present")
|
||||
}
|
||||
|
||||
val LocalDialogHost = compositionLocalOf<DialogHostState> {
|
||||
error("CompositionLocal LocalDialogController not present")
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package me.weishu.kernelsu.ui.util
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.system.Os
|
||||
import me.weishu.kernelsu.Natives
|
||||
import me.weishu.kernelsu.ui.screen.getManagerVersion
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
fun getBugreportFile(context: Context): File {
|
||||
|
||||
val bugreportDir = File(context.cacheDir, "bugreport")
|
||||
bugreportDir.mkdirs()
|
||||
|
||||
val dmesgFile = File(bugreportDir, "dmesg.txt")
|
||||
val logcatFile = File(bugreportDir, "logcat.txt")
|
||||
val tombstonesFile = File(bugreportDir, "tombstones.tar.gz")
|
||||
val dropboxFile = File(bugreportDir, "dropbox.tar.gz")
|
||||
val mountsFile = File(bugreportDir, "mounts.txt")
|
||||
val fileSystemsFile = File(bugreportDir, "filesystems.txt")
|
||||
val ksuFileTree = File(bugreportDir, "ksu_tree.txt")
|
||||
val appListFile = File(bugreportDir, "app_list.txt")
|
||||
|
||||
val shell = createRootShell()
|
||||
|
||||
shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec()
|
||||
shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec()
|
||||
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} /data/tombstones").exec()
|
||||
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} /data/system/dropbox").exec()
|
||||
shell.newJob().add("cat /proc/mounts > ${mountsFile.absolutePath}").exec()
|
||||
shell.newJob().add("cat /proc/filesystems > ${fileSystemsFile.absolutePath}").exec()
|
||||
shell.newJob().add("ls -alRZ /data/adb/ksu > ${ksuFileTree.absolutePath}").exec()
|
||||
shell.newJob().add("cat /data/system/packages.list > ${appListFile.absolutePath}").exec()
|
||||
|
||||
// basic information
|
||||
val buildInfo = File(bugreportDir, "basic.txt")
|
||||
PrintWriter(FileWriter(buildInfo)).use { pw ->
|
||||
pw.println("Kernel: ${System.getProperty("os.version")}")
|
||||
pw.println("BRAND: " + Build.BRAND)
|
||||
pw.println("MODEL: " + Build.MODEL)
|
||||
pw.println("PRODUCT: " + Build.PRODUCT)
|
||||
pw.println("MANUFACTURER: " + Build.MANUFACTURER)
|
||||
pw.println("SDK: " + Build.VERSION.SDK_INT)
|
||||
pw.println("PREVIEW_SDK: " + Build.VERSION.PREVIEW_SDK_INT)
|
||||
pw.println("FINGERPRINT: " + Build.FINGERPRINT)
|
||||
pw.println("DEVICE: " + Build.DEVICE)
|
||||
pw.println("Manager: " + getManagerVersion(context))
|
||||
|
||||
val uname = Os.uname()
|
||||
pw.println("KernelRelease: ${uname.release}")
|
||||
pw.println("KernelVersion: ${uname.version}")
|
||||
pw.println("Mahcine: ${uname.machine}")
|
||||
pw.println("Nodename: ${uname.nodename}")
|
||||
pw.println("Sysname: ${uname.sysname}")
|
||||
|
||||
val ksuKernel = Natives.getVersion()
|
||||
pw.println("KernelSU: $ksuKernel")
|
||||
val safeMode = Natives.isSafeMode()
|
||||
pw.println("SafeMode: $safeMode")
|
||||
}
|
||||
|
||||
// modules
|
||||
val modulesFile = File(bugreportDir, "modules.json")
|
||||
modulesFile.writeText(listModules())
|
||||
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm")
|
||||
val current = LocalDateTime.now().format(formatter)
|
||||
|
||||
val targetFile = File(context.cacheDir, "KernelSU_bugreport_${current}.tar.gz")
|
||||
|
||||
shell.newJob().add("tar czf ${targetFile.absolutePath} -C ${bugreportDir.absolutePath} .").exec()
|
||||
shell.newJob().add("rm -rf ${bugreportDir.absolutePath}").exec()
|
||||
shell.newJob().add("chmod 0644 ${targetFile.absolutePath}").exec()
|
||||
|
||||
return targetFile
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package me.weishu.kernelsu.ui.viewmodel
|
||||
|
||||
import android.content.Context
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -74,6 +73,7 @@ class ModuleViewModel : ViewModel() {
|
||||
}.toList()
|
||||
}.onFailure { e ->
|
||||
Log.e(TAG, "fetchModuleList: ", e)
|
||||
isRefreshing = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ class SuperUserViewModel : ViewModel() {
|
||||
)
|
||||
|
||||
var search by mutableStateOf("")
|
||||
var showSystemApps by mutableStateOf(false)
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
@@ -55,6 +56,9 @@ class SuperUserViewModel : ViewModel() {
|
||||
sortedList.filter {
|
||||
it.label.contains(search) || it.packageName.contains(search) || HanziToPinyin.getInstance()
|
||||
.toPinyinString(it.label).contains(search)
|
||||
}.filter {
|
||||
it.uid == 2000 // Always show shell
|
||||
|| showSystemApps || it.icon.applicationInfo.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
56
manager/app/src/main/res/values-in/strings.xml
Normal file
56
manager/app/src/main/res/values-in/strings.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">Beranda</string>
|
||||
<string name="home_not_installed">Tidak terinstall</string>
|
||||
<string name="home_click_to_install">Klik untuk menginstall</string>
|
||||
<string name="home_working">Bekerja</string>
|
||||
<string name="home_working_version">Versi: %d</string>
|
||||
<string name="home_unsupported">Tidak didukung</string>
|
||||
<string name="home_unsupported_reason">Saat ini kernelSu hanya mendukung GKI kernel</string>
|
||||
<string name="home_copied_to_clipboard">Salin ke clipboard</string>
|
||||
<string name="home_support">Dukungan</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Arch</string>
|
||||
<string name="home_manager_version">Versi manager</string>
|
||||
<string name="home_api">API Level</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">Patch keamanan</string>
|
||||
<string name="home_selinux_status">Status SElinux</string>
|
||||
<string name="selinux_status_disabled">Cacat</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">Tidak tersedia</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Gagal mengizinkan root untuk %d</string>
|
||||
<string name="module_failed_to_enable">Gagal mengaktifkan module: %s</string>
|
||||
<string name="module_failed_to_disable">Gagal menonaktifkan module: %s</string>
|
||||
<string name="module_empty">Tidak ada module terpasang</string>
|
||||
<string name="module">Module</string>
|
||||
<string name="uninstall">Hapus</string>
|
||||
<string name="module_install">Pasang</string>
|
||||
<string name="install">Pasang</string>
|
||||
<string name="reboot">Reboot perangkat</string>
|
||||
<string name="settings">Pengaturan</string>
|
||||
<string name="reboot_userspace">Soft Reboot</string>
|
||||
<string name="reboot_recovery">Reboot ke Recovery</string>
|
||||
<string name="reboot_bootloader">Reboot ke Bootloader</string>
|
||||
<string name="reboot_download">Reboot ke Download</string>
|
||||
<string name="reboot_edl">Reboot ke EDL</string>
|
||||
<string name="settings_system_rw">Buat system agar dapat ditulis sebagai OverlayFs</string>
|
||||
<string name="settings_system_rw_summary">restart perangkat setelah mengaktifkan fitur ini</string>
|
||||
<string name="about">Tentang</string>
|
||||
<string name="require_kernel_version_8">Membutuhkan KernelSU Versi 8+</string>
|
||||
<string name="module_uninstall_confirm">Apakah anda yakin ingin menghapus module ini %s?</string>
|
||||
<string name="module_uninstall_success">%s terhapus</string>
|
||||
<string name="module_uninstall_failed">Gagal untuk menghapus: %s</string>
|
||||
<string name="module_version">Versi</string>
|
||||
<string name="module_author">Pembuat</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs tidak tersedia, module tidak bekerja!</string>
|
||||
<string name="refresh">Segarkan</string>
|
||||
<string name="show_system_apps">Tampilkan system apps</string>
|
||||
<string name="hide_system_apps">Sembunyikan system apps</string>
|
||||
<string name="send_log">Kirim logs</string>
|
||||
<string name="safe_mode">Mode aman</string>
|
||||
<string name="reboot_to_apply">Restart untuk menerapkan</string>
|
||||
</resources>
|
||||
56
manager/app/src/main/res/values-ja/strings.xml
Normal file
56
manager/app/src/main/res/values-ja/strings.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">KernelSU</string>
|
||||
<string name="home">ホーム</string>
|
||||
<string name="home_not_installed">未インストール</string>
|
||||
<string name="home_click_to_install">タップでインストール</string>
|
||||
<string name="home_working">動作中</string>
|
||||
<string name="home_working_version">バージョン: %d</string>
|
||||
<string name="home_unsupported">非対応</string>
|
||||
<string name="home_unsupported_reason">KernelSUは現在、GKIカーネルのみサポートをしています</string>
|
||||
<string name="home_copied_to_clipboard">クリップボードにコピーしました</string>
|
||||
<string name="home_support">対応</string>
|
||||
<string name="home_kernel">カーネル</string>
|
||||
<string name="home_arch">アーキテクチャ</string>
|
||||
<string name="home_manager_version">バージョン</string>
|
||||
<string name="home_api">APIレベル</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">セキュリティパッチ</string>
|
||||
<string name="home_selinux_status">SELinuxの状態</string>
|
||||
<string name="selinux_status_disabled">無効</string>
|
||||
<string name="selinux_status_enforcing">Enforcing</string>
|
||||
<string name="selinux_status_permissive">Permissive</string>
|
||||
<string name="selinux_status_unknown">不明</string>
|
||||
<string name="superuser">スーパーユーザー</string>
|
||||
<string name="superuser_failed_to_grant_root">%dの権限の付与に失敗しました</string>
|
||||
<string name="module_failed_to_enable">モジュールの有効化に失敗: %s</string>
|
||||
<string name="module_failed_to_disable">モジュールの無効化に失敗: %s</string>
|
||||
<string name="module_empty">モジュールはインストールされていません</string>
|
||||
<string name="module">モジュール</string>
|
||||
<string name="uninstall">アンインストール</string>
|
||||
<string name="module_install">インストール</string>
|
||||
<string name="install">インストール</string>
|
||||
<string name="reboot">再起動</string>
|
||||
<string name="settings">設定</string>
|
||||
<string name="reboot_userspace">ソフトリブート</string>
|
||||
<string name="reboot_recovery">リカバリーで再起動</string>
|
||||
<string name="reboot_bootloader">Bootloaderで再起動</string>
|
||||
<string name="reboot_download">ダウンロードモードで再起動</string>
|
||||
<string name="reboot_edl">EDLで再起動</string>
|
||||
<string name="settings_system_rw">システムを書き込み可能にする</string>
|
||||
<string name="settings_system_rw_summary">OverlayFSを使用してシステムを書き込み可能にします。\nこの機能は再起動後に有効になります。</string>
|
||||
<string name="about">アプリについて</string>
|
||||
<string name="require_kernel_version_8">KernelSU バージョン8以降が必要です</string>
|
||||
<string name="module_uninstall_success">%sをアンインストールしました</string>
|
||||
<string name="module_uninstall_failed">アンインストールに失敗: %s</string>
|
||||
<string name="module_version">バージョン</string>
|
||||
<string name="module_author">作者</string>
|
||||
<string name="module_overlay_fs_not_available">OverlayFSが有効でないためモジュールは動作しません</string>
|
||||
<string name="refresh">更新</string>
|
||||
<string name="show_system_apps">システムアプリを表示</string>
|
||||
<string name="hide_system_apps">システムアプリを非表示</string>
|
||||
<string name="send_log">ログを送信</string>
|
||||
<string name="safe_mode">セーフモード</string>
|
||||
<string name="reboot_to_apply">再起動をして有効化する</string>
|
||||
</resources>
|
||||
58
manager/app/src/main/res/values-ru/strings.xml
Normal file
58
manager/app/src/main/res/values-ru/strings.xml
Normal file
@@ -0,0 +1,58 @@
|
||||
<resources>
|
||||
<string name="home">Главная</string>
|
||||
<string name="home_not_installed">Не установлен</string>
|
||||
<string name="home_click_to_install">Нажмите чтобы установить</string>
|
||||
<string name="home_working">Работает</string>
|
||||
<string name="home_working_version">Версия: %d</string>
|
||||
<string name="home_unsupported">Не поддерживается</string>
|
||||
<string name="home_unsupported_reason">KernelSU поддерживает только GKI ядра</string>
|
||||
<string name="home_copied_to_clipboard">Скопировано в буфер обмена</string>
|
||||
<string name="home_support">Поддержка</string>
|
||||
|
||||
<string name="home_kernel">Ядро</string>
|
||||
<string name="home_arch">Архитектура</string>
|
||||
<string name="home_manager_version">Версия</string>
|
||||
<string name="home_api">Уровень API</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Подпись</string>
|
||||
<string name="home_securitypatch">Патч Безопасности</string>
|
||||
|
||||
<string name="home_selinux_status">Состояние SELinux</string>
|
||||
<string name="selinux_status_disabled">Выключен</string>
|
||||
<string name="selinux_status_enforcing">Принудительный</string>
|
||||
<string name="selinux_status_permissive">Разрешающий</string>
|
||||
<string name="selinux_status_unknown">Неизвестно</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Failed to grant root for %d</string>
|
||||
<string name="module_failed_to_enable">Не удалось включить модуль: %s</string>
|
||||
<string name="module_failed_to_disable">Не удалось отключить модуль: %s</string>
|
||||
<string name="module_empty">Нет установленных модулей</string>
|
||||
|
||||
<string name="module">Модули</string>
|
||||
<string name="uninstall">Удалить</string>
|
||||
<string name="module_install">Установить</string>
|
||||
<string name="install">Установить</string>
|
||||
<string name="reboot">Перезагрузка</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="reboot_userspace">Soft Reboot</string>
|
||||
<string name="reboot_recovery">Reboot to Recovery</string>
|
||||
<string name="reboot_bootloader">Reboot to Bootloader</string>
|
||||
<string name="reboot_download">Reboot to Download</string>
|
||||
<string name="reboot_edl">Reboot to EDL</string>
|
||||
<string name="settings_system_rw">Сделать систему доступной для записи</string>
|
||||
<string name="settings_system_rw_summary">Использовать overlayfs, чтобы сделать системный раздел доступным для записи, необходима перезагрузка.</string>
|
||||
<string name="about">О KernelSU</string>
|
||||
<string name="require_kernel_version_8">Требуется KernelSU версии 8 и выше</string>
|
||||
<string name="module_uninstall_success">%s удален</string>
|
||||
<string name="module_uninstall_failed">Не удалось удалить: %s</string>
|
||||
<string name="module_version">Версия</string>
|
||||
<string name="module_author">Автор</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs выключен, модуль не будет работать!</string>
|
||||
<string name="refresh">Обновить</string>
|
||||
<string name="show_system_apps">Показать системные приложения</string>
|
||||
<string name="hide_system_apps">Скрыть системные приложения</string>
|
||||
<string name="send_log">Отправить лог</string>
|
||||
<string name="safe_mode">Безопасный режим</string>
|
||||
<string name="reboot_to_apply">Перезагрузите, чтобы вступить в силу</string>
|
||||
|
||||
</resources>
|
||||
53
manager/app/src/main/res/values-vi/strings.xml
Normal file
53
manager/app/src/main/res/values-vi/strings.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<resources>
|
||||
<string name="home">Trang chủ</string>
|
||||
<string name="home_not_installed">Chưa được cài đặt</string>
|
||||
<string name="home_click_to_install">Nhấn đề cài dặt</string>
|
||||
<string name="home_working">Đang chạy</string>
|
||||
<string name="home_working_version">Phiên bản: %d</string>
|
||||
<string name="home_unsupported">Không hỗ trợ</string>
|
||||
<string name="home_unsupported_reason">KernelSU hiện tại chỉ hỗ trợ kernel GKI</string>
|
||||
<string name="home_copied_to_clipboard">Sao chép vào clipboard</string>
|
||||
<string name="home_support">Ủng hộ</string>
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Kiến trúc</string>
|
||||
<string name="home_manager_version">Phiên bản</string>
|
||||
<string name="home_api">Cấp độ API</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
<string name="home_securitypatch">Bản vá bảo mật</string>
|
||||
<string name="home_selinux_status">Trạng thái SELinux</string>
|
||||
<string name="selinux_status_disabled">Vô hiệu hóa</string>
|
||||
<string name="selinux_status_enforcing">Thực thi</string>
|
||||
<string name="selinux_status_permissive">Cho phép</string>
|
||||
<string name="selinux_status_unknown">Không rõ</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
<string name="superuser_failed_to_grant_root">Không thể cấp quyền root cho %d</string>
|
||||
<string name="module_failed_to_enable">Không thể kích hoạt mô-đun: %s</string>
|
||||
<string name="module_failed_to_disable">Không thể vô hiệu hóa mô-đun: %s</string>
|
||||
<string name="module_empty">Chưa có mô-đun nào được cài đặt</string>
|
||||
<string name="module">Mô-đun</string>
|
||||
<string name="uninstall">Gỡ cài đặt</string>
|
||||
<string name="module_install">Cài đặt</string>
|
||||
<string name="install">Cài đặt</string>
|
||||
<string name="reboot">Khởi động lại</string>
|
||||
<string name="settings">Thiết lập</string>
|
||||
<string name="reboot_userspace">Khởi động mềm</string>
|
||||
<string name="reboot_recovery">Khởi động lại vào Recovery</string>
|
||||
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
|
||||
<string name="reboot_download">Khởi động lại vào Download Mode</string>
|
||||
<string name="reboot_edl">Khởi động vào EDL</string>
|
||||
<string name="settings_system_rw">Cho phép ghi vào phân vùng system</string>
|
||||
<string name="settings_system_rw_summary">Sử dụng overlayfs để làm cho phân vùng system có thể ghi được, khởi động lại để áp dụng</string>
|
||||
<string name="about">Thông tin</string>
|
||||
<string name="require_kernel_version_8">Yêu cầu KernelSU phiên bản 8+</string>
|
||||
<string name="module_uninstall_success">%s đã được gỡ cài đặt</string>
|
||||
<string name="module_uninstall_failed">Lỗi khi gỡ cài đặt: %s</string>
|
||||
<string name="module_version">Phiên bản</string>
|
||||
<string name="module_author">Tác giả</string>
|
||||
<string name="module_overlay_fs_not_available">Không tìm thấy overlayfs, mô-đun sẽ không thể hoạt động!</string>
|
||||
<string name="refresh">Làm mới</string>
|
||||
<string name="show_system_apps">Hiển thị ứng dụng hệ thống</string>
|
||||
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
|
||||
<string name="safe_mode">Chế độ an toàn</string>
|
||||
<string name="reboot_to_apply">Khởi động lại để có hiệu lực</string>
|
||||
</resources>
|
||||
@@ -11,7 +11,7 @@
|
||||
<string name="home_support">支持</string>
|
||||
<string name="home_kernel">内核版本</string>
|
||||
<string name="home_arch">设备架构</string>
|
||||
<string name="home_version">系统版本</string>
|
||||
<string name="home_manager_version">管理器版本</string>
|
||||
<string name="home_api">API 版本</string>
|
||||
<string name="home_abi">ABI 支持</string>
|
||||
<string name="home_fingerprint">系统指纹</string>
|
||||
@@ -41,9 +41,16 @@
|
||||
<string name="settings_system_rw_summary">使用 overlayfs 使系统分区可写, 重启生效</string>
|
||||
<string name="about">关于</string>
|
||||
<string name="require_kernel_version_8">需要 KernelSU 版本 8+</string>
|
||||
<string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string>
|
||||
<string name="module_uninstall_success">%s 已卸载</string>
|
||||
<string name="module_uninstall_failed">卸载失败: %s</string>
|
||||
<string name="module_version">版本</string>
|
||||
<string name="module_author">作者</string>
|
||||
<string name="module_overlay_fs_not_available">内核不支持 overlayfs,模块功能无法运作!</string>
|
||||
<string name="refresh">刷新</string>
|
||||
<string name="show_system_apps">显示系统应用</string>
|
||||
<string name="hide_system_apps">隐藏系统应用</string>
|
||||
<string name="send_log">发送日志</string>
|
||||
<string name="safe_mode">安全模式</string>
|
||||
<string name="reboot_to_apply">重启生效</string>
|
||||
</resources>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<string name="home_support">支持</string>
|
||||
<string name="home_kernel">内核版本</string>
|
||||
<string name="home_arch">设备架构</string>
|
||||
<string name="home_version">系统版本</string>
|
||||
<string name="home_manager_version">管理器版本</string>
|
||||
<string name="home_api">API 版本</string>
|
||||
<string name="home_abi">ABI 支持</string>
|
||||
<string name="home_fingerprint">系统指纹</string>
|
||||
@@ -46,4 +46,6 @@
|
||||
<string name="module_version">版本</string>
|
||||
<string name="module_author">作者</string>
|
||||
<string name="module_overlay_fs_not_available">內核不支持 overlayfs,模塊功能無法運作!</string>
|
||||
<string name="safe_mode">安全模式</string>
|
||||
<string name="reboot_to_apply">重啟生效</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,49 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="home">主頁</string>
|
||||
<string name="home">首頁</string>
|
||||
<string name="home_not_installed">未安裝</string>
|
||||
<string name="home_click_to_install">点击安装</string>
|
||||
<string name="home_click_to_install">點擊安裝</string>
|
||||
<string name="home_working">工作中</string>
|
||||
<string name="home_working_version">版本: %d</string>
|
||||
<string name="home_unsupported">不支持</string>
|
||||
<string name="home_unsupported_reason">KernelSU 现在只支持 GKI 内核</string>
|
||||
<string name="home_unsupported">不支援</string>
|
||||
<string name="home_unsupported_reason">KernelSU 現在只支援 GKI 內核</string>
|
||||
<string name="home_copied_to_clipboard">已複製到剪貼簿</string>
|
||||
<string name="home_support">支持</string>
|
||||
<string name="home_kernel">内核版本</string>
|
||||
<string name="home_arch">设备架构</string>
|
||||
<string name="home_version">系统版本</string>
|
||||
<string name="home_support">支援</string>
|
||||
<string name="home_kernel">內核版本</string>
|
||||
<string name="home_arch">設備架構</string>
|
||||
<string name="home_manager_version">管理器版本</string>
|
||||
<string name="home_api">API 版本</string>
|
||||
<string name="home_abi">ABI 支持</string>
|
||||
<string name="home_fingerprint">系统指纹</string>
|
||||
<string name="home_securitypatch">安全补丁</string>
|
||||
<string name="home_abi">ABI 支援</string>
|
||||
<string name="home_fingerprint">系統指紋</string>
|
||||
<string name="home_securitypatch">安全性更新</string>
|
||||
<string name="home_selinux_status">SELinux 狀態</string>
|
||||
<string name="selinux_status_disabled">被禁用</string>
|
||||
<string name="selinux_status_disabled">被停用</string>
|
||||
<string name="selinux_status_enforcing">強制執行</string>
|
||||
<string name="selinux_status_permissive">寬容模式</string>
|
||||
<string name="selinux_status_unknown">未知</string>
|
||||
<string name="superuser">超級用戶</string>
|
||||
<string name="superuser_failed_to_grant_root">无法为 %d 授予 Root</string>
|
||||
<string name="module_failed_to_enable">无法启用模块: %s</string>
|
||||
<string name="module_failed_to_disable">无法禁用模块: %s</string>
|
||||
<string name="module_empty">没有安装模块</string>
|
||||
<string name="module">模块</string>
|
||||
<string name="uninstall">卸载</string>
|
||||
<string name="module_install">安装</string>
|
||||
<string name="install">安装</string>
|
||||
<string name="reboot">重启</string>
|
||||
<string name="superuser_failed_to_grant_root">無法為 %d 授權 Root</string>
|
||||
<string name="module_failed_to_enable">無法啟用模組: %s</string>
|
||||
<string name="module_failed_to_disable">無法禁用模組: %s</string>
|
||||
<string name="module_empty">沒有安裝模組</string>
|
||||
<string name="module">模組</string>
|
||||
<string name="uninstall">解除安裝</string>
|
||||
<string name="module_install">安裝</string>
|
||||
<string name="install">安裝</string>
|
||||
<string name="reboot">重新開機</string>
|
||||
<string name="settings">設定</string>
|
||||
<string name="reboot_userspace">软重启</string>
|
||||
<string name="reboot_recovery">重启到 Recovery</string>
|
||||
<string name="reboot_bootloader">重启到 BootLoader</string>
|
||||
<string name="reboot_download">重启到 Download</string>
|
||||
<string name="reboot_edl">重启到 EDL</string>
|
||||
<string name="settings_system_rw">使系统可写</string>
|
||||
<string name="settings_system_rw_summary">使用 overlayfs 使系统分区可写, 重启生效</string>
|
||||
<string name="about">关于</string>
|
||||
<string name="reboot_userspace">重新啟動</string>
|
||||
<string name="reboot_recovery">重新啟動到 Recovery</string>
|
||||
<string name="reboot_bootloader">重新啟動到 BootLoader</string>
|
||||
<string name="reboot_download">重新啟用到 Download</string>
|
||||
<string name="reboot_edl">重新啟用到 EDL</string>
|
||||
<string name="settings_system_rw">讓系統可以寫入</string>
|
||||
<string name="settings_system_rw_summary">使用 overlayfs 使系統分區可以寫入,重新啟動後生效</string>
|
||||
<string name="about">關於</string>
|
||||
<string name="require_kernel_version_8">需要 KernelSU 版本 8+</string>
|
||||
<string name="module_uninstall_success">%s 已卸載</string>
|
||||
<string name="module_uninstall_failed">卸載失敗: %s</string>
|
||||
<string name="module_uninstall_success">%s 已解除安裝</string>
|
||||
<string name="module_uninstall_failed">解除安裝失敗: %s</string>
|
||||
<string name="module_version">版本</string>
|
||||
<string name="module_author">作者</string>
|
||||
<string name="module_overlay_fs_not_available">內核不支持 overlayfs,模塊功能無法運作!</string>
|
||||
<string name="module_overlay_fs_not_available">內核不支持 overlayfs,模組功能不能運作!</string>
|
||||
<string name="refresh">重新整理</string>
|
||||
<string name="show_system_apps">顯示系統應用程式</string>
|
||||
<string name="hide_system_apps">隱藏系統應用程式</string>
|
||||
<string name="safe_mode">安全模式</string>
|
||||
<string name="reboot_to_apply">重啟生效</string>
|
||||
</resources>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<string name="home_kernel">Kernel</string>
|
||||
<string name="home_arch">Arch</string>
|
||||
<string name="home_version">Version</string>
|
||||
<string name="home_manager_version">Manager Version</string>
|
||||
<string name="home_api">API Level</string>
|
||||
<string name="home_abi">ABI</string>
|
||||
<string name="home_fingerprint">Fingerprint</string>
|
||||
@@ -45,10 +45,17 @@
|
||||
<string name="settings_system_rw_summary">Use overlayfs to make system partition writable, reboot to take effect.</string>
|
||||
<string name="about">About</string>
|
||||
<string name="require_kernel_version_8">Require KernelSU version 8+</string>
|
||||
<string name="module_uninstall_confirm">Are you sure you want to uninstall module %s?</string>
|
||||
<string name="module_uninstall_success">%s uninstalled</string>
|
||||
<string name="module_uninstall_failed">Failed to uninstall: %s</string>
|
||||
<string name="module_version">Version</string>
|
||||
<string name="module_author">Author</string>
|
||||
<string name="module_overlay_fs_not_available">overlayfs is not available, module cannot work!</string>
|
||||
<string name="refresh">Refresh</string>
|
||||
<string name="show_system_apps">Show system apps</string>
|
||||
<string name="hide_system_apps">Hide system apps</string>
|
||||
<string name="send_log">Send Log</string>
|
||||
<string name="safe_mode">Safe mode</string>
|
||||
<string name="reboot_to_apply">Reboot to take effect</string>
|
||||
|
||||
</resources>
|
||||
|
||||
6
manager/app/src/main/res/xml/filepaths.xml
Normal file
6
manager/app/src/main/res/xml/filepaths.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<paths>
|
||||
<cache-path name="cache" path="."/>
|
||||
<root-path name="root" path="." />
|
||||
</paths>
|
||||
477
userspace/ksud/Cargo.lock
generated
477
userspace/ksud/Cargo.lock
generated
@@ -8,6 +8,12 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
@@ -35,6 +41,33 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27f0fc03f560e1aebde41c2398b691cb98b5ea5996a6184a7a67bbbb77448969"
|
||||
|
||||
[[package]]
|
||||
name = "android_logger"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d70a974711fabdc7555de36765bbc237f093f5992d62cab3dcaa0b0bfc7d756"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"env_logger",
|
||||
"log",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.68"
|
||||
@@ -55,9 +88,9 @@ checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.59.2"
|
||||
version = "0.60.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
|
||||
checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
@@ -87,6 +120,12 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@@ -138,6 +177,18 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.3.0"
|
||||
@@ -195,6 +246,16 @@ dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
version = "0.2.30"
|
||||
@@ -221,6 +282,12 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.5"
|
||||
@@ -316,6 +383,50 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-new"
|
||||
version = "0.5.9"
|
||||
@@ -492,9 +603,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -505,6 +616,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
@@ -530,13 +647,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.4"
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
|
||||
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include-flate"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfdcb449c721557c1cf89bbd3412bf33fa963289e26e9badbd824a960912e148"
|
||||
dependencies = [
|
||||
"include-flate-codegen-exports",
|
||||
"lazy_static",
|
||||
"libflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include-flate-codegen"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a7d6e1419fa3129eb0802b4c99603c0d425c79fb5d76191d5a20d0ab0d664e8"
|
||||
dependencies = [
|
||||
"libflate",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include-flate-codegen-exports"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75657043ffe3d8280f1cb8aef0f505532b392ed7758e0baeac22edadcee31a03"
|
||||
dependencies = [
|
||||
"include-flate-codegen",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -548,7 +723,7 @@ dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -586,6 +761,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jwalk"
|
||||
version = "0.8.1"
|
||||
@@ -601,6 +785,7 @@ name = "ksud"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"android-properties",
|
||||
"android_logger",
|
||||
"anyhow",
|
||||
"clap",
|
||||
"const_format",
|
||||
@@ -615,12 +800,15 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"nom",
|
||||
"proc-mounts",
|
||||
"procfs",
|
||||
"regex",
|
||||
"retry",
|
||||
"rust-embed",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sys-mount",
|
||||
"zip 0.6.3",
|
||||
"zip 0.6.4",
|
||||
"zip-extensions",
|
||||
]
|
||||
|
||||
@@ -642,6 +830,26 @@ version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05605ab2bce11bcfc0e9c635ff29ef8b2ea83f29be257ee7d730cac3ee373093"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"crc32fast",
|
||||
"libflate_lz77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libflate_lz77"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39a734c0493409afcd49deee13c006a04e3586b9761a03543c6272c9c51f2f5a"
|
||||
dependencies = [
|
||||
"rle-decode-fast",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.4"
|
||||
@@ -658,6 +866,15 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
@@ -675,9 +892,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "loopdev"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bfa0855b04611e38acaff718542e9e809cddfc16535d39f9d9c694ab19f7388"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/tiann/loopdev?branch=loopfix#b6ca5e3ea163f66239f6a835874fe231b2a9286f"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"errno",
|
||||
@@ -724,6 +940,25 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
@@ -752,6 +987,15 @@ version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "partition-identity"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa925f9becb532d758b0014b472c576869910929cf4c3f8054b386f19ab9e21"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.4.2"
|
||||
@@ -823,6 +1067,12 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.50"
|
||||
@@ -832,6 +1082,30 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-mounts"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d652f8435d0ab70bf4f3590a6a851d59604831a458086541b95238cc51ffcf2"
|
||||
dependencies = [
|
||||
"partition-identity",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "procfs"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "943ca7f9f29bab5844ecd8fdb3992c5969b6622bb9609b9502fef9b4310e3f1f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"flate2",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
@@ -919,6 +1193,47 @@ dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rle-decode-fast"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "6.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1"
|
||||
dependencies = [
|
||||
"include-flate",
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "6.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "7.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@@ -936,7 +1251,7 @@ dependencies = [
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -945,12 +1260,27 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
@@ -1032,9 +1362,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sys-mount"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793d38aa916d55e979587ea7cf551106f5f091e548a91e89ee4f0698bc9cf289"
|
||||
version = "2.0.2"
|
||||
source = "git+https://github.com/tiann/sys-mount#328928573feb9dbe6318d7ebcb8c8c9b681c47cd"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -1090,10 +1419,8 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1102,15 +1429,6 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.37"
|
||||
@@ -1155,6 +1473,12 @@ version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
@@ -1167,6 +1491,17 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
@@ -1179,6 +1514,60 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -1225,6 +1614,30 @@ dependencies = [
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.1"
|
||||
@@ -1283,9 +1696,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537ce7411d25e54e8ae21a7ce0b15840e7bfcff15b51d697ec3266cc76bdf080"
|
||||
checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"byteorder",
|
||||
@@ -1331,9 +1744,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.5+zstd.1.5.2"
|
||||
version = "2.0.6+zstd.1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596"
|
||||
checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
||||
@@ -22,13 +22,25 @@ encoding = "0.2.33"
|
||||
retry = "2.0.0"
|
||||
humansize = "2.0.0"
|
||||
libc = "0.2"
|
||||
sys-mount = "2.0.1"
|
||||
android-properties = { version = "0.2.2", features = ["bionic-deprecated"] }
|
||||
extattr = "1.0.0"
|
||||
jwalk = "0.8.1"
|
||||
is_executable = "1.0.1"
|
||||
nom = "7"
|
||||
derive-new = "0.5"
|
||||
rust-embed = { version = "6.4.2", features = [
|
||||
"debug-embed",
|
||||
"compression", # must clean build after updating binaries
|
||||
] }
|
||||
|
||||
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
||||
sys-mount = { git = "https://github.com/tiann/sys-mount" }
|
||||
# some android specific dependencies which compiles under unix are also listed here for convenience of coding
|
||||
android-properties = { version = "0.2.2", features = ["bionic-deprecated"] }
|
||||
procfs = "0.15"
|
||||
proc-mounts = "0.3"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.13"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
BIN
userspace/ksud/bin/aarch64/busybox
Executable file
BIN
userspace/ksud/bin/aarch64/busybox
Executable file
Binary file not shown.
BIN
userspace/ksud/bin/aarch64/resetprop
Normal file
BIN
userspace/ksud/bin/aarch64/resetprop
Normal file
Binary file not shown.
BIN
userspace/ksud/bin/x86_64/busybox
Normal file
BIN
userspace/ksud/bin/x86_64/busybox
Normal file
Binary file not shown.
BIN
userspace/ksud/bin/x86_64/resetprop
Normal file
BIN
userspace/ksud/bin/x86_64/resetprop
Normal file
Binary file not shown.
46
userspace/ksud/build.rs
Normal file
46
userspace/ksud/build.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
fn get_git_version() -> (u32, String) {
|
||||
let version_code = String::from_utf8(
|
||||
Command::new("git")
|
||||
.args(["rev-list", "--count", "HEAD"])
|
||||
.output()
|
||||
.expect("Failed to get git count")
|
||||
.stdout,
|
||||
)
|
||||
.expect("Failed to read git count stdout");
|
||||
let version_code: u32 = version_code
|
||||
.trim()
|
||||
.parse()
|
||||
.expect("Failed to parse git count");
|
||||
let version_code = 10000 + 200 + version_code; // For historical reasons
|
||||
|
||||
let version_name = String::from_utf8(
|
||||
Command::new("git")
|
||||
.args(["describe", "--tags", "--always"])
|
||||
.output()
|
||||
.expect("Failed to get git version")
|
||||
.stdout,
|
||||
)
|
||||
.expect("Failed to read git version stdout");
|
||||
(version_code, version_name)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (code, name) = get_git_version();
|
||||
let out_dir = env::var("OUT_DIR").expect("Failed to get $OUT_DIR");
|
||||
let out_dir = Path::new(&out_dir);
|
||||
File::create(Path::new(out_dir).join("VERSION_CODE"))
|
||||
.expect("Failed to create VERSION_CODE")
|
||||
.write_all(code.to_string().as_bytes())
|
||||
.expect("Failed to write VERSION_CODE");
|
||||
|
||||
File::create(Path::new(out_dir).join("VERSION_NAME"))
|
||||
.expect("Failed to create VERSION_NAME")
|
||||
.write_all(name.trim().as_bytes())
|
||||
.expect("Failed to write VERSION_NAME");
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
|
||||
let mut buffer = [0u8; 0x10];
|
||||
@@ -17,13 +16,13 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
|
||||
f.read_exact(&mut n)?;
|
||||
|
||||
let n = u16::from_le_bytes(n);
|
||||
if n as i64 == i {
|
||||
if i64::from(n) == i {
|
||||
f.seek(SeekFrom::Current(-22))?;
|
||||
f.read_exact(&mut size4)?;
|
||||
|
||||
if u32::from_le_bytes(size4) ^ 0xcafebabeu32 == 0xccfbf1eeu32 {
|
||||
if u32::from_le_bytes(size4) ^ 0xcafe_babe_u32 == 0xccfb_f1ee_u32 {
|
||||
if i > 0 {
|
||||
println!("warning: comment length is {}", i);
|
||||
println!("warning: comment length is {i}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -37,14 +36,14 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
|
||||
f.seek(SeekFrom::Current(12))?;
|
||||
// offset
|
||||
f.read_exact(&mut size4)?;
|
||||
f.seek(SeekFrom::Start(u32::from_le_bytes(size4) as u64 - 0x18))?;
|
||||
f.seek(SeekFrom::Start(u64::from(u32::from_le_bytes(size4)) - 0x18))?;
|
||||
|
||||
f.read_exact(&mut size8)?;
|
||||
f.read_exact(&mut buffer)?;
|
||||
|
||||
ensure!(&buffer == b"APK Sig Block 42", "Can not found sig block");
|
||||
|
||||
let pos = u32::from_le_bytes(size4) as u64 - (u64::from_le_bytes(size8) + 0x8);
|
||||
let pos = u64::from(u32::from_le_bytes(size4)) - (u64::from_le_bytes(size8) + 0x8);
|
||||
f.seek(SeekFrom::Start(pos))?;
|
||||
f.read_exact(&mut size_of_block)?;
|
||||
|
||||
@@ -62,7 +61,7 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
|
||||
f.read_exact(&mut id)?; // id
|
||||
|
||||
let id = u32::from_le_bytes(id);
|
||||
if (id ^ 0xdeadbeefu32) == 0xafa439f5u32 || (id ^ 0xdeadbeefu32) == 0x2efed62fu32 {
|
||||
if (id ^ 0xdead_beef_u32) == 0xafa4_39f5_u32 || (id ^ 0xdead_beef_u32) == 0x2efe_d62f_u32 {
|
||||
f.read_exact(&mut size4)?; // signer-sequence length
|
||||
f.read_exact(&mut size4)?; // signer length
|
||||
f.read_exact(&mut size4)?; // signed data length
|
||||
@@ -70,7 +69,7 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
|
||||
|
||||
f.read_exact(&mut size4)?; // digests-sequcence length
|
||||
let pos = u32::from_le_bytes(size4);
|
||||
f.seek(SeekFrom::Current(pos as i64))?;
|
||||
f.seek(SeekFrom::Current(i64::from(pos)))?;
|
||||
// offset += 0x4 + pos;
|
||||
|
||||
f.read_exact(&mut size4)?; // certificates length
|
||||
@@ -83,18 +82,20 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, u32)> {
|
||||
let j = u32::from_le_bytes(size4);
|
||||
for _ in 0..j {
|
||||
f.read_exact(&mut c)?;
|
||||
hash = hash.wrapping_mul(31).wrapping_add(c[0] as i8 as i32);
|
||||
hash = hash.wrapping_mul(31).wrapping_add(i32::from(c[0] as i8));
|
||||
}
|
||||
|
||||
// offset += j;
|
||||
|
||||
let out_size = j;
|
||||
let out_hash = (hash as u32) ^ 0x14131211u32;
|
||||
let out_hash = (hash as u32) ^ 0x1413_1211_u32;
|
||||
|
||||
return Ok((out_size, out_hash));
|
||||
}
|
||||
|
||||
f.seek(SeekFrom::Current(i64::from_le_bytes(size8) - offset as i64))?;
|
||||
f.seek(SeekFrom::Current(
|
||||
i64::from_le_bytes(size8) - i64::from(offset),
|
||||
))?;
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!("Unknown error"))
|
||||
|
||||
28
userspace/ksud/src/assets.rs
Normal file
28
userspace/ksud/src/assets.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use anyhow::Result;
|
||||
use const_format::concatcp;
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
use crate::{defs::BINARY_DIR, utils};
|
||||
|
||||
pub const RESETPROP_PATH: &str = concatcp!(BINARY_DIR, "resetprop");
|
||||
pub const BUSYBOX_PATH: &str = concatcp!(BINARY_DIR, "busybox");
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "bin/aarch64"]
|
||||
struct Asset;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "bin/x86_64"]
|
||||
struct Asset;
|
||||
|
||||
pub fn ensure_binaries() -> Result<()> {
|
||||
for file in Asset::iter() {
|
||||
utils::ensure_binary(
|
||||
format!("{BINARY_DIR}{file}"),
|
||||
&Asset::get(&file).unwrap().data,
|
||||
)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
use anyhow::{Ok, Result};
|
||||
use clap::Parser;
|
||||
|
||||
use crate::{apk_sign, debug, event, module};
|
||||
#[cfg(target_os = "android")]
|
||||
use android_logger::Config;
|
||||
#[cfg(target_os = "android")]
|
||||
use log::LevelFilter;
|
||||
|
||||
use crate::{apk_sign, debug, defs, event, module, utils};
|
||||
|
||||
/// KernelSU userspace cli
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(author, version = defs::VERSION_NAME, about, long_about = None)]
|
||||
struct Args {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
@@ -84,6 +89,12 @@ enum Sepolicy {
|
||||
/// sepolicy file path
|
||||
file: String,
|
||||
},
|
||||
|
||||
/// Check if sepolicy statement is supported/valid
|
||||
Check {
|
||||
/// sepolicy statements
|
||||
sepolicy: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(clap::Subcommand, Debug)]
|
||||
@@ -117,28 +128,44 @@ enum Module {
|
||||
}
|
||||
|
||||
pub fn run() -> Result<()> {
|
||||
#[cfg(target_os = "android")]
|
||||
android_logger::init_once(
|
||||
Config::default()
|
||||
.with_max_level(LevelFilter::Trace) // limit log level
|
||||
.with_tag("KernelSU"), // logs will show under mytag tag
|
||||
);
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
env_logger::init();
|
||||
|
||||
let cli = Args::parse();
|
||||
|
||||
log::info!("command: {:?}", cli.command);
|
||||
|
||||
let result = match cli.command {
|
||||
Commands::Daemon => event::daemon(),
|
||||
Commands::PostFsData => event::on_post_data_fs(),
|
||||
Commands::BootCompleted => event::on_boot_completed(),
|
||||
|
||||
Commands::Module { command } => {
|
||||
env_logger::init();
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
utils::switch_mnt_ns(1)?;
|
||||
utils::unshare_mnt_ns()?;
|
||||
}
|
||||
match command {
|
||||
Module::Install { zip } => module::install_module(zip),
|
||||
Module::Uninstall { id } => module::uninstall_module(id),
|
||||
Module::Enable { id } => module::enable_module(id),
|
||||
Module::Disable { id } => module::disable_module(id),
|
||||
Module::Install { zip } => module::install_module(&zip),
|
||||
Module::Uninstall { id } => module::uninstall_module(&id),
|
||||
Module::Enable { id } => module::enable_module(&id),
|
||||
Module::Disable { id } => module::disable_module(&id),
|
||||
Module::List => module::list_modules(),
|
||||
}
|
||||
}
|
||||
Commands::Install => event::install(),
|
||||
Commands::Sepolicy { command } => match command {
|
||||
Sepolicy::Patch { sepolicy } => crate::sepolicy::live_patch(&sepolicy),
|
||||
Sepolicy::Apply { file } => crate::sepolicy::apply_file(&file),
|
||||
Sepolicy::Apply { file } => crate::sepolicy::apply_file(file),
|
||||
Sepolicy::Check { sepolicy } => crate::sepolicy::check_rule(&sepolicy),
|
||||
},
|
||||
Commands::Services => event::on_services(),
|
||||
|
||||
@@ -154,7 +181,7 @@ pub fn run() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
Debug::Su => crate::ksu::grant_root(),
|
||||
Debug::Test => todo!()
|
||||
Debug::Test => todo!(),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ pub fn set_manager(pkg: &str) -> Result<()> {
|
||||
);
|
||||
|
||||
let path = get_apk_path(pkg).with_context(|| format!("{pkg} does not exist!"))?;
|
||||
let sign = get_apk_signature(path.as_str())?;
|
||||
let sign = get_apk_signature(&path)?;
|
||||
set_kernel_param(sign.0, sign.1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
use const_format::concatcp;
|
||||
|
||||
pub const DAEMON_PATH: &str = "/data/adb/ksud";
|
||||
pub const WORKING_DIR: &str = "/data/adb/ksu/";
|
||||
pub const ADB_DIR: &str = "/data/adb/";
|
||||
|
||||
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
|
||||
|
||||
pub const WORKING_DIR: &str = concatcp!(ADB_DIR, "ksu/");
|
||||
pub const BINARY_DIR: &str = concatcp!(WORKING_DIR, "bin/");
|
||||
|
||||
pub const MODULE_DIR: &str = concatcp!(WORKING_DIR, "modules/");
|
||||
pub const MODULE_IMG: &str = concatcp!(WORKING_DIR, "modules.img");
|
||||
@@ -15,3 +19,6 @@ pub const MODULE_UPDATE_TMP_DIR: &str = concatcp!(WORKING_DIR, "modules_update/"
|
||||
pub const DISABLE_FILE_NAME: &str = "disable";
|
||||
pub const UPDATE_FILE_NAME: &str = "update";
|
||||
pub const REMOVE_FILE_NAME: &str = "remove";
|
||||
|
||||
pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE"));
|
||||
pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME"));
|
||||
|
||||
@@ -1,41 +1,57 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use log::{info, warn};
|
||||
use std::{collections::HashMap, path::Path};
|
||||
|
||||
use crate::{
|
||||
defs,
|
||||
utils::{ensure_clean_dir, mount_image},
|
||||
assets, defs, mount,
|
||||
utils::{self, ensure_clean_dir, ensure_dir_exists},
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
use sys_mount::{FilesystemType, Mount, MountFlags};
|
||||
|
||||
fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) {
|
||||
fn mount_partition(partition: &str, lowerdir: &mut Vec<String>) -> Result<()> {
|
||||
if lowerdir.is_empty() {
|
||||
println!("partition: {partition} lowerdir is empty");
|
||||
return;
|
||||
warn!("partition: {partition} lowerdir is empty");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// if /partition is a symlink and linked to /system/partition, then we don't need to overlay it separately
|
||||
if Path::new(&format!("/{}", partition)).read_link().is_ok() {
|
||||
println!("partition: {} is a symlink", partition);
|
||||
return;
|
||||
if Path::new(&format!("/{partition}")).read_link().is_ok() {
|
||||
warn!("partition: {partition} is a symlink");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// handle stock mounts under /partition, we should restore the mount point after overlay
|
||||
let stock_mount = mount::StockMount::new(&format!("/{partition}/"))
|
||||
.with_context(|| format!("get stock mount of partition: {partition} failed"))?;
|
||||
let result = stock_mount.umount();
|
||||
if result.is_err() {
|
||||
let remount_result = stock_mount.remount();
|
||||
if remount_result.is_err() {
|
||||
log::error!("remount stock mount of failed: {:?}", remount_result);
|
||||
}
|
||||
bail!("umount stock mount of failed: {:?}", result);
|
||||
}
|
||||
|
||||
// add /partition as the lowerest dir
|
||||
let lowest_dir = format!("/{partition}");
|
||||
lowerdir.push(lowest_dir.clone());
|
||||
|
||||
let lowerdir = lowerdir.join(":");
|
||||
println!("partition: {partition} lowerdir: {lowerdir}");
|
||||
info!("partition: {partition} lowerdir: {lowerdir}");
|
||||
|
||||
if let Err(err) = Mount::builder()
|
||||
.fstype(FilesystemType::from("overlay"))
|
||||
.flags(MountFlags::RDONLY)
|
||||
.data(&format!("lowerdir={lowerdir}"))
|
||||
.mount("overlay", lowest_dir)
|
||||
{
|
||||
println!("mount partition: {partition} overlay failed: {err}");
|
||||
let result = mount::mount_overlay(&lowerdir, &lowest_dir);
|
||||
|
||||
if result.is_ok() && stock_mount.remount().is_err() {
|
||||
// if mount overlay ok but stock remount failed, we should umount overlay
|
||||
warn!("remount stock mount of failed, umount overlay {lowest_dir} now");
|
||||
if mount::umount_dir(&lowest_dir).is_err() {
|
||||
warn!("umount overlay {lowest_dir} failed");
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
|
||||
pub fn mount_systemlessly(module_dir: &str) -> Result<()> {
|
||||
// construct overlay mount params
|
||||
let dir = std::fs::read_dir(module_dir);
|
||||
let Ok(dir) = dir else {
|
||||
@@ -47,7 +63,7 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
|
||||
let partition = vec!["vendor", "product", "system_ext", "odm", "oem"];
|
||||
let mut partition_lowerdir: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for ele in &partition {
|
||||
partition_lowerdir.insert(ele.to_string(), Vec::new());
|
||||
partition_lowerdir.insert((*ele).to_string(), Vec::new());
|
||||
}
|
||||
|
||||
for entry in dir.flatten() {
|
||||
@@ -57,16 +73,14 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
|
||||
}
|
||||
let disabled = module.join(defs::DISABLE_FILE_NAME).exists();
|
||||
if disabled {
|
||||
println!("module: {} is disabled, ignore!", module.display());
|
||||
info!("module: {} is disabled, ignore!", module.display());
|
||||
continue;
|
||||
}
|
||||
|
||||
let module_system = Path::new(&module).join("system");
|
||||
if !module_system.as_path().exists() {
|
||||
println!("module: {} has no system overlay.", module.display());
|
||||
continue;
|
||||
if module_system.exists() {
|
||||
system_lowerdir.push(format!("{}", module_system.display()));
|
||||
}
|
||||
system_lowerdir.push(format!("{}", module_system.display()));
|
||||
|
||||
for part in &partition {
|
||||
// if /partition is a mountpoint, we would move it to $MODPATH/$partition when install
|
||||
@@ -82,11 +96,15 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
// mount /system first
|
||||
mount_partition("system", &mut system_lowerdir);
|
||||
if let Err(e) = mount_partition("system", &mut system_lowerdir) {
|
||||
warn!("mount system failed: {e}");
|
||||
}
|
||||
|
||||
// mount other partitions
|
||||
for (k, mut v) in partition_lowerdir {
|
||||
mount_partition(&k, &mut v);
|
||||
if let Err(e) = mount_partition(&k, &mut v) {
|
||||
warn!("mount {k} failed: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -94,6 +112,9 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
|
||||
|
||||
pub fn on_post_data_fs() -> Result<()> {
|
||||
crate::ksu::report_post_fs_data();
|
||||
|
||||
utils::umask(0);
|
||||
|
||||
let module_update_img = defs::MODULE_UPDATE_IMG;
|
||||
let module_img = defs::MODULE_IMG;
|
||||
let module_dir = defs::MODULE_DIR;
|
||||
@@ -105,6 +126,8 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
// we should clean the module mount point if it exists
|
||||
ensure_clean_dir(module_dir)?;
|
||||
|
||||
assets::ensure_binaries().with_context(|| "Failed to extract bin assets")?;
|
||||
|
||||
if Path::new(module_update_img).exists() {
|
||||
if module_update_flag.exists() {
|
||||
// if modules_update.img exists, and the the flag indicate this is an update
|
||||
@@ -119,42 +142,75 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// If there isn't any image exist, do nothing for module!
|
||||
if !Path::new(target_update_img).exists() {
|
||||
// no image exist, do nothing for module!
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("mount {} to {}", target_update_img, module_dir);
|
||||
mount_image(target_update_img, module_dir)?;
|
||||
// we should always mount the module.img to module dir
|
||||
// becuase we may need to operate the module dir in safe mode
|
||||
info!("mount module image: {target_update_img} to {module_dir}");
|
||||
mount::AutoMountExt4::try_new(target_update_img, module_dir, false)
|
||||
.with_context(|| "mount module image failed".to_string())?;
|
||||
|
||||
// check safe mode first.
|
||||
if crate::utils::is_safe_mode() {
|
||||
warn!("safe mode, skip post-fs-data scripts and disable all modules!");
|
||||
if let Err(e) = crate::module::disable_all_modules() {
|
||||
warn!("disable all modules failed: {}", e);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Then exec common post-fs-data scripts
|
||||
if let Err(e) = crate::module::exec_common_scripts("post-fs-data.d", true) {
|
||||
warn!("exec common post-fs-data scripts failed: {}", e);
|
||||
}
|
||||
|
||||
// load sepolicy.rule
|
||||
if (crate::module::load_sepolicy_rule().is_err()) {
|
||||
println!("load sepolicy.rule failed");
|
||||
if crate::module::load_sepolicy_rule().is_err() {
|
||||
warn!("load sepolicy.rule failed");
|
||||
}
|
||||
|
||||
// mount systemless overlay
|
||||
if let Err(e) = do_systemless_mount(module_dir) {
|
||||
println!("do systemless mount failed: {}", e);
|
||||
// exec modules post-fs-data scripts
|
||||
// TODO: Add timeout
|
||||
if let Err(e) = crate::module::exec_post_fs_data() {
|
||||
warn!("exec post-fs-data scripts failed: {}", e);
|
||||
}
|
||||
|
||||
// module mounted, exec modules post-fs-data scripts
|
||||
if !crate::utils::is_safe_mode() {
|
||||
// todo: Add timeout
|
||||
let _ = crate::module::exec_post_fs_data();
|
||||
let _ = crate::module::load_system_prop();
|
||||
} else {
|
||||
println!("safe mode, skip module post-fs-data scripts");
|
||||
// load system.prop
|
||||
if let Err(e) = crate::module::load_system_prop() {
|
||||
warn!("load system.prop failed: {}", e);
|
||||
}
|
||||
|
||||
// Finally, we should do systemless mount
|
||||
// But we should umount all stock overlayfs and remount them after module mounted
|
||||
let stock_overlay = mount::StockOverlay::new();
|
||||
stock_overlay.umount_all();
|
||||
|
||||
// mount moduke systemlessly by overlay
|
||||
if let Err(e) = mount_systemlessly(module_dir) {
|
||||
warn!("do systemless mount failed: {}", e);
|
||||
}
|
||||
|
||||
stock_overlay.mount_all();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn on_services() -> Result<()> {
|
||||
// exec modules service.sh scripts
|
||||
if !crate::utils::is_safe_mode() {
|
||||
let _ = crate::module::exec_services();
|
||||
} else {
|
||||
println!("safe mode, skip module service scripts");
|
||||
utils::umask(0);
|
||||
|
||||
// check safe mode first.
|
||||
if crate::utils::is_safe_mode() {
|
||||
warn!("safe mode, skip module service scripts");
|
||||
return Ok(());
|
||||
}
|
||||
if let Err(e) = crate::module::exec_common_scripts("service.d", false) {
|
||||
warn!("Failed to exec common service scripts: {}", e);
|
||||
}
|
||||
if let Err(e) = crate::module::exec_services() {
|
||||
warn!("Failed to exec service scripts: {}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -176,9 +232,9 @@ pub fn daemon() -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn install() -> Result<()> {
|
||||
let src = "/proc/self/exe";
|
||||
let dst = defs::DAEMON_PATH;
|
||||
ensure_dir_exists(defs::ADB_DIR)?;
|
||||
std::fs::copy("/proc/self/exe", defs::DAEMON_PATH)?;
|
||||
|
||||
std::fs::copy(src, dst)?;
|
||||
Ok(())
|
||||
// install binary assets
|
||||
assets::ensure_binaries().with_context(|| "Failed to extract assets")
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#!/system/bin/sh
|
||||
############################################
|
||||
# KernelSU installer script
|
||||
# Credit to Magisk!!!
|
||||
# mostly from module_installer.sh
|
||||
# and util_functions.sh in Magisk
|
||||
############################################
|
||||
|
||||
umask 022
|
||||
|
||||
ui_print() {
|
||||
if $BOOTMODE; then
|
||||
echo "$1"
|
||||
@@ -68,6 +71,11 @@ print_title() {
|
||||
ui_print "$bar"
|
||||
}
|
||||
|
||||
check_sepolicy() {
|
||||
/data/adb/ksud sepolicy check "$1"
|
||||
return $?
|
||||
}
|
||||
|
||||
######################
|
||||
# Environment Related
|
||||
######################
|
||||
@@ -267,10 +275,6 @@ request_size_check() {
|
||||
reqSizeM=`du -ms "$1" | cut -f1`
|
||||
}
|
||||
|
||||
unzip() {
|
||||
/system/bin/unzip -q "$@"
|
||||
}
|
||||
|
||||
request_zip_size_check() {
|
||||
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'`
|
||||
}
|
||||
@@ -366,7 +370,7 @@ install_module() {
|
||||
set_perm_recursive $MODPATH/system/bin 0 2000 0755 0755
|
||||
set_perm_recursive $MODPATH/system/xbin 0 2000 0755 0755
|
||||
set_perm_recursive $MODPATH/system/system_ext/bin 0 2000 0755 0755
|
||||
set_perm_recursive $MODPATH/system/vendor/bin 0 2000 0755 0755 u:object_r:vendor_file:s0
|
||||
set_perm_recursive $MODPATH/system/vendor 0 2000 0755 0755 u:object_r:vendor_file:s0
|
||||
fi
|
||||
|
||||
# Load customization script
|
||||
@@ -416,8 +420,5 @@ NVBASE=/data/adb/ksu
|
||||
TMPDIR=/dev/tmp
|
||||
|
||||
# Some modules dependents on this
|
||||
MAGISK_VER=25.2
|
||||
MAGISK_VER_CODE=25200
|
||||
|
||||
# KSU to recognize
|
||||
KSU=true
|
||||
export MAGISK_VER=25.2
|
||||
export MAGISK_VER_CODE=25200
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#![allow(dead_code, unused_mut, unused_variables, unused_imports)]
|
||||
use anyhow::Result;
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
#[cfg(unix)]
|
||||
use anyhow::ensure;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
pub const KERNEL_SU_OPTION: u32 = 0xDEADBEEF;
|
||||
pub const KERNEL_SU_OPTION: u32 = 0xDEAD_BEEF;
|
||||
|
||||
const CMD_GRANT_ROOT: u64 = 0;
|
||||
// const CMD_BECOME_MANAGER: u64 = 1;
|
||||
@@ -14,52 +16,76 @@ const CMD_GET_VERSION: u64 = 2;
|
||||
// const CMD_GET_DENY_LIST: u64 = 6;
|
||||
const CMD_REPORT_EVENT: u64 = 7;
|
||||
pub const CMD_SET_SEPOLICY: u64 = 8;
|
||||
pub const CMD_CHECK_SAFEMODE: u64 = 9;
|
||||
|
||||
const EVENT_POST_FS_DATA: u64 = 1;
|
||||
const EVENT_BOOT_COMPLETED: u64 = 2;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn grant_root() -> Result<()> {
|
||||
let mut result: u32 = 0;
|
||||
unsafe {
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
libc::prctl(
|
||||
KERNEL_SU_OPTION as i32,
|
||||
KERNEL_SU_OPTION as i32, // supposed to overflow
|
||||
CMD_GRANT_ROOT,
|
||||
0,
|
||||
0,
|
||||
&mut result as *mut _ as *mut libc::c_void,
|
||||
std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
|
||||
);
|
||||
}
|
||||
|
||||
ensure!(result == KERNEL_SU_OPTION, "grant root failed");
|
||||
return Err(std::process::Command::new("sh").exec().into());
|
||||
Err(std::process::Command::new("sh").exec().into())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn grant_root() -> Result<()> {
|
||||
unimplemented!("grant_root is only available on android");
|
||||
}
|
||||
|
||||
pub fn get_version() -> i32 {
|
||||
let mut result: i32 = 0;
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
unsafe {
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
libc::prctl(
|
||||
KERNEL_SU_OPTION as i32,
|
||||
KERNEL_SU_OPTION as i32, // supposed to overflow
|
||||
CMD_GET_VERSION,
|
||||
&mut result as *mut _ as *mut libc::c_void,
|
||||
std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
|
||||
);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn report_event(event: u64) {
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
unsafe {
|
||||
libc::prctl(KERNEL_SU_OPTION as i32, CMD_REPORT_EVENT, event);
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
libc::prctl(
|
||||
KERNEL_SU_OPTION as i32, // supposed to overflow
|
||||
CMD_REPORT_EVENT,
|
||||
event,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_kernel_safemode() -> bool {
|
||||
let mut result: i32 = 0;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
unsafe {
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
libc::prctl(
|
||||
KERNEL_SU_OPTION as i32, // supposed to overflow
|
||||
CMD_CHECK_SAFEMODE,
|
||||
0,
|
||||
0,
|
||||
std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
|
||||
);
|
||||
}
|
||||
result == KERNEL_SU_OPTION as i32
|
||||
}
|
||||
|
||||
pub fn report_post_fs_data() {
|
||||
report_event(EVENT_POST_FS_DATA);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
mod apk_sign;
|
||||
mod assets;
|
||||
mod cli;
|
||||
mod debug;
|
||||
mod defs;
|
||||
mod event;
|
||||
mod ksu;
|
||||
mod module;
|
||||
mod mount;
|
||||
mod restorecon;
|
||||
mod utils;
|
||||
mod sepolicy;
|
||||
mod utils;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
cli::run()
|
||||
|
||||
@@ -1,32 +1,59 @@
|
||||
use crate::{defs, restorecon, sepolicy};
|
||||
use crate::{restorecon::setsyscon, utils::*};
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use crate::utils::*;
|
||||
use crate::{
|
||||
assets, defs, mount,
|
||||
restorecon::{restore_syscon, setsyscon},
|
||||
sepolicy,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use const_format::concatcp;
|
||||
use is_executable::is_executable;
|
||||
use java_properties::PropertiesIter;
|
||||
use log::{info, warn};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{create_dir_all, remove_dir_all, File, OpenOptions},
|
||||
io::{Cursor, Read, Write},
|
||||
os::unix::{prelude::PermissionsExt, process::CommandExt},
|
||||
env::var as env_var,
|
||||
fs::{remove_dir_all, set_permissions, File, OpenOptions, Permissions},
|
||||
io::{Cursor, Write},
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
str::FromStr,
|
||||
};
|
||||
use zip_extensions::zip_extract_file_to_memory;
|
||||
|
||||
use anyhow::{bail, ensure, Context, Result};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::{prelude::PermissionsExt, process::CommandExt};
|
||||
|
||||
const UTIL_FUNCTIONS: &str = include_str!("./installer.sh");
|
||||
const INSTALL_MODULE_SCRIPT: &str =
|
||||
concatcp!(UTIL_FUNCTIONS, "\n", "install_module", "\n", "exit 0", "\n");
|
||||
const INSTALLER_CONTENT: &str = include_str!("./installer.sh");
|
||||
const INSTALL_MODULE_SCRIPT: &str = concatcp!(
|
||||
INSTALLER_CONTENT,
|
||||
"\n",
|
||||
"install_module",
|
||||
"\n",
|
||||
"exit 0",
|
||||
"\n"
|
||||
);
|
||||
|
||||
fn exec_install_script(module_file: &str) -> Result<()> {
|
||||
let realpath = std::fs::canonicalize(module_file)
|
||||
.with_context(|| format!("realpath: {module_file} failed"))?;
|
||||
|
||||
let result = Command::new("sh")
|
||||
.args(["-c", INSTALL_MODULE_SCRIPT])
|
||||
let result = Command::new(assets::BUSYBOX_PATH)
|
||||
.args(["sh", "-c", INSTALL_MODULE_SCRIPT])
|
||||
.env("ASH_STANDALONE", "1")
|
||||
.env(
|
||||
"PATH",
|
||||
format!(
|
||||
"{}:{}",
|
||||
env_var("PATH").unwrap(),
|
||||
defs::BINARY_DIR.trim_end_matches('/')
|
||||
),
|
||||
)
|
||||
.env("KSU", "true")
|
||||
.env("KSU_KERNEL_VER_CODE", crate::ksu::get_version().to_string())
|
||||
.env("KSU_VER", defs::VERSION_NAME)
|
||||
.env("KSU_VER_CODE", defs::VERSION_CODE)
|
||||
.env("OUTFD", "1")
|
||||
.env("ZIPFILE", realpath)
|
||||
.stderr(Stdio::null())
|
||||
@@ -48,33 +75,24 @@ fn ensure_boot_completed() -> Result<()> {
|
||||
}
|
||||
|
||||
fn mark_update() -> Result<()> {
|
||||
let update_file = Path::new(defs::WORKING_DIR).join(defs::UPDATE_FILE_NAME);
|
||||
if update_file.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
std::fs::File::create(update_file)?;
|
||||
Ok(())
|
||||
ensure_file_exists(concatcp!(defs::WORKING_DIR, defs::UPDATE_FILE_NAME))
|
||||
}
|
||||
|
||||
fn mark_module_state(module: &str, flag_file: &str, create_or_delete: bool) -> Result<()> {
|
||||
let module_state_file = Path::new(defs::MODULE_DIR).join(module).join(flag_file);
|
||||
if create_or_delete {
|
||||
if module_state_file.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
std::fs::File::create(module_state_file)?;
|
||||
ensure_file_exists(module_state_file)
|
||||
} else {
|
||||
if !module_state_file.exists() {
|
||||
return Ok(());
|
||||
if module_state_file.exists() {
|
||||
std::fs::remove_file(module_state_file)?;
|
||||
}
|
||||
std::fs::remove_file(module_state_file)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_minimal_image_size(img: &str) -> Result<u64> {
|
||||
check_image(img)?;
|
||||
|
||||
let output = Command::new("resize2fs")
|
||||
.args(["-P", img])
|
||||
.stdout(Stdio::piped())
|
||||
@@ -84,8 +102,8 @@ fn get_minimal_image_size(img: &str) -> Result<u64> {
|
||||
println!("- {}", output.trim());
|
||||
let regex = regex::Regex::new(r"filesystem: (\d+)")?;
|
||||
let result = regex
|
||||
.captures(output.as_ref())
|
||||
.ok_or_else(|| anyhow::anyhow!("regex not match"))?;
|
||||
.captures(&output)
|
||||
.ok_or(anyhow::anyhow!("regex not match"))?;
|
||||
let result = &result[1];
|
||||
let result = u64::from_str(result)?;
|
||||
Ok(result)
|
||||
@@ -112,8 +130,8 @@ fn check_image(img: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
fn grow_image_size(img: &str, extra_size: u64) -> Result<()> {
|
||||
let minimal_size = get_minimal_image_size(img)?;
|
||||
let target_size = minimal_size + extra_size;
|
||||
let minimal_size = get_minimal_image_size(img)?; // the minimal size is in KB
|
||||
let target_size = minimal_size * 1024 + extra_size;
|
||||
|
||||
// check image
|
||||
check_image(img)?;
|
||||
@@ -122,7 +140,7 @@ fn grow_image_size(img: &str, extra_size: u64) -> Result<()> {
|
||||
"- Target image size: {}",
|
||||
humansize::format_size(target_size, humansize::DECIMAL)
|
||||
);
|
||||
let target_size = target_size / 1024 + 1;
|
||||
let target_size = target_size / 1024 + 1024;
|
||||
|
||||
let result = Command::new("resize2fs")
|
||||
.args([img, &format!("{target_size}K")])
|
||||
@@ -131,6 +149,8 @@ fn grow_image_size(img: &str, extra_size: u64) -> Result<()> {
|
||||
.with_context(|| format!("Failed to exec resize2fs {img}"))?;
|
||||
ensure!(result.success(), "Failed to resize2fs: {}", result);
|
||||
|
||||
check_image(img)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -160,17 +180,6 @@ fn switch_cgroups() {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_executable(path: &Path) -> bool {
|
||||
let mut buffer: [u8; 2] = [0; 2];
|
||||
is_executable::is_executable(path)
|
||||
&& File::open(path).unwrap().read_exact(&mut buffer).is_ok()
|
||||
&& (
|
||||
buffer == [0x23, 0x21] // shebang #!
|
||||
|| buffer == [0x7f, 0x45]
|
||||
// ELF magic number 0x7F 'E'
|
||||
)
|
||||
}
|
||||
|
||||
pub fn load_sepolicy_rule() -> Result<()> {
|
||||
let modules_dir = Path::new(defs::MODULE_DIR);
|
||||
let dir = std::fs::read_dir(modules_dir)?;
|
||||
@@ -178,7 +187,7 @@ pub fn load_sepolicy_rule() -> Result<()> {
|
||||
let path = entry.path();
|
||||
let disabled = path.join(defs::DISABLE_FILE_NAME);
|
||||
if disabled.exists() {
|
||||
println!("{} is disabled, skip", path.display());
|
||||
info!("{} is disabled, skip", path.display());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -186,16 +195,57 @@ pub fn load_sepolicy_rule() -> Result<()> {
|
||||
if !rule_file.exists() {
|
||||
continue;
|
||||
}
|
||||
println!("load policy: {}", &rule_file.display());
|
||||
info!("load policy: {}", &rule_file.display());
|
||||
|
||||
if sepolicy::apply_file(&rule_file).is_err() {
|
||||
println!("Failed to load sepolicy.rule for {}", &rule_file.display());
|
||||
warn!("Failed to load sepolicy.rule for {}", &rule_file.display());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_script<T: AsRef<Path>>(path: T, wait: bool) -> Result<()> {
|
||||
info!("exec {}", path.as_ref().display());
|
||||
|
||||
let mut command = &mut Command::new(assets::BUSYBOX_PATH);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
command = command.process_group(0);
|
||||
command = unsafe {
|
||||
command.pre_exec(|| {
|
||||
// ignore the error?
|
||||
switch_cgroups();
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
}
|
||||
command = command
|
||||
.current_dir(path.as_ref().parent().unwrap())
|
||||
.arg("sh")
|
||||
.arg(path.as_ref())
|
||||
.env("ASH_STANDALONE", "1")
|
||||
.env("KSU", "true")
|
||||
.env("KSU_KERNEL_VER_CODE", crate::ksu::get_version().to_string())
|
||||
.env("KSU_VER_CODE", defs::VERSION_CODE)
|
||||
.env("KSU_VER", defs::VERSION_NAME)
|
||||
.env(
|
||||
"PATH",
|
||||
format!(
|
||||
"{}:{}",
|
||||
env_var("PATH").unwrap(),
|
||||
defs::BINARY_DIR.trim_end_matches('/')
|
||||
),
|
||||
);
|
||||
|
||||
let result = if wait {
|
||||
command.status().map(|_| ())
|
||||
} else {
|
||||
command.spawn().map(|_| ())
|
||||
};
|
||||
result.map_err(|err| anyhow!("Failed to exec {}: {}", path.as_ref().display(), err))
|
||||
}
|
||||
|
||||
/// execute every modules' post-fs-data.sh
|
||||
pub fn exec_post_fs_data() -> Result<()> {
|
||||
let modules_dir = Path::new(defs::MODULE_DIR);
|
||||
@@ -204,7 +254,7 @@ pub fn exec_post_fs_data() -> Result<()> {
|
||||
let path = entry.path();
|
||||
let disabled = path.join(defs::DISABLE_FILE_NAME);
|
||||
if disabled.exists() {
|
||||
println!("{} is disabled, skip", path.display());
|
||||
warn!("{} is disabled, skip", path.display());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -212,32 +262,30 @@ pub fn exec_post_fs_data() -> Result<()> {
|
||||
if !post_fs_data.exists() {
|
||||
continue;
|
||||
}
|
||||
println!("exec {} post-fs-data.sh", path.display());
|
||||
|
||||
let mut command_new;
|
||||
let mut command;
|
||||
if is_executable(&post_fs_data) {
|
||||
command_new = Command::new("sh");
|
||||
command = command_new.arg(&post_fs_data);
|
||||
} else {
|
||||
command_new = Command::new(&post_fs_data);
|
||||
command = &mut command_new;
|
||||
};
|
||||
exec_script(&post_fs_data, true)?;
|
||||
}
|
||||
|
||||
command = command
|
||||
.process_group(0)
|
||||
.current_dir(path)
|
||||
.env("KSU", "true");
|
||||
unsafe {
|
||||
command = command.pre_exec(|| {
|
||||
// ignore the error?
|
||||
switch_cgroups();
|
||||
Ok(())
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exec_common_scripts(dir: &str, wait: bool) -> Result<()> {
|
||||
let script_dir = Path::new(defs::WORKING_DIR).join(dir);
|
||||
if !script_dir.exists() {
|
||||
info!("{} not exists, skip", script_dir.display());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let dir = std::fs::read_dir(&script_dir)?;
|
||||
for entry in dir.flatten() {
|
||||
let path = entry.path();
|
||||
|
||||
if !is_executable(&path) {
|
||||
warn!("{} is not executable, skip", path.display());
|
||||
continue;
|
||||
}
|
||||
command
|
||||
.status()
|
||||
.with_context(|| format!("Failed to exec {}", post_fs_data.display()))?;
|
||||
|
||||
exec_script(path, wait)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -251,7 +299,7 @@ pub fn exec_services() -> Result<()> {
|
||||
let path = entry.path();
|
||||
let disabled = path.join(defs::DISABLE_FILE_NAME);
|
||||
if disabled.exists() {
|
||||
println!("{} is disabled, skip", path.display());
|
||||
warn!("{} is disabled, skip", path.display());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -259,58 +307,21 @@ pub fn exec_services() -> Result<()> {
|
||||
if !service.exists() {
|
||||
continue;
|
||||
}
|
||||
println!("exec {} service.sh", path.display());
|
||||
|
||||
let mut command_new;
|
||||
let mut command;
|
||||
if is_executable(&service) {
|
||||
command_new = Command::new("sh");
|
||||
command = command_new.arg(&service);
|
||||
} else {
|
||||
command_new = Command::new(&service);
|
||||
command = &mut command_new;
|
||||
};
|
||||
command = command
|
||||
.process_group(0)
|
||||
.current_dir(path)
|
||||
.env("KSU", "true");
|
||||
unsafe {
|
||||
command = command.pre_exec(|| {
|
||||
// ignore the error?
|
||||
switch_cgroups();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
command
|
||||
.spawn() // don't wait
|
||||
.with_context(|| format!("Failed to exec {}", service.display()))?;
|
||||
exec_script(&service, false)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const RESETPROP: &[u8] = include_bytes!("./resetprop");
|
||||
const RESETPROP_PATH: &str = concatcp!(defs::WORKING_DIR, "/resetprop");
|
||||
|
||||
fn ensure_resetprop() -> Result<()> {
|
||||
if Path::new(RESETPROP_PATH).exists() {
|
||||
return Ok(());
|
||||
}
|
||||
std::fs::write(RESETPROP_PATH, RESETPROP)?;
|
||||
std::fs::set_permissions(RESETPROP_PATH, std::fs::Permissions::from_mode(0o755))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_system_prop() -> Result<()> {
|
||||
ensure_resetprop()?;
|
||||
|
||||
let modules_dir = Path::new(defs::MODULE_DIR);
|
||||
let dir = std::fs::read_dir(modules_dir)?;
|
||||
for entry in dir.flatten() {
|
||||
let path = entry.path();
|
||||
let disabled = path.join(defs::DISABLE_FILE_NAME);
|
||||
if disabled.exists() {
|
||||
println!("{} is disabled, skip", path.display());
|
||||
info!("{} is disabled, skip", path.display());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -318,10 +329,10 @@ pub fn load_system_prop() -> Result<()> {
|
||||
if !system_prop.exists() {
|
||||
continue;
|
||||
}
|
||||
println!("load {} system.prop", path.display());
|
||||
info!("load {} system.prop", path.display());
|
||||
|
||||
// resetprop -n --file system.prop
|
||||
Command::new(RESETPROP_PATH)
|
||||
Command::new(assets::RESETPROP_PATH)
|
||||
.arg("-n")
|
||||
.arg("--file")
|
||||
.arg(&system_prop)
|
||||
@@ -332,27 +343,22 @@ pub fn load_system_prop() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_module(zip: String) -> Result<()> {
|
||||
fn _install_module(zip: &str) -> Result<()> {
|
||||
ensure_boot_completed()?;
|
||||
|
||||
// print banner
|
||||
println!(include_str!("banner"));
|
||||
|
||||
// first check if workding dir is usable
|
||||
let working_dir = Path::new(defs::WORKING_DIR);
|
||||
if !working_dir.exists() {
|
||||
create_dir_all(working_dir)?;
|
||||
}
|
||||
assets::ensure_binaries().with_context(|| "Failed to extract assets")?;
|
||||
|
||||
ensure!(
|
||||
working_dir.is_dir(),
|
||||
"working dir exists but it is not a regular directory!"
|
||||
);
|
||||
// first check if workding dir is usable
|
||||
ensure_dir_exists(defs::WORKING_DIR).with_context(|| "Failed to create working dir")?;
|
||||
ensure_dir_exists(defs::BINARY_DIR).with_context(|| "Failed to create bin dir")?;
|
||||
|
||||
// read the module_id from zip, if faild if will return early.
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
let entry_path = PathBuf::from_str("module.prop")?;
|
||||
let zip_path = PathBuf::from_str(&zip)?;
|
||||
let zip_path = PathBuf::from_str(zip)?;
|
||||
zip_extract_file_to_memory(&zip_path, &entry_path, &mut buffer)?;
|
||||
|
||||
let mut module_prop = HashMap::new();
|
||||
@@ -361,11 +367,11 @@ pub fn install_module(zip: String) -> Result<()> {
|
||||
module_prop.insert(k, v);
|
||||
},
|
||||
)?;
|
||||
info!("module prop: {:?}", module_prop);
|
||||
|
||||
let Some(module_id) = module_prop.get("id") else {
|
||||
bail!("module id not found in module.prop!");
|
||||
};
|
||||
info!("module id: {}", module_id);
|
||||
|
||||
let modules_img = Path::new(defs::MODULE_IMG);
|
||||
let modules_update_img = Path::new(defs::MODULE_UPDATE_IMG);
|
||||
@@ -382,9 +388,18 @@ pub fn install_module(zip: String) -> Result<()> {
|
||||
}
|
||||
|
||||
let default_reserve_size = 64 * 1024 * 1024;
|
||||
let zip_uncompressed_size = get_zip_uncompressed_size(&zip)?;
|
||||
let zip_uncompressed_size = get_zip_uncompressed_size(zip)?;
|
||||
let grow_size = default_reserve_size + zip_uncompressed_size;
|
||||
|
||||
info!(
|
||||
"zip uncompressed size: {}",
|
||||
humansize::format_size(zip_uncompressed_size, humansize::DECIMAL)
|
||||
);
|
||||
info!(
|
||||
"grow size: {}",
|
||||
humansize::format_size(grow_size, humansize::DECIMAL)
|
||||
);
|
||||
|
||||
println!("- Preparing image");
|
||||
println!(
|
||||
"- Module size: {}",
|
||||
@@ -394,6 +409,7 @@ pub fn install_module(zip: String) -> Result<()> {
|
||||
if !modules_img_exist && !modules_update_img_exist {
|
||||
// if no modules and modules_update, it is brand new installation, we should create a new img
|
||||
// create a tmp module img and mount it to modules_update
|
||||
info!("Creating brand new module image");
|
||||
File::create(tmp_module_img)
|
||||
.context("Failed to create ext4 image file")?
|
||||
.set_len(grow_size)
|
||||
@@ -413,6 +429,7 @@ pub fn install_module(zip: String) -> Result<()> {
|
||||
check_image(tmp_module_img)?;
|
||||
} else if modules_update_img_exist {
|
||||
// modules_update.img exists, we should use it as tmp img
|
||||
info!("Using existing modules_update.img as tmp image");
|
||||
std::fs::copy(modules_update_img, tmp_module_img).with_context(|| {
|
||||
format!(
|
||||
"Failed to copy {} to {}",
|
||||
@@ -424,6 +441,7 @@ pub fn install_module(zip: String) -> Result<()> {
|
||||
grow_image_size(tmp_module_img, grow_size)?;
|
||||
} else {
|
||||
// modules.img exists, we should use it as tmp img
|
||||
info!("Using existing modules.img as tmp image");
|
||||
std::fs::copy(modules_img, tmp_module_img).with_context(|| {
|
||||
format!(
|
||||
"Failed to copy {} to {}",
|
||||
@@ -441,52 +459,59 @@ pub fn install_module(zip: String) -> Result<()> {
|
||||
// mount the modules_update.img to mountpoint
|
||||
println!("- Mounting image");
|
||||
|
||||
mount_image(tmp_module_img, module_update_tmp_dir)?;
|
||||
let _dontdrop = mount::AutoMountExt4::try_new(tmp_module_img, module_update_tmp_dir, true)?;
|
||||
|
||||
info!("mounted {} to {}", tmp_module_img, module_update_tmp_dir);
|
||||
|
||||
setsyscon(module_update_tmp_dir)?;
|
||||
|
||||
let result = {
|
||||
let module_dir = format!("{}/{}", module_update_tmp_dir, module_id);
|
||||
ensure_clean_dir(&module_dir)?;
|
||||
info!("module dir: {}", module_dir);
|
||||
let module_dir = format!("{module_update_tmp_dir}/{module_id}");
|
||||
ensure_clean_dir(&module_dir)?;
|
||||
info!("module dir: {}", module_dir);
|
||||
|
||||
// unzip the image and move it to modules_update/<id> dir
|
||||
let file = File::open(&zip)?;
|
||||
let mut archive = zip::ZipArchive::new(file)?;
|
||||
archive.extract(&module_dir)?;
|
||||
// unzip the image and move it to modules_update/<id> dir
|
||||
let file = File::open(zip)?;
|
||||
let mut archive = zip::ZipArchive::new(file)?;
|
||||
archive.extract(&module_dir)?;
|
||||
|
||||
// set selinux for module/system dir
|
||||
let mut module_system_dir = PathBuf::from(module_dir);
|
||||
module_system_dir.push("system");
|
||||
let module_system_dir = module_system_dir.as_path();
|
||||
if module_system_dir.exists() {
|
||||
let path = format!("{}", module_system_dir.display());
|
||||
restorecon::restore_syscon(&path)?;
|
||||
}
|
||||
// set permission and selinux context for $MOD/system
|
||||
let module_system_dir = PathBuf::from(module_dir).join("system");
|
||||
if module_system_dir.exists() {
|
||||
#[cfg(unix)]
|
||||
set_permissions(&module_system_dir, Permissions::from_mode(0o755))?;
|
||||
restore_syscon(&module_system_dir)?;
|
||||
}
|
||||
|
||||
exec_install_script(&zip)
|
||||
};
|
||||
|
||||
// umount the modules_update.img
|
||||
let _ = umount_dir(module_update_tmp_dir);
|
||||
|
||||
// remove modules_update dir, ignore the error
|
||||
let _ = remove_dir_all(module_update_tmp_dir);
|
||||
|
||||
// return if exec script failed
|
||||
result.with_context(|| format!("Failed to execute install script for {}", module_id))?;
|
||||
exec_install_script(zip)?;
|
||||
|
||||
info!("rename {tmp_module_img} to {}", defs::MODULE_UPDATE_IMG);
|
||||
// all done, rename the tmp image to modules_update.img
|
||||
if std::fs::rename(tmp_module_img, defs::MODULE_UPDATE_IMG).is_err() {
|
||||
warn!("Rename image failed, try copy it.");
|
||||
std::fs::copy(tmp_module_img, defs::MODULE_UPDATE_IMG)
|
||||
.with_context(|| "Failed to copy image.".to_string())?;
|
||||
let _ = std::fs::remove_file(tmp_module_img);
|
||||
}
|
||||
|
||||
mark_update()?;
|
||||
|
||||
info!("Module install successfully!");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_module_update<F>(update_dir: &str, id: &str, func: F) -> Result<()>
|
||||
pub fn install_module(zip: &str) -> Result<()> {
|
||||
let result = _install_module(zip);
|
||||
if let Err(ref e) = result {
|
||||
// error happened, do some cleanup!
|
||||
let _ = std::fs::remove_file(defs::MODULE_UPDATE_TMP_IMG);
|
||||
let _ = mount::umount_dir(defs::MODULE_UPDATE_TMP_DIR);
|
||||
println!("- Error: {e}");
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn update_module<F>(update_dir: &str, id: &str, func: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&str, &str) -> Result<()>,
|
||||
{
|
||||
@@ -517,28 +542,27 @@ where
|
||||
ensure_clean_dir(update_dir)?;
|
||||
|
||||
// mount the modules_update img
|
||||
mount_image(defs::MODULE_UPDATE_TMP_IMG, update_dir)?;
|
||||
let _dontdrop = mount::AutoMountExt4::try_new(defs::MODULE_UPDATE_TMP_IMG, update_dir, true)?;
|
||||
|
||||
// call the operation func
|
||||
let result = func(id, update_dir);
|
||||
|
||||
// umount modules_update.img
|
||||
let _ = umount_dir(update_dir);
|
||||
let _ = remove_dir_all(update_dir);
|
||||
|
||||
std::fs::rename(modules_update_tmp_img, defs::MODULE_UPDATE_IMG)?;
|
||||
if let Err(e) = std::fs::rename(modules_update_tmp_img, defs::MODULE_UPDATE_IMG) {
|
||||
warn!("Rename image failed: {e}, try copy it.");
|
||||
std::fs::copy(modules_update_tmp_img, defs::MODULE_UPDATE_IMG)
|
||||
.with_context(|| "Failed to copy image.".to_string())?;
|
||||
let _ = std::fs::remove_file(modules_update_tmp_img);
|
||||
}
|
||||
|
||||
mark_update()?;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn uninstall_module(id: String) -> Result<()> {
|
||||
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| {
|
||||
pub fn uninstall_module(id: &str) -> Result<()> {
|
||||
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
|
||||
let dir = Path::new(update_dir);
|
||||
if !dir.exists() {
|
||||
bail!("No module installed");
|
||||
}
|
||||
ensure!(dir.exists(), "No module installed");
|
||||
|
||||
// iterate the modules_update dir, find the module to be removed
|
||||
let dir = std::fs::read_dir(dir)?;
|
||||
@@ -563,24 +587,22 @@ pub fn uninstall_module(id: String) -> Result<()> {
|
||||
}
|
||||
|
||||
// santity check
|
||||
let target_module_path = format!("{}/{}", update_dir, mid);
|
||||
let target_module_path = format!("{update_dir}/{mid}");
|
||||
let target_module = Path::new(&target_module_path);
|
||||
if target_module.exists() {
|
||||
remove_dir_all(target_module)?;
|
||||
}
|
||||
|
||||
let _ = mark_module_state(&id, defs::REMOVE_FILE_NAME, true);
|
||||
let _ = mark_module_state(id, defs::REMOVE_FILE_NAME, true);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn do_enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
|
||||
let src_module_path = format!("{}/{}", module_dir, mid);
|
||||
fn _enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
|
||||
let src_module_path = format!("{module_dir}/{mid}");
|
||||
let src_module = Path::new(&src_module_path);
|
||||
if !src_module.exists() {
|
||||
bail!("module: {} not found!", mid);
|
||||
}
|
||||
ensure!(src_module.exists(), "module: {} not found!", mid);
|
||||
|
||||
let disable_path = src_module.join(defs::DISABLE_FILE_NAME);
|
||||
if enable {
|
||||
@@ -589,8 +611,8 @@ fn do_enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
|
||||
format!("Failed to remove disable file: {}", &disable_path.display())
|
||||
})?;
|
||||
}
|
||||
} else if !disable_path.exists() {
|
||||
std::fs::File::create(disable_path)?;
|
||||
} else {
|
||||
ensure_file_exists(disable_path)?;
|
||||
}
|
||||
|
||||
let _ = mark_module_state(mid, defs::DISABLE_FILE_NAME, !enable);
|
||||
@@ -598,19 +620,33 @@ fn do_enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enable_module(id: String) -> Result<()> {
|
||||
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| {
|
||||
do_enable_module(update_dir, mid, true)
|
||||
pub fn enable_module(id: &str) -> Result<()> {
|
||||
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
|
||||
_enable_module(update_dir, mid, true)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn disable_module(id: String) -> Result<()> {
|
||||
do_module_update(defs::MODULE_UPDATE_TMP_DIR, &id, |mid, update_dir| {
|
||||
do_enable_module(update_dir, mid, false)
|
||||
pub fn disable_module(id: &str) -> Result<()> {
|
||||
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
|
||||
_enable_module(update_dir, mid, false)
|
||||
})
|
||||
}
|
||||
|
||||
fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
|
||||
pub fn disable_all_modules() -> Result<()> {
|
||||
// we assume the module dir is already mounted
|
||||
let dir = std::fs::read_dir(defs::MODULE_DIR)?;
|
||||
for entry in dir.flatten() {
|
||||
let path = entry.path();
|
||||
let disable_flag = path.join(defs::DISABLE_FILE_NAME);
|
||||
if let Err(e) = ensure_file_exists(disable_flag) {
|
||||
warn!("Failed to disable module: {}: {}", path.display(), e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn _list_modules(path: &str) -> Vec<HashMap<String, String>> {
|
||||
// first check enabled modules
|
||||
let dir = std::fs::read_dir(path);
|
||||
let Ok(dir) = dir else {
|
||||
@@ -631,7 +667,7 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
|
||||
warn!("Failed to read file: {}", module_prop.display());
|
||||
continue;
|
||||
};
|
||||
let mut module_prop_map = HashMap::new();
|
||||
let mut module_prop_map: HashMap<String, String> = HashMap::new();
|
||||
let encoding = encoding::all::UTF_8;
|
||||
let result =
|
||||
PropertiesIter::new_with_encoding(Cursor::new(content), encoding).read_into(|k, v| {
|
||||
@@ -643,9 +679,9 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
|
||||
let update = path.join(defs::UPDATE_FILE_NAME).exists();
|
||||
let remove = path.join(defs::REMOVE_FILE_NAME).exists();
|
||||
|
||||
module_prop_map.insert("enabled".to_string(), enabled.to_string());
|
||||
module_prop_map.insert("update".to_string(), update.to_string());
|
||||
module_prop_map.insert("remove".to_string(), remove.to_string());
|
||||
module_prop_map.insert("enabled".to_owned(), enabled.to_string());
|
||||
module_prop_map.insert("update".to_owned(), update.to_string());
|
||||
module_prop_map.insert("remove".to_owned(), remove.to_string());
|
||||
|
||||
if result.is_err() {
|
||||
warn!("Failed to parse module.prop: {}", module_prop.display());
|
||||
@@ -658,7 +694,7 @@ fn do_list_modules(path: &str) -> Vec<HashMap<String, String>> {
|
||||
}
|
||||
|
||||
pub fn list_modules() -> Result<()> {
|
||||
let modules = do_list_modules(defs::MODULE_DIR);
|
||||
let modules = _list_modules(defs::MODULE_DIR);
|
||||
println!("{}", serde_json::to_string_pretty(&modules)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
343
userspace/ksud/src/mount.rs
Normal file
343
userspace/ksud/src/mount.rs
Normal file
@@ -0,0 +1,343 @@
|
||||
use anyhow::{Ok, Result};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use anyhow::Context;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use retry::delay::NoDelay;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use sys_mount::{unmount, FilesystemType, Mount, MountFlags, Unmount, UnmountFlags};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use procfs::process::{MountInfo, Process};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub struct AutoMountExt4 {
|
||||
mnt: String,
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mount: Option<Mount>,
|
||||
auto_umount: bool,
|
||||
}
|
||||
|
||||
impl AutoMountExt4 {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn try_new(src: &str, mnt: &str, auto_umount: bool) -> Result<Self> {
|
||||
let result = Mount::builder()
|
||||
.fstype(FilesystemType::from("ext4"))
|
||||
.flags(MountFlags::empty())
|
||||
.mount(src, mnt)
|
||||
.map(|mount| {
|
||||
Ok(Self {
|
||||
mnt: mnt.to_string(),
|
||||
mount: Some(mount),
|
||||
auto_umount,
|
||||
})
|
||||
});
|
||||
if let Err(e) = result {
|
||||
println!("- Mount failed: {e}, retry with system mount");
|
||||
let result = std::process::Command::new("mount")
|
||||
.arg("-t")
|
||||
.arg("ext4")
|
||||
.arg(src)
|
||||
.arg(mnt)
|
||||
.status();
|
||||
if let Err(e) = result {
|
||||
Err(anyhow::anyhow!(
|
||||
"mount partition: {src} -> {mnt} failed: {e}"
|
||||
))
|
||||
} else {
|
||||
Ok(Self {
|
||||
mnt: mnt.to_string(),
|
||||
mount: None,
|
||||
auto_umount,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
result.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn try_new(_src: &str, _mnt: &str, _auto_umount: bool) -> Result<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn umount(&self) -> Result<()> {
|
||||
if let Some(ref mount) = self.mount {
|
||||
mount
|
||||
.unmount(UnmountFlags::empty())
|
||||
.map_err(|e| anyhow::anyhow!(e))
|
||||
} else {
|
||||
let result = std::process::Command::new("umount").arg(&self.mnt).status();
|
||||
if let Err(e) = result {
|
||||
Err(anyhow::anyhow!("umount: {} failed: {e}", self.mnt))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
impl Drop for AutoMountExt4 {
|
||||
fn drop(&mut self) {
|
||||
log::info!(
|
||||
"AutoMountExt4 drop: {}, auto_umount: {}",
|
||||
self.mnt,
|
||||
self.auto_umount
|
||||
);
|
||||
if self.auto_umount {
|
||||
let _ = self.umount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn mount_image(src: &str, target: &str, autodrop: bool) -> Result<()> {
|
||||
if autodrop {
|
||||
Mount::builder()
|
||||
.fstype(FilesystemType::from("ext4"))
|
||||
.mount_autodrop(src, target, UnmountFlags::empty())
|
||||
.with_context(|| format!("Failed to do mount: {src} -> {target}"))?;
|
||||
} else {
|
||||
Mount::builder()
|
||||
.fstype(FilesystemType::from("ext4"))
|
||||
.mount(src, target)
|
||||
.with_context(|| format!("Failed to do mount: {src} -> {target}"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn mount_ext4(src: &str, target: &str, autodrop: bool) -> Result<()> {
|
||||
// umount target first.
|
||||
let _ = umount_dir(target);
|
||||
let result = retry::retry(NoDelay.take(3), || mount_image(src, target, autodrop));
|
||||
result
|
||||
.map_err(|e| anyhow::anyhow!("mount partition: {src} -> {target} failed: {e}"))
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn umount_dir(src: &str) -> Result<()> {
|
||||
unmount(src, UnmountFlags::empty()).with_context(|| format!("Failed to umount {src}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn mount_overlay(lowerdir: &str, mnt: &str) -> Result<()> {
|
||||
Mount::builder()
|
||||
.fstype(FilesystemType::from("overlay"))
|
||||
.flags(MountFlags::RDONLY)
|
||||
.data(&format!("lowerdir={lowerdir}"))
|
||||
.mount("overlay", mnt)
|
||||
.map(|_| ())
|
||||
.map_err(|e| anyhow::anyhow!("mount partition: {mnt} overlay failed: {e}"))
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn mount_ext4(_src: &str, _target: &str, _autodrop: bool) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn umount_dir(_src: &str) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn mount_overlay(_lowerdir: &str, _mnt: &str) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub struct StockOverlay {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mountinfos: Vec<MountInfo>,
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
impl StockOverlay {
|
||||
pub fn new() -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn mount_all(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn umount_all(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
impl StockOverlay {
|
||||
pub fn new() -> Self {
|
||||
if let std::result::Result::Ok(process) = Process::myself() {
|
||||
if let std::result::Result::Ok(mountinfos) = process.mountinfo() {
|
||||
let overlay_mounts = mountinfos
|
||||
.into_iter()
|
||||
.filter(|m| m.fs_type == "overlay")
|
||||
.collect::<Vec<_>>();
|
||||
return Self {
|
||||
mountinfos: overlay_mounts,
|
||||
};
|
||||
}
|
||||
}
|
||||
Self { mountinfos: vec![] }
|
||||
}
|
||||
|
||||
pub fn mount_all(&self) {
|
||||
log::info!("stock overlay: mount all: {:?}", self.mountinfos);
|
||||
for mount in self.mountinfos.clone() {
|
||||
let Some(mnt) = mount.mount_point.to_str() else {
|
||||
log::warn!("Failed to get mount point");
|
||||
continue;
|
||||
};
|
||||
|
||||
if mnt == "/system" {
|
||||
log::warn!("stock overlay found /system, skip!");
|
||||
continue;
|
||||
}
|
||||
|
||||
let (_flags, b): (HashSet<_>, HashSet<_>) = mount
|
||||
.mount_options
|
||||
.into_iter()
|
||||
.chain(mount.super_options)
|
||||
.partition(|(_, m)| m.is_none());
|
||||
|
||||
let mut overlay_opts = vec![];
|
||||
for (opt, val) in b {
|
||||
if let Some(val) = val {
|
||||
overlay_opts.push(format!("{opt}={val}"));
|
||||
} else {
|
||||
log::warn!("opt empty: {}", opt);
|
||||
}
|
||||
}
|
||||
let overlay_data = overlay_opts.join(",");
|
||||
let result = Mount::builder()
|
||||
.fstype(FilesystemType::from("overlay"))
|
||||
.flags(MountFlags::RDONLY)
|
||||
.data(&overlay_data)
|
||||
.mount("overlay", mnt);
|
||||
if let Err(e) = result {
|
||||
log::error!(
|
||||
"stock mount overlay: {} failed: {}",
|
||||
mount.mount_point.display(),
|
||||
e
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
"stock mount :{} overlay_opts: {}",
|
||||
mount.mount_point.display(),
|
||||
overlay_opts.join(",")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn umount_all(&self) {
|
||||
log::info!("stock overlay: umount all: {:?}", self.mountinfos);
|
||||
for mnt in &self.mountinfos {
|
||||
let Some(p) = mnt.mount_point.to_str() else {
|
||||
log::warn!("Failed to umount: {}", mnt.mount_point.display());
|
||||
continue;
|
||||
};
|
||||
|
||||
let result = umount_dir(p);
|
||||
log::info!("stock umount {}: {:?}", p, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// some ROMs mount device(ext4,exfat) to /vendor, when we do overlay mount, it will overlay
|
||||
// the stock mounts, these mounts include bt_firmware, wifi_firmware, etc.
|
||||
// so we to remount these mounts when we do overlay mount.
|
||||
// this is a workaround, we should find a better way to do this.
|
||||
#[derive(Debug)]
|
||||
pub struct StockMount {
|
||||
mnt: String,
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mountlist: proc_mounts::MountList,
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
impl StockMount {
|
||||
pub fn new(mnt: &str) -> Result<Self> {
|
||||
let mountlist = proc_mounts::MountList::new()?;
|
||||
Ok(Self {
|
||||
mnt: mnt.to_string(),
|
||||
mountlist,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_target_mounts(&self) -> Vec<&proc_mounts::MountInfo> {
|
||||
let mounts = self
|
||||
.mountlist
|
||||
.destination_starts_with(std::path::Path::new(&self.mnt))
|
||||
.filter(|m| m.fstype != "overlay");
|
||||
mounts.collect()
|
||||
}
|
||||
|
||||
pub fn umount(&self) -> Result<()> {
|
||||
let mounts = self.get_target_mounts();
|
||||
log::info!("stock mount for {} : {:?}", self.mnt, mounts);
|
||||
for m in mounts {
|
||||
let dst = m
|
||||
.dest
|
||||
.to_str()
|
||||
.ok_or(anyhow::anyhow!("Failed to get dst"))?;
|
||||
umount_dir(dst)?;
|
||||
log::info!("umount: {:?}", m);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remount(&self) -> Result<()> {
|
||||
let mounts = self.get_target_mounts();
|
||||
for m in mounts {
|
||||
let src = std::fs::canonicalize(&m.source)?;
|
||||
|
||||
let src = src.to_str().ok_or(anyhow::anyhow!("Failed to get src"))?;
|
||||
let dst = m
|
||||
.dest
|
||||
.to_str()
|
||||
.ok_or(anyhow::anyhow!("Failed to get dst"))?;
|
||||
|
||||
let fstype = m.fstype.as_str();
|
||||
let options = m.options.join(",");
|
||||
|
||||
log::info!("mount: {:?}", m);
|
||||
std::process::Command::new("mount")
|
||||
.arg("-t")
|
||||
.arg(fstype)
|
||||
.arg("-o")
|
||||
.arg(options)
|
||||
.arg(src)
|
||||
.arg(dst)
|
||||
.status()
|
||||
.with_context(|| format!("Failed to mount {:?}", m))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
impl StockMount {
|
||||
pub fn new(mnt: &str) -> Result<Self> {
|
||||
Ok(Self {
|
||||
mnt: mnt.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn umount(&self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn remount(&self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,31 +1,44 @@
|
||||
use anyhow::{Context, Ok, Result};
|
||||
use extattr::{setxattr, Flags as XattrFlags};
|
||||
use anyhow::Result;
|
||||
use jwalk::{Parallelism::Serial, WalkDir};
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use anyhow::{Context, Ok};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use extattr::{setxattr, Flags as XattrFlags};
|
||||
|
||||
const SYSTEM_CON: &str = "u:object_r:system_file:s0";
|
||||
const _ADB_CON: &str = "u:object_r:adb_data_file:s0";
|
||||
const SELINUX_XATTR: &str = "security.selinux";
|
||||
|
||||
pub fn setcon(path: &str, con: &str) -> Result<()> {
|
||||
setxattr(path, "security.selinux", con, XattrFlags::empty())
|
||||
.with_context(|| format!("Failed to change SELinux context for {path}"))?;
|
||||
pub fn setcon<P: AsRef<Path>>(path: P, con: &str) -> Result<()> {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
setxattr(&path, SELINUX_XATTR, con, XattrFlags::empty()).with_context(|| {
|
||||
format!(
|
||||
"Failed to change SELinux context for {}",
|
||||
path.as_ref().display()
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setsyscon(path: &str) -> Result<()> {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn setsyscon<P: AsRef<Path>>(path: P) -> Result<()> {
|
||||
setcon(path, SYSTEM_CON)
|
||||
}
|
||||
|
||||
pub fn restore_syscon(dir: &str) -> Result<()> {
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn setsyscon<P: AsRef<Path>>(path: P) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn restore_syscon<P: AsRef<Path>>(dir: P) -> Result<()> {
|
||||
for dir_entry in WalkDir::new(dir).parallelism(Serial) {
|
||||
if let Some(path) = dir_entry.ok().map(|dir_entry| dir_entry.path()) {
|
||||
setxattr(&path, "security.selinux", SYSTEM_CON, XattrFlags::empty()).with_context(
|
||||
|| {
|
||||
format!(
|
||||
"Failed to change SELinux context for {}",
|
||||
path.to_str().unwrap()
|
||||
)
|
||||
},
|
||||
)?;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
setxattr(&path, SELINUX_XATTR, SYSTEM_CON, XattrFlags::empty()).with_context(|| {
|
||||
format!("Failed to change SELinux context for {}", path.display())
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Result};
|
||||
use derive_new::new;
|
||||
use nom::{
|
||||
branch::alt,
|
||||
@@ -11,7 +11,7 @@ use nom::{
|
||||
sequence::Tuple,
|
||||
IResult, Parser,
|
||||
};
|
||||
use std::{vec, path::Path};
|
||||
use std::{path::Path, vec};
|
||||
|
||||
type SeObject<'a> = Vec<&'a str>;
|
||||
|
||||
@@ -345,15 +345,20 @@ impl<'a> PolicyStatement<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_sepolicy<'a, 'b>(input: &'b str) -> Result<Vec<PolicyStatement<'a>>>
|
||||
fn parse_sepolicy<'a, 'b>(input: &'b str, strict: bool) -> Result<Vec<PolicyStatement<'a>>>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
let mut statements = vec![];
|
||||
|
||||
for line in input.split(['\n', ';']) {
|
||||
if line.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Ok((_, statement)) = PolicyStatement::parse(line.trim()) {
|
||||
statements.push(statement);
|
||||
} else if strict {
|
||||
bail!("Failed to parse policy statement: {}", line)
|
||||
}
|
||||
}
|
||||
Ok(statements)
|
||||
@@ -401,6 +406,7 @@ impl TryFrom<&str> for PolicyObject {
|
||||
/// normal statement would be expand to atomic statement, for example:
|
||||
/// allow domain1 domain2:file1 { read write }; would be expand to two atomic statement
|
||||
/// allow domain1 domain2:file1 read;allow domain1 domain2:file1 write;
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[derive(Debug, new)]
|
||||
struct AtomicStatement {
|
||||
cmd: u32,
|
||||
@@ -552,8 +558,7 @@ impl<'a> TryFrom<&'a TypeAttr<'a>> for Vec<AtomicStatement> {
|
||||
impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> {
|
||||
type Error = anyhow::Error;
|
||||
fn try_from(perm: &'a Attr<'a>) -> Result<Self> {
|
||||
let mut result = vec![];
|
||||
result.push(AtomicStatement {
|
||||
let result = vec![AtomicStatement {
|
||||
cmd: CMD_ATTR,
|
||||
subcmd: 0,
|
||||
sepol1: perm.name.try_into()?,
|
||||
@@ -563,7 +568,7 @@ impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> {
|
||||
sepol5: PolicyObject::None,
|
||||
sepol6: PolicyObject::None,
|
||||
sepol7: PolicyObject::None,
|
||||
});
|
||||
}];
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
@@ -670,9 +675,8 @@ struct FfiPolicy {
|
||||
|
||||
fn to_c_ptr(pol: &PolicyObject) -> *const libc::c_char {
|
||||
match pol {
|
||||
PolicyObject::None => std::ptr::null(),
|
||||
PolicyObject::All => std::ptr::null(),
|
||||
PolicyObject::One(s) => s.as_ptr() as *const libc::c_char,
|
||||
PolicyObject::None | PolicyObject::All => std::ptr::null(),
|
||||
PolicyObject::One(s) => s.as_ptr().cast::<libc::c_char>(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,35 +696,45 @@ impl From<AtomicStatement> for FfiPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>) -> Result<()> {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>, strict: bool) -> Result<()> {
|
||||
let policies: Vec<AtomicStatement> = statement.try_into()?;
|
||||
|
||||
for policy in policies {
|
||||
let mut result: u32 = 0;
|
||||
let cpolicy = FfiPolicy::from(policy);
|
||||
unsafe {
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
libc::prctl(
|
||||
crate::ksu::KERNEL_SU_OPTION as i32,
|
||||
crate::ksu::KERNEL_SU_OPTION as i32, // supposed to overflow
|
||||
crate::ksu::CMD_SET_SEPOLICY,
|
||||
0,
|
||||
&cpolicy as *const _ as *const libc::c_void,
|
||||
&mut result as *mut _ as *mut libc::c_void,
|
||||
std::ptr::addr_of!(cpolicy).cast::<libc::c_void>(),
|
||||
std::ptr::addr_of_mut!(result).cast::<libc::c_void>(),
|
||||
);
|
||||
}
|
||||
|
||||
if result != crate::ksu::KERNEL_SU_OPTION {
|
||||
log::warn!("apply rule failed: {result}");
|
||||
log::warn!("apply rule: {:?} failed.", statement);
|
||||
if strict {
|
||||
return Err(anyhow::anyhow!("apply rule {:?} failed.", statement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
fn apply_one_rule<'a>(_statement: &'a PolicyStatement<'a>, _strict: bool) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn live_patch(policy: &str) -> Result<()> {
|
||||
let result = parse_sepolicy(policy.trim())?;
|
||||
let result = parse_sepolicy(policy.trim(), false)?;
|
||||
for statement in result {
|
||||
println!("{statement:?}");
|
||||
apply_one_rule(&statement)?;
|
||||
apply_one_rule(&statement, false)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -729,3 +743,17 @@ pub fn apply_file<P: AsRef<Path>>(path: P) -> Result<()> {
|
||||
let input = std::fs::read_to_string(path)?;
|
||||
live_patch(&input)
|
||||
}
|
||||
|
||||
pub fn check_rule(policy: &str) -> Result<()> {
|
||||
let path = Path::new(policy);
|
||||
let policy = if path.exists() {
|
||||
std::fs::read_to_string(path)?
|
||||
} else {
|
||||
policy.to_string()
|
||||
};
|
||||
let result = parse_sepolicy(policy.trim(), true)?;
|
||||
for statement in result {
|
||||
apply_one_rule(&statement, true)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,49 +1,92 @@
|
||||
use std::path::Path;
|
||||
use anyhow::{bail, Context, Error, Ok, Result};
|
||||
use std::{
|
||||
fs::{create_dir_all, write, File},
|
||||
io::ErrorKind::AlreadyExists,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use retry::delay::NoDelay;
|
||||
use sys_mount::{unmount, FilesystemType, Mount, UnmountFlags};
|
||||
|
||||
fn do_mount_image(src: &str, target: &str) -> Result<()> {
|
||||
Mount::builder()
|
||||
.fstype(FilesystemType::from("ext4"))
|
||||
.mount(src, target)
|
||||
.with_context(|| format!("Failed to do mount: {src} -> {target}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mount_image(src: &str, target: &str) -> Result<()> {
|
||||
// umount target first.
|
||||
let _ = umount_dir(target);
|
||||
let result = retry::retry(NoDelay.take(3), || do_mount_image(src, target));
|
||||
ensure!(result.is_ok(), "Failed to mount {} -> {}", src, target);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn umount_dir(src: &str) -> Result<()> {
|
||||
unmount(src, UnmountFlags::empty()).with_context(|| format!("Failed to umount {src}"))?;
|
||||
Ok(())
|
||||
}
|
||||
#[allow(unused_imports)]
|
||||
use std::fs::{set_permissions, Permissions};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
|
||||
pub fn ensure_clean_dir(dir: &str) -> Result<()> {
|
||||
let path = Path::new(dir);
|
||||
log::debug!("ensure_clean_dir: {}", path.display());
|
||||
if path.exists() {
|
||||
log::debug!("ensure_clean_dir: {} exists, remove it", path.display());
|
||||
std::fs::remove_dir_all(path)?;
|
||||
}
|
||||
Ok(std::fs::create_dir_all(path)?)
|
||||
}
|
||||
|
||||
pub fn ensure_file_exists<T: AsRef<Path>>(file: T) -> Result<()> {
|
||||
match File::options().write(true).create_new(true).open(&file) {
|
||||
std::result::Result::Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
if err.kind() == AlreadyExists && file.as_ref().is_file() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::from(err))
|
||||
.with_context(|| format!("{} is not a regular file", file.as_ref().display()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_dir_exists<T: AsRef<Path>>(dir: T) -> Result<()> {
|
||||
let result = create_dir_all(&dir).map_err(Error::from);
|
||||
if dir.as_ref().is_dir() {
|
||||
result
|
||||
} else if result.is_ok() {
|
||||
bail!("{} is not a regular directory", dir.as_ref().display())
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_binary<T: AsRef<Path>>(path: T, contents: &[u8]) -> Result<()> {
|
||||
if path.as_ref().exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
ensure_dir_exists(path.as_ref().parent().ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"{} does not have parent directory",
|
||||
path.as_ref().to_string_lossy()
|
||||
)
|
||||
})?)?;
|
||||
|
||||
write(&path, contents)?;
|
||||
#[cfg(unix)]
|
||||
set_permissions(&path, Permissions::from_mode(0o755))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn getprop(prop: &str) -> Option<String> {
|
||||
android_properties::getprop(prop).value()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn getprop(_prop: &str) -> Option<String> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn is_safe_mode() -> bool {
|
||||
getprop("persist.sys.safemode")
|
||||
let safemode = getprop("persist.sys.safemode")
|
||||
.filter(|prop| prop == "1")
|
||||
.is_some()
|
||||
|| getprop("ro.sys.safemode")
|
||||
.filter(|prop| prop == "1")
|
||||
.is_some()
|
||||
.is_some();
|
||||
log::info!("safemode: {}", safemode);
|
||||
if safemode {
|
||||
return true;
|
||||
}
|
||||
let safemode = crate::ksu::check_kernel_safemode();
|
||||
log::info!("kernel_safemode: {}", safemode);
|
||||
safemode
|
||||
}
|
||||
|
||||
pub fn get_zip_uncompressed_size(zip_path: &str) -> Result<u64> {
|
||||
@@ -53,3 +96,32 @@ pub fn get_zip_uncompressed_size(zip_path: &str) -> Result<u64> {
|
||||
.sum();
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn switch_mnt_ns(pid: i32) -> Result<()> {
|
||||
use anyhow::ensure;
|
||||
use std::os::fd::AsRawFd;
|
||||
let path = format!("/proc/{}/ns/mnt", pid);
|
||||
let fd = std::fs::File::open(path)?;
|
||||
let ret = unsafe { libc::setns(fd.as_raw_fd(), libc::CLONE_NEWNS) };
|
||||
ensure!(ret == 0, "switch mnt ns failed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn unshare_mnt_ns() -> Result<()> {
|
||||
use anyhow::ensure;
|
||||
let ret = unsafe { libc::unshare(libc::CLONE_NEWNS) };
|
||||
ensure!(ret == 0, "unshare mnt ns failed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn umask(mask: u32) {
|
||||
unsafe { libc::umask(mask) };
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn umask(_mask: u32) {
|
||||
unimplemented!("umask is not supported on this platform")
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ function sidebarGuide() {
|
||||
{ text: 'Instalasi', link: '/id_ID/guide/installation' },
|
||||
{ text: 'Bagaimana cara buildnya?', link: '/id_ID/guide/how-to-build' },
|
||||
{ text: 'Integrasi untuk perangkat non-GKI', link: '/id_ID/guide/how-to-integrate-for-non-gki'},
|
||||
{ text: 'Perangkat yang didukung secara tidak resmi', link: '/id_ID/guide/unofficially-support-devices.md' },
|
||||
{ text: 'FAQ', link: '/id_ID/guide/faq' },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ Certainly, yes.
|
||||
|
||||
## Does KernelSU support modules?
|
||||
|
||||
Yes, But it is in early version, may be buggy. Please waiting it to be stable :)
|
||||
Yes, But it is in early version, it may be buggy. Please wait for it to be stable :)
|
||||
|
||||
## Does KernelSU support Xposed?
|
||||
|
||||
Yes, [Dreamland](https://github.com/canyie/Dreamland) and [TaiChi](https::/taichi.cool) partially works now, And we are trying to make other Xposed Framework work.
|
||||
Yes, [Dreamland](https://github.com/canyie/Dreamland) and [TaiChi](https::/taichi.cool) partially works now. For LSPosed, you can follow [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
|
||||
|
||||
## Is KernelSU compatible with Magisk?
|
||||
|
||||
@@ -46,3 +46,19 @@ It is possible, KernelSU is backported to kernel 4.14 now, for older kernel, you
|
||||
## How to integrate KernelSU for old kernel?
|
||||
|
||||
Please refer [guide](how-to-integrate-for-non-gki)
|
||||
|
||||
## Why my Android version is 13, and the kernel shows "android12-5.10"?
|
||||
|
||||
The Kernel version has nothing to do with Android version, if you need to flash kernel, always use the kernel version, Android version is not so important.
|
||||
|
||||
## Is there any --mount-master/global mount namespace in KernelSU?
|
||||
|
||||
There isn't now(maybe in the future), But you can use `nsenter -t 1 -m sh` to enter global mount namespace instead.
|
||||
|
||||
## Does KernelSU support Zygisk?
|
||||
|
||||
KernelSU has no builtin Zygisk support, but you can use [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU) instead.
|
||||
|
||||
## I am GKI1.0, can i use this?
|
||||
|
||||
GKI1 is completely different from GKI2, you must compile kernel by yourself.
|
||||
|
||||
@@ -37,7 +37,7 @@ But if you encounter a boot loop when integrated KernelSU, it is maybe *kprobe i
|
||||
|
||||
:::tip How to check if kprobe is broken?
|
||||
|
||||
comment out `ksu_enable_sucompat()` and `ksu_enable_ksud()` in `KernelSU/kernel/ksu.c`, the the device boot normally, then kprobe may be broken.
|
||||
comment out `ksu_enable_sucompat()` and `ksu_enable_ksud()` in `KernelSU/kernel/ksu.c`, if the device boots normally, then kprobe may be broken.
|
||||
:::
|
||||
|
||||
## Manully modify the kernel source
|
||||
@@ -142,4 +142,85 @@ You should found the four functions in kernel source:
|
||||
3. vfs_read, usually in `fs/read_write.c`
|
||||
4. vfs_statx, usually in `fs/stat.c`
|
||||
|
||||
If your kernel does not have the `vfs_statx`, use `vfs_fstatat` instead:
|
||||
|
||||
```diff
|
||||
diff --git a/fs/stat.c b/fs/stat.c
|
||||
index 068fdbcc9e26..5348b7bb9db2 100644
|
||||
--- a/fs/stat.c
|
||||
+++ b/fs/stat.c
|
||||
@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_fstat);
|
||||
|
||||
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||
+
|
||||
int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
||||
int flag)
|
||||
{
|
||||
@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
||||
int error = -EINVAL;
|
||||
unsigned int lookup_flags = 0;
|
||||
|
||||
+ ksu_handle_stat(&dfd, &filename, &flag);
|
||||
+
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
|
||||
AT_EMPTY_PATH)) != 0)
|
||||
goto out;
|
||||
```
|
||||
|
||||
For kernels eariler than 4.17, if you cannot find `do_faccessat`, just go to the definition of the `faccessat` syscall and place the call there:
|
||||
|
||||
```diff
|
||||
diff --git a/fs/open.c b/fs/open.c
|
||||
index 2ff887661237..e758d7db7663 100644
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
|
||||
return error;
|
||||
}
|
||||
|
||||
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
+ int *flags);
|
||||
+
|
||||
/*
|
||||
* access() needs to use the real uid/gid, not the effective uid/gid.
|
||||
* We do this by temporarily clearing all FS-related capabilities and
|
||||
@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
|
||||
+
|
||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||
return -EINVAL;
|
||||
```
|
||||
|
||||
To enable KernelSU's builtin SafeMode, You should also modify `input_handle_event` in `drivers/input/input.c`:
|
||||
|
||||
:::tip
|
||||
It is strongly recommended to enable this feature, it is very helpful for recusing from bootloop!
|
||||
:::
|
||||
|
||||
```diff
|
||||
diff --git a/drivers/input/input.c b/drivers/input/input.c
|
||||
index 45306f9ef247..815091ebfca4 100755
|
||||
--- a/drivers/input/input.c
|
||||
+++ b/drivers/input/input.c
|
||||
@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
|
||||
return disposition;
|
||||
}
|
||||
|
||||
+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
|
||||
+
|
||||
static void input_handle_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
int disposition = input_get_disposition(dev, type, code, &value);
|
||||
+ ksu_handle_input_handle_event(&type, &code, &value);
|
||||
|
||||
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
||||
add_input_randomness(type, code, value);
|
||||
```
|
||||
|
||||
Finally, build your kernel again, KernelSU should works well.
|
||||
|
||||
@@ -2,31 +2,167 @@
|
||||
|
||||
## Check if your device is supported
|
||||
|
||||
Download the KernelSU manager app from [github releases](https://github.com/tiann/KernelSU/releases) or [github actions](https://github.com/tiann/KernelSU/actions/workflows/build-manager.yml), and then install the app to device and open the app:
|
||||
Download KernelSU manager APP from [GitHub Releases](https://github.com/tiann/KernelSU/releases) or [Coolapk market](https://www.coolapk.com/apk/me.weishu.kernelsu), and install it to your device:
|
||||
|
||||
- If the app shows `Unsupported`, it means **You should compile the kernel yourself**, KernelSU won't and never provide a boot image for you to flash.
|
||||
- If the app shows `Not installed`, then your devices is officially supported by KernelSU.
|
||||
|
||||
## Find proper boot.img
|
||||
:::info
|
||||
For devices showing `Unsupported`, here is an [Unofficially-support-devices](unofficially-support-devices.md), you can compile the kernel yourself.
|
||||
:::
|
||||
|
||||
KernelSU provides a general boot.img for GKI devices, you should flash the boot.img to the boot partition of your device.
|
||||
## Backup stock boot.img
|
||||
|
||||
You can download the boot.img from [github actions for kernel](https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml), Please be aware that your should use the right version of boot.img. For example, if your device show that the kernel is `5.10.101`, then you need to download the `5.10.101-xxxx.boot.xxx`.
|
||||
Before flashing, you must first backup your stock boot.img. If you encounter any bootloop, you can always restore the system by flashing back to the stock factory boot using fastboot.
|
||||
|
||||
And also, please check your stock boot.img's format, you should use the right format, such as `lz4`、`gz`.
|
||||
::: warning
|
||||
Flashing may cause data loss, be sure to do this step well before proceeding to the next step!! You can also back up all the data on your phone if necessary.
|
||||
:::
|
||||
|
||||
## Flash the boot.img to device
|
||||
## Necessary knowledge
|
||||
|
||||
Connect your device with `adb` and then execute `adb reboot bootloader` to enter fastboot mode, and then use this command to flash KernelSU:
|
||||
### ADB and fastboot
|
||||
|
||||
By default, you will use ADB and fastboot tools in this tutorial, so if you don't know them, we recommend using a search engine to learn about them first.
|
||||
|
||||
### KMI
|
||||
|
||||
Kernel Module Interface (KMI), kernel versions with the same KMI are **compatible** This is what "genernal" means in GKI; conversely, if the KMI is different, then these kernels are not compatible with each other, and brushing in a kernel image with a different KMI than your device may cause a bootloop.
|
||||
|
||||
Specifically, for GKI devices, the kernel version format should be as follows:
|
||||
|
||||
```txt
|
||||
KernelRelease :=
|
||||
Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
|
||||
w .x .y -zzz -k -something
|
||||
```
|
||||
|
||||
`w.x-zzz-k` is the KMI version. For example, if a device kernel version is `5.10.101-android12-9-g30979850fc20`, then its KMI is `5.10-android12-9`; theoretically, it can boot up normally with other KMI kernels.
|
||||
|
||||
::: tip
|
||||
Note that the SubLevel in the kernel version is not part of the KMI! That means that `5.10.101-android12-9-g30979850fc20` has the same KMI as `5.10.137-android12-9-g30979850fc20`!
|
||||
:::
|
||||
|
||||
### Kernel version vs. Android version
|
||||
|
||||
Please note: **Kernel version and Android version are not necessarily the same!**
|
||||
|
||||
If you find that your kernel version is `android12-5.10.101`, but your Android system version is Android 13 or other; please don't be surprised, because the version number of the Android system is not necessarily the same as the version number of the Linux kernel; The version number of the Linux kernel is generally consistent with the version of the Android system that comes with the **device when it is shipped**. If the Android system is upgraded later, the kernel version will generally not change. If you need to flash, **please always refer to the kernel version!!**
|
||||
|
||||
## Introduction
|
||||
|
||||
There are several installation methods for KernelSU, each suitable for a different scenario, so please choose as needed.
|
||||
|
||||
1. Install with custom Recovery (e.g. TWRP)
|
||||
2. Install with a kernel flash app, such as Franco Kernel Manager
|
||||
3. Install with fastboot using the boot.img provided by KernelSU
|
||||
4. Repair the boot.img manually and install it
|
||||
|
||||
## Install with custom Recovery
|
||||
|
||||
Prerequisite: Your device must have a custom Recovery, such as TWRP; if not or only official Recovery is available, use another method.
|
||||
|
||||
Step:
|
||||
|
||||
1. From the [Release page](https://github.com/tiann/KernelSU/releases) of KernelSU, download the zip package starting with AnyKernel3 that matches your phone version; for example, the phone kernel version is `android12-5.10. 66`, then you should download the file `AnyKernel3-android12-5.10.66_yyyy-MM.zip` (where `yyyy` is the year and `MM` is the month).
|
||||
2. Reboot the phone into TWRP.
|
||||
3. Use adb to put AnyKernel3-*.zip into the phone /sdcard and choose to install it in the TWRP GUI; or you can directly `adb sideload AnyKernel-*.zip` to install.
|
||||
|
||||
PS. This method is suitable for any installation (not limited to initial installation or subsequent upgrades), as long as you use TWRP.
|
||||
|
||||
## Install with Kernel Flasher
|
||||
|
||||
Prerequisite: Your device must be rooted. For example, you have installed Magisk to get root, or you have installed an old version of KernelSU and need to upgrade to another version of KernelSU; if your device is not rooted, please try other methods.
|
||||
|
||||
Step:
|
||||
|
||||
1. Download the AnyKernel3 zip; refer to the section *Installing with Custom Recovery* for downloading instructions.
|
||||
2. Open the Kernel Flash App and use the provided AnyKernel3 zip to flash.
|
||||
|
||||
If you haven't used the Kernel flash App before, the following are the more popular ones.
|
||||
|
||||
1. [Franco Kernel Manager](https://play.google.com/store/apps/details?id=com.franco.kernel)
|
||||
2. [Ex Kernel Manager](https://play.google.com/store/apps/details?id=flar2.exkernelmanager)
|
||||
|
||||
PS. This method is more convenient when upgrading KernelSU and can be done without a computer (backup first!). .
|
||||
|
||||
## Install with boot.img provided by KernelSU
|
||||
|
||||
This method does not require you to have TWRP, nor does it require your phone to have root privileges; it is suitable for your first installation of KernelSU.
|
||||
|
||||
### Find proper boot.img
|
||||
|
||||
KernelSU provides a generic boot.img for GKI devices and you should flush the boot.img to the boot partition of the device.
|
||||
|
||||
You can download boot.img from [GitHub Release](https://github.com/tiann/KernelSU/releases), please note that you should use the correct version of boot.img. For example, if your device displays the kernel `android12-5.10.101` , you need to download `android-5.10.101_yyyy-MM.boot-<format>.img`. , you need to download `android-5.10.101_yyyy-MM.boot-<format>.img`.(Keep KMI consistent!)
|
||||
|
||||
Where `<format>` refers to the kernel compression format of your official boot.img, please check the kernel compression format of your original boot.img, you should use the correct format, e.g. `lz4`, `gz`; if you use an incorrect compression format, you may encounter bootloop.
|
||||
|
||||
::: info
|
||||
1. You can use magiskboot to get the compression format of your original boot; of course you can also ask other, more experienced kids with the same model as your device. Also, the compression format of the kernel usually does not change, so if you boot successfully with a certain compression format, you can try that format later.
|
||||
2. Xiaomi devices usually use `gz` or **uncompressed**.
|
||||
3. For Pixel devices, follow below instructions.
|
||||
:::
|
||||
|
||||
### flash boot.img to device
|
||||
|
||||
Use `adb` to connect your device, then execute `adb reboot bootloader` to enter fastboot mode, then use this command to flash KernelSU:
|
||||
|
||||
```sh
|
||||
fastboot flash boot boot.img
|
||||
```
|
||||
|
||||
## Reboot
|
||||
::: info
|
||||
If your device supports `fastboot boot`, you can first use `fastboot boot boot.img` to try to use boot.img to boot the system first. If something unexpected happens, restart it again to boot.
|
||||
:::
|
||||
|
||||
When flashed, you should reboot your device:
|
||||
### reboot
|
||||
|
||||
After flashing is complete, you should reboot your device:
|
||||
|
||||
```sh
|
||||
fastboot reboot
|
||||
```
|
||||
|
||||
## Patch boot.img manully
|
||||
|
||||
For some devices, the boot.img format is not so common, such as not `lz4`, `gz` and uncompressed; the most typical is Pixel, its boot.img format is `lz4_legacy` compressed, ramdisk may be `gz` may also be `lz4_legacy` compression; at this time, if you directly flash the boot.img provided by KernelSU, the phone may not be able to boot; at this time, you can manually patch the boot.img to achieve.
|
||||
|
||||
There are generally two patch methods:
|
||||
|
||||
1. [Android-Image-Kitchen](https://forum.xda-developers.com/t/tool-android-image-kitchen-unpack-repack-kernel-ramdisk-win-android-linux-mac.2073775/)
|
||||
2. [magiskboot](https://github.com/topjohnwu/Magisk/releases)
|
||||
|
||||
Among them, Android-Image-Kitchen is suitable for operation on PC, and magiskboot needs the cooperation of mobile phone.
|
||||
|
||||
### Preparation
|
||||
|
||||
1. Get your phone's stock boot.img; you can get it from your device manufacturers, you may need [payload-dumper-go](https://github.com/ssut/payload-dumper-go)
|
||||
2. Download the AnyKernel3 zip file provided by KernelSU that matches the KMI version of your device (you can refer to the *Install with custom Recovery*).
|
||||
3. Unpack the AnyKernel3 package and get the `Image` file, which is the kernel file of KernelSU.
|
||||
|
||||
### Using Android-Image-Kitchen
|
||||
|
||||
1. Download Android-Image-Kitchen to your computer.
|
||||
2. Put stock boot.img to Android-Image-Kitchen's root folder.
|
||||
3. Execute `./unpackimg.sh boot.img` at root directory of Android-Image-Kitchen, this command would unpack boot.img and you will get some files.
|
||||
4. Replace `boot.img-kernel` in the `split_img` directory with the `Image` you extracted from AnyKernel3 (note the name change to boot.img-kernel).
|
||||
5. Execute `./repackimg.sh` at root directory of 在 Android-Image-Kitchen; And you will get a file named `image-new.img`; Flash this boot.img by fastboot(Refer to the previous section).
|
||||
|
||||
### Using magiskboot
|
||||
|
||||
1. Download latest Magisk from [Release Page](https://github.com/topjohnwu/Magisk/releases)
|
||||
2. Rename Magisk-*.apk to Magisk-vesion.zip and unzip it.
|
||||
3. Push `Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so` to your device by adb: `adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/adb/tmp/magiskboot`
|
||||
4. Push stock boot.img and Image in AnyKernel3 to your device.
|
||||
5. Enter adb shell and cd `/data/local/tmp/` directory, then `chmod +x magiskboot`
|
||||
6. Enter adb shell and cd `/data/local/tmp/` directory, execute `./magiskboot unpack boot.img` to unpack `boot.img`, you will get a `kernel` file, this is your stock kernel.
|
||||
7. Replace `kernel` with `Image`: `mv -f Image kernel`
|
||||
8. Execute `./magiskboot repack boot.img` to repack boot img, and you will get a `new-boot.img` file, flash this file to device by fastboot.
|
||||
|
||||
## Other methods
|
||||
|
||||
In fact, all these installation methods have only one main idea, which is to **replace the original kernel for the one provided by KernelSU**; as long as this can be achieved, it can be installed; for example, the following are other possible methods.
|
||||
|
||||
1. First install Magisk, get root privileges through Magisk and then use the kernel flasher to flash in the AnyKernel zip from KernelSU.
|
||||
2. Use some flashing toolkit on PCs to flash in the kernel provided KernelSU.
|
||||
|
||||
@@ -137,4 +137,85 @@ Anda harus menemukan empat fungsi dalam kernel source:
|
||||
3. vfs_read, usually in `fs/read_write.c`
|
||||
4. vfs_statx, usually in `fs/stat.c`
|
||||
|
||||
Jika kernel anda tidak memiliki `vfs_statx`, maka gunakan `vfs_fstatat` alih-alih:
|
||||
|
||||
```diff
|
||||
diff --git a/fs/stat.c b/fs/stat.c
|
||||
index 068fdbcc9e26..5348b7bb9db2 100644
|
||||
--- a/fs/stat.c
|
||||
+++ b/fs/stat.c
|
||||
@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_fstat);
|
||||
|
||||
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||
+
|
||||
int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
||||
int flag)
|
||||
{
|
||||
@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
||||
int error = -EINVAL;
|
||||
unsigned int lookup_flags = 0;
|
||||
|
||||
+ ksu_handle_stat(&dfd, &filename, &flag);
|
||||
+
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
|
||||
AT_EMPTY_PATH)) != 0)
|
||||
goto out;
|
||||
```
|
||||
|
||||
Untuk kernel lebih awal dari 4.17, jika anda menemukan `do_faccessat`, hanya pergi ke definisi yang sama `faccessat` syscall dan tempatkan pemanggil di sini:
|
||||
|
||||
```diff
|
||||
diff --git a/fs/open.c b/fs/open.c
|
||||
index 2ff887661237..e758d7db7663 100644
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
|
||||
return error;
|
||||
}
|
||||
|
||||
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
+ int *flags);
|
||||
+
|
||||
/*
|
||||
* access() needs to use the real uid/gid, not the effective uid/gid.
|
||||
* We do this by temporarily clearing all FS-related capabilities and
|
||||
@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
|
||||
+
|
||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||
return -EINVAL;
|
||||
```
|
||||
|
||||
To enable KernelSU's builtin SafeMode, You should also modify `input_handle_event` in `drivers/input/input.c`:
|
||||
|
||||
:::tip
|
||||
Fitur ini sangat direkomendasikan, serta sangat membantu untuk memulihkan pada saat bootloop!
|
||||
:::
|
||||
|
||||
```diff
|
||||
diff --git a/drivers/input/input.c b/drivers/input/input.c
|
||||
index 45306f9ef247..815091ebfca4 100755
|
||||
--- a/drivers/input/input.c
|
||||
+++ b/drivers/input/input.c
|
||||
@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
|
||||
return disposition;
|
||||
}
|
||||
|
||||
+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
|
||||
+
|
||||
static void input_handle_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
int disposition = input_get_disposition(dev, type, code, &value);
|
||||
+ ksu_handle_input_handle_event(&type, &code, &value);
|
||||
|
||||
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
||||
add_input_randomness(type, code, value);
|
||||
```
|
||||
|
||||
Terakhir, edit `KernelSU/kernel/ksu.c` dan beri komentar pada `enable_sucompat()` lalu build kernel Anda lagi, KernelSU akan bekerja dengan baik.
|
||||
|
||||
30
website/docs/id_ID/guide/unofficially-support-devices.md
Normal file
30
website/docs/id_ID/guide/unofficially-support-devices.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Perangkat Yang Didukung Tidak Resmi
|
||||
|
||||
:::peringatan
|
||||
di halaman ini, terdapat kernel untuk perangkat non-GKI yang mendukung KernelSU yang dikelola oleh pengembang lain.
|
||||
:::
|
||||
|
||||
:::peringatan
|
||||
Halaman ini hanya untuk Anda yang ingin menemukan kode sumber yang sesuai dengan perangkat Anda, itu **BUKAN** berarti kode sumber telah ditinjau oleh _KernelSU Developers_. Anda harus menggunakannya dengan risiko Anda sendiri.
|
||||
:::
|
||||
|
||||
<script setup>
|
||||
import data from '../../repos.json'
|
||||
</script>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pengelola</th>
|
||||
<th>Repository</th>
|
||||
<th>Perangkat yang didukung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="repo in data" :key="repo.devices">
|
||||
<td><a :href="repo.maintainer_link" target="_blank" rel="noreferrer">{{ repo.maintainer }}</a></td>
|
||||
<td><a :href="repo.kernel_link" target="_blank" rel="noreferrer">{{ repo.kernel_name }}</a></td>
|
||||
<td>{{ repo.devices }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -19,7 +19,7 @@ hero:
|
||||
|
||||
features:
|
||||
- title: Kernel-based
|
||||
details: KernslSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace.
|
||||
details: KernelSU bekerja dalam mode Linux kernel, dan mempunyai kelebihan diatas aplikasi userspace.
|
||||
- title: Kontrol akses daftar putih
|
||||
details: Hanya aplikasi yang diberikan izin root yang bisa mengakses `su`, aplikasi lain tidak bisa mengakses su.
|
||||
- title: Dukungan modul
|
||||
|
||||
@@ -19,7 +19,7 @@ hero:
|
||||
|
||||
features:
|
||||
- title: Kernel-based
|
||||
details: KernslSU is working in Linux kernel mode, it has more control over userspace applications.
|
||||
details: KernelSU is working in Linux kernel mode, it has more control over userspace applications.
|
||||
- title: Whitelist access control
|
||||
details: Only App that is granted root permission can access `su`, other apps cannot perceive su.
|
||||
- title: Module support
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
"kernel_link": "https://github.com/hmtheboy154/Darkmatter-kernel",
|
||||
"devices": "Generic x86_64 devices running Android-x86"
|
||||
},
|
||||
{
|
||||
"maintainer": "Vincent4440",
|
||||
"maintainer_link": "https://github.com/Vincent4440",
|
||||
"kernel_name": "noxious_kernel_xiaomi_sm8250",
|
||||
"kernel_link": "https://github.com/Vincent4440/noxious_kernel_xiaomi_sm8250/tree/thirteen",
|
||||
"devices": "Poco F4: munch"
|
||||
},
|
||||
{
|
||||
"maintainer": "Asuka-mio",
|
||||
"maintainer_link": "https://github.com/asuka-mio",
|
||||
@@ -75,5 +82,89 @@
|
||||
"kernel_name": "android_kernel_xiaomi_sdm845",
|
||||
"kernel_link": "https://github.com/OnlyTomInSecond/android_kernel_xiaomi_sdm845",
|
||||
"devices": "Mi 8 (dipper) for LineageOS"
|
||||
},
|
||||
{
|
||||
"maintainer": "WeeAris",
|
||||
"maintainer_link": "https://github.com/WeeAris",
|
||||
"kernel_name": "Realking_su_xiaomi_sm8250",
|
||||
"kernel_link": "https://github.com/WeeAris/Realking_su_xiaomi_sm8250",
|
||||
"devices": "Apollo(Redmi K30S Ultra/Mi 10T/Mi 10T Pro)\uff0cAlioth(Redmi K40/POCO F3/Mi 11X)\uff0cMunch(Redmi K40S/POCO F4), repo for AOSP only."
|
||||
},
|
||||
{
|
||||
"maintainer": "lateautumn233",
|
||||
"maintainer_link": "https://github.com/lateautumn233",
|
||||
"kernel_name": "android_kernel_oneplus_sm8250",
|
||||
"kernel_link": "https://github.com/lateautumn233/android_kernel_oneplus_sm8250",
|
||||
"devices": "OnePlus 8 Serials"
|
||||
},
|
||||
{
|
||||
"maintainer": "Molyuu",
|
||||
"maintainer_link": "https://github.com/Molyuu",
|
||||
"kernel_name": "neko_kernel_xiaomi_gauguin",
|
||||
"kernel_link": "https://github.com/Molyuu/neko_kernel_xiaomi_gauguin",
|
||||
"devices": "Redmi Note 9 Pro/ Mi 10T Lite/ Mi 10i "
|
||||
},
|
||||
{
|
||||
"maintainer": "guh0613",
|
||||
"maintainer_link": "https://github.com/guh0613",
|
||||
"kernel_name": "android_kernel_oppo_sm8150",
|
||||
"kernel_link": "https://github.com/guh0613/android_kernel_oppo_sm8150",
|
||||
"devices": "OPPO Reno Ace (OP4A89)"
|
||||
},
|
||||
{
|
||||
"maintainer": "LeviMarvin",
|
||||
"maintainer_link": "https://github.com/LeviMarvin",
|
||||
"kernel_name": "kernel_xiaomi_alioth",
|
||||
"kernel_link": "https://github.com/LeviMarvin/kernel_xiaomi_alioth",
|
||||
"devices": "Redmi K40 / POCO F3"
|
||||
},
|
||||
{
|
||||
"maintainer": "cibimo",
|
||||
"maintainer_link": "https://github.com/cibimo",
|
||||
"kernel_name": "kernel_xiaomi_raphael_ksu",
|
||||
"kernel_link": "https://github.com/cibimo/kernel_xiaomi_raphael_ksu",
|
||||
"devices": "Xiaomi Redmi K20 Pro / Mi 9T Pro (raphael)"
|
||||
},
|
||||
{
|
||||
"maintainer": "EndCredits",
|
||||
"maintainer_link": "https://github.com/EndCredits",
|
||||
"kernel_name": "kernel_xiaomi_sm7250",
|
||||
"kernel_link": "https://github.com/EndCredits/kernel_xiaomi_sm7250",
|
||||
"devices": "Redmi K30 5G ( picasso ) , Redmi K30i 5G ( picasso_48m)"
|
||||
},
|
||||
{
|
||||
"maintainer": "msnx",
|
||||
"maintainer_link": "https://github.com/msnx",
|
||||
"kernel_name": "android-msm-coral-4.14-android13",
|
||||
"kernel_link": "https://github.com/msnx/KernelSU-Pixel4XL",
|
||||
"devices": "Pixel 4 XL"
|
||||
},
|
||||
{
|
||||
"maintainer": "SoDebug",
|
||||
"maintainer_link": "https://github.com/SoDebug",
|
||||
"kernel_name": "kernel_xiaomi_raphael",
|
||||
"kernel_link": "https://github.com/SoDebug/kernel_xiaomi_raphael",
|
||||
"devices": "Xiaomi Redmi K20 Pro / Mi 9T Pro (raphael)"
|
||||
},
|
||||
{
|
||||
"maintainer": "rxuglr",
|
||||
"maintainer_link": "https://github.com/rxuglr",
|
||||
"kernel_name": "android_kernel_xiaomi_surya",
|
||||
"kernel_link": "https://github.com/rxuglr/android_kernel_xiaomi_surya",
|
||||
"devices": "Xiaomi POCO X3 / NFC (surya)"
|
||||
},
|
||||
{
|
||||
"maintainer": "H1mJT",
|
||||
"maintainer_link": "https://github.com/H1mJT",
|
||||
"kernel_name": "kernel_realme_RMX1901",
|
||||
"kernel_link": "https://github.com/H1mJT/kernel_realme_RMX1901",
|
||||
"devices": "Realme X (RMX1901/RMX1901CN)"
|
||||
},
|
||||
{
|
||||
"maintainer": "SonalSingh18",
|
||||
"maintainer_link": "https://github.com/SonalSingh18",
|
||||
"kernel_name": "android_kernel_xiaomi_sm6250.git",
|
||||
"kernel_link": "https://github.com/SonalSingh18/android_kernel_xiaomi_sm6250.git",
|
||||
"devices": "Miatoll [curtana, excalibur, gram, joyeuse]"
|
||||
}
|
||||
]
|
||||
@@ -16,7 +16,7 @@ Có, nhưng ở những phiên bản thử nghiệm này có thể có rất nhi
|
||||
|
||||
## KernelSU có hỗ trợ Xposed không?
|
||||
|
||||
Có, [Dreamland](https://github.com/canyie/Dreamland) và [TaiChi](https::/taichi.cool) hiện đã hoạt động được một phần nào đó và chúng tôi đang cố gắng làm cho các Xposed Framework khác có thể hoạt động.
|
||||
Có, [Dreamland](https://github.com/canyie/Dreamland) và [TaiChi](https::/taichi.cool) hiện đã hoạt động được một phần nào đó. Với Lsposed, bạn có thể thử [Zygisk trên KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
|
||||
|
||||
## KernelSU có tương thích với Magisk không?
|
||||
|
||||
@@ -45,4 +45,20 @@ Có thể, KernelSU hiện đã được backport xuống kernel 4.14, đối v
|
||||
|
||||
## Làm cách nào để tích hợp KernelSU cho kernel cũ?
|
||||
|
||||
Vui lòng tham khảo [hướng dẫn này](how-to-integrate-for-non-gki)
|
||||
Vui lòng tham khảo [hướng dẫn này](how-to-integrate-for-non-gki)
|
||||
|
||||
## Tại sao tôi đang chạy Android 13 nhưng kernel lại ghi "android12-5.10" ?
|
||||
|
||||
Phiên bản kernel hoàn toàn không liên quan gì đến phiên bản Android, nếu bạn muốn flash kernel thì hãy luôn để ý đến **phiên bản kernel**, phiên bản Android ở phần đầu (VD : android12-\*) thường không quan trọng lắm.
|
||||
|
||||
## Đã có mount namespace --mount-master/global trên KernelSU chưa ?
|
||||
|
||||
Hiện tại chưa có (hoặc có thể sẽ có trong tương lại), nhưng bạn có thể dùng `nsenter -t 1 -m sh` để vào global mount namespace.
|
||||
|
||||
## KernelSU có hỗ trợ Zygisk không ?
|
||||
|
||||
KernelSU không có Zygisk bên trong, nhưng bạn có thể dùng [Zygisk trên KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
|
||||
|
||||
## Tôi đang ở GKI 1.0, tôi dùng được cái này chứ ?
|
||||
|
||||
GKI1 khác hoàn toàn với GKI2 nên bạn sẽ phải tự compile kernel cho mình.
|
||||
|
||||
@@ -46,3 +46,19 @@ KernelSU 的模块系统与 Magisk 的 magic mount 有冲突,如果 KernelSU
|
||||
## 如何为旧内核集成 KernelSU?
|
||||
|
||||
参考[教程](how-to-integrate-for-non-gki)
|
||||
|
||||
## 为什么我手机系统是 Android 13,但内核版本却是 "android12-5.10"?
|
||||
|
||||
内核版本与 Android 版本无关,如果你需要刷入 KernelSU,请永远使用**内核版本**而非 Android 版本,如果你为 "android12-5.10" 的设备刷入 Android 13 的内核,等待你的将是 bootloop.
|
||||
|
||||
## KernelSU 支持 --mount-master/全局挂载命名空间吗?
|
||||
|
||||
目前没有(未来可能会支持),现在你可以用 `nsenter -t 1 -m sh` 在 root shell 里面进入全局命名空间。
|
||||
|
||||
## KernelSU 支持 Zygisk 吗?
|
||||
|
||||
KernelSU 本体不支持 Zygisk,但是你可以用 [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU) 来使用 Zygisk 模块。
|
||||
|
||||
## 我是 GKI1.0, 能用 KernelSU 吗?
|
||||
|
||||
GKI1 跟 GKI2 完全是两个东西,所以你需要自行编译内核。
|
||||
|
||||
@@ -142,4 +142,85 @@ index 376543199b5a..82adcef03ecc 100644
|
||||
3. vfs_read,通常位于 `fs/read_write.c`
|
||||
4. vfs_statx,通常位于 `fs/stat.c`
|
||||
|
||||
如果你的内核没有 `vfs_statx`, 使用 `vfs_fstatat` 来代替它:
|
||||
|
||||
```diff
|
||||
diff --git a/fs/stat.c b/fs/stat.c
|
||||
index 068fdbcc9e26..5348b7bb9db2 100644
|
||||
--- a/fs/stat.c
|
||||
+++ b/fs/stat.c
|
||||
@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_fstat);
|
||||
|
||||
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||
+
|
||||
int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
||||
int flag)
|
||||
{
|
||||
@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
|
||||
int error = -EINVAL;
|
||||
unsigned int lookup_flags = 0;
|
||||
|
||||
+ ksu_handle_stat(&dfd, &filename, &flag);
|
||||
+
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
|
||||
AT_EMPTY_PATH)) != 0)
|
||||
goto out;
|
||||
```
|
||||
|
||||
对于早于 4.17 的内核,如果没有 `do_faccessat`,可以直接找到 `faccessat` 系统调用的定义然后修改:
|
||||
|
||||
```diff
|
||||
diff --git a/fs/open.c b/fs/open.c
|
||||
index 2ff887661237..e758d7db7663 100644
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -355,6 +355,9 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
|
||||
return error;
|
||||
}
|
||||
|
||||
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
+ int *flags);
|
||||
+
|
||||
/*
|
||||
* access() needs to use the real uid/gid, not the effective uid/gid.
|
||||
* We do this by temporarily clearing all FS-related capabilities and
|
||||
@@ -370,6 +373,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
|
||||
+
|
||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||
return -EINVAL;
|
||||
```
|
||||
|
||||
要使用 KernelSU 内置的安全模式,你还需要修改 `drivers/input/input.c` 中的 `input_handle_event` 方法:
|
||||
|
||||
:::tip
|
||||
强烈建议开启此功能,对用户救砖会非常有帮助!
|
||||
:::
|
||||
|
||||
```diff
|
||||
diff --git a/drivers/input/input.c b/drivers/input/input.c
|
||||
index 45306f9ef247..815091ebfca4 100755
|
||||
--- a/drivers/input/input.c
|
||||
+++ b/drivers/input/input.c
|
||||
@@ -367,10 +367,13 @@ static int input_get_disposition(struct input_dev *dev,
|
||||
return disposition;
|
||||
}
|
||||
|
||||
+extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
|
||||
+
|
||||
static void input_handle_event(struct input_dev *dev,
|
||||
unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
int disposition = input_get_disposition(dev, type, code, &value);
|
||||
+ ksu_handle_input_handle_event(&type, &code, &value);
|
||||
|
||||
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
||||
add_input_randomness(type, code, value);
|
||||
```
|
||||
|
||||
改完之后重新编译内核即可。
|
||||
|
||||
@@ -2,20 +2,109 @@
|
||||
|
||||
## 检查您的设备是否被支持
|
||||
|
||||
从 [GitHub Releases](https://github.com/tiann/KernelSU/releases) 或 [GitHub Actions](https://github.com/tiann/KernelSU/actions/workflows/build-manager.yml) 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:
|
||||
从 [GitHub Releases](https://github.com/tiann/KernelSU/releases) 或 [酷安](https://www.coolapk.com/apk/me.weishu.kernelsu) 下载 KernelSU 管理器应用,然后将应用程序安装到设备并打开:
|
||||
|
||||
- 如果应用程序显示 “不支持”,则表示您的设备不支持 KernelSU,你需要自己编译设备的内核才能使用,KernelSU 官方不会也永远不会为你提供一个可以刷写的 boot 镜像。
|
||||
- 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备。
|
||||
- 如果应用程序显示 “未安装”,那么 KernelSU 支持您的设备;可以进行下一步操作。
|
||||
|
||||
## 找到合适的 boot.img
|
||||
:::info
|
||||
对于显示“不支持”的设备,这里有一个[非官方支持设备列表](unofficially-support-devices.md),你可以用这个列表里面的内核自行编译。
|
||||
:::
|
||||
|
||||
KernelSU 为 GKI 设备提供了通用的 boot.img,您应该将 boot.img 刷写到设备的引导分区。
|
||||
## 备份你的 boot.img
|
||||
|
||||
您可以从 [GitHub Actions for Kernel](https://github.com/tiann/KernelSU/actions/workflows/build-kernel.yml) 下载 boot.img, 请注意您应该使用正确版本的 boot.img. 例如,如果您的设备显示内核是 `5.10.101`, 需要下载 `5.10.101-xxxx.boot.xxx`.
|
||||
在进行刷机操作之前,你必须先备份好自己的原厂 boot.img。如果你后续刷机出现了任何问题,你都可以通过使用 fastboot 刷回原厂 boot 来恢复系统。
|
||||
|
||||
另外,请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 `lz4`、`gz`。
|
||||
::: warning
|
||||
任何刷机操作都是有风险的,请务必做好这一步再进行下一步操作!!必要时你还可以备份你手机的所有数据。
|
||||
:::
|
||||
|
||||
## 将 boot.img 刷入设备
|
||||
## 必备知识
|
||||
|
||||
### ADB 和 fastboot
|
||||
|
||||
此教程默认你会使用 ADB 和 fastboot 工具,如果你没有了解过,建议使用搜索引擎先学习相关知识。
|
||||
|
||||
### KMI
|
||||
|
||||
KMI 全称 Kernel Module Interface,相同 KMI 的内核版本是**兼容的** 这也是 GKI 中“通用”的含义所在;反之,如果 KMI 不同,那么这些内核之间无法互相兼容,刷入与你设备 KMI 不同的内核镜像可能会导致死机。
|
||||
|
||||
具体来说,对 GKI 的设备,其内核版本格式应该如下:
|
||||
|
||||
```txt
|
||||
KernelRelease :=
|
||||
Version.PatchLevel.SubLevel-AndroidRelease-KmiGeneration-suffix
|
||||
w .x .y -zzz -k -something
|
||||
```
|
||||
|
||||
其中,`w.x-zzz-k` 为 KMI 版本。例如,一个设备内核版本为`5.10.101-android12-9-g30979850fc20`,那么它的 KMI 为 `5.10-android12-9`;理论上刷入其他这个 KMI 的内核也能正常开机。
|
||||
|
||||
::: tip
|
||||
请注意,内核版本中的 SubLevel 不属于 KMI 的范畴!也就是说 `5.10.101-android12-9-g30979850fc20` 与 `5.10.137-android12-9-g30979850fc20` 的 KMI 相同!
|
||||
:::
|
||||
|
||||
### 内核版本与 Android 版本
|
||||
|
||||
请注意:**内核版本与 Android 版本并不一定相同!**
|
||||
|
||||
如果您发现您的内核版本是 `android12-5.10.101`,然而你 Android 系统的版本为 Android 13 或者其他;请不要觉得奇怪,因为 Android 系统的版本与 Linux 内核的版本号不一定是一致的;Linux 内核的版本号一般与**设备出厂的时候自带的 Android 系统的版本一致**,如果后续 Android 系统升级,内核版本一般不会发生变化。如果你需要刷机,**请以内核版本为准!!**
|
||||
|
||||
## 安装介绍
|
||||
|
||||
KernelSU 的安装方法有如下几种,各自适用于不同的场景,请按需选择:
|
||||
|
||||
1. 使用自定义 Recovery(如 TWRP)安装
|
||||
2. 使用内核刷写 App,如 (Franco Kernel Manager)安装
|
||||
3. 使用 KernelSU 提供的 boot.img 使用 fastboot 安装
|
||||
4. 手动修补 boot.img 然后安装
|
||||
|
||||
## 使用自定义 Recovery 安装
|
||||
|
||||
前提:你的设备必须有自定义的 Recovery,如 TWRP;如果没有或者只有官方 Recovery,请使用其他方法。
|
||||
|
||||
步骤:
|
||||
|
||||
1. 在 KernelSU 的 [Release 页面](https://github.com/tiann/KernelSU/releases) 下载与你手机版本匹配的以 AnyKernel3 开头的 zip 刷机包;例如,手机内核版本为 `android12-5.10.66`,那么你应该下载 `AnyKernel3-android12-5.10.66_yyyy-MM.zip` 这个文件(其中 `yyyy` 为年份,`MM` 为月份)。
|
||||
2. 重启手机进入 TWRP。
|
||||
3. 使用 adb 将 AnyKernel3-*.zip 放到手机 /sdcard 然后在 TWRP 图形界面选择安装;或者你也可以直接 `adb sideload AnyKernel-*.zip` 安装。
|
||||
|
||||
PS. 这种方法适用于任何情况下的安装(不限于初次安装或者后续升级),只要你用 TWRP 就可以操作。
|
||||
|
||||
## 使用内核刷写 App 安装
|
||||
|
||||
前提:你的设备必须已经 root。例如你已经安装了 Magisk 获取了 root,或者你已经安装了旧版本的 KernelSU 需要升级到其他版本的 KernelSU;如果你的设备无 root,请尝试其他方法。
|
||||
|
||||
步骤:
|
||||
|
||||
1. 下载 AnyKernel3 的刷机包;下载方法参考 *使用自定义 Recovery 安装*那一节的内容。
|
||||
2. 打开内核刷写 App 使用提供的 AnyKernel3 刷机包刷入。
|
||||
|
||||
如果你之前没有用过内核刷写 App,那么下面几个是比较流行的:
|
||||
|
||||
1. [Franco Kernel Manager](https://play.google.com/store/apps/details?id=com.franco.kernel)
|
||||
2. [Ex Kernel Manager](https://play.google.com/store/apps/details?id=flar2.exkernelmanager)
|
||||
|
||||
PS. 这种方法在升级 KernelSU 的时候较为方便,无需电脑即可完成(注意备份!)。
|
||||
|
||||
## 使用 KernelSU 提供的 boot.img 安装
|
||||
|
||||
这张方法无需你有 TWRP,也不需要你的手机有 root 权限;适用于你初次安装 KernelSU。
|
||||
|
||||
### 找到合适的 boot.img
|
||||
|
||||
KernelSU 为 GKI 设备提供了通用的 boot.img,您应该将 boot.img 刷写到设备的 boot 分区。
|
||||
|
||||
您可以从 [GitHub Release](https://github.com/tiann/KernelSU/releases) 下载 boot.img, 请注意您应该使用正确版本的 boot.img. 例如,如果您的设备显示内核是 `android12-5.10.101`, 需要下载 `android-5.10.101_yyyy-MM.boot-<format>.img`.
|
||||
|
||||
其中 `<format>` 指的是你的官方 boot.img 的内核压缩格式,请检查您原有 boot.img 的内核压缩格式,您应该使用正确的格式,例如 `lz4`、`gz`;如果是用不正确的压缩格式,刷入 boot 后可能无法开机。
|
||||
|
||||
::: info
|
||||
1. 您可以通过 magiskboot 来获取你原来 boot 的压缩格式;当然您也可以询问与您机型相同的其他更有经验的童鞋。另外,内核的压缩格式通常不会发生变化,如果您使用某个压缩格式成功开机,后续可优先尝试这个格式。
|
||||
2. 小米设备通常使用 `gz` 或者 **不压缩**。
|
||||
3. Pixel 设备有些特殊,请查看下面的教程。
|
||||
:::
|
||||
|
||||
### 将 boot.img 刷入设备
|
||||
|
||||
使用 `adb` 连接您的设备,然后执行 `adb reboot bootloader` 进入 fastboot 模式,然后使用此命令刷入 KernelSU:
|
||||
|
||||
@@ -23,10 +112,57 @@ KernelSU 为 GKI 设备提供了通用的 boot.img,您应该将 boot.img 刷
|
||||
fastboot flash boot boot.img
|
||||
```
|
||||
|
||||
## 重启
|
||||
::: info
|
||||
如果你的设备支持 `fastboot boot`,可以先使用 `fastboot boot boot.img` 来先尝试使用 boot.img 引导系统,如果出现意外,再重启一次即可开机。
|
||||
:::
|
||||
|
||||
### 重启
|
||||
|
||||
刷入完成后,您应该重新启动您的设备:
|
||||
|
||||
```sh
|
||||
fastboot reboot
|
||||
```
|
||||
|
||||
## 手动修补 boot.img
|
||||
|
||||
对于某些设备来说,其 boot.img 格式不那么常见,比如不是 `lz4`, `gz` 和未压缩;最典型的就是 Pixel,它 boot.img 的格式是 `lz4_legacy` 压缩,ramdisk 可能是 `gz` 也可能是 `lz4_legacy` 压缩;此时如果你直接刷入 KernelSU 提供的 boot.img,手机可能无法开机;这时候,你可以通过手动修补 boot.img 来实现。
|
||||
|
||||
修补方法总体有两种:
|
||||
|
||||
1. [Android-Image-Kitchen](https://forum.xda-developers.com/t/tool-android-image-kitchen-unpack-repack-kernel-ramdisk-win-android-linux-mac.2073775/)
|
||||
2. [magiskboot](https://github.com/topjohnwu/Magisk/releases)
|
||||
|
||||
其中,Android-Image-Kitchen 适用于 PC 上操作,magiskboot 需要手机配合。
|
||||
|
||||
### 准备
|
||||
|
||||
1. 获取你手机的原厂 boot.img;你可以通过你手机的线刷包解压后之间获取,如果你是卡刷包,那你也许需要[payload-dumper-go](https://github.com/ssut/payload-dumper-go)
|
||||
2. 下载 KernelSU 提供的与你设备 KMI 版本一致的 AnyKernel3 刷机包(可以参考 *自定义 TWRP 刷入一节*)。
|
||||
3. 解压缩 AnyKernel3 刷机包,获取其中的 `Image` 文件,此文件为 KernelSU 的内核文件。
|
||||
|
||||
### 使用 Android-Image-Kitchen
|
||||
|
||||
1. 下载 Android-Image-Kitchen 至你电脑
|
||||
2. 将手机原厂 boot.img 放入 Android-Image-Kitchen 根目录
|
||||
3. 在 Android-Image-Kitchen 根目录执行 `./unpackimg.sh boot.img`;此命名会将 boot.img 拆开,你会得到若干文件。
|
||||
4. 将`split_img` 目录中的 `boot.img-kernel` 替换为你从 AnyKernel3 解压出来的 `Image`(注意名字改为 boot.img-kernel)。
|
||||
5. 在 Android-Image-Kitchecn 根目录执行 `./repackimg.sh`;此时你会得到一个 `image-new.img` 的文件;使用此 boot.img 通过 fastboot 刷入即可(刷入方法参考上一节)。
|
||||
|
||||
### 使用 magiskboot
|
||||
|
||||
1. 在 Magisk 的 [Release 页面](https://github.com/topjohnwu/Magisk/releases) 下载最新的 Magisk 安装包。
|
||||
2. 将 Magisk-*.apk 重命名为 Magisk-vesion.zip 然后解压缩。
|
||||
3. 将解压后的 `Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so` 文件,使用 adb push 到手机:`adb push Magisk-v25.2/lib/arm64-v8a/libmagiskboot.so /data/adb/tmp/magiskboot`
|
||||
4. 使用 adb 将原厂 boot.img 和 AnyKernel3 中的 Image 推送到手机
|
||||
5. adb shell 进入 /data/local/tmp/ 目录,然后赋予刚 push 文件的可执行权限 `chmod +x magiskboot`
|
||||
6. adb shell 进入 /data/local/tmp/ 目录,执行 `./magiskboot unpack boot.img` 此时会解包 `boot.img` 得到一个叫做 `kernel` 的文件,这个文件为你原厂的 kernel
|
||||
7. 使用 `Image` 替换 `kernel`: `mv -f Image kernel`
|
||||
8. 执行 `./magiskboot repack boot.img` 打包 img,此时你会得到一个 `new-boot.img` 的文件,使用这个文件 fastboot 刷入设备即可。
|
||||
|
||||
## 其他变通方法
|
||||
|
||||
其实所有这些安装方法的主旨只有一个,那就是**替换原厂的内核为 KernelSU 提供的内核**;只要能实现这个目的,就可以安装;比如以下是其他可行的方法:
|
||||
|
||||
1. 首先安装 Magisk,通过 Magisk 获取 root 权限后使用内核刷写器刷入 KernelSU 的 AnyKernel 包。
|
||||
2. 使用某些 PC 上的刷机工具箱刷入 KernelSU 提供的内核。
|
||||
|
||||
@@ -19,7 +19,7 @@ hero:
|
||||
|
||||
features:
|
||||
- title: 基于内核
|
||||
details: KernslSU 运行在内核空间,对用户空间应用有更强的掌控。
|
||||
details: KernelSU 运行在内核空间,对用户空间应用有更强的掌控。
|
||||
- title: 白名单访问控制
|
||||
details: 只有被授权的 App 才可以访问 `su`,而其他 App 无法感知其存在。
|
||||
- title: 模块支持
|
||||
|
||||
Reference in New Issue
Block a user