diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index 128ecc0c..6f01118b 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -7,7 +7,8 @@ on: - '.github/workflows/build-manager.yml' - 'manager/**' - 'kernel/**' - - 'userspace/ksud/**' + - 'userspace/ksud_magic/**' + - 'userspace/ksud_overlayfs**' pull_request: branches: [ "next" ] paths: @@ -20,20 +21,32 @@ jobs: uses: ./.github/workflows/build-lkm.yml secrets: inherit - build-ksud: + build-ksud_overlayfs: needs: build-lkm strategy: matrix: include: - target: aarch64-linux-android os: ubuntu-latest - uses: ./.github/workflows/ksud.yml + uses: ./.github/workflows/ksud_overlayfs.yml + with: + target: ${{ matrix.target }} + os: ${{ matrix.os }} + + build-ksud_magic: + needs: build-ksud_overlayfs + strategy: + matrix: + include: + - target: aarch64-linux-android + os: ubuntu-latest + uses: ./.github/workflows/ksud_magic.yml with: target: ${{ matrix.target }} os: ${{ matrix.os }} build-manager: - needs: build-ksud + needs: build-ksud_magic runs-on: ubuntu-latest defaults: run: @@ -69,15 +82,33 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v3 - - name: Download arm64 ksud + - name: Download arm64 ksud_overlayfs uses: actions/download-artifact@v4 with: - name: ksud-aarch64-linux-android - path: . + name: ksud_overlayfs-aarch64-linux-android + path: ./overlayfs - - name: Copy ksud to app jniLibs + - name: Verify artifact structure run: | - cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so + ls -R ./overlayfs + + - name: Copy ksud_overlayfs to app jniLibs + run: | + cp -f ./overlayfs/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_overlayfs.so + + - name: Download arm64 ksud_magic + uses: actions/download-artifact@v4 + with: + name: ksud_magic-aarch64-linux-android + path: ./magic + + - name: Verify artifact structure + run: | + ls -R ./magic + + - name: Copy ksud_magic to app jniLibs + run: | + cp -f ./magic/aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud_magic.so - name: Build with Gradle run: | diff --git a/.github/workflows/ksud.yml b/.github/workflows/ksud_magic.yml similarity index 78% rename from .github/workflows/ksud.yml rename to .github/workflows/ksud_magic.yml index 5e74bb17..c80c8f66 100644 --- a/.github/workflows/ksud.yml +++ b/.github/workflows/ksud_magic.yml @@ -1,4 +1,4 @@ -name: Build ksud +name: Build ksud_magic on: workflow_call: inputs: @@ -31,7 +31,7 @@ jobs: - name: Prepare LKM fies if: ${{ inputs.pack_lkm }} run: | - cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/ + cp android*-lkm/*_kernelsu.ko ./userspace/ksud_magic/bin/aarch64/ - name: Setup rustup run: | @@ -40,18 +40,18 @@ jobs: rustup target add aarch64-apple-darwin - uses: Swatinem/rust-cache@v2 with: - workspaces: userspace/ksud + workspaces: userspace/ksud_magic cache-targets: false - name: Install cross run: | cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 --force - - name: Build ksud - run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud/Cargo.toml + - name: Build ksud_magic + run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud_magic/Cargo.toml - - name: Upload ksud artifact + - name: Upload ksud_magic artifact uses: actions/upload-artifact@v4 with: - name: ksud-${{ inputs.target }} + name: ksud_magic-${{ inputs.target }} path: userspace/ksud/target/**/release/ksud* diff --git a/.github/workflows/ksud_overlayfs.yml b/.github/workflows/ksud_overlayfs.yml new file mode 100644 index 00000000..a716fe63 --- /dev/null +++ b/.github/workflows/ksud_overlayfs.yml @@ -0,0 +1,57 @@ +name: Build ksud_overlayfs +on: + workflow_call: + inputs: + target: + required: true + type: string + os: + required: false + type: string + default: ubuntu-latest + pack_lkm: + required: false + type: boolean + default: true + use_cache: + required: false + type: boolean + default: true +jobs: + build: + runs-on: ${{ inputs.os }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Prepare LKM fies + if: ${{ inputs.pack_lkm }} + run: | + cp android*-lkm/*_kernelsu.ko ./userspace/ksud_overlayfs/bin/aarch64/ + + - name: Setup rustup + run: | + rustup update stable + rustup target add x86_64-apple-darwin + rustup target add aarch64-apple-darwin + - uses: Swatinem/rust-cache@v2 + with: + workspaces: userspace/ksud_overlayfs + cache-targets: false + + - name: Install cross + run: | + cargo install cross --git https://github.com/cross-rs/cross --rev 66845c1 --force + + - name: Build ksud_overlayfs + run: CROSS_NO_WARNINGS=0 cross build --target ${{ inputs.target }} --release --manifest-path ./userspace/ksud_overlayfs/Cargo.toml + + - name: Upload ksud_overlayfs artifact + uses: actions/upload-artifact@v4 + with: + name: ksud_overlayfs-${{ inputs.target }} + path: userspace/ksud/target/**/release/ksud* diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Home.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Home.kt index b81bba34..2816a88a 100644 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Home.kt +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Home.kt @@ -18,6 +18,7 @@ import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -347,6 +348,12 @@ fun WarningCard( private fun InfoCard() { val context = LocalContext.current + val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) + + var useOverlayFs by rememberSaveable { + mutableStateOf(prefs.getBoolean("use_overlay_fs", false)) + } + ElevatedCard { Column( modifier = Modifier @@ -421,7 +428,13 @@ private fun InfoCard() { Spacer(Modifier.height(16.dp)) InfoCardItem( label = stringResource(R.string.home_module_mount), - content = stringResource(R.string.home_magic_mount), + content = if (useOverlayFs) { + // Show different content if OverlayFS is enabled + stringResource(R.string.home_overlayfs_mount) + } else { + // Default content when OverlayFS is not enabled + stringResource(R.string.home_magic_mount) + }, icon = Icons.Filled.SettingsSuggest, ) } diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt index a7377ce5..80a79a92 100644 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt @@ -497,7 +497,7 @@ private fun ModuleList( }, ) { when { - !viewModel.isDummy -> { + !viewModel.isOverlayAvailable -> { item { Box( modifier = Modifier.fillParentMaxSize(), diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt index 2604ce8c..ef426a05 100644 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt @@ -28,6 +28,8 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.Text +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior @@ -160,6 +162,46 @@ fun SettingScreen(navigator: DestinationsNavigator) { val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) + var useOverlayFs by rememberSaveable { + mutableStateOf( + prefs.getBoolean("use_overlay_fs", false) + ) + } + + var showRestartDialog by remember { mutableStateOf(false) } + + SwitchItem( + icon = Icons.Filled.Build, + title = stringResource(id = R.string.use_overlay_fs), + summary = stringResource(id = R.string.use_overlay_fs_summary), + checked = useOverlayFs + ) { + prefs.edit().putBoolean("use_overlay_fs", it).apply() + useOverlayFs = it + showRestartDialog = true + } + + if (showRestartDialog) { + AlertDialog( + onDismissRequest = { showRestartDialog = false }, + title = { Text(stringResource(R.string.restart_required)) }, + text = { Text(stringResource(R.string.restart_message)) }, + confirmButton = { + TextButton(onClick = { + showRestartDialog = false + restartKsuNext(context) + }) { + Text(stringResource(R.string.restart_now)) + } + }, + dismissButton = { + TextButton(onClick = { showRestartDialog = false }) { + Text(stringResource(R.string.later)) + } + } + ) + } + var checkUpdate by rememberSaveable { mutableStateOf( prefs.getBoolean("check_update", true) @@ -299,27 +341,31 @@ 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() - // } - // } - // } - // } - // ) + + if (useOverlayFs) { + 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) { diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt index e47a4278..627d9285 100644 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt @@ -2,6 +2,7 @@ package com.rifsxd.ksunext.ui.util import android.content.ContentResolver import android.content.Context +import android.content.Intent import android.database.Cursor import android.net.Uri import android.os.Environment @@ -22,15 +23,30 @@ import com.rifsxd.ksunext.ksuApp import org.json.JSONArray import java.io.File - /** * @author weishu * @date 2023/1/1. */ private const val TAG = "KsuCli" -private fun getKsuDaemonPath(): String { - return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud.so" +private fun ksuDaemonMagicPath(): String { + return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_magic.so" +} + +private fun ksuDaemonOverlayfsPath(): String { + return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_overlayfs.so" +} + +// Get the path based on the user's choice +fun getKsuDaemonPath(): String { + val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE) + val useOverlayFs = prefs.getBoolean("use_overlay_fs", false) + + return if (useOverlayFs) { + ksuDaemonOverlayfsPath() + } else { + ksuDaemonMagicPath() + } } object KsuCli { @@ -358,12 +374,10 @@ suspend fun getSupportedKmis(): List = withContext(Dispatchers.IO) { out.filter { it.isNotBlank() }.map { it.trim() } } -fun hasDummy(): Boolean { -//fun overlayFsAvailable(): Boolean { - // val shell = getRootShell() - // // check /proc/filesystems - // return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay") - return true +fun overlayFsAvailable(): Boolean { + val shell = getRootShell() + // check /proc/filesystems + return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay") } fun hasMagisk(): Boolean { @@ -447,3 +461,10 @@ fun restartApp(packageName: String) { forceStopApp(packageName) launchApp(packageName) } + +fun restartKsuNext(context: Context) { + val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) + intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + Runtime.getRuntime().exit(0) +} diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/ModuleViewModel.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/ModuleViewModel.kt index 34f262e3..a2a429ee 100644 --- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/ModuleViewModel.kt +++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/viewmodel/ModuleViewModel.kt @@ -11,7 +11,7 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import com.rifsxd.ksunext.ui.util.listModules -import com.rifsxd.ksunext.ui.util.hasDummy +import com.rifsxd.ksunext.ui.util.overlayFsAvailable import org.json.JSONArray import org.json.JSONObject @@ -45,7 +45,7 @@ class ModuleViewModel : ViewModel() { val changelog: String, ) - var isDummy by mutableStateOf(hasDummy()) + var isOverlayAvailable by mutableStateOf(overlayFsAvailable()) private set var isRefreshing by mutableStateOf(false) @@ -82,7 +82,7 @@ class ModuleViewModel : ViewModel() { val start = SystemClock.elapsedRealtime() kotlin.runCatching { - isDummy = hasDummy() + isOverlayAvailable = overlayFsAvailable() val result = listModules() diff --git a/manager/app/src/main/jniLibs/.gitignore b/manager/app/src/main/jniLibs/.gitignore index 3e487112..a7a71352 100644 --- a/manager/app/src/main/jniLibs/.gitignore +++ b/manager/app/src/main/jniLibs/.gitignore @@ -1 +1,2 @@ -libksud.so +libksud_overlayfs.so +libksud_magic.so diff --git a/manager/app/src/main/res/values-in/strings.xml b/manager/app/src/main/res/values-in/strings.xml index ac6e32ba..12d9c61e 100644 --- a/manager/app/src/main/res/values-in/strings.xml +++ b/manager/app/src/main/res/values-in/strings.xml @@ -65,6 +65,13 @@ Konflik dengan Magisk, fungsi modul ditiadakan! Modul Sistem Magic Mount + OverlayFS + Use OverlayFS + Toggle between using OverlayFS or Magic for KSU daemon + Restart Required + Changes will take effect after restarting the app. Would you like to restart now? + Restart Now + Later 🔥 Pembangunan Next https://github.com/rifsxd/KernelSU-Next Next cabang eksperimental. Lihat di GitHub! 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 3ff6310d..0d3543dc 100644 --- a/manager/app/src/main/res/values-pt-rBR/strings.xml +++ b/manager/app/src/main/res/values-pt-rBR/strings.xml @@ -65,6 +65,13 @@ Os módulos estão indisponíveis devido a um conflito com Magisk! Sistema de módulos Magic Mount + OverlayFS + Use OverlayFS + Toggle between using OverlayFS or Magic for KSU daemon + Restart Required + Changes will take effect after restarting the app. Would you like to restart now? + Restart Now + Later 🔥 Compilação next https://github.com/rifsxd/KernelSU-Next Branch next experimental. Confira no GitHub! 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 2de70edd..0aae4cee 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -64,6 +64,13 @@ 重启生效 模块系统 Magic Mount + OverlayFS + Use OverlayFS + Toggle between using OverlayFS or Magic for KSU daemon + Restart Required + Changes will take effect after restarting the app. Would you like to restart now? + Restart Now + Later 因与 Magisk 有冲突,所有模块不可用! 🔥 Next 构建 https://github.com/rifsxd/KernelSU-Next diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index b2e95725..f7ae27c0 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -65,6 +65,13 @@ Modules are unavailable due to a conflict with Magisk! Module system Magic Mount + OverlayFS + Use OverlayFS + Toggle between using OverlayFS or Magic for KSU daemon + Restart Required + Changes will take effect after restarting the app. Would you like to restart now? + Restart Now + Later 🔥 Next build https://github.com/rifsxd/KernelSU-Next Next experimental branch. Check it out on GitHub! diff --git a/userspace/ksud/.gitignore b/userspace/ksud_magic/.gitignore similarity index 100% rename from userspace/ksud/.gitignore rename to userspace/ksud_magic/.gitignore diff --git a/userspace/ksud/Cargo.lock b/userspace/ksud_magic/Cargo.lock similarity index 100% rename from userspace/ksud/Cargo.lock rename to userspace/ksud_magic/Cargo.lock diff --git a/userspace/ksud/Cargo.toml b/userspace/ksud_magic/Cargo.toml similarity index 100% rename from userspace/ksud/Cargo.toml rename to userspace/ksud_magic/Cargo.toml diff --git a/userspace/ksud/bin/.gitignore b/userspace/ksud_magic/bin/.gitignore similarity index 100% rename from userspace/ksud/bin/.gitignore rename to userspace/ksud_magic/bin/.gitignore diff --git a/userspace/ksud/bin/aarch64/bootctl b/userspace/ksud_magic/bin/aarch64/bootctl similarity index 100% rename from userspace/ksud/bin/aarch64/bootctl rename to userspace/ksud_magic/bin/aarch64/bootctl diff --git a/userspace/ksud/bin/aarch64/busybox b/userspace/ksud_magic/bin/aarch64/busybox similarity index 100% rename from userspace/ksud/bin/aarch64/busybox rename to userspace/ksud_magic/bin/aarch64/busybox diff --git a/userspace/ksud/bin/aarch64/ksuinit b/userspace/ksud_magic/bin/aarch64/ksuinit similarity index 100% rename from userspace/ksud/bin/aarch64/ksuinit rename to userspace/ksud_magic/bin/aarch64/ksuinit diff --git a/userspace/ksud/bin/aarch64/resetprop b/userspace/ksud_magic/bin/aarch64/resetprop similarity index 100% rename from userspace/ksud/bin/aarch64/resetprop rename to userspace/ksud_magic/bin/aarch64/resetprop diff --git a/userspace/ksud/build.rs b/userspace/ksud_magic/build.rs similarity index 100% rename from userspace/ksud/build.rs rename to userspace/ksud_magic/build.rs diff --git a/userspace/ksud/src/apk_sign.rs b/userspace/ksud_magic/src/apk_sign.rs similarity index 100% rename from userspace/ksud/src/apk_sign.rs rename to userspace/ksud_magic/src/apk_sign.rs diff --git a/userspace/ksud/src/assets.rs b/userspace/ksud_magic/src/assets.rs similarity index 100% rename from userspace/ksud/src/assets.rs rename to userspace/ksud_magic/src/assets.rs diff --git a/userspace/ksud/src/banner b/userspace/ksud_magic/src/banner similarity index 100% rename from userspace/ksud/src/banner rename to userspace/ksud_magic/src/banner diff --git a/userspace/ksud/src/boot_patch.rs b/userspace/ksud_magic/src/boot_patch.rs similarity index 100% rename from userspace/ksud/src/boot_patch.rs rename to userspace/ksud_magic/src/boot_patch.rs diff --git a/userspace/ksud/src/cli.rs b/userspace/ksud_magic/src/cli.rs similarity index 100% rename from userspace/ksud/src/cli.rs rename to userspace/ksud_magic/src/cli.rs diff --git a/userspace/ksud/src/debug.rs b/userspace/ksud_magic/src/debug.rs similarity index 100% rename from userspace/ksud/src/debug.rs rename to userspace/ksud_magic/src/debug.rs diff --git a/userspace/ksud/src/defs.rs b/userspace/ksud_magic/src/defs.rs similarity index 100% rename from userspace/ksud/src/defs.rs rename to userspace/ksud_magic/src/defs.rs diff --git a/userspace/ksud/src/init_event.rs b/userspace/ksud_magic/src/init_event.rs similarity index 100% rename from userspace/ksud/src/init_event.rs rename to userspace/ksud_magic/src/init_event.rs diff --git a/userspace/ksud/src/installer.sh b/userspace/ksud_magic/src/installer.sh similarity index 100% rename from userspace/ksud/src/installer.sh rename to userspace/ksud_magic/src/installer.sh diff --git a/userspace/ksud/src/ksucalls.rs b/userspace/ksud_magic/src/ksucalls.rs similarity index 100% rename from userspace/ksud/src/ksucalls.rs rename to userspace/ksud_magic/src/ksucalls.rs diff --git a/userspace/ksud/src/magic_mount.rs b/userspace/ksud_magic/src/magic_mount.rs similarity index 100% rename from userspace/ksud/src/magic_mount.rs rename to userspace/ksud_magic/src/magic_mount.rs diff --git a/userspace/ksud/src/main.rs b/userspace/ksud_magic/src/main.rs similarity index 100% rename from userspace/ksud/src/main.rs rename to userspace/ksud_magic/src/main.rs diff --git a/userspace/ksud/src/module.rs b/userspace/ksud_magic/src/module.rs similarity index 100% rename from userspace/ksud/src/module.rs rename to userspace/ksud_magic/src/module.rs diff --git a/userspace/ksud/src/profile.rs b/userspace/ksud_magic/src/profile.rs similarity index 100% rename from userspace/ksud/src/profile.rs rename to userspace/ksud_magic/src/profile.rs diff --git a/userspace/ksud/src/restorecon.rs b/userspace/ksud_magic/src/restorecon.rs similarity index 100% rename from userspace/ksud/src/restorecon.rs rename to userspace/ksud_magic/src/restorecon.rs diff --git a/userspace/ksud/src/sepolicy.rs b/userspace/ksud_magic/src/sepolicy.rs similarity index 100% rename from userspace/ksud/src/sepolicy.rs rename to userspace/ksud_magic/src/sepolicy.rs diff --git a/userspace/ksud/src/su.rs b/userspace/ksud_magic/src/su.rs similarity index 100% rename from userspace/ksud/src/su.rs rename to userspace/ksud_magic/src/su.rs diff --git a/userspace/ksud/src/utils.rs b/userspace/ksud_magic/src/utils.rs similarity index 100% rename from userspace/ksud/src/utils.rs rename to userspace/ksud_magic/src/utils.rs diff --git a/userspace/ksud_overlayfs/.gitignore b/userspace/ksud_overlayfs/.gitignore new file mode 100644 index 00000000..3c71873d --- /dev/null +++ b/userspace/ksud_overlayfs/.gitignore @@ -0,0 +1,2 @@ +/target +.cargo/ \ No newline at end of file diff --git a/userspace/ksud_overlayfs/Cargo.lock b/userspace/ksud_overlayfs/Cargo.lock new file mode 100644 index 00000000..9ef6a263 --- /dev/null +++ b/userspace/ksud_overlayfs/Cargo.lock @@ -0,0 +1,1687 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-properties" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" + +[[package]] +name = "android_logger" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cc" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dary_heap" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" + +[[package]] +name = "deflate64" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive-new" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", +] + +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + +[[package]] +name = "env_logger" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "extattr" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b59f8a77817ff1b795adafc535941bdf664184f5f95e0b6d1d77dd6d12815dc" +dependencies = [ + "bitflags 1.3.2", + "errno 0.2.8", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hole-punch" +version = "0.0.4-alpha.0" +source = "git+https://github.com/tiann/hole-punch#11ab7a61bfb98682b72fd7f58a47d8e5d997328e" +dependencies = [ + "cfg-if 0.1.10", + "errno 0.2.8", + "libc", + "memmap", + "thiserror 1.0.69", + "winapi", +] + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "include-flate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df49c16750695486c1f34de05da5b7438096156466e7f76c38fcdf285cf0113e" +dependencies = [ + "include-flate-codegen", + "lazy_static", + "libflate", +] + +[[package]] +name = "include-flate-codegen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c5b246c6261be723b85c61ecf87804e8ea4a35cb68be0ff282ed84b95ffe7d7" +dependencies = [ + "libflate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "is_executable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2" +dependencies = [ + "winapi", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "java-properties" +version = "2.0.0" +source = "git+https://github.com/Kernel-SU/java-properties.git?branch=master#42a4aa941b70ded2dd3be9e9f892471023e70229" +dependencies = [ + "encoding_rs", + "lazy_static", + "regex-lite", +] + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2735847566356cd2179a2a38264839308f7079fa96e6bd5a42d740460e003c56" +dependencies = [ + "crossbeam", + "rayon", +] + +[[package]] +name = "ksud" +version = "0.1.0" +dependencies = [ + "android-properties", + "android_logger", + "anyhow", + "chrono", + "clap", + "const_format", + "derive-new", + "encoding_rs", + "env_logger", + "extattr", + "getopts", + "hole-punch", + "humansize", + "is_executable", + "java-properties", + "jwalk", + "libc", + "log", + "loopdev", + "nom", + "procfs", + "regex-lite", + "retry", + "rust-embed", + "rustix 0.38.34", + "serde", + "serde_json", + "sha1", + "sha256", + "tempfile", + "which", + "zip", + "zip-extensions", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libflate" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" +dependencies = [ + "core2", + "hashbrown 0.14.5", + "rle-decode-fast", +] + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "loopdev" +version = "0.5.0" +source = "git+https://github.com/Kernel-SU/loopdev#7a921f8d966477a645b1188732fac486c71a68ef" +dependencies = [ + "errno 0.2.8", + "libc", +] + +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "procfs" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" +dependencies = [ + "bitflags 2.6.0", + "chrono", + "flate2", + "hex", + "procfs-core", + "rustix 0.38.42", +] + +[[package]] +name = "procfs-core" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" +dependencies = [ + "bitflags 2.6.0", + "chrono", + "hex", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "retry" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9166d72162de3575f950507683fac47e30f6f2c3836b71b7fbc61aa517c9c5f4" +dependencies = [ + "rand", +] + +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "include-flate", + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "git+https://github.com/Kernel-SU/rustix.git?branch=main#4a53fbc7cb7a07cabe87125cc21dbc27db316259" +dependencies = [ + "bitflags 2.6.0", + "errno 0.3.10", + "itoa", + "libc", + "linux-raw-sys", + "once_cell", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags 2.6.0", + "errno 0.3.10", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha256" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "once_cell", + "rustix 0.38.42", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "pin-project-lite", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "which" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028" +dependencies = [ + "either", + "env_home", + "rustix 0.38.42", + "winsafe", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "indexmap", + "lzma-rs", + "memchr", + "thiserror 2.0.9", + "time", + "zopfli", +] + +[[package]] +name = "zip-extensions" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "386508a00aae1d8218b9252a41f59bba739ccee3f8e420bb90bcb1c30d960d4a" +dependencies = [ + "zip", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] diff --git a/userspace/ksud_overlayfs/Cargo.toml b/userspace/ksud_overlayfs/Cargo.toml new file mode 100644 index 00000000..826a10b3 --- /dev/null +++ b/userspace/ksud_overlayfs/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "ksud" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +clap = { version = "4", features = ["derive"] } +const_format = "0.2" +zip = { version = "2", default-features = false } +zip-extensions = { version = "0.8", features = [ + "deflate", + "deflate64", + "time", + "lzma", + "xz", +], default-features = false } +java-properties = { git = "https://github.com/Kernel-SU/java-properties.git", branch = "master", default-features = false } +log = "0.4" +env_logger = { version = "0.11", default-features = false } +serde = { version = "1" } +serde_json = "1" +encoding_rs = "0.8" +retry = "2" +humansize = "2" +libc = "0.2" +extattr = "1" +jwalk = "0.8" +is_executable = "1" +nom = "7" +derive-new = "0.7" +rust-embed = { version = "8", features = [ + "debug-embed", + "compression", # must clean build after updating binaries +] } +which = "7" +getopts = "0.2" +sha256 = "1" +sha1 = "0.10" +tempfile = "3" +chrono = "0.4" +hole-punch = { git = "https://github.com/tiann/hole-punch" } +regex-lite = "0.1" + +[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] +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", features = ["bionic-deprecated"] } +procfs = "0.17" +loopdev = { git = "https://github.com/Kernel-SU/loopdev" } + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = { version = "0.14", default-features = false } + +[profile.release] +strip = true +opt-level = "z" +lto = true +codegen-units = 1 \ No newline at end of file diff --git a/userspace/ksud_overlayfs/bin/.gitignore b/userspace/ksud_overlayfs/bin/.gitignore new file mode 100644 index 00000000..1464b7ed --- /dev/null +++ b/userspace/ksud_overlayfs/bin/.gitignore @@ -0,0 +1 @@ +**/*.ko \ No newline at end of file diff --git a/userspace/ksud_overlayfs/bin/aarch64/bootctl b/userspace/ksud_overlayfs/bin/aarch64/bootctl new file mode 100644 index 00000000..cf5c6136 Binary files /dev/null and b/userspace/ksud_overlayfs/bin/aarch64/bootctl differ diff --git a/userspace/ksud_overlayfs/bin/aarch64/busybox b/userspace/ksud_overlayfs/bin/aarch64/busybox new file mode 100755 index 00000000..1ddf3f73 Binary files /dev/null and b/userspace/ksud_overlayfs/bin/aarch64/busybox differ diff --git a/userspace/ksud_overlayfs/bin/aarch64/ksuinit b/userspace/ksud_overlayfs/bin/aarch64/ksuinit new file mode 100755 index 00000000..8d3ba057 Binary files /dev/null and b/userspace/ksud_overlayfs/bin/aarch64/ksuinit differ diff --git a/userspace/ksud_overlayfs/bin/aarch64/resetprop b/userspace/ksud_overlayfs/bin/aarch64/resetprop new file mode 100644 index 00000000..305032c9 Binary files /dev/null and b/userspace/ksud_overlayfs/bin/aarch64/resetprop differ diff --git a/userspace/ksud_overlayfs/bin/aarch64/susfsd b/userspace/ksud_overlayfs/bin/aarch64/susfsd new file mode 100755 index 00000000..13c131f9 Binary files /dev/null and b/userspace/ksud_overlayfs/bin/aarch64/susfsd differ diff --git a/userspace/ksud_overlayfs/build.rs b/userspace/ksud_overlayfs/build.rs new file mode 100644 index 00000000..021418ac --- /dev/null +++ b/userspace/ksud_overlayfs/build.rs @@ -0,0 +1,56 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use std::process::Command; + +fn get_git_version() -> Result<(u32, String), std::io::Error> { + let output = Command::new("git") + .args(["rev-list", "--count", "HEAD"]) + .output()?; + + let output = output.stdout; + let version_code = String::from_utf8(output).expect("Failed to read git count stdout"); + let version_code: u32 = version_code + .trim() + .parse() + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Failed to parse git count"))?; + let version_code = 10000 + 200 + version_code; // For historical reasons + + let version_name = String::from_utf8( + Command::new("git") + .args(["describe", "--tags", "--always"]) + .output()? + .stdout, + ) + .map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::Other, + "Failed to read git describe stdout", + ) + })?; + let version_name = version_name.trim_start_matches('v').to_string(); + Ok((version_code, version_name)) +} + +fn main() { + let (code, name) = match get_git_version() { + Ok((code, name)) => (code, name), + Err(_) => { + // show warning if git is not installed + println!("cargo:warning=Failed to get git version, using 0.0.0"); + (0, "0.0.0".to_string()) + } + }; + let out_dir = env::var("OUT_DIR").expect("Failed to get $OUT_DIR"); + let out_dir = Path::new(&out_dir); + File::create(Path::new(out_dir).join("VERSION_CODE")) + .expect("Failed to create VERSION_CODE") + .write_all(code.to_string().as_bytes()) + .expect("Failed to write VERSION_CODE"); + + File::create(Path::new(out_dir).join("VERSION_NAME")) + .expect("Failed to create VERSION_NAME") + .write_all(name.trim().as_bytes()) + .expect("Failed to write VERSION_NAME"); +} diff --git a/userspace/ksud_overlayfs/src/apk_sign.rs b/userspace/ksud_overlayfs/src/apk_sign.rs new file mode 100644 index 00000000..6d047cca --- /dev/null +++ b/userspace/ksud_overlayfs/src/apk_sign.rs @@ -0,0 +1,115 @@ +use anyhow::{ensure, Result}; +use std::io::{Read, Seek, SeekFrom}; + +pub fn get_apk_signature(apk: &str) -> Result<(u32, String)> { + let mut buffer = [0u8; 0x10]; + let mut size4 = [0u8; 4]; + let mut size8 = [0u8; 8]; + let mut size_of_block = [0u8; 8]; + + let mut f = std::fs::File::open(apk)?; + + let mut i = 0; + loop { + let mut n = [0u8; 2]; + f.seek(SeekFrom::End(-i - 2))?; + f.read_exact(&mut n)?; + + let n = u16::from_le_bytes(n); + if i64::from(n) == i { + f.seek(SeekFrom::Current(-22))?; + f.read_exact(&mut size4)?; + + if u32::from_le_bytes(size4) ^ 0xcafe_babe_u32 == 0xccfb_f1ee_u32 { + if i > 0 { + println!("warning: comment length is {i}"); + } + break; + } + } + + ensure!(n != 0xffff, "not a zip file"); + + i += 1; + } + + f.seek(SeekFrom::Current(12))?; + // offset + f.read_exact(&mut size4)?; + f.seek(SeekFrom::Start(u64::from(u32::from_le_bytes(size4)) - 0x18))?; + + f.read_exact(&mut size8)?; + f.read_exact(&mut buffer)?; + + ensure!(&buffer == b"APK Sig Block 42", "Can not found sig block"); + + let pos = u64::from(u32::from_le_bytes(size4)) - (u64::from_le_bytes(size8) + 0x8); + f.seek(SeekFrom::Start(pos))?; + f.read_exact(&mut size_of_block)?; + + ensure!(size_of_block == size8, "not a signed apk"); + + let mut v2_signing: Option<(u32, String)> = None; + let mut v3_signing_exist = false; + let mut v3_1_signing_exist = false; + + loop { + let mut id = [0u8; 4]; + let mut offset = 4u32; + + f.read_exact(&mut size8)?; // sequence length + if size8 == size_of_block { + break; + } + + f.read_exact(&mut id)?; // id + + let id = u32::from_le_bytes(id); + if id == 0x7109_871a_u32 { + v2_signing = Some(calc_cert_sha256(&mut f, &mut size4, &mut offset)?); + } else if id == 0xf053_68c0_u32 { + // v3 signature scheme + v3_signing_exist = true; + } else if id == 0x1b93_ad61_u32 { + // v3.1 signature scheme: credits to vvb2060 + v3_1_signing_exist = true; + } + + f.seek(SeekFrom::Current( + i64::from_le_bytes(size8) - i64::from(offset), + ))?; + } + + if v3_signing_exist || v3_1_signing_exist { + return Err(anyhow::anyhow!("Unexpected v3 signature found!",)); + } + + v2_signing.ok_or(anyhow::anyhow!("No signature found!")) +} + +fn calc_cert_sha256( + f: &mut std::fs::File, + size4: &mut [u8; 4], + offset: &mut u32, +) -> Result<(u32, String)> { + f.read_exact(size4)?; // signer-sequence length + f.read_exact(size4)?; // signer length + f.read_exact(size4)?; // signed data length + *offset += 0x4 * 3; + + f.read_exact(size4)?; // digests-sequence length + let pos = u32::from_le_bytes(*size4); // skip digests + f.seek(SeekFrom::Current(i64::from(pos)))?; + *offset += 0x4 + pos; + + f.read_exact(size4)?; // certificates length + f.read_exact(size4)?; // certificate length + *offset += 0x4 * 2; + + let cert_len = u32::from_le_bytes(*size4); + let mut cert: Vec = vec![0; cert_len as usize]; + f.read_exact(&mut cert)?; + *offset += cert_len; + + Ok((cert_len, sha256::digest(&cert))) +} diff --git a/userspace/ksud_overlayfs/src/assets.rs b/userspace/ksud_overlayfs/src/assets.rs new file mode 100644 index 00000000..e8255c82 --- /dev/null +++ b/userspace/ksud_overlayfs/src/assets.rs @@ -0,0 +1,50 @@ +use anyhow::Result; +use const_format::concatcp; +use rust_embed::RustEmbed; +use std::path::Path; + +use crate::{defs::BINARY_DIR, utils}; + +pub const RESETPROP_PATH: &str = concatcp!(BINARY_DIR, "resetprop"); +pub const BUSYBOX_PATH: &str = concatcp!(BINARY_DIR, "busybox"); +pub const BOOTCTL_PATH: &str = concatcp!(BINARY_DIR, "bootctl"); + +#[cfg(all(target_arch = "x86_64", target_os = "android"))] +#[derive(RustEmbed)] +#[folder = "bin/x86_64"] +struct Asset; + +// IF NOT x86_64 ANDROID, ie. macos, linux, windows, always use aarch64 +#[cfg(not(all(target_arch = "x86_64", target_os = "android")))] +#[derive(RustEmbed)] +#[folder = "bin/aarch64"] +struct Asset; + +pub fn ensure_binaries(ignore_if_exist: bool) -> Result<()> { + for file in Asset::iter() { + if file == "ksuinit" || file.ends_with(".ko") { + // don't extract ksuinit and kernel modules + continue; + } + let asset = Asset::get(&file).ok_or(anyhow::anyhow!("asset not found: {}", file))?; + utils::ensure_binary(format!("{BINARY_DIR}{file}"), &asset.data, ignore_if_exist)? + } + Ok(()) +} + +pub fn copy_assets_to_file(name: &str, dst: impl AsRef) -> Result<()> { + let asset = Asset::get(name).ok_or(anyhow::anyhow!("asset not found: {}", name))?; + 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_overlayfs/src/banner b/userspace/ksud_overlayfs/src/banner new file mode 100644 index 00000000..bf46a6ce --- /dev/null +++ b/userspace/ksud_overlayfs/src/banner @@ -0,0 +1,9 @@ + _ __ _ ____ _ _ + | |/ /___ _ __ _ __ ___| / ___|| | | | + | ' // _ \ '__| '_ \ / _ \ \___ \| | | | + | . \ __/ | | | | | __/ |___) | |_| | + |_|\_\___|_| |_| |_|\___|_|____/ \___/ + | \ | | _____ _| |_ + | \| |/ _ \ \/ / __| + | |\ | __/> <| |_ + |_| \_|\___/_/\_\\__| diff --git a/userspace/ksud_overlayfs/src/boot_patch.rs b/userspace/ksud_overlayfs/src/boot_patch.rs new file mode 100644 index 00000000..859f836b --- /dev/null +++ b/userspace/ksud_overlayfs/src/boot_patch.rs @@ -0,0 +1,714 @@ +#[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 regex_lite::Regex; +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")] +fn ensure_gki_kernel() -> Result<()> { + let version = get_kernel_version()?; + let is_gki = version.0 == 5 && version.1 >= 10 || version.2 > 5; + ensure!(is_gki, "only support GKI kernel"); + Ok(()) +} + +#[cfg(target_os = "android")] +pub fn get_kernel_version() -> Result<(i32, i32, i32)> { + let uname = rustix::system::uname(); + let version = uname.release().to_string_lossy(); + let re = Regex::new(r"(\d+)\.(\d+)\.(\d+)")?; + if let Some(captures) = re.captures(&version) { + let major = captures + .get(1) + .and_then(|m| m.as_str().parse::().ok()) + .ok_or_else(|| anyhow!("Major version parse error"))?; + let minor = captures + .get(2) + .and_then(|m| m.as_str().parse::().ok()) + .ok_or_else(|| anyhow!("Minor version parse error"))?; + let patch = captures + .get(3) + .and_then(|m| m.as_str().parse::().ok()) + .ok_or_else(|| anyhow!("Patch version parse error"))?; + Ok((major, minor, patch)) + } else { + Err(anyhow!("Invalid kernel version string")) + } +} + +#[cfg(target_os = "android")] +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!("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}")) +} + +#[cfg(target_os = "android")] +fn parse_kmi_from_uname() -> Result { + let uname = rustix::system::uname(); + let version = uname.release().to_string_lossy(); + parse_kmi(&version) +} + +#[cfg(target_os = "android")] +fn parse_kmi_from_modules() -> Result { + use std::io::BufRead; + // 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!("Parse KMI from modules failed") +} + +#[cfg(target_os = "android")] +pub fn get_current_kmi() -> Result { + parse_kmi_from_uname().or_else(|_| parse_kmi_from_modules()) +} + +#[cfg(not(target_os = "android"))] +pub fn get_current_kmi() -> Result { + bail!("Unsupported platform") +} + +fn parse_kmi_from_kernel(kernel: &PathBuf, workdir: &Path) -> Result { + use std::fs::{copy, File}; + use std::io::{BufReader, Read}; + let kernel_path = workdir.join("kernel"); + copy(kernel, &kernel_path).context("Failed to copy kernel")?; + + let file = File::open(&kernel_path).context("Failed to open kernel file")?; + let mut reader = BufReader::new(file); + let mut buffer = Vec::new(); + reader + .read_to_end(&mut buffer) + .context("Failed to read kernel file")?; + + let printable_strings: Vec<&str> = buffer + .split(|&b| b == 0) + .filter_map(|slice| std::str::from_utf8(slice).ok()) + .filter(|s| s.chars().all(|c| c.is_ascii_graphic() || c == ' ')) + .collect(); + + let re = + Regex::new(r"(?:.* )?(\d+\.\d+)(?:\S+)?(android\d+)").context("Failed to compile regex")?; + for s in printable_strings { + if let Some(caps) = re.captures(s) { + if let (Some(kernel_version), Some(android_version)) = (caps.get(1), caps.get(2)) { + let kmi = format!("{}-{}", android_version.as_str(), kernel_version.as_str()); + return Ok(kmi); + } + } + } + println!("- Failed to get KMI version"); + bail!("Try to choose LKM manually") +} + +fn parse_kmi_from_boot(magiskboot: &Path, image: &PathBuf, workdir: &Path) -> Result { + let image_path = workdir.join("image"); + + std::fs::copy(image, &image_path).context("Failed to copy image")?; + + let status = Command::new(magiskboot) + .current_dir(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .arg("unpack") + .arg(&image_path) + .status() + .context("Failed to execute magiskboot command")?; + + if !status.success() { + bail!( + "magiskboot unpack failed with status: {:?}", + status.code().unwrap() + ); + } + + parse_kmi_from_kernel(&image_path, workdir) +} + +fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> { + let status = Command::new(magiskboot) + .current_dir(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .arg("cpio") + .arg("ramdisk.cpio") + .arg(cmd) + .status()?; + + ensure!(status.success(), "magiskboot cpio {} failed", cmd); + Ok(()) +} + +fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result { + let status = Command::new(magiskboot) + .current_dir(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .args(["cpio", "ramdisk.cpio", "test"]) + .status()?; + + // 0: stock, 1: magisk + 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()) + .stderr(Stdio::null()) + .arg(format!("if={}", ifile.as_ref().display())) + .arg(format!("of={}", ofile.as_ref().display())) + .status()?; + ensure!( + status.success(), + "dd if={:?} of={:?} failed", + ifile.as_ref(), + ofile.as_ref() + ); + Ok(()) +} + +pub fn restore( + image: Option, + magiskboot_path: Option, + flash: bool, +) -> Result<()> { + let tmpdir = tempfile::Builder::new() + .prefix("KernelSU Next") + .tempdir() + .context("create temp dir failed")?; + let workdir = tmpdir.path(); + let magiskboot = find_magiskboot(magiskboot_path, workdir)?; + + let kmi = get_current_kmi().unwrap_or_else(|_| String::from("")); + + let skip_init = kmi.starts_with("android12-"); + + let (bootimage, bootdevice) = find_boot_image(&image, skip_init, 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 Next"); + + 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, + kernel: Option, + kmod: Option, + init: Option, + ota: bool, + flash: bool, + out: Option, + magiskboot: Option, + kmi: Option, +) -> Result<()> { + let result = do_patch(image, kernel, kmod, init, ota, flash, out, magiskboot, kmi); + if let Err(ref e) = result { + println!("- Install Error: {e}"); + } + result +} + +#[allow(clippy::too_many_arguments)] +fn do_patch( + image: Option, + kernel: Option, + kmod: Option, + init: Option, + ota: bool, + flash: bool, + out: Option, + magiskboot_path: Option, + kmi: Option, +) -> Result<()> { + println!(include_str!("banner")); + + let patch_file = image.is_some(); + + #[cfg(target_os = "android")] + if !patch_file { + ensure_gki_kernel()?; + } + + let is_replace_kernel = kernel.is_some(); + + if is_replace_kernel { + ensure!( + init.is_none() && kmod.is_none(), + "init and module must not be specified." + ); + } + + let tmpdir = tempfile::Builder::new() + .prefix("KernelSU Next") + .tempdir() + .context("create temp dir failed")?; + let workdir = tmpdir.path(); + + // extract magiskboot + let magiskboot = find_magiskboot(magiskboot_path, workdir)?; + + let kmi = if let Some(kmi) = kmi { + kmi + } else { + match get_current_kmi() { + Ok(value) => value, + Err(e) => { + println!("- {}", e); + if let Some(image_path) = &image { + println!( + "- Trying to auto detect KMI version for {}", + image_path.to_str().unwrap() + ); + parse_kmi_from_boot(&magiskboot, image_path, tmpdir.path())? + } else if let Some(kernel_path) = &kernel { + println!( + "- Trying to auto detect KMI version for {}", + kernel_path.to_str().unwrap() + ); + parse_kmi_from_kernel(kernel_path, tmpdir.path())? + } else { + "".to_string() + } + } + } + }; + + let skip_init = kmi.starts_with("android12-"); + + let (bootimage, bootdevice) = + find_boot_image(&image, skip_init, ota, is_replace_kernel, workdir)?; + + let bootimage = bootimage.display().to_string(); + + // try extract magiskboot/bootctl + let _ = assets::ensure_binaries(false); + + if let Some(kernel) = kernel { + std::fs::copy(kernel, workdir.join("kernel")).context("copy kernel from failed")?; + } + + println!("- Preparing assets"); + + let kmod_file = workdir.join("kernelsu.ko"); + if let Some(kmod) = kmod { + std::fs::copy(kmod, kmod_file).context("copy kernel module failed")?; + } else { + // If kmod is not specified, extract from assets + 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 = workdir.join("init"); + if let Some(init) = init { + std::fs::copy(init, init_file).context("copy init failed")?; + } else { + assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?; + } + + // magiskboot unpack boot.img + // magiskboot cpio ramdisk.cpio 'cp init init.real' + // magiskboot cpio ramdisk.cpio 'add 0755 ksuinit init' + // magiskboot cpio ramdisk.cpio 'add 0755 kernelsu.ko' + + println!("- Unpacking boot image"); + let status = Command::new(&magiskboot) + .current_dir(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .arg("unpack") + .arg(&bootimage) + .status()?; + ensure!(status.success(), "magiskboot unpack failed"); + + 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 Next LKM"); + 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, workdir, "exists init"); + if status.is_ok() { + do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?; + } + + need_backup = flash; + } + + 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(workdir) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .arg("repack") + .arg(&bootimage) + .status()?; + ensure!(status.success(), "magiskboot repack failed"); + let new_boot = workdir.join("new-boot.img"); + + 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_next_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).context("copy out new boot failed")?; + } + println!("- Output file is written to"); + println!("- {}", output_image.display().to_string().trim_matches('"')); + } + + if flash { + println!("- Flashing new boot image"); + flash_boot(&bootdevice, new_boot)?; + + if ota { + post_ota()?; + } + } + + println!("- Done!"); + 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, + skip_init: bool, + 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 { + if cfg!(not(target_os = "android")) { + println!("- Current OS is not android, refusing auto bootimage/bootdevice detection"); + bail!("Please specify a boot image"); + } + 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 && !skip_init { + 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; + let status = Command::new(BOOTCTL_PATH).arg("hal-info").status()?; + if !status.success() { + return Ok(()); + } + + let current_slot = Command::new(BOOTCTL_PATH) + .arg("get-current-slot") + .output()? + .stdout; + let current_slot = String::from_utf8(current_slot)?; + let current_slot = current_slot.trim(); + let target_slot = if current_slot == "0" { 1 } else { 0 }; + + Command::new(BOOTCTL_PATH) + .arg(format!("set-active-boot-slot {target_slot}")) + .status()?; + + let post_fs_data = std::path::Path::new(ADB_DIR).join("post-fs-data.d"); + utils::ensure_dir_exists(&post_fs_data)?; + let post_ota_sh = post_fs_data.join("post_ota.sh"); + + let sh_content = format!( + r###" +{BOOTCTL_PATH} mark-boot-successful +rm -f {BOOTCTL_PATH} +rm -f /data/adb/post-fs-data.d/post_ota.sh +"### + ); + + std::fs::write(&post_ota_sh, sh_content)?; + #[cfg(unix)] + std::fs::set_permissions(post_ota_sh, std::fs::Permissions::from_mode(0o755))?; + + Ok(()) +} diff --git a/userspace/ksud_overlayfs/src/cli.rs b/userspace/ksud_overlayfs/src/cli.rs new file mode 100644 index 00000000..53a065ac --- /dev/null +++ b/userspace/ksud_overlayfs/src/cli.rs @@ -0,0 +1,406 @@ +use anyhow::{Ok, Result}; +use clap::Parser; +use std::path::PathBuf; + +#[cfg(target_os = "android")] +use android_logger::Config; +#[cfg(target_os = "android")] +use log::LevelFilter; + +use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, utils}; + +/// KernelSU Next userspace cli +#[derive(Parser, Debug)] +#[command(author, version = defs::VERSION_NAME, about, long_about = None)] +struct Args { + #[command(subcommand)] + command: Commands, +} + +#[derive(clap::Subcommand, Debug)] +enum Commands { + /// Manage KernelSU Next modules + Module { + #[command(subcommand)] + command: Module, + }, + + /// Trigger `post-fs-data` event + PostFsData, + + /// Trigger `service` event + Services, + + /// Trigger `boot-complete` event + BootCompleted, + + /// Install KernelSU Next userspace component to system + Install { + #[arg(long, default_value = None)] + magiskboot: Option, + }, + + /// Uninstall KernelSU Next 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 { + #[command(subcommand)] + command: Sepolicy, + }, + + /// Manage App Profiles + Profile { + #[command(subcommand)] + command: Profile, + }, + + /// Patch boot or init_boot images to apply KernelSU Next + BootPatch { + /// boot image path, if not specified, will try to find the boot image automatically + #[arg(short, long)] + boot: Option, + + /// kernel image path to replace + #[arg(short, long)] + kernel: Option, + + /// LKM module path to replace, if not specified, will use the builtin one + #[arg(short, long)] + module: Option, + + /// init to be replaced + #[arg(short, long, requires("module"))] + init: Option, + + /// will use another slot when boot image is not specified + #[arg(short = 'u', long, default_value = "false")] + ota: bool, + + /// Flash it to boot partition after patch + #[arg(short, long, default_value = "false")] + flash: bool, + + /// output path, if not specified, will use current directory + #[arg(short, long, default_value = None)] + out: Option, + + /// magiskboot path, if not specified, will search from $PATH + #[arg(long, default_value = None)] + magiskboot: Option, + + /// KMI version, if specified, will use the specified KMI + #[arg(long, default_value = None)] + kmi: Option, + }, + + /// Restore boot or init_boot images patched by KernelSU Next + 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. + SetManager { + /// manager package name + #[arg(default_value_t = String::from("com.rifsxd.ksunext"))] + apk: String, + }, + + /// Get apk size and hash + GetSign { + /// apk path + apk: String, + }, + + /// Root Shell + Su { + /// switch to gloabl mount namespace + #[arg(short, long, default_value = "false")] + global_mnt: bool, + }, + + /// Get kernel version + Version, + + Mount, + + /// Copy sparse file + Xcp { + /// source file + src: String, + /// destination file + dst: String, + /// punch hole + #[arg(short, long, default_value = "false")] + punch_hole: bool, + }, + + /// For testing + Test, +} + +#[derive(clap::Subcommand, Debug)] +enum Sepolicy { + /// Patch sepolicy + Patch { + /// sepolicy statements + sepolicy: String, + }, + + /// Apply sepolicy from file + Apply { + /// sepolicy file path + file: String, + }, + + /// Check if sepolicy statement is supported/valid + Check { + /// sepolicy statements + sepolicy: String, + }, +} + +#[derive(clap::Subcommand, Debug)] +enum Module { + /// Install module + Install { + /// module zip file path + zip: String, + }, + + /// Uninstall module + Uninstall { + /// module id + id: String, + }, + + /// Restore module + Restore { + /// module id + id: String, + }, + + /// enable module + Enable { + /// module id + id: String, + }, + + /// disable module + Disable { + // module id + id: String, + }, + + /// run action for module + Action { + // module id + id: String, + }, + + /// list all modules + List, + + /// Shrink module image size + Shrink, +} + +#[derive(clap::Subcommand, Debug)] +enum Profile { + /// get root profile's selinux policy of + GetSepolicy { + /// package name + package: String, + }, + + /// set root profile's selinux policy of to + SetSepolicy { + /// package name + package: String, + /// policy statements + policy: String, + }, + + /// get template of + GetTemplate { + /// template id + id: String, + }, + + /// set template of to