diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index ec69d8d4c277..f094b0234e5e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -2,7 +2,6 @@ name: Feature Request description: "Suggest an idea for this project" title: "[Feature]" labels: "feature" -assignees: tiann body: - type: markdown id: feature-info diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..018b1c009ce3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,21 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + + - package-ecosystem: cargo + directory: userspace/ksud + schedule: + interval: daily + + - package-ecosystem: gradle + directory: manager + schedule: + interval: daily + + - package-ecosystem: npm + directory: website + schedule: + interval: daily diff --git a/.github/manifests/android-15-avd_aarch64.xml b/.github/manifests/android-15-avd_aarch64.xml index 6591a1af65c6..e3a5e086e202 100644 --- a/.github/manifests/android-15-avd_aarch64.xml +++ b/.github/manifests/android-15-avd_aarch64.xml @@ -1,37 +1,89 @@ + + - - - - - - - + + + + + + + + + + + + + + - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/manifests/android-15-avd_x86_64.xml b/.github/manifests/android-15-avd_x86_64.xml new file mode 100644 index 000000000000..0504d8a546b6 --- /dev/null +++ b/.github/manifests/android-15-avd_x86_64.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/workflows/add-device.yml b/.github/workflows/add-device.yml index 3d30dd446ea4..0b3a01a7ab11 100644 --- a/.github/workflows/add-device.yml +++ b/.github/workflows/add-device.yml @@ -26,7 +26,7 @@ jobs: - name: Make pull request if: steps.handle-add-device.outputs.success == 'true' id: cpr - uses: peter-evans/create-pull-request@v4 + uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "[add device]: ${{ steps.handle-add-device.outputs.device }}" @@ -53,7 +53,7 @@ jobs: 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 + uses: peter-evans/close-issue@v3 with: issue-number: ${{ github.event.issue.number }} token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/avd-kernel.yml b/.github/workflows/avd-kernel.yml index c7311222b50e..c09bda5ae8ca 100644 --- a/.github/workflows/avd-kernel.yml +++ b/.github/workflows/avd-kernel.yml @@ -88,7 +88,9 @@ jobs: 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" || printf "\nobj-y += kernelsu/\n" >> "$DRIVER_MAKEFILE" + DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig + grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" + grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" echo "[+] Apply KernelSU patches" cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found" @@ -133,32 +135,3 @@ jobs: with: name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }} path: "${{ env.file_path }}" - - - name: Bot session cache - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.ref_type == 'tag' - id: bot_session_cache - uses: actions/cache@v3 - with: - path: scripts/ksubot.session - key: ${{ runner.os }}-bot-session - - - name: Post to Telegram - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.ref_type == 'tag' - env: - CHAT_ID: ${{ secrets.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 }} - run: | - TITLE=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }} - echo "[+] title: $TITLE" - export TITLE - export VERSION="${{ env.kernelsu_version }}" - echo "[+] Image to upload" - ls -l "${{ env.file_path }}" - if [ -n "${{ secrets.BOT_TOKEN }}" ]; then - pip3 install telethon==1.31.1 - python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}" - fi diff --git a/.github/workflows/build-kernel-a12.yml b/.github/workflows/build-kernel-a12.yml index 0986c0e2cba7..633e19c5fa45 100644 --- a/.github/workflows/build-kernel-a12.yml +++ b/.github/workflows/build-kernel-a12.yml @@ -71,7 +71,7 @@ jobs: 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 telethon==1.31.1 + pip3 install telethon==1.34.0 - name: Set boot sign key env: @@ -83,7 +83,7 @@ jobs: - name: Bot session cache id: bot_session_cache - uses: actions/cache@v3 + uses: actions/cache@v4 if: false with: path: scripts/ksubot.session @@ -116,7 +116,7 @@ jobs: uses: ./.github/workflows/gki-kernel.yml with: version: android12-5.10 - version_name: android12-5.10.177 - tag: android12-5.10-2023-06 - os_patch_level: 2023-06 + version_name: android12-5.10.205 + tag: android12-5.10-2024-03 + os_patch_level: 2024-03 patch_path: "5.10" \ No newline at end of file diff --git a/.github/workflows/build-kernel-a13.yml b/.github/workflows/build-kernel-a13.yml index e2b3483fccdd..7966bb465610 100644 --- a/.github/workflows/build-kernel-a13.yml +++ b/.github/workflows/build-kernel-a13.yml @@ -48,6 +48,9 @@ jobs: - version: "5.10" sub_level: 205 os_patch_level: 2024-03 + - version: "5.10" + sub_level: 209 + os_patch_level: 2024-04 - version: "5.15" sub_level: 94 os_patch_level: 2023-05 @@ -65,7 +68,10 @@ jobs: os_patch_level: 2023-12 - version: "5.15" sub_level: 144 - os_patch_level: 2024-02 + os_patch_level: 2024-03 + - version: "5.15" + sub_level: 148 + os_patch_level: 2024-04 uses: ./.github/workflows/gki-kernel.yml secrets: inherit with: @@ -106,7 +112,7 @@ jobs: 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 telethon==1.31.1 + pip3 install telethon==1.34.0 - name: Set boot sign key env: @@ -118,7 +124,7 @@ jobs: - name: Bot session cache id: bot_session_cache - uses: actions/cache@v3 + uses: actions/cache@v4 if: false with: path: scripts/ksubot.session diff --git a/.github/workflows/build-kernel-a14.yml b/.github/workflows/build-kernel-a14.yml index d0ca90851872..fae679b982c2 100644 --- a/.github/workflows/build-kernel-a14.yml +++ b/.github/workflows/build-kernel-a14.yml @@ -33,6 +33,9 @@ jobs: - version: "5.15" sub_level: 144 os_patch_level: 2024-03 + - version: "5.15" + sub_level: 148 + os_patch_level: 2024-04 - version: "6.1" sub_level: 25 os_patch_level: 2023-10 @@ -45,6 +48,9 @@ jobs: - version: "6.1" sub_level: 68 os_patch_level: 2024-03 + - version: "6.1" + sub_level: 75 + os_patch_level: 2024-04 uses: ./.github/workflows/gki-kernel.yml secrets: inherit with: @@ -85,7 +91,7 @@ jobs: 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 telethon==1.31.1 + pip3 install telethon==1.34.0 - name: Set boot sign key env: @@ -97,7 +103,7 @@ jobs: - name: Bot session cache id: bot_session_cache - uses: actions/cache@v3 + uses: actions/cache@v4 if: false with: path: scripts/ksubot.session @@ -134,8 +140,8 @@ jobs: sub_level: 110 os_patch_level: 2023-09 - version: "6.1" - sub_level: 68 - os_patch_level: 2024-03 + sub_level: 75 + os_patch_level: 2024-04 uses: ./.github/workflows/gki-kernel.yml with: version: android14-${{ matrix.version }} diff --git a/.github/workflows/build-kernel-arcvm.yml b/.github/workflows/build-kernel-arcvm.yml index f54365c16880..18c97c2f949d 100644 --- a/.github/workflows/build-kernel-arcvm.yml +++ b/.github/workflows/build-kernel-arcvm.yml @@ -13,17 +13,23 @@ on: workflow_call: workflow_dispatch: +env: + git_tag: chromeos-5.10-arcvm + jobs: build: if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && !github.event.pull_request.draft) strategy: matrix: - arch: [x86_64] - version: ["5.10.178"] include: - arch: x86_64 - git_tag: chromeos-5.10-arcvm - file_name: "bzImage" + kernel_image_name: bzImage + build_config: build.config.gki.x86_64 + defconfig: x86_64_arcvm_defconfig + - arch: arm64 + kernel_image_name: Image + build_config: build.config.gki.aarch64 + defconfig: arm64_arcvm_defconfig name: Build ChromeOS ARCVM kernel runs-on: ubuntu-20.04 @@ -66,8 +72,17 @@ jobs: fetch-depth: 0 - name: Setup kernel source - run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ matrix.git_tag }} --depth=1 + run: git clone https://chromium.googlesource.com/chromiumos/third_party/kernel.git -b ${{ env.git_tag }} --depth=1 + - name: Extract version from Makefile + working-directory: kernel + run: | + VERSION=$(grep -E '^VERSION = ' Makefile | awk '{print $3}') + PATCHLEVEL=$(grep -E '^PATCHLEVEL = ' Makefile | awk '{print $3}') + SUBLEVEL=$(grep -E '^SUBLEVEL = ' Makefile | awk '{print $3}') + echo "ChromeOS ARCVM Linux kernel version: $VERSION.$PATCHLEVEL.$SUBLEVEL" + echo "version=$VERSION.$PATCHLEVEL.$SUBLEVEL" >> $GITHUB_ENV + - name: Setup KernelSU working-directory: kernel run: | @@ -89,15 +104,18 @@ jobs: echo "[+] KernelSU setup done." cd $GITHUB_WORKSPACE/KernelSU - VERSION=$(($(git rev-list --count HEAD) + 10200)) - echo "VERSION: $VERSION" - echo "kernelsu_version=$VERSION" >> $GITHUB_ENV + KSU_VERSION=$(($(git rev-list --count HEAD) + 10200)) + echo "KernelSU version: $KSU_VERSION" + echo "kernelsu_version=$KSU_VERSION" >> $GITHUB_ENV - name: Build Kernel working-directory: kernel + env: + KERNEL_IMAGE_NAME: ${{ matrix.kernel_image_name }} + ARCH: ${{ matrix.arch }} run: | - set -a && . build.config.gki.x86_64; set +a - export DEFCONFIG=x86_64_arcvm_defconfig + set -a && . ${{ matrix.build_config }}; set +a + export DEFCONFIG=${{ matrix.defconfig }} if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }} export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }} @@ -106,43 +124,12 @@ jobs: make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} mrproper make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} ${DEFCONFIG} < /dev/null scripts/config --file .config -e LTO_CLANG -d LTO_NONE -e LTO_CLANG_THIN -d LTO_CLANG_FULL -e THINLTO - make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} -j$(nproc) bzImage modules prepare-objtool - - echo "file_path=${PWD}/arch/x86/boot/bzImage" >> $GITHUB_ENV + make LLVM=1 LLVM_IAS=1 DEPMOD=depmod DTC=dtc O=${PWD} -j$(nproc) ${KERNEL_IMAGE_NAME} modules prepare-objtool + ls -l -h ${PWD}/arch/${ARCH}/boot + echo "file_path=${PWD}/arch/${ARCH}/boot/${KERNEL_IMAGE_NAME}" >> $GITHUB_ENV - - name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }} + - name: Upload kernel-ARCVM-${{ matrix.arch }}-${{ env.version }} uses: actions/upload-artifact@v4 with: - name: kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }} + name: kernel-ARCVM-${{ matrix.arch }}-${{ env.version }} path: "${{ env.file_path }}" - - - name: Bot session cache - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }} - id: bot_session_cache - uses: actions/cache@v3 - with: - path: scripts/ksubot.session - key: ${{ runner.os }}-bot-session - - - name: Post to Telegram - if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }} - env: - CHAT_ID: ${{ secrets.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 }} - run: | - TITLE=kernel-ARCVM-${{ matrix.arch }}-${{ matrix.version }} - echo "[+] title: $TITLE" - export TITLE - export VERSION="${{ env.kernelsu_version }}" - echo "[+] Compress images" - gzip -n -f -9 "${{ env.file_path }}" - echo "[+] Image to upload" - ls -l "${{ env.file_path }}.gz" - if [ -n "${{ secrets.BOT_TOKEN }}" ]; then - pip3 install telethon==1.31.1 - python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz" - fi diff --git a/.github/workflows/build-kernel-avd.yml b/.github/workflows/build-kernel-avd.yml index d66d8ae1e06f..504a5207ee10 100644 --- a/.github/workflows/build-kernel-avd.yml +++ b/.github/workflows/build-kernel-avd.yml @@ -30,6 +30,9 @@ jobs: - version: "android-15-avd_aarch64" manifest: "android-15-avd_aarch64.xml" arch: "aarch64" + - version: "android-15-avd_x86_64" + manifest: "android-15-avd_x86_64.xml" + arch: "x86_64" with: version_name: ${{ matrix.version }} manifest_name: ${{ matrix.manifest }} diff --git a/.github/workflows/build-ksud.yml b/.github/workflows/build-ksud.yml deleted file mode 100644 index 45e853b917f4..000000000000 --- a/.github/workflows/build-ksud.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Build KSUD -on: - push: - 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-lkm: - uses: ./.github/workflows/build-lkm.yml - secrets: inherit - build: - needs: build-lkm - strategy: - matrix: - include: - - target: aarch64-linux-android - os: ubuntu-latest - - target: x86_64-linux-android - os: ubuntu-latest - - target: x86_64-pc-windows-gnu # windows pc - os: ubuntu-latest - - target: x86_64-apple-darwin # Intel mac - os: macos-latest - - target: aarch64-apple-darwin # M chip mac - os: macos-latest - - target: aarch64-unknown-linux-musl # arm64 Linux - os: ubuntu-latest - - target: x86_64-unknown-linux-musl # x86 Linux - os: ubuntu-latest - uses: ./.github/workflows/ksud.yml - with: - target: ${{ matrix.target }} - os: ${{ matrix.os }} diff --git a/.github/workflows/build-lkm.yml b/.github/workflows/build-lkm.yml index f9fe6d7cbf2e..8f207ce9d1b1 100644 --- a/.github/workflows/build-lkm.yml +++ b/.github/workflows/build-lkm.yml @@ -3,15 +3,11 @@ on: push: branches: ["main", "ci", "checkci"] paths: - - ".github/workflows/gki-kernel.yml" - ".github/workflows/build-lkm.yml" - - "kernel/**" pull_request: branches: ["main"] paths: - - ".github/workflows/gki-kernel.yml" - ".github/workflows/build-lkm.yml" - - "kernel/**" workflow_call: jobs: build-lkm: diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index 5b9f64c52ff8..8be2f7a957d4 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -2,10 +2,11 @@ name: Build Manager on: push: - branches: [ "main" ] + branches: [ "main", "ci" ] paths: - '.github/workflows/build-manager.yml' - 'manager/**' + - 'kernel/**' - 'userspace/ksud/**' pull_request: branches: [ "main" ] @@ -24,10 +25,23 @@ jobs: matrix: include: - target: aarch64-linux-android + os: ubuntu-latest - target: x86_64-linux-android + os: ubuntu-latest + - target: x86_64-pc-windows-gnu # windows pc + os: ubuntu-latest + - target: x86_64-apple-darwin # Intel mac + os: macos-latest + - target: aarch64-apple-darwin # M chip mac + os: macos-latest + - target: aarch64-unknown-linux-musl # arm64 Linux + os: ubuntu-latest + - target: x86_64-unknown-linux-musl # x86 Linux + os: ubuntu-latest uses: ./.github/workflows/ksud.yml with: target: ${{ matrix.target }} + os: ${{ matrix.os }} build-manager: needs: build-ksud @@ -59,7 +73,7 @@ jobs: echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}' echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' - echo KEYSTORE_FILE='../key.jks' + echo KEYSTORE_FILE='key.jks' } >> gradle.properties echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks fi @@ -67,11 +81,11 @@ jobs: - name: Setup Java uses: actions/setup-java@v4 with: - distribution: "temurin" - java-version: "17" + distribution: temurin + java-version: 21 - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 with: gradle-home-cache-cleanup: true @@ -107,12 +121,14 @@ jobs: - name: Upload build artifact uses: actions/upload-artifact@v4 + if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }} with: name: manager path: manager/app/build/outputs/apk/release/*.apk - name: Upload mappings uses: actions/upload-artifact@v4 + if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }} with: name: "mappings" path: "manager/app/build/outputs/mapping/release/" @@ -139,6 +155,6 @@ jobs: if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then export VERSION=$(git rev-list --count HEAD) APK=$(find ./app/build/outputs/apk/release -name "*.apk") - pip3 install telethon==1.31.1 + pip3 install telethon==1.34.0 python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK - fi + fi \ No newline at end of file diff --git a/.github/workflows/build-su.yml b/.github/workflows/build-su.yml index 76f7e900bfe8..2fbb93a9a485 100644 --- a/.github/workflows/build-su.yml +++ b/.github/workflows/build-su.yml @@ -28,7 +28,7 @@ jobs: fi - uses: nttld/setup-ndk@v1 with: - ndk-version: r25c + ndk-version: r26d - name: Build su working-directory: ./userspace/su run: ndk-build @@ -36,29 +36,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: su - path: ./userspace/su/libs - - name: Bot session cache - if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' - id: bot_session_cache - uses: actions/cache@v3 - with: - path: scripts/ksubot.session - key: ${{ runner.os }}-bot-session - - name: Upload to telegram - if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' - env: - CHAT_ID: ${{ secrets.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: SU - run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - export VERSION=$(git rev-list --count HEAD) - pip3 install telethon==1.31.1 - mv ./userspace/su/libs/arm64-v8a/su su-arm64 - mv ./userspace/su/libs/x86_64/su su-x86_64 - python3 scripts/ksubot.py su-arm64 su-x86_64 - fi + path: ./userspace/su/libs \ No newline at end of file diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 7e8139b362ea..2ea5bc48b5eb 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -22,14 +22,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - # cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222 - - run: rustup default 1.67.0 + - run: rustup update --force-non-host stable-x86_64-unknown-linux-gnu - uses: Swatinem/rust-cache@v2 with: workspaces: userspace/ksud - name: Install cross - run: cargo install cross --locked + run: | + cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 - name: Run clippy run: | diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index 2ac07cf0ef11..b2b2b763556f 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -37,11 +37,11 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 cache: yarn # or pnpm / yarn cache-dependency-path: website/yarn.lock - name: Setup Pages - uses: actions/configure-pages@v4 + uses: actions/configure-pages@v5 - name: Install dependencies run: yarn install --frozen-lockfile - name: Build with VitePress diff --git a/.github/workflows/gki-kernel.yml b/.github/workflows/gki-kernel.yml index a49d2e9741c5..a70977d9a9d4 100644 --- a/.github/workflows/gki-kernel.yml +++ b/.github/workflows/gki-kernel.yml @@ -154,7 +154,7 @@ jobs: - name: Setup ccache if: inputs.use_cache == true - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@v1 with: key: gki-kernel-aarch64-${{ inputs.version_name }} max-size: 2G @@ -248,4 +248,4 @@ jobs: if: ${{ inputs.build_lkm == true }} with: name: ${{ inputs.version }}-lkm - path: ./output/*_kernelsu.ko + path: ./output/*_kernelsu.ko \ No newline at end of file diff --git a/.github/workflows/ksud.yml b/.github/workflows/ksud.yml index 0480e79e64fc..e0fe80a1a6d3 100644 --- a/.github/workflows/ksud.yml +++ b/.github/workflows/ksud.yml @@ -9,6 +9,10 @@ on: required: false type: string default: ubuntu-latest + pack_lkm: + required: false + type: boolean + default: true use_cache: required: false type: boolean @@ -25,13 +29,13 @@ jobs: uses: actions/download-artifact@v4 - name: Prepare LKM fies + if: ${{ inputs.pack_lkm }} run: | cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/ - # cross build failed after Rust 1.68, see https://github.com/cross-rs/cross/issues/1222 - name: Setup rustup run: | - rustup default 1.67.0 + rustup update stable rustup target add x86_64-apple-darwin rustup target add aarch64-apple-darwin - uses: Swatinem/rust-cache@v2 @@ -40,10 +44,11 @@ jobs: cache-targets: false - name: Install cross - run: cargo install cross --locked + run: | + cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 - name: Build ksud - run: cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml + run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml - name: Upload ksud artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4226ec20b20e..26b3171c8e68 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,7 +67,7 @@ jobs: run: ls -R - name: release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: | manager/*.apk @@ -76,3 +76,4 @@ jobs: boot-images-*/Image-*/*.img.gz kernel-WSA*.zip kernel-ARCVM*.zip + ksud-* diff --git a/.github/workflows/wsa-kernel.yml b/.github/workflows/wsa-kernel.yml index b7db060e3d44..1f87e4975411 100644 --- a/.github/workflows/wsa-kernel.yml +++ b/.github/workflows/wsa-kernel.yml @@ -15,7 +15,7 @@ on: jobs: build: name: Build WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion" CCACHE_NOHASHDIR: "true" @@ -30,7 +30,7 @@ jobs: - name: Cache LLVM id: cache-llvm - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ./llvm key: llvm-12.0.1 @@ -57,7 +57,7 @@ jobs: path: WSA-Linux-Kernel - name: Setup Ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@v1 with: key: WSA-Kernel-${{ inputs.version }}-${{ inputs.arch }} save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} @@ -73,7 +73,9 @@ jobs: 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 + DRIVER_KCONFIG=$KERNEL_ROOT/drivers/Kconfig + grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" + grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" echo "[+] Apply KernelSU patches" cd $KERNEL_ROOT && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/5.15/*.patch || echo "[-] No patch found" echo "[+] KernelSU setup done." @@ -101,35 +103,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: kernel-WSA-${{ inputs.arch }}-${{ inputs.version }} - path: "${{ env.file_path }}" - - - name: Bot session cache - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.ref_type == 'tag' - id: bot_session_cache - uses: actions/cache@v3 - with: - path: scripts/ksubot.session - key: ${{ runner.os }}-bot-session - - - name: Post to Telegram - if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.ref_type == 'tag' - env: - CHAT_ID: ${{ secrets.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 }} - run: | - TITLE=kernel-${{ inputs.arch }}-WSA-${{ inputs.version }} - echo "[+] title: $TITLE" - export TITLE - export VERSION="${{ env.kernelsu_version }}" - echo "[+] Compress images" - gzip -n -f -9 "${{ env.file_path }}" - echo "[+] Image to upload" - ls -l "${{ env.file_path }}.gz" - if [ -n "${{ secrets.BOT_TOKEN }}" ]; then - pip3 install telethon==1.31.1 - python3 "$GITHUB_WORKSPACE/KernelSU/scripts/ksubot.py" "${{ env.file_path }}.gz" - fi + path: "${{ env.file_path }}" \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 7131a49a951c..0128cd3d221e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +**English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU diff --git a/docs/README_CN.md b/docs/README_CN.md index 50bbd71780ea..dc34b86ab139 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | **简体中文** | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU diff --git a/docs/README_ES.md b/docs/README_ES.md index 518800989525..ed4f699b791d 100644 --- a/docs/README_ES.md +++ b/docs/README_ES.md @@ -1,4 +1,4 @@ -[English](README.md) | **Español** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | **Español** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU diff --git a/docs/README_ID.md b/docs/README_ID.md index 3a077ab910f5..511683a7503d 100644 --- a/docs/README_ID.md +++ b/docs/README_ID.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | **Indonesia** | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | **Indonesia** | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU diff --git a/docs/README_IN.md b/docs/README_IN.md index 33ddde5e55a5..7396ab5c2b10 100644 --- a/docs/README_IN.md +++ b/docs/README_IN.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | **हिंदी** +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | **हिंदी** # KernelSU diff --git a/docs/README_IW.md b/docs/README_IW.md index 83bd4f1e3473..5997487e2a08 100644 --- a/docs/README_IW.md +++ b/docs/README_IW.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | **עברית** | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | **עברית** | [हिंदी](README_IN.md) # KernelSU diff --git a/docs/README_JP.md b/docs/README_JP.md index 60ccf2e8bb60..093618753775 100644 --- a/docs/README_JP.md +++ b/docs/README_JP.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | **日本語** | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU diff --git a/docs/README_KR.md b/docs/README_KR.md new file mode 100644 index 000000000000..3226008920a1 --- /dev/null +++ b/docs/README_KR.md @@ -0,0 +1,57 @@ +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **한국어** | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) + +# KernelSU + +logo + +안드로이드 기기에서 사용되는 커널 기반 루팅 솔루션입니다. + +[![Latest release](https://img.shields.io/github/v/release/tiann/KernelSU?label=Release&logo=github)](https://github.com/tiann/KernelSU/releases/latest) +[![Weblate](https://img.shields.io/badge/Localization-Weblate-teal?logo=weblate)](https://hosted.weblate.org/engage/kernelsu) +[![Channel](https://img.shields.io/badge/Follow-Telegram-blue.svg?logo=telegram)](https://t.me/KernelSU) +[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) +[![GitHub License](https://img.shields.io/github/license/tiann/KernelSU?logo=gnu)](/LICENSE) + +## 기능들 + +1. 커널 기반 `su` 및 루트 액세스 관리. +2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 기반 모듈 시스템. +3. [App Profile](https://kernelsu.org/guide/app-profile.html): 루트 권한을 케이지에 가둡니다. + +## 호환 상태 + +KernelSU는 공식적으로 안드로이드 GKI 2.0 디바이스(커널 5.10 이상)를 지원합니다. 오래된 커널(4.14 이상)도 사용할 수 있지만, 커널을 수동으로 빌드해야 합니다. + +KernelSU는 WSA, ChromeOS, 컨테이너 기반 안드로이드 모두를 지원합니다. + +현재는 `arm64-v8a`와 `x86_64`만 지원됩니다. + +## 사용 방법 + +- [설치 방법](https://kernelsu.org/guide/installation.html) +- [어떻게 빌드하나요?](https://kernelsu.org/guide/how-to-build.html) +- [공식 웹사이트](https://kernelsu.org/) + +## 번역 + +KernelSU 번역을 돕거나 기존 번역을 개선하려면 [Weblate](https://hosted.weblate.org/engage/kernelsu/)를 이용해 주세요. 매니저의 번역은 Weblate와 충돌할 수 있으므로 더 이상 허용되지 않습니다. + +## 토론 + +- 텔레그램: [@KernelSU](https://t.me/KernelSU) + +## 보안 + +KernelSU의 보안 취약점 보고에 대한 자세한 내용은 [SECURITY.md](/SECURITY.md)를 참조하세요. + +## 저작권 + +- `kernel` 디렉터리 아래의 파일은 [GPL-2.0 전용](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)입니다. +- `kernel` 디렉토리를 제외한 다른 모든 부분은 [GPL-3.0-이상](https://www.gnu.org/licenses/gpl-3.0.html)입니다. + +## 크래딧 + +- [kernel-assisted-superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU의 아이디어. +- [Magisk](https://github.com/topjohnwu/Magisk): 강력한 루팅 도구. +- [genuine](https://github.com/brevent/genuine/): apk v2 서명 유효성 검사. +- [Diamorphine](https://github.com/m0nad/Diamorphine): 일부 rootkit 스킬. diff --git a/docs/README_PL.md b/docs/README_PL.md index 69d7fa2ae722..ab9066296dd9 100644 --- a/docs/README_PL.md +++ b/docs/README_PL.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | **Polski** | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | **Polski** | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU diff --git a/docs/README_PT-BR.md b/docs/README_PT-BR.md index 8509f8d50010..b5421e7f9be0 100644 --- a/docs/README_PT-BR.md +++ b/docs/README_PT-BR.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | **Português (Brasil)** | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | **Português (Brasil)** | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU @@ -29,7 +29,7 @@ Atualmente, apenas `arm64-v8a` e `x86_64` são suportados. ## Uso - [Instalação](https://kernelsu.org/pt_BR/guide/installation.html) - - [Como construir o KernelSU?](https://kernelsu.org/pt_BR/guide/how-to-build.html) + - [Como compilar o KernelSU?](https://kernelsu.org/pt_BR/guide/how-to-build.html) - [Site oficial](https://kernelsu.org/pt_BR/) ## Tradução diff --git a/docs/README_RU.md b/docs/README_RU.md index 7fdb275e40c9..7e50f47adfe5 100644 --- a/docs/README_RU.md +++ b/docs/README_RU.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | **Русский** | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | **Русский** | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU diff --git a/docs/README_TR.md b/docs/README_TR.md index adfdebf47ddc..bc3363e8b490 100644 --- a/docs/README_TR.md +++ b/docs/README_TR.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | **Türkçe** | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | **Türkçe** | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU @@ -34,7 +34,7 @@ Bununla birlikte; WSA, ChromeOS ve konteyner tabanlı Android'in tamamı destekl ## Çeviri -KernelSU'nun çevirisine veya mevcut çevirilerin iyileştirilmesine yardımcı olmak için lütfen [Weblate](https://hosted.weblate.org/engage/kernelsu/) kullanın. Yönetici uygulamasının PR ile çevirisi, Weblate ile çakışacağından artık kabul edilmeyecektir. +KernelSU'nun başka dillere çevrilmesine veya mevcut çevirilerin iyileştirilmesine yardımcı olmak için lütfen [Weblate](https://hosted.weblate.org/engage/kernelsu/) kullanın. Yönetici uygulamasının PR ile çevirisi, Weblate ile çakışacağından artık kabul edilmeyecektir. ## Tartışma diff --git a/docs/README_TW.md b/docs/README_TW.md index 459931e65f11..d025e1742a67 100644 --- a/docs/README_TW.md +++ b/docs/README_TW.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | **繁體中文** | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU @@ -16,12 +16,13 @@ - 基於核心的 `su` 和 Root 存取權管理。 - 基於 [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模組系統。 +- [App Profile](https://kernelsu.org/zh_TW/guide/app-profile.html): 將 Root 的權限鎖在牢籠中. ## 相容性狀態 -KernelSU 官方支援 Android GKI 2.0 的裝置 (核心版本 5.10+);舊版核心同樣相容 (最低 4.14+),但需要自行編譯核心。 +KernelSU 官方支援 Android GKI 2.0 的裝置 (核心版本 5.10+ );舊版核心同樣相容 (最低 4.14+ ),但需要自行編譯核心。 -WSA 和執行在容器中的 Android 也可以與 KernelSU 一同運作。 +WSA和ChromeOS和執行在容器中的 Android 也可以與 KernelSU 一同運作。 目前支援架構:`arm64-v8a` 和 `x86_64`。 @@ -29,11 +30,19 @@ WSA 和執行在容器中的 Android 也可以與 KernelSU 一同運作。 - [安裝教學](https://kernelsu.org/zh_TW/guide/installation.html) - [如何建置?](https://kernelsu.org/zh_TW/guide/how-to-build.html) +- [官方網站](https://kernelsu.org/zh_TW/) + +## 翻譯 + +若要協助翻譯 KernelSU 或改進現有翻譯,請使用 [Weblate](https://hosted.weblate.org/engage/kernelsu/)。 翻譯管理器的PR不再被接受,因為它會與Weblate衝突。 ### 討論 - Telegram:[@KernelSU](https://t.me/KernelSU) +## 安全 +有關報告 KernelSU 中的安全漏洞的資訊,請參閱 [SECURITY.md](/SECURITY.md)。 + ## 授權 - 目錄 `kernel` 下所有檔案為 [GPL-2-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。 diff --git a/docs/README_VI.md b/docs/README_VI.md index d2c8be442a9d..7dd3b4227aec 100644 --- a/docs/README_VI.md +++ b/docs/README_VI.md @@ -1,4 +1,4 @@ -[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | **Tiếng Việt** | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) +[English](README.md) | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | **Tiếng Việt** | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) # KernelSU diff --git a/kernel/Makefile b/kernel/Makefile index 85a4491e61f6..9fec3ef387b7 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,10 +1,8 @@ kernelsu-objs := ksu.o kernelsu-objs += allowlist.o kernelsu-objs += apk_sign.o -kernelsu-objs += module_api.o kernelsu-objs += sucompat.o -kernelsu-objs += uid_observer.o -kernelsu-objs += manager.o +kernelsu-objs += throne_tracker.o kernelsu-objs += core_hook.o kernelsu-objs += ksud.o kernelsu-objs += embed_ksud.o diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 0e154a6b0445..9daceef2011b 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -1,19 +1,20 @@ -#include "ksu.h" -#include "linux/compiler.h" -#include "linux/fs.h" -#include "linux/gfp.h" -#include "linux/kernel.h" -#include "linux/list.h" -#include "linux/printk.h" -#include "linux/slab.h" -#include "linux/types.h" -#include "linux/version.h" -#include "linux/compiler_types.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ksu.h" #include "klog.h" // IWYU pragma: keep #include "selinux/selinux.h" #include "kernel_compat.h" #include "allowlist.h" +#include "manager.h" #define FILE_MAGIC 0x7f4b5355 // ' KSU', u32 #define FILE_FORMAT_VERSION 3 // u32 @@ -272,6 +273,11 @@ bool __ksu_is_allow_uid(uid_t uid) return false; } + if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) { + // manager is always allowed! + return true; + } + if (likely(uid <= BITMAP_UID_MAX)) { return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE))); } else { @@ -287,6 +293,10 @@ bool __ksu_is_allow_uid(uid_t uid) bool ksu_uid_should_umount(uid_t uid) { struct app_profile profile = { .current_uid = uid }; + if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) { + // we should not umount on manager! + return false; + } bool found = ksu_get_app_profile(&profile); if (!found) { // no app profile found, it must be non root app diff --git a/kernel/allowlist.h b/kernel/allowlist.h index 298624bca4ee..e89bf71fa10c 100644 --- a/kernel/allowlist.h +++ b/kernel/allowlist.h @@ -1,7 +1,7 @@ #ifndef __KSU_H_ALLOWLIST #define __KSU_H_ALLOWLIST -#include "linux/types.h" +#include #include "ksu.h" void ksu_allowlist_init(void); diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index fda32ac7e9ea..ba8b73f2eb2b 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -1,21 +1,23 @@ -#include "linux/err.h" -#include "linux/fs.h" -#include "linux/gfp.h" -#include "linux/kernel.h" -#include "linux/moduleparam.h" +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KSU_DEBUG +#include +#endif +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) +#include +#else +#include +#endif #include "apk_sign.h" #include "klog.h" // IWYU pragma: keep #include "kernel_compat.h" -#include "crypto/hash.h" -#include "linux/slab.h" -#include "linux/version.h" -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) -#include "crypto/sha2.h" -#else -#include "crypto/sha.h" -#endif struct sdesc { struct shash_desc shash; @@ -188,7 +190,7 @@ static __always_inline bool check_v2_signature(char *path, struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); if (IS_ERR(fp)) { pr_err("open %s error.\n", path); - return PTR_ERR(fp); + return false; } // disable inotify for this file @@ -229,7 +231,8 @@ static __always_inline bool check_v2_signature(char *path, goto clean; } - for (;;) { + int loop_count = 0; + while (loop_count++ < 10) { uint32_t id; uint32_t offset; ksu_kernel_read_compat(fp, &size8, 0x8, @@ -239,7 +242,6 @@ static __always_inline bool check_v2_signature(char *path, } ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id offset = 4; - pr_info("id: 0x%08x\n", id); if (id == 0x7109871au) { v2_signing_blocks++; v2_signing_valid = @@ -251,13 +253,19 @@ static __always_inline bool check_v2_signature(char *path, } else if (id == 0x1b93ad61u) { // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74 v3_1_signing_exist = true; + } else { +#ifdef CONFIG_KSU_DEBUG + pr_info("Unknown id: 0x%08x\n", id); +#endif } pos += (size8 - offset); } if (v2_signing_blocks != 1) { +#ifdef CONFIG_KSU_DEBUG pr_err("Unexpected v2 signature count: %d\n", v2_signing_blocks); +#endif v2_signing_valid = false; } @@ -273,7 +281,9 @@ static __always_inline bool check_v2_signature(char *path, filp_close(fp, 0); if (v3_signing_exist || v3_1_signing_exist) { +#ifdef CONFIG_KSU_DEBUG pr_err("Unexpected v3 signature scheme found!\n"); +#endif return false; } @@ -282,25 +292,15 @@ static __always_inline bool check_v2_signature(char *path, #ifdef CONFIG_KSU_DEBUG -unsigned ksu_expected_size = EXPECTED_SIZE; -const char *ksu_expected_hash = EXPECTED_HASH; +int ksu_debug_manager_uid = -1; #include "manager.h" static int set_expected_size(const char *val, const struct kernel_param *kp) { int rv = param_set_uint(val, kp); - ksu_invalidate_manager_uid(); - pr_info("ksu_expected_size set to %x\n", ksu_expected_size); - return rv; -} - -static int set_expected_hash(const char *val, const struct kernel_param *kp) -{ - pr_info("set_expected_hash: %s\n", val); - int rv = param_set_charp(val, kp); - ksu_invalidate_manager_uid(); - pr_info("ksu_expected_hash set to %s\n", ksu_expected_hash); + ksu_set_manager_uid(ksu_debug_manager_uid); + pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid); return rv; } @@ -309,27 +309,12 @@ static struct kernel_param_ops expected_size_ops = { .get = param_get_uint, }; -static struct kernel_param_ops expected_hash_ops = { - .set = set_expected_hash, - .get = param_get_charp, - .free = param_free_charp, -}; - -module_param_cb(ksu_expected_size, &expected_size_ops, &ksu_expected_size, - S_IRUSR | S_IWUSR); -module_param_cb(ksu_expected_hash, &expected_hash_ops, &ksu_expected_hash, - S_IRUSR | S_IWUSR); +module_param_cb(ksu_debug_manager_uid, &expected_size_ops, + &ksu_debug_manager_uid, S_IRUSR | S_IWUSR); -bool is_manager_apk(char *path) -{ - return check_v2_signature(path, ksu_expected_size, ksu_expected_hash); -} - -#else +#endif bool is_manager_apk(char *path) { return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH); -} - -#endif +} \ No newline at end of file diff --git a/kernel/apk_sign.h b/kernel/apk_sign.h index ebd78a8b0b23..bed501c49264 100644 --- a/kernel/apk_sign.h +++ b/kernel/apk_sign.h @@ -1,7 +1,7 @@ #ifndef __KSU_H_APK_V2_SIGN #define __KSU_H_APK_V2_SIGN -#include "linux/types.h" +#include bool is_manager_apk(char *path); diff --git a/kernel/arch.h b/kernel/arch.h index 5925e7f11041..8199f6fa0f9c 100644 --- a/kernel/arch.h +++ b/kernel/arch.h @@ -1,7 +1,7 @@ #ifndef __KSU_H_ARCH #define __KSU_H_ARCH -#include "linux/version.h" +#include #if defined(__aarch64__) @@ -19,6 +19,10 @@ #define __PT_IP_REG pc #define PRCTL_SYMBOL "__arm64_sys_prctl" +#define SYS_READ_SYMBOL "__arm64_sys_read" +#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat" +#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat" +#define SYS_EXECVE_SYMBOL "__arm64_sys_execve" #elif defined(__x86_64__) @@ -36,6 +40,10 @@ #define __PT_SP_REG sp #define __PT_IP_REG ip #define PRCTL_SYMBOL "__x64_sys_prctl" +#define SYS_READ_SYMBOL "__x64_sys_read" +#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat" +#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat" +#define SYS_EXECVE_SYMBOL "__x64_sys_execve" #else #error "Unsupported arch" @@ -59,4 +67,10 @@ #define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) #define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +#define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs)) +#else +#define PT_REAL_REGS(regs) ((regs)) +#endif + #endif diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 324e4f8585dc..6719f7ee5def 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -1,31 +1,36 @@ -#include "linux/capability.h" -#include "linux/cred.h" -#include "linux/dcache.h" -#include "linux/err.h" -#include "linux/init.h" -#include "linux/init_task.h" -#include "linux/kallsyms.h" -#include "linux/kernel.h" -#include "linux/kprobes.h" -#include "linux/list.h" -#include "linux/lsm_hooks.h" -#include "linux/mm.h" -#include "linux/mm_types.h" -#include "linux/nsproxy.h" -#include "linux/path.h" -#include "linux/printk.h" -#include "linux/sched.h" -#include "linux/security.h" -#include "linux/stddef.h" -#include "linux/types.h" -#include "linux/uaccess.h" -#include "linux/uidgid.h" -#include "linux/version.h" -#include "linux/mount.h" - -#include "linux/fs.h" -#include "linux/namei.h" -#include "linux/rcupdate.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef MODULE +#include +#include +#include +#include +#include +#endif #include "allowlist.h" #include "arch.h" @@ -33,10 +38,10 @@ #include "klog.h" // IWYU pragma: keep #include "ksu.h" #include "ksud.h" -#include "linux/vmalloc.h" #include "manager.h" #include "selinux/selinux.h" -#include "uid_observer.h" +#include "throne_tracker.h" +#include "throne_tracker.h" #include "kernel_compat.h" static bool ksu_module_mounted = false; @@ -52,16 +57,11 @@ static inline bool is_allow_su() return ksu_is_allow_uid(current_uid().val); } -static inline bool is_isolated_uid(uid_t uid) +static inline bool is_unsupported_uid(uid_t uid) { -#define FIRST_ISOLATED_UID 99000 -#define LAST_ISOLATED_UID 99999 -#define FIRST_APP_ZYGOTE_ISOLATED_UID 90000 -#define LAST_APP_ZYGOTE_ISOLATED_UID 98999 +#define LAST_APPLICATION_UID 19999 uid_t appid = uid % 100000; - return (appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID) || - (appid >= FIRST_APP_ZYGOTE_ISOLATED_UID && - appid <= LAST_APP_ZYGOTE_ISOLATED_UID); + return appid > LAST_APPLICATION_UID; } static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; @@ -199,7 +199,7 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry) pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname, new_dentry->d_iname, buf); - update_uid(); + track_throne(); return 0; } @@ -215,13 +215,11 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, return 0; } - // always ignore isolated app uid - if (is_isolated_uid(current_uid().val)) { - return 0; - } + bool from_root = 0 == current_uid().val; + bool from_manager = is_manager(); - static uid_t last_failed_uid = -1; - if (last_failed_uid == current_uid().val) { + if (!from_root && !from_manager) { + // only root or manager can access this interface return 0; } @@ -230,75 +228,12 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, #endif if (arg2 == CMD_BECOME_MANAGER) { - // quick check - if (is_manager()) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("become_manager: prctl reply error\n"); - } - return 0; - } - if (ksu_is_manager_uid_valid()) { -#ifdef CONFIG_KSU_DEBUG - pr_info("manager already exist: %d\n", - ksu_get_manager_uid()); -#endif - return 0; - } - - // someone wants to be root manager, just check it! - // arg3 should be `/data/user//` - char param[128]; - if (ksu_strncpy_from_user_nofault(param, arg3, sizeof(param)) == - -EFAULT) { -#ifdef CONFIG_KSU_DEBUG - pr_err("become_manager: copy param err\n"); -#endif - goto block; - } - - // for user 0, it is /data/data - // for user 999, it is /data/user/999 - const char *prefix; - char prefixTmp[64]; - int userId = current_uid().val / 100000; - if (userId == 0) { - prefix = "/data/data"; - } else { - snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d", - userId); - prefix = prefixTmp; - } - - if (startswith(param, (char *)prefix) != 0) { - pr_info("become_manager: invalid param: %s\n", param); - goto block; - } - - // stat the param, app must have permission to do this - // otherwise it may fake the path! - struct path path; - if (kern_path(param, LOOKUP_DIRECTORY, &path)) { - pr_err("become_manager: kern_path err\n"); - goto block; - } - uid_t inode_uid = path.dentry->d_inode->i_uid.val; - path_put(&path); - if (inode_uid != current_uid().val) { - pr_err("become_manager: path uid != current uid\n"); - goto block; - } - char *pkg = param + strlen(prefix); - pr_info("become_manager: param pkg: %s\n", pkg); - - bool success = become_manager(pkg); - if (success) { + if (from_manager) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { pr_err("become_manager: prctl reply error\n"); } return 0; } - block: - last_failed_uid = current_uid().val; return 0; } @@ -315,25 +250,23 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, // Both root manager and root processes should be allowed to get version if (arg2 == CMD_GET_VERSION) { - if (is_manager() || 0 == current_uid().val) { - u32 version = KERNEL_SU_VERSION; - if (copy_to_user(arg3, &version, sizeof(version))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } + u32 version = KERNEL_SU_VERSION; + if (copy_to_user(arg3, &version, sizeof(version))) { + pr_err("prctl reply error, cmd: %lu\n", arg2); + } #ifdef MODULE - u32 is_lkm = 0x1; + u32 is_lkm = 0x1; #else - u32 is_lkm = 0x0; + u32 is_lkm = 0x0; #endif - if (arg4 && copy_to_user(arg4, &is_lkm, sizeof(is_lkm))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } + if (arg4 && copy_to_user(arg4, &is_lkm, sizeof(is_lkm))) { + pr_err("prctl reply error, cmd: %lu\n", arg2); } return 0; } if (arg2 == CMD_REPORT_EVENT) { - if (0 != current_uid().val) { + if (!from_root) { return 0; } switch (arg3) { @@ -366,7 +299,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, } if (arg2 == CMD_SET_SEPOLICY) { - if (0 != current_uid().val) { + if (!from_root) { return 0; } if (!handle_sepolicy(arg3, arg4)) { @@ -379,9 +312,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, } if (arg2 == CMD_CHECK_SAFEMODE) { - if (!is_manager() && 0 != current_uid().val) { - return 0; - } if (ksu_is_safe_mode()) { pr_warn("safemode enabled!\n"); if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { @@ -392,57 +322,49 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, } 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: %lu\n", - arg2); - } - } else { - pr_err("prctl copy allowlist error\n"); + 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: %lu\n", + arg2); } + } else { + pr_err("prctl copy allowlist error\n"); } } return 0; } if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) { - if (is_manager() || 0 == current_uid().val) { - uid_t target_uid = (uid_t)arg3; - bool allow = false; - if (arg2 == CMD_UID_GRANTED_ROOT) { - allow = ksu_is_allow_uid(target_uid); - } else if (arg2 == CMD_UID_SHOULD_UMOUNT) { - allow = ksu_uid_should_umount(target_uid); - } else { - pr_err("unknown cmd: %lu\n", arg2); - } - if (!copy_to_user(arg4, &allow, sizeof(allow))) { - if (copy_to_user(result, &reply_ok, - sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", - arg2); - } - } else { - pr_err("prctl copy err, cmd: %lu\n", arg2); + uid_t target_uid = (uid_t)arg3; + bool allow = false; + if (arg2 == CMD_UID_GRANTED_ROOT) { + allow = ksu_is_allow_uid(target_uid); + } else if (arg2 == CMD_UID_SHOULD_UMOUNT) { + allow = ksu_uid_should_umount(target_uid); + } else { + pr_err("unknown cmd: %lu\n", arg2); + } + if (!copy_to_user(arg4, &allow, sizeof(allow))) { + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("prctl reply error, cmd: %lu\n", arg2); } + } else { + pr_err("prctl copy err, cmd: %lu\n", arg2); } return 0; } // all other cmds are for 'root manager' - if (!is_manager()) { - last_failed_uid = current_uid().val; + if (!from_manager) { return 0; } @@ -515,7 +437,7 @@ static bool should_umount(struct path *path) return false; } -static void ksu_umount_mnt(struct path *path, int flags) +static int ksu_umount_mnt(struct path *path, int flags) { int err = path_umount(path, flags); if (err) { @@ -541,7 +463,10 @@ static void try_umount(const char *mnt, bool check_mnt, int flags) return; } - ksu_umount_mnt(&path, flags); + err = ksu_umount_mnt(&path, flags); + if (err) { + pr_warn("umount %s failed: %d\n", mnt, err); + } } int ksu_handle_setuid(struct cred *new, const struct cred *old) @@ -563,7 +488,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) return 0; } - if (!is_appuid(new_uid) || is_isolated_uid(new_uid.val)) { + if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) { // pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val); return 0; } @@ -590,9 +515,11 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) current->pid); return 0; } +#ifdef CONFIG_KSU_DEBUG // umount the target mnt pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val, current->pid); +#endif // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and // filter the mountpoint whose target is `/data/adb` @@ -612,7 +539,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) static int handler_pre(struct kprobe *p, struct pt_regs *regs) { - struct pt_regs *real_regs = (struct pt_regs *)PT_REGS_PARM1(regs); + struct pt_regs *real_regs = PT_REAL_REGS(regs); int option = (int)PT_REGS_PARM1(real_regs); unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs); unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs); @@ -690,6 +617,7 @@ static int ksu_task_fix_setuid(struct cred *new, const struct cred *old, return ksu_handle_setuid(new, old); } +#ifndef MODULE static struct security_hook_list ksu_hooks[] = { LSM_HOOK_INIT(task_prctl, ksu_task_prctl), LSM_HOOK_INIT(inode_rename, ksu_inode_rename), @@ -701,7 +629,7 @@ void __init ksu_lsm_hook_init(void) security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu"); } -#ifdef MODULE +#else static int override_security_head(void *head, const void *new_head, size_t len) { unsigned long base = (unsigned long)head & PAGE_MASK; @@ -719,7 +647,9 @@ static int override_security_head(void *head, const void *new_head, size_t len) if (!addr) { return -ENOMEM; } + local_irq_disable(); memcpy(addr + offset, new_head, len); + local_irq_enable(); vunmap(addr); return 0; } @@ -820,7 +750,7 @@ static void *find_head_addr(void *security_ptr, int *index) } \ } while (0) -void __init ksu_lsm_hook_init_hack(void) +void __init ksu_lsm_hook_init(void) { void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl); void *prctl_head = find_head_addr(cap_prctl, NULL); @@ -869,20 +799,14 @@ void __init ksu_lsm_hook_init_hack(void) void __init ksu_core_init(void) { -#ifndef MODULE - pr_info("ksu_lsm_hook_init\n"); ksu_lsm_hook_init(); - -#else - pr_info("ksu_lsm_hook_init hack!!!!\n"); - ksu_lsm_hook_init_hack(); -#endif } void ksu_core_exit(void) { -#ifndef MODULE - pr_info("ksu_kprobe_exit\n"); - ksu_kprobe_exit(); +#ifdef CONFIG_KPROBES + pr_info("ksu_core_kprobe_exit\n"); + // we dont use this now + // ksu_kprobe_exit(); #endif } diff --git a/kernel/core_hook.h b/kernel/core_hook.h index 8e8bfc2caef4..616951e8db35 100644 --- a/kernel/core_hook.h +++ b/kernel/core_hook.h @@ -1,7 +1,7 @@ #ifndef __KSU_H_KSU_CORE #define __KSU_H_KSU_CORE -#include "linux/init.h" +#include void __init ksu_core_init(void); void ksu_core_exit(void); diff --git a/kernel/include/ksu_hook.h b/kernel/include/ksu_hook.h index d4a2cddb06e2..ea0b04d3e538 100644 --- a/kernel/include/ksu_hook.h +++ b/kernel/include/ksu_hook.h @@ -1,8 +1,8 @@ #ifndef __KSU_H_KSHOOK #define __KSU_H_KSHOOK -#include "linux/fs.h" -#include "linux/types.h" +#include +#include // For sucompat diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index 7a6ffa76040c..5d40b8427556 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -1,8 +1,8 @@ -#include "linux/version.h" -#include "linux/fs.h" -#include "linux/nsproxy.h" -#include "linux/sched/task.h" -#include "linux/uaccess.h" +#include +#include +#include +#include +#include #include "klog.h" // IWYU pragma: keep extern struct task_struct init_task; diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index c7a22a1c3749..4bcfbf389318 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -1,9 +1,24 @@ #ifndef __KSU_H_KERNEL_COMPAT #define __KSU_H_KERNEL_COMPAT -#include "linux/fs.h" +#include +#include +#include "ss/policydb.h" #include "linux/key.h" -#include "linux/version.h" + +/* + * Adapt to Huawei HISI kernel without affecting other kernels , + * Huawei Hisi Kernel EBITMAP Enable or Disable Flag , + * From ss/ebitmap.h + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) +#ifdef HISI_SELINUX_EBITMAP_RO +#define CONFIG_IS_HW_HISI +#endif +#endif extern long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, diff --git a/kernel/ksu.c b/kernel/ksu.c index 699290e4bb50..d517c3b54396 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -1,15 +1,15 @@ -#include "linux/export.h" -#include "linux/fs.h" -#include "linux/kobject.h" -#include "linux/module.h" -#include "linux/workqueue.h" +#include +#include +#include +#include +#include #include "allowlist.h" #include "arch.h" #include "core_hook.h" #include "klog.h" // IWYU pragma: keep #include "ksu.h" -#include "uid_observer.h" +#include "throne_tracker.h" static struct workqueue_struct *ksu_workqueue; @@ -32,8 +32,10 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, flags); } -extern void ksu_enable_sucompat(); -extern void ksu_enable_ksud(); +extern void ksu_sucompat_init(); +extern void ksu_sucompat_exit(); +extern void ksu_ksud_init(); +extern void ksu_ksud_exit(); int __init kernelsu_init(void) { @@ -53,11 +55,11 @@ int __init kernelsu_init(void) ksu_allowlist_init(); - ksu_uid_observer_init(); + ksu_throne_tracker_init(); #ifdef CONFIG_KPROBES - ksu_enable_sucompat(); - ksu_enable_ksud(); + ksu_sucompat_init(); + ksu_ksud_init(); #else pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html"); #endif @@ -74,10 +76,15 @@ void kernelsu_exit(void) { ksu_allowlist_exit(); - ksu_uid_observer_exit(); + ksu_throne_tracker_exit(); destroy_workqueue(ksu_workqueue); +#ifdef CONFIG_KPROBES + ksu_ksud_exit(); + ksu_sucompat_exit(); +#endif + ksu_core_exit(); } diff --git a/kernel/ksu.h b/kernel/ksu.h index b98c0fd1bfb5..35d1b14dbb7c 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -1,8 +1,8 @@ #ifndef __KSU_H_KSU #define __KSU_H_KSU -#include "linux/types.h" -#include "linux/workqueue.h" +#include +#include #define KERNEL_SU_VERSION KSU_VERSION #define KERNEL_SU_OPTION 0xDEADBEEF diff --git a/kernel/ksud.c b/kernel/ksud.c index 4a35ce28e388..a3b6fc179aeb 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -1,16 +1,17 @@ -#include "asm/current.h" -#include "linux/compat.h" -#include "linux/dcache.h" -#include "linux/err.h" -#include "linux/fs.h" -#include "linux/version.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "allowlist.h" #include "arch.h" @@ -108,7 +109,7 @@ static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) * count() counts the number of strings in array ARGV. */ - /* +/* * Make sure old GCC compiler can use __maybe_unused, * Test passed in 4.4.x ~ 4.9.x when use GCC. */ @@ -141,7 +142,8 @@ static int __maybe_unused count(struct user_arg_ptr argv, int max) // IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, - struct user_arg_ptr *argv, struct user_arg_ptr *envp, int *flags) + struct user_arg_ptr *argv, + struct user_arg_ptr *envp, int *flags) { #ifndef CONFIG_KPROBES if (!ksu_execveat_hook) { @@ -167,8 +169,9 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, return 0; } - if (unlikely(!memcmp(filename->name, system_bin_init, - sizeof(system_bin_init) - 1) && argv)) { + if (unlikely(!memcmp(filename->name, system_bin_init, + sizeof(system_bin_init) - 1) && + argv)) { // /system/bin/init executed int argc = count(*argv, MAX_ARG_STRINGS); pr_info("/system/bin/init argc: %d\n", argc); @@ -176,8 +179,10 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, const char __user *p = get_user_arg_ptr(*argv, 1); if (p && !IS_ERR(p)) { char first_arg[16]; - ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); - pr_info("/system/bin/init first arg: %s\n", first_arg); + ksu_strncpy_from_user_nofault( + first_arg, p, sizeof(first_arg)); + pr_info("/system/bin/init first arg: %s\n", + first_arg); if (!strcmp(first_arg, "second_stage")) { pr_info("/system/bin/init second_stage executed\n"); apply_kernelsu_rules(); @@ -189,7 +194,8 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, } } } else if (unlikely(!memcmp(filename->name, old_system_init, - sizeof(old_system_init) - 1) && argv)) { + sizeof(old_system_init) - 1) && + argv)) { // /init executed int argc = count(*argv, MAX_ARG_STRINGS); pr_info("/init argc: %d\n", argc); @@ -198,7 +204,8 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, const char __user *p = get_user_arg_ptr(*argv, 1); if (p && !IS_ERR(p)) { char first_arg[16]; - ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); + ksu_strncpy_from_user_nofault( + first_arg, p, sizeof(first_arg)); pr_info("/init first arg: %s\n", first_arg); if (!strcmp(first_arg, "--second-stage")) { pr_info("/init second_stage executed\n"); @@ -215,13 +222,15 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, if (envc > 0) { int n; for (n = 1; n <= envc; n++) { - const char __user *p = get_user_arg_ptr(*envp, n); + const char __user *p = + get_user_arg_ptr(*envp, n); if (!p || IS_ERR(p)) { continue; } char env[256]; // Reading environment variable strings from user space - if (ksu_strncpy_from_user_nofault(env, p, sizeof(env)) < 0) + if (ksu_strncpy_from_user_nofault( + env, p, sizeof(env)) < 0) continue; // Parsing environment variable names and values char *env_name = env; @@ -232,10 +241,14 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, *env_value = '\0'; env_value++; // Check if the environment variable name and value are matching - if (!strcmp(env_name, "INIT_SECOND_STAGE") && (!strcmp(env_value, "1") || !strcmp(env_value, "true"))) { + if (!strcmp(env_name, + "INIT_SECOND_STAGE") && + (!strcmp(env_value, "1") || + !strcmp(env_value, "true"))) { pr_info("/init second_stage executed\n"); apply_kernelsu_rules(); - init_second_stage_executed = true; + init_second_stage_executed = + true; ksu_android_ns_fs_check(); } } @@ -243,10 +256,11 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, } } - if (unlikely(first_app_process && - !memcmp(filename->name, app_process, sizeof(app_process) - 1))) { + if (unlikely(first_app_process && !memcmp(filename->name, app_process, + sizeof(app_process) - 1))) { first_app_process = false; - pr_info("exec app_process, /data prepared, second_stage: %d\n", init_second_stage_executed); + pr_info("exec app_process, /data prepared, second_stage: %d\n", + init_second_stage_executed); on_post_fs_data(); // we keep this for old ksud stop_execve_hook(); } @@ -265,7 +279,8 @@ static ssize_t read_proxy(struct file *file, char __user *buf, size_t count, 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\n", ret, read_count_append); + pr_info("read_proxy append %ld + %ld\n", ret, + read_count_append); ret += read_count_append; } return ret; @@ -377,6 +392,18 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, return 0; } +int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, + size_t *count_ptr) +{ + struct file *file = fget(fd); + if (!file) { + return 0; + } + int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL); + fput(file); + return result; +} + static unsigned int volumedown_pressed_count = 0; static bool is_volumedown_enough(unsigned int count) @@ -452,7 +479,32 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL); } -static int read_handler_pre(struct kprobe *p, struct pt_regs *regs) +static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM1(real_regs); + const char __user *const __user *__argv = + (const char __user *const __user *)PT_REGS_PARM2(real_regs); + struct user_arg_ptr argv = { .ptr.native = __argv }; + struct filename filename_in, *filename_p; + char path[32]; + + if (!filename_user) + return 0; + + memset(path, 0, sizeof(path)); + ksu_strncpy_from_user_nofault(path, *filename_user, 32); + filename_in.name = path; + + filename_p = &filename_in; + return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, + NULL); +} + +// remove this later! +__maybe_unused static int vfs_read_handler_pre(struct kprobe *p, + struct pt_regs *regs) { struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs); char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs); @@ -462,6 +514,16 @@ 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 sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + unsigned int fd = PT_REGS_PARM1(real_regs); + char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs); + size_t count_ptr = (size_t *)&PT_REGS_PARM3(real_regs); + + return ksu_handle_sys_read(fd, buf_ptr, count_ptr); +} + static int input_handle_event_handler_pre(struct kprobe *p, struct pt_regs *regs) { @@ -471,18 +533,32 @@ static int input_handle_event_handler_pre(struct kprobe *p, return ksu_handle_input_handle_event(type, code, value); } +#if 1 +static struct kprobe execve_kp = { + .symbol_name = SYS_EXECVE_SYMBOL, + .pre_handler = sys_execve_handler_pre, +}; +#else static struct kprobe execve_kp = { .symbol_name = "do_execveat_common", .pre_handler = execve_handler_pre, }; +#endif +#if 1 +static struct kprobe vfs_read_kp = { + .symbol_name = SYS_READ_SYMBOL, + .pre_handler = sys_read_handler_pre, +}; +#else static struct kprobe vfs_read_kp = { .symbol_name = "vfs_read", - .pre_handler = read_handler_pre, + .pre_handler = vfs_read_handler_pre, }; +#endif -static struct kprobe input_handle_event_kp = { - .symbol_name = "input_handle_event", +static struct kprobe input_event_kp = { + .symbol_name = "input_event", .pre_handler = input_handle_event_handler_pre, }; @@ -498,7 +574,7 @@ static void do_stop_execve_hook(struct work_struct *work) static void do_stop_input_hook(struct work_struct *work) { - unregister_kprobe(&input_handle_event_kp); + unregister_kprobe(&input_event_kp); } #endif @@ -541,7 +617,7 @@ static void stop_input_hook() } // ksud: module support -void ksu_enable_ksud() +void ksu_ksud_init() { #ifdef CONFIG_KPROBES int ret; @@ -552,11 +628,21 @@ 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); + ret = register_kprobe(&input_event_kp); + pr_info("ksud: input_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 } + +void ksu_ksud_exit() +{ +#ifdef CONFIG_KPROBES + unregister_kprobe(&execve_kp); + // this should be done before unregister vfs_read_kp + // unregister_kprobe(&vfs_read_kp); + unregister_kprobe(&input_event_kp); +#endif +} \ No newline at end of file diff --git a/kernel/ksud.h b/kernel/ksud.h index 5a32a527757f..eafb31472f12 100644 --- a/kernel/ksud.h +++ b/kernel/ksud.h @@ -1,6 +1,8 @@ #ifndef __KSU_H_KSUD #define __KSU_H_KSUD +#include + #define KSUD_PATH "/data/adb/ksud" void on_post_fs_data(void); diff --git a/kernel/manager.c b/kernel/manager.c deleted file mode 100644 index 15258832ce22..000000000000 --- a/kernel/manager.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "linux/cred.h" -#include "linux/gfp.h" -#include "linux/slab.h" -#include "linux/uidgid.h" -#include "linux/version.h" - -#include "linux/fdtable.h" -#include "linux/fs.h" -#include "linux/rcupdate.h" - -#include "apk_sign.h" -#include "klog.h" // IWYU pragma: keep -#include "ksu.h" -#include "manager.h" - -uid_t ksu_manager_uid = KSU_INVALID_UID; - -bool become_manager(char *pkg) -{ - struct fdtable *files_table; - int i = 0; - struct path files_path; - char *cwd; - char *buf; - bool result = false; - -#ifdef KSU_MANAGER_PACKAGE - // pkg is `/` - if (strncmp(pkg + 1, KSU_MANAGER_PACKAGE, - sizeof(KSU_MANAGER_PACKAGE)) != 0) { - pr_info("manager package is inconsistent with kernel build: %s\n", - KSU_MANAGER_PACKAGE); - return false; - } -#endif - // must be zygote's direct child, otherwise any app can fork a new process and - // open manager's apk - if (task_uid(current->real_parent).val != 0) { - pr_info("parent is not zygote!\n"); - return false; - } - - buf = (char *)kmalloc(PATH_MAX, GFP_ATOMIC); - if (!buf) { - pr_err("kalloc path failed.\n"); - return false; - } - - files_table = files_fdtable(current->files); - - int pkg_len = strlen(pkg); - // todo: use iterate_fd - for (i = 0; files_table->fd[i] != NULL; i++) { - files_path = files_table->fd[i]->f_path; - if (!d_is_reg(files_path.dentry)) { - continue; - } - cwd = d_path(&files_path, buf, PATH_MAX); - if (startswith(cwd, "/data/app/") != 0 || - endswith(cwd, "==/base.apk") != 0) { - // AOSP generate ramdom base64 with 16bit, without NO_PADDING, so it must have two "=" - continue; - } - // we have found the apk! - pr_info("found apk: %s\n", cwd); - char *pkg_index = strstr(cwd, pkg); - if (!pkg_index) { - pr_info("apk path not match package name!\n"); - continue; - } - char *next_char = pkg_index + pkg_len; - // because we ensure the cwd must startswith `/data/app` and endswith `base.apk` - // we don't need to check if the pointer is out of bounds - if (*next_char != '-') { - // from android 8.1: http://aospxref.com/android-8.1.0_r81/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#17612 - // to android 13: http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java#1208 - // /data/app/~~[randomStringA]/[packageName]-[randomStringB] - // the previous char must be `/` and the next char must be `-` - // because we use strstr instead of equals, this is a strong verfication. - pr_info("invalid pkg: %s\n", pkg); - continue; - } - if (is_manager_apk(cwd)) { - // check passed - uid_t uid = current_uid().val; - pr_info("manager uid: %d\n", uid); - - ksu_set_manager_uid(uid); - - result = true; - goto clean; - } else { - pr_info("manager signature invalid!\n"); - } - - break; - } - -clean: - kfree(buf); - return result; -} diff --git a/kernel/manager.h b/kernel/manager.h index 9429d758f89d..be5bbced6f73 100644 --- a/kernel/manager.h +++ b/kernel/manager.h @@ -1,8 +1,8 @@ #ifndef __KSU_H_KSU_MANAGER #define __KSU_H_KSU_MANAGER -#include "linux/cred.h" -#include "linux/types.h" +#include +#include #define KSU_INVALID_UID -1 @@ -33,6 +33,4 @@ static inline void ksu_invalidate_manager_uid() ksu_manager_uid = KSU_INVALID_UID; } -bool become_manager(char *pkg); - #endif diff --git a/kernel/module_api.c b/kernel/module_api.c deleted file mode 100644 index 999d5102741f..000000000000 --- a/kernel/module_api.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "linux/kallsyms.h" - -#define RE_EXPORT_SYMBOL1(ret, func, t1, v1) \ - ret ksu_##func(t1 v1) \ - { \ - return func(v1); \ - } \ - EXPORT_SYMBOL(ksu_##func); - -#define RE_EXPORT_SYMBOL2(ret, func, t1, v1, t2, v2) \ - ret ksu_##func(t1 v1, t2 v2) \ - { \ - return func(v1, v2); \ - } \ - EXPORT_SYMBOL(ksu_##func); - -RE_EXPORT_SYMBOL1(unsigned long, kallsyms_lookup_name, const char *, name) - -// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p) -// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p) - -// RE_EXPORT_SYMBOL2(int, register_kprobe, struct kprobe *, p) -// RE_EXPORT_SYMBOL2(void, unregister_kprobe, struct kprobe *, p) - -// int ksu_register_kprobe(struct kprobe *p); -// void ksu_unregister_kprobe(struct kprobe *p); -// int ksu_register_kprobes(struct kprobe **kps, int num); -// void ksu_unregister_kprobes(struct kprobe **kps, int num); - -// int ksu_register_kretprobe(struct kretprobe *rp); -// void unregister_kretprobe(struct kretprobe *rp); -// int register_kretprobes(struct kretprobe **rps, int num); -// void unregister_kretprobes(struct kretprobe **rps, int num); diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index eb72f202ae39..1ba6d853f2a9 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -1,6 +1,6 @@ -#include "linux/uaccess.h" -#include "linux/types.h" -#include "linux/version.h" +#include +#include +#include #include "../klog.h" // IWYU pragma: keep #include "selinux.h" @@ -69,6 +69,11 @@ void apply_kernelsu_rules() // 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 need to search /data/app + ksu_allow(db, "kernel", "apk_data_file", "file", "open"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "open"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "read"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "search"); // we may need to do mount on shell ksu_allow(db, "kernel", "shell_data_file", "file", ALL); // we need to read /data/system/packages.list @@ -125,12 +130,6 @@ void apply_kernelsu_rules() // Allow all binder transactions ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL); - // Allow system server devpts - ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file", - "read"); - ksu_allow(db, "system_server", "untrusted_app_all_devpts", "chr_file", - "write"); - // Allow system server kill su process ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid"); ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill"); diff --git a/kernel/selinux/sepolicy.c b/kernel/selinux/sepolicy.c index ca0f13bb4bab..bff5f32c7763 100644 --- a/kernel/selinux/sepolicy.c +++ b/kernel/selinux/sepolicy.c @@ -1,28 +1,15 @@ -#include "sepolicy.h" -#include "linux/gfp.h" -#include "linux/printk.h" -#include "linux/slab.h" -#include "linux/version.h" +#include +#include +#include +#include +#include "sepolicy.h" #include "../klog.h" // IWYU pragma: keep #include "ss/symtab.h" +#include "../kernel_compat.h" // Add check Huawei Device #define KSU_SUPPORT_ADD_TYPE -/* - * Adapt to Huawei HISI kernel without affecting other kernels , - * Huawei Hisi Kernel EBITMAP Enable or Disable Flag , - * From ss/ebitmap.h - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) && \ - LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ - LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) && \ - LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) -#ifdef HISI_SELINUX_EBITMAP_RO -#define CONFIG_IS_HW_HISI -#endif -#endif - ////////////////////////////////////////////////////// // Declaration ////////////////////////////////////////////////////// @@ -582,6 +569,22 @@ static bool add_genfscon(struct policydb *db, const char *fs_name, return false; } +static void *ksu_realloc(void *old, size_t new_size, size_t old_size) +{ + // we can't use krealloc, because it may be read-only + void *new = kzalloc(new_size, GFP_ATOMIC); + if (!new) { + return NULL; + } + if (old_size) { + memcpy(new, old, old_size); + } + // we can't use kfree, because it may be read-only + // there maybe some leaks, maybe we can check ptr_write, but it's not a big deal + // kfree(old); + return new; +} + static bool add_type(struct policydb *db, const char *type_name, bool attr) { #ifdef KSU_SUPPORT_ADD_TYPE @@ -616,27 +619,29 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) 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)); - - struct type_datum **new_type_val_to_struct = - krealloc(db->type_val_to_struct, - sizeof(*db->type_val_to_struct) * db->p_types.nprim, - GFP_ATOMIC); + ksu_realloc(db->type_attr_map_array, + value * sizeof(struct ebitmap), + (value - 1) * sizeof(struct ebitmap)); if (!new_type_attr_map_array) { pr_err("add_type: alloc type_attr_map_array failed\n"); return false; } + struct type_datum **new_type_val_to_struct = + ksu_realloc(db->type_val_to_struct, + sizeof(*db->type_val_to_struct) * value, + sizeof(*db->type_val_to_struct) * (value - 1)); + if (!new_type_val_to_struct) { pr_err("add_type: alloc type_val_to_struct failed\n"); return false; } char **new_val_to_name_types = - krealloc(db->sym_val_to_name[SYM_TYPES], - sizeof(char *) * db->symtab[SYM_TYPES].nprim, - GFP_KERNEL); + ksu_realloc(db->sym_val_to_name[SYM_TYPES], + sizeof(char *) * value, + sizeof(char *) * (value - 1)); if (!new_val_to_name_types) { pr_err("add_type: alloc val_to_name failed\n"); return false; diff --git a/kernel/selinux/sepolicy.h b/kernel/selinux/sepolicy.h index a50712369f42..675d1499e46d 100644 --- a/kernel/selinux/sepolicy.h +++ b/kernel/selinux/sepolicy.h @@ -1,7 +1,7 @@ #ifndef __KSU_H_SEPOLICY #define __KSU_H_SEPOLICY -#include "linux/types.h" +#include #include "ss/policydb.h" diff --git a/kernel/setup.sh b/kernel/setup.sh index 68107e0191a4..e688dbaf3ae5 100755 --- a/kernel/setup.sh +++ b/kernel/setup.sh @@ -1,50 +1,75 @@ #!/bin/sh -set -eux +set -eu GKI_ROOT=$(pwd) -echo "[+] GKI_ROOT: $GKI_ROOT" +display_usage() { + echo "Usage: $0 [--cleanup | ]" + echo " --cleanup: Cleans up previous modifications made by the script." + echo " : Sets up or updates the KernelSU to specified tag or commit." + echo " -h, --help: Displays this usage information." + echo " (no args): Sets up or updates the KernelSU environment to the latest tagged version." +} -if test -d "$GKI_ROOT/common/drivers"; then - DRIVER_DIR="$GKI_ROOT/common/drivers" -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 script by yourself.' - exit 127 -fi +initialize_variables() { + if test -d "$GKI_ROOT/common/drivers"; then + DRIVER_DIR="$GKI_ROOT/common/drivers" + elif test -d "$GKI_ROOT/drivers"; then + DRIVER_DIR="$GKI_ROOT/drivers" + else + echo '[ERROR] "drivers/" directory not found.' + exit 127 + fi -test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU -cd "$GKI_ROOT/KernelSU" -git stash -if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then - git checkout main -fi -git pull -if [ -z "${1-}" ]; then - git checkout "$(git describe --abbrev=0 --tags)" -else - git checkout "$1" -fi -cd "$GKI_ROOT" + DRIVER_MAKEFILE=$DRIVER_DIR/Makefile + DRIVER_KCONFIG=$DRIVER_DIR/Kconfig +} -echo "[+] GKI_ROOT: $GKI_ROOT" -echo "[+] Copy kernel su driver to $DRIVER_DIR" +# Reverts modifications made by this script +perform_cleanup() { + echo "[+] Cleaning up..." + [ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed." + grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted." + grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted." + if [ -d "$GKI_ROOT/KernelSU" ]; then + rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted." + fi +} -cd "$DRIVER_DIR" -if test -d "$GKI_ROOT/common/drivers"; then - ln -sf "../../KernelSU/kernel" "kernelsu" -elif test -d "$GKI_ROOT/drivers"; then - ln -sf "../KernelSU/kernel" "kernelsu" -fi -cd "$GKI_ROOT" - -echo '[+] Add kernel su driver to Makefile' +# Sets up or update KernelSU environment +setup_kernelsu() { + echo "[+] Setting up KernelSU..." + test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU && echo "[+] Repository cloned." + cd "$GKI_ROOT/KernelSU" + git stash && echo "[-] Stashed current changes." + if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then + git checkout main && echo "[-] Switched to main branch." + fi + git pull && echo "[+] Repository updated." + if [ -z "${1-}" ]; then + git checkout "$(git describe --abbrev=0 --tags)" && echo "[-] Checked out latest tag." + else + git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch" + fi + cd "$DRIVER_DIR" + ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created." -DRIVER_MAKEFILE=$DRIVER_DIR/Makefile -DRIVER_KCONFIG=$DRIVER_DIR/Kconfig -grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" -grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" + # Add entries in Makefile and Kconfig if not already existing + grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile." + grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig." + echo '[+] Done.' +} -echo '[+] Done.' +# Process command-line arguments +if [ "$#" -eq 0 ]; then + initialize_variables + setup_kernelsu +elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + display_usage +elif [ "$1" = "--cleanup" ]; then + initialize_variables + perform_cleanup +else + initialize_variables + setup_kernelsu "$@" +fi diff --git a/kernel/sucompat.c b/kernel/sucompat.c index b3299c5230f9..e8bb8eedb85d 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -1,12 +1,12 @@ -#include "asm/current.h" -#include "linux/cred.h" -#include "linux/err.h" -#include "linux/fs.h" -#include "linux/kprobes.h" -#include "linux/types.h" -#include "linux/uaccess.h" -#include "linux/version.h" -#include "linux/sched/task_stack.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "allowlist.h" #include "arch.h" @@ -35,8 +35,15 @@ static char __user *sh_user_path(void) return userspace_stack_buffer(sh_path, sizeof(sh_path)); } +static char __user *ksud_user_path(void) +{ + static const char ksud_path[] = KSUD_PATH; + + return userspace_stack_buffer(ksud_path, sizeof(ksud_path)); +} + int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, - int *flags) + int * __unused_flags) { const char su[] = SU_PATH; @@ -71,7 +78,8 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) char path[sizeof(su) + 1]; memset(path, 0, sizeof(path)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) +// Remove this later!! we use syscall hook, so this will never happen!!!!! +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0 // it becomes a `struct filename *` after 5.18 // https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216 const char sh[] = SH_PATH; @@ -125,11 +133,37 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, return 0; } +int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user, + void *__never_use_argv, void *__never_use_envp, int *__never_use_flags) +{ + const char su[] = SU_PATH; + char path[sizeof(su) + 1]; + + if (unlikely(!filename_user)) + return 0; + + memset(path, 0, sizeof(path)); + ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + + if (likely(memcmp(path, su, sizeof(su)))) + return 0; + + if (!ksu_is_allow_uid(current_uid().val)) + return 0; + + pr_info("sys_execve su found\n"); + *filename_user = ksud_user_path(); + + escape_to_root(); + + return 0; +} + #ifdef CONFIG_KPROBES -static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) +__maybe_unused static int faccessat_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); int *mode = (int *)&PT_REGS_PARM3(regs); // Both sys_ and do_ is C function @@ -138,7 +172,17 @@ static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_faccessat(dfd, filename_user, mode, flags); } -static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) +static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int *dfd = (int *)&PT_REGS_PARM1(real_regs); + const char __user **filename_user = (const char **)&PT_REGS_PARM2(real_regs); + int *mode = (int *)&PT_REGS_PARM3(real_regs); + + return ksu_handle_faccessat(dfd, filename_user, mode, NULL); +} + +__maybe_unused static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) { int *dfd = (int *)&PT_REGS_PARM1(regs); const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs); @@ -147,6 +191,26 @@ static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_stat(dfd, filename_user, flags); } +static int sys_newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int *dfd = (int *)&PT_REGS_PARM1(real_regs); + const char __user **filename_user = (const char **)&PT_REGS_PARM2(real_regs); + int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs); + + return ksu_handle_stat(dfd, filename_user, flags); +} + +static int sys_newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int *dfd = (int *)&PT_REGS_PARM1(real_regs); + const char __user **filename_user = (const char **)&PT_REGS_PARM2(real_regs); + int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs); + + return ksu_handle_stat(dfd, filename_user, flags); +} + // https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864 static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { @@ -157,25 +221,54 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL); } +static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + const char __user **filename_user = (const char **)&PT_REGS_PARM1(real_regs); + + return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, NULL); +} + +#if 1 +static struct kprobe faccessat_kp = { + .symbol_name = SYS_FACCESSAT_SYMBOL, + .pre_handler = sys_faccessat_handler_pre, +}; +#else static struct kprobe faccessat_kp = { .symbol_name = "do_faccessat", .pre_handler = faccessat_handler_pre, }; +#endif +#if 1 +static struct kprobe newfstatat_kp = { + .symbol_name = SYS_NEWFSTATAT_SYMBOL, + .pre_handler = sys_newfstatat_handler_pre, +}; +#else static struct kprobe newfstatat_kp = { .symbol_name = "vfs_statx", .pre_handler = newfstatat_handler_pre, }; +#endif +#if 1 +static struct kprobe execve_kp = { + .symbol_name = SYS_EXECVE_SYMBOL, + .pre_handler = sys_execve_handler_pre, +}; +#else static struct kprobe execve_kp = { .symbol_name = "do_execveat_common", .pre_handler = execve_handler_pre, }; +#endif #endif // sucompat: permited process can execute 'su' to gain root access. -void ksu_enable_sucompat() +void ksu_sucompat_init() { #ifdef CONFIG_KPROBES int ret; @@ -187,3 +280,11 @@ void ksu_enable_sucompat() pr_info("sucompat: faccessat_kp: %d\n", ret); #endif } + +void ksu_sucompat_exit() { +#ifdef CONFIG_KPROBES + unregister_kprobe(&execve_kp); + unregister_kprobe(&newfstatat_kp); + unregister_kprobe(&faccessat_kp); +#endif +} diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c new file mode 100644 index 000000000000..665ee3bf2b44 --- /dev/null +++ b/kernel/throne_tracker.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "allowlist.h" +#include "klog.h" // IWYU pragma: keep +#include "ksu.h" +#include "manager.h" +#include "throne_tracker.h" +#include "kernel_compat.h" + +uid_t ksu_manager_uid = KSU_INVALID_UID; + +#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list" +static struct work_struct ksu_update_uid_work; + +struct uid_data { + struct list_head list; + u32 uid; + char package[KSU_MAX_PACKAGE_NAME]; +}; + +static int get_pkg_from_apk_path(char *pkg, const char *path) +{ + int len = strlen(path); + if (len >= KSU_MAX_PACKAGE_NAME || len < 1) + return -1; + + const char *last_slash = NULL; + const char *second_last_slash = NULL; + + int i; + for (i = len - 1; i >= 0; i--) { + if (path[i] == '/') { + if (!last_slash) { + last_slash = &path[i]; + } else { + second_last_slash = &path[i]; + break; + } + } + } + + if (!last_slash || !second_last_slash) + return -1; + + const char *last_hyphen = strchr(second_last_slash, '-'); + if (!last_hyphen || last_hyphen > last_slash) + return -1; + + int pkg_len = last_hyphen - second_last_slash - 1; + if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0) + return -1; + + // Copying the package name + strncpy(pkg, second_last_slash + 1, pkg_len); + pkg[pkg_len] = '\0'; + + return 0; +} + +static void crown_manager(const char *apk, struct list_head *uid_data) +{ + char pkg[KSU_MAX_PACKAGE_NAME]; + if (get_pkg_from_apk_path(pkg, apk) < 0) { + pr_err("Failed to get package name from apk path: %s\n", apk); + return; + } + + pr_info("manager pkg: %s\n", pkg); + +#ifdef KSU_MANAGER_PACKAGE + // pkg is `/` + if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) { + pr_info("manager package is inconsistent with kernel build: %s\n", + KSU_MANAGER_PACKAGE); + return; + } +#endif + struct list_head *list = (struct list_head *)uid_data; + struct uid_data *np; + + list_for_each_entry (np, list, list) { + if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) { + pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid); + ksu_set_manager_uid(np->uid); + break; + } + } +} + +struct my_dir_context { + struct dir_context ctx; + char *parent_dir; + void *private_data; + int depth; + int *stop; +}; +// https://docs.kernel.org/filesystems/porting.html +// filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted. +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +#define FILLDIR_RETURN_TYPE bool +#define FILLDIR_ACTOR_CONTINUE true +#define FILLDIR_ACTOR_STOP false +#else +#define FILLDIR_RETURN_TYPE int +#define FILLDIR_ACTOR_CONTINUE 0 +#define FILLDIR_ACTOR_STOP -EINVAL +#endif + +FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, + int namelen, loff_t off, u64 ino, + unsigned int d_type) +{ + struct my_dir_context *my_ctx = + container_of(ctx, struct my_dir_context, ctx); + struct file *file; + char dirpath[384]; // 384 is enough for /data/app//base.apk + + if (!my_ctx) { + pr_err("Invalid context\n"); + return FILLDIR_ACTOR_STOP; + } + if (my_ctx->stop && *my_ctx->stop) { + pr_info("Stop searching\n"); + return FILLDIR_ACTOR_STOP; + } + + if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) + return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".." + + if (snprintf(dirpath, sizeof(dirpath), "%s/%.*s", my_ctx->parent_dir, + namelen, name) >= sizeof(dirpath)) { + pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen, + name); + return FILLDIR_ACTOR_CONTINUE; + } + + if (d_type == DT_DIR && my_ctx->depth > 0 && + (my_ctx->stop && !*my_ctx->stop)) { + struct my_dir_context sub_ctx = { .ctx.actor = my_actor, + .parent_dir = dirpath, + .private_data = + my_ctx->private_data, + .depth = my_ctx->depth - 1, + .stop = my_ctx->stop }; + file = ksu_filp_open_compat(dirpath, O_RDONLY | O_NOFOLLOW, 0); + if (IS_ERR(file)) { + pr_err("Failed to open directory: %s, err: %ld\n", + dirpath, PTR_ERR(file)); + return FILLDIR_ACTOR_CONTINUE; + } + + iterate_dir(file, &sub_ctx.ctx); + filp_close(file, NULL); + } else { + if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) { + bool is_manager = is_manager_apk(dirpath); + pr_info("Found base.apk at path: %s, is_manager: %d\n", + dirpath, is_manager); + if (is_manager) { + crown_manager(dirpath, my_ctx->private_data); + *my_ctx->stop = 1; + } + } + } + + return FILLDIR_ACTOR_CONTINUE; +} + +void search_manager(const char *path, int depth, struct list_head *uid_data) +{ + struct file *file; + int stop = 0; + struct my_dir_context ctx = { .ctx.actor = my_actor, + .parent_dir = (char *)path, + .private_data = uid_data, + .depth = depth, + .stop = &stop }; + + file = ksu_filp_open_compat(path, O_RDONLY | O_NOFOLLOW, 0); + if (IS_ERR(file)) { + pr_err("Failed to open directory: %s\n", path); + return; + } + + iterate_dir(file, &ctx.ctx); + filp_close(file, NULL); +} + +static bool is_uid_exist(uid_t uid, char *package, void *data) +{ + struct list_head *list = (struct list_head *)data; + struct uid_data *np; + + bool exist = false; + list_for_each_entry (np, list, list) { + if (np->uid == uid % 100000 && + strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { + exist = true; + break; + } + } + return exist; +} + +static void do_update_uid(struct work_struct *work) +{ + struct file *fp = + ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH + " failed: %ld\n", + PTR_ERR(fp)); + return; + } + + struct list_head uid_list; + INIT_LIST_HEAD(&uid_list); + + char chr = 0; + loff_t pos = 0; + loff_t line_start = 0; + char buf[KSU_MAX_PACKAGE_NAME]; + for (;;) { + ssize_t count = + ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos); + if (count != sizeof(chr)) + break; + if (chr != '\n') + continue; + + count = ksu_kernel_read_compat(fp, buf, sizeof(buf), + &line_start); + + struct uid_data *data = + kzalloc(sizeof(struct uid_data), GFP_ATOMIC); + if (!data) { + filp_close(fp, 0); + goto out; + } + + char *tmp = buf; + const char *delim = " "; + char *package = strsep(&tmp, delim); + char *uid = strsep(&tmp, delim); + if (!uid || !package) { + pr_err("update_uid: package or uid is NULL!\n"); + break; + } + + u32 res; + if (kstrtou32(uid, 10, &res)) { + pr_err("update_uid: uid parse err\n"); + break; + } + data->uid = res; + strncpy(data->package, package, KSU_MAX_PACKAGE_NAME); + list_add_tail(&data->list, &uid_list); + // reset line start + line_start = pos; + } + filp_close(fp, 0); + + // now update uid list + struct uid_data *np; + struct uid_data *n; + + // first, check if manager_uid exist! + bool manager_exist = false; + list_for_each_entry (np, &uid_list, list) { + // if manager is installed in work profile, the uid in packages.list is still equals main profile + // don't delete it in this case! + int manager_uid = ksu_get_manager_uid() % 100000; + if (np->uid == manager_uid) { + manager_exist = true; + break; + } + } + + if (!manager_exist) { + if (ksu_is_manager_uid_valid()) { + pr_info("manager is uninstalled, invalidate it!\n"); + ksu_invalidate_manager_uid(); + } + pr_info("Searching manager...\n"); + search_manager("/data/app", 2, &uid_list); + pr_info("Search manager finished\n"); + } + + // then prune the allowlist + ksu_prune_allowlist(is_uid_exist, &uid_list); +out: + // free uid_list + list_for_each_entry_safe (np, n, &uid_list, list) { + list_del(&np->list); + kfree(np); + } +} + +void track_throne() +{ + ksu_queue_work(&ksu_update_uid_work); +} + +void ksu_throne_tracker_init() +{ + INIT_WORK(&ksu_update_uid_work, do_update_uid); +} + +void ksu_throne_tracker_exit() +{ + // nothing to do +} diff --git a/kernel/throne_tracker.h b/kernel/throne_tracker.h new file mode 100644 index 000000000000..5d7f477003ac --- /dev/null +++ b/kernel/throne_tracker.h @@ -0,0 +1,10 @@ +#ifndef __KSU_H_UID_OBSERVER +#define __KSU_H_UID_OBSERVER + +void ksu_throne_tracker_init(); + +void ksu_throne_tracker_exit(); + +void track_throne(); + +#endif diff --git a/kernel/uid_observer.c b/kernel/uid_observer.c deleted file mode 100644 index cd216e1733b7..000000000000 --- a/kernel/uid_observer.c +++ /dev/null @@ -1,144 +0,0 @@ -#include "linux/err.h" -#include "linux/fs.h" -#include "linux/list.h" -#include "linux/slab.h" -#include "linux/string.h" -#include "linux/types.h" -#include "linux/version.h" -#include "linux/workqueue.h" - -#include "allowlist.h" -#include "klog.h" // IWYU pragma: keep -#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; - -struct uid_data { - struct list_head list; - u32 uid; - char package[KSU_MAX_PACKAGE_NAME]; -}; - -static bool is_uid_exist(uid_t uid, char *package, void *data) -{ - struct list_head *list = (struct list_head *)data; - struct uid_data *np; - - bool exist = false; - list_for_each_entry (np, list, list) { - if (np->uid == uid % 100000 && - strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { - exist = true; - break; - } - } - return exist; -} - -static void do_update_uid(struct work_struct *work) -{ - struct file *fp = - ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); - if (IS_ERR(fp)) { - pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH - " failed: %ld\n", - PTR_ERR(fp)); - return; - } - - struct list_head uid_list; - INIT_LIST_HEAD(&uid_list); - - char chr = 0; - loff_t pos = 0; - loff_t line_start = 0; - char buf[KSU_MAX_PACKAGE_NAME]; - for (;;) { - ssize_t count = - ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos); - if (count != sizeof(chr)) - break; - if (chr != '\n') - continue; - - count = ksu_kernel_read_compat(fp, buf, sizeof(buf), - &line_start); - - struct uid_data *data = - kzalloc(sizeof(struct uid_data), GFP_ATOMIC); - if (!data) { - goto out; - } - - char *tmp = buf; - const char *delim = " "; - char *package = strsep(&tmp, delim); - char *uid = strsep(&tmp, delim); - if (!uid || !package) { - pr_err("update_uid: package or uid is NULL!\n"); - break; - } - - u32 res; - if (kstrtou32(uid, 10, &res)) { - pr_err("update_uid: uid parse err\n"); - break; - } - data->uid = res; - strncpy(data->package, package, KSU_MAX_PACKAGE_NAME); - list_add_tail(&data->list, &uid_list); - // reset line start - line_start = pos; - } - - // now update uid list - struct uid_data *np; - struct uid_data *n; - - // first, check if manager_uid exist! - bool manager_exist = false; - list_for_each_entry (np, &uid_list, list) { - // if manager is installed in work profile, the uid in packages.list is still equals main profile - // don't delete it in this case! - int manager_uid = ksu_get_manager_uid() % 100000; - if (np->uid == manager_uid) { - manager_exist = true; - break; - } - } - - if (!manager_exist && ksu_is_manager_uid_valid()) { - pr_info("manager is uninstalled, invalidate it!\n"); - ksu_invalidate_manager_uid(); - } - - // then prune the allowlist - ksu_prune_allowlist(is_uid_exist, &uid_list); -out: - // free uid_list - list_for_each_entry_safe (np, n, &uid_list, list) { - list_del(&np->list); - kfree(np); - } - filp_close(fp, 0); -} - -void update_uid() -{ - ksu_queue_work(&ksu_update_uid_work); -} - -int ksu_uid_observer_init() -{ - INIT_WORK(&ksu_update_uid_work, do_update_uid); - return 0; -} - -int ksu_uid_observer_exit() -{ - return 0; -} diff --git a/kernel/uid_observer.h b/kernel/uid_observer.h deleted file mode 100644 index 6d06fd6ce192..000000000000 --- a/kernel/uid_observer.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __KSU_H_UID_OBSERVER -#define __KSU_H_UID_OBSERVER - -int ksu_uid_observer_init(); - -int ksu_uid_observer_exit(); - -void update_uid(); - -#endif diff --git a/manager/app/build.gradle.kts b/manager/app/build.gradle.kts index bc6c6b5dff8d..e0f49ba8d988 100644 --- a/manager/app/build.gradle.kts +++ b/manager/app/build.gradle.kts @@ -36,11 +36,11 @@ android { } kotlinOptions { - jvmTarget = "17" + jvmTarget = "21" } composeOptions { - kotlinCompilerExtensionVersion = "1.4.3" + kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get() } packaging { @@ -116,4 +116,4 @@ dependencies { implementation(libs.markdown) implementation(libs.androidx.webkit) -} +} \ No newline at end of file diff --git a/manager/app/src/main/AndroidManifest.xml b/manager/app/src/main/AndroidManifest.xml index 4d6860e51cdc..32c920ab4c3b 100644 --- a/manager/app/src/main/AndroidManifest.xml +++ b/manager/app/src/main/AndroidManifest.xml @@ -15,7 +15,7 @@ android:supportsRtl="true" android:networkSecurityConfig="@xml/network_security_config" android:theme="@style/Theme.KernelSU" - tools:targetApi="33"> + tools:targetApi="34"> + + - + \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/Kernels.kt b/manager/app/src/main/java/me/weishu/kernelsu/Kernels.kt index dadbd699e1ae..860e46fcb149 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/Kernels.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/Kernels.kt @@ -37,23 +37,6 @@ fun parseKernelVersion(version: String): KernelVersion { } } -fun parseKMI(input: String): String? { - val regex = Regex("(.* )?(\\d+\\.\\d+)(\\S+)?(android\\d+)(.*)") - val result = regex.find(input) - - return result?.let { - val androidVersion = it.groups[4]?.value ?: "" - val kernelVersion = it.groups[2]?.value ?: "" - "$androidVersion-$kernelVersion" - } -} - -fun getKMI(): String? { - Os.uname().release.let { - return parseKMI(it) - } -} - fun getKernelVersion(): KernelVersion { Os.uname().release.let { return parseKernelVersion(it) diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt index b90d83b172e1..042155fbee62 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt @@ -3,9 +3,14 @@ package me.weishu.kernelsu.ui import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.layout.padding -import androidx.compose.material3.* +import androidx.compose.material3.Icon +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue @@ -14,8 +19,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController -import androidx.navigation.compose.currentBackStackEntryAsState -import com.google.accompanist.navigation.animation.rememberAnimatedNavController +import androidx.navigation.compose.rememberNavController import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.navigation.popBackStack import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState @@ -29,19 +33,15 @@ import me.weishu.kernelsu.ui.util.rootAvailable class MainActivity : ComponentActivity() { - @OptIn(ExperimentalAnimationApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { KernelSUTheme { - val navController = rememberAnimatedNavController() + val navController = rememberNavController() val snackbarHostState = remember { SnackbarHostState() } - val navBackStackEntry by navController.currentBackStackEntryAsState() - val route = navBackStackEntry?.destination?.route - val showBottomBar = route == null || !route.startsWith("web_screen") Scaffold( - bottomBar = { if (showBottomBar) BottomBar(navController) }, + bottomBar = { BottomBar(navController) }, snackbarHost = { SnackbarHost(snackbarHostState) } ) { innerPadding -> CompositionLocalProvider( @@ -64,7 +64,7 @@ private fun BottomBar(navController: NavHostController) { val isManager = Natives.becomeManager(ksuApp.packageName) val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable() NavigationBar(tonalElevation = 8.dp) { - BottomBarDestination.values().forEach { destination -> + BottomBarDestination.entries.forEach { destination -> if (!fullFeatured && destination.rootRequired) return@forEach val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) NavigationBarItem( diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt index 269f48b2d04c..0807d0521dfe 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt @@ -12,13 +12,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.AlertDialog import androidx.compose.material3.ElevatedCard import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt index af47ee64a6bb..8195cdddd40b 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt @@ -10,9 +10,9 @@ 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.automirrored.outlined.ArrowBack import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Search -import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -27,7 +27,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -39,7 +38,7 @@ import androidx.compose.ui.unit.dp private const val TAG = "SearchBar" -@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SearchAppBar( title: @Composable () -> Unit, @@ -115,7 +114,7 @@ fun SearchAppBar( if (onBackClick != null) { IconButton( onClick = onBackClick, - content = { Icon(Icons.Outlined.ArrowBack, null) } + content = { Icon(Icons.AutoMirrored.Outlined.ArrowBack, null) } ) } }, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt index c3e3b5c4e65b..6db20b163cde 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt @@ -1,11 +1,13 @@ package me.weishu.kernelsu.ui.component +import androidx.compose.foundation.clickable import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.RadioButton import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector @Composable @@ -18,6 +20,9 @@ fun SwitchItem( onCheckedChange: (Boolean) -> Unit ) { ListItem( + modifier = Modifier.clickable { + onCheckedChange.invoke(!checked) + }, headlineContent = { Text(title) }, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt index 500ae7860ab0..d2cb3f342e51 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt @@ -22,15 +22,13 @@ import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text -import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource @@ -148,7 +146,7 @@ fun RootProfileConfig( val selectedGroups = profile.groups.ifEmpty { listOf(0) }.let { e -> e.mapNotNull { g -> - Groups.values().find { it.gid == g } + Groups.entries.find { it.gid == g } } } GroupsPanel(selectedGroups) { @@ -161,7 +159,7 @@ fun RootProfileConfig( } val selectedCaps = profile.capabilities.mapNotNull { e -> - Capabilities.values().find { it.cap == e } + Capabilities.entries.find { it.cap == e } } CapsPanel(selectedCaps) { @@ -190,7 +188,7 @@ fun RootProfileConfig( @Composable fun GroupsPanel(selected: List, closeSelection: (selection: Set) -> Unit) { val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit -> - val groups = Groups.values().sortedWith( + val groups = Groups.entries.toTypedArray().sortedWith( compareBy { if (selected.contains(it)) 0 else 1 } .then(compareBy { when (it) { @@ -265,7 +263,7 @@ fun CapsPanel( closeSelection: (selection: Set) -> Unit ) { val selectCapabilitiesDialog = rememberCustomDialog { dismiss -> - val caps = Capabilities.values().sortedWith( + val caps = Capabilities.entries.toTypedArray().sortedWith( compareBy { if (selected.contains(it)) 0 else 1 } .then(compareBy { it.name }) ) @@ -323,7 +321,6 @@ fun CapsPanel( } } -@OptIn(ExperimentalComposeUiApi::class) @Composable private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) { @@ -447,7 +444,7 @@ private fun SELinuxPanel( editSELinuxDialog.show() }, enabled = false, - colors = TextFieldDefaults.outlinedTextFieldColors( + colors = OutlinedTextFieldDefaults.colors( disabledTextColor = MaterialTheme.colorScheme.onSurface, disabledBorderColor = MaterialTheme.colorScheme.outline, disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt index b1ed8a8b9d99..d09a23434f85 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt @@ -2,10 +2,10 @@ package me.weishu.kernelsu.ui.component.profile import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ReadMore import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.ArrowDropUp import androidx.compose.material.icons.filled.Create -import androidx.compose.material.icons.filled.ReadMore import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox @@ -105,7 +105,7 @@ fun TemplateConfig( IconButton(onClick = { onViewTemplate(tid) }) { - Icon(Icons.Filled.ReadMore, null) + Icon(Icons.AutoMirrored.Filled.ReadMore, null) } } ) diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt index b68e45a29e6f..ca53388f8ea4 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt @@ -15,15 +15,15 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.Android -import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Security -import androidx.compose.material3.Divider import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem @@ -246,12 +246,11 @@ private fun TopBar(onBack: () -> Unit) { navigationIcon = { IconButton( onClick = onBack - ) { Icon(Icons.Filled.ArrowBack, contentDescription = null) } + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, ) } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ProfileBox( mode: Mode, @@ -263,7 +262,7 @@ private fun ProfileBox( supportingContent = { Text(mode.text) }, leadingContent = { Icon(Icons.Filled.AccountCircle, null) }, ) - Divider(thickness = Dp.Hairline) + HorizontalDivider(thickness = Dp.Hairline) ListItem(headlineContent = { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt index 3a1a8074d626..1e3d48fdae05 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt @@ -9,13 +9,24 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Save -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.key @@ -25,25 +36,35 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.KeyEventBlocker +import me.weishu.kernelsu.ui.util.LkmSelection import me.weishu.kernelsu.ui.util.LocalSnackbarHost +import me.weishu.kernelsu.ui.util.flashModule import me.weishu.kernelsu.ui.util.installBoot -import me.weishu.kernelsu.ui.util.installModule import me.weishu.kernelsu.ui.util.reboot +import me.weishu.kernelsu.ui.util.restoreBoot +import me.weishu.kernelsu.ui.util.uninstallPermanently import java.io.File import java.text.SimpleDateFormat -import java.util.* +import java.util.Date +import java.util.Locale + +enum class FlashingStatus { + FLASHING, + SUCCESS, + FAILED +} /** * @author weishu * @date 2023/1/1. */ -@OptIn(ExperimentalComposeUiApi::class) @Composable @Destination fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { @@ -55,28 +76,37 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { val snackBarHost = LocalSnackbarHost.current val scope = rememberCoroutineScope() val scrollState = rememberScrollState() + var flashing by rememberSaveable { + mutableStateOf(FlashingStatus.FLASHING) + } LaunchedEffect(Unit) { if (text.isNotEmpty()) { return@LaunchedEffect } withContext(Dispatchers.IO) { - flashIt(flashIt, onFinish = { showReboot -> + flashIt(flashIt, onFinish = { showReboot, code -> + if (code != 0) { + text += "Error: exit code = $code.\nPlease save and check the log.\n" + } if (showReboot) { + text += "\n\n\n" showFloatAction = true } + flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED }, onStdout = { text += "$it\n" logContent.append(it).append("\n") }, onStderr = { logContent.append(it).append("\n") - }); + }) } } Scaffold( topBar = { TopBar( + flashing, onBack = { navigator.popBackStack() }, @@ -137,38 +167,58 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { @Parcelize sealed class FlashIt : Parcelable { - data class FlashBoot(val bootUri: Uri? = null, val ota: Boolean) : FlashIt() + data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) : + FlashIt() data class FlashModule(val uri: Uri) : FlashIt() + + data object FlashRestore : FlashIt() + + data object FlashUninstall : FlashIt() } fun flashIt( - flashIt: FlashIt, onFinish: (Boolean) -> Unit, + flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit ) { when (flashIt) { is FlashIt.FlashBoot -> installBoot( - flashIt.bootUri, + flashIt.boot, + flashIt.lkm, flashIt.ota, onFinish, onStdout, onStderr ) - is FlashIt.FlashModule -> installModule(flashIt.uri, onFinish, onStdout, onStderr) + is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr) + + FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr) + + FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr) } } @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(onBack: () -> Unit = {}, onSave: () -> Unit = {}) { +private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) { TopAppBar( - title = { Text(stringResource(R.string.install)) }, + title = { + Text( + stringResource( + when (status) { + FlashingStatus.FLASHING -> R.string.flashing + FlashingStatus.SUCCESS -> R.string.flash_success + FlashingStatus.FAILED -> R.string.flash_failed + } + ) + ) + }, navigationIcon = { IconButton( onClick = onBack - ) { Icon(Icons.Filled.ArrowBack, contentDescription = null) } + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, actions = { IconButton(onClick = onSave) { @@ -184,5 +234,5 @@ private fun TopBar(onBack: () -> Unit = {}, onSave: () -> Unit = {}) { @Preview @Composable fun InstallPreview() { -// InstallScreen(DestinationsNavigator(), uri = Uri.EMPTY) + InstallScreen(EmptyDestinationsNavigator) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt index 289bdccae049..3425826ef946 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt @@ -327,7 +327,7 @@ fun LearnMoreCard() { uriHandler.openUri(url) } .padding(24.dp), verticalAlignment = Alignment.CenterVertically) { - Column() { + Column { Text( text = stringResource(R.string.home_learn_kernelsu), style = MaterialTheme.typography.titleSmall @@ -354,7 +354,7 @@ fun DonateCard() { uriHandler.openUri("https://patreon.com/weishu") } .padding(24.dp), verticalAlignment = Alignment.CenterVertically) { - Column() { + Column { Text( text = stringResource(R.string.home_support_title), style = MaterialTheme.typography.titleSmall diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt index e50362d5308e..71b5c9756909 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt @@ -3,7 +3,6 @@ package me.weishu.kernelsu.ui.screen import android.app.Activity import android.content.Intent import android.net.Uri -import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes @@ -13,7 +12,8 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.FileUpload import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -26,26 +26,30 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue 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.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.maxkeppeker.sheets.core.models.base.Header +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.list.ListDialog +import com.maxkeppeler.sheets.list.models.ListOption +import com.maxkeppeler.sheets.list.models.ListSelection import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import me.weishu.kernelsu.R +import me.weishu.kernelsu.ui.component.DialogHandle import me.weishu.kernelsu.ui.component.rememberConfirmDialog -import me.weishu.kernelsu.ui.component.rememberLoadingDialog +import me.weishu.kernelsu.ui.component.rememberCustomDialog import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination -import me.weishu.kernelsu.ui.util.DownloadListener -import me.weishu.kernelsu.ui.util.download -import me.weishu.kernelsu.ui.util.getLKMUrl +import me.weishu.kernelsu.ui.util.LkmSelection +import me.weishu.kernelsu.ui.util.getCurrentKmi +import me.weishu.kernelsu.ui.util.getSupportedKmis import me.weishu.kernelsu.ui.util.isAbDevice import me.weishu.kernelsu.ui.util.isInitBoot import me.weishu.kernelsu.ui.util.rootAvailable @@ -61,36 +65,81 @@ fun InstallScreen(navigator: DestinationsNavigator) { mutableStateOf(null) } - val onClickInstall = { + var lkmSelection by remember { + mutableStateOf(LkmSelection.KmiNone) + } + + val onInstall = { installMethod?.let { method -> val flashIt = FlashIt.FlashBoot( - if (method is InstallMethod.SelectFile) method.uri else null, - method is InstallMethod.DirectInstallToInactiveSlot + boot = if (method is InstallMethod.SelectFile) method.uri else null, + lkm = lkmSelection, + ota = method is InstallMethod.DirectInstallToInactiveSlot ) navigator.navigate(FlashScreenDestination(flashIt)) } } - Scaffold(topBar = { - TopBar { - navigator.popBackStack() + val currentKmi by produceState(initialValue = "") { value = getCurrentKmi() } + + val selectKmiDialog = rememberSelectKmiDialog { kmi -> + kmi?.let { + lkmSelection = LkmSelection.KmiString(it) + onInstall() } + } + + val onClickNext = { + if (lkmSelection == LkmSelection.KmiNone && currentKmi.isBlank()) { + // no lkm file selected and cannot get current kmi + selectKmiDialog.show() + } else { + onInstall() + } + } + + val selectLkmLauncher = + rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == Activity.RESULT_OK) { + it.data?.data?.let { uri -> + lkmSelection = LkmSelection.LkmUri(uri) + } + } + } + + val onLkmUpload = { + selectLkmLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply { + type = "application/octet-stream" + }) + } + + Scaffold(topBar = { + TopBar( + onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload + ) }) { Column(modifier = Modifier.padding(it)) { SelectInstallMethod { method -> installMethod = method } - Row( + Column( modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { - Button( - modifier = Modifier.fillMaxWidth(), + (lkmSelection as? LkmSelection.LkmUri)?.let { + Text( + stringResource( + id = R.string.selected_lkm, + it.uri.lastPathSegment ?: "(file)" + ) + ) + } + Button(modifier = Modifier.fillMaxWidth(), enabled = installMethod != null, onClick = { - onClickInstall() + onClickNext() }) { Text( stringResource(id = R.string.install_next), @@ -109,12 +158,12 @@ sealed class InstallMethod { override val summary: String? ) : InstallMethod() - object DirectInstall : InstallMethod() { + data object DirectInstall : InstallMethod() { override val label: Int get() = R.string.direct_install } - object DirectInstallToInactiveSlot : InstallMethod() { + data object DirectInstallToInactiveSlot : InstallMethod() { override val label: Int get() = R.string.install_inactive_slot } @@ -128,8 +177,7 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) { val rootAvailable = rootAvailable() val isAbDevice = isAbDevice() val selectFileTip = stringResource( - id = R.string.select_file_tip, - if (isInitBoot()) "init_boot" else "boot" + id = R.string.select_file_tip, if (isInitBoot()) "init_boot" else "boot" ) val radioOptions = mutableListOf(InstallMethod.SelectFile(summary = selectFileTip)) @@ -165,11 +213,9 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) { when (option) { is InstallMethod.SelectFile -> { - selectImageLauncher.launch( - Intent(Intent.ACTION_GET_CONTENT).apply { - type = "application/octet-stream" - } - ) + selectImageLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply { + type = "application/octet-stream" + }) } is InstallMethod.DirectInstall -> { @@ -217,19 +263,49 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(onBack: () -> Unit = {}) { - TopAppBar( - title = { Text(stringResource(R.string.install)) }, - navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.Filled.ArrowBack, contentDescription = null) } - }, - ) +fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle { + return rememberCustomDialog { dismiss -> + val supportedKmi by produceState(initialValue = emptyList()) { + value = getSupportedKmis() + } + val options = supportedKmi.map { value -> + ListOption( + titleText = value + ) + } + + var selection by remember { mutableStateOf(null) } + ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = { + onSelected(selection) + }, onCloseRequest = { + dismiss() + }), header = Header.Default( + title = stringResource(R.string.select_kmi), + ), selection = ListSelection.Single( + showRadioButtons = true, + options = options, + ) { _, option -> + selection = option.titleText + }) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) { + TopAppBar(title = { Text(stringResource(R.string.install)) }, navigationIcon = { + IconButton( + onClick = onBack + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } + }, actions = { + IconButton(onClick = onLkmUpload) { + Icon(Icons.Filled.FileUpload, contentDescription = null) + } + }) } @Composable @Preview fun SelectInstall_Preview() { -// InstallScreen(DestinationsNavigator()) + InstallScreen(EmptyDestinationsNavigator) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt index 501b08746cde..9812e87ebba6 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt @@ -8,7 +8,17 @@ import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape @@ -18,9 +28,29 @@ 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.material3.Button +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarResult +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -44,15 +74,22 @@ import me.weishu.kernelsu.ui.component.ConfirmResult import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberLoadingDialog import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination -import me.weishu.kernelsu.ui.screen.destinations.WebScreenDestination -import me.weishu.kernelsu.ui.util.* +import me.weishu.kernelsu.ui.util.DownloadListener +import me.weishu.kernelsu.ui.util.LocalSnackbarHost +import me.weishu.kernelsu.ui.util.download +import me.weishu.kernelsu.ui.util.hasMagisk +import me.weishu.kernelsu.ui.util.reboot +import me.weishu.kernelsu.ui.util.toggleModule +import me.weishu.kernelsu.ui.util.uninstallModule import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel +import me.weishu.kernelsu.ui.webui.WebUIActivity import okhttp3.OkHttpClient @Destination @Composable fun ModuleScreen(navigator: DestinationsNavigator) { val viewModel = viewModel() + val context = LocalContext.current LaunchedEffect(Unit) { if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) { @@ -126,7 +163,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) { navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it))) }, onClickModule = { id, name, hasWebUi -> if (hasWebUi) { - navigator.navigate(WebScreenDestination(id, name)) + context.startActivity(Intent(context, WebUIActivity::class.java) + .setData(Uri.parse("kernelsu://webui/$id")) + .putExtra("id", id) + .putExtra("name", name) + ) } }) } @@ -392,15 +433,13 @@ private fun ModuleItem( onClick: (ModuleViewModel.ModuleInfo) -> Unit ) { ElevatedCard( - modifier = Modifier - .fillMaxWidth() - .clickable { onClick(module) }, + modifier = Modifier.fillMaxWidth(), colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface) ) { val textDecoration = if (!module.remove) null else TextDecoration.LineThrough - Column(modifier = Modifier.padding(24.dp, 16.dp, 24.dp, 0.dp)) { + Column(modifier = Modifier.clickable { onClick(module) }.padding(24.dp, 16.dp, 24.dp, 0.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, @@ -465,7 +504,7 @@ private fun ModuleItem( Spacer(modifier = Modifier.height(16.dp)) - Divider(thickness = Dp.Hairline) + HorizontalDivider(thickness = Dp.Hairline) Row( horizontalArrangement = Arrangement.SpaceBetween, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt index 71502e98b1a0..4af20bd4e726 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt @@ -3,20 +3,52 @@ package me.weishu.kernelsu.ui.screen import android.content.Context import android.content.Intent import android.net.Uri +import android.widget.Toast import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.Undo +import androidx.compose.material.icons.filled.BugReport +import androidx.compose.material.icons.filled.Compress +import androidx.compose.material.icons.filled.ContactPage +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.DeleteForever +import androidx.compose.material.icons.filled.DeveloperMode +import androidx.compose.material.icons.filled.Fence +import androidx.compose.material.icons.filled.RemoveModerator +import androidx.compose.material.icons.filled.Update +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.core.content.FileProvider +import com.maxkeppeker.sheets.core.models.base.Header +import com.maxkeppeker.sheets.core.models.base.IconSource +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.list.ListDialog +import com.maxkeppeler.sheets.list.models.ListOption +import com.maxkeppeler.sheets.list.models.ListSelection import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -24,11 +56,16 @@ import me.weishu.kernelsu.BuildConfig import me.weishu.kernelsu.Natives import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.AboutDialog +import me.weishu.kernelsu.ui.component.ConfirmResult +import me.weishu.kernelsu.ui.component.DialogHandle import me.weishu.kernelsu.ui.component.SwitchItem +import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberCustomDialog import me.weishu.kernelsu.ui.component.rememberLoadingDialog import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination +import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination import me.weishu.kernelsu.ui.util.getBugreportFile +import me.weishu.kernelsu.ui.util.shrinkModules /** * @author weishu @@ -48,8 +85,13 @@ fun SettingScreen(navigator: DestinationsNavigator) { AboutDialog(it) } val loadingDialog = rememberLoadingDialog() + val shrinkDialog = rememberConfirmDialog() - Column(modifier = Modifier.padding(paddingValues)) { + Column( + modifier = Modifier + .padding(paddingValues) + .verticalScroll(rememberScrollState()) + ) { val context = LocalContext.current val scope = rememberCoroutineScope() @@ -148,6 +190,37 @@ fun SettingScreen(navigator: DestinationsNavigator) { } ) + val shrink = stringResource(id = R.string.shrink_sparse_image) + val shrinkMessage = stringResource(id = R.string.shrink_sparse_image_message) + ListItem( + leadingContent = { + Icon( + Icons.Filled.Compress, + shrink + ) + }, + headlineContent = { Text(shrink) }, + modifier = Modifier.clickable { + scope.launch { + val result = + shrinkDialog.awaitConfirm(title = shrink, content = shrinkMessage) + if (result == ConfirmResult.Confirmed) { + loadingDialog.withLoading { + shrinkModules() + } + } + } + } + ) + + val lkmMode = + Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode + if (lkmMode) { + UninstallItem(navigator) { + loadingDialog.withLoading(it) + } + } + val about = stringResource(id = R.string.about) ListItem( leadingContent = { @@ -165,6 +238,110 @@ fun SettingScreen(navigator: DestinationsNavigator) { } } +@Composable +fun UninstallItem( + navigator: DestinationsNavigator, + withLoading: suspend (suspend () -> Unit) -> Unit +) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + val uninstallConfirmDialog = rememberConfirmDialog() + val showTodo = { + Toast.makeText(context, "TODO", Toast.LENGTH_SHORT).show() + } + val uninstallDialog = rememberUninstallDialog { uninstallType -> + scope.launch { + val result = uninstallConfirmDialog.awaitConfirm( + title = context.getString(uninstallType.title), + content = context.getString(uninstallType.message) + ) + if (result == ConfirmResult.Confirmed) { + withLoading { + when (uninstallType) { + UninstallType.TEMPORARY -> showTodo() + UninstallType.PERMANENT -> navigator.navigate( + FlashScreenDestination(FlashIt.FlashUninstall) + ) + + UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate( + FlashScreenDestination(FlashIt.FlashRestore) + ) + + UninstallType.NONE -> Unit + } + } + } + } + } + val uninstall = stringResource(id = R.string.settings_uninstall) + ListItem( + leadingContent = { + Icon( + Icons.Filled.Delete, + uninstall + ) + }, + headlineContent = { Text(uninstall) }, + modifier = Modifier.clickable { + uninstallDialog.show() + } + ) +} + +enum class UninstallType(val title: Int, val message: Int, val icon: ImageVector) { + TEMPORARY( + R.string.settings_uninstall_temporary, + R.string.settings_uninstall_temporary_message, + Icons.Filled.Delete + ), + PERMANENT( + R.string.settings_uninstall_permanent, + R.string.settings_uninstall_permanent_message, + Icons.Filled.DeleteForever + ), + RESTORE_STOCK_IMAGE( + R.string.settings_restore_stock_image, + R.string.settings_restore_stock_image_message, + Icons.AutoMirrored.Filled.Undo + ), + NONE(0, 0, Icons.Filled.Delete) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle { + return rememberCustomDialog { dismiss -> + val options = listOf( + // UninstallType.TEMPORARY, + UninstallType.PERMANENT, + UninstallType.RESTORE_STOCK_IMAGE + ) + val listOptions = options.map { + ListOption( + titleText = stringResource(it.title), + subtitleText = if (it.message != 0) stringResource(it.message) else null, + icon = IconSource(it.icon) + ) + } + + var selection = UninstallType.NONE + ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = { + if (selection != UninstallType.NONE) { + onSelected(selection) + } + }, onCloseRequest = { + dismiss() + }), header = Header.Default( + title = stringResource(R.string.settings_uninstall), + ), selection = ListSelection.Single( + showRadioButtons = false, + options = listOptions, + ) { index, _ -> + selection = options[index] + }) + } +} + @OptIn(ExperimentalMaterial3Api::class) @Composable private fun TopBar(onBack: () -> Unit = {}) { @@ -173,7 +350,13 @@ private fun TopBar(onBack: () -> Unit = {}) { navigationIcon = { IconButton( onClick = onBack - ) { Icon(Icons.Filled.ArrowBack, contentDescription = null) } + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, ) } + +@Preview +@Composable +private fun SettingsPreview() { + SettingScreen(EmptyDestinationsNavigator) +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt index c933e0d1c8cc..bf353b0afa84 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt @@ -6,14 +6,15 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding 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.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ImportExport import androidx.compose.material.icons.filled.Sync import androidx.compose.material.pullrefresh.PullRefreshIndicator @@ -43,6 +44,7 @@ import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator @@ -149,7 +151,9 @@ fun AppProfileTemplateScreen( .padding(innerPadding) .pullRefresh(refreshState) ) { - LazyColumn(Modifier.fillMaxSize()) { + LazyColumn(Modifier.fillMaxSize(), contentPadding = remember { + PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */) + }) { items(viewModel.templateList, key = { it.id }) { app -> TemplateItem(navigator, app) } @@ -214,7 +218,7 @@ private fun TopBar( navigationIcon = { IconButton( onClick = onBack - ) { Icon(Icons.Filled.ArrowBack, contentDescription = null) } + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, actions = { IconButton(onClick = onSync) { diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt index 5059244f8188..b6b7cc8028ec 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt @@ -10,7 +10,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.DeleteForever import androidx.compose.material.icons.filled.Save import androidx.compose.material3.ExperimentalMaterial3Api @@ -40,16 +40,12 @@ import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.result.ResultBackNavigator import me.weishu.kernelsu.Natives import me.weishu.kernelsu.R -import me.weishu.kernelsu.profile.Capabilities -import me.weishu.kernelsu.profile.Groups import me.weishu.kernelsu.ui.component.profile.RootProfileConfig import me.weishu.kernelsu.ui.util.deleteAppProfileTemplate import me.weishu.kernelsu.ui.util.getAppProfileTemplate import me.weishu.kernelsu.ui.util.setAppProfileTemplate import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel import me.weishu.kernelsu.ui.viewmodel.toJSON -import org.json.JSONArray -import org.json.JSONObject /** * @author weishu @@ -259,7 +255,7 @@ private fun TopBar( }, navigationIcon = { IconButton( onClick = onBack - ) { Icon(Icons.Filled.ArrowBack, contentDescription = null) } + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, actions = { if (readOnly) { return@TopAppBar @@ -279,7 +275,6 @@ private fun TopBar( }) } -@OptIn(ExperimentalComposeUiApi::class) @Composable private fun TextEdit( label: String, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/WebScreen.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/WebScreen.kt deleted file mode 100644 index 54d97d5ae2dc..000000000000 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/WebScreen.kt +++ /dev/null @@ -1,77 +0,0 @@ -package me.weishu.kernelsu.ui.screen - -import android.annotation.SuppressLint -import android.app.Activity -import android.content.Context -import android.util.Log -import android.webkit.WebResourceRequest -import android.webkit.WebResourceResponse -import android.webkit.WebView -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.webkit.WebViewAssetLoader -import com.google.accompanist.web.AccompanistWebViewClient -import com.google.accompanist.web.WebView -import com.google.accompanist.web.rememberWebViewState -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import me.weishu.kernelsu.ui.webui.SuFilePathHandler -import me.weishu.kernelsu.ui.webui.WebViewInterface -import me.weishu.kernelsu.ui.webui.showSystemUI -import java.io.File - -@SuppressLint("SetJavaScriptEnabled") -@Destination -@Composable -fun WebScreen(navigator: DestinationsNavigator, moduleId: String, moduleName: String) { - - val context = LocalContext.current - - DisposableEffect(Unit) { - val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - WebView.setWebContentsDebuggingEnabled(prefs.getBoolean("enable_web_debugging", false)) - onDispose { - if (WebViewInterface.isHideSystemUI && context is Activity) { - showSystemUI(context.window) - } - } - } - - Scaffold { innerPadding -> - val webRoot = File("/data/adb/modules/${moduleId}/webroot") - val webViewAssetLoader = WebViewAssetLoader.Builder() - .setDomain("mui.kernelsu.org") - .addPathHandler("/", - SuFilePathHandler(context, webRoot) - ) - .build() - - val webViewClient = object : AccompanistWebViewClient() { - override fun shouldInterceptRequest( - view: WebView, - request: WebResourceRequest - ): WebResourceResponse? { - return webViewAssetLoader.shouldInterceptRequest(request.url) - } - } - WebView( - state = rememberWebViewState(url = "https://mui.kernelsu.org/index.html"), - Modifier - .fillMaxSize() - .padding(innerPadding), - client = webViewClient, - factory = { context -> - WebView(context).apply { - settings.javaScriptEnabled = true - settings.domStorageEnabled = true - settings.allowFileAccess = false - addJavascriptInterface(WebViewInterface(context, this), "ksu") - } - }) - } -} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt index 040b2bf86971..3b3945d0cdc5 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt @@ -1,17 +1,17 @@ package me.weishu.kernelsu.ui.theme -import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect -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 private val DarkColorScheme = darkColorScheme( diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/Downloader.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/Downloader.kt index 0447176cc7d2..e72d70b2d074 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/Downloader.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/Downloader.kt @@ -7,10 +7,10 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.Uri +import android.os.Build import android.os.Environment import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import me.weishu.kernelsu.getKMI /** * @author weishu @@ -95,38 +95,6 @@ fun checkNewVersion(): Triple { } return defaultValue } -fun getLKMUrl(): Result> { - val url = "https://api.github.com/repos/tiann/KernelSU/releases/latest" - - val kmi = getKMI() ?: return Result.failure(RuntimeException("Get KMI failed")) - runCatching { - okhttp3.OkHttpClient().newCall(okhttp3.Request.Builder().url(url).build()).execute() - .use { response -> - val body = response.body?.string() ?: return Result.failure(RuntimeException("request body failed")) - if (!response.isSuccessful) { - return Result.failure(RuntimeException("Request failed, code: ${response.code}, message: $body")) - } - val json = org.json.JSONObject(body) - - val assets = json.getJSONArray("assets") - for (i in 0 until assets.length()) { - val asset = assets.getJSONObject(i) - val name = asset.getString("name") - if (!name.endsWith(".ko")) { - continue - } - - if (name.contains(kmi)) { - return Result.success(Pair(name, asset.getString("browser_download_url"))) - } - } - } - }.onFailure { - return Result.failure(it) - } - - return Result.failure(RuntimeException("Cannot find LKM for $kmi")) -} @Composable fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) { @@ -156,12 +124,20 @@ fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) { } } } - context.registerReceiver( - receiver, - IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.registerReceiver( + receiver, + IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), + Context.RECEIVER_EXPORTED + ) + } else { + context.registerReceiver( + receiver, + IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) + ) + } onDispose { context.unregisterReceiver(receiver) } } -} \ No newline at end of file +} diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/HanziToPinyin.java b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/HanziToPinyin.java index ab905a388520..d3d57cefe2ea 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/HanziToPinyin.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/HanziToPinyin.java @@ -391,9 +391,9 @@ public static HanziToPinyin getInstance() { return sInstance; } // Check if zh_CN collation data is available - final Locale locale[] = Collator.getAvailableLocales(); - for (int i = 0; i < locale.length; i++) { - if (locale[i].equals(Locale.CHINA) || locale[i].getLanguage().contains("zh")) { + final Locale[] locale = Collator.getAvailableLocales(); + for (Locale value : locale) { + if (value.equals(Locale.CHINA) || value.getLanguage().contains("zh")) { // Do self validation just once. if (DEBUG) { Log.d(TAG, "Self validation. Result: " + doSelfValidation()); @@ -508,7 +508,7 @@ private Token getToken(char character) { * Token. If these is no China collator, the empty token array is returned. */ public ArrayList get(final String input) { - ArrayList tokens = new ArrayList(); + ArrayList tokens = new ArrayList<>(); if (!mHasChinaCollator || TextUtils.isEmpty(input)) { // return empty tokens. return tokens; diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt index 712c3ea68f25..a4e1066b32d2 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt @@ -3,12 +3,16 @@ package me.weishu.kernelsu.ui.util import android.net.Uri import android.os.Build import android.os.Environment +import android.os.Parcelable import android.os.SystemClock import android.util.Log import com.topjohnwu.superuser.CallbackList import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.io.SuFile +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.parcelize.Parcelize import me.weishu.kernelsu.BuildConfig import me.weishu.kernelsu.Natives import me.weishu.kernelsu.ksuApp @@ -37,6 +41,13 @@ fun getRootShell(globalMnt: Boolean = false): Shell { } } +inline fun withNewRootShell( + globalMnt: Boolean = false, + block: Shell.() -> T +): T { + return createRootShell(globalMnt).use(block) +} + fun createRootShell(globalMnt: Boolean = false): Shell { Shell.enableVerboseLogging = BuildConfig.DEBUG val builder = Shell.Builder.create() @@ -47,19 +58,34 @@ fun createRootShell(globalMnt: Boolean = false): Shell { builder.build(getKsuDaemonPath(), "debug", "su") } } catch (e: Throwable) { - Log.e(TAG, "su failed: ", e) - builder.build("sh") + Log.w(TAG, "ksu failed: ", e) + try { + if (globalMnt) { + builder.build("su") + } else { + builder.build("su", "-mm") + } + } catch (e: Throwable) { + Log.e(TAG, "su failed: ", e) + builder.build("sh") + } } } -fun execKsud(args: String): Boolean { - val shell = getRootShell() - return ShellUtils.fastCmdResult(shell, "${getKsuDaemonPath()} $args") +fun execKsud(args: String, newShell: Boolean = false): Boolean { + return if (newShell) { + withNewRootShell { + ShellUtils.fastCmdResult(this, "${getKsuDaemonPath()} $args") + } + } else { + ShellUtils.fastCmdResult(getRootShell(), "${getKsuDaemonPath()} $args") + } } fun install() { val start = SystemClock.elapsedRealtime() - val result = execKsud("install") + val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so").absolutePath + val result = execKsud("install --magiskboot $magiskboot", true) Log.w(TAG, "install result: $result, cost: ${SystemClock.elapsedRealtime() - start}ms") } @@ -89,21 +115,44 @@ fun toggleModule(id: String, enable: Boolean): Boolean { } else { "module disable $id" } - val result = execKsud(cmd) + val result = execKsud(cmd, true) Log.i(TAG, "$cmd result: $result") return result } fun uninstallModule(id: String): Boolean { val cmd = "module uninstall $id" - val result = execKsud(cmd) + val result = execKsud(cmd, true) Log.i(TAG, "uninstall module $id result: $result") return result } -fun installModule( +private fun flashWithIO( + cmd: String, + onStdout: (String) -> Unit, + onStderr: (String) -> Unit +): Shell.Result { + + val stdoutCallback: CallbackList = object : CallbackList() { + override fun onAddElement(s: String?) { + onStdout(s ?: "") + } + } + + val stderrCallback: CallbackList = object : CallbackList() { + override fun onAddElement(s: String?) { + onStderr(s ?: "") + } + } + + return withNewRootShell { + newJob().add(cmd).to(stdoutCallback, stderrCallback).exec() + } +} + +fun flashModule( uri: Uri, - onFinish: (Boolean) -> Unit, + onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit ): Boolean { @@ -114,39 +163,52 @@ fun installModule( this?.copyTo(output) } val cmd = "module install ${file.absolutePath}" - - val shell = createRootShell() - - val stdoutCallback: CallbackList = object : CallbackList() { - override fun onAddElement(s: String?) { - onStdout(s ?: "") - } - } - - val stderrCallback: CallbackList = object : CallbackList() { - override fun onAddElement(s: String?) { - onStderr(s ?: "") - } - } - - val result = - shell.newJob().add("${getKsuDaemonPath()} $cmd").to(stdoutCallback, stderrCallback) - .exec() + val result = flashWithIO("${getKsuDaemonPath()} $cmd", onStdout, onStderr) Log.i("KernelSU", "install module $uri result: $result") file.delete() - onFinish(result.isSuccess) + onFinish(result.isSuccess, result.code) return result.isSuccess } } +fun restoreBoot( + onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit +): Boolean { + val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so") + val result = flashWithIO("${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot", onStdout, onStderr) + onFinish(result.isSuccess, result.code) + return result.isSuccess +} + +fun uninstallPermanently( + onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit +): Boolean { + val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so") + val result = flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr) + onFinish(result.isSuccess, result.code) + return result.isSuccess +} + +suspend fun shrinkModules(): Boolean = withContext(Dispatchers.IO) { + execKsud("module shrink", true) +} + +@Parcelize +sealed class LkmSelection : Parcelable { + data class LkmUri(val uri: Uri) : LkmSelection() + data class KmiString(val value: String) : LkmSelection() + data object KmiNone : LkmSelection() +} + fun installBoot( bootUri: Uri?, + lkm: LkmSelection, ota: Boolean, - onFinish: (Boolean) -> Unit, + onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, - onStderr: (String) -> Unit + onStderr: (String) -> Unit, ): Boolean { val resolver = ksuApp.contentResolver @@ -175,34 +237,42 @@ fun installBoot( cmd += " -u" } - // output dir - val downloadsDir = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - cmd += " -o $downloadsDir" + var lkmFile: File? = null + when (lkm) { + is LkmSelection.LkmUri -> { + lkmFile = with(resolver.openInputStream(lkm.uri)) { + val file = File(ksuApp.cacheDir, "kernelsu-tmp-lkm.ko") + file.outputStream().use { output -> + this?.copyTo(output) + } - val shell = createRootShell() + file + } + cmd += " -m ${lkmFile.absolutePath}" + } - val stdoutCallback: CallbackList = object : CallbackList() { - override fun onAddElement(s: String?) { - onStdout(s ?: "") + is LkmSelection.KmiString -> { + cmd += " --kmi ${lkm.value}" } - } - val stderrCallback: CallbackList = object : CallbackList() { - override fun onAddElement(s: String?) { - onStderr(s ?: "") + LkmSelection.KmiNone -> { + // do nothing } } - val result = - shell.newJob().add("${getKsuDaemonPath()} $cmd").to(stdoutCallback, stderrCallback) - .exec() + // output dir + val downloadsDir = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + cmd += " -o $downloadsDir" + + val result = flashWithIO("${getKsuDaemonPath()} $cmd", onStdout, onStderr) Log.i("KernelSU", "install boot result: ${result.isSuccess}") bootFile?.delete() + lkmFile?.delete() // if boot uri is empty, it is direct install, when success, we should show reboot button - onFinish(bootUri == null && result.isSuccess) + onFinish(bootUri == null && result.isSuccess, result.code) return result.isSuccess } @@ -240,6 +310,19 @@ fun isInitBoot(): Boolean { .toInt() >= Build.VERSION_CODES.TIRAMISU } +suspend fun getCurrentKmi(): String = withContext(Dispatchers.IO) { + val shell = getRootShell() + val cmd = "boot-info current-kmi" + ShellUtils.fastCmd(shell, "${getKsuDaemonPath()} $cmd") +} + +suspend fun getSupportedKmis(): List = withContext(Dispatchers.IO) { + val shell = getRootShell() + val cmd = "boot-info supported-kmi" + val out = shell.newJob().add("${getKsuDaemonPath()} $cmd").to(ArrayList(), null).exec().out + out.filter { it.isNotBlank() }.map { it.trim() } +} + fun overlayFsAvailable(): Boolean { val shell = getRootShell() // check /proc/filesystems @@ -275,9 +358,8 @@ fun getSepolicy(pkg: String): String { fun setSepolicy(pkg: String, rules: String): Boolean { val shell = getRootShell() - val result = - shell.newJob().add("${getKsuDaemonPath()} profile set-sepolicy $pkg '$rules'") - .to(ArrayList(), null).exec() + val result = shell.newJob().add("${getKsuDaemonPath()} profile set-sepolicy $pkg '$rules'") + .to(ArrayList(), null).exec() Log.i(TAG, "set sepolicy result: ${result.code}") return result.isSuccess } @@ -291,22 +373,21 @@ fun listAppProfileTemplates(): List { fun getAppProfileTemplate(id: String): String { val shell = getRootShell() return shell.newJob().add("${getKsuDaemonPath()} profile get-template '${id}'") - .to(ArrayList(), null) - .exec().out.joinToString("\n") + .to(ArrayList(), null).exec().out.joinToString("\n") } fun setAppProfileTemplate(id: String, template: String): Boolean { val shell = getRootShell() - return shell.newJob().add("${getKsuDaemonPath()} profile set-template '${id}' '${template}'") - .to(ArrayList(), null) - .exec().isSuccess + val escapedTemplate = template.replace("\"", "\\\"") + val cmd = """${getKsuDaemonPath()} profile set-template "$id" "$escapedTemplate'"""" + return shell.newJob().add(cmd) + .to(ArrayList(), null).exec().isSuccess } fun deleteAppProfileTemplate(id: String): Boolean { val shell = getRootShell() return shell.newJob().add("${getKsuDaemonPath()} profile delete-template '${id}'") - .to(ArrayList(), null) - .exec().isSuccess + .to(ArrayList(), null).exec().isSuccess } fun forceStopApp(packageName: String) { @@ -319,11 +400,13 @@ fun launchApp(packageName: String) { val shell = getRootShell() val result = - shell.newJob().add("monkey -p $packageName -c android.intent.category.LAUNCHER 1").exec() + shell.newJob() + .add("cmd package resolve-activity --brief $packageName | tail -n 1 | xargs cmd activity start-activity -n") + .exec() Log.i(TAG, "launch $packageName result: $result") } fun restartApp(packageName: String) { forceStopApp(packageName) launchApp(packageName) -} \ No newline at end of file +} diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt index 913b70623d2a..5e81a73da4ac 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt @@ -22,7 +22,9 @@ fun getBugreportFile(context: Context): File { val tombstonesFile = File(bugreportDir, "tombstones.tar.gz") val dropboxFile = File(bugreportDir, "dropbox.tar.gz") val pstoreFile = File(bugreportDir, "pstore.tar.gz") + // Xiaomi/Readmi devices have diag in /data/vendor/diag val diagFile = File(bugreportDir, "diag.tar.gz") + val opulsFile = File(bugreportDir, "opuls.tar.gz") val bootlogFile = File(bugreportDir, "bootlog.tar.gz") val mountsFile = File(bugreportDir, "mounts.txt") val fileSystemsFile = File(bugreportDir, "filesystems.txt") @@ -43,7 +45,8 @@ fun getBugreportFile(context: Context): File { shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec() shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec() shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec() - shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag .").exec() + shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec() + shell.newJob().add("tar -czf ${opulsFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec() shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec() shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec() diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt index d51fd91ef035..78346dd9412c 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt @@ -12,7 +12,9 @@ fun getSELinuxStatus(): String { .build("sh") val list = ArrayList() - val result = shell.newJob().add("getenforce").to(list, list).exec() + val result = shell.use { + it.newJob().add("getenforce").to(list, list).exec() + } val output = result.out.joinToString("\n").trim() if (result.isSuccess) { diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt index 2d0fef8a39e6..013453fbfeed 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt @@ -1,6 +1,5 @@ package me.weishu.kernelsu.ui.viewmodel -import android.net.Uri import android.os.SystemClock import android.util.Log import androidx.compose.runtime.derivedStateOf @@ -16,7 +15,7 @@ import me.weishu.kernelsu.ui.util.overlayFsAvailable import org.json.JSONArray import org.json.JSONObject import java.text.Collator -import java.util.* +import java.util.Locale class ModuleViewModel : ViewModel() { diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt index 8dcdeda33fb9..37e05aa82aff 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt @@ -107,7 +107,7 @@ class SuperUserViewModel : ViewModel() { } } - val intent = Intent(ksuApp, KsuService::class.java); + val intent = Intent(ksuApp, KsuService::class.java) val task = KsuService.bindOrTask( intent, @@ -119,7 +119,7 @@ class SuperUserViewModel : ViewModel() { } private fun stopKsuService() { - val intent = Intent(ksuApp, KsuService::class.java); + val intent = Intent(ksuApp, KsuService::class.java) KsuService.stop(intent) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/TemplateViewModel.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/TemplateViewModel.kt index 2ccfa147c12d..cbed82f21c05 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/TemplateViewModel.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/TemplateViewModel.kt @@ -22,6 +22,8 @@ import org.json.JSONArray import org.json.JSONObject import java.text.Collator import java.util.Locale +import java.util.concurrent.TimeUnit + /** * @author weishu @@ -136,7 +138,13 @@ class TemplateViewModel : ViewModel() { private fun fetchRemoteTemplates() { runCatching { - OkHttpClient().newCall( + val client: OkHttpClient = OkHttpClient.Builder() + .connectTimeout(5, TimeUnit.SECONDS) + .writeTimeout(5, TimeUnit.SECONDS) + .readTimeout(10, TimeUnit.SECONDS) + .build() + + client.newCall( Request.Builder().url(TEMPLATE_INDEX_URL).build() ).execute().use { response -> if (!response.isSuccessful) { @@ -146,7 +154,8 @@ private fun fetchRemoteTemplates() { Log.i(TAG, "fetchRemoteTemplates: $remoteTemplateIds") 0.until(remoteTemplateIds.length()).forEach { i -> val id = remoteTemplateIds.getString(i) - val templateJson = OkHttpClient().newCall( + Log.i(TAG, "fetch template: $id") + val templateJson = client.newCall( Request.Builder().url(TEMPLATE_URL.format(id)).build() ).runCatching { execute().use { response -> @@ -209,9 +218,14 @@ private fun getLocaleString(json: JSONObject, key: String): String { val locale = Locale.getDefault() val localeKey = "${locale.language}_${locale.country}" json.optJSONObject("locales")?.let { + // check locale first it.optJSONObject(localeKey)?.let { json-> return json.optString(key, fallback) } + // fallback to language + it.optJSONObject(locale.language)?.let { json-> + return json.optString(key, fallback) + } } return fallback } @@ -221,7 +235,7 @@ private fun fromJSON(templateJson: JSONObject): TemplateViewModel.TemplateInfo? val groupsJsonArray = templateJson.optJSONArray("groups") val capabilitiesJsonArray = templateJson.optJSONArray("capabilities") val context = templateJson.optString("context").takeIf { it.isNotEmpty() } - ?: Natives.KERNEL_SU_DOMAIN; + ?: Natives.KERNEL_SU_DOMAIN val namespace = templateJson.optString("namespace").takeIf { it.isNotEmpty() } ?: Natives.Profile.Namespace.INHERITED.name @@ -262,13 +276,13 @@ fun TemplateViewModel.TemplateInfo.toJSON(): JSONObject { if (template.author.isNotEmpty()) { put("author", template.author) } - put("namespace", Natives.Profile.Namespace.values()[template.namespace].name) + put("namespace", Natives.Profile.Namespace.entries[template.namespace].name) put("uid", template.uid) put("gid", template.gid) if (template.groups.isNotEmpty()) { put("groups", JSONArray( - Groups.values().filter { + Groups.entries.filter { template.groups.contains(it.gid) }.map { it.name @@ -278,7 +292,7 @@ fun TemplateViewModel.TemplateInfo.toJSON(): JSONObject { if (template.capabilities.isNotEmpty()) { put("capabilities", JSONArray( - Capabilities.values().filter { + Capabilities.entries.filter { template.capabilities.contains(it.cap) }.map { it.name diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/MimeUtil.java b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/MimeUtil.java index 79ba2ff60b09..1fc2f4a4ceab 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/MimeUtil.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/MimeUtil.java @@ -51,89 +51,38 @@ private static String guessHardcodedMime(String fileName) { final String extension = fileName.substring(finalFullStop + 1).toLowerCase(); - switch (extension) { - case "webm": - return "video/webm"; - case "mpeg": - case "mpg": - return "video/mpeg"; - case "mp3": - return "audio/mpeg"; - case "wasm": - return "application/wasm"; - case "xhtml": - case "xht": - case "xhtm": - return "application/xhtml+xml"; - case "flac": - return "audio/flac"; - case "ogg": - case "oga": - case "opus": - return "audio/ogg"; - case "wav": - return "audio/wav"; - case "m4a": - return "audio/x-m4a"; - case "gif": - return "image/gif"; - case "jpeg": - case "jpg": - case "jfif": - case "pjpeg": - case "pjp": - return "image/jpeg"; - case "png": - return "image/png"; - case "apng": - return "image/apng"; - case "svg": - case "svgz": - return "image/svg+xml"; - case "webp": - return "image/webp"; - case "mht": - case "mhtml": - return "multipart/related"; - case "css": - return "text/css"; - case "html": - case "htm": - case "shtml": - case "shtm": - case "ehtml": - return "text/html"; - case "js": - case "mjs": - return "application/javascript"; - case "xml": - return "text/xml"; - case "mp4": - case "m4v": - return "video/mp4"; - case "ogv": - case "ogm": - return "video/ogg"; - case "ico": - return "image/x-icon"; - case "woff": - return "application/font-woff"; - case "gz": - case "tgz": - return "application/gzip"; - case "json": - return "application/json"; - case "pdf": - return "application/pdf"; - case "zip": - return "application/zip"; - case "bmp": - return "image/bmp"; - case "tiff": - case "tif": - return "image/tiff"; - default: - return null; - } + return switch (extension) { + case "webm" -> "video/webm"; + case "mpeg", "mpg" -> "video/mpeg"; + case "mp3" -> "audio/mpeg"; + case "wasm" -> "application/wasm"; + case "xhtml", "xht", "xhtm" -> "application/xhtml+xml"; + case "flac" -> "audio/flac"; + case "ogg", "oga", "opus" -> "audio/ogg"; + case "wav" -> "audio/wav"; + case "m4a" -> "audio/x-m4a"; + case "gif" -> "image/gif"; + case "jpeg", "jpg", "jfif", "pjpeg", "pjp" -> "image/jpeg"; + case "png" -> "image/png"; + case "apng" -> "image/apng"; + case "svg", "svgz" -> "image/svg+xml"; + case "webp" -> "image/webp"; + case "mht", "mhtml" -> "multipart/related"; + case "css" -> "text/css"; + case "html", "htm", "shtml", "shtm", "ehtml" -> "text/html"; + case "js", "mjs" -> "application/javascript"; + case "xml" -> "text/xml"; + case "mp4", "m4v" -> "video/mp4"; + case "ogv", "ogm" -> "video/ogg"; + case "ico" -> "image/x-icon"; + case "woff" -> "application/font-woff"; + case "gz", "tgz" -> "application/gzip"; + case "json" -> "application/json"; + case "pdf" -> "application/pdf"; + case "zip" -> "application/zip"; + case "bmp" -> "image/bmp"; + case "tiff", "tif" -> "image/tiff"; + default -> null; + }; } } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/SuFilePathHandler.java b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/SuFilePathHandler.java index 824cac1f0796..ff450e8e9cfe 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/SuFilePathHandler.java +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/SuFilePathHandler.java @@ -13,13 +13,10 @@ import com.topjohnwu.superuser.io.SuFileInputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.zip.GZIPInputStream; -import me.weishu.kernelsu.ui.util.KsuCliKt; - /** * Handler class to open files from file system by root access * For more information about android storage please refer to @@ -84,14 +81,14 @@ public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler { * which files can be loaded. * @throws IllegalArgumentException if the directory is not allowed. */ - public SuFilePathHandler(@NonNull Context context, @NonNull File directory) { + public SuFilePathHandler(@NonNull Context context, @NonNull File directory, Shell rootShell) { try { mDirectory = new File(getCanonicalDirPath(directory)); if (!isAllowedInternalStorageDir(context)) { throw new IllegalArgumentException("The given directory \"" + directory + "\" doesn't exist under an allowed app internal storage directory"); } - mShell = KsuCliKt.createRootShell(true); + mShell = rootShell; } catch (IOException e) { throw new IllegalArgumentException( "Failed to resolve the canonical path for the given directory: " @@ -172,8 +169,7 @@ private static InputStream handleSvgzStream(@NonNull String path, return path.endsWith(".svgz") ? new GZIPInputStream(stream) : stream; } - public static InputStream openFile(@NonNull File file, @NonNull Shell shell) throws FileNotFoundException, - IOException { + public static InputStream openFile(@NonNull File file, @NonNull Shell shell) throws IOException { SuFile suFile = new SuFile(file.getAbsolutePath()); suFile.setShell(shell); InputStream fis = SuFileInputStream.open(suFile); diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt new file mode 100644 index 000000000000..eccb002a1688 --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt @@ -0,0 +1,68 @@ +package me.weishu.kernelsu.ui.webui + +import android.annotation.SuppressLint +import android.app.ActivityManager +import android.content.Context +import android.os.Bundle +import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.activity.ComponentActivity +import androidx.webkit.WebViewAssetLoader +import com.topjohnwu.superuser.Shell +import me.weishu.kernelsu.ui.util.createRootShell +import java.io.File + +@SuppressLint("SetJavaScriptEnabled") +class WebUIActivity : ComponentActivity() { + private lateinit var webviewInterface: WebViewInterface + + private var rootShell: Shell? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val moduleId = intent.getStringExtra("id")!! + val name = intent.getStringExtra("name")!! + setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name")) + + val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE) + WebView.setWebContentsDebuggingEnabled(prefs.getBoolean("enable_web_debugging", false)) + + val webRoot = File("/data/adb/modules/${moduleId}/webroot") + val rootShell = createRootShell(true).also { this.rootShell = it } + val webViewAssetLoader = WebViewAssetLoader.Builder() + .setDomain("mui.kernelsu.org") + .addPathHandler( + "/", + SuFilePathHandler(this, webRoot, rootShell) + ) + .build() + + val webViewClient = object : WebViewClient() { + override fun shouldInterceptRequest( + view: WebView, + request: WebResourceRequest + ): WebResourceResponse? { + return webViewAssetLoader.shouldInterceptRequest(request.url) + } + } + + val webView = WebView(this).apply { + settings.javaScriptEnabled = true + settings.domStorageEnabled = true + settings.allowFileAccess = false + webviewInterface = WebViewInterface(this@WebUIActivity, this) + addJavascriptInterface(webviewInterface, "ksu") + setWebViewClient(webViewClient) + loadUrl("https://mui.kernelsu.org/index.html") + } + + setContentView(webView) + } + + override fun onDestroy() { + super.onDestroy() + runCatching { rootShell?.close() } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt index 9d94c6006fe7..4b6a3c8f755d 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt @@ -15,20 +15,16 @@ import androidx.core.view.WindowInsetsControllerCompat import com.topjohnwu.superuser.CallbackList import com.topjohnwu.superuser.ShellUtils import me.weishu.kernelsu.ui.util.createRootShell +import me.weishu.kernelsu.ui.util.withNewRootShell import org.json.JSONArray import org.json.JSONObject import java.util.concurrent.CompletableFuture class WebViewInterface(val context: Context, private val webView: WebView) { - companion object { - var isHideSystemUI: Boolean = false - } - @JavascriptInterface fun exec(cmd: String): String { - val shell = createRootShell(true) - return ShellUtils.fastCmd(shell, cmd) + return withNewRootShell(true) { ShellUtils.fastCmd(this, cmd) } } @JavascriptInterface @@ -63,8 +59,9 @@ class WebViewInterface(val context: Context, private val webView: WebView) { processOptions(finalCommand, options) finalCommand.append(cmd) - val shell = createRootShell(true) - val result = shell.newJob().add(finalCommand.toString()).to(ArrayList(), ArrayList()).exec() + val result = withNewRootShell(true) { + newJob().add(finalCommand.toString()).to(ArrayList(), ArrayList()).exec() + } val stdout = result.out.joinToString(separator = "\n") val stderr = result.err.joinToString(separator = "\n") @@ -148,6 +145,8 @@ class WebViewInterface(val context: Context, private val webView: WebView) { webView.loadUrl(emitErrCode) } } + }.whenComplete { _, _ -> + runCatching { shell.close() } } } @@ -167,7 +166,6 @@ class WebViewInterface(val context: Context, private val webView: WebView) { } else { showSystemUI(context.window) } - isHideSystemUI = enable } } } diff --git a/manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml similarity index 100% rename from manager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml diff --git a/manager/app/src/main/res/values-ar/strings.xml b/manager/app/src/main/res/values-ar/strings.xml index 4bcdd1195c83..43a1fc9f719c 100644 --- a/manager/app/src/main/res/values-ar/strings.xml +++ b/manager/app/src/main/res/values-ar/strings.xml @@ -6,7 +6,7 @@ يعمل الإصدار: %d مستخدمين الجذر: %d - الوحدات: %d + الإضافات: %d غير مدعوم KernelSU يدعم GKI kernels فقط إصدار النواة @@ -18,10 +18,10 @@ متساهل مجهول مستخدم خارق - فشل في تمكين الوحدة: %s - فشل تعطيل الوحدة : %s - لا توجد وحدات مثبتة - الوحدات + فشل في تمكين الإضافة: %s + فشل تعطيل الإضافة : %s + لا توجد إضافات مثبتة + الإضافات إلغاء التثبيت تثبيت الوحدة تثبيت @@ -33,28 +33,28 @@ إعادة تشغيل إلى وضع Download إعادة تشغيل إلى وضع EDL من نحن - هل أنت متأكد أنك تريد إلغاء تثبيت الوحدة %s ? + هل أنت متأكد أنك تريد إلغاء تثبيت الإضافة %s ? تم إلغاء تثبيتها %s فشل إلغاء التثبيت: %s الإصدار المطور - التراكبات غير متوفرة ، لا يمكن للوحدة أن تعمل! + التراكبات غير متوفرة ، لا يمكن للإضافة أن تعمل! إنعاش إظهار تطبيقات النظام إخفاء تطبيقات النظام إرسال السجلات الوضع الآمن إعادة التشغيل لتطبيق التغييرات - تم تعطيل الوحدات النمطية لأنها تتعارض مع Magisk! + تم تعطيل الإضافات لأنها تتعارض مع Magisk! تعلم KernelSU https://kernelsu.org/guide/what-is-kernelsu.html - تعرف على كيفية تثبيت KernelSU واستخدام الوحدات + تعرف على كيفية تثبيت KernelSU واستخدام الإضافات إدعمنا KernelSU سيظل دائماً مجانياً ومفتوح المصدر. مع ذلك، يمكنك أن تظهر لنا أنك تهتم بالتبرع. إنضم إلى قناتنا في %2$s ]]> القدرات تحديث - تحمبل الوحدة : %s + تحميل الإضافة: %s ابدأ التنزيل: %s الإصدار الجديد: %s متاح ، انقر للتحديث تشغيل @@ -66,13 +66,13 @@ مجموعات مُخصّص تركيب مساحة الاسم - الغاء تحميل الوحدات + الغاء تحميل الإضافات فشل تحديث ملف تعريف التطبيق لـ %s سياق SELinux ايقاف إجباري - الغاء تحميل الوحدات بشكل افتراضي - القيمة الافتراضية العامة ل \"إلغاء تحميل الوحدات \" في ملفات تعريف التطبيقات. إذا تم تمكينه ، إزالة جميع تعديلات الوحدة النمطية على النظام للتطبيقات التي لا تحتوي على مجموعة ملف تعريف. - سيسمح تمكين هذا الخيار ل KernelSU باستعادة أي ملفات معدلة بواسطة الوحدات النمطية لهذا التطبيق. + الغاء تحميل الإضافات بشكل افتراضي + القيمة الافتراضية العامة لـ\"إلغاء تحميل الإضافات\" في ملفات تعريف التطبيقات. إذا تم تمكينه، إزالة جميع تعديلات الإضافات على النظام للتطبيقات التي لا تحتوي على مجموعة ملف تعريف. + سيسمح تمكين هذا الخيار لـKernelSU باستعادة أي ملفات معدلة بواسطة الإضافات لهذا التطبيق. المجال القواعد إعادة تشغيل التطبيق @@ -115,4 +115,19 @@ سيتم **إجبار** جهازك على التمهيد إلى الفتحة غير النشطة الحالية بعد إعادة التشغيل! \nاستخدم هذا الخيار فقط بعد انتهاء التحديث. \nأستمرار؟ + اختر KMI + يوصى باستخدام صورة القسم %1$s + تصغير الصورة المتفرقة + قم بتغيير حجم الصورة المتفرقة حيث توجد الإضافة إلى حجمها الفعلي. لاحظ أن هذا قد يتسبب في عمل الإضافة بشكل غير طبيعي، لذا يرجى استخدامها فقط عند الضرورة (مثل النسخ الاحتياطي) + إلغاء التثبيت + إلغاء التثبيت مؤقتًا + إلغاء التثبيت بشكل دائم + استعادة الصورة الاصلية + ‬إلغاء تثبيت KernelSU (الجذر وجميع الوحدات) بشكل كامل ودائم. + تركيب + نجح التركيب + فشل التركيب + صورة lkm المحددة: %s + استعادة صورة المصنع المخزنة (في حالة وجود نسخة احتياطية)، والتي تُستخدم عادة قبل OTA؛ إذا كنت بحاجة إلى إلغاء تثبيت KernelSU، فيرجى استخدام \"إلغاء التثبيت الدائم\". + قم بإلغاء تثبيت KernelSU مؤقتًا، واستعد إلى حالته الأصلية بعد إعادة التشغيل التالية. \ No newline at end of file diff --git a/manager/app/src/main/res/values-de/strings.xml b/manager/app/src/main/res/values-de/strings.xml index 40395528eaca..d6482f0a0f8c 100644 --- a/manager/app/src/main/res/values-de/strings.xml +++ b/manager/app/src/main/res/values-de/strings.xml @@ -10,20 +10,20 @@ Superuser: %d Unbekannt Erzwingen - Neustart mit Bootloader - Neustart mit Download-Modus + Neustart in Bootloader + Neustart in Download-Modus Neustart mit EDL-Modus Autor overlayfs nicht verfügbar, Modul kann nicht funktionieren! Über Module sind deaktiviert, weil es einen Konflikt mit Magisk gibt! https://kernelsu.org/guide/what-is-kernelsu.html - Verstehe, wie du KernelSU installieren und Module verwendest + Erfahren, wie KernelSU installiert und Module verwendet werden Unterstütze uns - KernelSU ist und wird immer frei und quelloffen sein. Du kannst uns jedoch zeigen, dass du dich für uns interessierst, indem du eine Spende tätigst. + KernelSU ist und wird immer frei und quelloffen sein. Du kannst uns jedoch deine Unterstützung zeigen, indem du eine Spende tätigst. SELinux-Kontext Module standardmäßig aushängen - Globaler Standardwert für \'Module aushängen\' in App-Profilen. Wenn er aktiviert ist, werden alle Moduländerungen im App-System entfernt, für die kein Profil festgelegt ist. + Globaler Standardwert für \'Module aushängen\' in App-Profilen. Falls aktiviert, werden alle Moduländerungen im System für alle Apps entfernt, für die kein Profil festgelegt ist. Standard Vorlage Benutzerdefiniert @@ -31,18 +31,18 @@ Vererbt Global Individuell - Domain + Domäne Aktualisieren Wenn du diese Option aktivierst, kann KernelSU alle von den Modulen für diese App geänderten Dateien wiederherstellen. Regeln - Herunterladen starten: %s + Herunterladen startet: %s Fehler beim Aktualisieren der SELinux-Regeln für: %s Starten Neue Version: %s verfügbar, tippen zum Aktualisieren Stopp erzwingen Neustart Module: %d - Verwalter-Version + Manager-Version SELinux-Status Deaktiviert Modulaktivierung fehlgeschlagen: %s @@ -51,9 +51,9 @@ Modul Deinstallieren Installieren - Neustart + Neustarten Einstellungen - Neustart mit Recovery + Neustart in Recovery %s deinstalliert Version Neu laden @@ -62,7 +62,7 @@ Protokoll senden KernelSU verstehen Sicherer Modus - Neu starten, damit die Effekte auftreten + Neustarten, damit Änderungen wirksam werden Quellcode unter %1$s ansehen
Unserem %2$s-Kanal beitreten
Profilname Namespace einhängen @@ -75,31 +75,46 @@ Kernel Fingerabdruck Installieren - Leichter Neustart + Soft-Reboot Sicher, dass du das Modul %s deinstallieren möchtest\? Deinstallation fehlgeschlagen: %s Die aktuelle Kernel-Version %d ist zu alt für diese Manager-Version. Bitte auf Version %d oder höher upgraden! Änderungsprotokoll - erfolgreich importiert! - in Zwischenablage exportieren + Erfolgreich importiert + In Zwischenablage exportieren Kann lokale Vorlage nicht finden! - Vorlagen ID existiert bereits! - aus Zwischenablage importieren + Vorlagen-ID existiert bereits! + Aus Zwischenablage importieren Konnte Changelog nicht laden: %s Name - ungültige Vorlagen id - Online Vorlagen synchronisieren - Erstelle Vorlage - Nur-Lesen + Ungültige Vorlagen-ID + Online-Vorlagen synchronisieren + Vorlage erstellen + Schreibgeschützt Import/Export - Fehler beim speichern - Bearbeite Vorlage - id - App Profil Template + Fehler beim Speichern + Vorlage bearbeiten + ID + App-Profil-Template Beschreibung Speichern verwalte lokale und online Profil Vorlagen Löschen Zwischenablage ist leer! Vorlage ansehen + WebView-Debugging aktivieren + Kann verwendet werden zum Debugging von WebUI, bitte nur falls nötig aktivieren. + %1$s Partitionsabbild empfohlen + KMI auswählen + Weiter + Direkte Installation (empfohlen) + Datei auswählen + In inaktiven Slot installieren (nach OTA) + Nach einem Neustart wird dein Gerät **GEZWUNGEN** in den derzeit inaktiven Slot zu booten. +\nBenutze dies nur nach Fertigstellung des OTA. +\nFortfahren? + Root-Zugriff konnte nicht gewährt werden! + Öffnen + Updates suchen + Automatisch nach Updates suchen beim Öffnen der App \ No newline at end of file diff --git a/manager/app/src/main/res/values-fr/strings.xml b/manager/app/src/main/res/values-fr/strings.xml index 8532ee1d0c14..163e8b596b22 100644 --- a/manager/app/src/main/res/values-fr/strings.xml +++ b/manager/app/src/main/res/values-fr/strings.xml @@ -108,7 +108,7 @@ Activer le débogage de WebView Peut être utilisé pour déboguer WebUI, n\'activez cette option que si nécessaire. Échec de l\'octroi des privilèges root ! - Ouvert + Ouvrir Installation directe (recommandé) Sélectionner un fichier Installer dans l\'emplacement inactif (après OTA) @@ -116,4 +116,8 @@ \nN\'utilisez cette option qu\'une fois la mise à jour OTA terminée. \nContinuer ?
Suivant + L\'image de la partition %1$s est recommandée + Sélectionner une KMI + Minimiser l\'image clairsemée + Redimensionne l\'image clairsemée où se trouve le module à sa taille réelle. Notez que cela peut entraîner un dysfonctionnement du module, alors utilisez cette fonctionnalité uniquement lorsque nécessaire (pour la sauvegarde, par exemple) \ No newline at end of file diff --git a/manager/app/src/main/res/values-in/strings.xml b/manager/app/src/main/res/values-in/strings.xml index f598715ec7f6..864f9c3f2d1e 100644 --- a/manager/app/src/main/res/values-in/strings.xml +++ b/manager/app/src/main/res/values-in/strings.xml @@ -1,106 +1,134 @@ Beranda - Tidak terpasang - Klik untuk memasang - Bekerja + Tidak terinstal + Klik untuk menginstal + Berfungsi Versi: %d - Superuser: %d + SuperUser: %d Modul: %d Tidak didukung KernelSU saat ini hanya mendukung kernel GKI Kernel Versi Manager - Sidik jari + Identitas Status SELinux - Dinonaktifkan + Nonaktif Enforcing Permissive - Tidak dikenal + Unknown SuperUser Gagal mengaktifkan modul: %s Gagal menonaktifkan modul: %s - Tidak ada modul yang terpasang + Tidak ada modul Modul - Copot - Pasang - Pasang - Mulai ulang + Hapus + Instal + Instal + Reboot Pengaturan - Soft Reboot - Mulai ulang ke Recovery - Mulai ulang ke Bootloader - Mulai ulang ke Download - Mulai ulang ke EDL + SoftReboot + But ke Recovery + But ke Bootloader + But ke Download + But ke EDL Tentang - Apakah Anda yakin ingin mencopot modul %s? - %s Telah dicopot - Gagal untuk mencopot: %s + Yakin menghapus modul %s? + %s berhasil dihapus + Gagal menghapus: %s Versi - Pembuat - overlayfs tidak tersedia, modul tidak dapat bekerja! - Segarkan - Tampilkan apl sistem - Sembunyikan apl sistem + Oleh + OverlayFS tidak tersedia, modul tidak berfungsi! + Muat ulang + Tampilkan aplikasi sistem + Sembunyikan aplikasi sistem Laporkan Log Mode aman - Mulai ulang untuk menerapkan - Modul dinonaktifkan karena bertentangan dengan Magisk! + Reboot agar berfungsi + Konflik dengan Magisk, fungsi modul ditiadakan! Pelajari KernelSU https://kernelsu.org/id_ID/guide/what-is-kernelsu.html - Pelajari cara memasang KernelSU dan menggunakan modul + Pelajari cara instal KernelSU dan menggunakan modul Dukung Kami - KernelSU gratis dan bersumber terbuka, dan akan selalu seperti itu. Bagaimanapun juga Anda dapat menunjukan kepedulian Anda kepada kami dengan mengirimkan sedikit donasi. - Gabung kanal %2$s kami]]> + KernelSU akan selalu menjadi aplikasi gratis dan terbuka. Anda dapat memberikan donasi sebagai bentuk dukungan. + Lihat kode sumber di %1$s
Gabung kanal %2$s kami
Profil Apl Bawaan Templat Khusus Nama profil - Ikat ruang-nama + Mount Namespace Diwariskan Universal - Personal + Individual Kelompok Kemampuan Konteks SELinux - Lepas modul - Gagal memperbarui Profil Apl untuk %s - Lepas modul secara bawaan - Nilai bawaan universal untuk \"Lepas modul\" di Profil-profil Apl. Jika diaktifkan, ini akan menghapus semua modifikasi modul pada sistem untuk aplikasi yang tidak memiliki set Profil. - Mengaktifkan opsi ini akan mengizinkan KernelSU memulihkan file-file yang dimodifikasi oleh modul untuk aplikasi ini. + Umount Modul + Gagal membarui Profil pada %s + Melepas Modul secara bawaan + Menggunakan \"Umount Modul\" secara universal pada Profil aplikasi. Jika diaktifkan, akan menghapus semua modifikasi sistem untuk aplikasi yang tidak memiliki set Profil. + Aktifkan opsi ini agar KernelSU dapat memulihkan kembali berkas termodifikasi oleh modul pada aplikasi ini. Domain Aturan - Perbarui - Mengunduh module: %s + Membarui + Mengunduh modul: %s Mulai mengunduh: %s - Versi baru: %s sudah tersedia, tap untuk mengunduh + Tersedia versi terbaru: %s, Klik untuk membarui Jalankan Paksa Berhenti Mulai ulang - Gagal memperbarui aturan SELinux untuk: %s - Versi KernelSU saat ini %d terlalu rendah bagi manajer untuk dapat berfungsi dengan baik. Harap tingkatkan ke versi %d atau yang lebih tinggi! + Gagal membarui aturan SELinux pada: %s + Versi KernelSU %d terlalu rendah agar manajer berfungsi normal. Harap membarui ke versi %d atau di atasnya! Catatan Perubahan Berhasil diimpor - Expor ke clipboard - Tidak dapat menemukan template lokal untuk di expor! - Id template telah ada sebelumnya! - Impor dari clipboard + Ekspor ke papan klip + Tidak ditemukan templat lokal untuk diekspor! + Id templat sudah ada! + Impor dari papan klip Gagal mengambil Changelog: %s Nama - Id template tidak valid - Sinkronkan template online - Buat Template - Impor/Expor - Gagal untuk menyimpan template + Id templat tidak valid + Sinkronkan templat daring + Buat Templat + Impor/Ekspor + Gagal menyimpan templat Edit Templat id - Template Profil Aplikasi + Templat Profil Aplikasi Deskripsi Simpan - Atur template lokal dan online Profil Aplikasi + Atur templat Profil yang lokal dan daring Hapus - Clipboard kosong! - Lihat Template - Hanya baca + Papan klip kosong! + Lihat Templat + ReadOnly + Pengawakutuan WebView + Dapat mengawakutu WebView, hanya aktifkan jika butuh. + %1$s image partisi terekomendasi + Pilih KMI + Selanjutnya + Gawai akan **DIPAKSA** untuk but ke slot nonaktif! +\nHANYA gunakan setelah proses OTA selesai. +\nLanjutkan? + Instal Langsung (rekomendasi) + Pilih berkas + Instal ke slot nonaktif (setelah OTA) + Gagal memberikan akses root! + Buka + Cek terbaru + Cek terbaru setiap membuka aplikasi + Meminimalkan sparse image + Mengembalikan sparse image, lokasi modul disimpan, ke ukuran sebenarnya. Dapat menyebabkan modul bekerja abnormal, maka gunakan saat dibutuhkan saja (mis. untuk pencadangan) + Hapus permanen KernelSU (root dan modul). + Hapus Temporer + Pulihkan Image Asal + Hapus + Hapus Temporer KernelSU, pulihkan ke kondisi asali setelah but berikutnya. + Hapus Permanen + Pulihkan image bawaan ROM (jika cadangan tersedia), umumnya dilakukan sebelum OTA; jika ingin menghapus KernelSU, gunakan fungsi \"Hapus Permanen\". + Pemasangan Berhasil + LKM dipilih: %s + Pasang + Pemasangan Gagal
\ No newline at end of file diff --git a/manager/app/src/main/res/values-it/strings.xml b/manager/app/src/main/res/values-it/strings.xml index f56068af520b..bf224c17fd25 100644 --- a/manager/app/src/main/res/values-it/strings.xml +++ b/manager/app/src/main/res/values-it/strings.xml @@ -5,14 +5,14 @@ Clicca per installare In esecuzione Versione: %d - Superuser: %d - Moduli: %d + Applicazioni con accesso root: %d + Moduli installati: %d Non supportato KernelSU ora supporta solo i kernel GKI Kernel Versione del manager - Fingerprint - Stato SELinux + Impronta + Stato di SELinux Disabilitato Enforcing Permissive @@ -21,14 +21,14 @@ Impossibile abilitare il modulo: %s Impossibile disabilitare il modulo: %s Nessun modulo installato - Moduli + Modulo Disinstalla Installa Installa Riavvia Impostazioni - Riavvio veloce - Riavvia in Recovery + Riavvio rapido + Riavvia in modalità Recovery Riavvia in modalità Bootloader Riavvia in modalità Download Riavvia in modalità EDL @@ -38,7 +38,7 @@ Impossibile disinstallare: %s Versione Autore - overlayfs non è disponibile, il modulo non può funzionare! + overlayfs non è disponibile, i moduli non possono funzionare! Ricarica Mostra app di sistema Nascondi app di sistema @@ -53,17 +53,17 @@ KernelSU è, e sempre sarà, gratuito e open source. Puoi comunque mostrarci il tuo apprezzamento facendo una donazione. Unisciti al nostro canale %2$s]]> Nome profilo - Namespace di mount + Spazio dei nomi del mount Globale Gruppi Ereditato Individuale Predefinito Personalizzato - Template + Modello Scollega moduli Contesto SELinux - Aggiornamento Profilo per %s fallito + Aggiornamento App Profile per %s fallito Aggiorna Apri Capacità @@ -77,33 +77,57 @@ Aggiornamento regole SELinux per %s fallito Attivando questa opzione permetterai a KernelSU di ripristinare ogni file modificato dai moduli per questa app. Dominio - Il valore predefinito per \"Scollega moduli\" in Profili App. Se attivato, rimuoverà tutte le modifiche al sistema da parte dei moduli per le applicazioni che non hanno un profilo impostato. + Il valore predefinito per \"Scollega moduli\" in App Profile. Se attivato, rimuoverà tutte le modifiche al sistema da parte dei moduli per le applicazioni che non hanno un profilo impostato. La versione attualmente installata di KernelSU (%d) è troppo vecchia ed il gestore non può funzionare correttamente. Si prega di aggiornare alla versione %d o successiva! Registro aggiornamenti Crea modello - Modifica Modello + Modifica modello identificativo Identificativo modello non valido Nome Visualizza modello Sola lettura - Esiste già l\'identificativo del modello! + L\'identificativo del modello esiste già! Importa/Esporta Importa dagli appunti Esporta negli appunti - Impossibile trovare profilo locale da esportare! + Impossibile trovare modello locale da esportare! Importato con successo Sincronizza i modelli remoti Gli appunti sono vuoti! Impossibile ottenere l\'accesso root! - Modelli Profili App - Gestisci i modelli locali e remoti dei Profili App + Modelli App Profile + Gestisci i modelli locali e remoti di App Profile Elimina Descrizione Salva - Impossibile salvare profilo + Impossibile salvare il modello Apri Impossibile reperire il changelog: %s Controlla aggiornamenti Controlla automaticamente la disponibilità di aggiornamenti all\'apertura dell\'applicazione + Abilita il Debug di WebView + Può essere usato per svolgere il debug di WebUI, è consigliato attivarlo solo quando necessario. + È consigliato usare immagine della partizione %1$s + Scegli il KMI + Avanti + Installazione diretta (Raccomandata) + Scegli un File + Installa nello Slot Inattivo (Dopo OTA) + Il tuo dispositivo sarà **FORZATO** ad avviarsi nello slot inattivo dopo il riavvio! +\nUsa questa opzione solo quando l\'applicazione dell\'aggiornamento OTA è terminata. +\nProcedere? + Riduci la dimensione dell\'immagine moduli sparse al minimo + Riduci la dimensione dell\'immagine sparse dei moduli alla sua reale dimenzione. Nota che questo potrebbe causare malfunzionamenti dei moduli quindi utilizzala solo quando necessario (ad esempio in caso di backup) + Disinstalla + Disinstalla Temporaneamente + Disinstalla Permanentemente + Ripristina immagine originale del produttore + Disinstalla temporaneamente KernelSU, ripristina lo stato originale dopo il prossimo riavvio. + Disinstalla KernelSU (Root e tutti i moduli) completamente e permanentemente. + Installazione + Installazione completata + Installazione fallita + LKM selezionato: %s + Ripristina l\'immagine di fabbrica del produttore (se il backup è presente), solitamente usato prima di applicare l\'OTA; se devi disinstallare KernelSU, utilizza invece \"Disinstalla Permanentemente\". \ No newline at end of file diff --git a/manager/app/src/main/res/values-ja/strings.xml b/manager/app/src/main/res/values-ja/strings.xml index 8deab9d6be65..f1c5c401cd34 100644 --- a/manager/app/src/main/res/values-ja/strings.xml +++ b/manager/app/src/main/res/values-ja/strings.xml @@ -109,4 +109,26 @@ 開く WebView デバッグを有効にする WebUI のデバッグに使用できます。必要な場合にのみ有効にしてください。 + %1$s パーティション イメージが推奨されます + KMI を選択してください + 次に + 非アクティブなスロットにインストール (OTA 後) + 再起動後、デバイスは**強制的に**、現在非アクティブなスロットから起動します。 +\nこのオプションは、OTA が完了した後にのみ使用してください。 +\n続く? + 直接インストール (推奨) + ファイルを選択してください + まばらな画像を最小限に抑える + モジュールが配置されているスパース イメージのサイズを実際のサイズに変更します。 モジュールが正常に動作しなくなる可能性がありますので、必要な場合(バックアップ等)にのみご使用ください + 完全にアンインストールする + ストック图像を復元 + 一時的にアンインストールする + アンインストール + KernelSU を一時的にアンインストールし、次回の再起動後に元の状態に戻します。 + KernelSU (ルートおよびすべてのモジュール) を完全かつ永久にアンインストールします。 + 通常、OTA の前に使用される工場出荷時のイメージを復元します (バックアップが存在する場合)。 KernelSUをアンインストールする必要がある場合は、「永久アンインストール」を使用してください。 + フラッシュ + フラッシュ成功 + フラッシュ失敗 + 選択された lkm: %s \ No newline at end of file diff --git a/manager/app/src/main/res/values-night-v27/themes.xml b/manager/app/src/main/res/values-night-v27/themes.xml new file mode 100644 index 000000000000..10a773b16f28 --- /dev/null +++ b/manager/app/src/main/res/values-night-v27/themes.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/manager/app/src/main/res/values-night/themes.xml b/manager/app/src/main/res/values-night/themes.xml new file mode 100644 index 000000000000..91abf657bfdc --- /dev/null +++ b/manager/app/src/main/res/values-night/themes.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/manager/app/src/main/res/values-nl/strings.xml b/manager/app/src/main/res/values-nl/strings.xml index 71d89e02e9cd..8e82e2af05b4 100644 --- a/manager/app/src/main/res/values-nl/strings.xml +++ b/manager/app/src/main/res/values-nl/strings.xml @@ -108,4 +108,15 @@ Succesvol geïmporteerd Open Controleer automatisch op updates bij het openen van de app + Directe installatie (aanbevolen) + Selecteer een bestand + Kan geen root verlenen! + Minimaliseer schaarse afbeeldingen + Wijzig het formaat van de beperkte afbeelding waar de module zich bevindt naar de werkelijke grootte. Houd er rekening mee dat dit ervoor kan zorgen dat de module abnormaal werkt, dus gebruik deze alleen wanneer dit nodig is (zoals voor back-up) + %1$s partitie-image wordt aanbevolen + Naast + Uw apparaat wordt **GEFORCEERD** om na een herstart op te starten naar het huidige inactieve slot! +\nGebruik deze optie alleen nadat OTA is voltooid. +\nDoorgaan? + Installeren in inactief slot (na OTA) \ No newline at end of file diff --git a/manager/app/src/main/res/values-pl/strings.xml b/manager/app/src/main/res/values-pl/strings.xml index 30e531f970ed..a1a17d8fa7c5 100644 --- a/manager/app/src/main/res/values-pl/strings.xml +++ b/manager/app/src/main/res/values-pl/strings.xml @@ -6,8 +6,8 @@ Kliknij, aby zainstalować Działa Wersja: %d - Superużytkowników: %d - Modułów: %d + Superużytkownicy: %d + Moduły: %d Nieobsługiwany KernelSU obsługuje obecnie tylko jądra GKI Jądro @@ -16,7 +16,7 @@ Status SELinux Wyłączony Enforcing - Permissive + Dozwolony Nieznany Superużytkownik Nie udało się włączyć modułu: %s @@ -43,13 +43,13 @@ Odśwież Pokaż aplikacje systemowe Ukryj aplikacje systemowe - Wyślij log + Raportuj log Tryb bezpieczny Uruchom ponownie, aby zastosować zmiany Moduły są wyłączone, ponieważ są w konflikcie z modułami Magiska! Poznaj KernelSU https://kernelsu.org/guide/what-is-kernelsu.html - Dowiedz się jak zainstalować KernelSU i jak korzystać z modułów. + Dowiedz się jak zainstalować KernelSU i jak korzystać z modułów Wesprzyj nas KernelSU jest i zawsze będzie darmowy oraz otwarty. Niemniej jednak możesz nam pokazać, że Ci zależy, wysyłając darowiznę. Dołącz do kanału %2$s]]> @@ -68,18 +68,68 @@ Odmontuj moduły Nie udało się zaktualizować profilu aplikacji dla %s Domyślnie odmontuj moduły - Globalna wartość domyślna opcji \"Odmontuj moduły\" w profilach aplikacji. Jeśli jest włączona, odwraca wszystkie modyfikacje dokonane przez moduły dla aplikacji, które nie mają ustawionego profilu. + Globalna wartość domyślna opcji \"Odmontuj moduły\" w profilach aplikacji. Jeśli jest włączona, wycofuje wszystkie modyfikacje dokonane przez moduły dla aplikacji, które nie mają ustawionego profilu. Włączenie tej opcji umożliwi KernelSU przywrócenie wszelkich zmodyfikowanych plików przez moduły dla tej aplikacji. Domena Reguły Zaktualizuj Pobieranie modułu: %s Rozpocznij pobieranie: %s - Nowa wersja: %s jest dostępna, kliknij, aby zaktualizować + Nowa wersja: %s jest dostępna. Kliknij, aby zaktualizować Uruchom Wymuś zatrzymanie Restartuj Nie udało się zaktualizować reguł SELinux dla: %s Obecna wersja KernelSU %d jest zbyt stara, aby menedżer działał poprawnie. Prosimy o aktualizację do wersji %d lub nowszej! Dziennik zmian + Włącz debugowanie WebView + Może być użyte do debugowania WebUI. Włącz tylko w razie potrzeby. + Obraz partycji %1$s jest zalecany + Wybierz KMI + Dalej + Instalacja bezpośrednia (zalecane) + Wybierz plik + Zainstaluj do nieaktywnego slotu (po aktualizcji OTA) + Po ponownym uruchomieniu Twoje urządzenie zostanie **ZMUSZONE** do uruchomia się z obecnie nieaktywnego slotu! +\nUżyj tej opcji dopiero po zakończeniu aktualizacji OTA. +\nCzy chcesz kontynuować? + Stwórz szablon + Edytuj szablon + Nazwa + Opis + Zapisz + Usuń + tylko do odczytu + Importuj/Eksportuj + Importuj ze schowka + Eksportuj do schowka + Nie można znaleźć lokalnego szablonu do eksportu! + Zaimportowano pomyślnie + Nie udało się zapisać szablonu + Schowek jest pusty! + Zarządzaj lokalnym i internetowym szablonem profilu aplikacji + Synchronizuj internetowe szablony + Zobacz szablon + Błędny identyfikator szablonu + Szablon profilu aplikacji + Identyfikator + Szablon o takim identyfikatorze już istnieje! + Nie udało się przyznać roota! + Otwórz + Pobranie dziennika zmian nie powiodło się: %s + Wyszukaj aktualizacje + Wyszukuj aktualizacje automatycznie przy otwieraniu aplikacji + Minimalizuj rozrzedzony (sparse) obraz + Zmienia rozmiar obrazu rozrzedzonego(sparse), w którym znajduje się moduł, do jego rzeczywistego rozmiaru. Należy pamiętać, że może to spowodować nieprawidłowe działanie modułu, więc należy go używać tylko wtedy, gdy jest to konieczne (np. do tworzenia kopii zapasowych) + Odinstaluj zupełnie + Przywróć obraz fabryczny + Odinstaluj tymczasowo + Odinstaluj + Tymczasowo odinstaluj KernelSU, przywróć do oryginalnego stanu po następnym ponownym uruchomieniu. + Całkowite i trwałe odinstalowanie KernelSU(Root i wszystkich modułów). + Przywróć obraz fabryczny (jeśli istnieje kopia zapasowa), zwykle używany przed OTA; jeśli chcesz odinstalować KernelSU, użyj opcji \"Odinstaluj zupełnie\". + Flashowanie + Flashowanie ukończone pomyślnie + Flashowanie nieudane + Wybrano lkm: %s \ No newline at end of file diff --git a/manager/app/src/main/res/values-pt-rBR/strings.xml b/manager/app/src/main/res/values-pt-rBR/strings.xml index 79c91c39710e..2445bd50f5a7 100644 --- a/manager/app/src/main/res/values-pt-rBR/strings.xml +++ b/manager/app/src/main/res/values-pt-rBR/strings.xml @@ -73,7 +73,7 @@ Regras Atualizar Baixando módulo %s - Iniciando download: %s + Começando a baixar %s Nova versão %s está disponível, clique para atualizar. Iniciar Forçar parada @@ -112,8 +112,23 @@ Selecione um arquivo Instalação direta (recomendada) Instalar no slot inativo (após o OTA) - Seu dispositivo será FORÇADO a inicializar no slot inativo atual após uma reinicialização! + Seu dispositivo será **FORÇADO** a inicializar no slot inativo atual após uma reinicialização! \nSó use esta opção após a conclusão do OTA. \nDeseja continuar? Próximo + A imagem da partição %1$s é recomendada + Selecionar KMI + Minimizar imagem esparsa + Redimensione a imagem esparsa onde o módulo está localizado para seu tamanho real. Observe que isso pode fazer com que o módulo funcione de forma anormal, portanto, use-o somente quando necessário (como um backup). + Desinstalar + Desinstalar temporariamente + Desinstalar permanentemente + Restaurar imagem de fábrica + Restaure a imagem de fábrica (se existir um backup), geralmente usada antes do OTA. Se você precisar desinstalar o KernelSU, use \"Desinstalar permanentemente\". + Desinstale temporariamente o KernelSU e restaure ao estado original após a próxima reinicialização + Desinstale o KernelSU (Root e todos os módulos) completamente e permanentemente + LKM selecionado: %s + Flash falhou + Flashando + Flash bem-sucedido \ No newline at end of file diff --git a/manager/app/src/main/res/values-ro/strings.xml b/manager/app/src/main/res/values-ro/strings.xml index 4cd6c428c3fb..933108910714 100644 --- a/manager/app/src/main/res/values-ro/strings.xml +++ b/manager/app/src/main/res/values-ro/strings.xml @@ -108,4 +108,21 @@ Poate fi folosit pentru a depana WebUI, activează numai când este necesar. Nu s-a acordat acces root! Deschide + Se recomandă imaginea partiției %1$s + Înainte + Dispozitivul va fi **FORȚAT** să pornească în slot-ul inactiv curent după o repornire! +\nFolosește această opțiune numai după finisarea OTA. +\nContinui? + Selectează KMI + Instalare directă (recomandat) + Selectează un fișier + Instalează într-un slot inactiv (după OTA) + Dezinstalează + Restaurare imagine stoc + Dezinstalează temporar KernelSU, se revine la starea inițială după următoarea repornire. + Lkm selectat: %s + Dezinstalează temporar + Dezinstalează definitiv + Dezinstalare KernelSU (Root și toate modulele) complet și permanent. + Restaurează imaginea stoc din fabrică (dacă există o copie de rezervă), utilizată de obicei înainte de OTA; dacă trebuie să dezinstalezi KernelSU, utilizează „Dezinstalare permanentă”. \ No newline at end of file diff --git a/manager/app/src/main/res/values-ru/strings.xml b/manager/app/src/main/res/values-ru/strings.xml index 5e02165b9744..b22f275919cc 100644 --- a/manager/app/src/main/res/values-ru/strings.xml +++ b/manager/app/src/main/res/values-ru/strings.xml @@ -111,12 +111,27 @@ Не удалось выдать root! Открыть Включить отладку WebView - Используется для отладки веб-интерфейса. Пожалуйста, включайте только при необходимости. + Используется для отладки WebUI. Пожалуйста, включайте только при необходимости. Прямая установка (Рекомендуется) Установка в неактивный слот (После OTA) Далее Выбрать файл Ваше устройство будет **ПРИНУДИТЕЛЬНО** загружено в текущий неактивный слот после перезагрузки! \n Используйте эту опцию только после завершения OTA. -\n Продолжать? +\n Продолжить? + Выбрать KMI + %1$s образ раздела рекомендуется + Минимизировать разреженный образ + Изменить размер разреженного образа в котором находятся модули, до его фактического размера. Обратите внимание, что это может вызвать ненормальную работу модулей, поэтому используйте это только при необходимости (например, для резервного копирования) + Удалить Временно + Удалить KernelSU (Root и все модули) полностью и навсегда. + Удалить Навсегда + Временно удалить KernelSU, восстановить исходное состояние после следующей перезагрузки. + Удалить + Восстановить Сток образ + Восстановить стоковый заводской образ (если существует резервная копия), обычно используется перед OTA; если вам нужно удалить KernelSU, используйте «Удалить Навсегда». + Прошивка выполнена + Прошивка + Прошивка не выполнена + Выбран lkm: %s \ No newline at end of file diff --git a/manager/app/src/main/res/values-th/strings.xml b/manager/app/src/main/res/values-th/strings.xml index e009f94201a6..598cf4011dbc 100644 --- a/manager/app/src/main/res/values-th/strings.xml +++ b/manager/app/src/main/res/values-th/strings.xml @@ -58,7 +58,7 @@ ชื่อโปรไฟล์ Mount เนมสเปซ ทั่วไป - การสืบทอด + สืบทอด ส่วนบุคคล หมวดหมู่ ความสามารถของแอป @@ -106,4 +106,28 @@ ไม่สามารถให้สิทธิ์รูทได้! ตรวจสอบการอัปเดต ตรวจสอบการอัปเดตโดยอัตโนมัติเมื่อเปิดแอป + เปิดใช้งานการแก้ไขข้อบกพร่อง WebView + เลือก KMI + ต่อไป + เลือกไฟล์ + ติดตั้งลงในสล็อตที่ไม่ใช้งาน (หลังจาก OTA) + ติดตั้งโดยตรง (แนะนำ) + แนะนำให้ใช้อิมเมจพาร์ติชั่น %1$s + สามารถใช้เพื่อดีบัก WebUI โปรดเปิดใช้งานเมื่อจำเป็นเท่านั้น + อุปกรณ์ของคุณจะถูก **บังคับ** ให้บูตไปยังสล็อตที่ไม่ได้ใช้งานหลังจากรีบูต! +\nโปรดใช้ตัวเลือกนี้หลังจาก OTA เสร็จแล้วเท่านั้น +\nดำเนินการต่อหรือไม่? + ลดความกระจายของอิมเมจ + ปรับขนาดความกระจายของอิมเมจในโมดูลให้เป็นขนาดจริง โปรดทราบว่านี่อาจทำให้โมดูลทำงานผิดปกติ โปรดใช้เมื่อจำเป็นเท่านั้น (เช่น การสำรองข้อมูล) + ถอนการติดตั้งถาวร + เรียกคืนอิมเมจดั้งเดิม + ถอนการติดตั้ง KernelSU ชั่วคราว จะคืนค่าเป็นสถานะดั้งเดิมหลังจากรีบูตในครั้งถัดไป + กำลังแฟลช + แฟลชสำเร็จ + แฟลชล้มเหลว + เลือก lkm: %s + ถอนการติดตั้ง + ถอนการติดตั้งชั่วคราว + การถอนการติดตั้ง KernelSU (การรูทและโมดูลทั้งหมด) อย่างสมบูรณ์โดยถาวร + คืนค่าโรงงานอิมเมจดั้งเดิม (หากมีข้อมูลสำรอง) ส่วนใหญ่มักใช้ก่อนทำการ OTA ซึ่งหากคุณต้องการถอนการติดตั้ง KernelSU โปรดใช้ \"ถอนการติดตั้งถาวร\" \ No newline at end of file diff --git a/manager/app/src/main/res/values-tr/strings.xml b/manager/app/src/main/res/values-tr/strings.xml index 6e2e19a957bb..47f7e0028a80 100644 --- a/manager/app/src/main/res/values-tr/strings.xml +++ b/manager/app/src/main/res/values-tr/strings.xml @@ -46,13 +46,13 @@ Günlük raporu gönder Güvenli mod Değişikliklerin uygulanması için cihazı yeniden başlat - Modüller, Magisk ile çakıştığı için devre dışı bırakıldı! + Modüller, Magisk\'tekiler ile çakıştığı için devre dışı bırakıldı! KernelSU\'yu öğrenin https://kernelsu.org/guide/what-is-kernelsu.html KernelSU\'nun nasıl kurulacağını ve modüllerin nasıl kullanılacağını öğrenin Bizi destekleyin KernelSU ücretsiz ve açık kaynaklı bir yazılımdır ve her zaman öyle kalacaktır. Ancak bağış yaparak bize destek olduğunuzu gösterebilirsiniz. - %1$s adresinde kaynak kodunu görüntüleyin
%2$s kanalımıza katılın
+ %1$s adresinde kaynak kodunu görüntüleyin.
%2$s kanalımıza katılın.
Uygulama profili Varsayılan Şablon @@ -67,7 +67,7 @@ SELinux içeriği Modüllerin bağlantısını kes %s için uygulama profili güncellenemedi. - Mevcut KernelSU sürümü %d, yöneticinin düzgün çalışabilmesi için çok düşük. Lütfen %d veya daha yüksek bir sürüme güncelleyin! + Mevcut KernelSU sürümü %d, yöneticinin düzgün çalışabilmesi için çok düşük. Lütfen %d sürümüne veya daha yüksek bir sürüme güncelleyin! Varsayılan olarak modüllerin bağlantısını kes Uygulama profilindeki \"Modüllerin bağlantısını kes\" seçeneği için varsayılan değer. Etkinleştirilirse, profil ayarı yapılmamış uygulamalar için modüllerin sistemde yaptığı tüm değişiklikler kaldırılacaktır. Bu seçeneği etkinleştirmek, KernelSU\'nun bu uygulama için modüller tarafından değiştirilen dosyaları geri yüklemesine izin verir. @@ -93,7 +93,7 @@ Kaydet Sil Şablonu görüntüle - Salt okunur + salt okunur Şablon kimliği zaten mevcut! İçe aktar/Dışa aktar Panodan içe aktar @@ -113,8 +113,23 @@ Doğrudan Kur (Tavsiye Edilen) Bir Dosya Seçin Etkin Olmayan Yuvaya Kur (OTA\'dan Sonra) - Aygıtınız yeniden başlatıldıktan sonra geçerli etkin olmayan yuvaya **ZORLA** önyükleme yapılacaktır! + Cihazınız geçerli etkin olmayan yuvaya **ZORLA** yeniden başlatılacaktır! \nBu seçeneği yalnızca OTA tamamlandıktan sonra kullanın. \nDevam edilsin mi? Sonraki + KMI seçin + %1$s bölüm imajı önerilir + Sparse imajını küçültün + Modülün bulunduğu sparse imajını gerçek boyutuna yeniden boyutlandırın. Bunun modülün anormal çalışmasına neden olabileceğini unutmayın, bu nedenle lütfen yalnızca gerekli olduğunda kullanın (yedekleme gibi) + Geçici Olarak Kaldır + Kalıcı Olarak Kaldır + Stok İmajı Geri Yükle + KernelSU\'yu geçici olarak kaldır, bir sonraki yeniden başlatmadan sonra orijinal durumuna geri yükle. + Kaldır + KernelSU (Root ve tüm modüller) tamamen ve kalıcı olarak kaldırılıyor. + Stok fabrika imajını geri yükler (eğer yedek varsa), genellikle OTA\'dan önce kullanılır; KernelSU\'yu kaldırmanız gerekiyorsa, lütfen \"Kalıcı Olarak Kaldır\" seçeneğini kullanın. + Flaşlama başarılı + Seçili lkm: %s + Flaşlanıyor + Flaşlama başarısız \ No newline at end of file diff --git a/manager/app/src/main/res/values-uk/strings.xml b/manager/app/src/main/res/values-uk/strings.xml index 10b0533bc1cd..f1a1ea11e539 100644 --- a/manager/app/src/main/res/values-uk/strings.xml +++ b/manager/app/src/main/res/values-uk/strings.xml @@ -103,4 +103,32 @@ Видалити Буфер обміну пустий! Переглянути шаблон + Увімкнути налагодження WebView + Виберіть KMI + Далі + Перевірка оновлень + Автоматична перевірка оновлень під час відкриття програми + Використовується для налагодження WebUI. Будь ласка, вмикайте тільки за потреби. + Пряме встановлення (рекомендовано) + Виберіть файл + Встановлення в неактивний слот (Після OTA) + Ваш пристрій буде **ПРИМУСОВО** завантажено в поточний неактивний слот після перезавантаження! +\n Використовуйте цю опцію тільки після завершення OTA. +\n Продовжити? + %1$s образ розділу рекомендується + Не вдалося отримати root! + Відкрити + Мінімізувати розріджений образ + Змінити розмір розрідженого образу, в якому знаходяться модулі, до його фактичного розміру. Зверніть увагу, що це може спричинити ненормальну роботу модулів, тому використовуйте це лише за потреби (наприклад, для резервного копіювання) + Тимчасово видалити + Назавжди видалити + Відновити Стоковий образ + Тимчасово видалити KernelSU, відновити початковий стан після наступного перезавантаження. + Видалити + Прошивка + Прошивку виконано + Прошивка не виконана + Обрано lkm: %s + Видалити KernelSU (Root і всі модулі) повністю і назавжди. + Відновити стоковий заводський образ (якщо є резервна копія), зазвичай використовується перед OTA; якщо вам потрібно видалити KernelSU, використовуйте \"Назавжди видалити\". \ No newline at end of file diff --git a/manager/app/src/main/res/values-v27/themes.xml b/manager/app/src/main/res/values-v27/themes.xml new file mode 100644 index 000000000000..325416c0d9c2 --- /dev/null +++ b/manager/app/src/main/res/values-v27/themes.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/manager/app/src/main/res/values-vi/strings.xml b/manager/app/src/main/res/values-vi/strings.xml index 51a4e3b38c0d..7cf1490299a7 100644 --- a/manager/app/src/main/res/values-vi/strings.xml +++ b/manager/app/src/main/res/values-vi/strings.xml @@ -109,4 +109,26 @@ Kiểm tra cập nhật Tự động kiểm tra cập nhật khi mở ứng dụng Mở + Cài đặt vào khe không hoạt động (Sau OTA) + Thiết bị của bạn sẽ **BẮT BUỘC** khởi động vào khe không hoạt động hiện tại sau khi khởi động lại! +\nChỉ sử dụng tùy chọn này sau khi OTA hoàn tất. +\nTiếp tục? + Tạm thời gỡ cài đặt KernelSU, khôi phục về trạng thái ban đầu sau lần khởi động lại tiếp theo. + Chọn KMI + Kế tiếp + Cài đặt trực tiếp (Được khuyến nghị) + Chọn một tệp + Gỡ cài đặt + Gỡ cài đặt tạm thời + Gỡ cài đặt vĩnh viễn + Khôi phục hình ảnh gốc + Gỡ cài đặt KernelSU (Root và tất cả các mô-đun) hoàn toàn và vĩnh viễn. + Khôi phục hình ảnh gốc của nhà máy (nếu có bản sao lưu), thường được sử dụng trước OTA; nếu bạn cần gỡ cài đặt KernelSU, vui lòng sử dụng \"Gỡ cài đặt vĩnh viễn\". + Đang cài + Cài thành công + Cài thất bại + Đã chọn lkm: %s + Nên sử dụng hình ảnh phân vùng %1$s + Giảm thiểu hình ảnh thưa thớt + Thay đổi kích thước hình ảnh thưa nơi đặt mô-đun theo kích thước thực tế của nó. Lưu ý điều này có thể khiến module hoạt động không bình thường nên vui lòng chỉ sử dụng khi cần thiết (chẳng hạn như để sao lưu) \ No newline at end of file diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml index 388ed0c29ecd..ba93a10fa871 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -114,4 +114,18 @@ 将在重启后强制切换到另一个槽位!\n注意只能在 OTA 更新完成后的重启之前使用。\n确认? 下一步 建议选择 %1$s 分区镜像 + 选择 KMI + 最小化稀疏文件 + 将模块所在的稀疏文件镜像调整为其实际大小,注意这可能导致模块工作异常,请仅在必要时(如备份)使用 + 卸载 + 临时卸载 + 永久卸载 + 恢复原厂镜像 + 临时卸载 KernelSU,下次重启后恢复 + 完全并永久移除 KernelSU 和所有模块 + 恢复原厂镜像,一般在 OTA 前使用;如需卸载请使用“永久卸载” + 刷写中 + 刷写完成 + 刷写失败 + 选择的 LKM :%s \ No newline at end of file diff --git a/manager/app/src/main/res/values-zh-rHK/strings.xml b/manager/app/src/main/res/values-zh-rHK/strings.xml index 1958397b9cfc..d92ff99cabc4 100644 --- a/manager/app/src/main/res/values-zh-rHK/strings.xml +++ b/manager/app/src/main/res/values-zh-rHK/strings.xml @@ -102,4 +102,22 @@ 刪除 剪貼簿沒有內容! 查看模板 + 啟用 WebView 偵錯 + 可用於偵錯WebUI,請僅在需要時啟用。 + 直接安裝(建議) + 選擇一個文件 + 安裝到非活動插槽(OTA 後) + 重新啟動後,您的裝置將強制啟動到目前非活動插槽! +\n僅在 OTA 完成後使用此選項。 +\n繼續? + 下一個 + 選擇KMI + 建議使用 %1$s 分割區映像 + 授予root權限失敗! + 打開 + 檢查更新 + 開啟應用程式時自動檢查更新 + 最小化稀疏影像 + 將模組所在的稀疏影像調整為實際大小。 請注意,這可能會導致模組工作異常,因此請僅在必要時使用(例如備份) + 解除安裝 \ No newline at end of file diff --git a/manager/app/src/main/res/values-zh-rTW/strings.xml b/manager/app/src/main/res/values-zh-rTW/strings.xml index 609e2ba3818d..eb1a7f186193 100644 --- a/manager/app/src/main/res/values-zh-rTW/strings.xml +++ b/manager/app/src/main/res/values-zh-rTW/strings.xml @@ -115,4 +115,19 @@ \n請問是否繼續? 直接安裝(建議) 下一步 + 選擇 KMI + 建議使用 %1$s 分區 + 最小化模組稀疏映像 + 將模組的稀疏映像調整為其實際大小,請注意這可能導致模組工作異常,請僅在需要時(如備份)使用 + 解除安裝 + 暫時解除安裝 + 還原原廠映像 + 暫時卸載KernelSU,下次重啟後恢復原狀。 + 永久解除安裝 + 完全的(永久的)解除安裝 KernelSU(Root 和所有模組)。 + 寫入中 + 寫入完成 + 恢復原廠映像(如果有備份),通常在OTA之前使用;如果需要解除安裝KernelSU,請使用「永久解除安裝」。 + 寫入失敗 + 選擇的 LKM :%s \ No newline at end of file diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index e7533f44fb1b..7202b4e22c34 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -116,4 +116,18 @@ Your device will be **FORCED** to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue? Next %1$s partition image is recommended - + Select KMI + Minimize sparse image + Resize the sparse image where the module is located to its actual size. Note that this may cause the module to work abnormally, so please only use when necessary (such as for backup) + Uninstall + Uninstall Temporarily + Uninstall Permanently + Restore Stock Image + Temporarily uninstall KernelSU, restore to original state after next reboot. + Uninstalling KernelSU(Root and all modules) completely and permanently. + Restore the stock factory image (if a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Permanent Uninstall\". + Flashing + Flash success + Flash failed + Selected lkm: %s + \ No newline at end of file diff --git a/manager/app/src/main/res/values/themes.xml b/manager/app/src/main/res/values/themes.xml index 325416c0d9c2..7d41d8ec77fc 100644 --- a/manager/app/src/main/res/values/themes.xml +++ b/manager/app/src/main/res/values/themes.xml @@ -1,7 +1,10 @@ - \ No newline at end of file diff --git a/manager/build.gradle.kts b/manager/build.gradle.kts index 01fafd81aa4b..d2c372b36c3f 100644 --- a/manager/build.gradle.kts +++ b/manager/build.gradle.kts @@ -33,19 +33,15 @@ cmaker { } val androidMinSdkVersion = 26 -val androidTargetSdkVersion = 33 +val androidTargetSdkVersion = 34 val androidCompileSdkVersion = 34 val androidBuildToolsVersion = "34.0.0" -val androidCompileNdkVersion = "25.2.9519653" -val androidSourceCompatibility = JavaVersion.VERSION_17 -val androidTargetCompatibility = JavaVersion.VERSION_17 +val androidCompileNdkVersion = "26.3.11579264" +val androidSourceCompatibility = JavaVersion.VERSION_21 +val androidTargetCompatibility = JavaVersion.VERSION_21 val managerVersionCode by extra(getVersionCode()) val managerVersionName by extra(getVersionName()) -tasks.register("clean") { - delete(rootProject.buildDir) -} - fun getGitCommitCount(): Int { val out = ByteArrayOutputStream() exec { @@ -101,4 +97,4 @@ subprojects { } } } -} +} \ No newline at end of file diff --git a/manager/gradle/libs.versions.toml b/manager/gradle/libs.versions.toml index e01f75f33c0a..d031942affd8 100644 --- a/manager/gradle/libs.versions.toml +++ b/manager/gradle/libs.versions.toml @@ -1,27 +1,39 @@ [versions] -agp = "8.3.0" -kotlin = "1.8.10" -ksp = "1.8.10-1.0.9" -compose-bom = "2023.06.01" -lifecycle = "2.6.1" -accompanist = "0.30.1" -navigation = "2.6.0" -compose-destination = "1.9.42-beta" -libsu = "5.2.1" -sheets-compose-dialogs = "1.2.0" +agp = "8.3.2" +kotlin = "1.9.23" +ksp = "1.9.23-1.0.20" +compose-compiler = "1.5.11" +compose-bom = "2024.05.00" +lifecycle = "2.7.0" +accompanist = "0.34.0" +navigation = "2.7.7" +activity-compose = "1.9.0" +kotlinx-coroutines = "1.8.0" +coil-compose = "2.6.0" +compose-destination = "1.10.2" +sheets-compose-dialogs = "1.3.0" markdown = "4.6.2" -webkit = "1.10.0" +webkit = "1.11.0" +appiconloader-coil = "1.5.0" +parcelablelist = "2.0.1" +libsu = "5.2.2" +apksign = "1.4" +cmaker = "1.2" [plugins] agp-app = { id = "com.android.application", version.ref = "agp" } agp-lib = { id = "com.android.library", version.ref = "agp" } + kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -lsplugin-apksign = { id = "org.lsposed.lsplugin.apksign", version = "1.1" } -lsplugin-cmaker = { id = "org.lsposed.lsplugin.cmaker", version = "1.1" } + +lsplugin-apksign = { id = "org.lsposed.lsplugin.apksign", version.ref = "apksign" } +lsplugin-cmaker = { id = "org.lsposed.lsplugin.cmaker", version.ref = "cmaker" } [libraries] -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version = "1.7.1" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" } + androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } @@ -38,6 +50,7 @@ androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lif androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" } + com-google-accompanist-drawablepainter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" } com-google-accompanist-navigation-animation = { group = "com.google.accompanist", name = "accompanist-navigation-animation", version.ref = "accompanist" } com-google-accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" } @@ -47,19 +60,19 @@ com-github-topjohnwu-libsu-core = { group = "com.github.topjohnwu.libsu", name = com-github-topjohnwu-libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" } com-github-topjohnwu-libsu-io= { group = "com.github.topjohnwu.libsu", name = "io", version.ref = "libsu" } -dev-rikka-rikkax-parcelablelist = { module = "dev.rikka.rikkax.parcelablelist:parcelablelist", version = "2.0.1" } +dev-rikka-rikkax-parcelablelist = { module = "dev.rikka.rikkax.parcelablelist:parcelablelist", version.ref = "parcelablelist" } -io-coil-kt-coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.3.0" } +io-coil-kt-coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil-compose" } -kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.7.1" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } -me-zhanghai-android-appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version = "1.5.0" } +me-zhanghai-android-appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version.ref = "appiconloader-coil" } compose-destinations-animations-core = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "compose-destination" } compose-destinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "compose-destination" } -sheet-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "core", version.ref = "sheets-compose-dialogs"} -sheet-compose-dialogs-list = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "list", version.ref = "sheets-compose-dialogs"} -sheet-compose-dialogs-input = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "input", version.ref = "sheets-compose-dialogs"} +sheet-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "core", version.ref = "sheets-compose-dialogs" } +sheet-compose-dialogs-list = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "list", version.ref = "sheets-compose-dialogs" } +sheet-compose-dialogs-input = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "input", version.ref = "sheets-compose-dialogs" } markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown" } diff --git a/manager/gradle/wrapper/gradle-wrapper.jar b/manager/gradle/wrapper/gradle-wrapper.jar index d64cd4917707..e6441136f3d4 100644 Binary files a/manager/gradle/wrapper/gradle-wrapper.jar and b/manager/gradle/wrapper/gradle-wrapper.jar differ diff --git a/manager/gradle/wrapper/gradle-wrapper.properties b/manager/gradle/wrapper/gradle-wrapper.properties index a80b22ce5cff..b82aa23a4f05 100644 --- a/manager/gradle/wrapper/gradle-wrapper.properties +++ b/manager/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/manager/settings.gradle.kts b/manager/settings.gradle.kts index 1a4947bfc73f..2230bf48671a 100644 --- a/manager/settings.gradle.kts +++ b/manager/settings.gradle.kts @@ -1,3 +1,5 @@ +@file:Suppress("UnstableApiUsage") + enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") pluginManagement { diff --git a/scripts/ksubot.py b/scripts/ksubot.py index e443a1906081..c3b3672772ed 100644 --- a/scripts/ksubot.py +++ b/scripts/ksubot.py @@ -82,7 +82,7 @@ async def main(): exit(1) print("[+] Logging in Telegram with bot") script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) - session_dir = os.path.join(script_dir, "ksubot.session") + session_dir = os.path.join(script_dir, "ksubot") async with await TelegramClient(session=session_dir, api_id=API_ID, api_hash=API_HASH).start(bot_token=BOT_TOKEN) as bot: caption = [""] * len(files) caption[-1] = get_caption() @@ -98,4 +98,4 @@ async def main(): try: asyncio.run(main()) except Exception as e: - print(f"[-] An error occurred: {e}") \ No newline at end of file + print(f"[-] An error occurred: {e}") diff --git a/userspace/ksud/Cargo.lock b/userspace/ksud/Cargo.lock index 927a0f03f478..87b219a09e48 100644 --- a/userspace/ksud/Cargo.lock +++ b/userspace/ksud/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -25,21 +25,20 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "aes" -version = "0.7.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if 1.0.0", "cipher", "cpufeatures", - "opaque-debug", ] [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -50,20 +49,26 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_log-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f0fc03f560e1aebde41c2398b691cb98b5ea5996a6184a7a67bbbb77448969" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" [[package]] name = "android_logger" -version = "0.13.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d70a974711fabdc7555de36765bbc237f093f5992d62cab3dcaa0b0bfc7d756" +checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" dependencies = [ "android_log-sys", - "env_logger", + "env_logger 0.10.2", "log", "once_cell", ] @@ -77,34 +82,91 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "arbitrary" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -129,36 +191,36 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bzip2" @@ -183,11 +245,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -204,89 +267,88 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] name = "clap" -version = "4.1.4" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ - "bitflags 1.3.2", + "clap_builder", "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", "clap_lex", - "is-terminal", - "once_cell", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.58", ] [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "colorchoice" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "const_format" -version = "0.2.30" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.29" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ "proc-macro2", "quote", @@ -301,35 +363,34 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "crossbeam" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" dependencies = [ - "cfg-if 1.0.0", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -339,9 +400,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -367,11 +428,10 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if 1.0.0", "crossbeam-utils", ] @@ -392,99 +452,114 @@ dependencies = [ ] [[package]] -name = "cxx" -version = "1.0.89" +name = "deflate64" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] +checksum = "83ace6c86376be0b6cdcf3fb41882e81d94b31587573d1cfa9d01cd06bba210d" [[package]] -name = "cxx-build" -version = "1.0.89" +name = "deranged" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.107", + "powerfmt", ] [[package]] -name = "cxxbridge-flags" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.89" +name = "derive-new" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.58", ] [[package]] -name = "derive-new" -version = "0.5.9" +name = "derive_arbitrary" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.58", ] [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "either" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", +] + [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ - "humantime", - "is-terminal", "log", "regex", - "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "env_filter", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.2.8" @@ -503,7 +578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -529,9 +604,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -545,9 +620,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -564,41 +639,32 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "heck" -version = "0.4.1" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] -name = "hermit-abi" -version = "0.2.6" +name = "hashbrown" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] -name = "hermit-abi" -version = "0.3.4" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hex" @@ -630,11 +696,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -646,41 +712,34 @@ dependencies = [ "libm", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] name = "include-flate" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfdcb449c721557c1cf89bbd3412bf33fa963289e26e9badbd824a960912e148" +checksum = "c2e11569346406931d20276cc460215ee2826e7cad43aa986999cb244dd7adb0" dependencies = [ "include-flate-codegen-exports", "lazy_static", @@ -697,7 +756,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -711,25 +770,22 @@ dependencies = [ ] [[package]] -name = "io-lifetimes" -version = "1.0.5" +name = "indexmap" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ - "libc", - "windows-sys 0.45.0", + "equivalent", + "hashbrown", ] [[package]] -name = "is-terminal" -version = "0.4.2" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes", - "rustix 0.36.7", - "windows-sys 0.42.0", + "generic-array", ] [[package]] @@ -743,9 +799,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "java-properties" @@ -760,18 +816,18 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -798,7 +854,7 @@ dependencies = [ "const_format", "derive-new", "encoding_rs", - "env_logger", + "env_logger 0.11.3", "extattr", "getopts", "hole-punch", @@ -814,13 +870,14 @@ dependencies = [ "regex", "retry", "rust-embed", - "rustix 0.38.30 (git+https://github.com/Kernel-SU/rustix.git?branch=main)", + "rustix 0.38.30", "serde", "serde_json", + "sha1", "sha256", "tempdir", "which", - "zip 0.6.4", + "zip 1.1.4", "zip-extensions", ] @@ -832,15 +889,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libflate" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05605ab2bce11bcfc0e9c635ff29ef8b2ea83f29be257ee7d730cac3ee373093" +checksum = "5ff4ae71b685bbad2f2f391fe74f6b7659a34871c08b210fdc039e43bee07d18" dependencies = [ "adler32", "crc32fast", @@ -849,33 +906,18 @@ dependencies = [ [[package]] name = "libflate_lz77" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a734c0493409afcd49deee13c006a04e3586b9761a03543c6272c9c51f2f5a" +checksum = "a52d3a8bfc85f250440e4424db7d857e241a3aebbbe301f3eb606ab15c39acbf" dependencies = [ "rle-decode-fast", ] [[package]] name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linux-raw-sys" @@ -885,9 +927,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "loopdev" @@ -900,9 +942,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap" @@ -922,9 +964,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -940,60 +982,55 @@ dependencies = [ ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", - "num-traits", ] [[package]] -name = "num-traits" -version = "0.2.15" +name = "num_enum" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ - "autocfg", + "num_enum_derive", ] [[package]] -name = "num_cpus" -version = "1.16.0" +name = "num_enum_derive" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "hermit-abi 0.3.4", - "libc", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] name = "object" -version = "0.30.4" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "os_str_bytes" -version = "6.4.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "password-hash" @@ -1020,15 +1057,21 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" @@ -1037,27 +1080,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro-crate" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.107", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "toml_edit", ] [[package]] @@ -1068,9 +1096,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -1081,13 +1109,13 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "chrono", "flate2", "hex", "lazy_static", "procfs-core", - "rustix 0.38.30 (registry+https://github.com/rust-lang/crates.io-index)", + "rustix 0.38.32", ] [[package]] @@ -1096,16 +1124,16 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "chrono", "hex", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1170,9 +1198,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -1180,14 +1208,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.2" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -1201,9 +1227,21 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -1212,9 +1250,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "remove_dir_all" @@ -1242,9 +1280,9 @@ checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" [[package]] name = "rust-embed" -version = "6.4.2" +version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1" +checksum = "fb78f46d0066053d16d4ca7b898e9343bc3530f71c61d5ad84cd404ada068745" dependencies = [ "include-flate", "rust-embed-impl", @@ -1254,22 +1292,22 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "6.3.1" +version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d" +checksum = "b91ac2a3c6c0520a3fb3dd89321177c3c692937c4eb21893378219da10c44fc8" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 1.0.107", + "syn 2.0.58", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "7.3.0" +version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" +checksum = "86f69089032567ffff4eada41c573fc43ff466c7db7c5688b2e7969584345581" dependencies = [ "sha2", "walkdir", @@ -1281,52 +1319,38 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustix" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" -dependencies = [ - "bitflags 1.3.2", - "errno 0.2.8", - "io-lifetimes", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.42.0", -] - [[package]] name = "rustix" version = "0.38.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +source = "git+https://github.com/Kernel-SU/rustix.git?branch=main#0e270bce2d97466be6b987bb5f7ea5b1e8d84969" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno 0.3.8", + "itoa", "libc", - "linux-raw-sys 0.4.13", - "windows-sys 0.52.0", + "linux-raw-sys", + "once_cell", + "windows-sys", ] [[package]] name = "rustix" -version = "0.38.30" -source = "git+https://github.com/Kernel-SU/rustix.git?branch=main#0e270bce2d97466be6b987bb5f7ea5b1e8d84969" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno 0.3.8", - "itoa", "libc", - "linux-raw-sys 0.4.13", - "once_cell", - "windows-sys 0.52.0", + "linux-raw-sys", + "windows-sys", ] [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -1337,37 +1361,31 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -1376,9 +1394,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -1387,9 +1405,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -1398,9 +1416,9 @@ dependencies = [ [[package]] name = "sha256" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" dependencies = [ "async-trait", "bytes", @@ -1411,21 +1429,21 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -1434,9 +1452,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -1453,91 +1471,90 @@ dependencies = [ "remove_dir_all", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", + "syn 2.0.58", ] [[package]] name = "time" -version = "0.3.20" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ + "deranged", + "num-conv", + "powerfmt", "serde", "time-core", ] [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tokio" -version = "1.29.1" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ - "autocfg", "backtrace", "bytes", "pin-project-lite", ] +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-xid" @@ -1545,6 +1562,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "version_check" version = "0.9.4" @@ -1553,20 +1576,14 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1575,9 +1592,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -1585,24 +1602,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.58", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1610,34 +1627,33 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "which" -version = "5.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" +checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" dependencies = [ "either", "home", - "once_cell", - "rustix 0.38.30 (registry+https://github.com/rust-lang/crates.io-index)", - "windows-sys 0.48.0", + "rustix 0.38.32", + "winsafe", ] [[package]] @@ -1658,9 +1674,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -1672,36 +1688,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1710,221 +1702,128 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "winnow" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" +name = "winsafe" +version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "zip" -version = "0.5.13" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ + "aes", "byteorder", "bzip2", + "constant_time_eq", "crc32fast", + "crossbeam-utils", "flate2", - "thiserror", - "time 0.1.45", + "hmac", + "pbkdf2", + "sha1", + "time", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] name = "zip" -version = "0.6.4" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" dependencies = [ - "aes", - "byteorder", + "arbitrary", "bzip2", - "constant_time_eq", "crc32fast", "crossbeam-utils", + "deflate64", + "displaydoc", "flate2", - "hmac", - "pbkdf2", - "sha1", - "time 0.3.20", - "zstd", + "indexmap", + "num_enum", + "thiserror", + "time", + "zstd 0.13.1", ] [[package]] name = "zip-extensions" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c3c977bc3434ce2d4bcea8ad3c644672de0f2c402b72b9171ca80a8885d14" +checksum = "cecf62554c4ff96bce01a7ef123d160c3ffe9180638820f8b4d545c65b221b8c" dependencies = [ - "zip 0.5.13", + "zip 0.6.6", ] [[package]] @@ -1933,7 +1832,16 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe", + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe 7.1.0", ] [[package]] @@ -1946,13 +1854,21 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/userspace/ksud/Cargo.toml b/userspace/ksud/Cargo.toml index d7e08a093e17..3aaf32d41d32 100644 --- a/userspace/ksud/Cargo.toml +++ b/userspace/ksud/Cargo.toml @@ -2,7 +2,7 @@ name = "ksud" version = "0.1.0" edition = "2021" -rust-version = "1.65" +rust-version = "1.77.2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,11 +10,17 @@ rust-version = "1.65" anyhow = "1" clap = { version = "4", features = ["derive"] } const_format = "0.2" -zip = "0.6" +zip = { version = "1.1", features = [ + "deflate", + "deflate64", + "bzip2", + "time", + "zstd", +], default-features = false } zip-extensions = "0.6" -java-properties = "2.0.0" +java-properties = "2" log = "0.4" -env_logger = "0.10" +env_logger = { version = "0.11", default-features = false } serde = { version = "1" } serde_json = "1" regex = "1" @@ -26,22 +32,25 @@ extattr = "1" jwalk = "0.8" is_executable = "1" nom = "7" -derive-new = "0.5" -rust-embed = { version = "6", features = [ +derive-new = "0.6" +rust-embed = { version = "8", features = [ "debug-embed", "compression", # must clean build after updating binaries ] } -which = "5" +which = "6" getopts = "0.2" sha256 = "1" +sha1 = "0.10" tempdir = "0.3" chrono = "0.4" hole-punch = { git = "https://github.com/tiann/hole-punch" } [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] -rustix = { git = "https://github.com/Kernel-SU/rustix.git", branch = "main", features = ["all-apis"] } +rustix = { git = "https://github.com/Kernel-SU/rustix.git", branch = "main", features = [ + "all-apis", +] } # 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"] } +android-properties = { version = "0.2", features = ["bionic-deprecated"] } procfs = "0.16" loopdev = { git = "https://github.com/Kernel-SU/loopdev" } diff --git a/userspace/ksud/bin/aarch64/resetprop b/userspace/ksud/bin/aarch64/resetprop index 462c3cc80e97..571363e3e2ba 100644 Binary files a/userspace/ksud/bin/aarch64/resetprop and b/userspace/ksud/bin/aarch64/resetprop differ diff --git a/userspace/ksud/bin/x86_64/resetprop b/userspace/ksud/bin/x86_64/resetprop index 9318dce18c46..07b216e73b94 100644 Binary files a/userspace/ksud/bin/x86_64/resetprop and b/userspace/ksud/bin/x86_64/resetprop differ diff --git a/userspace/ksud/src/assets.rs b/userspace/ksud/src/assets.rs index b9ed994ddf7a..e8255c8204a7 100644 --- a/userspace/ksud/src/assets.rs +++ b/userspace/ksud/src/assets.rs @@ -37,3 +37,14 @@ pub fn copy_assets_to_file(name: &str, dst: impl AsRef) -> Result<()> { std::fs::write(dst, asset.data)?; Ok(()) } + +pub fn list_supported_kmi() -> Result> { + let mut list = Vec::new(); + for file in Asset::iter() { + // kmi_name = "xxx_kernelsu.ko" + if let Some(kmi) = file.strip_suffix("_kernelsu.ko") { + list.push(kmi.to_string()); + } + } + Ok(list) +} diff --git a/userspace/ksud/src/boot_patch.rs b/userspace/ksud/src/boot_patch.rs index cfd119992935..b518de09e8b2 100644 --- a/userspace/ksud/src/boot_patch.rs +++ b/userspace/ksud/src/boot_patch.rs @@ -1,17 +1,20 @@ #[cfg(unix)] use std::os::unix::fs::PermissionsExt; +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; +use std::process::Stdio; use anyhow::anyhow; use anyhow::bail; use anyhow::ensure; use anyhow::Context; use anyhow::Result; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; -use std::process::Stdio; use which::which; +use crate::defs; +use crate::defs::BACKUP_FILENAME; +use crate::defs::{KSU_BACKUP_DIR, KSU_BACKUP_FILE_PREFIX}; use crate::{assets, utils}; #[cfg(target_os = "android")] @@ -53,7 +56,7 @@ fn parse_kmi(version: &str) -> Result { let re = Regex::new(r"(.* )?(\d+\.\d+)(\S+)?(android\d+)(.*)")?; let cap = re .captures(version) - .ok_or_else(|| anyhow::anyhow!("No match found"))?; + .ok_or_else(|| anyhow::anyhow!("Failed to get KMI from boot/modules"))?; let android_version = cap.get(4).map_or("", |m| m.as_str()); let kernel_version = cap.get(2).map_or("", |m| m.as_str()); Ok(format!("{android_version}-{kernel_version}")) @@ -69,30 +72,34 @@ fn parse_kmi_from_uname() -> Result { #[cfg(target_os = "android")] fn parse_kmi_from_modules() -> Result { use std::io::BufRead; - let output = Command::new("modinfo") - .arg("/vendor/lib/modules/fips140.ko") - .output()?; - for line in output.stdout.lines().flatten() { + // find a *.ko in /vendor/lib/modules + let modfile = std::fs::read_dir("/vendor/lib/modules")? + .filter_map(Result::ok) + .find(|entry| entry.path().extension().map_or(false, |ext| ext == "ko")) + .map(|entry| entry.path()) + .ok_or_else(|| anyhow!("No kernel module found"))?; + let output = Command::new("modinfo").arg(modfile).output()?; + for line in output.stdout.lines().map_while(Result::ok) { if line.starts_with("vermagic") { return parse_kmi(&line); } } - anyhow::bail!("Unknown KMI, try use --kmi to specify it.") + anyhow::bail!("Parse KMI from modules failed") } #[cfg(target_os = "android")] -fn get_kmi() -> Result { +pub fn get_current_kmi() -> Result { parse_kmi_from_uname().or_else(|_| parse_kmi_from_modules()) } #[cfg(not(target_os = "android"))] -fn get_kmi() -> Result { - bail!("Unknown KMI, try use --kmi to specify it.") +pub fn get_current_kmi() -> Result { + bail!("Unsupported platform") } -fn do_cpio_cmd(magiskboot: &Path, workding_dir: &Path, cmd: &str) -> Result<()> { +fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> { let status = Command::new(magiskboot) - .current_dir(workding_dir) + .current_dir(workdir) .stdout(Stdio::null()) .stderr(Stdio::null()) .arg("cpio") @@ -104,9 +111,9 @@ fn do_cpio_cmd(magiskboot: &Path, workding_dir: &Path, cmd: &str) -> Result<()> Ok(()) } -fn is_magisk_patched(magiskboot: &Path, workding_dir: &Path) -> Result { +fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result { let status = Command::new(magiskboot) - .current_dir(workding_dir) + .current_dir(workdir) .stdout(Stdio::null()) .stderr(Stdio::null()) .args(["cpio", "ramdisk.cpio", "test"]) @@ -116,6 +123,17 @@ fn is_magisk_patched(magiskboot: &Path, workding_dir: &Path) -> Result { Ok(status.code() == Some(1)) } +fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result { + let status = Command::new(magiskboot) + .current_dir(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .args(["cpio", "ramdisk.cpio", "exists kernelsu.ko"]) + .status()?; + + Ok(status.success()) +} + fn dd, Q: AsRef>(ifile: P, ofile: Q) -> Result<()> { let status = Command::new("dd") .stdout(Stdio::null()) @@ -132,6 +150,113 @@ fn dd, Q: AsRef>(ifile: P, ofile: Q) -> Result<()> { Ok(()) } +pub fn restore( + image: Option, + magiskboot_path: Option, + flash: bool, +) -> Result<()> { + let tmpdir = tempdir::TempDir::new("KernelSU").context("create temp dir failed")?; + let workdir = tmpdir.path(); + let magiskboot = find_magiskboot(magiskboot_path, workdir)?; + + let (bootimage, bootdevice) = find_boot_image(&image, false, false, workdir)?; + + println!("- Unpacking boot image"); + let status = Command::new(&magiskboot) + .current_dir(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .arg("unpack") + .arg(bootimage.display().to_string()) + .status()?; + ensure!(status.success(), "magiskboot unpack failed"); + + let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?; + ensure!(is_kernelsu_patched, "boot image is not patched by KernelSU"); + + let mut new_boot = None; + let mut from_backup = false; + + #[cfg(target_os = "android")] + if do_cpio_cmd(&magiskboot, workdir, &format!("exists {BACKUP_FILENAME}")).is_ok() { + do_cpio_cmd( + &magiskboot, + workdir, + &format!("extract {0} {0}", BACKUP_FILENAME), + )?; + let sha = std::fs::read(workdir.join(BACKUP_FILENAME))?; + let sha = String::from_utf8(sha)?; + let sha = sha.trim(); + let backup_path = + PathBuf::from(KSU_BACKUP_DIR).join(format!("{KSU_BACKUP_FILE_PREFIX}{sha}")); + if backup_path.is_file() { + new_boot = Some(backup_path); + from_backup = true; + } else { + println!("- Warning: no backup {backup_path:?} found!"); + } + + if let Err(e) = clean_backup(sha) { + println!("- Warning: Cleanup backup image failed: {e}"); + } + } else { + println!("- Backup info is absent!"); + } + + if new_boot.is_none() { + // remove kernelsu.ko + do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?; + + // if init.real exists, restore it + let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok(); + if status { + do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?; + } else { + let ramdisk = workdir.join("ramdisk.cpio"); + std::fs::remove_file(ramdisk)?; + } + + println!("- Repacking boot image"); + let status = Command::new(&magiskboot) + .current_dir(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .arg("repack") + .arg(bootimage.display().to_string()) + .status()?; + ensure!(status.success(), "magiskboot repack failed"); + new_boot = Some(workdir.join("new-boot.img")); + } + + let new_boot = new_boot.unwrap(); + + if image.is_some() { + // if image is specified, write to output file + let output_dir = std::env::current_dir()?; + let now = chrono::Utc::now(); + let output_image = output_dir.join(format!( + "kernelsu_restore_{}.img", + now.format("%Y%m%d_%H%M%S") + )); + + if from_backup || std::fs::rename(&new_boot, &output_image).is_err() { + std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?; + } + println!("- Output file is written to"); + println!("- {}", output_image.display().to_string().trim_matches('"')); + } + if flash { + if from_backup { + println!("- Flashing new boot image from {}", new_boot.display()); + } else { + println!("- Flashing new boot image"); + } + flash_boot(&bootdevice, new_boot)?; + } + println!("- Done!"); + Ok(()) +} + #[allow(clippy::too_many_arguments)] pub fn patch( image: Option, @@ -165,8 +290,10 @@ fn do_patch( ) -> Result<()> { println!(include_str!("banner")); - if image.is_none() { - #[cfg(target_os = "android")] + let patch_file = image.is_some(); + + #[cfg(target_os = "android")] + if !patch_file { ensure_gki_kernel()?; } @@ -179,96 +306,46 @@ fn do_patch( ); } - let workding_dir = - tempdir::TempDir::new("KernelSU").with_context(|| "create temp dir failed")?; - - let bootimage; - - let mut bootdevice = None; - - if let Some(ref image) = image { - ensure!(image.exists(), "boot image not found"); - bootimage = std::fs::canonicalize(image)?; - } else { - let mut slot_suffix = - utils::getprop("ro.boot.slot_suffix").unwrap_or_else(|| String::from("")); - - if !slot_suffix.is_empty() && ota { - if slot_suffix == "_a" { - slot_suffix = "_b".to_string() - } else { - slot_suffix = "_a".to_string() - } - }; - - let init_boot_exist = - Path::new(&format!("/dev/block/by-name/init_boot{slot_suffix}")).exists(); - let boot_partition = if !is_replace_kernel && init_boot_exist { - format!("/dev/block/by-name/init_boot{slot_suffix}") - } else { - format!("/dev/block/by-name/boot{slot_suffix}") - }; - - println!("- Bootdevice: {boot_partition}"); - let tmp_boot_path = workding_dir.path().join("boot.img"); - - dd(&boot_partition, &tmp_boot_path)?; + let tmpdir = tempdir::TempDir::new("KernelSU").context("create temp dir failed")?; + let workdir = tmpdir.path(); - ensure!(tmp_boot_path.exists(), "boot image not found"); + let (bootimage, bootdevice) = find_boot_image(&image, ota, is_replace_kernel, workdir)?; - bootimage = tmp_boot_path; - bootdevice = Some(boot_partition); - }; + let bootimage = bootimage.display().to_string(); // try extract magiskboot/bootctl let _ = assets::ensure_binaries(false); // extract magiskboot - let magiskboot = { - if which("magiskboot").is_ok() { - let _ = assets::ensure_binaries(true); - "magiskboot".into() - } else { - // magiskboot is not in $PATH, use builtin or specified one - let magiskboot = if let Some(magiskboot_path) = magiskboot_path { - std::fs::canonicalize(magiskboot_path)? - } else { - let magiskboot_path = workding_dir.path().join("magiskboot"); - assets::copy_assets_to_file("magiskboot", &magiskboot_path) - .with_context(|| "copy magiskboot failed")?; - magiskboot_path - }; - ensure!(magiskboot.exists(), "{magiskboot:?} is not exist"); - #[cfg(unix)] - let _ = std::fs::set_permissions(&magiskboot, std::fs::Permissions::from_mode(0o755)); - magiskboot - } - }; + let magiskboot = find_magiskboot(magiskboot_path, workdir)?; if let Some(kernel) = kernel { - std::fs::copy(kernel, workding_dir.path().join("kernel")) - .with_context(|| "copy kernel from failed".to_string())?; + std::fs::copy(kernel, workdir.join("kernel")).context("copy kernel from failed")?; } println!("- Preparing assets"); - let kmod_file = workding_dir.path().join("kernelsu.ko"); + let kmod_file = workdir.join("kernelsu.ko"); if let Some(kmod) = kmod { - std::fs::copy(kmod, kmod_file).with_context(|| "copy kernel module failed".to_string())?; + std::fs::copy(kmod, kmod_file).context("copy kernel module failed")?; } else { // If kmod is not specified, extract from assets - let kmi = if let Some(kmi) = kmi { kmi } else { get_kmi()? }; + let kmi = if let Some(kmi) = kmi { + kmi + } else { + get_current_kmi().context("Unknown KMI, please choose LKM manually")? + }; println!("- KMI: {kmi}"); let name = format!("{kmi}_kernelsu.ko"); assets::copy_assets_to_file(&name, kmod_file) .with_context(|| format!("Failed to copy {name}"))?; }; - let init_file = workding_dir.path().join("init"); + let init_file = workdir.join("init"); if let Some(init) = init { - std::fs::copy(init, init_file).with_context(|| "copy init failed".to_string())?; + std::fs::copy(init, init_file).context("copy init failed")?; } else { - assets::copy_assets_to_file("ksuinit", init_file).with_context(|| "copy ksuinit failed")?; + assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?; } // magiskboot unpack boot.img @@ -278,61 +355,68 @@ fn do_patch( println!("- Unpacking boot image"); let status = Command::new(&magiskboot) - .current_dir(workding_dir.path()) + .current_dir(workdir) .stdout(Stdio::null()) .stderr(Stdio::null()) .arg("unpack") - .arg(bootimage.display().to_string()) + .arg(&bootimage) .status()?; ensure!(status.success(), "magiskboot unpack failed"); - let no_ramdisk = !workding_dir.path().join("ramdisk.cpio").exists(); - let is_magisk_patched = is_magisk_patched(&magiskboot, workding_dir.path())?; + let no_ramdisk = !workdir.join("ramdisk.cpio").exists(); + let is_magisk_patched = is_magisk_patched(&magiskboot, workdir)?; ensure!( no_ramdisk || !is_magisk_patched, "Cannot work with Magisk patched image" ); println!("- Adding KernelSU LKM"); - let is_kernelsu_patched = - do_cpio_cmd(&magiskboot, workding_dir.path(), "exists kernelsu.ko").is_ok(); + let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?; + + let mut need_backup = false; if !is_kernelsu_patched { // kernelsu.ko is not exist, backup init if necessary - let status = do_cpio_cmd(&magiskboot, workding_dir.path(), "exists init"); + let status = do_cpio_cmd(&magiskboot, workdir, "exists init"); if status.is_ok() { - do_cpio_cmd(&magiskboot, workding_dir.path(), "mv init init.real")?; + do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?; } + + need_backup = flash; } - do_cpio_cmd(&magiskboot, workding_dir.path(), "add 0755 init init")?; - do_cpio_cmd( - &magiskboot, - workding_dir.path(), - "add 0755 kernelsu.ko kernelsu.ko", - )?; + do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?; + do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?; + + #[cfg(target_os = "android")] + if need_backup { + if let Err(e) = do_backup(&magiskboot, workdir, &bootimage) { + println!("- Backup stock image failed: {e}"); + } + } println!("- Repacking boot image"); // magiskboot repack boot.img let status = Command::new(&magiskboot) - .current_dir(workding_dir.path()) + .current_dir(workdir) .stdout(Stdio::null()) .stderr(Stdio::null()) .arg("repack") - .arg(bootimage.display().to_string()) + .arg(&bootimage) .status()?; ensure!(status.success(), "magiskboot repack failed"); - let new_boot = workding_dir.path().join("new-boot.img"); + let new_boot = workdir.join("new-boot.img"); - if image.is_some() { + if patch_file { // if image is specified, write to output file let output_dir = out.unwrap_or(std::env::current_dir()?); let now = chrono::Utc::now(); - let output_image = - output_dir.join(format!("kernelsu_boot_{}.img", now.format("%Y%m%d_%H%M%S"))); + let output_image = output_dir.join(format!( + "kernelsu_patched_{}.img", + now.format("%Y%m%d_%H%M%S") + )); if std::fs::rename(&new_boot, &output_image).is_err() { - std::fs::copy(&new_boot, &output_image) - .with_context(|| "copy out new boot failed".to_string())?; + std::fs::copy(&new_boot, &output_image).context("copy out new boot failed")?; } println!("- Output file is written to"); println!("- {}", output_image.display().to_string().trim_matches('"')); @@ -340,16 +424,7 @@ fn do_patch( if flash { println!("- Flashing new boot image"); - let Some(bootdevice) = bootdevice else { - bail!("boot device not found") - }; - let status = Command::new("blockdev") - .arg("--setrw") - .arg(&bootdevice) - .status()?; - ensure!(status.success(), "set boot device rw failed"); - - dd(&new_boot, &bootdevice).with_context(|| "flash boot failed")?; + flash_boot(&bootdevice, new_boot)?; if ota { post_ota()?; @@ -360,6 +435,150 @@ fn do_patch( Ok(()) } +#[cfg(target_os = "android")] +fn calculate_sha1(file_path: impl AsRef) -> Result { + use sha1::Digest; + use std::io::Read; + let mut file = std::fs::File::open(file_path.as_ref())?; + let mut hasher = sha1::Sha1::new(); + let mut buffer = [0; 1024]; + + loop { + let n = file.read(&mut buffer)?; + if n == 0 { + break; + } + hasher.update(&buffer[..n]); + } + + let result = hasher.finalize(); + Ok(format!("{:x}", result)) +} + +#[cfg(target_os = "android")] +fn do_backup(magiskboot: &Path, workdir: &Path, image: &str) -> Result<()> { + let sha1 = calculate_sha1(image)?; + let filename = format!("{KSU_BACKUP_FILE_PREFIX}{sha1}"); + + println!("- Backup stock boot image"); + // magiskboot cpio ramdisk.cpio 'add 0755 $BACKUP_FILENAME' + let target = format!("{KSU_BACKUP_DIR}{filename}"); + std::fs::copy(image, &target).with_context(|| format!("backup to {target}"))?; + std::fs::write(workdir.join(BACKUP_FILENAME), sha1.as_bytes()).context("write sha1")?; + do_cpio_cmd( + magiskboot, + workdir, + &format!("add 0755 {0} {0}", BACKUP_FILENAME), + )?; + println!("- Stock image has been backup to"); + println!("- {target}"); + Ok(()) +} + +#[cfg(target_os = "android")] +fn clean_backup(sha1: &str) -> Result<()> { + println!("- Clean up backup"); + let backup_name = format!("{}{}", KSU_BACKUP_FILE_PREFIX, sha1); + let dir = std::fs::read_dir(defs::KSU_BACKUP_DIR)?; + for entry in dir.flatten() { + let path = entry.path(); + if !path.is_file() { + continue; + } + if let Some(name) = path.file_name() { + let name = name.to_string_lossy().to_string(); + if name != backup_name + && name.starts_with(KSU_BACKUP_FILE_PREFIX) + && std::fs::remove_file(path).is_ok() + { + println!("- removed {name}"); + } + } + } + Ok(()) +} + +fn flash_boot(bootdevice: &Option, new_boot: PathBuf) -> Result<()> { + let Some(bootdevice) = bootdevice else { + bail!("boot device not found") + }; + let status = Command::new("blockdev") + .arg("--setrw") + .arg(bootdevice) + .status()?; + ensure!(status.success(), "set boot device rw failed"); + dd(new_boot, bootdevice).context("flash boot failed")?; + Ok(()) +} + +fn find_magiskboot(magiskboot_path: Option, workdir: &Path) -> Result { + let magiskboot = { + if which("magiskboot").is_ok() { + let _ = assets::ensure_binaries(true); + "magiskboot".into() + } else { + // magiskboot is not in $PATH, use builtin or specified one + let magiskboot = if let Some(magiskboot_path) = magiskboot_path { + std::fs::canonicalize(magiskboot_path)? + } else { + let magiskboot_path = workdir.join("magiskboot"); + assets::copy_assets_to_file("magiskboot", &magiskboot_path) + .context("copy magiskboot failed")?; + magiskboot_path + }; + ensure!(magiskboot.exists(), "{magiskboot:?} is not exist"); + #[cfg(unix)] + let _ = std::fs::set_permissions(&magiskboot, std::fs::Permissions::from_mode(0o755)); + magiskboot + } + }; + Ok(magiskboot) +} + +fn find_boot_image( + image: &Option, + ota: bool, + is_replace_kernel: bool, + workdir: &Path, +) -> Result<(PathBuf, Option)> { + let bootimage; + let mut bootdevice = None; + if let Some(ref image) = *image { + ensure!(image.exists(), "boot image not found"); + bootimage = std::fs::canonicalize(image)?; + } else { + let mut slot_suffix = + utils::getprop("ro.boot.slot_suffix").unwrap_or_else(|| String::from("")); + + if !slot_suffix.is_empty() && ota { + if slot_suffix == "_a" { + slot_suffix = "_b".to_string() + } else { + slot_suffix = "_a".to_string() + } + }; + + let init_boot_exist = + Path::new(&format!("/dev/block/by-name/init_boot{slot_suffix}")).exists(); + let boot_partition = if !is_replace_kernel && init_boot_exist { + format!("/dev/block/by-name/init_boot{slot_suffix}") + } else { + format!("/dev/block/by-name/boot{slot_suffix}") + }; + + println!("- Bootdevice: {boot_partition}"); + let tmp_boot_path = workdir.join("boot.img"); + + dd(&boot_partition, &tmp_boot_path)?; + + ensure!(tmp_boot_path.exists(), "boot image not found"); + + bootimage = tmp_boot_path; + bootdevice = Some(boot_partition); + }; + Ok((bootimage, bootdevice)) +} + fn post_ota() -> Result<()> { use crate::defs::ADB_DIR; use assets::BOOTCTL_PATH; diff --git a/userspace/ksud/src/cli.rs b/userspace/ksud/src/cli.rs index 4f8537d51333..b3954bd168a8 100644 --- a/userspace/ksud/src/cli.rs +++ b/userspace/ksud/src/cli.rs @@ -1,6 +1,6 @@ use anyhow::{Ok, Result}; use clap::Parser; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; #[cfg(target_os = "android")] use android_logger::Config; @@ -35,7 +35,17 @@ enum Commands { BootCompleted, /// Install KernelSU userspace component to system - Install, + Install { + #[arg(long, default_value = None)] + magiskboot: Option, + }, + + /// Uninstall KernelSU modules and itself(LKM Only) + Uninstall { + /// magiskboot path, if not specified, will search from $PATH + #[arg(long, default_value = None)] + magiskboot: Option, + }, /// SELinux policy Patch tool Sepolicy { @@ -79,7 +89,7 @@ enum Commands { #[arg(short, long, default_value = None)] out: Option, - /// magiskboot path, if not specified, will use builtin one + /// magiskboot path, if not specified, will search from $PATH #[arg(long, default_value = None)] magiskboot: Option, @@ -87,12 +97,43 @@ enum Commands { #[arg(long, default_value = None)] kmi: Option, }, + + /// Restore boot or init_boot images patched by KernelSU + BootRestore { + /// boot image path, if not specified, will try to find the boot image automatically + #[arg(short, long)] + boot: Option, + + /// Flash it to boot partition after patch + #[arg(short, long, default_value = "false")] + flash: bool, + + /// magiskboot path, if not specified, will search from $PATH + #[arg(long, default_value = None)] + magiskboot: Option, + }, + + /// Show boot information + BootInfo { + #[command(subcommand)] + command: BootInfo, + }, /// For developers Debug { #[command(subcommand)] command: Debug, }, } + +#[derive(clap::Subcommand, Debug)] +enum BootInfo { + /// show current kmi version + CurrentKmi, + + /// show supported kmi versions + SupportedKmi, +} + #[derive(clap::Subcommand, Debug)] enum Debug { /// Set the manager app, kernel CONFIG_KSU_DEBUG should be enabled. @@ -242,7 +283,7 @@ pub fn run() -> Result<()> { // the kernel executes su with argv[0] = "su" and replace it with us let arg0 = std::env::args().next().unwrap_or_default(); - if arg0 == "su" || arg0 == "/system/bin/su" { + if Path::new(&arg0).file_name().and_then(|f| f.to_str()) == Some("su") { return crate::su::root_shell(); } @@ -269,7 +310,8 @@ pub fn run() -> Result<()> { Module::Shrink => module::shrink_ksu_images(), } } - Commands::Install => utils::install(), + Commands::Install { magiskboot } => utils::install(magiskboot), + Commands::Uninstall { magiskboot } => utils::uninstall(magiskboot), Commands::Sepolicy { command } => match command { Sepolicy::Patch { sepolicy } => crate::sepolicy::live_patch(&sepolicy), Sepolicy::Apply { file } => crate::sepolicy::apply_file(file), @@ -322,6 +364,25 @@ pub fn run() -> Result<()> { magiskboot, kmi, } => crate::boot_patch::patch(boot, kernel, module, init, ota, flash, out, magiskboot, kmi), + + Commands::BootInfo { command } => match command { + BootInfo::CurrentKmi => { + let kmi = crate::boot_patch::get_current_kmi()?; + println!("{}", kmi); + // return here to avoid printing the error message + return Ok(()); + } + BootInfo::SupportedKmi => { + let kmi = crate::assets::list_supported_kmi()?; + kmi.iter().for_each(|kmi| println!("{}", kmi)); + return Ok(()); + } + }, + Commands::BootRestore { + boot, + magiskboot, + flash, + } => crate::boot_patch::restore(boot, magiskboot, flash), }; if let Err(e) = &result { diff --git a/userspace/ksud/src/debug.rs b/userspace/ksud/src/debug.rs index 042ec1464c52..8ff851d30b2f 100644 --- a/userspace/ksud/src/debug.rs +++ b/userspace/ksud/src/debug.rs @@ -4,8 +4,6 @@ use std::{ process::Command, }; -use crate::apk_sign::get_apk_signature; - const KERNEL_PARAM_PATH: &str = "/sys/module/kernelsu"; fn read_u32(path: &PathBuf) -> Result { @@ -15,38 +13,26 @@ fn read_u32(path: &PathBuf) -> Result { Ok(content) } -fn set_kernel_param(size: u32, hash: String) -> Result<()> { +fn set_kernel_param(uid: u32) -> Result<()> { let kernel_param_path = Path::new(KERNEL_PARAM_PATH).join("parameters"); - let expeced_size_path = kernel_param_path.join("ksu_expected_size"); - let expeced_hash_path = kernel_param_path.join("ksu_expected_hash"); - - print!( - "before size: {:#x} hash: {}", - read_u32(&expeced_size_path)?, - std::fs::read_to_string(&expeced_hash_path)? - ); + let ksu_debug_manager_uid = kernel_param_path.join("ksu_debug_manager_uid"); + let before_uid = read_u32(&ksu_debug_manager_uid)?; + std::fs::write(&ksu_debug_manager_uid, uid.to_string())?; + let after_uid = read_u32(&ksu_debug_manager_uid)?; - std::fs::write(&expeced_size_path, size.to_string())?; - std::fs::write(&expeced_hash_path, hash)?; - - print!( - "after size: {:#x} hash: {}", - read_u32(&expeced_size_path)?, - std::fs::read_to_string(&expeced_hash_path)? - ); + println!("set manager uid: {before_uid} -> {after_uid}"); Ok(()) } -fn get_apk_path(package_name: &str) -> Result { - // `cmd package path` is not available below Android 9 - let output = Command::new("pm").args(["path", package_name]).output()?; - - // package:/data/app//base.apk - let output = String::from_utf8_lossy(&output.stdout); - let path = output.trim().replace("package:", ""); - Ok(path) +#[cfg(target_os = "android")] +fn get_pkg_uid(pkg: &str) -> Result { + // stat /data/data/ + let uid = rustix::fs::stat(format!("/data/data/{pkg}")) + .with_context(|| format!("stat /data/data/{}", pkg))? + .st_uid; + Ok(uid) } pub fn set_manager(pkg: &str) -> Result<()> { @@ -55,10 +41,11 @@ pub fn set_manager(pkg: &str) -> Result<()> { "CONFIG_KSU_DEBUG is not enabled" ); - 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)?; - + #[cfg(target_os = "android")] + let uid = get_pkg_uid(pkg)?; + #[cfg(not(target_os = "android"))] + let uid = 0; + set_kernel_param(uid)?; // force-stop it let _ = Command::new("am").args(["force-stop", pkg]).status(); Ok(()) diff --git a/userspace/ksud/src/defs.rs b/userspace/ksud/src/defs.rs index 9508f562955b..bcde77120ee4 100644 --- a/userspace/ksud/src/defs.rs +++ b/userspace/ksud/src/defs.rs @@ -12,6 +12,7 @@ pub const PROFILE_TEMPLATE_DIR: &str = concatcp!(PROFILE_DIR, "templates/"); pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc"); pub const KSU_OVERLAY_SOURCE: &str = "KSU"; pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud"); +pub const MAGISKBOOT_PATH: &str = concatcp!(BINARY_DIR, "magiskboot"); #[cfg(target_os = "android")] pub const DAEMON_LINK_PATH: &str = concatcp!(BINARY_DIR, "ksud"); @@ -38,3 +39,9 @@ pub const SKIP_MOUNT_FILE_NAME: &str = "skip_mount"; 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")); + +pub const KSU_BACKUP_DIR: &str = WORKING_DIR; +pub const KSU_BACKUP_FILE_PREFIX: &str = "ksu_backup_"; +pub const BACKUP_FILENAME: &str = "stock_image.sha1"; + +pub const PTS_NAME: &str = "pts"; diff --git a/userspace/ksud/src/init_event.rs b/userspace/ksud/src/init_event.rs index 7107ff74225d..f2334d1bf897 100644 --- a/userspace/ksud/src/init_event.rs +++ b/userspace/ksud/src/init_event.rs @@ -2,6 +2,7 @@ use anyhow::{bail, Context, Result}; use log::{info, warn}; use std::{collections::HashMap, path::Path}; +use crate::defs::PTS_NAME; use crate::module::prune_modules; use crate::{ assets, defs, ksucalls, mount, restorecon, @@ -193,6 +194,11 @@ pub fn on_post_data_fs() -> Result<()> { // mount temp dir if let Err(e) = mount::mount_tmpfs(utils::get_tmp_path()) { warn!("do temp dir mount failed: {}", e); + } else { + let pts_dir = format!("{}/{PTS_NAME}", utils::get_tmp_path()); + if let Err(e) = mount::mount_devpts(pts_dir) { + warn!("do devpts mount failed: {}", e); + } } // exec modules post-fs-data scripts diff --git a/userspace/ksud/src/main.rs b/userspace/ksud/src/main.rs index 3b51720557c1..b22de94805bb 100644 --- a/userspace/ksud/src/main.rs +++ b/userspace/ksud/src/main.rs @@ -9,6 +9,8 @@ mod ksucalls; mod module; mod mount; mod profile; +#[cfg(any(target_os = "linux", target_os = "android"))] +mod pty; mod restorecon; mod sepolicy; mod su; diff --git a/userspace/ksud/src/module.rs b/userspace/ksud/src/module.rs index ccb5367ffbb8..ac3ff213b62f 100644 --- a/userspace/ksud/src/module.rs +++ b/userspace/ksud/src/module.rs @@ -604,13 +604,21 @@ pub fn disable_module(id: &str) -> Result<()> { } pub fn disable_all_modules() -> Result<()> { + mark_all_modules(defs::DISABLE_FILE_NAME) +} + +pub fn uninstall_all_modules() -> Result<()> { + mark_all_modules(defs::REMOVE_FILE_NAME) +} + +fn mark_all_modules(flag_file: &str) -> 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); + let flag = path.join(flag_file); + if let Err(e) = ensure_file_exists(flag) { + warn!("Failed to mark module: {}: {}", path.display(), e); } } diff --git a/userspace/ksud/src/mount.rs b/userspace/ksud/src/mount.rs index d60241956cf5..36aab8213e84 100644 --- a/userspace/ksud/src/mount.rs +++ b/userspace/ksud/src/mount.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, bail, Ok, Result}; +use std::fs::create_dir; #[cfg(any(target_os = "linux", target_os = "android"))] use anyhow::Context; @@ -164,6 +165,19 @@ pub fn mount_tmpfs(dest: impl AsRef) -> Result<()> { Ok(()) } +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn mount_devpts(dest: impl AsRef) -> Result<()> { + create_dir(dest.as_ref())?; + mount( + KSU_OVERLAY_SOURCE, + dest.as_ref(), + "devpts", + MountFlags::empty(), + "newinstance", + )?; + Ok(()) +} + #[cfg(any(target_os = "linux", target_os = "android"))] pub fn bind_mount(from: impl AsRef, to: impl AsRef) -> Result<()> { info!( @@ -301,3 +315,8 @@ pub fn mount_overlay( pub fn mount_tmpfs(_dest: impl AsRef) -> Result<()> { unimplemented!() } + +#[cfg(not(any(target_os = "linux", target_os = "android")))] +pub fn mount_devpts(_dest: impl AsRef) -> Result<()> { + unimplemented!() +} diff --git a/userspace/ksud/src/pty.rs b/userspace/ksud/src/pty.rs new file mode 100644 index 000000000000..80a0d18d81e1 --- /dev/null +++ b/userspace/ksud/src/pty.rs @@ -0,0 +1,192 @@ +use std::ffi::c_int; +use std::fs::File; +use std::io::{stderr, stdin, stdout, Read, Write}; +use std::mem::MaybeUninit; +use std::os::fd::{AsFd, AsRawFd, OwnedFd, RawFd}; +use std::process::exit; +use std::ptr::null_mut; +use std::thread; + +use anyhow::{bail, Ok, Result}; +use libc::{ + __errno, fork, pthread_sigmask, sigaddset, sigemptyset, sigset_t, sigwait, waitpid, winsize, + EINTR, SIGWINCH, SIG_BLOCK, SIG_UNBLOCK, TIOCGWINSZ, TIOCSWINSZ, +}; +use rustix::fs::{open, Mode, OFlags}; +use rustix::io::dup; +use rustix::ioctl::{ioctl, Getter, ReadOpcode}; +use rustix::process::setsid; +use rustix::pty::{grantpt, unlockpt}; +use rustix::stdio::{dup2_stderr, dup2_stdin, dup2_stdout}; +use rustix::termios::{isatty, tcgetattr, tcsetattr, OptionalActions, Termios}; + +use crate::defs::PTS_NAME; +use crate::utils::get_tmp_path; + +// https://github.com/topjohnwu/Magisk/blob/5627053b7481618adfdf8fa3569b48275589915b/native/src/core/su/pts.cpp + +fn get_pty_num(fd: F) -> Result { + Ok(unsafe { + let tiocgptn = Getter::, u32>::new(); + ioctl(fd, tiocgptn)? + }) +} + +static mut OLD_STDIN: Option = None; + +fn watch_sigwinch_async(slave: RawFd) { + let mut winch = MaybeUninit::::uninit(); + unsafe { + sigemptyset(winch.as_mut_ptr()); + sigaddset(winch.as_mut_ptr(), SIGWINCH); + pthread_sigmask(SIG_BLOCK, winch.as_mut_ptr(), null_mut()); + } + + thread::spawn(move || unsafe { + let mut winch = MaybeUninit::::uninit(); + sigemptyset(winch.as_mut_ptr()); + sigaddset(winch.as_mut_ptr(), SIGWINCH); + pthread_sigmask(SIG_UNBLOCK, winch.as_mut_ptr(), null_mut()); + let mut sig: c_int = 0; + loop { + let mut w = MaybeUninit::::uninit(); + if libc::ioctl(1, TIOCGWINSZ, w.as_mut_ptr()) < 0 { + continue; + } + libc::ioctl(slave, TIOCSWINSZ, w.as_mut_ptr()); + if sigwait(winch.as_mut_ptr(), &mut sig) != 0 { + break; + } + } + }); +} + +fn set_stdin_raw() { + let mut termios = match tcgetattr(stdin()) { + Result::Ok(termios) => { + unsafe { + OLD_STDIN = Some(termios.clone()); + } + termios + } + Err(_) => return, + }; + + termios.make_raw(); + + if tcsetattr(stdin(), OptionalActions::Flush, &termios).is_err() { + let _ = tcsetattr(stdin(), OptionalActions::Drain, &termios); + } +} + +fn restore_stdin() { + let Some(termios) = (unsafe { OLD_STDIN.take() }) else { + return; + }; + + if tcsetattr(stdin(), OptionalActions::Flush, &termios).is_err() { + let _ = tcsetattr(stdin(), OptionalActions::Drain, &termios); + } +} + +fn pump(mut from: R, mut to: W) { + let mut buf = [0u8; 4096]; + loop { + match from.read(&mut buf) { + Result::Ok(len) => { + if len == 0 { + return; + } + if to.write_all(&buf[0..len]).is_err() { + return; + } + if to.flush().is_err() { + return; + } + } + Err(_) => { + return; + } + } + } +} + +fn pump_stdin_async(mut ptmx: File) { + set_stdin_raw(); + + thread::spawn(move || { + let mut stdin = stdin(); + pump(&mut stdin, &mut ptmx); + }); +} + +fn pump_stdout_blocking(mut ptmx: File) { + let mut stdout = stdout(); + pump(&mut ptmx, &mut stdout); + + restore_stdin(); +} + +fn create_transfer(ptmx: OwnedFd) -> Result<()> { + let pid = unsafe { fork() }; + match pid { + d if d < 0 => bail!("fork"), + 0 => return Ok(()), + _ => {} + } + + let ptmx_r = ptmx; + let ptmx_w = dup(&ptmx_r).unwrap(); + + let ptmx_r = File::from(ptmx_r); + let ptmx_w = File::from(ptmx_w); + + watch_sigwinch_async(ptmx_w.as_raw_fd()); + pump_stdin_async(ptmx_r); + pump_stdout_blocking(ptmx_w); + + let mut status: c_int = -1; + + unsafe { + loop { + if waitpid(pid, &mut status, 0) == -1 && *__errno() != EINTR { + continue; + } + break; + } + } + + exit(status) +} + +pub fn prepare_pty() -> Result<()> { + let tty_in = isatty(stdin()); + let tty_out = isatty(stdout()); + let tty_err = isatty(stderr()); + if !tty_in && !tty_out && !tty_err { + return Ok(()); + } + + let mut pts_path = format!("{}/{}", get_tmp_path(), PTS_NAME); + if !std::path::Path::new(&pts_path).exists() { + pts_path = "/dev/pts".to_string(); + } + let ptmx_path = format!("{}/ptmx", pts_path); + let ptmx_fd = open(ptmx_path, OFlags::RDWR, Mode::empty())?; + grantpt(&ptmx_fd)?; + unlockpt(&ptmx_fd)?; + let pty_num = get_pty_num(&ptmx_fd)?; + create_transfer(ptmx_fd)?; + setsid()?; + let pty_fd = open(format!("{pts_path}/{pty_num}"), OFlags::RDWR, Mode::empty())?; + if tty_in { + dup2_stdin(&pty_fd)?; + } + if tty_out { + dup2_stdout(&pty_fd)?; + } + if tty_err { + dup2_stderr(&pty_fd)?; + } + Ok(()) +} diff --git a/userspace/ksud/src/su.rs b/userspace/ksud/src/su.rs index 547105def556..d97eff49a1f9 100644 --- a/userspace/ksud/src/su.rs +++ b/userspace/ksud/src/su.rs @@ -11,6 +11,8 @@ use crate::{ utils::{self, umask}, }; +#[cfg(any(target_os = "linux", target_os = "android"))] +use crate::pty::prepare_pty; #[cfg(any(target_os = "linux", target_os = "android"))] use rustix::{ process::getuid, @@ -138,6 +140,7 @@ pub fn root_shell() -> Result<()> { "Specify a supplementary group. The first specified supplementary group is also used as a primary group if the option -g is not specified.", "GROUP", ); + opts.optflag("", "no-pty", "Do not allocate a new pseudo terminal."); // Replace -cn with -z, -mm with -M for supporting getopt_long let args = args @@ -217,7 +220,7 @@ pub fn root_shell() -> Result<()> { let name = &matches.free[free_idx]; uid = unsafe { #[cfg(target_arch = "aarch64")] - let pw = libc::getpwnam(name.as_ptr() as *const u8).as_ref(); + let pw = libc::getpwnam(name.as_ptr()).as_ref(); #[cfg(target_arch = "x86_64")] let pw = libc::getpwnam(name.as_ptr() as *const i8).as_ref(); @@ -265,6 +268,13 @@ pub fn root_shell() -> Result<()> { command = command.env("ENV", defs::KSURC_PATH); } + #[cfg(target_os = "android")] + if !matches.opt_present("no-pty") { + if let Err(e) = prepare_pty() { + log::error!("failed to prepare pty: {:?}", e); + } + } + // escape from the current cgroup and become session leader // WARNING!!! This cause some root shell hang forever! // command = command.process_group(0); diff --git a/userspace/ksud/src/utils.rs b/userspace/ksud/src/utils.rs index 1c8d66bf5bca..5abd6a4bfc53 100644 --- a/userspace/ksud/src/utils.rs +++ b/userspace/ksud/src/utils.rs @@ -1,11 +1,15 @@ use anyhow::{bail, Context, Error, Ok, Result}; use std::{ fs::{create_dir_all, remove_file, write, File, OpenOptions}, - io::{ErrorKind::AlreadyExists, ErrorKind::NotFound, Write}, + io::{ + ErrorKind::{AlreadyExists, NotFound}, + Write, + }, path::Path, + process::Command, }; -use crate::{assets, defs, ksucalls, restorecon}; +use crate::{assets, boot_patch, defs, ksucalls, module, restorecon}; use std::fs::metadata; #[allow(unused_imports)] use std::fs::{set_permissions, Permissions}; @@ -204,7 +208,7 @@ fn link_ksud_to_bin() -> Result<()> { Ok(()) } -pub fn install() -> Result<()> { +pub fn install(magiskboot: Option) -> Result<()> { ensure_dir_exists(defs::ADB_DIR)?; std::fs::copy("/proc/self/exe", defs::DAEMON_PATH)?; restorecon::lsetfilecon(defs::DAEMON_PATH, restorecon::ADB_CON)?; @@ -214,6 +218,32 @@ pub fn install() -> Result<()> { #[cfg(target_os = "android")] link_ksud_to_bin()?; + if let Some(magiskboot) = magiskboot { + ensure_dir_exists(defs::BINARY_DIR)?; + let _ = std::fs::copy(magiskboot, defs::MAGISKBOOT_PATH); + } + + Ok(()) +} + +pub fn uninstall(magiskboot_path: Option) -> Result<()> { + if Path::new(defs::MODULE_DIR).exists() { + println!("- Uninstall modules.."); + module::uninstall_all_modules()?; + module::prune_modules()?; + } + println!("- Removing directories.."); + std::fs::remove_dir_all(defs::WORKING_DIR)?; + std::fs::remove_file(defs::DAEMON_PATH)?; + println!("- Restore boot image.."); + boot_patch::restore(None, magiskboot_path, true)?; + println!("- Uninstall KernelSU manager.."); + Command::new("pm") + .args(["uninstall", "me.weishu.kernelsu"]) + .spawn()?; + println!("- Rebooting in 5 seconds.."); + std::thread::sleep(std::time::Duration::from_secs(5)); + Command::new("reboot").spawn()?; Ok(()) } diff --git a/website/docs/.vitepress/locales/pt_BR.ts b/website/docs/.vitepress/locales/pt_BR.ts index cd28a8176b9b..c1dc5023302d 100644 --- a/website/docs/.vitepress/locales/pt_BR.ts +++ b/website/docs/.vitepress/locales/pt_BR.ts @@ -22,7 +22,7 @@ export default defineConfig({ ], footer: { - message: 'Lançado sob a Licença GPL3.', + message: 'Lançado sob a Licença GPL3', copyright: 'Copyright © Desenvolvedores do KernelSU atuais de 2022' }, @@ -47,7 +47,7 @@ function sidebarGuide() { { text: 'O que é KernelSU?', link: '/pt_BR/guide/what-is-kernelsu' }, { text: 'Diferença com Magisk', link: '/pt_BR/guide/difference-with-magisk' }, { text: 'Instalação', link: '/pt_BR/guide/installation' }, - { text: 'Como construir?', link: '/pt_BR/guide/how-to-build' }, + { text: 'Como compilar?', link: '/pt_BR/guide/how-to-build' }, { text: 'Integração para dispositivos não GKI', link: '/pt_BR/guide/how-to-integrate-for-non-gki'}, { text: 'Dispositivos com suporte não oficial', link: '/pt_BR/guide/unofficially-support-devices.md' }, { text: 'Guias de módulo', link: '/pt_BR/guide/module.md' }, diff --git a/website/docs/guide/app-profile.md b/website/docs/guide/app-profile.md index 9ce0937d0430..ba90e282ff4c 100644 --- a/website/docs/guide/app-profile.md +++ b/website/docs/guide/app-profile.md @@ -114,5 +114,5 @@ Additionally, the settings interface of the KernelSU manager provides a switch f 2. Disable the switch for "umount modules by default" and individually enable the "umount modules" option in the App Profile for apps requiring module unloading (acting as a "blacklist"). :::info -In devices using kernel version 5.10 and above, the kernel performs without any further action the unloading of modules. However, for devices running kernel versions below 5.10, this switch is merely a configuration option, and KernelSU itself does not take any action. If you want to be able to use the "umount modules" option in kernel versions before 5.10 you need to backport the ```path_umount``` function in ```fs/namespace.c```, you can get more information at the end of the [How to integrate for non GKI](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#how-to-backport-path_umount) page. Some modules, such as Zygisksu, may also use this switch to determine whether module unloading is necessary. +In devices using kernel version 5.10 and above, the kernel performs without any further action the unloading of modules. However, for devices running kernel versions below 5.10, this switch is merely a configuration option, and KernelSU itself does not take any action. If you want to be able to use the "umount modules" option in kernel versions before 5.10 you need to backport the `path_umount` function in `fs/namespace.c`, you can get more information at the end of the [How to integrate for non GKI](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#how-to-backport-path_umount) page. Some modules, such as Zygisksu, may also use this switch to determine whether module unloading is necessary. ::: diff --git a/website/docs/guide/how-to-integrate-for-non-gki.md b/website/docs/guide/how-to-integrate-for-non-gki.md index c3efca892bb5..18ba342b837a 100644 --- a/website/docs/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/guide/how-to-integrate-for-non-gki.md @@ -299,7 +299,7 @@ index 45306f9ef247..815091ebfca4 100755 ### How to backport path_umount -You can get module umount feature working on pre-GKI kernels by manually backporting ```path_umount``` from 5.9. You can use this patch as reference: +You can get module umount feature working on pre-GKI kernels by manually backporting `path_umount` from 5.9. You can use this patch as reference: ```diff --- a/fs/namespace.c diff --git a/website/docs/guide/installation.md b/website/docs/guide/installation.md index a6b719e589c3..8e6904a9b22c 100644 --- a/website/docs/guide/installation.md +++ b/website/docs/guide/installation.md @@ -57,7 +57,98 @@ If you find that your kernel version is `android12-5.10.101`, but your Android s ## Introduction -There are several installation methods for KernelSU, each suitable for a different scenario, so please choose as needed. +Since version `0.9.0`, KernelSU supports two running modes on GKI devices: + +1. `GKI`: Replace the original kernel of the device with the **Generic Kernel Image** (GKI) provided by KernelSU. +2. `LKM`: Load the **Loadable Kernel Module** (LKM) into the device kernel without replacing the original kernel. + +These two modes are suitable for different scenarios, and you can choose according to your needs. + +### GKI Mode {#gki-mode} + +In GKI mode, the original kernel of the device will be replaced with the generic kernel image provided by KernelSU. The advantages of GKI mode are: + +1. Strong universality, suitable for most devices; for example, Samsung has enabled KNOX devices, and LKM mode cannot operate. There are also some niche modified devices that can only use GKI mode; +2. Can be used without relying on official firmware; no need to wait for official firmware updates, as long as the KMI is consistent, it can be used; + +### LKM Mode {#lkm-mode} + +In LKM mode, the original kernel of the device will not be replaced, but the loadable kernel module will be loaded into the device kernel. The advantages of LKM mode are: + +1. Will not replace the original kernel of the device; if you have special requirements for the original kernel of the device, or you want to use KernelSU while using a third-party kernel, you can use LKM mode; +2. It is more convenient to upgrade and OTA; when upgrading KernelSU, you can directly install it in the manager without manually flashing; after the system OTA, you can directly install it to the second slot without manually flashing; +3. Suitable for some special scenarios; for example, LKM can also be loaded with temporary ROOT permissions. Since it does not need to replace the boot partition, it will not trigger avb and will not cause the device to be bricked; +4. LKM can be temporarily uninstalled; if you want to temporarily cancel root, you can uninstall LKM, this process does not require flashing partitions, or even rebooting the device; if you want to root again, just reboot the device; + +:::tip Coexistence of two modes +After opening the manager, you can see the current mode of the device on the homepage; note that the priority of GKI mode is higher than that of LKM. For example, if you use GKI kernel to replace the original kernel, and use LKM to patch the GKI kernel, then LKM will be ignored, and the device will always run in GKI mode. +::: + +### Which one to choose? {#which-one} + +If your device is a mobile phone, we recommend that you prioritize LKM mode; if your device is an emulator, WSA, or Waydroid, we recommend that you prioritize GKI mode. + +## LKM Installation + +### Get the official firmware + +To use LKM mode, you need to get the official firmware and then patch it on the basis of the official firmware; if you use a third-party kernel, you can use the `boot.img` of the third-party kernel as the official firmware. + +There are many ways to get the official firmware. If your device supports `fastboot boot`, then we recommend **the most recommended and simplest** method is to use `fastboot boot` to temporarily boot the GKI kernel provided by KernelSU, then install the manager, and finally install it directly in the manager; this method does not require you to manually download the official firmware, nor do you need to manually extract the boot. + +If your device does not support `fastboot boot`, then you may need to manually download the official firmware package and then extract the boot from it. + +Unlike GKI mode, LKM mode will modify the `ramdisk`, so on devices with Android 13, it needs to patch the `init_boot` partition instead of the `boot` partition; while GKI mode always operates the `boot` partition. + +### Use the manager + +Open the manager, click the installation icon in the upper right corner, and several options will appear: + +1. Select and patch a file; if your phone does not have root permissions, you can choose this option, and then select your official firmware, and the manager will automatically patch it; you only need to flash this patched file to permanently obtain root permissions; +2. Install directly; if your phone is already rooted, you can choose this option, the manager will automatically get your device information, and then automatically patch the official firmware, and then flash it; you can consider using `fastboot boot` KernelSU's GKI kernel to get temporary root and install the manager, and then use this option; this is also the main way to upgrade KernelSU; +3. Install to another partition; if your device supports A/B partition, you can choose this option, the manager will automatically patch the official firmware, and then install it to another partition; this method is suitable for devices after OTA, you can directly install it to another partition after OTA, and then restart the device; + +### Use the command line + +If you don’t want to use the manager, you can also use the command line to install LKM; the `ksud` tool provided by KernelSU can help you quickly patch the official firmware and then flash it. + +This tool supports macOS, Linux, and Windows. You can download the corresponding version from [GitHub Release](https://github.com/tiann/KernelSU/releases). + +Usage: `ksud boot-patch` You can check the command line help for specific usage. + +```sh +oriole:/ # ksud boot-patch -h +Patch boot or init_boot images to apply KernelSU + +Usage: ksud boot-patch [OPTIONS] + +Options: + -b, --boot boot image path, if not specified, will try to find the boot image automatically + -k, --kernel kernel image path to replace + -m, --module LKM module path to replace, if not specified, will use the builtin one + -i, --init init to be replaced + -u, --ota will use another slot when boot image is not specified + -f, --flash Flash it to boot partition after patch + -o, --out output path, if not specified, will use current directory + --magiskboot magiskboot path, if not specified, will use builtin one + --kmi KMI version, if specified, will use the specified KMI + -h, --help Print help +``` + +A few options that need to be explained: + +1. The `--magiskboot` option can specify the path of magiskboot. If it is not specified, ksud will look for it in the environment variables; if you don’t know how to get magiskboot, you can check [here](#patch-boot-image); +2. The `--kmi` option can specify the `KMI` version. If the kernel name of your device does not follow the KMI specification, you can specify it through this option; + +The most common usage is: + +```sh +ksud boot-patch -b --kmi android13-5.10 +``` + +## GKI mode Installation + +There are several installation methods for GKI mode, each suitable for a different scenario, so please choose as needed. 1. Install with fastboot using the boot.img provided by KernelSU 2. Install with a kernel flash app, such as KernelFlasher @@ -76,7 +167,7 @@ You can download boot.img from [GitHub Release](https://github.com/tiann/KernelS Normally, there are three boot files in different formats under the same KMI and security patch level. They are all the same except for the kernel compression format. Please check the kernel compression format of your original boot.img. You should use the correct format, such as `lz4`, `gz`; if you use an incorrect compression format, you may encounter bootloop after flashing boot. -::: info +:::info Compression format of boot.img 1. You can use magiskboot to get the compression format of your original boot; of course you can also ask other, more experienced kids with the same model as your device. Also, the compression format of the kernel usually does not change, so if you boot successfully with a certain compression format, you can try that format later. 2. Xiaomi devices usually use `gz` or **uncompressed**. 3. For Pixel devices, follow below instructions. @@ -120,7 +211,7 @@ This way requires the kernel flash App to have root permissions. You can use the PS. This method is more convenient when upgrading KernelSU and can be done without a computer (backup first!). . -## Patch boot.img manually +## Patch boot.img manually{#patch-boot-image} For some devices, the boot.img format is not so common, such as not `lz4`, `gz` and uncompressed; the most typical is Pixel, its boot.img format is `lz4_legacy` compressed, ramdisk may be `gz` may also be `lz4_legacy` compression; at this time, if you directly flash the boot.img provided by KernelSU, the phone may not be able to boot; at this time, you can manually patch the boot.img to achieve. diff --git a/website/docs/pt_BR/guide/app-profile.md b/website/docs/pt_BR/guide/app-profile.md index 403d14548154..53e6c3e1bf45 100644 --- a/website/docs/pt_BR/guide/app-profile.md +++ b/website/docs/pt_BR/guide/app-profile.md @@ -2,11 +2,11 @@ O Perfil do Aplicativo é um mecanismo fornecido pelo KernelSU para personalizar a configuração de vários apps. -Para apps com permissões de root (ou seja, capazes de usar `su`), o Perfil do Aplicativo também pode ser chamado de Perfil Root. Ele permite a customização das regras `uid`, `gid`, `groups`, `capabilities` e `SELinux` do comando `su`, restringindo assim os privilégios do usuário root. Por exemplo, ele pode conceder permissões de rede apenas para apps de firewall enquanto nega permissões de acesso a arquivos, ou pode conceder permissões de shell em vez de acesso root completo para apps congelados: **mantendo o poder confinado com o princípio do menor privilégio.** +Para apps com privilégios root (ou seja, capazes de usar `su`), o Perfil do Aplicativo também pode ser chamado de Perfil root. Ele permite a customização das regras `uid`, `gid`, `grupos`, `capacidades` e `SELinux` do comando `su`, restringindo assim os privilégios do usuário root. Por exemplo, ele pode conceder permissões de rede apenas para apps de firewall enquanto nega permissões de acesso a arquivos, ou pode conceder permissões de shell em vez de acesso root completo para apps congelados: **mantendo o poder confinado com o princípio do menor privilégio.** -Para apps comuns sem permissões de root, o Perfil do Aplicativo pode controlar o comportamento do kernel e do sistema de módulos em relação a esses apps. Por exemplo, pode determinar se as modificações resultantes dos módulos devem ser abordadas. O kernel e o sistema de módulos podem tomar decisões com base nesta configuração, como realizar operações semelhantes a "ocultar" +Para apps comuns sem privilégios root, o Perfil do Aplicativo pode controlar o comportamento do kernel e do sistema de módulos em relação a esses apps. Por exemplo, pode determinar se as modificações resultantes dos módulos devem ser abordadas. O kernel e o sistema de módulos podem tomar decisões com base nesta configuração, como realizar operações semelhantes a "ocultar". -## Perfil Root +## Perfil root ### UID, GID e Grupos @@ -31,13 +31,13 @@ uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(ad Aqui, o UID é `2000` e o GID (ID do grupo primário) também é `2000`. Além disso, pertence a vários grupos suplementares, como `inet` (indicando a capacidade de criar soquetes `AF_INET` e `AF_INET6`) e `sdcard_rw` (indicando permissões de leitura/gravação para o cartão SD). -O Perfil Root do KernelSU permite a personalização do UID, GID e grupos para o processo root após a execução de `su`. Por exemplo, o Perfil Root de um app root pode definir seu UID como `2000`, que significa que ao usar `su`, as permissões reais do app estão no nível do ADB shell. O grupo `inet` pode ser removido, evitando que o comando `su` acesse a rede. +O Perfil root do KernelSU permite a personalização do UID, GID e grupos para o processo root após a execução de `su`. Por exemplo, o Perfil root de um app root pode definir seu UID como `2000`, que significa que ao usar `su`, as permissões reais do app estão no nível do ADB shell. O grupo `inet` pode ser removido, evitando que o comando `su` acesse a rede. :::tip OBSERVAÇÃO O Perfil do Aplicativo controla apenas as permissões do processo root após usar `su`, e ele não controla as permissões do próprio app. Se um app solicitou permissão de acesso à rede, ele ainda poderá acessar a rede mesmo sem usar `su`. Remover o grupo `inet` de `su` apenas impede que `su` acesse a rede. ::: -O Perfil Root é aplicado no kernel e não depende do comportamento voluntário de apps root, ao contrário da troca de usuários ou grupos por meio de `su` A concessão da permissão `su` depende inteiramente do usuário e não do desenvolvedor. +O Perfil root é aplicado no kernel e não depende do comportamento voluntário de apps root, ao contrário da troca de usuários ou grupos por meio de `su`. A concessão da permissão `su` depende inteiramente do usuário e não do desenvolvedor. ### Capacidades @@ -49,10 +49,10 @@ A partir do Linux 2.2, o Linux divide os privilégios tradicionalmente associado Cada capacidade representa um ou mais privilégios. Por exemplo, `CAP_DAC_READ_SEARCH` representa a capacidade de ignorar verificações de permissão para leitura de arquivos, bem como permissões de leitura e execução de diretório. Se um usuário com um UID efetivo `0` (usuário root) não tiver recursos `CAP_DAC_READ_SEARCH` ou superiores, isso significa que mesmo sendo root, ele não pode ler arquivos à vontade. -O Perfil Root do KernelSU permite a personalização das capacidades do processo root após a execução de `su`, conseguindo assim conceder parcialmente "permissões de root". Ao contrário do UID e GID mencionados acima, certos apps root exigem um UID de `0` após usar `su`. Nesses casos, limitar as capacidades deste usuário root com UID `0` pode restringir suas operações permitidas. +O Perfil root do KernelSU permite a personalização das capacidades do processo root após a execução de `su`, conseguindo assim conceder parcialmente "privilégios root". Ao contrário do UID e GID mencionados acima, certos apps root exigem um UID de `0` após usar `su`. Nesses casos, limitar as capacidades deste usuário root com UID `0` pode restringir suas operações permitidas. :::tip FORTE RECOMENDAÇÃO -A [documentação oficial](https://man7.org/linux/man-pages/man7/capabilities.7.html) da Capacidade do Linux fornece explicações detalhadas das habilidades representadas por cada Capacidade. Se você pretende customizar Capacidades, é altamente recomendável que você leia este documento primeiro. +A [documentação oficial](https://man7.org/linux/man-pages/man7/capabilities.7.html) da capacidade do Linux fornece explicações detalhadas das habilidades representadas por cada capacidade. Se você pretende customizar as capacidade, é altamente recomendável que você leia este documento primeiro. ::: ### SELinux @@ -74,9 +74,9 @@ Explicar o conceito completo do SELinux é complexo e está além do objetivo de 2. [Red Hat: O que é SELinux?](https://www.redhat.com/pt-br/topics/linux/what-is-selinux) 3. [ArchLinux: SELinux](https://wiki.archlinux.org/title/SELinux) -O Perfil Root do KernelSU permite a personalização do contexto SELinux do processo root após a execução de `su`. Regras específicas de controle de acesso podem ser definidas para este contexto para permitir um controle refinado sobre as permissões de root. +O Perfil root do KernelSU permite a personalização do contexto SELinux do processo root após a execução de `su`. Regras específicas de controle de acesso podem ser definidas para este contexto para permitir um controle refinado sobre os privilégios root. -Em cenários típicos, quando um app executa `su`, ele alterna o processo para um domínio SELinux com **acesso irrestrito**, como `u:r:su:s0`. Através do Perfil Root, este domínio pode ser mudado para um domínio personalizado, como `u:r:app1:s0`, e uma série de regras podem ser definidas para este domínio: +Em cenários típicos, quando um app executa `su`, ele alterna o processo para um domínio SELinux com **acesso irrestrito**, como `u:r:su:s0`. Através do Perfil root, este domínio pode ser mudado para um domínio personalizado, como `u:r:app1:s0`, e uma série de regras podem ser definidas para este domínio: ```sh type app1 @@ -89,26 +89,26 @@ Observe que a regra `allow app1 * * *` é usada apenas para fins de demonstraç ### Escalação -Se a configuração do Perfil Root não estiver definida corretamente, poderá ocorrer um cenário de escalação. As restrições impostas pelo Perfil Root poderão falhar involuntariamente. +Se a configuração do Perfil root não estiver definida corretamente, poderá ocorrer um cenário de escalação. As restrições impostas pelo Perfil root poderão falhar involuntariamente. -Por exemplo, se você conceder permissão root a um usuário ADB shell (que é um caso comum) e, em seguida, conceder permissão root a um app normal, mas configurar seu Perfil Root com UID 2000 (que é o UID do usuário ADB shell), o app pode obter acesso root completo executando o comando `su` duas vezes: +Por exemplo, se você conceder permissão root a um usuário ADB shell (que é um caso comum) e, em seguida, conceder permissão root a um app normal, mas configurar seu Perfil root com UID 2000 (que é o UID do usuário ADB shell), o app pode obter acesso root completo executando o comando `su` duas vezes: 1. A primeira execução `su` está sujeita à aplicação do Perfil do Aplicativo e mudará para UID `2000` (ADB shell) em vez de `0` (root). -2. A segunda execução `su`, como o UID é `2000` e você concedeu acesso root ao UID `2000` (ADB shell) na configuração, o app obterá privilégio de root completo. +2. A segunda execução `su`, como o UID é `2000` e você concedeu acesso root ao UID `2000` (ADB shell) na configuração, o app obterá privilégios root completo. :::warning OBSERVAÇÃO Este comportamento é totalmente esperado e não é um bug. Portanto, recomendamos o seguinte: -Se você realmente precisa conceder permissões de root ao ADB (por exemplo, como desenvolvedor), não é aconselhável alterar o UID para `2000` ao configurar o Perfil Root. Usar `1000` (system) seria uma melhor escolha. +Se você realmente precisa conceder privilégios root ao ADB (por exemplo, como desenvolvedor), não é aconselhável alterar o UID para `2000` ao configurar o Perfil root. Usar `1000` (system) seria uma melhor escolha. ::: -## Perfil não Root +## Perfil não root ### Desmontar módulos O KernelSU fornece um mecanismo sem sistema para modificar partições do sistema, obtido através da montagem de OverlayFS. No entanto, alguns apps podem ser sensíveis a esse comportamento. Assim, podemos descarregar módulos montados nesses apps configurando a opção "Desmontar módulos". -Além disso, a interface de configurações do gerenciador KernelSU fornece uma opção para "Desmontar módulos por padrão". Por padrão, essa opção está **ativada**, o que significa que o KernelSU ou alguns módulos descarregarão módulos para este app, a menos que configurações adicionais sejam aplicadas. Se você não preferir esta configuração ou se ela afetar determinados apps, você terá as seguintes opções: +Além disso, a interface de configurações do gerenciador do KernelSU fornece uma opção para "Desmontar módulos por padrão". Por padrão, essa opção está **ativada**, o que significa que o KernelSU ou alguns módulos descarregarão módulos para este app, a menos que configurações adicionais sejam aplicadas. Se você não preferir esta configuração ou se ela afetar determinados apps, você terá as seguintes opções: 1. Mantenha a opção "Desmontar módulos por padrão" e desative individualmente a opção "Desmontar módulos" no Perfil do Aplicativo para apps que exigem carregamento do módulo (agindo como uma "lista de permissões"). 2. Desative a opção "Desmontar módulos por padrão" e ative individualmente a opção "Desmontar módulos" no Perfil do Aplicativo para apps que exigem descarregamento do módulo (agindo como uma "lista negra"). diff --git a/website/docs/pt_BR/guide/faq.md b/website/docs/pt_BR/guide/faq.md index 8d61df159907..43c35d06cb5f 100644 --- a/website/docs/pt_BR/guide/faq.md +++ b/website/docs/pt_BR/guide/faq.md @@ -4,7 +4,7 @@ Primeiro, seu dispositivo deve ser capaz de desbloquear o bootloader. Se não, então não há suporte. -Em seguida, instale o app gerenciador do KernelSU em seu dispositivo e abra-o, se mostrar `Sem suporte` então seu dispositivo não pode ser suportado imediatamente, mas você pode construir a fonte do kernel e integrar o KernelSU para fazê-lo funcionar ou usar [Dispositivos com suporte não oficial](unofficially-support-devices). +Em seguida, instale o gerenciador do KernelSU em seu dispositivo e abra-o, se mostrar `Sem suporte` então seu dispositivo não pode ser suportado imediatamente, mas você pode compilar a fonte do kernel e integrar o KernelSU para fazê-lo funcionar ou usar [Dispositivos com suporte não oficial](unofficially-support-devices). ## Para usar o KernelSU precisa desbloquear o bootloader? @@ -12,7 +12,7 @@ Certamente, sim. ## KernelSU suporta módulos? -Sim, verifique [Guias de módulo](module.md) por favor. +Sim, verifique [Guias de módulo](module.md). ## KernelSU suporta Xposed? @@ -24,7 +24,7 @@ KernelSU não tem suporte integrado ao Zygisk, mas você pode usar [ZygiskNext]( ## KernelSU é compatível com o Magisk? -O sistema de módulos do KernelSU está em conflito com a montagem mágica do Magisk, se houver algum módulo habilitado no KernelSU, então todo o Magisk não funcionaria. +O sistema de módulos do KernelSU está em conflito com a montagem mágica do Magisk, se houver algum módulo ativado no KernelSU, então todo o Magisk não funcionaria. Mas se você usar apenas o `su` do KernelSU, então funcionará bem com o Magisk. KernelSU modifica o `kernel` e o Magisk modifica o `ramdisk`, eles podem trabalhar juntos. @@ -41,11 +41,11 @@ Achamos que não e esse não é o nosso objetivo. O Magisk é bom o suficiente p É o kernel do dispositivo que afeta a compatibilidade do KernelSU e não tem nada a ver com a versão do Android. A única restrição é que os dispositivos lançados com Android 12 devem ser kernel 5.10+ (dispositivos GKI). Então: 1. Os dispositivos lançados com Android 12 devem ser compatíveis. -2. Dispositivos com kernel antigo (alguns dispositivos Android 12 também têm o kernel antigo) são compatíveis (você mesmo deve construir o kernel). +2. Dispositivos com kernel antigo (alguns dispositivos com Android 12 também têm o kernel antigo) são compatíveis (você mesmo deve compilar o kernel). ## KernelSU suporta kernel antigo? -É possível, o KernelSU é portado para o kernel 4.14 agora, para o kernel mais antigo, você precisa fazer o backport manualmente e PRs são sempre bem-vindas! +É possível, o KernelSU é portado para o kernel 4.14 agora, para o kernel mais antigo, você precisa portar manualmente e PRs são sempre bem-vindas! ## Como integrar o KernelSU para o kernel antigo? @@ -55,13 +55,13 @@ Por favor, consulte a guia [Como integrar o KernelSU para kernels não GKI](how- A versão do Kernel não tem nada a ver com a versão do Android, se você precisar fazer o flash do kernel, use sempre a versão do kernel, a versão do Android não é tão importante. -## Eu sou GKI1.0, posso usar isso? +## Eu sou GKI 1.0, posso usar isso? -GKI1 é completamente diferente do GKI2, você deve compilar o kernel sozinho. +GKI 1.0 é completamente diferente do GKI 2.0, você deve compilar o kernel sozinho. ## Como posso fazer `/system` RW? -Não recomendamos que você modifique a partição do sistema diretamente. Você deve usar [Guias de módulo](module.md) para modificá-lo sem sistema. Se você insiste em fazer isso, verifique [magisk_overlayfs](https://github.com/HuskyDG/magic_overlayfs). +Não recomendamos que você modifique a partição do sistema diretamente. Por favor, verifique [Guias de módulo](module.md) para modificá-lo sem sistema. Se você insiste em fazer isso, verifique [magisk_overlayfs](https://github.com/HuskyDG/magic_overlayfs). ## KernelSU pode modificar hosts? Como posso usar AdAway? @@ -73,6 +73,6 @@ O arquivo `modules.img` de 1 TB é um arquivo de imagem de disco, **não se preo Se você estiver realmente insatisfeito com o tamanho deste arquivo, você pode usar o comando `resize2fs -M` para torná-lo seu tamanho real, mas o módulo pode não funcionar corretamente neste momento e não forneceremos nenhum suporte para isso. -## Por que meu dispositivo mostra o tamanho do armazenamento errado? +## Por que meu dispositivo mostra o tamanho de armazenamento errado? -Certos dispositivos usam métodos não padrão para calcular o tamanho do armazenamento do dispositivo, potencialmente levando a cálculos de armazenamento imprecisos em apps e menus do sistema, especialmente ao lidar com arquivos esparsos de 1 TB. Embora esse problema pareça ser específico para os dispositivos Samsung, afetando apenas apps e serviços da Samsung, é essencial observar que a discrepância está principalmente no tamanho total do armazenamento, e o cálculo do espaço livre permanece preciso. +Certos dispositivos usam métodos não padrão para calcular o tamanho de armazenamento do dispositivo, potencialmente levando a cálculos de armazenamento imprecisos em apps e menus do sistema, especialmente ao lidar com arquivos esparsos de 1 TB. Embora esse problema pareça ser específico para os dispositivos Samsung, afetando apenas apps e serviços da Samsung, é essencial observar que a discrepância está principalmente no tamanho total do armazenamento, e o cálculo do espaço livre permanece preciso. diff --git a/website/docs/pt_BR/guide/how-to-build.md b/website/docs/pt_BR/guide/how-to-build.md index 669a43bf4021..5f4402cd65b6 100644 --- a/website/docs/pt_BR/guide/how-to-build.md +++ b/website/docs/pt_BR/guide/how-to-build.md @@ -1,6 +1,6 @@ -# Como construir o KernelSU? +# Como compilar o KernelSU? -Primeiro, você deve ler a documentação oficial do Android para construção do kernel: +Primeiro, você deve ler a documentação oficial do Android para compilação do kernel: 1. [Como criar kernels](https://source.android.com/docs/setup/build/building-kernels) 2. [Builds de versão de imagem genérica do kernel (GKI)](https://source.android.com/docs/core/architecture/kernel/gki-release-builds) @@ -9,7 +9,7 @@ Primeiro, você deve ler a documentação oficial do Android para construção d Esta página é para dispositivos GKI, se você usa um kernel antigo, consulte [Como integrar o KernelSU para kernels não GKI](how-to-integrate-for-non-gki). ::: -## Construir o kernel +## Compilar o kernel ### Sincronize o código-fonte do kernel @@ -20,13 +20,13 @@ repo init -m manifest.xml repo sync ``` -O `` é um arquivo de manifesto que pode determinar uma construção exclusivamente, você pode usar o manifesto para fazer uma construção re-preduzível. Você deve baixar o arquivo de manifesto em [Builds de versão de imagem genérica do kernel (GKI)](https://source.android.com/docs/core/architecture/kernel/gki-release-builds). +O `` é um arquivo de manifesto que pode determinar uma compilação exclusivamente, você pode usar o manifesto para fazer uma compilação re-preduzível. Você deve baixar o arquivo de manifesto em [Builds de versão de imagem genérica do kernel (GKI)](https://source.android.com/docs/core/architecture/kernel/gki-release-builds). ### Construir Por favor, verifique [Como criar kernels](https://source.android.com/docs/setup/build/building-kernels) primeiro. -Por exemplo, precisamos construir a imagem do kernel `aarch64`: +Por exemplo, precisamos compilar a imagem do kernel `aarch64`: ```sh LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh @@ -34,23 +34,23 @@ LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh Não se esqueça de adicionar o sinalizador `LTO=thin`, caso contrário a compilação poderá falhar se a memória do seu computador for inferior a 24 GB. -A partir do Android 13, o kernel é construído pelo `bazel`: +A partir do Android 13, o kernel é compilado pelo `bazel`: ```sh tools/bazel build --config=fast //common:kernel_aarch64_dist ``` :::info INFORMAÇÕES -Para alguns kernel do Android 14, para fazer o Wi-Fi/Bluetooth funcionar. Pode ser necessário remover todas as exportações protegidas por GKI: +Para alguns kernel do Android 14, para fazer o Wi-Fi/Bluetooth funcionar. Pode ser necessário remover todas as exportações protegidas pelo GKI: ```sh rm common/android/abi_gki_protected_exports_* ``` ::: -## Construir o kernel com KernelSU +## Compilar o kernel com KernelSU -Se você conseguir construir o kernel com sucesso, então construir o KernelSU é muito fácil. Selecione qualquer um executado no diretório raiz de origem do kernel: +Se você conseguir compilar o kernel com sucesso, então compilar o KernelSU é muito fácil. Selecione qualquer um executado no diretório raiz de origem do kernel: ::: code-group diff --git a/website/docs/pt_BR/guide/how-to-integrate-for-non-gki.md b/website/docs/pt_BR/guide/how-to-integrate-for-non-gki.md index cf0be1a07c0e..b6944c57e678 100644 --- a/website/docs/pt_BR/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/pt_BR/guide/how-to-integrate-for-non-gki.md @@ -2,11 +2,11 @@ O KernelSU pode ser integrado em kernels não GKI e foi portado para 4.14 e versões anteriores. -Devido à fragmentação de kernels não GKI, não temos uma maneira uniforme de construí-lo, portanto não podemos fornecer boot.img não GKI. Mas você mesmo pode construir o kernel com o KernelSU integrado. +Devido à fragmentação de kernels não GKI, não temos uma maneira uniforme de construí-lo, portanto não podemos fornecer o boot.img não GKI. Mas você mesmo pode compilar o kernel com o KernelSU integrado. -Primeiro, você deve ser capaz de construir um kernel inicializável a partir do código-fonte do kernel. Se o kernel não for de código aberto, será difícil executar o KernelSU no seu dispositivo. +Primeiro, você deve ser capaz de compilar um kernel inicializável a partir do código-fonte do kernel. Se o kernel não for de código aberto, será difícil executar o KernelSU no seu dispositivo. -Se você puder construir um kernel inicializável, existem duas maneiras de integrar o KernelSU ao código-fonte do kernel: +Se você puder compilar um kernel inicializável, existem duas maneiras de integrar o KernelSU ao código-fonte do kernel: 1. Automaticamente com `kprobe` 2. Manualmente @@ -297,9 +297,9 @@ index 45306f9ef247..815091ebfca4 100755 add_input_randomness(type, code, value); ``` -### Como fazer backport de path_umount +### Como portar path_umount -Você pode fazer com que o recurso "Desmontar módulos" funcione em kernels pré-GKI fazendo backport manualmente do `path_umount` da versão 5.9. Você pode usar este patch como referência: +Você pode fazer com que o recurso "Desmontar módulos" funcione em kernels pré-GKI portando manualmente `path_umount` da versão 5.9. Você pode usar este patch como referência: ```diff --- a/fs/namespace.c diff --git a/website/docs/pt_BR/guide/installation.md b/website/docs/pt_BR/guide/installation.md index 4dc83520d09b..0223581c48f8 100644 --- a/website/docs/pt_BR/guide/installation.md +++ b/website/docs/pt_BR/guide/installation.md @@ -2,9 +2,9 @@ ## Verifique se o seu dispositivo é compatível -Baixe o app gerenciador do KernelSU em [GitHub Releases](https://github.com/tiann/KernelSU/releases), e instale-o no seu dispositivo: +Baixe o gerenciador do KernelSU em [GitHub Releases](https://github.com/tiann/KernelSU/releases), e instale-o no seu dispositivo: -- Se o app mostrar `Sem suporte`, significa que **você deve compilar o kernel sozinho**. O KernelSU não fornecerá e nunca fornecerá uma boot.img para você instalar. +- Se o app mostrar `Sem suporte`, significa que **você deve compilar o kernel sozinho**. O KernelSU não fornecerá e nunca fornecerá um boot.img para você instalar. - Se o app mostrar `Não instalado`, então seu dispositivo é oficialmente suportado pelo KernelSU. ::: info INFORMAÇÕES @@ -45,9 +45,9 @@ Observe que o SubLevel na versão do kernel não faz parte do KMI! Isso signific ### Nível do patch de segurança {#security-patch-level} -Dispositivos Android mais recentes podem ter mecanismos anti-rollback que não permitem flashar uma boot.img com um nível de patch de segurança antigo. Por exemplo, se o kernel do seu dispositivo for `5.10.101-android12-9-g30979850fc20`, o patch de segurança será `2023-11`, mesmo se você atualizar o kernel consistente com o KMI do kernel, se o nível do patch de segurança for anterior a `2023-11` (como `2023-06`), então isso pode causar bootloop. +Dispositivos Android mais recentes podem ter mecanismos anti-rollback que não permitem flashar um boot.img com um nível do patch de segurança antigo. Por exemplo, se o kernel do seu dispositivo for `5.10.101-android12-9-g30979850fc20`, o patch de segurança será `2023-11`, mesmo se você atualizar o kernel consistente com o KMI do kernel, se o nível do patch de segurança for anterior a `2023-11` (como `2023-06`), então isso pode causar bootloop. -Portanto, os kernels com os níveis de patch de segurança mais recentes são preferidos, mantendo a consistência do KMI. +Portanto, os kernels com os níveis do patch de segurança mais recentes são preferidos, mantendo a consistência do KMI. ### Versão do kernel vs Versão do Android @@ -57,14 +57,105 @@ Se você descobrir que a versão do seu kernel é `android12-5.10.101`, mas a ve ## Introdução -Existem vários métodos de instalação do KernelSU, cada um adequado para um cenário diferente, portanto escolha conforme necessário. +Desde a versão [0.9.0](https://github.com/tiann/KernelSU/releases/tag/v0.9.0), KernelSU suporta dois modos de execução em dispositivos GKI: -1. Instalar com fastboot usando o boot.img fornecido por KernelSU -2. Instalar com um app kernel flash, como KernelFlasher -3. Repare o boot.img manualmente e instale-o +1. `GKI`: Substitua o kernel original do dispositivo pelo **Generic Kernel Image** (GKI) fornecido pelo KernelSU. +2. `LKM`: Carregue o **Loadable Kernel Module** (LKM) no kernel do dispositivo sem substituir o kernel original. + +Esses dois modos são adequados para diferentes cenários e você pode escolher de acordo com suas necessidades. + +### Modo GKI {#gki-mode} + +No modo GKI, o kernel original do dispositivo será substituído pela imagem genérica do kernel fornecida pelo KernelSU. As vantagens do modo GKI são: + +1. Forte universalidade, adequada para a maioria dos dispositivos. Por exemplo, a Samsung ativou dispositivos KNOX e o modo LKM não pode funcionar. Existem também alguns dispositivos modificados de nicho que só podem usar o modo GKI. +2. Pode ser usado sem depender de firmware oficial e não há necessidade de esperar por atualizações oficiais de firmware, desde que o KMI seja consistente, ele pode ser usado. + +### Modo LKM {#lkm-mode} + +No modo LKM, o kernel original do dispositivo não será substituído, mas o módulo do kernel carregável será carregado no kernel do dispositivo. As vantagens do modo LKM são: + +1. Não substituirá o kernel original do dispositivo. Se você tiver os requisitos especiais para o kernel original do dispositivo ou quiser usar o KernelSU enquanto usa um kernel de terceiros, poderá usar o modo LKM. +2. É mais conveniente atualizar o OTA. Ao atualizar o KernelSU, você pode instalá-lo diretamente no gerenciador sem atualizar manualmente. Após o sistema OTA, você pode instalá-lo diretamente no segundo slot sem flashar manualmente. +3. Adequado para alguns cenários especiais, por exemplo, o LKM também pode ser carregado com privilégios root temporários. Como não é necessário substituir a partição boot, ele não acionará o AVB e não causará o bloqueio do dispositivo. +4. O LKM pode ser desinstalado temporariamente. Se você deseja cancelar temporariamente o root, você pode desinstalar o LKM, este processo não requer o flash de partições, nem mesmo a reinicialização do dispositivo. Se quiser fazer root novamente, basta reiniciar o dispositivo. + +:::tip COEXISTÊNCIA DE DOIS MODOS +Após abrir o gerenciador, você pode ver o modo atual do dispositivo na página inicial. Observe que a prioridade do modo GKI é maior que a do LKM. Por exemplo, se você usar o kernel GKI para substituir o kernel original e usar LKM para corrigir o kernel GKI, o LKM será ignorado e o dispositivo sempre será executado no modo GKI. +::: + +### Qual escolher? {#which-one} + +Se o seu aparelho for um celular, recomendamos que você priorize o modo LKM. Se o seu dispositivo for um emulador, WSA ou Waydroid, recomendamos que você priorize o modo GKI. + +## Instalação do LKM + +### Obtenha o firmware oficial + +Para usar o modo LKM, você precisa obter o firmware oficial e corrigi-lo com base no firmware oficial. Se você usar um kernel de terceiros, poderá usar o `boot.img` do kernel de terceiros como firmware oficial. + +Existem muitas maneiras de obter o firmware oficial. Se o seu dispositivo suportar `fastboot boot`, então recomendamos **o método mais recomendado e o mais simples** que é usar `fastboot boot` para inicializar temporariamente o kernel GKI fornecido pelo KernelSU, depois instalar o gerenciador e, finalmente, instalá-lo diretamente no gerenciador. Este método não exige que você baixe manualmente o firmware oficial, nem extraia manualmente o boot. + +Se o seu dispositivo não suportar `fastboot boot`, pode ser necessário baixar manualmente o pacote de firmware oficial e extrair o boot dele. + +Ao contrário do modo GKI, o modo LKM modificará o `ramdisk`, portanto, em dispositivos com Android 13, ele precisa corrigir a partição `init_boot` em vez da partição `boot`, enquanto o modo GKI sempre opera a partição `boot`. + +### Use o gerenciador + +Abra o gerenciador, clique no ícone de instalação no canto superior direito e diversas opções aparecerão: + +1. Selecione e corrija um arquivo. Se o seu telefone não tiver privilégios root, você pode escolher esta opção e, em seguida, selecionar seu firmware oficial, e o gerenciador irá corrigi-lo automaticamente. Você só precisa flashar este arquivo corrigido para obter privilégios root permanentemente. +2. Instale diretamente. Se o seu telefone já estiver rooteado, você pode escolher esta opção, o gerenciador obterá automaticamente as informações do seu dispositivo e, em seguida, corrigirá o firmware oficial e irá fazer o flash automaticamente. Você pode considerar usar `fastboot boot` e o kernel GKI do KernelSU para obter root temporário e instalar o gerenciador, e então usar esta opção. Esta também é a principal forma de atualizar o KernelSU. +3. Instale em outra partição. Se o seu dispositivo suportar partição A/B, você pode escolher esta opção, o gerenciador irá corrigir automaticamente o firmware oficial e, em seguida, instalá-lo em outra partição. Este método é adequado para dispositivos após o OTA, você pode instalá-lo diretamente em outra partição após o OTA e, em seguida, reiniciar o dispositivo. + +### Use a linha de comando + +Se não quiser usar o gerenciador, você também pode usar a linha de comando para instalar o LKM. A ferramenta `ksud` fornecida pelo KernelSU pode ajudá-lo a corrigir rapidamente o firmware oficial e depois fazer o flash. + +Esta ferramenta oferece suporte ao macOS, Linux e Windows. Você pode baixar a versão correspondente em [GitHub Release](https://github.com/tiann/KernelSU/releases). + +Uso: `ksud boot-patch` você pode verificar a ajuda da linha de comando para o uso específico. + +```sh +oriole:/ # ksud boot-patch -h +Patch boot ou imagens init_boot para aplicar o KernelSU + +Uso: ksud boot-patch [OPTIONS] + +Opções: + -b, --boot Caminho da imagem boot, se não for especificado, tentará encontrar a imagem boot automaticamente + -k, --kernel Caminho da imagem do kernel para substituir + -m, --module O caminho do módulo LKM a ser substituído, se não for especificado, usará o integrado + -i, --init init a ser substituído + -u, --ota Usará outro slot quando a imagem boot não for especificada + -f, --flash Flash para a partição boot após o patch + -o, --out Caminho de saída, se não for especificado, usará o diretório atual + --magiskboot Caminho do magiskboot, se não for especificado, usará um integrado + --kmi A versão do KMI, se especificada, usará o KMI especificado + -h, --help Imprimir ajuda +``` + +Algumas opções que precisam ser explicadas: + +1. A opção `--magiskboot` pode especificar o caminho do magiskboot. Se não for especificado, o ksud irá procurá-lo nas variáveis ​​de ambiente. Se você não sabe como obter o magiskboot, você pode verificar [aqui](#patch-boot-image). +2. A opção `--kmi` pode especificar a versão do `KMI`. Se o nome do kernel do seu dispositivo não seguir a especificação KMI, você poderá especificá-lo através desta opção. + +O uso mais comum é: + +```sh +ksud boot-patch -b --kmi android13-5.10 +``` + +## Instalação no modo GKI + +Existem vários métodos de instalação para o modo GKI, cada um adequado para um cenário diferente, portanto escolha conforme necessário. + +1. Instalar com fastboot usando o boot.img fornecido pelo KernelSU +2. Instalar com um app kernel flash, como o Kernel Flasher +3. Corrigir boot.img manualmente e instala-lo 4. Instalar com Recovery personalizado (por exemplo, TWRP) -## Instalar com o boot.img fornecido por KernelSU +## Instalar com o boot.img fornecido pelo KernelSU Se o `boot.img` do seu dispositivo usa um formato de compactação comumente usado, você pode usar as imagens GKI fornecidas pelo KernelSU para atualizá-lo diretamente. Não requer TWRP ou autocorreção da imagem. @@ -76,13 +167,13 @@ Você pode baixar o boot.img em [GitHub Release](https://github.com/tiann/Kernel Normalmente, existem três arquivos de inicialização em formatos diferentes no mesmo KMI e nível do patch de segurança. Eles são todos iguais, exceto pelo formato de compactação do kernel. Por favor, verifique o formato de compactação do kernel de seu boot.img original. Você deve usar o formato correto, como `lz4` ou `gz`. Se você usar um formato de compactação incorreto, poderá encontrar bootloop após flashar o boot.img. -::: info INFORMAÇÕES +::: info FORMATO DE COMPACTAÇÃO DO BOOT.IMG 1. Você pode usar o magiskboot para obter o formato de compactação de seu boot original; é claro que você também pode perguntar a outras pessoas mais experientes com o mesmo modelo do seu dispositivo. Além disso, o formato de compactação do kernel geralmente não muda, portanto, se você inicializar com êxito com um determinado formato de compactação, poderá tentar esse formato mais tarde. 2. Os dispositivos Xiaomi geralmente usam `gz` ou `uncompressed`. 3. Para dispositivos Pixel, siga as instruções abaixo: ::: -### Flash boot.img para o dispositivo +### Flash o boot.img para o dispositivo Use o `adb` para conectar seu dispositivo, execute `adb reboot bootloader` para entrar no modo fastboot e use este comando para flashar o KernelSU: @@ -106,23 +197,23 @@ fastboot reboot Etapa: -1. Baixe o zip AnyKernel3. Se você não sabe qual arquivo baixar, leia atentamente a descrição do [KMI](#kmi) e [Nível do patch de segurança](#security-patch-level) neste documento. +1. Baixe o ZIP AnyKernel3. Se você não sabe qual arquivo baixar, leia atentamente a descrição do [KMI](#kmi) e [Nível do patch de segurança](#security-patch-level) neste documento. 2. Abra o app Kernel Flasher (conceda as permissões de root necessárias) e use o ZIP AnyKernel3 fornecido para fazer o flash. -Dessa forma, é necessário que o app Kernel Flasher tenha permissões root. Você pode usar os seguintes métodos para conseguir isso: +Dessa forma, é necessário que o app Kernel Flasher tenha privilégios root. Você pode usar os seguintes métodos para conseguir isso: 1. Seu dispositivo está rooteado. Por exemplo, você instalou o KernelSU e deseja atualizar para a versão mais recente ou fez o root por meio de outros métodos (como Magisk). -2. Se o seu telefone não estiver rooteado, mas o telefone suportar o método de inicialização temporária como `fastboot boot boot.img`, você pode usar a imagem GKI fornecida pelo KernelSU para inicializar temporariamente o seu dispositivo, obter permissões de root temporária e, em seguida, usar o Kernel Flasher para obter privilégios de root permanente. +2. Se o seu telefone não estiver rooteado, mas o telefone suportar o método de inicialização temporária como `fastboot boot boot.img`, você pode usar a imagem GKI fornecida pelo KernelSU para inicializar temporariamente o seu dispositivo, obter privilégios root temporário e, em seguida, usar o Kernel Flasher para obter privilégios root permanente. 1. [Kernel Flasher](https://github.com/capntrips/KernelFlasher/releases) 2. [Franco Kernel Manager](https://play.google.com/store/apps/details?id=com.franco.kernel) 3. [Ex Kernel Manager](https://play.google.com/store/apps/details?id=flar2.exkernelmanager) -PS. Este método é mais conveniente ao atualizar o KernelSU e pode ser feito sem um computador (faça um backup primeiro). +Observação: Este método é mais conveniente ao atualizar o KernelSU e pode ser feito sem um computador (faça um backup primeiro). -## Corrigir boot.img manualmente +## Corrigir boot.img manualmente {#patch-boot-image} -Para alguns dispositivos, o formato boot.img não é tão comum como `lz4`, `gz` e `uncompressed`. O mais típico é o Pixel, seu formato boot.img é `lz4_legacy` compactado, ramdisk pode ser `gz` também pode ser compactado `lz4_legacy`. Neste momento, se você flashar diretamente o boot.img fornecido pelo KernelSU, o telefone pode não conseguir inicializar. Neste momento, você pode corrigir manualmente o boot.img para conseguir isso. +Para alguns dispositivos, o formato boot.img não é tão comum como `lz4`, `gz` e `uncompressed`. O mais típico é o Pixel, seu formato boot.img é `lz4_legacy` compactado, ramdisk pode ser `gz` e também pode ser compactado `lz4_legacy`. Neste momento, se você flashar diretamente o boot.img fornecido pelo KernelSU, o telefone pode não conseguir inicializar. Neste momento, você pode corrigir manualmente o boot.img para conseguir isso. É sempre recomendado usar `magiskboot` para corrigir imagens, existem duas maneiras: @@ -137,7 +228,7 @@ Android-Image-Kitchen não é recomendado agora, porque ele não lida corretamen ### Preparação -1. Obtenha o boot.img padrão do telefone. Você pode obtê-lo com os fabricantes do seu dispositivo Talvez você precise do [payload-dumper-go](https://github.com/ssut/payload-dumper-go). +1. Obtenha o boot.img padrão do telefone. Você pode obtê-lo com os fabricantes do seu dispositivo. Talvez você precise do [payload-dumper-go](https://github.com/ssut/payload-dumper-go). 2. Baixe o arquivo ZIP AnyKernel3 fornecido pelo KernelSU que corresponde à versão KMI do seu dispositivo. Você pode consultar [Instalar com Recovery personalizado](#install-with-custom-recovery). 3. Descompacte o pacote AnyKernel3 e obtenha o arquivo `Image`, que é o arquivo do kernel do KernelSU. @@ -155,7 +246,7 @@ Android-Image-Kitchen não é recomendado agora, porque ele não lida corretamen ### Usando o magiskboot no PC Windows/macOS/Linux {#using-magiskboot-on-PC} 1. Baixe o `magiskboot` adequado para o seu sistema operacional em [magiskboot_build](https://github.com/ookiineko/magiskboot_build/releases/tag/last-ci). -2. Prepare o boot.img padrão e Image em seu PC. +2. Prepare o `boot.img` padrão e `Image` em seu PC. 3. `chmod +x magiskboot` 4. Entre no diretório apropriado, execute `./magiskboot unpack boot.img` para descompactar `boot.img`. Você obterá um arquivo `kernel`, este é o seu kernel padrão. 5. Substitua `kernel` por `Image`: `mv -f Image kernel`. @@ -175,13 +266,13 @@ Etapa: 2. Reinicie o telefone no TWRP. 3. Use o ADB para colocar AnyKernel3-*.zip no telefone em /sdcard e escolha instalá-lo na interface do TWRP, ou você pode diretamente `adb sideload AnyKernel-*.zip` para instalar. -PS. Este método é adequado para qualquer instalação (não limitado à instalação inicial ou atualizações subsequentes), desde que você use o TWRP. +Observação: Este método é adequado para qualquer instalação (não limitado à instalação inicial ou atualizações subsequentes), desde que você use o TWRP. ## Outros métodos Na verdade, todos esses métodos de instalação têm apenas uma ideia principal, que é **substituir o kernel original pelo fornecido pelo KernelSU**, desde que isso possa ser alcançado, ele pode ser instalado. Por exemplo, a seguir estão outros métodos possíveis. -1. Primeiro instale o Magisk, obtenha privilégios de root através do Magisk e então use o kernel flasher para fazer o flash no zip AnyKernel do KernelSU. +1. Primeiro instale o Magisk, obtenha privilégios root através do Magisk e então use o Kernel Flasher para fazer o flash no zip AnyKernel do KernelSU. 2. Use algum kit de ferramentas de flash em PCs para flashar no kernel fornecido pelo KernelSU. Mas se não funcionar, por favor, tente o método `magiskboot`. diff --git a/website/docs/pt_BR/guide/module-webui.md b/website/docs/pt_BR/guide/module-webui.md index cf520eaa5eac..fe59f606d462 100644 --- a/website/docs/pt_BR/guide/module-webui.md +++ b/website/docs/pt_BR/guide/module-webui.md @@ -24,7 +24,7 @@ Se sua página contém CSS e JavaScript, você também precisa colocá-la neste ## API JavaScript -Se for apenas uma página de exibição, não será diferente de uma página da web normal. Mais importante ainda, KernelSU fornece uma série de APIs de sistema que permitem implementar as funções exclusivas do módulo. +Se for apenas uma página de exibição, não será diferente de uma página web normal. Mais importante ainda, KernelSU fornece uma série de APIs de sistema que permitem implementar as funções exclusivas do módulo. KernelSU fornece uma biblioteca JavaScript e publica-a no [npm](https://www.npmjs.com/package/kernelsu), que você pode usar no código JavaScript de suas páginas da web. @@ -36,7 +36,7 @@ import { exec } from 'kernelsu'; const { errno, stdout } = exec("getprop ro.product.model"); ``` -Para outro exemplo, você pode fazer com que a página da web seja exibida em tela inteira ou exibir um dica. +Para outro exemplo, você pode fazer com que a página web seja exibida em tela inteira ou exibir um dica. [Documentação da API](https://www.npmjs.com/package/kernelsu) @@ -45,4 +45,4 @@ Se você achar que a API existente não atende às suas necessidades ou é incon ## Algumas dicas 1. Você pode usar `localStorage` normalmente para armazenar alguns dados, mas eles serão perdidos após a desinstalação do app gerenciador. Se precisar salvar persistentemente, você mesmo pode gravar os dados em algum diretório. -2. Para páginas simples, recomendo que você use [parceljs](https://parceljs.org/) para empacotamento. Não requer configuração do zero e é muito conveniente de usar. Porém, se você é um mestre front-end ou tem suas próprias preferências, basta escolher o que você gosta! +2. Para páginas simples, recomendo que você use [parceljs](https://parceljs.org/) para o empacotamento. Não requer configuração do zero e é muito conveniente de usar. Porém, se você é um mestre do front-end ou tem suas próprias preferências, basta escolher o que você gosta! diff --git a/website/docs/pt_BR/guide/module.md b/website/docs/pt_BR/guide/module.md index b25222eb2025..4f23dcececcb 100644 --- a/website/docs/pt_BR/guide/module.md +++ b/website/docs/pt_BR/guide/module.md @@ -2,7 +2,7 @@ O KernelSU fornece um mecanismo de módulo que consegue modificar o diretório do sistema enquanto mantém a integridade da partição do sistema. Este mecanismo é conhecido como "sem sistema". -O mecanismo de módulo do KernelSU é quase o mesmo do Magisk. Se você está familiarizado com o desenvolvimento de módulos Magisk, o desenvolvimento de módulos KernelSU é muito semelhante. Você pode pular a introdução dos módulos abaixo e só precisa ler [Diferença com Magisk](difference-with-magisk.md). +O mecanismo de módulos do KernelSU é quase o mesmo do Magisk. Se você está familiarizado com o desenvolvimento de módulos Magisk, o desenvolvimento de módulos KernelSU é muito semelhante. Você pode pular a introdução dos módulos abaixo e só precisa ler [Diferença com Magisk](difference-with-magisk.md). ## WebUI @@ -10,13 +10,13 @@ Os módulos do KernelSU suportam a exibição de interfaces e a interação com ## BusyBox -O KernelSU vem com um recurso binário BusyBox completo (incluindo suporte completo ao SELinux). O executável está localizado em `/data/adb/ksu/bin/busybox`. O BusyBox do KernelSU suporta o "ASH Standalone Shell Mode" alternável em tempo de execução. O que este Modo Autônomo significa é que ao executar no shell `ash` do BusyBox, cada comando usará diretamente o miniaplicativo dentro do BusyBox, independentemente do que estiver definido como `PATH`. Por exemplo, comandos como `ls`, `rm`, `chmod` **NÃO** usarão o que está em `PATH` (no caso do Android por padrão será `/system/bin/ls`, `/system/bin/rm` e `/system/bin/chmod` respectivamente), mas em vez disso chamará diretamente os miniaplicativos internos do BusyBox. Isso garante que os scripts sempre sejam executados em um ambiente previsível e sempre tenham o conjunto completo de comandos, independentemente da versão do Android em que estão sendo executados. Para forçar um comando a **NÃO** usar o BusyBox, você deve chamar o executável com caminhos completos. +O KernelSU vem com um recurso binário BusyBox completo (incluindo suporte completo ao SELinux). O executável está localizado em `/data/adb/ksu/bin/busybox`. O BusyBox do KernelSU suporta "ASH Standalone Shell Mode" alternável em tempo de execução. O que este Modo Autônomo significa é que ao executar no shell `ash` do BusyBox, cada comando usará diretamente o miniaplicativo dentro do BusyBox, independentemente do que estiver definido em `PATH`. Por exemplo, comandos como `ls`, `rm`, `chmod` **NÃO** usarão o que está em `PATH` (no caso do Android, por padrão será `/system/bin/ls`, `/system/bin/rm` e `/system/bin/chmod` respectivamente), mas em vez disso chamará diretamente os miniaplicativos internos do BusyBox. Isso garante que os scripts sempre sejam executados em um ambiente previsível e sempre tenham o conjunto completo de comandos, independentemente da versão do Android em que estão sendo executados. Para forçar um comando a **NÃO** usar o BusyBox, você deve chamar o executável com caminhos completos. Cada script shell executado no contexto do KernelSU será executado no shell `ash` do BusyBox com o Modo Autônomo ativado. Para o que é relevante para desenvolvedores terceirizados, isso inclui todos os scripts de inicialização e scripts de instalação de módulos. -Para aqueles que desejam usar o recurso “Modo Autônomo” fora do KernelSU, existem 2 maneiras de ativá-los: +Para aqueles que desejam usar o recurso Modo Autônomo fora do KernelSU, existem 2 maneiras de ativá-los: -1. Defina a variável de ambiente `ASH_STANDALONE` como `1`.
Exemplo: `ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh