Compare commits

...

242 Commits

Author SHA1 Message Date
tiann
c4c597da9e website: Add docs for REPLACE variables 2023-03-27 17:27:53 +08:00
tiann
e5617e236c website: Fix sidebar of zh_CN 2023-03-27 17:01:01 +08:00
tiann
b1af4ada60 ksud: fix clippy 2023-03-27 15:44:18 +08:00
tiann
284b962d64 website: Add anchors for zh_CN 2023-03-27 15:35:57 +08:00
tiann
86ff022dc6 website: Add docs for REMOVE 2023-03-27 15:17:55 +08:00
tiann
dcbbbab11e ksud: Support remove files in customize.sh by REMOVE variables.
e.g:

REMOVE="
/system/app/YouTube
/system/app/Bloatware
"
2023-03-27 15:04:28 +08:00
tiann
14b2afe78d feat: Add documentation for rescuing from bootloop.
- Add new link to sidebar of zh_CN documentation page
- Add new link to sidebar navigation in English documentation page
- Add new guide for rescuing from bootloop
- Include methods for rescuing wrong module flashing
- Provide mechanisms for safe mode and AB updates for module recovery
2023-03-27 11:37:18 +08:00
tiann
ba4ffa7598 ksud: Since we have forbidden module running when Magisk installed, we
can now move common script from /data/adb/ksu/post-fs-data.d ->
/data/adb/post-fs-data.d and /data/adb/ksu/service.d ->
/data/adb/service.d
2023-03-27 10:51:25 +08:00
tiann
92ae0e5460 ci: Fix cross build error with Rust 1.68 2023-03-26 17:35:33 +08:00
Kung-chih
1ace028cef Update zh-rHK and zh-rTW strings (#323) 2023-03-26 15:51:01 +08:00
tiann
43ca2b9831 Add docs for module 2023-03-26 15:43:34 +08:00
Shatyuka
ecd5af76ab kernel: use git from PATH (#316)
One should have git in PATH in order to build Android kernel.
Also it's weird that git is used in `setup.sh` without absolute path.
2023-03-23 15:01:24 +08:00
Shatyuka
be452a22f0 kernel: Makefile new line with posix compatible (#318)
30abd9e310 (commitcomment-105526985)
2023-03-23 10:41:12 +08:00
tiann
6d6f793c69 Revert "kernel: add new line in driver Makefile (#317)"
This reverts commit 30abd9e310.
2023-03-22 19:33:08 +08:00
Shatyuka
30abd9e310 kernel: add new line in driver Makefile (#317)
some device tree has no new line in Makefile
2023-03-22 15:39:12 +08:00
Soo-Hwan Na
d0e3b2672d kernel: Add input hook to ksu_hooks.h (#315)
Then we don't have to do extern input_hook(...) ; input_hook(..) and
just include header file and use input_hook(...) only
Like
f39f4823a1
Not

0c491aca8a
2023-03-21 09:45:12 +08:00
github-actions[bot]
3b8a3ca26f [add device]: (#311)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/310

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
2023-03-20 10:23:16 +08:00
github-actions[bot]
33a096da22 [add device]: (#309)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/308

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
2023-03-20 10:22:01 +08:00
github-actions[bot]
72ba3ba086 [add device]: Asus Zenfone Max Pro M1/M2 (#313)
Asus Zenfone Max Pro M1/M2 has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/312

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-20 10:19:47 +08:00
tiann
5dd430e6a6 website: update FAQ 2023-03-14 11:00:35 +08:00
tiann
77056a7100 kernel: remove unused poll wait becuase we always call this in post-fs-data, no need to wait for /data mounted 2023-03-08 15:45:56 +08:00
TinyHai
76b9790ffb manager: make a small adjustment to module screen (#291) 2023-03-03 17:29:53 +08:00
weishu
54d2962a0d kernel: add_type for 5.4 is supported actually. 2023-03-03 11:17:17 +07:00
TinyHai
7846b2a440 manager: replace SwipeRefresh with PullRefreshIndicator & refactor so… (#288)
- replace SwipeRefresh with PullRefreshIndicator
- optimize pull refresh
- refactor some code
- fix install bottom in module page again
2023-03-02 12:35:41 +08:00
tiann
8bbfe0c26d manager: Add pstore and diag to bugreport 2023-03-02 11:46:12 +08:00
Amicia De Rune
5c67334889 manager: update id translation (#286)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-03-02 11:00:43 +08:00
tiann
0b1b73a05d manager: Fix install button in module page 2023-03-01 14:22:46 +08:00
tiann
203dc42e75 manager: hide install button when magisk installed. 2023-03-01 12:51:04 +08:00
tiann
9f6e9f5db6 manager: Show module conflict state with Magisk 2023-03-01 12:12:31 +08:00
tiann
ee09b9f9f4 ksud: Breaking change: modules are mounted to /data/adb/modules and will disable itself when magisk installed. 2023-03-01 11:49:54 +08:00
tiann
c534ef672e manager: Add prop to bugreport 2023-03-01 11:48:24 +08:00
tiann
c34a5ae2a6 ksud: Disable modules when magisk installed 2023-03-01 11:41:53 +08:00
tiann
1c65048813 website: fix typo and add KernelFlasher 2023-03-01 10:56:20 +08:00
uaevuon
3b8d0b83d4 website: Fix typo (#283) 2023-02-28 14:10:31 +08:00
Fankesyooni
b2c39af069 website: Fix wrong directory name in document (#282)
I think there is a misspell in document
2023-02-27 22:57:40 +08:00
rhjdvsgsgks
5c3df7e7a5 kernel: make some compiler happy again (#279)
e743722449
still not fix #252
2023-02-26 22:08:50 +08:00
tiann
ffa5a93c75 ksud: fix mount and remount order 2023-02-26 11:44:08 +08:00
tiann
794b725928 website: Update sirius device. close #275 2023-02-25 10:50:50 +08:00
tiann
ffc06525fb ksud: don't touch rootfs mount 2023-02-24 20:00:57 +08:00
tiann
3fe99712ba manager: Remove the confusing UI of system rw. Many users don't know what is used for and most users don't need this (it is recommended to use module to modify /system). Maybe i'll add it back for cli usage (such as ksud enable system_rw 2023-02-24 12:41:32 +08:00
TinyHai
765c2b7d1f manager: update dep version to fix bug #269 (#272) 2023-02-24 12:25:01 +08:00
tiann
f789bb8c53 ksud: make it buildable without git installed 2023-02-24 11:55:03 +08:00
tiann
745f109686 Revert "ksud: make it buildable without git installed"
This reverts commit 8ed3bd53ac.
2023-02-24 11:53:04 +08:00
tiann
8ed3bd53ac ksud: make it buildable without git installed 2023-02-24 11:41:39 +08:00
weishu
40d7d62af2 kernel: optimize vfs_read probe. 2023-02-23 12:12:50 +07:00
weishu
09fb118d22 kernel: return early for prctl command 2023-02-22 14:26:53 +07:00
Howard Wu
0c3731b0bd Fix WSA Kernel build (#267)
1. Use ubuntu-20.04
1. Use LLVM 12
1. Fix build cache
1. Fix KernelSU version
2023-02-22 09:20:37 +08:00
tiann
349fd09440 ksud: remove original file after copy 2023-02-22 00:08:18 +08:00
tiann
bd3773f32a Revert "ci: use ubuntu-latest for wsa build"
This reverts commit 572708c283.
2023-02-21 23:58:31 +08:00
tiann
572708c283 ci: use ubuntu-latest for wsa build 2023-02-21 23:56:19 +08:00
tiann
fafdacfc41 ksud: copy file when rename failed 2023-02-21 23:52:04 +08:00
tiann
c3ba483b81 ksud: fix clippy 2023-02-21 22:09:41 +08:00
tiann
e309a03515 ksud: Fix windows compile 2023-02-21 21:39:14 +08:00
tiann
82a304e054 ksud: fmt 2023-02-21 20:24:10 +08:00
tiann
b76d973f3a ksud: restore stock mount after overlay mount. close #233 2023-02-21 20:22:00 +08:00
github-actions[bot]
237e477876 [add device]: Miatoll [curtana, excalibur, gram, joyeuse] (#265)
Miatoll [curtana, excalibur, gram, joyeuse] has been added to the
website.
Related issue: https://github.com/tiann/KernelSU/issues/264

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-21 18:50:58 +08:00
tiann
47bcccdce3 ksud: fmt 2023-02-21 13:10:32 +08:00
tiann
ba8ca1f9f2 ksud: fmt 2023-02-21 13:09:03 +08:00
tiann
4837f2101c ksud: fix incorrect umask 2023-02-21 13:06:37 +08:00
tiann
37d2914611 ksud: Fix common script may not be executed 2023-02-21 12:54:05 +08:00
weishu
849164e4de kernel: fix compile err 2023-02-20 20:06:56 +07:00
weishu
683ba112aa kernel: add prefix to avoid symbol confliction 2023-02-20 18:51:59 +07:00
weishu
e743722449 kernel: make some compiler happy 2023-02-20 18:51:59 +07:00
Ylarod
a5ee2ef93b ksud: setns to init for module commands (#262) 2023-02-20 19:43:49 +08:00
Re*Index. (ot_inc)
3e66f5e8cf fix translation (#244)
Japanese Translation Adjustments and Fixes

---------

Co-authored-by: weishu <twsxtd@gmail.com>
2023-02-20 19:37:35 +08:00
weishu
83b0aed52a kernel: apply rules should return success when selinux is disabled or permissive 2023-02-20 18:13:53 +07:00
Amicia De Rune
498763505a manager: update id translation language (#259)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-20 14:33:16 +08:00
Wahid Khan
8cc4ad4d80 website: fix typos (#258) 2023-02-20 14:13:21 +08:00
TinyHai
6ec0c25173 manager: show confirm dialog before uninstall module (#257)
manager: Add Dialog component, show confirm dialog before uninstall
module, fix a bug in listModules
2023-02-20 10:52:23 +08:00
TinyHai
93bcd78f89 manager: Update the bottom bar navigation logic(#254)
close #232
2023-02-20 10:37:40 +08:00
TinyHai
ebf6a52237 manager: Add key to LazyColumn's items to prevent incorrect allow sta… (#255) 2023-02-20 10:31:58 +08:00
tiann
a2906093ec ksud: Fix denpendency 2023-02-20 10:07:30 +08:00
Nullptr
a161c318a1 kernel: allow root processes to get allow/deny list (#256) 2023-02-19 16:09:21 +08:00
github-actions[bot]
3f1ee2f784 [add device]: (#250)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/249

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-18 09:26:51 +08:00
Ylarod
60de2e4a6e manager: send app list in bug report (#248)
We need this for uid in dmesg
2023-02-16 23:39:21 +08:00
tiann
d5bb79edd5 website: Add safemode integrate instruction 2023-02-16 12:08:30 +08:00
tiann
7264a00813 ci: Fix ksud version incorrect 2023-02-16 10:05:35 +08:00
Arya Hajalizade
cf21767975 Update faq.md (#246)
Changed a line towards a better grammar and made it more readable.
2023-02-16 10:00:20 +08:00
Enes Sastim
8fbdd996de use selinux_state for 4.9.212+ (#245)
_selinux_state_ is backported to 4.9 kernel with the 4.9.212 release,
use it to fix the build.
many thanks to @reallysnow for figuring this out.
inspired by
d7c2c5f02a
2023-02-15 11:39:16 +08:00
Amicia De Rune
2c3dcae117 manager: update id translation language (#243)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-14 22:41:41 +08:00
weishu
b024b5d006 kernel: support add_type for 4.x kernel
Co-authored-by: Ookiineko <chiisaineko@protonmail.com>
2023-02-14 19:08:18 +07:00
weishu
d5bab2317e kernel: correctly handle add attr 2023-02-14 18:52:48 +07:00
tiann
0c8b4a48de ksud: ignore empty sepolicy rule 2023-02-14 18:10:39 +08:00
weishu
f9b3218ded kernel: fix policydb length 2023-02-14 14:41:00 +07:00
Amicia De Rune
acc37fb387 manager: Update ID language translation (#239)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-14 13:02:16 +08:00
tiann
f50b4dfe34 ksud: fmt 2023-02-14 12:59:21 +08:00
tiann
9ce7351aaa ksud: support check_sepolicy in scripts 2023-02-14 12:57:08 +08:00
tiann
c691a1adb2 manager: Add reboot shortcut for module screen 2023-02-14 12:01:05 +08:00
tiann
9a2a21ec5d manager: Add more info to bugreport 2023-02-14 11:44:57 +08:00
tiann
a9fd0aa132 manager: Add kernel version to bugreport 2023-02-14 11:40:24 +08:00
tiann
198674d889 manager: show manager version 2023-02-14 11:37:37 +08:00
tiann
203a5683ac manager: show safemode in homescreen 2023-02-14 11:29:16 +08:00
tiann
eeb8cda175 manager: show module state in safemode 2023-02-13 23:33:56 +08:00
tiann
b268971323 ksud: support module disable in safemode 2023-02-13 23:33:56 +08:00
weishu
051fc53a4f kernel: count time of volumedown to enter safemode 2023-02-13 22:01:59 +07:00
tiann
55602f1f16 manager: ui state for safemode 2023-02-13 22:41:32 +08:00
tiann
42428345ff ksud: check kernel safemode 2023-02-13 22:28:43 +08:00
weishu
ca950d909b kernel: press KEY_VOLUMEDOWN over 2 seconds will enter safemode and disable all modules
Co-authored-by: Ylarod <me@ylarod.cn>
2023-02-13 21:23:28 +07:00
weishu
20ff530962 kernel: fix sepolicy apply may not work #227
Co-authored-by: sekaiacg <sekaiacg@gmail.com>
2023-02-13 09:12:31 +07:00
tiann
a5dbbf4881 Revert "[add device]: GKI 1.0 (#236)"
This reverts commit e91b1fc89a.
2023-02-13 09:38:03 +08:00
github-actions[bot]
e91b1fc89a [add device]: GKI 1.0 (#236)
GKI 1.0 has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/235

---------

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
2023-02-12 18:44:31 +08:00
tiann
4e35e4ae86 ksud: fix vendor only overlay not working 2023-02-12 18:29:27 +08:00
Aquarius223
6d15cb7e33 kernel: Fix f6967d2c lost parameters after ack-linux 4.14.y (>=163) (#234)
* [android-4.14-stable tree:
https://github.com/aosp-mirror/kernel_common/commit/5d0939e1]

Change-Id: Ice92dd83df4c4f1ae272156cb57f95998e45819f

Co-authored-by: stic-server-open <1138705738@qq.com>
2023-02-12 18:11:28 +08:00
tiann
095acad8a6 ksud: Fix mount ext4 failed of sys-mount 2023-02-12 18:07:50 +08:00
skbeh
c187d1ad8a ksud: remove tailing "/" from PATH (#230) 2023-02-12 18:07:07 +08:00
weishu
f6967d2cfb kernel: reset avc cache for sepolicy
Co-authored-by: sekaiacg <sekaiacg@gmail.com>
2023-02-12 13:09:00 +07:00
weishu
388d2b0b59 kernel: disable inofity for fd opened for manager 2023-02-12 12:15:58 +07:00
github-actions[bot]
d9aecbcbca [add device]: (#229)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/228

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-11 19:55:23 +08:00
rxuglr
eabdf3e78c manager: Add Russian language (#225) 2023-02-11 15:26:55 +08:00
tiann
a1fb7c5fdf ksud: don't hardcode system unzip becuase we use busybox now 2023-02-10 18:51:22 +08:00
f19
89394245b1 fix KSU_GIT_VERSION is not defined while importing KSU by 'git submodule add' (#224)
.git is a text file while the module is imported by 'git submodule add'.
2023-02-10 16:34:47 +08:00
Re*Index. (ot_inc)
91f1eb2d6a Added Japanese language to KernelSU Manager (#223)
Japanese translation added.
2023-02-09 23:51:53 +08:00
tiann
ab5f6db54b website: Update installation 2023-02-09 14:04:41 +08:00
tiann
626642af76 ksud: e2fsck before resize2fs 2023-02-09 13:53:07 +08:00
tiann
473f02396f website: Update installation 2023-02-09 10:51:16 +08:00
tiann
aa4b1bf9d8 website: Update installation instruction 2023-02-09 09:28:16 +08:00
tiann
8e5a72fc35 ci: Fix release build 2023-02-08 23:58:47 +08:00
weishu
7302653879 Add release ci (#218) 2023-02-08 22:31:43 +08:00
github-actions[bot]
b2b563547c [add device]: (#220)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/219

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-08 22:18:05 +08:00
tiann
cc29ad151a ksud: Add KSU_KERNEL_VER_CODE 2023-02-08 15:09:36 +08:00
tiann
a4a93d8945 ksud: clippy 2023-02-08 12:33:00 +08:00
tiann
3389cd0aea ksud: Fix module operation order 2023-02-08 12:26:33 +08:00
tiann
d26956ff72 ksud: Fix confliction with stock system overlayfs 2023-02-08 12:14:31 +08:00
Aquarius223
66284bfbe3 kernel: Correctly judge the version of selinux_state (#214)
* Merged related changes in android-common-kernel-4.14.163 [1] Handle
correct checks for older versions (below 4.14.163, 4.9, 4.4)
* fixes 14be7562, it is required in 4.14.163 kernel, it is not needed in
4.14.163 and later

[1]: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/commits/1cfd8419

Change-Id: Iade104312d058a249667ca836259d5608418bb55

Co-authored-by: admin <paper@localhost>
2023-02-07 15:02:48 +08:00
初羽
14be75629b kernel: selinux: Fix build error (#213)
* d77ab8d although fixed some typo, but since there is no include avc.h,
this will cause the build to fail
* Error:


/mnt/HDD/fish/aospa/kernel/msm-4.9/drivers/staging/ksu/selinux/selinux.c:60:2:
error: use of undeclared identifier 'selinux_enforcing'
        selinux_enforcing = enforce;
        ^

/mnt/HDD/fish/aospa/kernel/msm-4.9/drivers/staging/ksu/selinux/selinux.c:81:9:
error: use of undeclared identifier 'selinux_enforcing'
        return selinux_enforcing;
               ^
2 errors generated.

Change-Id: I4cc8917b2b73ca467873222e02d498da8718988e
2023-02-07 09:50:55 +08:00
小さい猫
d77ab8dbff kernel: selinux: fix more typos introduced in #166 (#212)
i didn't realize these typos in #181

Signed-off-by: Ookiineko <chiisaineko@protonmail.com>
2023-02-06 16:31:26 +08:00
weishu
c2ac548ac7 kernel: allow use without git submodule 2023-02-06 15:30:58 +07:00
tiann
ad4d8e939e ksud: clippy 2023-02-06 09:16:15 +08:00
tiann
aa7a00b299 ksud: don't mount module when in safe mode 2023-02-06 08:58:02 +08:00
tiann
1ff421365e ksud: pass KSU_VER and KSU_VER_CODE to installer script 2023-02-06 08:53:18 +08:00
weishu
0a12d0139d kernel: make sure KernelSU is a submodule to avoding incorrect git version 2023-02-05 16:31:50 +07:00
Daybit
181ab4f545 [add device]:Pixel4XL (#210)
[add device]:Pixel4XL
2023-02-05 13:21:40 +08:00
github-actions[bot]
3e29e98f2c [add device]: (#209)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/208

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-05 11:42:58 +08:00
Amicia De Rune
a66c1de660 manager: update translation ID language (#207)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-05 07:15:21 +08:00
f19
0c322a33bc kernel: fix filp_open on older kernel's kworker (#205)
On older kernel, kworker missing keyring from init process , and this
keyring is related to FBE , which causes filp_open return ENOKEY or
other errors.To fix this,just install init's keyring to per
kworkers.This works on Kernel 4.4 and 4.9.
2023-02-05 07:14:59 +08:00
小さい猫
cd33a6dd07 website: document the replacement of do_faccessat for some non-GKI (#206)
Signed-off-by: Ookiineko <chiisaineko@protonmail.com>
2023-02-04 21:56:04 +08:00
github-actions[bot]
c1dceaf11f [add device]: (#204)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/203

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-04 15:35:26 +08:00
skbeh
3181dd17bc ci: add CI for clippy, rustfmt and shell scripts (#193) 2023-02-04 13:52:20 +08:00
VINC3NT
c93fa1af59 [add device]: Redmi K40s / POCO F4 (#201) 2023-02-04 13:36:01 +08:00
skbeh
9f4a8d3dfc ksud, kernel: update explanation and fix some issues (#194) 2023-02-04 13:16:51 +08:00
weishu
4da829792f kernel: unify version 2023-02-03 23:01:02 +07:00
github-actions[bot]
1a073224c3 [add device]: Redmi K40 / POCO F3 (#198)
Redmi K40 / POCO F3 has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/197

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-03 23:33:36 +08:00
tiann
175de861bf ksud: Fix incorrect dependency 2023-02-03 23:19:22 +08:00
tiann
aa73c34db2 ksud: unify version 2023-02-03 21:02:11 +08:00
github-actions[bot]
ed2176af8c [add device]: (#196)
has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/195

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-03 19:12:44 +08:00
tiann
49f7d56fdd manager: Add send log 2023-02-03 18:14:42 +08:00
tiann
71cc166f72 ksud: Fix macOS compile 2023-02-03 14:46:48 +08:00
tiann
8ee00839dc ksud: Fix script exec 2023-02-03 14:26:26 +08:00
github-actions[bot]
6239662a7f [add device]: Redmi Note 9 Pro/ Mi 10T Lite/ Mi 10i (#190)
Redmi Note 9 Pro/ Mi 10T Lite/ Mi 10i has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/189

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-03 14:12:24 +08:00
Huy Minh
216c2aa2cd website: vi_VN: Update FAQs (#191) 2023-02-03 14:03:05 +08:00
tiann
70b8b43b48 ksud: support sepolicy check 2023-02-03 13:16:17 +08:00
tiann
430c2e709f website: Add some faq 2023-02-03 11:11:41 +08:00
tiann
b975950b07 ksud: fix module update 2023-02-03 10:05:09 +08:00
tiann
ed42cf42d2 ksud: restore syscon before module install script; this revert 2a4675e25b 2023-02-03 10:00:52 +08:00
tiann
d80c282644 ksud: tweaks for setcon 2023-02-03 09:55:03 +08:00
tiann
a05edb3872 manager: coming soon for system writable 2023-02-03 09:45:42 +08:00
tiann
43b8987b4b manager: open installation guide when install 2023-02-03 09:45:42 +08:00
skbeh
219ea1c458 ksud: fix issues found by clippy (#167)
These issues are mostly found by `cargo clippy -- -W clippy::pedantic`.
2023-02-03 09:45:07 +08:00
skbeh
bea93f6ad7 ksud: compress embed assets (#186) 2023-02-03 09:31:32 +08:00
耀風
e4267848f0 manager: Update Traditional Chinese (#185) 2023-02-03 08:37:07 +08:00
Huy Minh
bc5953b510 manager: vi_VN: small edit (#184) 2023-02-03 05:15:32 +08:00
Arthur
e41b7cd117 website: fix description error (#183) 2023-02-03 05:14:36 +08:00
Nguyễn Long
cab78e7893 manager: add vi translation (#182)
Signed-off-by: HoangLong-Lumi <hoanglong.lumi404@gmail.com>
2023-02-02 23:39:10 +08:00
小さい猫
199f5cc223 fix some typos introduced in #166 (#181)
Signed-off-by: Ookiineko <chiisaineko@protonmail.com>
Co-authored-by: f19 <58457605+F-19-F@users.noreply.github.com>
Co-authored-by: Scirese <nuclearlight91@gmail.com>
2023-02-02 23:38:04 +08:00
Amicia De Rune
962649f7ca manager: add id translation (#179)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-02 22:40:33 +08:00
tiann
7b32c0e37b ksud: opti module mount 2023-02-02 21:56:08 +08:00
tiann
12f353a1ae ksud: Add bin path to script PATH 2023-02-02 21:42:04 +08:00
tiann
c26e170c87 ksud: default umask 022(0644 for file) 2023-02-02 21:24:11 +08:00
tiann
6a706de09e ksud: use busybox to exec all shell scripts 2023-02-02 20:37:17 +08:00
tiann
2a4675e25b ksud: set /system permission after installation script 2023-02-02 20:37:17 +08:00
tiann
0bc36b3299 ksud: fix incorrecy RAII 2023-02-02 20:37:17 +08:00
tiann
dc902b16d4 ksud: copy when rename failed 2023-02-02 20:37:17 +08:00
tiann
86998a032e ksud: fallback to system mount when rust lib mount failed 2023-02-02 20:37:17 +08:00
github-actions[bot]
1727ec41c4 [add device]: OnePlus 8 Serials (#178)
OnePlus 8 Serials has been added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/177

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-02 20:10:51 +08:00
tiann
3eb812be5b ksud: Fix module may mount failed 2023-02-02 14:02:02 +08:00
Amicia De Rune
64c2f6ba5c website: update translation in ID language (#176)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-02 13:59:10 +08:00
WeeAris
2db7da0766 website:fix devices info error (#175)
Fix devices info error for unofficially supported devices.
#173
2023-02-02 11:23:21 +08:00
weishu
d1e7bad18f kernel: version 15 2023-02-02 09:05:37 +07:00
tiann
d7cef25665 manager: Superuser support refresh and hide system apps 2023-02-02 09:59:40 +08:00
tiann
23f41145b0 ksud: Fix module resize error 2023-02-02 09:25:41 +08:00
tiann
a969af8159 relicense kernel source code to GPL-v2 2023-02-02 09:04:07 +08:00
tiann
001fa00355 ksud: Add logs for module installation 2023-02-02 08:58:34 +08:00
tiann
a6dddd32b3 website: fix build err 2023-02-02 08:40:28 +08:00
tiann
cd825e34da ksud: support common post-fs-data.d and service.d 2023-02-02 08:39:26 +08:00
github-actions[bot]
2bba088319 [add device]: non-GKI devices, 4.19.x kernel, sm8250, repo for AOSP only. (#171)
non-GKI devices, 4.19.x kernel, sm8250, repo for AOSP only. has been
added to the website.
Related issue: https://github.com/tiann/KernelSU/issues/170

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-02 03:20:19 +08:00
Amicia De Rune
4905bd9bb8 website: fix missing directory in ID language (#169)
sry missing /guide/

Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-02 03:19:27 +08:00
Amicia De Rune
3f625a000b website: fix missing pages in ID language (#168)
Signed-off-by: RooGhz720 <rooghz720@gmail.com>

Signed-off-by: RooGhz720 <rooghz720@gmail.com>
2023-02-01 22:41:53 +08:00
tiann
3bfee10a0d ksud: set SELinux context for /vendor 2023-02-01 21:10:16 +08:00
tiann
d5a05da5b8 ci: don't build kernel when ksud changes 2023-02-01 21:01:32 +08:00
tiann
d8042a36c3 ksud: fix post-fs-data.sh and service.sh may be not executed 2023-02-01 20:28:38 +08:00
tiann
85bf01eb65 ksud: use logcat log 2023-02-01 20:28:38 +08:00
f19
4f2b8b7077 kernel: backport to 4.4 (#166)
These changes make KernelSU work on kernel4.4
[link](https://github.com/F-19-F/android_kernel_oneplus_msm8998).
LINUX_VERSION_CODE macro changes have been vertied on 4.4 4.9 4.14.
For kernel 4.4,just pick two commits
* [introduce
KernelSU](2993524f2f)
* [allow init exec ksud under
nosuid](3df9df42a6)
2023-02-01 19:48:36 +08:00
tiann
417ff8a6c5 ksud: fix module installation failed when size < 1M 2023-02-01 19:30:39 +08:00
tiann
681c4a3f0d ksud: minor tweaks 2023-02-01 19:12:25 +08:00
tiann
619dd1ace1 ksud: make it compilable for non-android 2023-02-01 19:04:33 +08:00
tiann
1cd18a643d ksud: some refactor 2023-02-01 18:33:38 +08:00
tiann
3519d61636 ksud: format code 2023-02-01 17:59:16 +08:00
skbeh
46913671a8 kernel, ksud: collect binaries into /data/adb/ksu (#161) 2023-02-01 17:58:58 +08:00
tiann
b7ff6b1a51 ksud: support module sepolicy.rule 2023-01-31 21:58:59 +08:00
weishu
62be9eb589 kernel: version 14 2023-01-31 20:55:25 +07:00
weishu
ab6d483c32 kernel: fix sepolicy rule which contains * 2023-01-31 20:51:02 +07:00
weishu
2d77f84736 kernel: bump version 13 2023-01-31 19:21:21 +07:00
tiann
c2f7963a43 ci: Add android13-5.10.149 2023-01-31 20:19:36 +08:00
weishu
b9e27621ee kernel: support sepolicy 2023-01-31 18:54:31 +07:00
skbeh
2766e24007 ci: replace actions-rs/cargo with naive command (#162)
This avoids node 12 warnings.

Co-authored-by: Ylarod <me@ylarod.cn>
2023-01-31 19:11:00 +08:00
Ylarod
45911ac3c4 ci: trigger ci if yml changes in pr (#163) 2023-01-31 19:06:11 +08:00
tiann
ad89c5a80f ksud: fix compile for x86_64 2023-01-31 19:00:58 +08:00
tiann
3413f4a4fe ksud: sepolicy support 2023-01-31 18:47:43 +08:00
Ylarod
2a88cca50c ci: update kernel build, remove embed ksud (#159) 2023-01-31 10:37:54 +08:00
skbeh
634978c14f ksud: remove subprocess dependency (#156) 2023-01-30 20:14:27 +08:00
Ylarod
093f7d9758 website: fix typo (#158) 2023-01-30 20:06:09 +08:00
Ylarod
40960f60f8 ci: handle add device automatically (#157) 2023-01-30 19:41:46 +08:00
The_second_Tom
25a4c2930a website: add Xiaomi 8 (dipper) (#151)
Add kernelsu support for MI 8 (dipper)
2023-01-30 15:04:59 +08:00
Sakura桜
342344db0c website: Remove "For LineageOS" on MI 8 SE (#150)
Change _MI8SE(sirius) for LineageOS_ To _Xiaomi MI 8 SE(sirius)_

With a lot of tests, it also can use on Crdroid, PixelExperience and so
on.
2023-01-30 15:04:03 +08:00
skbeh
7785d2a3f8 ksud: replace some utils with rust libraries (#142) 2023-01-30 12:57:25 +08:00
tarsin
bd6b0d3d12 Proper way to fix #107 (#148) 2023-01-30 09:53:24 +08:00
Amicia De Rune
a871b92dc9 website: add xiaomi redmi note 10 pro (sweet) (#147) 2023-01-30 08:19:37 +08:00
Aquarius223
47d15c47f3 website: Merged MI 6 and MIX 2 information into one line (#146)
Change-Id: Ide86c6ea2256b06390852d73dc0b79bc8ced4792

Co-authored-by: admin <paper@localhost>
2023-01-30 08:19:09 +08:00
tiann
684283c585 manager: fix uninstall button is overlayed. close #107 2023-01-29 21:31:20 +08:00
tiann
d2d9b0eaad ksud: fix broken /system/vendor when it is a symlink 2023-01-29 21:27:29 +08:00
SlackerState
22e6b1eec5 website:add RedmiK30 4G(phoenix) (#144)
add RedmiK30 4G(phoenix)
2023-01-29 20:27:25 +08:00
Aquarius223
f6301c5a7c website: Add Xiaomi MI 6 (sagit) and Xiaomi MIX 2 (chiron) (#143)
Change-Id: I1d68b41d74c9cf237b834d96c53a5949add2f4ba

Co-authored-by: stic-server-open <1138705738@qq.com>
2023-01-29 19:06:23 +08:00
Sakura桜
b191415173 Add MI8SE (#141) 2023-01-29 16:21:33 +08:00
Ylarod
59542bc99a add issue template (#139) 2023-01-29 16:14:23 +08:00
tiann
0e4b5b3765 website: fix device list link broken 2023-01-29 16:12:22 +08:00
Ylarod
a986251773 ci: fix kernel build (#137) 2023-01-29 13:00:47 +08:00
tiann
0e651fdc99 website: opti device list 2023-01-29 12:59:37 +08:00
Ylarod
b1e279bd2a ci: update build (#129)
1. update kernel ci
2. fix manager build
2023-01-29 11:25:10 +08:00
tarsin
e76cf08934 Fix Navbar color (#136) 2023-01-29 10:32:34 +08:00
tarsin
4cf930a9dc Update unofficially-support-devices.md (#135)
Add cas
2023-01-29 08:46:59 +08:00
Huy Minh
599723515e docs: vi_VN: Translate some strings (#134)
Signed-off-by: Huy Minh <buingoc67@gmail.com>
2023-01-28 23:11:40 +08:00
Huy Minh
e1e8d53da5 docs: Adding Android-x86 kernel supported by me (#133)
Chinese translated using Google Translate
2023-01-28 22:36:44 +08:00
Huy Minh
20a2c0092e docs: vi_VN: translate unofficially supported devices (#131) 2023-01-28 22:15:18 +08:00
tiann
a40ad07e2b website: repo list for munch 2023-01-28 21:13:48 +08:00
Ylarod
65d80aad03 docs: add unofficially-support-devices (#128) 2023-01-28 20:41:23 +08:00
Ylarod
905c041a76 ci: common kernel build and embed ksud (#127) 2023-01-28 12:24:43 +08:00
tiann
0dae6ebaee docs: simplify 2023-01-28 09:33:03 +08:00
Ylarod
d41954b09c website: update description 2023-01-27 16:58:52 +08:00
tiann
6e8771b8d9 manager: check overlayfs support 2023-01-26 17:27:16 +08:00
weishu
cf5bcc09e8 kernel: version 12 2023-01-26 16:16:00 +07:00
weishu
413a8d0a2f kernel: support selinux state transition 2023-01-26 16:05:14 +07:00
weishu
1852652029 kernel: add sepolicy calls 2023-01-26 16:05:14 +07:00
120 changed files with 7816 additions and 1345 deletions

33
.github/ISSUE_TEMPLATE/add_device.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Contribute to Unofficially Supported Device
description: Add your device kernel source to KernelSU's Unofficially Supported Device List
title: "[Add Device]: "
labels: ["add-device"]
body:
- type: markdown
attributes:
value: |
Thanks for supporting KernelSU !
- type: input
id: repo-url
attributes:
label: Repository URL
description: Your repository URL
placeholder: https://github.com/tiann/KernelSU
validations:
required: true
- type: input
id: device
attributes:
label: Device
description: Please describe the device maintained by you.
placeholder: GKI 2.0 Device
validations:
required: true
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you should be the maintainer of the repository.
options:
- label: I'm the maintainer of this repository
required: true

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

10
.github/ISSUE_TEMPLATE/custom.md vendored Normal file
View File

@@ -0,0 +1,10 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

56
.github/scripts/build_a12.sh vendored Normal file
View File

@@ -0,0 +1,56 @@
#!/bin/bash
set -euo pipefail
build_from_image() {
export TITLE
TITLE=kernel-aarch64-${1//Image-/}
echo "[+] title: $TITLE"
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
unzip gki-kernel.zip && rm gki-kernel.zip
echo '[+] Unpack prebuilt boot.img'
BOOT_IMG=$(find . -maxdepth 1 -name "boot*.img")
$UNPACK_BOOTIMG --boot_img="$BOOT_IMG"
rm "$BOOT_IMG"
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-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 '[+] 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"
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 ..
fi
done

43
.github/scripts/build_a13.sh vendored Normal file
View File

@@ -0,0 +1,43 @@
#!/bin/bash
set -euo pipefail
build_from_image() {
export TITLE
TITLE=kernel-aarch64-${1//Image-/}
echo "[+] title: $TITLE"
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
$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
$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
$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"-"$1"-"$image".gz
done
echo '[+] Images to upload'
find . -type f -name "*.gz"
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 ..
fi
done

59
.github/workflows/add-device.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: handle-add-device-issue
on:
issues:
types: [labeled]
jobs:
handle-add-device:
if: github.event.label.name == 'add-device'
runs-on: ubuntu-latest
env:
ISSUE_CONTENT: ${{ github.event.issue.body }}
steps:
- uses: actions/checkout@v3
- name: Parse issue body
id: handle-add-device
run: |
python3 scripts/add_device_handler.py website/docs/repos.json || true
- name: Commit
if: steps.handle-add-device.outputs.success == 'true'
run: |
git config --local user.name "GitHub Actions"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add website/docs/repos.json
git commit -m "add device: ${{ steps.handle-add-device.outputs.device }}"
- name: Make pull request
if: steps.handle-add-device.outputs.success == 'true'
id: cpr
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
title: "[add device]: ${{ steps.handle-add-device.outputs.device }}"
body: |
${{ steps.handle-add-device.outputs.device }} has been added to the website.
Related issue: ${{ github.event.issue.html_url }}
branch: "add-device-${{ github.event.issue.number }}"
labels: add-device
delete-branch: true
- name: Check outputs
if: ${{ steps.cpr.outputs.pull-request-number }}
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
- uses: ben-z/actions-comment-on-issue@1.0.2
if: ${{ steps.cpr.outputs.pull-request-number }}
with:
message: "Automatically created pull request: ${{ steps.cpr.outputs.pull-request-url }}"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: ben-z/actions-comment-on-issue@1.0.2
if: steps.handle-add-device.outputs.success != 'true'
with:
message: "Cannot create pull request. Please check the issue content. Or you can create a pull request manually."
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: close issue
uses: peter-evans/close-issue@v1
with:
issue-number: ${{ github.event.issue.number }}
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,98 +1,103 @@
name: Build WSA-5.10.117-Kernel
on:
push:
branches: [ "main" ]
paths:
- '.github/workflows/build-WSA-5.10.117-kernel.yml'
- 'kernel/**'
branches: ["main"]
paths:
- ".github/workflows/build-WSA-5.10.117-kernel.yml"
- "kernel/**"
pull_request:
branches: [ "main" ]
paths:
- 'kernel/**'
branches: ["main"]
paths:
- "kernel/**"
workflow_call:
workflow_dispatch:
jobs:
build:
strategy:
matrix:
arch: [x86_64, arm64]
version: [5.10.117.2]
include:
- version: 5.10.117.2
arch: "x86_64"
out_file: "arch/x86/boot/bzImage"
kernel_make_cmd: "bzImage"
- file_name: "bzImage"
make_config: "config-wsa"
date: "20220906"
- version: 5.10.117.2
arch: "arm64"
out_file: "arch/arm64/boot/Image"
kernel_make_cmd: "ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu Image"
arch: x86_64
- file_name: "Image"
cross_compile: "aarch64-linux-gnu"
make_config: "config-wsa-arm64"
date: "20220906"
arch: arm64
name: Build WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
runs-on: ubuntu-18.04
runs-on: ubuntu-20.04
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_MAXSIZE: "2G"
CCACHE_HARDLINK: "true"
steps:
- uses: hendrikmuhs/ccache-action@v1.2
with:
key: ccache-WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
append-timestamp: false
save: ${{ github.event_name != 'pull_request' }}
- uses: actions/checkout@v3
with:
path: KernelSU
- name: Install Build Tools
run: |
sudo apt update
sudo apt install -y --no-install-recommends bc bison build-essential ca-certificates flex git gnupg libelf-dev libssl-dev lsb-release software-properties-common wget libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget
export LLVM_VERSION=12
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh $LLVM_VERSION
rm ./llvm.sh
sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang
sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld
sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump
sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar
sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm
sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip
sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy
sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf
sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++
- name: Install LLVM
run: |
sudo apt install -y --no-install-recommends bc bison build-essential ca-certificates flex git gnupg libelf-dev libssl-dev lsb-release software-properties-common wget libncurses-dev binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu nuget
export LLVM_VERSION=10
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh $LLVM_VERSION
rm ./llvm.sh
sudo ln -s --force /usr/bin/clang-$LLVM_VERSION /usr/bin/clang
sudo ln -s --force /usr/bin/ld.lld-$LLVM_VERSION /usr/bin/ld.lld
sudo ln -s --force /usr/bin/llvm-objdump-$LLVM_VERSION /usr/bin/llvm-objdump
sudo ln -s --force /usr/bin/llvm-ar-$LLVM_VERSION /usr/bin/llvm-ar
sudo ln -s --force /usr/bin/llvm-nm-$LLVM_VERSION /usr/bin/llvm-nm
sudo ln -s --force /usr/bin/llvm-strip-$LLVM_VERSION /usr/bin/llvm-strip
sudo ln -s --force /usr/bin/llvm-objcopy-$LLVM_VERSION /usr/bin/llvm-objcopy
sudo ln -s --force /usr/bin/llvm-readelf-$LLVM_VERSION /usr/bin/llvm-readelf
sudo ln -s --force /usr/bin/clang++-$LLVM_VERSION /usr/bin/clang++
- name: Checkout KernelSU
uses: actions/checkout@v3
with:
path: KernelSU
ref: main
fetch-depth: 0
- name: Download kernel source
run: |
cd $GITHUB_WORKSPACE
KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel
echo "[+] 克隆远程仓库 WSA-Linux-Kernel..."
git clone https://github.com/microsoft/WSA-Linux-Kernel
cd WSA-Linux-Kernel && git checkout android-lts/latte/${{ matrix.version }}
echo "[+] 切换到分支 android-lts/latte/${{ matrix.version }}"
echo "[+] 导入 KernelSU"
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
echo "[+] 复制 kernel su driver 到路径:$KERNEL_ROOT/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
echo "[+] 添加 kernel su driver 到文件:$DRIVER_MAKEFILE"
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] KernelSU 导入完成"
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch
cd -
- name: Build Kernel
working-directory: WSA-Linux-Kernel
run: |
KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel
echo "[+] 构建 kernel"
cp configs/wsa/${{ matrix.make_config }}-5.10 $KERNEL_ROOT/.config
echo "[+] 复制配置文件 configs/wsa/${{ matrix.make_config }}-5.10 到 $KERNEL_ROOT/.config"
echo "执行: make -j`nproc` LLVM=1 ${{ matrix.kernel_make_cmd }}"
make -j`nproc` LLVM=1 ${{ matrix.kernel_make_cmd }} CCACHE="/usr/bin/ccache"
- name: Setup kernel source
uses: actions/checkout@v3
with:
repository: microsoft/WSA-Linux-Kernel
ref: android-lts/latte/${{ matrix.version }}
path: WSA-Linux-Kernel
- name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }}
uses: actions/upload-artifact@v3
with:
name: kernel-WSA-${{ matrix.arch }}-${{ matrix.version }}-${{ matrix.date }}
path: WSA-Linux-Kernel/${{ matrix.out_file }}
- name: Setup Ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: WSA-Kernel-${{ matrix.version }}-${{ matrix.arch }}
save: ${{ github.event_name != 'pull_request' }}
- name: Setup KernelSU
working-directory: WSA-Linux-Kernel
run: |
echo "[+] KernelSU setup"
KERNEL_ROOT=$GITHUB_WORKSPACE/WSA-Linux-Kernel
echo "[+] KERNEL_ROOT: $KERNEL_ROOT"
echo "[+] Copy KernelSU driver to $KERNEL_ROOT/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $KERNEL_ROOT/drivers/kernelsu
echo "[+] Add KernelSU driver to Makefile"
DRIVER_MAKEFILE=$KERNEL_ROOT/drivers/Makefile
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches"
cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.10/*.patch
echo "[+] KernelSU setup done."
- name: Build Kernel
working-directory: WSA-Linux-Kernel
run: |
cp configs/wsa/${{ matrix.make_config }}-5.10 .config
make -j`nproc` LLVM=1 ARCH=${{ matrix.arch }} CROSS_COMPILE=${{ matrix.cross_compile }} ${{ matrix.file_name }} CCACHE="/usr/bin/ccache"
- name: Upload kernel-${{ matrix.arch }}-${{ matrix.version }}
uses: actions/upload-artifact@v3
with:
name: kernel-WSA-${{ matrix.arch }}-${{ matrix.version }}
path: WSA-Linux-Kernel/arch/${{ matrix.arch }}/boot/${{ matrix.file_name }}

View File

@@ -1,186 +0,0 @@
name: Build Kernel 5.10
on:
push:
branches: [ "main" ]
paths:
- '.github/workflows/build-kernel-5.10.yml'
- 'kernel/**'
pull_request:
branches: [ "main" ]
paths:
- 'kernel/**'
jobs:
build:
strategy:
matrix:
include:
- version: android12-5.10-66
tag: android12-5.10-2021-11
os_version: 12.0.0
os_patch_level: 2021-11
- version: android12-5.10-81
tag: android12-5.10-2022-03
os_version: 12.0.0
os_patch_level: 2022-03
- version: android12-5.10-101
tag: android12-5.10-2022-05
os_version: 12.0.0
os_patch_level: 2022-05
- version: android12-5.10-110
tag: android12-5.10-2022-07
os_version: 12.0.0
os_patch_level: 2022-07
- version: android12-5.10-136
tag: android12-5.10-2022-11
os_version: 12.0.0
os_patch_level: 2022-11
name: Build aarch64-${{ matrix.version }}
runs-on: ubuntu-latest
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_MAXSIZE: "2G"
CCACHE_HARDLINK: "true"
steps:
- uses: actions/checkout@v3
with:
path: KernelSU
fetch-depth: 0
- uses: hendrikmuhs/ccache-action@v1.2
with:
key: ccache-aarch64-${{ matrix.version }}
append-timestamp: false
save: ${{ github.event_name != 'pull_request' }}
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Setup kernel source
run: |
cd $GITHUB_WORKSPACE
git clone https://gerrit.googlesource.com/git-repo
mkdir android-kernel && cd android-kernel
../git-repo/repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ matrix.tag }}
../git-repo/repo sync -j$(nproc --all)
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-${{ matrix.tag }}_r1.zip
unzip gki-kernel.zip
tools/mkbootimg/unpack_bootimg.py --boot_img=$(find . -maxdepth 1 -name "*.img")
- name: Setup KernelSU
env:
PATCH_PATH: "5.10"
run: |
cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup"
GKI_ROOT=$(pwd)
echo "[+] GKI_ROOT: $GKI_ROOT"
echo "[+] Copy kernel su driver to $GKI_ROOT/common/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
echo "[+] Add kernel su driver to Makefile"
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches"
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch
cd -
echo "[+] KernelSU setup Done."
- name: Symbol magic
run: |
echo "[+] Export all symbol from abi_gki_aarch64.xml"
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
echo "[+] Add KernelSU symbols"
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
- name: Set boot sign key
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
working-directory: android-kernel
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Build boot.img
working-directory: android-kernel
run: CCACHE="/usr/bin/ccache" BUILD_BOOT_IMG=1 SKIP_VENDOR_BOOT=1 KERNEL_BINARY=Image GKI_RAMDISK_PREBUILT_BINARY=out/ramdisk AVB_SIGN_BOOT_IMG=1 AVB_BOOT_PARTITION_SIZE=$((64*1024*1024)) AVB_BOOT_ALGORITHM=SHA256_RSA2048 AVB_BOOT_KEY=prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem BOOT_IMAGE_HEADER_VERSION=4 LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
- name: Build boot-lz4.img
working-directory: android-kernel
run: |
tools/mkbootimg/mkbootimg.py --header_version 4 --kernel ./out/android12-5.10/dist/Image.lz4 --ramdisk out/ramdisk --output ./out/android12-5.10/dist/boot-lz4.img --os_version ${{ matrix.os_version }} --os_patch_level ${{ matrix.os_patch_level }}
./build/build-tools/path/linux-x86/avbtool add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image out/android12-5.10/dist/boot-lz4.img --algorithm SHA256_RSA2048 --key ./prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
- name: Build boot-gz.img
working-directory: android-kernel
run: |
cat out/android12-5.10/dist/Image | ./prebuilts/build-tools/path/linux-x86/gzip -n -f -9 > out/android12-5.10/dist/Image.gz
tools/mkbootimg/mkbootimg.py --header_version 4 --kernel ./out/android12-5.10/dist/Image.gz --ramdisk out/ramdisk --output ./out/android12-5.10/dist/boot-gz.img --os_version ${{ matrix.os_version }} --os_patch_level ${{ matrix.os_patch_level }}
./build/build-tools/path/linux-x86/avbtool add_hash_footer --partition_name boot --partition_size $((64*1024*1024)) --image out/android12-5.10/dist/boot-gz.img --algorithm SHA256_RSA2048 --key ./prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
- name: Upload Image.gz
uses: actions/upload-artifact@v3
with:
name: kernel-aarch64-${{ matrix.version }}-Image.gz
path: android-kernel/out/android12-5.10/dist/Image.gz
- name: Upload boot.img
uses: actions/upload-artifact@v3
with:
name: kernel-aarch64-${{ matrix.version }}-boot.img
path: android-kernel/out/android12-5.10/dist/boot.img
- name: Upload boot-lz4.img
uses: actions/upload-artifact@v3
with:
name: kernel-aarch64-${{ matrix.version }}-boot-lz4.img
path: android-kernel/out/android12-5.10/dist/boot-lz4.img
- name: Upload boot-gz.img
uses: actions/upload-artifact@v3
with:
name: kernel-aarch64-${{ matrix.version }}-boot-gz.img
path: android-kernel/out/android12-5.10/dist/boot-gz.img
- name: Setup mutex for uploading
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
uses: ben-z/gh-action-mutex@v1.0-alpha-7
- name: Upload to telegram
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TITLE: kernel-aarch64-${{ matrix.version }}
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(git rev-list --count HEAD)
cd -
OUTDIR=android-kernel/out/android12-5.10/dist
IMAGE_GZ=${{ matrix.version }}-Image.gz
BOOT=${{ matrix.version }}-boot.img.zip
BOOT_LZ4=${{ matrix.version }}-boot-lz4.img.zip
BOOT_GZ=${{ matrix.version }}-boot-gz.img.zip
mv $OUTDIR/Image.gz $IMAGE_GZ
zip $BOOT -j -r $OUTDIR/boot.img
zip $BOOT_LZ4 -j -r $OUTDIR/boot-lz4.img
zip $BOOT_GZ -j -r $OUTDIR/boot-gz.img
pip3 install python-telegram-bot
python3 $GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py $IMAGE_GZ $BOOT $BOOT_LZ4 $BOOT_GZ
fi

View File

@@ -1,137 +0,0 @@
name: Build Kernel 5.15
on:
push:
branches: [ "main" ]
paths:
- '.github/workflows/build-kernel-5.15.yml'
- 'kernel/**'
pull_request:
branches: [ "main" ]
paths:
- 'kernel/**'
jobs:
build:
strategy:
matrix:
include:
- version: "android13-5.15-41"
tag: "android13-5.15-2022-11"
os_version: 13.0.0
os_version_level: "2022-11"
- version: "android13-5.15-74"
tag: "android13-5.15-2022-12"
os_version: 13.0.0
os_version_level: "2022-12"
name: Build aarch64-${{ matrix.version }}
runs-on: ubuntu-latest
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_MAXSIZE: "2G"
CCACHE_HARDLINK: "true"
steps:
- uses: actions/checkout@v3
with:
path: KernelSU
fetch-depth: 0
- uses: hendrikmuhs/ccache-action@v1.2
with:
key: ccache-aarch64-${{ matrix.version }}
append-timestamp: false
save: ${{ github.event_name != 'pull_request' }}
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Setup kernel source
run: |
cd $GITHUB_WORKSPACE
git clone https://gerrit.googlesource.com/git-repo
mkdir android-kernel && cd android-kernel
../git-repo/repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ matrix.tag }}
../git-repo/repo sync -j$(nproc --all)
curl -Lo gki-kernel.zip https://dl.google.com/android/gki/gki-certified-boot-${{ matrix.tag }}_r1.zip
unzip gki-kernel.zip
tools/mkbootimg/unpack_bootimg.py --boot_img=$(find . -maxdepth 1 -name "*.img")
- name: Setup KernelSU
env:
PATCH_PATH: "5.15"
run: |
cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup"
GKI_ROOT=$(pwd)
echo "[+] GKI_ROOT: $GKI_ROOT"
echo "[+] Copy kernel su driver to $GKI_ROOT/common/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
echo "[+] Add kernel su driver to Makefile"
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches"
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch
cd -
echo "[+] KernelSU setup Done."
- name: Symbol magic
run: |
echo "[+] Export all symbol from abi_gki_aarch64.xml"
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
echo "[+] Add KernelSU symbols"
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
- name: Set boot sign key
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
working-directory: android-kernel
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Build boot.img
working-directory: android-kernel
run: CCACHE="/usr/bin/ccache" BUILD_BOOT_IMG=1 SKIP_VENDOR_BOOT=1 KERNEL_BINARY=Image AVB_SIGN_BOOT_IMG=1 AVB_BOOT_PARTITION_SIZE=$((64*1024*1024)) AVB_BOOT_ALGORITHM=SHA256_RSA2048 AVB_BOOT_KEY=prebuilts/kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem BOOT_IMAGE_HEADER_VERSION=4 LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
- name: Upload boot.img
uses: actions/upload-artifact@v3
with:
name: kernel-aarch64-${{ matrix.version }}-boot.img
path: android-kernel/out/*/dist/boot.img
- name: Setup mutex for uploading
uses: ben-z/gh-action-mutex@v1.0-alpha-7
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
- name: Upload to telegram
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
CACHE_CHAT_ID: ${{ secrets.CACHE_CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TITLE: kernel-aarch64-${{ matrix.version }}
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(git rev-list --count HEAD)
cd -
OUTDIR=android-kernel/out/android13-5.15/dist
BOOT=${{ matrix.version }}-boot.img.zip
zip $BOOT -j -r $OUTDIR/boot.img
pip3 install python-telegram-bot
python3 $GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py $BOOT
fi

117
.github/workflows/build-kernel-a12.yml vendored Normal file
View File

@@ -0,0 +1,117 @@
name: Build Kernel - Android 12
on:
push:
branches: ["main", "ci"]
paths:
- ".github/workflows/build-kernel-a12.yml"
- ".github/workflows/gki-kernel.yml"
- ".github/scripts/build_a12.sh"
- "kernel/**"
pull_request:
branches: ["main"]
paths:
- ".github/workflows/build-kernel-a12.yml"
- ".github/workflows/gki-kernel.yml"
- ".github/scripts/build-a12.sh"
- "kernel/**"
workflow_call:
jobs:
build-kernel:
if: github.event_name != 'pull_request'
strategy:
matrix:
include:
- sub_level: 66
os_patch_level: 2021-11
- sub_level: 81
os_patch_level: 2022-03
- sub_level: 101
os_patch_level: 2022-05
- sub_level: 110
os_patch_level: 2022-07
- sub_level: 136
os_patch_level: 2022-11
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
version: android12-5.10
version_name: android12-5.10.${{ matrix.sub_level }}
tag: android12-5.10-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
patch_path: "5.10"
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
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 }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
- uses: actions/checkout@v3
with:
path: KernelSU
fetch-depth: 0
- name: List artifacts
run: |
tree
- name: Download prebuilt toolchain
run: |
AOSP_MIRROR=https://android.googlesource.com
BRANCH=master-kernel-build-2022
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
pip3 install python-telegram-bot
- name: Set boot sign key
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Setup mutex for uploading
uses: ben-z/gh-action-mutex@v1.0-alpha-7
- name: Build boot images
run: |
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
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) + 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
with:
version: android12-5.10
version_name: android12-5.10.101
tag: android12-5.10-2022-05
os_patch_level: 2022-05
patch_path: "5.10"

126
.github/workflows/build-kernel-a13.yml vendored Normal file
View File

@@ -0,0 +1,126 @@
name: Build Kernel - Android 13
on:
push:
branches: ["main", "ci"]
paths:
- ".github/workflows/build-kernel-a13.yml"
- ".github/workflows/gki-kernel.yml"
- ".github/scripts/build_a13.sh"
- "kernel/**"
pull_request:
branches: ["main"]
paths:
- ".github/workflows/build-kernel-a13.yml"
- ".github/workflows/gki-kernel.yml"
- ".github/scripts/build-a13.sh"
- "kernel/**"
workflow_call:
jobs:
build-kernel:
if: github.event_name != 'pull_request'
strategy:
matrix:
include:
- version: "5.10"
sub_level: 107
os_patch_level: 2022-11
- version: "5.10"
sub_level: 149
os_patch_level: 2023-01
- version: "5.15"
sub_level: 41
os_patch_level: 2022-11
- version: "5.15"
sub_level: 74
os_patch_level: 2022-12
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
version: android13-${{ matrix.version }}
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
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 }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
- uses: actions/checkout@v3
with:
path: KernelSU
fetch-depth: 0
- name: List artifacts
run: |
tree
- name: Download prebuilt toolchain
run: |
AOSP_MIRROR=https://android.googlesource.com
BRANCH=master-kernel-build-2022
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
pip3 install python-telegram-bot
- name: Set boot sign key
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Setup mutex for uploading
uses: ben-z/gh-action-mutex@v1.0-alpha-7
- name: Build boot images
run: |
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
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) + 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'
strategy:
matrix:
include:
- version: "5.10"
sub_level: 107
os_patch_level: 2022-11
- version: "5.15"
sub_level: 41
os_patch_level: 2022-11
uses: ./.github/workflows/gki-kernel.yml
with:
version: android13-${{ matrix.version }}
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}

View File

@@ -1,14 +1,16 @@
name: Build KSUD
on:
workflow_call:
push:
branches: [ "main" ]
branches: [ "main", "ci" ]
paths:
- '.github/workflows/build-ksud.yml'
- '.github/workflows/ksud.yml'
- 'userspace/ksud/**'
pull_request:
branches: [ "main" ]
paths:
- '.github/workflows/build-ksud.yml'
- '.github/workflows/ksud.yml'
- 'userspace/ksud/**'
jobs:
build:
@@ -17,37 +19,7 @@ jobs:
include:
- target: aarch64-linux-android
- target: x86_64-linux-android
name: Build KSUD
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up cargo cache
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-
- name: Set up ksud build cache
uses: actions/cache@v3
continue-on-error: false
with:
path: ./userspace/ksud/target/
key: ${{ runner.os }}-ksud-${{ hashFiles('**/Cargo.lock') }}-${{ matrix.target }}
restore-keys: ${{ runner.os }}-ksud-
- name: Build ksud
uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
args: --target ${{ matrix.target }} --release --manifest-path ./userspace/ksud/Cargo.toml
- name: Upload ksud artifact
uses: actions/upload-artifact@v3
with:
name: ksud-${{ matrix.target }}
path: ./userspace/ksud/target/**/release/ksud
- target: x86_64-pc-windows-gnu # only for build
uses: ./.github/workflows/ksud.yml
with:
target: ${{ matrix.target }}

View File

@@ -10,9 +10,17 @@ on:
branches: [ "main" ]
paths:
- 'manager/**'
workflow_call:
jobs:
build-ksud:
uses: ./.github/workflows/build-ksud.yml
strategy:
matrix:
include:
- target: aarch64-linux-android
- target: x86_64-linux-android
uses: ./.github/workflows/ksud.yml
with:
target: ${{ matrix.target }}
build-manager:
needs: build-ksud
runs-on: ubuntu-latest

22
.github/workflows/clippy-pr.yml vendored Normal file
View 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
View 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

152
.github/workflows/gki-kernel.yml vendored Normal file
View File

@@ -0,0 +1,152 @@
name: GKI Kernel Build
on:
workflow_call:
inputs:
version:
required: true
type: string
description: >
Output directory of gki,
for example: android12-5.10
version_name:
required: true
type: string
description: >
With SUBLEVEL of kernel,
for example: android12-5.10.66
tag:
required: true
type: string
description: >
Part of branch name of common kernel manifest,
for example: android12-5.10-2021-11
os_patch_level:
required: false
type: string
description: >
Patch level of common kernel manifest,
for example: 2021-11
default: 2022-05
patch_path:
required: true
type: string
description: >
Directory name of .github/patches/<patch_path>
for example: 5.10
use_cache:
required: false
type: boolean
default: true
embed_ksud:
required: false
type: string
default: ksud-aarch64-linux-android
description: >
Artifact name of prebuilt ksud to be embedded
for example: ksud-aarch64-linux-android
secrets:
BOOT_SIGN_KEY:
required: false
CHAT_ID:
required: false
CACHE_CHAT_ID:
required: false
BOT_TOKEN:
required: false
MESSAGE_THREAD_ID:
required: false
jobs:
build:
name: Build ${{ inputs.version_name }}
runs-on: ubuntu-latest
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_MAXSIZE: "2G"
CCACHE_HARDLINK: "true"
steps:
- uses: actions/checkout@v3
with:
path: KernelSU
fetch-depth: 0
- uses: hendrikmuhs/ccache-action@v1.2
if: inputs.use_cache == true
with:
key: ccache-aarch64-${{ inputs.version_name }}
append-timestamp: false
save: ${{ github.event_name != 'pull_request' }}
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Setup kernel source
run: |
cd $GITHUB_WORKSPACE
git clone https://gerrit.googlesource.com/git-repo
mkdir android-kernel && cd android-kernel
../git-repo/repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }}
../git-repo/repo sync -j$(nproc --all)
- name: Setup KernelSU
env:
PATCH_PATH: ${{ inputs.patch_path }}
run: |
cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup"
GKI_ROOT=$(pwd)
echo "[+] GKI_ROOT: $GKI_ROOT"
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
echo "[+] Add KernelSU driver to Makefile"
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
grep -q "kernelsu" $DRIVER_MAKEFILE || echo "obj-y += kernelsu/" >> $DRIVER_MAKEFILE
echo "[+] Apply KernelSU patches"
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch
echo "[+] KernelSU setup done."
- name: Symbol magic
run: |
echo "[+] Export all symbol from abi_gki_aarch64.xml"
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
echo "[+] Add KernelSU symbols"
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
- name: Build boot.img
working-directory: android-kernel
run: CCACHE="/usr/bin/ccache" LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
- name: Prepare artifacts
id: prepareArtifacts
run: |
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
mkdir output
cp $OUTDIR/Image ./output/
cp $OUTDIR/Image.lz4 ./output/
git clone https://github.com/Kernel-SU/AnyKernel3
rm -rf ./AnyKernel3/.git
cp $OUTDIR/Image ./AnyKernel3/
- name: Upload Image and Image.gz
uses: actions/upload-artifact@v3
with:
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./output/*
- name: Upload AnyKernel3
uses: actions/upload-artifact@v3
with:
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./AnyKernel3/*

36
.github/workflows/ksud.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Build ksud
on:
workflow_call:
inputs:
target:
required: true
type: string
use_cache:
required: false
type: boolean
default: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222
- run: rustup default 1.67.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
- name: Upload ksud artifact
uses: actions/upload-artifact@v3
with:
name: ksud-${{ inputs.target }}
path: userspace/ksud/target/**/release/ksud

56
.github/workflows/release.yml vendored Normal file
View 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
View 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
View 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

View File

@@ -2,58 +2,37 @@
# KernelSU
A Kernel based root solution for Android GKI.
A Kernel based root solution for Android devices.
## Before Reading
## Features
Now KernelSU supports old kernel under 5.10, but **THERE WILL NEVER** be a CI for old kernels, because they are not generic.
ANY ISSUES ABOUT HOW TO COMPILE A OLD KERNEL WILL NOT BE ANSWERED AND WILL BE CLOSED.
KernelSU is in a early development stage and you should not put it into production enviroment. KernelSU developers will not be responsible for any of your losses.
If you face any issue, feel free to open a [issue](https://github.com/tiann/KernelSU/issues) and tell us about it!
1. Kernel-based `su` and root access management.
2. Module system based on overlayfs.
## Compatibility State
Now KernelSU will work on these version of kernels without any modification
KernelSU officially supports Android GKI 2.0 devices(with kernel 5.10+), old kernels(4.14+) is also compatiable, but you need to build kernel yourself.
- `5.15`
- `5.10`
- `5.4`
- `4.19`
- `4.14`
WSA and containter-based Android should also work with KernelSU integrated.
And the current supported ABIs are : `arm64-v8a` & `x86_64`
If you confirm KernelSU works on other version, open a [issue](https://github.com/tiann/KernelSU/issues) tell us about it!
And the current supported ABIs are : `arm64-v8a` and `x86_64`
## Usage
1. Flash a custom kernel with KernelSU, you can build it yourself or [download it from CI](https://github.com/tiann/KernelSU/actions).
2. Install Manager App and enjoy :)
For old kernels under 5.10, you must build custom kernels by yourself.
[Installation](https://kernelsu.org/guide/installation.html)
## Build
### Build GKI Kernel
1. Download the GKI source first, you can refer the [GKI build instruction](https://source.android.com/docs/setup/build/building-kernels)
2. cd `<GKI kernel source dir>`
3. `curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -`
4. Build the kernel.
### Build the Manager App
Android Studio / Gradle
[How to build?](https://kernelsu.org/guide/how-to-build.html)
### Discussion
[@KernelSU](https://t.me/KernelSU)
- Telegram: [@KernelSU](https://t.me/KernelSU)
## 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

View File

@@ -2,58 +2,37 @@
# KernelSU
一个基于内核,为安卓 GKI 准备的 root 方案。
一个 Android 上基于内核的 root 方案。
## 阅读之前
## 特性
现在 KernelSU 已经支持 5.10 以下的旧内核,但**永远不会**有旧内核的 CI因为它们不是通用的
**任何关于如何编译旧内核的问题都不会得到任何答复并将被关闭。**
KernelSU 还处于早期开发阶段你不应该生产环境中使用它。KernelSU 的开发者将不对你的任何损失负责。
如果你遇到任何问题,请打开 [issue](https://github.com/tiann/KernelSU/issues) 告诉我们! (最好使用英语)
- 基于内核的 su 和权限管理
- 基于 overlayfs 的模块系统。
## 兼容状态
现在,KernelSU 可以在这些版本的内核上正常工作,不需要任何修改
KernelSU 官方支持 GKI 2.0 的设备内核版本5.10以上旧内核也是兼容的最低4.14+,不需要自己编译内核
- `5.15`
- `5.10`
- `5.4`
- `4.19`
- `4.14`
WSA 和运行在容器上的 Android 也可以与 KernelSU 一起工作。
目前支持架构 : `arm64-v8a` & `x86_64`
如果你确认 KernelSU 能在其他版本上工作,请打开一个 [issue](https://github.com/tiann/KernelSU/issues) 告诉我们!
目前支持架构 : `arm64-v8a` `x86_64`
## 使用方法
1. 用 KernelSU 刷入一个自定义的内核,你可以自己构建它或者[从 CI 下载](https://github.com/tiann/KernelSU/actions)
2. 安装管理器应用, 然后享受吧 :)
对于 5.10 以下的旧内核, 你必须自己构建。
[安装教程](https://kernelsu.org/zh_CN/guide/installation.html)
## 构建
### 构建GKI内核
1. 首先下载 GKI 源代码,你可以参考[ GKI 构建说明](https://source.android.com/docs/setup/build/building-kernels)。
2. cd `< GKI 内核源代码目录 >`
3. `curl -LSs "https://raw.githubusercontent.com/tiann/KernelSU/main/kernel/setup.sh" | bash -`
4. 构建内核。
### 构建管理器应用
Android Studio / Gradle
[如何构建?](https://kernelsu.org/zh_CN/guide/how-to-build.html)
### 讨论
[@KernelSU](https://t.me/KernelSU)
- Telegram: [@KernelSU](https://t.me/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
View 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.

View File

@@ -8,8 +8,15 @@ obj-y += uid_observer.o
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)
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH=$$PATH:/usr/bin:/usr/local/bin git rev-list --count HEAD)
ccflags-y += -DKSU_GIT_VERSION=$(KSU_GIT_VERSION)
endif
ifndef EXPECTED_SIZE
EXPECTED_SIZE := 0x033b

View File

@@ -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,21 +159,7 @@ 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);
if (IS_ERR(fp)) {
int errno = PTR_ERR(fp);
pr_err("load_allow_list open '/data/adb': %d\n", PTR_ERR(fp));
if (errno == -ENOENT) {
msleep(2000);
ksu_queue_work(&ksu_load_work);
return;
} else {
pr_info("load_allow list dir exist now!");
}
} else {
filp_close(fp, 0);
}
KWORKER_INSTALL_KEYRING();
// load allowlist now!
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
@@ -194,13 +181,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 +198,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);

View File

@@ -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);
for (i = 0; i < size4; ++i) {
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
hash = 31 * hash + c;
}
offset += size4;
@@ -98,8 +103,8 @@ check_v2_signature(char *path, unsigned expected_size, unsigned expected_hash)
if (size4 == expected_size) {
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
kernel_read(fp, &c, 0x1, &pos);
for (i = 0; i < size4; ++i) {
ksu_kernel_read_compat(fp, &c, 0x1, &pos);
hash = 31 * hash + c;
}
offset += size4;
@@ -172,4 +177,4 @@ int is_manager_apk(char *path)
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
}
#endif
#endif

View File

@@ -22,6 +22,9 @@
#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);
static inline bool is_allow_su()
{
@@ -197,9 +200,9 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
u32 version = KERNEL_SU_VERSION;
if (copy_to_user(arg3, &version, sizeof(version))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
return 0;
}
}
return 0;
}
if (arg2 == CMD_REPORT_EVENT) {
@@ -230,6 +233,51 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
return 0;
}
if (arg2 == CMD_SET_SEPOLICY) {
if (!handle_sepolicy(arg3, arg4)) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("sepolicy: prctl reply error\n");
}
}
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");
}
}
}
return 0;
}
// all other cmds are for 'root manager'
if (!is_manager()) {
pr_info("Only manager can do cmd: %d\n", arg2);
@@ -248,25 +296,7 @@ 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;
}
return 0;
@@ -344,7 +374,23 @@ 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)
{
@@ -354,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)

5
kernel/embed_ksud.c Normal file
View File

@@ -0,0 +1,5 @@
// WARNING: THIS IS A STUB FILE
// This file will be regenerated by CI
unsigned int ksud_size = 0;
const char ksud[0] = {};

View File

@@ -21,4 +21,8 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
void *envp, int *flags);
#endif
// For volume button
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
int *value);
#endif

34
kernel/kernel_compat.c Normal file
View 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
View 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

View File

@@ -3,7 +3,12 @@
#include "linux/workqueue.h"
#define KERNEL_SU_VERSION 11
#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
@@ -15,6 +20,8 @@
#define CMD_GET_ALLOW_LIST 5
#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

View File

@@ -1,18 +1,22 @@
#include "asm/current.h"
#include "linux/string.h"
#include "linux/cred.h"
#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 +24,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)
@@ -56,8 +63,10 @@ void on_post_fs_data(void)
return;
}
done = true;
pr_info("ksu_load_allow_list");
pr_info("on_post_fs_data!");
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,
@@ -105,6 +114,35 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
return 0;
}
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
static struct file_operations fops_proxy;
static ssize_t read_count_append = 0;
static ssize_t read_proxy(struct file *file, char __user *buf, size_t count,
loff_t *pos)
{
bool first_read = file->f_pos == 0;
ssize_t ret = orig_read(file, buf, count, pos);
if (first_read) {
pr_info("read_proxy append %ld + %ld", ret, read_count_append);
ret += read_count_append;
}
return ret;
}
static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
{
bool first_read = iocb->ki_pos == 0;
ssize_t ret = orig_read_iter(iocb, to);
if (first_read) {
pr_info("read_iter_proxy append %ld + %ld", ret,
read_count_append);
ret += read_count_append;
}
return ret;
}
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos)
{
@@ -177,12 +215,80 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
return 0;
}
// we've succeed to insert ksud.rc, now we need to proxy the read and modify the result!
// But, we can not modify the file_operations directly, because it's in read-only memory.
// We just replace the whole file_operations with a proxy one.
memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations));
orig_read = file->f_op->read;
if (orig_read) {
fops_proxy.read = read_proxy;
}
orig_read_iter = file->f_op->read_iter;
if (orig_read_iter) {
fops_proxy.read_iter = read_iter_proxy;
}
// replace the file_operations
file->f_op = &fops_proxy;
read_count_append = rc_count;
*buf_ptr = buf + rc_count;
*count_ptr = count - rc_count;
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 +314,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 +339,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 +353,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 +380,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 +407,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
}

View File

@@ -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

View File

@@ -1,9 +1,13 @@
#include "linux/uaccess.h"
#include "linux/types.h"
#include "linux/version.h"
#include "../klog.h" // IWYU pragma: keep
#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
@@ -11,18 +15,14 @@
#define KERNEL_SU_DOMAIN "su"
#define KERNEL_SU_FILE "ksu_file"
#define KERNEL_EXEC_TYPE "ksu_exec"
#define ALL NULL
void apply_kernelsu_rules()
static struct policydb *get_policydb(void)
{
struct policydb *db;
if (!getenforce()) {
pr_info("SELinux permissive or disabled, don't apply rules.");
return;
}
rcu_read_lock();
// 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;
@@ -30,6 +30,21 @@ void apply_kernelsu_rules()
struct selinux_ss *ss = rcu_dereference(selinux_state.ss);
db = &ss->policydb;
#endif
#else
db = &policydb;
#endif
return db;
}
void apply_kernelsu_rules()
{
if (!getenforce()) {
pr_info("SELinux permissive or disabled, don't apply rules.");
return;
}
rcu_read_lock();
struct policydb *db = get_policydb();
ksu_permissive(db, KERNEL_SU_DOMAIN);
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
@@ -51,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
@@ -61,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
@@ -108,4 +125,341 @@ void apply_kernelsu_rules()
"write");
rcu_read_unlock();
}
}
#define MAX_SEPOL_LEN 128
#define CMD_NORMAL_PERM 1
#define CMD_XPERM 2
#define CMD_TYPE_STATE 3
#define CMD_TYPE 4
#define CMD_TYPE_ATTR 5
#define CMD_ATTR 6
#define CMD_TYPE_TRANSITION 7
#define CMD_TYPE_CHANGE 8
#define CMD_GENFSCON 9
struct sepol_data {
u32 cmd;
u32 subcmd;
char __user *sepol1;
char __user *sepol2;
char __user *sepol3;
char __user *sepol4;
char __user *sepol5;
char __user *sepol6;
char __user *sepol7;
};
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
char **object)
{
if (!user_object) {
*object = ALL;
return 0;
}
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
return -1;
}
*object = buf;
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) {
return -1;
}
if (!getenforce()) {
pr_info("SELinux permissive or disabled, don't apply policies.");
return 0;
}
struct sepol_data data;
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
pr_err("sepol: copy sepol_data failed.\n");
return -1;
}
u32 cmd = data.cmd;
u32 subcmd = data.subcmd;
rcu_read_lock();
struct policydb *db = get_policydb();
int ret = -1;
if (cmd == CMD_NORMAL_PERM) {
char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN];
char perm_buf[MAX_SEPOL_LEN];
char *s, *t, *c, *p;
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
0) {
pr_err("sepol: copy perm failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_allow(db, s, t, c, p);
} else if (subcmd == 2) {
success = ksu_deny(db, s, t, c, p);
} else if (subcmd == 3) {
success = ksu_auditallow(db, s, t, c, p);
} else if (subcmd == 4) {
success = ksu_dontaudit(db, s, t, c, p);
} else {
pr_err("sepol: unknown subcmd: %d", subcmd);
}
ret = success ? 0 : -1;
} else if (cmd == CMD_XPERM) {
char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN];
char __maybe_unused
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
char perm_set[MAX_SEPOL_LEN];
char *s, *t, *c;
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(operation, data.sepol4,
sizeof(operation)) < 0) {
pr_err("sepol: copy operation failed.\n");
goto exit;
}
if (strncpy_from_user(perm_set, data.sepol5, sizeof(perm_set)) <
0) {
pr_err("sepol: copy perm_set failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_allowxperm(db, s, t, c, perm_set);
} else if (subcmd == 2) {
success = ksu_auditallowxperm(db, s, t, c, perm_set);
} else if (subcmd == 3) {
success = ksu_dontauditxperm(db, s, t, c, perm_set);
} else {
pr_err("sepol: unknown subcmd: %d", subcmd);
}
ret = success ? 0 : -1;
} else if (cmd == CMD_TYPE_STATE) {
char src[MAX_SEPOL_LEN];
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_permissive(db, src);
} else if (subcmd == 2) {
success = ksu_enforce(db, src);
} else {
pr_err("sepol: unknown subcmd: %d", subcmd);
}
if (success)
ret = 0;
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
char type[MAX_SEPOL_LEN];
char attr[MAX_SEPOL_LEN];
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
pr_err("sepol: copy type failed.\n");
goto exit;
}
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n");
goto exit;
}
bool success = false;
if (cmd == CMD_TYPE) {
success = ksu_type(db, type, attr);
} else {
success = ksu_typeattribute(db, type, attr);
}
if (!success) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
} else if (cmd == CMD_ATTR) {
char attr[MAX_SEPOL_LEN];
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n");
goto exit;
}
if (!ksu_attribute(db, attr)) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
} else if (cmd == CMD_TYPE_TRANSITION) {
char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN];
char object[MAX_SEPOL_LEN];
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(default_type, data.sepol4,
sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n");
goto exit;
}
char *real_object;
if (data.sepol5 == NULL) {
real_object = NULL;
} else {
if (strncpy_from_user(object, data.sepol5,
sizeof(object)) < 0) {
pr_err("sepol: copy object failed.\n");
goto exit;
}
real_object = object;
}
bool success = ksu_type_transition(db, src, tgt, cls,
default_type, real_object);
if (success)
ret = 0;
} else if (cmd == CMD_TYPE_CHANGE) {
char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN];
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(default_type, data.sepol4,
sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_type_change(db, src, tgt, cls,
default_type);
} else if (subcmd == 2) {
success = ksu_type_member(db, src, tgt, cls,
default_type);
} else {
pr_err("sepol: unknown subcmd: %d", subcmd);
}
if (success)
ret = 0;
} else if (cmd == CMD_GENFSCON) {
char name[MAX_SEPOL_LEN];
char path[MAX_SEPOL_LEN];
char context[MAX_SEPOL_LEN];
if (strncpy_from_user(name, data.sepol1, sizeof(name)) < 0) {
pr_err("sepol: copy name failed.\n");
goto exit;
}
if (strncpy_from_user(path, data.sepol2, sizeof(path)) < 0) {
pr_err("sepol: copy path failed.\n");
goto exit;
}
if (strncpy_from_user(context, data.sepol3, sizeof(context)) <
0) {
pr_err("sepol: copy context failed.\n");
goto exit;
}
if (!ksu_genfscon(db, name, path, context)) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
} else {
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;
}

View File

@@ -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;
}
}

View File

@@ -1,14 +1,13 @@
#include "sepolicy.h"
#include "linux/gfp.h"
#include "linux/printk.h"
#include "linux/slab.h"
#include "linux/version.h"
#include "../klog.h" // IWYU pragma: keep
#include "ss/symtab.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
// TODO: backport to lower kernel
#define KSU_SUPPORT_ADD_TYPE
#endif
//////////////////////////////////////////////////////
// Declaration
@@ -36,11 +35,12 @@ static bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
static bool add_type_rule(struct policydb *db, const char *s, const char *t,
const char *c, const char *d, int effect);
static bool add_filename_trans(const char *s, const char *t, const char *c,
const char *d, const char *o);
static bool add_filename_trans(struct policydb *db, const char *s,
const char *t, const char *c, const char *d,
const char *o);
static bool add_genfscon(const char *fs_name, const char *path,
const char *context);
static bool add_genfscon(struct policydb *db, const char *fs_name,
const char *path, const char *context);
static bool add_type(struct policydb *db, const char *type_name, bool attr);
@@ -79,6 +79,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) \
@@ -125,14 +126,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;
}
@@ -453,14 +453,151 @@ static bool add_type_rule(struct policydb *db, const char *s, const char *t,
return true;
}
static bool add_filename_trans(const char *s, const char *t, const char *c,
const char *d, const char *o)
// 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)
{
return false;
const struct filename_trans_key *ft = k;
unsigned long hash;
unsigned int byte_num;
unsigned char focus;
hash = ft->ttype ^ ft->tclass;
byte_num = 0;
while ((focus = ft->name[byte_num++]))
hash = partial_name_hash(focus, hash);
return hash;
}
static bool add_genfscon(const char *fs_name, const char *path,
const char *context)
static int filenametr_cmp(const void *k1, const void *k2)
{
const struct filename_trans_key *ft1 = k1;
const struct filename_trans_key *ft2 = k2;
int v;
v = ft1->ttype - ft2->ttype;
if (v)
return v;
v = ft1->tclass - ft2->tclass;
if (v)
return v;
return strcmp(ft1->name, ft2->name);
}
static const struct hashtab_key_params filenametr_key_params = {
.hash = filenametr_hash,
.cmp = filenametr_cmp,
};
#endif
static bool add_filename_trans(struct policydb *db, const char *s,
const char *t, const char *c, const char *d,
const char *o)
{
struct type_datum *src, *tgt, *def;
struct class_datum *cls;
src = symtab_search(&db->p_types, s);
if (src == NULL) {
pr_warn("source type %s does not exist\n", s);
return false;
}
tgt = symtab_search(&db->p_types, t);
if (tgt == NULL) {
pr_warn("target type %s does not exist\n", t);
return false;
}
cls = symtab_search(&db->p_classes, c);
if (cls == NULL) {
pr_warn("class %s does not exist\n", c);
return false;
}
def = symtab_search(&db->p_types, d);
if (def == NULL) {
pr_warn("default type %s does not exist\n", d);
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
trans->otype = def->value;
return true;
}
if (trans->otype == def->value)
break;
last = trans;
trans = trans->next;
}
if (trans == NULL) {
trans = (struct filename_trans_datum*) kcalloc(sizeof(*trans), 1, GFP_ATOMIC);
struct filename_trans_key *new_key =
(struct filename_trans_key*) kmalloc(sizeof(*new_key), GFP_ATOMIC);
*new_key = key;
new_key->name = kstrdup(key.name, GFP_ATOMIC);
trans->next = last;
trans->otype = def->value;
hashtab_insert(&db->filename_trans, new_key,
trans, filenametr_key_params);
}
db->compat_filename_trans_count++;
return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
#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
}
static bool add_genfscon(struct policydb *db, const char *fs_name,
const char *path, const char *context)
{
return false;
}
@@ -475,7 +612,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");
@@ -484,6 +621,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) {
@@ -496,6 +634,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));
@@ -541,6 +680,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
@@ -713,23 +958,28 @@ bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def, const char *obj)
{
return false;
if (obj) {
return add_filename_trans(db, src, tgt, cls, def, obj);
} else {
return add_type_rule(db, src, tgt, cls, def, AVTAB_TRANSITION);
}
}
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def)
{
return false;
return add_type_rule(db, src, tgt, cls, def, AVTAB_CHANGE);
}
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def)
{
return false;
return add_type_rule(db, src, tgt, cls, def, AVTAB_MEMBER);
}
// File system labeling
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
const char *ctx)
{
return false;
}
return add_genfscon(db, fs_name, path, ctx);
}

View File

@@ -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" || printf "\nobj-y += kernelsu/\n" >> "$DRIVER_MAKEFILE"
echo "[+] Done."
echo '[+] Done.'

View File

@@ -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,

View File

@@ -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);

View File

@@ -62,10 +62,10 @@ dependencies {
val accompanistVersion = "0.28.0"
val composeDestinationsVersion = "1.7.27-beta"
implementation(platform("androidx.compose:compose-bom:2022.12.00"))
debugImplementation("androidx.compose.ui:ui-test-manifest")
debugImplementation("androidx.compose.ui:ui-tooling")
implementation("androidx.activity:activity-compose:1.6.1")
implementation("androidx.compose.material:material:1.4.0-beta02")
implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui")
@@ -75,7 +75,6 @@ dependencies {
implementation("androidx.navigation:navigation-compose:2.5.3")
implementation("com.google.accompanist:accompanist-drawablepainter:$accompanistVersion")
implementation("com.google.accompanist:accompanist-navigation-animation:$accompanistVersion")
implementation("com.google.accompanist:accompanist-swiperefresh:$accompanistVersion")
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
implementation("io.github.raamcosta.compose-destinations:animations-core:$composeDestinationsVersion")
@@ -83,7 +82,7 @@ dependencies {
implementation("me.zhanghai.android.appiconloader:appiconloader-coil:1.5.0")
implementation("com.github.topjohnwu.libsu:core:5.0.3")
implementation("com.github.alorma:compose-settings-ui-m3:0.15.0")
implementation("com.github.alorma:compose-settings-ui-m3:0.22.0")
ksp("io.github.raamcosta.compose-destinations:ksp:$composeDestinationsVersion")

View File

@@ -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>

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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
@@ -15,12 +16,14 @@ import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.ramcosta.composedestinations.DestinationsNavHost
import me.weishu.kernelsu.ui.component.rememberDialogHostState
import me.weishu.kernelsu.ui.screen.BottomBarDestination
import me.weishu.kernelsu.ui.screen.NavGraphs
import me.weishu.kernelsu.ui.screen.appCurrentDestinationAsState
import me.weishu.kernelsu.ui.screen.destinations.Destination
import me.weishu.kernelsu.ui.screen.startAppDestination
import me.weishu.kernelsu.ui.theme.KernelSUTheme
import me.weishu.kernelsu.ui.util.LocalDialogHost
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
class MainActivity : ComponentActivity() {
@@ -37,7 +40,10 @@ class MainActivity : ComponentActivity() {
bottomBar = { BottomBar(navController) },
snackbarHost = { SnackbarHost(snackbarHostState) }
) { innerPadding ->
CompositionLocalProvider(LocalSnackbarHost provides snackbarHostState) {
CompositionLocalProvider(
LocalSnackbarHost provides snackbarHostState,
LocalDialogHost provides rememberDialogHostState(),
) {
DestinationsNavHost(
modifier = Modifier.padding(innerPadding),
navGraph = NavGraphs.root,
@@ -52,31 +58,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

View File

@@ -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)
}
}
},
)
}

View File

@@ -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()
}
}
)
}

View File

@@ -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() {

View File

@@ -8,8 +8,12 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -17,23 +21,22 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.ramcosta.composedestinations.annotation.Destination
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.screen.destinations.InstallScreenDestination
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
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)
@@ -41,9 +44,6 @@ import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
@Composable
fun ModuleScreen(navigator: DestinationsNavigator) {
val viewModel = viewModel<ModuleViewModel>()
val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
if (viewModel.moduleList.isEmpty()) {
@@ -51,11 +51,18 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
}
}
Scaffold(
topBar = {
TopBar()
},
floatingActionButton = {
val isSafeMode = Natives.isSafeMode()
val isKSUVersionInvalid = Natives.getVersion() < 0
val hasMagisk = hasMagisk()
val hideInstallButton = isSafeMode || isKSUVersionInvalid || hasMagisk
Scaffold(topBar = {
TopBar()
}, floatingActionButton = if (hideInstallButton) {
{ /* Empty */ }
} else {
{
val moduleInstall = stringResource(id = R.string.module_install)
val selectZipLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
@@ -81,87 +88,170 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
icon = { Icon(Icons.Filled.Add, moduleInstall) },
text = { Text(text = moduleInstall) },
)
},
) { innerPadding ->
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)
val successUninstall = stringResource(R.string.module_uninstall_success)
val swipeState = rememberSwipeRefreshState(viewModel.isRefreshing)
// TODO: Replace SwipeRefresh with RefreshIndicator when it's ready
if (Natives.getVersion() < 8) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.require_kernel_version_8))
}
return@Scaffold
}
SwipeRefresh(
state = swipeState,
onRefresh = {
scope.launch { viewModel.fetchModuleList() }
},
modifier = Modifier
.padding(innerPadding)
.padding(16.dp)
.fillMaxSize()
) {
val isEmpty = viewModel.moduleList.isEmpty()
if (isEmpty) {
swipeState.isRefreshing = false
}) { innerPadding ->
ConfirmDialog()
when {
isKSUVersionInvalid -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.module_empty))
Text(stringResource(R.string.require_kernel_version_8))
}
} else {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(15.dp)
}
hasMagisk -> {
Box(
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
contentAlignment = Alignment.Center
) {
Text(
stringResource(R.string.module_magisk_conflict),
textAlign = TextAlign.Center,
)
}
}
else -> {
ModuleList(
viewModel = viewModel,
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
)
}
}
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier) {
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)
val successUninstall = stringResource(R.string.module_uninstall_success)
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)
val dialogHost = LocalDialogHost.current
val snackBarHost = LocalSnackbarHost.current
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
val dialogResult = dialogHost.showDialog(
moduleStr,
content = moduleUninstallConfirm.format(module.name),
confirm = uninstall,
dismiss = cancel
)
if (dialogResult != DialogResult.Confirmed) {
return
}
val success = uninstallModule(module.id)
if (success) {
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()
}
}
val refreshState = rememberPullRefreshState(
refreshing = viewModel.isRefreshing,
onRefresh = { viewModel.fetchModuleList() }
)
Box(modifier.pullRefresh(refreshState)) {
if (viewModel.isOverlayAvailable) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = remember {
PaddingValues(
start = 16.dp,
top = 16.dp,
end = 16.dp,
bottom = 16.dp
+ 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */
)
},
) {
val isEmpty = viewModel.moduleList.isEmpty()
if (isEmpty) {
item {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(stringResource(R.string.module_empty))
}
}
} else {
items(viewModel.moduleList) { module ->
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
ModuleItem(module,
isChecked,
onUninstall = {
val scope = rememberCoroutineScope()
ModuleItem(module, isChecked, onUninstall = {
scope.launch { onModuleUninstall(module) }
}, onCheckChanged = {
val success = toggleModule(module.id, !isChecked)
if (success) {
isChecked = it
scope.launch {
val result = uninstallModule(module.id)
if (result) {
viewModel.fetchModuleList()
}
snackBarHost.showSnackbar(
if (result) {
successUninstall.format(module.name)
} else {
failedUninstall.format(module.name)
}
viewModel.fetchModuleList()
val result = snackBarHost.showSnackbar(
rebootToApply, actionLabel = reboot
)
}
},
onCheckChanged = {
val success = toggleModule(module.id, !isChecked)
if (success) {
isChecked = it
scope.launch {
viewModel.fetchModuleList()
if (result == SnackbarResult.ActionPerformed) {
reboot()
}
} else scope.launch {
val message = if (isChecked) failedDisable else failedEnable
snackBarHost.showSnackbar(message.format(module.name))
}
} 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))
}
}
}
} else {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.module_overlay_fs_not_available))
}
}
PullRefreshIndicator(
refreshing = viewModel.isRefreshing,
state = refreshState,
modifier = Modifier.align(
Alignment.TopCenter
)
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar() {
TopAppBar(
title = { Text(stringResource(R.string.module)) }
)
TopAppBar(title = { Text(stringResource(R.string.module)) })
}
@Composable

View File

@@ -1,21 +1,28 @@
package me.weishu.kernelsu.ui.screen
import android.content.Intent
import android.net.Uri
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.BuildConfig
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SimpleDialog
import me.weishu.kernelsu.ui.util.LinkifyText
import me.weishu.kernelsu.ui.util.LocalDialogHost
import me.weishu.kernelsu.ui.util.getBugreportFile
/**
* @author weishu
@@ -34,46 +41,45 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
) { paddingValues ->
var openDialog by remember { mutableStateOf(false) }
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 {
SupportCard()
}
Column(modifier = Modifier.padding(paddingValues)) {
SettingsSwitch(
title = {
Text(stringResource(id = R.string.settings_system_rw))
},
subtitle = {
Text(stringResource(id = R.string.settings_system_rw_summary))
}
)
val context = LocalContext.current
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()
val dialogHost = LocalDialogHost.current
SettingsMenuLink(title = {
Text(about)
},
onClick = {
scope.launch {
dialogHost.showDialog(about, content = "unused", confirm = ok)
}
}
)
}

View File

@@ -1,35 +1,25 @@
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.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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
import kotlinx.coroutines.launch
import me.weishu.kernelsu.Natives
@@ -37,10 +27,9 @@ 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)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Destination
@Composable
fun SuperUserScreen() {
@@ -60,24 +49,61 @@ 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 ->
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
// TODO: Replace SwipeRefresh with RefreshIndicator when it's ready
SwipeRefresh(
state = rememberSwipeRefreshState(viewModel.isRefreshing),
onRefresh = {
scope.launch { viewModel.fetchAppList() }
},
val refreshState = rememberPullRefreshState(
refreshing = viewModel.isRefreshing,
onRefresh = { scope.launch { viewModel.fetchAppList() } },
)
Box(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
.pullRefresh(refreshState)
) {
LazyColumn {
items(viewModel.appList) { app ->
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
LazyColumn(Modifier.fillMaxSize()) {
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)
@@ -89,6 +115,12 @@ fun SuperUserScreen() {
}
}
}
PullRefreshIndicator(
refreshing = viewModel.isRefreshing,
state = refreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
}
}
}

View File

@@ -10,6 +10,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import androidx.core.view.ViewCompat
import com.google.accompanist.systemuicontroller.rememberSystemUiController
@@ -47,6 +48,12 @@ fun KernelSUTheme(
color = colorScheme.surface,
darkIcons = !darkTheme
)
// To match the App Navbar color
systemUiController.setNavigationBarColor(
color = colorScheme.surfaceColorAtElevation(8.dp),
darkIcons = !darkTheme,
)
}
MaterialTheme(
@@ -54,4 +61,4 @@ fun KernelSUTheme(
typography = Typography,
content = content
)
}
}

View File

@@ -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")
}

View File

@@ -102,4 +102,17 @@ fun reboot(reason: String = "") {
ShellUtils.fastCmd(shell, "/system/bin/input keyevent 26")
}
ShellUtils.fastCmd(shell, "/system/bin/svc power reboot $reason || /system/bin/reboot $reason")
}
fun overlayFsAvailable(): Boolean {
val shell = createRootShell()
// check /proc/filesystems
return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay")
}
fun hasMagisk(): Boolean {
val shell = createRootShell()
val result = shell.newJob().add("nsenter --mount=/proc/1/ns/mnt which magisk").exec()
Log.i(TAG, "has magisk: ${result.isSuccess}")
return result.isSuccess
}

View File

@@ -0,0 +1,87 @@
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 pstoreFile = File(bugreportDir, "pstore.tar.gz")
val diagFile = File(bugreportDir, "diag.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 propFile = File(bugreportDir, "props.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("tar -czf ${pstoreFile.absolutePath} /sys/fs/pstore").exec()
shell.newJob().add("tar -czf ${diagFile.absolutePath} /data/vendor/diag").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 > ${ksuFileTree.absolutePath}").exec()
shell.newJob().add("cat /data/system/packages.list > ${appListFile.absolutePath}").exec()
shell.newJob().add("getprop > ${propFile.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
}

View File

@@ -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
@@ -8,9 +7,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.launch
import me.weishu.kernelsu.ui.util.listModules
import me.weishu.kernelsu.ui.util.overlayFsAvailable
import org.json.JSONArray
import java.text.Collator
import java.util.*
@@ -37,6 +38,9 @@ class ModuleViewModel : ViewModel() {
var isRefreshing by mutableStateOf(false)
private set
var isOverlayAvailable by mutableStateOf(overlayFsAvailable())
private set
val moduleList by derivedStateOf {
val comparator = compareBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
modules.sortedWith(comparator).also {
@@ -44,12 +48,16 @@ class ModuleViewModel : ViewModel() {
}
}
suspend fun fetchModuleList() {
withContext(Dispatchers.IO) {
fun fetchModuleList() {
viewModelScope.launch(Dispatchers.IO) {
isRefreshing = true
val oldModuleList = modules
val start = SystemClock.elapsedRealtime()
kotlin.runCatching {
isOverlayAvailable = overlayFsAvailable()
val result = listModules()
@@ -74,8 +82,14 @@ class ModuleViewModel : ViewModel() {
}.toList()
}.onFailure { e ->
Log.e(TAG, "fetchModuleList: ", e)
isRefreshing = false
}
// when both old and new is kotlin.collections.EmptyList
// moduleList update will don't trigger
if (oldModuleList === modules) {
isRefreshing = false
}
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules")
}

View File

@@ -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
}
}

View File

@@ -0,0 +1,55 @@
<?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="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>
<string name="module_magisk_conflict">Module akan di nonaktifkan karna konflik dengan magisk\'s!</string>
</resources>

View File

@@ -0,0 +1,54 @@
<?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="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>

View File

@@ -0,0 +1,56 @@
<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="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>

View File

@@ -0,0 +1,51 @@
<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="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>

View File

@@ -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>
@@ -37,12 +37,19 @@
<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_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>
<string name="module_magisk_conflict">所有模块已被禁用,因为它与 Magisk 的模块系统有冲突!</string>
</resources>

View File

@@ -1,48 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<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"></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_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_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_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">超級使用者</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="require_kernel_version_8">需要 KernelSU 版本 8+</string>
<string name="module_uninstall_success">%s 已卸載</string>
<string name="module_uninstall_failed">卸載失敗: %s</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="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>
<string name="module_magisk_conflict">模組已停用,因其與 Magisk 的模組存在衝突!</string>
</resources>

View File

@@ -1,48 +1,58 @@
<?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_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_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_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_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_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">超級使用者</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="require_kernel_version_8">需要 KernelSU 版本 8+</string>
<string name="module_uninstall_success">%s 已卸載</string>
<string name="module_uninstall_failed">卸載失敗: %s</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="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>
<string name="module_magisk_conflict">模組已停用,因其與 Magisk 的模組存在衝突!</string>
</resources>

View File

@@ -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>
@@ -41,13 +41,20 @@
<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">Make system writable</string>
<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>
<string name="module_magisk_conflict">Modules are disabled because it is conflict with Magisk\'s!</string>
</resources>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="."/>
<root-path name="root" path="." />
</paths>

View File

@@ -0,0 +1,50 @@
import json
import sys
import os
def main():
assert len(sys.argv) == 2
file_name = sys.argv[1]
github = "https://github.com/"
issue_content = os.environ["ISSUE_CONTENT"]
lines = issue_content.split("\n\n")
assert len(lines) == 6
url = lines[1]
print(url)
device = lines[3]
print(device)
code_of_conduct = lines[5]
print(code_of_conduct)
assert code_of_conduct.find("[X]") > 0
tmp = url.removesuffix("/").replace(github, "").split("/")
print(tmp)
assert len(tmp) == 2
maintainer = tmp[0]
print(maintainer)
maintainer_link = "%s%s" % (github, maintainer)
print(maintainer_link)
kernel_name = tmp[1]
print(kernel_name)
kernel_link = "%s%s/%s" % (github, maintainer, kernel_name)
print(kernel_link)
with open(file_name, "r") as f:
data = json.loads(f.read())
data.append(
{
"maintainer": maintainer,
"maintainer_link": maintainer_link,
"kernel_name": kernel_name,
"kernel_link": kernel_link,
"devices": device,
}
)
os.remove(file_name)
with open(file_name, "w") as f:
f.write(json.dumps(data, indent=4))
os.system("echo success=true >> $GITHUB_OUTPUT")
os.system("echo device=%s >> $GITHUB_OUTPUT" % device)
if __name__ == "__main__":
main()

51
scripts/bin2c.py Normal file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/python3
import argparse
import os
import re
line_size = 80
def bin2c(filename, varname='data'):
if not os.path.isfile(filename):
print('File "%s" is not found!' % filename)
return ''
if not re.match('[a-zA-Z_][a-zA-Z0-9_]*', varname):
print('Invalid variable name "%s"' % varname)
return
with open(filename, 'rb') as in_file:
data = in_file.read()
# limit the line length
byte_len = 6 # '0x00, '
out = 'unsigned int %s_size = %d;\n' \
'const char %s[%d] = {\n' % (varname, len(data), varname, len(data))
line = ''
for byte in data:
line += '0x%02x, ' % byte
if len(line) + 4 + byte_len >= line_size:
out += ' ' * 4 + line + '\n'
line = ''
# add the last line
if len(line) + 4 + byte_len < line_size:
out += ' ' * 4 + line + '\n'
# strip the last comma
out = out.rstrip(', \n') + '\n'
out += '};'
return out
def main():
""" Main func """
parser = argparse.ArgumentParser()
parser.add_argument(
'filename', help='filename to convert to C array')
parser.add_argument(
'varname', nargs='?', help='variable name', default='data')
args = parser.parse_args()
# print out the data
print(bin2c(args.filename, args.varname))
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,6 @@ rust-version = "1.65"
anyhow = "1.0.68"
clap = { version = "4.0.32", features = ["derive"] }
const_format = "0.2.30"
subprocess = "0.2.9"
zip = "0.6.3"
zip-extensions = "0.6.1"
java-properties = "1.4.1"
@@ -23,8 +22,28 @@ encoding = "0.2.33"
retry = "2.0.0"
humansize = "2.0.0"
libc = "0.2"
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
] }
which = "4.2.2"
[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
opt-level = "z"
lto = true
lto = true

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

55
userspace/ksud/build.rs Normal file
View File

@@ -0,0 +1,55 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;
fn get_git_version() -> Result<(u32, String), std::io::Error> {
let output = Command::new("git")
.args(["rev-list", "--count", "HEAD"])
.output()?;
let output = output.stdout;
let version_code = String::from_utf8(output).expect("Failed to read git count stdout");
let version_code: u32 = version_code
.trim()
.parse()
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "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()?
.stdout,
)
.map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
"Failed to read git describe stdout",
)
})?;
Ok((version_code, version_name))
}
fn main() {
let (code, name) = match get_git_version() {
Ok((code, name)) => (code, name),
Err(_) => {
// show warning if git is not installed
println!("cargo:warning=Failed to get git version, using 0.0.0");
(0, "0.0.0".to_string())
}
};
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");
}

View File

@@ -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"))

View 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(())
}

View File

@@ -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,
@@ -35,7 +40,10 @@ enum Commands {
Install,
/// SELinux policy Patch tool
Sepolicy,
Sepolicy {
#[command(subcommand)]
command: Sepolicy,
},
/// For developers
Debug {
@@ -68,6 +76,27 @@ enum Debug {
Test,
}
#[derive(clap::Subcommand, Debug)]
enum Sepolicy {
/// Patch sepolicy
Patch {
/// sepolicy statements
sepolicy: String,
},
/// Apply sepolicy from file
Apply {
/// sepolicy file path
file: String,
},
/// Check if sepolicy statement is supported/valid
Check {
/// sepolicy statements
sepolicy: String,
},
}
#[derive(clap::Subcommand, Debug)]
enum Module {
/// Install module <ZIP>
@@ -99,26 +128,45 @@ 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 => todo!(),
Commands::Sepolicy { command } => match command {
Sepolicy::Patch { sepolicy } => crate::sepolicy::live_patch(&sepolicy),
Sepolicy::Apply { file } => crate::sepolicy::apply_file(file),
Sepolicy::Check { sepolicy } => crate::sepolicy::check_rule(&sepolicy),
},
Commands::Services => event::on_services(),
Commands::Debug { command } => match command {

View File

@@ -39,8 +39,8 @@ fn set_kernel_param(size: u32, hash: u32) -> Result<()> {
}
fn get_apk_path(package_name: &str) -> Result<String> {
let cmd = format!("pm path {}", package_name);
let output = Command::new("sh").arg("-c").arg(cmd).output()?;
// `cmd package path` is not available below Android 9
let output = Command::new("pm").args(["path", package_name]).output()?;
// package:/data/app/<xxxx>/base.apk
let output = String::from_utf8_lossy(&output.stdout);
@@ -54,8 +54,8 @@ pub fn set_manager(pkg: &str) -> Result<()> {
"CONFIG_KSU_DEBUG is not enabled"
);
let path = get_apk_path(pkg).with_context(|| format!("{} not exist!", pkg))?;
let sign = get_apk_signature(path.as_str())?;
let path = get_apk_path(pkg).with_context(|| format!("{pkg} does not exist!"))?;
let sign = get_apk_signature(&path)?;
set_kernel_param(sign.0, sign.1)?;
Ok(())
}

View File

@@ -1,9 +1,13 @@
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 MODULE_DIR: &str = concatcp!(WORKING_DIR, "modules/");
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!(ADB_DIR, "modules/");
pub const MODULE_IMG: &str = concatcp!(WORKING_DIR, "modules.img");
pub const MODULE_UPDATE_IMG: &str = concatcp!(WORKING_DIR, "modules_update.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"));

View File

@@ -1,39 +1,59 @@
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 subprocess::Exec;
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: {} lowerdir is empty", partition);
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() {
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 let Err(e) = remount_result {
log::error!("remount stock failed: {:?}", e);
}
bail!("umount stock mount of failed: {:?}", result);
}
// add /partition as the lowerest dir
let lowest_dir = format!("/{}", partition);
let lowest_dir = format!("/{partition}");
lowerdir.push(lowest_dir.clone());
let lowerdir = lowerdir.join(":");
println!("partition: {} lowerdir: {}", partition, lowerdir);
info!("partition: {partition} lowerdir: {lowerdir}");
let mount_args = format!(
"mount -t overlay overlay -o ro,lowerdir={} {}",
lowerdir, lowest_dir
);
if let Ok(result) = Exec::shell(mount_args).join() {
if !result.success() {
println!("mount partition: {} overlay failed", partition);
let result = mount::mount_overlay(&lowerdir, &lowest_dir);
if let Err(e) = stock_mount.remount() {
if result.is_ok() {
// if mount overlay ok but stock remount failed, we should umount overlay
warn!("remount stock failed: {:?}, umount overlay {lowest_dir}", e);
if mount::umount_dir(&lowest_dir).is_err() {
warn!("umount overlay {lowest_dir} failed");
}
}
} else {
println!("mount partition: {} overlay failed", partition);
}
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 {
@@ -45,7 +65,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() {
@@ -55,19 +75,19 @@ 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 {
let part_path = Path::new(&module_system).join(part);
// if /partition is a mountpoint, we would move it to $MODPATH/$partition when install
// otherwise it must be a symlink and we don't need to overlay!
let part_path = Path::new(&module).join(part);
if !part_path.exists() {
continue;
}
@@ -78,11 +98,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(())
@@ -90,6 +114,14 @@ pub fn do_systemless_mount(module_dir: &str) -> Result<()> {
pub fn on_post_data_fs() -> Result<()> {
crate::ksu::report_post_fs_data();
if utils::has_magisk() {
warn!("Magisk detected, skip post-fs-data!");
return Ok(());
}
utils::umask(0);
let module_update_img = defs::MODULE_UPDATE_IMG;
let module_img = defs::MODULE_IMG;
let module_dir = defs::MODULE_DIR;
@@ -101,6 +133,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
@@ -115,37 +149,80 @@ 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())?;
// mount systemless overlay
if let Err(e) = do_systemless_mount(module_dir) {
println!("do systemless mount failed: {}", e);
// 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(());
}
// module mounted, exec modules post-fs-data scripts
if !crate::utils::is_safe_mode().unwrap_or(false) {
// 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");
// 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() {
warn!("load sepolicy.rule failed");
}
// 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);
}
// 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().unwrap_or(false) {
let _ = crate::module::exec_services();
} else {
println!("safe mode, skip module service scripts");
utils::umask(0);
if utils::has_magisk() {
warn!("Magisk detected, skip services!");
return Ok(());
}
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(())
@@ -153,11 +230,17 @@ pub fn on_services() -> Result<()> {
pub fn on_boot_completed() -> Result<()> {
crate::ksu::report_boot_complete();
info!("on_boot_completed triggered!");
let module_update_img = Path::new(defs::MODULE_UPDATE_IMG);
let module_img = Path::new(defs::MODULE_IMG);
if module_update_img.exists() {
// this is a update and we successfully booted
std::fs::rename(module_update_img, module_img)?;
if std::fs::rename(module_update_img, module_img).is_err() {
warn!("Failed to rename images, copy it now.",);
std::fs::copy(module_update_img, module_img)
.with_context(|| "Failed to copy images")?;
std::fs::remove_file(module_update_img).with_context(|| "Failed to remove image!")?;
}
}
Ok(())
}
@@ -167,9 +250,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")
}

View File

@@ -1,8 +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"
@@ -67,6 +71,11 @@ print_title() {
ui_print "$bar"
}
check_sepolicy() {
/data/adb/ksud sepolicy check "$1"
return $?
}
######################
# Environment Related
######################
@@ -89,14 +98,15 @@ setup_flashable() {
}
ensure_bb() {
:
}
recovery_actions() {
:
}
recovery_cleanup() {
:
}
#######################
@@ -261,12 +271,20 @@ mktouch() {
chmod 644 $1
}
request_size_check() {
reqSizeM=`du -ms "$1" | cut -f1`
mark_remove() {
mkdir -p ${1%/*} 2>/dev/null
mknod $1 c 0 0
chmod 644 $1
}
unzip() {
/system/bin/unzip -q "$@"
mark_replace() {
mkdir -p ${1%/*} 2>/dev/null
setfattr -n trusted.overlay.opaque -v y $1
chmod 644 $1
}
request_size_check() {
reqSizeM=`du -ms "$1" | cut -f1`
}
request_zip_size_check() {
@@ -281,6 +299,22 @@ is_legacy_script() {
return $?
}
handle_partition() {
# if /system/vendor is a symlink, we need to move it out of $MODPATH/system, otherwise it will be overlayed
# if /system/vendor is a normal directory, it is ok to overlay it and we don't need to overlay it separately.
if [ ! -e $MODPATH/system/$1 ]; then
# no partition found
return;
fi
if [ -L "/system/$1" ] && [ "$(readlink -f /system/$1)" = "/$1" ]; then
ui_print "- Handle partition /$1"
# we create a symlink if module want to access $MODPATH/system/$1
# but it doesn't always work(ie. write it in post-fs-data.sh would fail because it is readonly)
mv -f $MODPATH/system/$1 $MODPATH/$1 && ln -sf /$1 $MODPATH/system/$1
fi
}
# Require OUTFD, ZIPFILE to be set
install_module() {
rm -rf $TMPDIR
@@ -348,17 +382,27 @@ 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
[ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
fi
handle_partition vendor
handle_partition system_ext
handle_partition product
# Handle replace folders
for TARGET in $REPLACE; do
ui_print "- Replace target: $TARGET"
mktouch $MODPATH$TARGET/.replace
mark_replace $MODPATH$TARGET
done
# Handle remove files
for TARGET in $REMOVE; do
ui_print "- Remove target: $TARGET"
mark_remove $MODPATH$TARGET
done
if $BOOTMODE; then
@@ -394,8 +438,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

View File

@@ -1,9 +1,11 @@
#![allow(dead_code, unused_mut, unused_variables, unused_imports)]
use anyhow::Result;
#[cfg(unix)]
use anyhow::ensure;
#[cfg(unix)]
use std::os::unix::process::CommandExt;
use anyhow::{Result, ensure};
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;
@@ -13,61 +15,81 @@ const CMD_GET_VERSION: u64 = 2;
// const CMD_GET_ALLOW_LIST: u64 = 5;
// 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");
std::process::Command::new("/system/bin/sh").exec();
Ok(())
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 {
#[allow(clippy::cast_possible_wrap)]
libc::prctl(
KERNEL_SU_OPTION as i32,
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);
}
pub fn report_boot_complete() {
report_event(EVENT_BOOT_COMPLETED);
}
}

View File

@@ -1,11 +1,14 @@
mod apk_sign;
mod assets;
mod cli;
mod debug;
mod defs;
mod event;
mod ksu;
mod module;
mod mount;
mod restorecon;
mod sepolicy;
mod utils;
fn main() -> anyhow::Result<()> {

View File

@@ -1,38 +1,64 @@
#[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},
env::var as env_var,
fs::{remove_dir_all, set_permissions, File, OpenOptions, Permissions},
io::{Cursor, Write},
os::unix::{prelude::PermissionsExt, process::CommandExt},
path::{Path, PathBuf},
process::{Command, Stdio},
str::FromStr,
};
use subprocess::Exec;
use zip_extensions::*;
use zip_extensions::zip_extract_file_to_memory;
use crate::{defs, restorecon};
use crate::{restorecon::setsyscon, utils::*};
#[cfg(unix)]
use std::os::unix::{prelude::PermissionsExt, process::CommandExt};
use anyhow::{bail, ensure, Context, Result};
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: {} failed", module_file))?;
.with_context(|| format!("realpath: {module_file} failed"))?;
let result = Command::new("/system/bin/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())
.status()?;
ensure!(result.success(), "install module script failed!");
ensure!(result.success(), "Failed to install module script");
Ok(())
}
@@ -41,46 +67,32 @@ fn exec_install_script(module_file: &str) -> Result<()> {
// if someone(such as the module) install a module before the boot_completed
// then it may cause some problems, just forbid it
fn ensure_boot_completed() -> Result<()> {
// ensure getprop sys.boot_completed = 1
let output = Command::new("getprop")
.arg("sys.boot_completed")
.stdout(Stdio::piped())
.output()?;
let output = String::from_utf8_lossy(&output.stdout);
if output.trim() != "1" {
// ensure getprop sys.boot_completed == 1
if getprop("sys.boot_completed").as_deref() != Some("1") {
bail!("Android is Booting!");
}
Ok(())
}
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())
@@ -90,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)
@@ -103,7 +115,7 @@ fn check_image(img: &str) -> Result<()> {
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.with_context(|| format!("Failed exec e2fsck {}", img))?;
.with_context(|| format!("Failed to exec e2fsck {img}"))?;
let code = result.code();
// 0 or 1 is ok
// 0: no error
@@ -111,15 +123,15 @@ fn check_image(img: &str) -> Result<()> {
// https://man7.org/linux/man-pages/man8/e2fsck.8.html
ensure!(
code == Some(0) || code == Some(1),
"check image e2fsck exec failed: {}",
"Failed to check image, e2fsck exit code: {}",
code.unwrap_or(-1)
);
Ok(())
}
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)?;
@@ -128,14 +140,16 @@ 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 = Exec::shell(format!("resize2fs {} {}K", img, target_size))
.stdout(subprocess::NullFile)
.stderr(subprocess::Redirection::Merge)
.join()
.with_context(|| format!("Failed to resize2fs {}", img))?;
ensure!(result.success(), "resize2fs exec failed.");
let result = Command::new("resize2fs")
.args([img, &format!("{target_size}K")])
.stdout(Stdio::null())
.status()
.with_context(|| format!("Failed to exec resize2fs {img}"))?;
ensure!(result.success(), "Failed to resize2fs: {}", result);
check_image(img)?;
Ok(())
}
@@ -148,22 +162,90 @@ fn switch_cgroup(grp: &str, pid: u32) {
let fp = OpenOptions::new().append(true).open(path);
if let Ok(mut fp) = fp {
let _ = writeln!(fp, "{}", pid);
let _ = writeln!(fp, "{pid}");
}
}
fn switch_cgroups() -> Result<()> {
fn switch_cgroups() {
let pid = std::process::id();
switch_cgroup("/acct", pid);
switch_cgroup("/dev/cg2_bpf", pid);
switch_cgroup("/sys/fs/cgroup", pid);
if getprop("ro.config.per_app_memcg")? != "false" {
if getprop("ro.config.per_app_memcg")
.filter(|prop| prop == "false")
.is_none()
{
switch_cgroup("/dev/memcg/apps", pid);
}
}
pub fn load_sepolicy_rule() -> Result<()> {
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() {
info!("{} is disabled, skip", path.display());
continue;
}
let rule_file = path.join("sepolicy.rule");
if !rule_file.exists() {
continue;
}
info!("load policy: {}", &rule_file.display());
if sepolicy::apply_file(&rule_file).is_err() {
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);
@@ -172,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;
}
@@ -180,23 +262,30 @@ pub fn exec_post_fs_data() -> Result<()> {
if !post_fs_data.exists() {
continue;
}
println!("exec {} post-fs-data.sh", path.display());
// pre_exec is unsafe!
unsafe {
Command::new("/system/bin/sh")
.arg(&post_fs_data)
.process_group(0)
.pre_exec(|| {
// ignore the error?
let _ = switch_cgroups();
Ok(())
})
.current_dir(path)
.env("KSU", "true")
.status()
.with_context(|| format!("Failed to exec {}", post_fs_data.display()))?;
exec_script(&post_fs_data, true)?;
}
Ok(())
}
pub fn exec_common_scripts(dir: &str, wait: bool) -> Result<()> {
let script_dir = Path::new(defs::ADB_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;
}
exec_script(path, wait)?;
}
Ok(())
@@ -210,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;
}
@@ -218,50 +307,21 @@ pub fn exec_services() -> Result<()> {
if !service.exists() {
continue;
}
println!("exec {} service.sh", path.display());
// pre_exec is unsafe!
unsafe {
Command::new("/system/bin/sh")
.arg(&service)
.process_group(0)
.pre_exec(|| {
// ignore the error?
let _ = switch_cgroups();
Ok(())
})
.current_dir(path)
.env("KSU", "true")
.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;
}
@@ -269,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 --file system.prop
Command::new(RESETPROP_PATH)
// resetprop -n --file system.prop
Command::new(assets::RESETPROP_PATH)
.arg("-n")
.arg("--file")
.arg(&system_prop)
@@ -283,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();
@@ -312,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);
@@ -333,9 +388,17 @@ 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;
let grow_size_per_m = grow_size / 1024 / 1024 + 1;
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!(
@@ -346,25 +409,27 @@ 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
let result = Exec::shell(format!(
"dd if=/dev/zero of={} bs=1M count={}",
tmp_module_img, grow_size_per_m
))
.stdout(subprocess::NullFile)
.stderr(subprocess::Redirection::Merge)
.join()?;
ensure!(result.success(), "create ext4 image failed!");
info!("Creating brand new module image");
File::create(tmp_module_img)
.context("Failed to create ext4 image file")?
.set_len(grow_size)
.context("Failed to extend ext4 image")?;
// format the img to ext4 filesystem
let result = Exec::shell(format!("mkfs.ext4 {}", tmp_module_img))
.stdout(subprocess::NullFile)
.stderr(subprocess::Redirection::Merge)
.join()?;
ensure!(result.success(), "format ext4 image failed!");
let result = Command::new("mkfs.ext4")
.arg(tmp_module_img)
.stdout(Stdio::null())
.output()?;
ensure!(
result.status.success(),
"Failed to format ext4 image: {}",
String::from_utf8(result.stderr).unwrap()
);
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 {}",
@@ -376,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 {}",
@@ -393,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<()>,
{
@@ -469,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)?;
@@ -515,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 {
@@ -541,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);
@@ -550,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 {
@@ -583,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| {
@@ -595,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());
@@ -610,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(())
}

352
userspace/ksud/src/mount.rs Normal file
View File

@@ -0,0 +1,352 @@
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 mut mounts = self
.mountlist
.destination_starts_with(std::path::Path::new(&self.mnt))
.filter(|m| m.fstype != "overlay" && m.fstype != "rootfs")
.collect::<Vec<_>>();
mounts.sort_by(|a, b| b.dest.cmp(&a.dest)); // inverse order
mounts
}
pub fn umount(&self) -> Result<()> {
let mounts = self.get_target_mounts();
log::info!("umount stock 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);
}
log::info!("umount stock succeed!");
Ok(())
}
pub fn remount(&self) -> Result<()> {
let mut mounts = self.get_target_mounts();
mounts.reverse(); // remount it in order
log::info!("remount stock for {} : {:?}", self.mnt, 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!("begin remount: {src} -> {dst}");
let result = std::process::Command::new("mount")
.arg("-t")
.arg(fstype)
.arg("-o")
.arg(options)
.arg(src)
.arg(dst)
.status();
if let Err(e) = result {
log::error!("remount failed: {}", e);
} else {
log::info!("remount {src} -> {dst} succeed!");
}
}
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.

View File

@@ -1,27 +1,45 @@
use anyhow::ensure;
use anyhow::Ok;
use anyhow::Result;
use subprocess::Exec;
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<()> {
// todo use libselinux directly
let cmd = format!("chcon {} {}", con, path);
let result = Exec::shell(cmd).join()?;
ensure!(result.success(), "chcon for: {} failed.", 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<()> {
// todo use libselinux directly
let cmd = format!("chcon -R {} {}", SYSTEM_CON, dir);
let result = Exec::shell(cmd).join()?;
ensure!(result.success(), "chcon for: {} failed.", dir);
#[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()) {
#[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(())
}

View File

@@ -0,0 +1,754 @@
use anyhow::{bail, Result};
use derive_new::new;
use nom::{
branch::alt,
bytes::complete::{tag, take_while, take_while1, take_while_m_n},
character::{
complete::{space0, space1},
is_alphanumeric,
},
combinator::map,
sequence::Tuple,
IResult, Parser,
};
use std::{path::Path, vec};
type SeObject<'a> = Vec<&'a str>;
fn is_sepolicy_char(c: char) -> bool {
is_alphanumeric(c as u8) || c == '_' || c == '-'
}
fn parse_single_word(input: &str) -> IResult<&str, &str> {
take_while1(is_sepolicy_char).parse(input)
}
fn parse_bracket_objs(input: &str) -> IResult<&str, SeObject> {
let (input, (_, words, _)) = (
tag("{"),
take_while_m_n(1, 100, |c: char| is_sepolicy_char(c) || c.is_whitespace()),
tag("}"),
)
.parse(input)?;
Ok((input, words.split_whitespace().collect()))
}
fn parse_single_obj(input: &str) -> IResult<&str, SeObject> {
let (input, word) = take_while1(is_sepolicy_char).parse(input)?;
Ok((input, vec![word]))
}
fn parse_star(input: &str) -> IResult<&str, SeObject> {
let (input, _) = tag("*").parse(input)?;
Ok((input, vec!["*"]))
}
// 1. a single sepolicy word
// 2. { obj1 obj2 obj3 ...}
// 3. *
fn parse_seobj(input: &str) -> IResult<&str, SeObject> {
let (input, strs) = alt((parse_single_obj, parse_bracket_objs, parse_star)).parse(input)?;
Ok((input, strs))
}
fn parse_seobj_no_star(input: &str) -> IResult<&str, SeObject> {
let (input, strs) = alt((parse_single_obj, parse_bracket_objs)).parse(input)?;
Ok((input, strs))
}
trait SeObjectParser<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self>
where
Self: Sized;
}
#[derive(Debug, PartialEq, Eq, new)]
struct NormalPerm<'a> {
op: &'a str,
source: SeObject<'a>,
target: SeObject<'a>,
class: SeObject<'a>,
perm: SeObject<'a>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct XPerm<'a> {
op: &'a str,
source: SeObject<'a>,
target: SeObject<'a>,
class: SeObject<'a>,
operation: &'a str,
perm_set: &'a str,
}
#[derive(Debug, PartialEq, Eq, new)]
struct TypeState<'a> {
op: &'a str,
stype: SeObject<'a>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct TypeAttr<'a> {
stype: SeObject<'a>,
sattr: SeObject<'a>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct Type<'a> {
name: &'a str,
attrs: SeObject<'a>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct Attr<'a> {
name: &'a str,
}
#[derive(Debug, PartialEq, Eq, new)]
struct TypeTransition<'a> {
source: &'a str,
target: &'a str,
class: &'a str,
default_type: &'a str,
object_name: Option<&'a str>,
}
#[derive(Debug, PartialEq, Eq, new)]
struct TypeChange<'a> {
op: &'a str,
source: &'a str,
target: &'a str,
class: &'a str,
default_type: &'a str,
}
#[derive(Debug, PartialEq, Eq, new)]
struct GenFsCon<'a> {
fs_name: &'a str,
partial_path: &'a str,
fs_context: &'a str,
}
#[derive(Debug)]
enum PolicyStatement<'a> {
// "allow *source_type *target_type *class *perm_set"
// "deny *source_type *target_type *class *perm_set"
// "auditallow *source_type *target_type *class *perm_set"
// "dontaudit *source_type *target_type *class *perm_set"
NormalPerm(NormalPerm<'a>),
// "allowxperm *source_type *target_type *class operation xperm_set"
// "auditallowxperm *source_type *target_type *class operation xperm_set"
// "dontauditxperm *source_type *target_type *class operation xperm_set"
XPerm(XPerm<'a>),
// "permissive ^type"
// "enforce ^type"
TypeState(TypeState<'a>),
// "type type_name ^(attribute)"
Type(Type<'a>),
// "typeattribute ^type ^attribute"
TypeAttr(TypeAttr<'a>),
// "attribute ^attribute"
Attr(Attr<'a>),
// "type_transition source_type target_type class default_type (object_name)"
TypeTransition(TypeTransition<'a>),
// "type_change source_type target_type class default_type"
// "type_member source_type target_type class default_type"
TypeChange(TypeChange<'a>),
// "genfscon fs_name partial_path fs_context"
GenFsCon(GenFsCon<'a>),
}
impl<'a> SeObjectParser<'a> for NormalPerm<'a> {
fn parse(input: &'a str) -> IResult<&str, Self> {
let (input, op) = alt((
tag("allow"),
tag("deny"),
tag("auditallow"),
tag("dontaudit"),
))(input)?;
let (input, _) = space0(input)?;
let (input, source) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, target) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, class) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, perm) = parse_seobj(input)?;
Ok((input, NormalPerm::new(op, source, target, class, perm)))
}
}
impl<'a> SeObjectParser<'a> for XPerm<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, op) = alt((
tag("allowxperm"),
tag("auditallowxperm"),
tag("dontauditxperm"),
))(input)?;
let (input, _) = space0(input)?;
let (input, source) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, target) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, class) = parse_seobj(input)?;
let (input, _) = space0(input)?;
let (input, operation) = parse_single_word(input)?;
let (input, _) = space0(input)?;
let (input, perm_set) = parse_single_word(input)?;
Ok((
input,
XPerm::new(op, source, target, class, operation, perm_set),
))
}
}
impl<'a> SeObjectParser<'a> for TypeState<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, op) = alt((tag("permissive"), tag("enforce")))(input)?;
let (input, _) = space1(input)?;
let (input, stype) = parse_seobj_no_star(input)?;
Ok((input, TypeState::new(op, stype)))
}
}
impl<'a> SeObjectParser<'a> for Type<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = tag("type")(input)?;
let (input, _) = space1(input)?;
let (input, name) = parse_single_word(input)?;
if input.is_empty() {
return Ok((input, Type::new(name, vec!["domain"]))); // default to domain
}
let (input, _) = space1(input)?;
let (input, attrs) = parse_seobj_no_star(input)?;
Ok((input, Type::new(name, attrs)))
}
}
impl<'a> SeObjectParser<'a> for TypeAttr<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = alt((tag("typeattribute"), tag("attradd")))(input)?;
let (input, _) = space1(input)?;
let (input, stype) = parse_seobj_no_star(input)?;
let (input, _) = space1(input)?;
let (input, attr) = parse_seobj_no_star(input)?;
Ok((input, TypeAttr::new(stype, attr)))
}
}
impl<'a> SeObjectParser<'a> for Attr<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = tag("attribute")(input)?;
let (input, _) = space1(input)?;
let (input, attr) = parse_single_word(input)?;
Ok((input, Attr::new(attr)))
}
}
impl<'a> SeObjectParser<'a> for TypeTransition<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = alt((tag("type_transition"), tag("name_transition")))(input)?;
let (input, _) = space1(input)?;
let (input, source) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, target) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, class) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, default) = parse_single_word(input)?;
if input.is_empty() {
return Ok((
input,
TypeTransition::new(source, target, class, default, None),
));
}
let (input, _) = space1(input)?;
let (input, object) = parse_single_word(input)?;
Ok((
input,
TypeTransition::new(source, target, class, default, Some(object)),
))
}
}
impl<'a> SeObjectParser<'a> for TypeChange<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, op) = alt((tag("type_change"), tag("type_member")))(input)?;
let (input, _) = space1(input)?;
let (input, source) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, target) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, class) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, default) = parse_single_word(input)?;
Ok((input, TypeChange::new(op, source, target, class, default)))
}
}
impl<'a> SeObjectParser<'a> for GenFsCon<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self>
where
Self: Sized,
{
let (input, _) = tag("genfscon")(input)?;
let (input, _) = space1(input)?;
let (input, fs) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, path) = parse_single_word(input)?;
let (input, _) = space1(input)?;
let (input, context) = parse_single_word(input)?;
Ok((input, GenFsCon::new(fs, path, context)))
}
}
impl<'a> PolicyStatement<'a> {
fn parse(input: &'a str) -> IResult<&'a str, Self> {
let (input, _) = space0(input)?;
let (input, statement) = alt((
map(NormalPerm::parse, PolicyStatement::NormalPerm),
map(XPerm::parse, PolicyStatement::XPerm),
map(TypeState::parse, PolicyStatement::TypeState),
map(Type::parse, PolicyStatement::Type),
map(TypeAttr::parse, PolicyStatement::TypeAttr),
map(Attr::parse, PolicyStatement::Attr),
map(TypeTransition::parse, PolicyStatement::TypeTransition),
map(TypeChange::parse, PolicyStatement::TypeChange),
map(GenFsCon::parse, PolicyStatement::GenFsCon),
))(input)?;
let (input, _) = space0(input)?;
let (input, _) = take_while(|c| c == ';')(input)?;
let (input, _) = space0(input)?;
Ok((input, statement))
}
}
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)
}
const SEPOLICY_MAX_LEN: usize = 128;
const CMD_NORMAL_PERM: u32 = 1;
const CMD_XPERM: u32 = 2;
const CMD_TYPE_STATE: u32 = 3;
const CMD_TYPE: u32 = 4;
const CMD_TYPE_ATTR: u32 = 5;
const CMD_ATTR: u32 = 6;
const CMD_TYPE_TRANSITION: u32 = 7;
const CMD_TYPE_CHANGE: u32 = 8;
const CMD_GENFSCON: u32 = 9;
#[derive(Debug, Default)]
enum PolicyObject {
All, // for "*", stand for all objects, and is NULL in ffi
One([u8; SEPOLICY_MAX_LEN]),
#[default]
None,
}
impl TryFrom<&str> for PolicyObject {
type Error = anyhow::Error;
fn try_from(s: &str) -> Result<Self> {
anyhow::ensure!(s.len() <= SEPOLICY_MAX_LEN, "policy object too long");
if s == "*" {
return Ok(PolicyObject::All);
}
let mut buf = [0u8; SEPOLICY_MAX_LEN];
buf[..s.len()].copy_from_slice(s.as_bytes());
Ok(PolicyObject::One(buf))
}
}
/// atomic statement, such as: allow domain1 domain2:file1 read;
/// 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,
subcmd: u32,
sepol1: PolicyObject,
sepol2: PolicyObject,
sepol3: PolicyObject,
sepol4: PolicyObject,
sepol5: PolicyObject,
sepol6: PolicyObject,
sepol7: PolicyObject,
}
impl<'a> TryFrom<&'a NormalPerm<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a NormalPerm<'a>) -> Result<Self> {
let mut result = vec![];
let subcmd = match perm.op {
"allow" => 1,
"deny" => 2,
"auditallow" => 3,
"dontaudit" => 4,
_ => 0,
};
for &s in &perm.source {
for &t in &perm.target {
for &c in &perm.class {
for &p in &perm.perm {
result.push(AtomicStatement {
cmd: CMD_NORMAL_PERM,
subcmd,
sepol1: s.try_into()?,
sepol2: t.try_into()?,
sepol3: c.try_into()?,
sepol4: p.try_into()?,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
}
}
}
Ok(result)
}
}
impl<'a> TryFrom<&'a XPerm<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a XPerm<'a>) -> Result<Self> {
let mut result = vec![];
let subcmd = match perm.op {
"allowxperm" => 1,
"auditallowxperm" => 2,
"dontauditxperm" => 3,
_ => 0,
};
for &s in &perm.source {
for &t in &perm.target {
for &c in &perm.class {
result.push(AtomicStatement {
cmd: CMD_XPERM,
subcmd,
sepol1: s.try_into()?,
sepol2: t.try_into()?,
sepol3: c.try_into()?,
sepol4: perm.operation.try_into()?,
sepol5: perm.perm_set.try_into()?,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
}
}
Ok(result)
}
}
impl<'a> TryFrom<&'a TypeState<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a TypeState<'a>) -> Result<Self> {
let mut result = vec![];
let subcmd = match perm.op {
"permissive" => 1,
"enforcing" => 2,
_ => 0,
};
for &t in &perm.stype {
result.push(AtomicStatement {
cmd: CMD_TYPE_STATE,
subcmd,
sepol1: t.try_into()?,
sepol2: PolicyObject::None,
sepol3: PolicyObject::None,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
Ok(result)
}
}
impl<'a> TryFrom<&'a Type<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a Type<'a>) -> Result<Self> {
let mut result = vec![];
for &attr in &perm.attrs {
result.push(AtomicStatement {
cmd: CMD_TYPE,
subcmd: 0,
sepol1: perm.name.try_into()?,
sepol2: attr.try_into()?,
sepol3: PolicyObject::None,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
Ok(result)
}
}
impl<'a> TryFrom<&'a TypeAttr<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a TypeAttr<'a>) -> Result<Self> {
let mut result = vec![];
for &t in &perm.stype {
for &attr in &perm.sattr {
result.push(AtomicStatement {
cmd: CMD_TYPE_ATTR,
subcmd: 0,
sepol1: t.try_into()?,
sepol2: attr.try_into()?,
sepol3: PolicyObject::None,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
}
}
Ok(result)
}
}
impl<'a> TryFrom<&'a Attr<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a Attr<'a>) -> Result<Self> {
let result = vec![AtomicStatement {
cmd: CMD_ATTR,
subcmd: 0,
sepol1: perm.name.try_into()?,
sepol2: PolicyObject::None,
sepol3: PolicyObject::None,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
}];
Ok(result)
}
}
impl<'a> TryFrom<&'a TypeTransition<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a TypeTransition<'a>) -> Result<Self> {
let mut result = vec![];
let obj = match perm.object_name {
Some(obj) => obj.try_into()?,
None => PolicyObject::None,
};
result.push(AtomicStatement {
cmd: CMD_TYPE_TRANSITION,
subcmd: 0,
sepol1: perm.source.try_into()?,
sepol2: perm.target.try_into()?,
sepol3: perm.class.try_into()?,
sepol4: perm.default_type.try_into()?,
sepol5: obj,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
Ok(result)
}
}
impl<'a> TryFrom<&'a TypeChange<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a TypeChange<'a>) -> Result<Self> {
let mut result = vec![];
let subcmd = match perm.op {
"type_change" => 1,
"type_member" => 2,
_ => 0,
};
result.push(AtomicStatement {
cmd: CMD_TYPE_CHANGE,
subcmd,
sepol1: perm.source.try_into()?,
sepol2: perm.target.try_into()?,
sepol3: perm.class.try_into()?,
sepol4: perm.default_type.try_into()?,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
});
Ok(result)
}
}
impl<'a> TryFrom<&'a GenFsCon<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(perm: &'a GenFsCon<'a>) -> Result<Self> {
let result = vec![AtomicStatement {
cmd: CMD_GENFSCON,
subcmd: 0,
sepol1: perm.fs_name.try_into()?,
sepol2: perm.partial_path.try_into()?,
sepol3: perm.fs_context.try_into()?,
sepol4: PolicyObject::None,
sepol5: PolicyObject::None,
sepol6: PolicyObject::None,
sepol7: PolicyObject::None,
}];
Ok(result)
}
}
impl<'a> TryFrom<&'a PolicyStatement<'a>> for Vec<AtomicStatement> {
type Error = anyhow::Error;
fn try_from(value: &'a PolicyStatement) -> Result<Self> {
match value {
PolicyStatement::NormalPerm(perm) => perm.try_into(),
PolicyStatement::XPerm(perm) => perm.try_into(),
PolicyStatement::TypeState(perm) => perm.try_into(),
PolicyStatement::Type(perm) => perm.try_into(),
PolicyStatement::TypeAttr(perm) => perm.try_into(),
PolicyStatement::Attr(perm) => perm.try_into(),
PolicyStatement::TypeTransition(perm) => perm.try_into(),
PolicyStatement::TypeChange(perm) => perm.try_into(),
PolicyStatement::GenFsCon(perm) => perm.try_into(),
}
}
}
////////////////////////////////////////////////////////////////
/// for C FFI to call kernel interface
///////////////////////////////////////////////////////////////
#[derive(Debug)]
#[repr(C)]
struct FfiPolicy {
cmd: u32,
subcmd: u32,
sepol1: *const libc::c_char,
sepol2: *const libc::c_char,
sepol3: *const libc::c_char,
sepol4: *const libc::c_char,
sepol5: *const libc::c_char,
sepol6: *const libc::c_char,
sepol7: *const libc::c_char,
}
fn to_c_ptr(pol: &PolicyObject) -> *const libc::c_char {
match pol {
PolicyObject::None | PolicyObject::All => std::ptr::null(),
PolicyObject::One(s) => s.as_ptr().cast::<libc::c_char>(),
}
}
impl From<AtomicStatement> for FfiPolicy {
fn from(policy: AtomicStatement) -> FfiPolicy {
FfiPolicy {
cmd: policy.cmd,
subcmd: policy.subcmd,
sepol1: to_c_ptr(&policy.sepol1),
sepol2: to_c_ptr(&policy.sepol2),
sepol3: to_c_ptr(&policy.sepol3),
sepol4: to_c_ptr(&policy.sepol4),
sepol5: to_c_ptr(&policy.sepol5),
sepol6: to_c_ptr(&policy.sepol6),
sepol7: to_c_ptr(&policy.sepol7),
}
}
}
#[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, // supposed to overflow
crate::ksu::CMD_SET_SEPOLICY,
0,
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.", 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(), false)?;
for statement in result {
println!("{statement:?}");
apply_one_rule(&statement, false)?;
}
Ok(())
}
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(())
}

View File

@@ -1,57 +1,92 @@
use anyhow::{bail, Context, Error, Ok, Result};
use std::{
fs::{create_dir_all, write, File},
io::ErrorKind::AlreadyExists,
path::Path,
process::{Command, Stdio},
};
use anyhow::{ensure, Result};
use retry::delay::NoDelay;
use subprocess::Exec;
fn do_mount_image(src: &str, target: &str) -> Result<()> {
let result = Exec::shell(format!("mount -t ext4 {} {}", src, target))
.stdout(subprocess::NullFile)
.stderr(subprocess::Redirection::Merge)
.join()?;
ensure!(result.success(), "do mount: {} -> {} failed.", 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(), "mount: {} -> {} failed.", src, target);
Ok(())
}
pub fn umount_dir(src: &str) -> Result<()> {
let result = Exec::shell(format!("umount {}", src))
.stdout(subprocess::NullFile)
.stderr(subprocess::Redirection::Merge)
.join()?;
ensure!(result.success(), "umount: {} failed", 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 getprop(prop: &str) -> Result<String> {
let output = Command::new("getprop")
.arg(prop)
.stdout(Stdio::piped())
.output()?;
let output = String::from_utf8_lossy(&output.stdout);
Ok(output.trim().to_string())
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 is_safe_mode() -> Result<bool> {
Ok(getprop("persist.sys.safemode")?.eq("1") || getprop("ro.sys.safemode")?.eq("1"))
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 {
let safemode = getprop("persist.sys.safemode")
.filter(|prop| prop == "1")
.is_some()
|| getprop("ro.sys.safemode")
.filter(|prop| prop == "1")
.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> {
@@ -61,3 +96,36 @@ 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/{pid}/ns/mnt");
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")
}
pub fn has_magisk() -> bool {
which::which("magisk").is_ok()
}

View File

@@ -49,6 +49,9 @@ function sidebarGuide() {
{ text: 'Installation', link: '/guide/installation' },
{ text: 'How to build?', link: '/guide/how-to-build' },
{ text: 'Intergrate for non-GKI devices', link: '/guide/how-to-integrate-for-non-gki'},
{ text: 'Unofficially supported devices', link: '/guide/unofficially-support-devices.md' },
{ text: 'Module Guide', link: '/guide/module.md' },
{ text: 'Rescue from bootloop', link: '/guide/rescue-from-bootloop.md' },
{ text: 'FAQ', link: '/guide/faq' },
]
}

View File

@@ -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' },
]
}

View File

@@ -49,6 +49,7 @@ function sidebarGuide() {
{ text: 'Cách cài đặt', link: '/vi_VN/guide/installation' },
{ text: 'Cách để build?', link: '/vi_VN/guide/how-to-build' },
{ text: 'Tích hợp vào thiết bị không sử dụng GKI', link: '/vi_VN/guide/how-to-integrate-for-non-gki'},
{ text: 'Thiết bị hỗ trợ không chính thức', link: '/vi_VN/guide/unofficially-support-devices.md' },
{ text: 'FAQ - Câu hỏi thường gặp', link: '/vi_VN/guide/faq' },
]
}

View File

@@ -49,6 +49,9 @@ function sidebarGuide() {
{ text: '安装', link: '/zh_CN/guide/installation' },
{ text: '如何构建?', link: '/zh_CN/guide/how-to-build' },
{ text: '如何为非GKI设备集成 KernelSU', link: '/zh_CN/guide/how-to-integrate-for-non-gki'},
{ text: '非官方支持设备', link: '/zh_CN/guide/unofficially-support-devices.md' },
{ text: '模块开发指南', link: '/zh_CN/guide/module.md' },
{ text: '救砖', link: '/zh_CN/guide/rescue-from-bootloop.md' },
{ text: '常见问题', link: '/zh_CN/guide/faq' },
]
}

View File

@@ -0,0 +1,26 @@
# Difference with Magisk
Although there are many similarities between KernelSU modules and Magisk modules, there are inevitably some differences due to their completely different implementation mechanisms. If you want your module to run on both Magisk and KernelSU, you must understand these differences.
## Similarities
- Module file format: both use zip format to organize modules, and the format of modules is almost the same
- Module installation directory: both located in `/data/adb/modules`
- Systemless: both support modifying /system in a systemless way through modules
- post-fs-data.sh: the execution time and semantics are exactly the same
- service.sh: the execution time and semantics are exactly the same
- system.prop: completely the same
- sepolicy.rule: completely the same
- BusyBox: scripts are run in BusyBox with "standalone mode" enabled in both cases
## Differences
Before understanding the differences, you need to know how to differentiate whether your module is running in KernelSU or Magisk. You can use the environment variable `KSU` to differentiate it in all places where you can run module scripts (`customize.sh`, `post-fs-data.sh`, `service.sh`). In KernelSU, this environment variable will be set to `true`.
Here are some differences:
- KernelSU modules cannot be installed in Recovery mode.
- KernelSU modules do not have built-in support for Zygisk (but you can use Zygisk modules through [ZygiskOnKernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU).
- The method for replacing or deleting files in KernelSU modules is completely different from Magisk. KernelSU does not support the `.replace` method. Instead, you need to create a same-named file with `mknod filename c 0 0` to delete the corresponding file.
- The directories for BusyBox are different. The built-in BusyBox in KernelSU is located in `/data/adb/ksu/bin/busybox`, while in Magisk it is in `/data/adb/magisk/busybox`. **Note that this is an internal behavior of KernelSU and may change in the future!**
- KernelSU does not support `.replace` files; however, KernelSU supports the `REMOVE` and `REPLACE` variable to remove or replace files and folders.

View File

@@ -4,7 +4,7 @@
First, your devices should be able to unlock the bootloader. If it can't, then it is unsupported.
Then install KernelSU manager App to your device and open it, if it shows `Unsupported` then your device is unsupported and won't be supported in the future.
Then install KernelSU manager App to your device and open it, if it shows `Unsupported` then your device cannot be supported out of box, but you can build kernel source and integrate KernelSU to make it work or using [unofficially-support-devices](unofficially-support-devices).
## Does KernelSU need to unlock Bootloader?
@@ -12,11 +12,15 @@ 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) work now. For LSPosed, you can make it work by [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU)
## Does KernelSU support Zygisk?
KernelSU has no builtin Zygisk support, but you can use [Zygisk on KernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU) instead.
## Is KernelSU compatible with Magisk?
@@ -46,3 +50,18 @@ 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 there are many ways to switch to global mount namespace manully, such as:
1. `nsenter -t 1 -m sh` to get a shell in global mount namespace.
2. Add `nsenter --mount=/proc/1/ns/mnt` to the command you want to execute, then the command is executed in global mount namespace. KernelSU is also [using this way](https://github.com/tiann/KernelSU/blob/77056a710073d7a5f7ee38f9e77c9fd0b3256576/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt#L115)
## I am GKI1.0, can i use this?
GKI1 is completely different from GKI2, you must compile kernel by yourself.

View File

@@ -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.

Some files were not shown because too many files have changed in this diff Show More