You've already forked KernelSU-Next
mirror of
https://github.com/KernelSU-Next/KernelSU-Next.git
synced 2025-08-27 23:46:34 +00:00
@@ -362,6 +362,20 @@ private fun ModuleList(
|
|||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
|
!viewModel.isOverlayAvailable -> {
|
||||||
|
item {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillParentMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.module_overlay_fs_not_available),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.moduleList.isEmpty() -> {
|
viewModel.moduleList.isEmpty() -> {
|
||||||
item {
|
item {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -351,6 +351,12 @@ suspend fun getSupportedKmis(): List<String> = withContext(Dispatchers.IO) {
|
|||||||
out.filter { it.isNotBlank() }.map { it.trim() }
|
out.filter { it.isNotBlank() }.map { it.trim() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun overlayFsAvailable(): Boolean {
|
||||||
|
val shell = getRootShell()
|
||||||
|
// check /proc/filesystems
|
||||||
|
return ShellUtils.fastCmdResult(shell, "cat /proc/filesystems | grep overlay")
|
||||||
|
}
|
||||||
|
|
||||||
fun hasMagisk(): Boolean {
|
fun hasMagisk(): Boolean {
|
||||||
val shell = getRootShell(true)
|
val shell = getRootShell(true)
|
||||||
val result = shell.newJob().add("which magisk").exec()
|
val result = shell.newJob().add("which magisk").exec()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.weishu.kernelsu.ui.util.listModules
|
import me.weishu.kernelsu.ui.util.listModules
|
||||||
|
import me.weishu.kernelsu.ui.util.overlayFsAvailable
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
@@ -48,6 +49,9 @@ class ModuleViewModel : ViewModel() {
|
|||||||
var isRefreshing by mutableStateOf(false)
|
var isRefreshing by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
var isOverlayAvailable by mutableStateOf(overlayFsAvailable())
|
||||||
|
private set
|
||||||
|
|
||||||
val moduleList by derivedStateOf {
|
val moduleList by derivedStateOf {
|
||||||
val comparator = compareBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
val comparator = compareBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
||||||
modules.sortedWith(comparator).also {
|
modules.sortedWith(comparator).also {
|
||||||
@@ -71,6 +75,8 @@ class ModuleViewModel : ViewModel() {
|
|||||||
val start = SystemClock.elapsedRealtime()
|
val start = SystemClock.elapsedRealtime()
|
||||||
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
|
isOverlayAvailable = overlayFsAvailable()
|
||||||
|
|
||||||
val result = listModules()
|
val result = listModules()
|
||||||
|
|
||||||
Log.i(TAG, "result: $result")
|
Log.i(TAG, "result: $result")
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">فشل إلغاء التثبيت: %s</string>
|
<string name="module_uninstall_failed">فشل إلغاء التثبيت: %s</string>
|
||||||
<string name="module_version">الإصدار</string>
|
<string name="module_version">الإصدار</string>
|
||||||
<string name="module_author">المطور</string>
|
<string name="module_author">المطور</string>
|
||||||
|
<string name="module_overlay_fs_not_available">الوحدات غير متوفرة حيث يتم تعطيل نظام الملفات المتراكب بواسطة النواة.</string>
|
||||||
<string name="refresh">إنعاش</string>
|
<string name="refresh">إنعاش</string>
|
||||||
<string name="show_system_apps">إظهار تطبيقات النظام</string>
|
<string name="show_system_apps">إظهار تطبيقات النظام</string>
|
||||||
<string name="hide_system_apps">إخفاء تطبيقات النظام</string>
|
<string name="hide_system_apps">إخفاء تطبيقات النظام</string>
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
<string name="module_uninstall_failed">Silmək mümkün olmadı: %s</string>
|
<string name="module_uninstall_failed">Silmək mümkün olmadı: %s</string>
|
||||||
<string name="module_uninstall_success">%s silindi</string>
|
<string name="module_uninstall_success">%s silindi</string>
|
||||||
<string name="hide_system_apps">Sistem proqramlarını gizlət</string>
|
<string name="hide_system_apps">Sistem proqramlarını gizlət</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs mövcud deyil,modul işləyə bilməyəcək!</string>
|
||||||
<string name="send_log">Log-u göndər</string>
|
<string name="send_log">Log-u göndər</string>
|
||||||
<string name="refresh">Yenilə</string>
|
<string name="refresh">Yenilə</string>
|
||||||
<string name="safe_mode">Təhlükəsiz rejimi</string>
|
<string name="safe_mode">Təhlükəsiz rejimi</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">আনইন্সটল ব্যর্থ: %s</string>
|
<string name="module_uninstall_failed">আনইন্সটল ব্যর্থ: %s</string>
|
||||||
<string name="module_version">ভার্সন</string>
|
<string name="module_version">ভার্সন</string>
|
||||||
<string name="module_author">লেখক</string>
|
<string name="module_author">লেখক</string>
|
||||||
|
<string name="module_overlay_fs_not_available">ওভারলেএফএস উপলব্ধ নয়, মডিউল কাজ করতে পারে না!</string>
|
||||||
<string name="refresh">রিফ্রেশ</string>
|
<string name="refresh">রিফ্রেশ</string>
|
||||||
<string name="show_system_apps">শো সিস্টেম অ্যাপস</string>
|
<string name="show_system_apps">শো সিস্টেম অ্যাপস</string>
|
||||||
<string name="hide_system_apps">হাইড সিস্টেম অ্যাপস</string>
|
<string name="hide_system_apps">হাইড সিস্টেম অ্যাপস</string>
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
<string name="selinux_status_disabled">Isključeno</string>
|
<string name="selinux_status_disabled">Isključeno</string>
|
||||||
<string name="about">O</string>
|
<string name="about">O</string>
|
||||||
<string name="module_uninstall_confirm">Jeste li sigurni da želite deinstalirati modulu %s\?</string>
|
<string name="module_uninstall_confirm">Jeste li sigurni da želite deinstalirati modulu %s\?</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs nije dostupan, modula ne može raditi!</string>
|
||||||
<string name="home_support_content">KernelSU je, i uvijek če biti, besplatan, i otvorenog izvora. Možete nam međutim pokazati da vas je briga s time da napravite donaciju.</string>
|
<string name="home_support_content">KernelSU je, i uvijek če biti, besplatan, i otvorenog izvora. Možete nam međutim pokazati da vas je briga s time da napravite donaciju.</string>
|
||||||
<string name="profile_default">Zadano</string>
|
<string name="profile_default">Zadano</string>
|
||||||
<string name="profile_template">Šablon</string>
|
<string name="profile_template">Šablon</string>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
<string name="module_uninstall_confirm">Er du sikker på, at du vil afinstallere modulet %s\?</string>
|
<string name="module_uninstall_confirm">Er du sikker på, at du vil afinstallere modulet %s\?</string>
|
||||||
<string name="module_uninstall_success">%s afinstalleret</string>
|
<string name="module_uninstall_success">%s afinstalleret</string>
|
||||||
<string name="module_uninstall_failed">Afinstallation af: %s fejlede</string>
|
<string name="module_uninstall_failed">Afinstallation af: %s fejlede</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs er ikke tilgængeligt, modulet kan ikke fungere!</string>
|
||||||
<string name="refresh">Opdater</string>
|
<string name="refresh">Opdater</string>
|
||||||
<string name="send_log">Send Log</string>
|
<string name="send_log">Send Log</string>
|
||||||
<string name="safe_mode">Sikker tilstand</string>
|
<string name="safe_mode">Sikker tilstand</string>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<string name="reboot_download">In den Download-Modus neustarten</string>
|
<string name="reboot_download">In den Download-Modus neustarten</string>
|
||||||
<string name="reboot_edl">In den EDL-Modus neustarten</string>
|
<string name="reboot_edl">In den EDL-Modus neustarten</string>
|
||||||
<string name="module_author">Autor</string>
|
<string name="module_author">Autor</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Module sind nicht verfügbar, da OverlayFS vom Kernel deaktiviert ist.</string>
|
||||||
<string name="about">Über KernelSU</string>
|
<string name="about">Über KernelSU</string>
|
||||||
<string name="module_magisk_conflict">Module sind aufgrund eines Konfliktes mit Magisk nicht verfügbar!</string>
|
<string name="module_magisk_conflict">Module sind aufgrund eines Konfliktes mit Magisk nicht verfügbar!</string>
|
||||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<string name="module_uninstall_failed">Fallo al desinstalar: %s</string>
|
<string name="module_uninstall_failed">Fallo al desinstalar: %s</string>
|
||||||
<string name="module_version">Versión</string>
|
<string name="module_version">Versión</string>
|
||||||
<string name="module_author">Autor</string>
|
<string name="module_author">Autor</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Los módulos no están disponibles ya que OverlayFS está desactivado por el kernel.</string>
|
||||||
<string name="refresh">Refrescar</string>
|
<string name="refresh">Refrescar</string>
|
||||||
<string name="show_system_apps">Mostrar aplicaciones del sistema</string>
|
<string name="show_system_apps">Mostrar aplicaciones del sistema</string>
|
||||||
<string name="hide_system_apps">Ocultar aplicaciones del sistema</string>
|
<string name="hide_system_apps">Ocultar aplicaciones del sistema</string>
|
||||||
|
|||||||
@@ -59,6 +59,7 @@
|
|||||||
<string name="module_author">Autor</string>
|
<string name="module_author">Autor</string>
|
||||||
<string name="module_uninstall_failed">Eemaldamine ebaõnnestus: %s</string>
|
<string name="module_uninstall_failed">Eemaldamine ebaõnnestus: %s</string>
|
||||||
<string name="module_version">Versioon</string>
|
<string name="module_version">Versioon</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Moodulid pole saadaval, kuna OverlayFS on kernelis keelatud.</string>
|
||||||
<string name="show_system_apps">Kuva süsteemirakendused</string>
|
<string name="show_system_apps">Kuva süsteemirakendused</string>
|
||||||
<string name="hide_system_apps">Peida süsteemirakendused</string>
|
<string name="hide_system_apps">Peida süsteemirakendused</string>
|
||||||
<string name="module_magisk_conflict">Moodulid pole saadaval Magiski konflikti tõttu!</string>
|
<string name="module_magisk_conflict">Moodulid pole saadaval Magiski konflikti tõttu!</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">پاک کردن ناموفق بود: %s</string>
|
<string name="module_uninstall_failed">پاک کردن ناموفق بود: %s</string>
|
||||||
<string name="module_version">نسخه</string>
|
<string name="module_version">نسخه</string>
|
||||||
<string name="module_author">سازنده</string>
|
<string name="module_author">سازنده</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs موجود نیست. مازول کار نمیکند!!</string>
|
||||||
<string name="refresh">تازهسازی</string>
|
<string name="refresh">تازهسازی</string>
|
||||||
<string name="show_system_apps">نمایش برنامه های سیستمی</string>
|
<string name="show_system_apps">نمایش برنامه های سیستمی</string>
|
||||||
<string name="hide_system_apps">مخفی کردن برنامه های سیستمی</string>
|
<string name="hide_system_apps">مخفی کردن برنامه های سیستمی</string>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
<string name="module_uninstall_success">Na-uninstall ang %s</string>
|
<string name="module_uninstall_success">Na-uninstall ang %s</string>
|
||||||
<string name="module_uninstall_failed">Nabigong i-uninstall: %s</string>
|
<string name="module_uninstall_failed">Nabigong i-uninstall: %s</string>
|
||||||
<string name="module_author">May-akda</string>
|
<string name="module_author">May-akda</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Ang overlayfs ay hindi magagamit, ang modyul ay hindi gagana!</string>
|
||||||
<string name="refresh">I-refresh</string>
|
<string name="refresh">I-refresh</string>
|
||||||
<string name="show_system_apps">Ipakita ang mga application ng system</string>
|
<string name="show_system_apps">Ipakita ang mga application ng system</string>
|
||||||
<string name="send_log">Magpadala ng Log</string>
|
<string name="send_log">Magpadala ng Log</string>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<string name="module_author">Auteur</string>
|
<string name="module_author">Auteur</string>
|
||||||
<string name="module_uninstall_confirm">Êtes-vous sûr(e) de vouloir désinstaller le module %s \?</string>
|
<string name="module_uninstall_confirm">Êtes-vous sûr(e) de vouloir désinstaller le module %s \?</string>
|
||||||
<string name="home_learn_kernelsu">Découvrir KernelSU</string>
|
<string name="home_learn_kernelsu">Découvrir KernelSU</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Les modules sont indisponibles car OverlayFS est désactivé par le noyau.</string>
|
||||||
<string name="refresh">Rafraîchir</string>
|
<string name="refresh">Rafraîchir</string>
|
||||||
<string name="show_system_apps">Afficher les applications système</string>
|
<string name="show_system_apps">Afficher les applications système</string>
|
||||||
<string name="hide_system_apps">Masquer les applications système</string>
|
<string name="hide_system_apps">Masquer les applications système</string>
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
<string name="home_click_to_install">इंस्टाल करने के लिए क्लिक करें</string>
|
<string name="home_click_to_install">इंस्टाल करने के लिए क्लिक करें</string>
|
||||||
<string name="profile_selinux_rules">नियम</string>
|
<string name="profile_selinux_rules">नियम</string>
|
||||||
<string name="profile_groups">समूह</string>
|
<string name="profile_groups">समूह</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Overlayfs उपलब्ध नहीं है, मॉड्यूल काम नहीं कर सकता !</string>
|
||||||
<string name="module">मॉड्यूल</string>
|
<string name="module">मॉड्यूल</string>
|
||||||
<string name="module_author">निर्माता</string>
|
<string name="module_author">निर्माता</string>
|
||||||
<string name="about">हमारे बारे में</string>
|
<string name="about">हमारे बारे में</string>
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
<string name="module_version">Verzija</string>
|
<string name="module_version">Verzija</string>
|
||||||
<string name="module_author">Autor</string>
|
<string name="module_author">Autor</string>
|
||||||
<string name="refresh">Osvježi</string>
|
<string name="refresh">Osvježi</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs nije dostupan, modula ne može raditi!</string>
|
||||||
<string name="module_magisk_conflict">Module su isključene jer je u sukobu sa Magisk-om!</string>
|
<string name="module_magisk_conflict">Module su isključene jer je u sukobu sa Magisk-om!</string>
|
||||||
<string name="home_learn_kernelsu">Naučite KernelSU</string>
|
<string name="home_learn_kernelsu">Naučite KernelSU</string>
|
||||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<string name="module_uninstall_confirm">Biztos vagy benne hogy eltávolítod a következő modult: %s\?</string>
|
<string name="module_uninstall_confirm">Biztos vagy benne hogy eltávolítod a következő modult: %s\?</string>
|
||||||
<string name="module_uninstall_failed">Nem sikerült eltávolítani: %s</string>
|
<string name="module_uninstall_failed">Nem sikerült eltávolítani: %s</string>
|
||||||
<string name="module_author">Készítő</string>
|
<string name="module_author">Készítő</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Overlayfs nem elérhető, a modul nem tud enélkül működni!</string>
|
||||||
<string name="refresh">Újratöltés</string>
|
<string name="refresh">Újratöltés</string>
|
||||||
<string name="show_system_apps">Mutasd a rendszer alkalmazásokat</string>
|
<string name="show_system_apps">Mutasd a rendszer alkalmazásokat</string>
|
||||||
<string name="hide_system_apps">Rejtsd el a rendszer alkalmazásokat</string>
|
<string name="hide_system_apps">Rejtsd el a rendszer alkalmazásokat</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">Gagal menghapus: %s</string>
|
<string name="module_uninstall_failed">Gagal menghapus: %s</string>
|
||||||
<string name="module_version">Versi</string>
|
<string name="module_version">Versi</string>
|
||||||
<string name="module_author">Oleh</string>
|
<string name="module_author">Oleh</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Kernel tidak mendukung OverlayFS, modul tidak berfungsi.</string>
|
||||||
<string name="refresh">Muat ulang</string>
|
<string name="refresh">Muat ulang</string>
|
||||||
<string name="show_system_apps">Tampilkan aplikasi sistem</string>
|
<string name="show_system_apps">Tampilkan aplikasi sistem</string>
|
||||||
<string name="hide_system_apps">Sembunyikan aplikasi sistem</string>
|
<string name="hide_system_apps">Sembunyikan aplikasi sistem</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">Impossibile disinstallare: %s</string>
|
<string name="module_uninstall_failed">Impossibile disinstallare: %s</string>
|
||||||
<string name="module_version">Versione</string>
|
<string name="module_version">Versione</string>
|
||||||
<string name="module_author">Autore</string>
|
<string name="module_author">Autore</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs non è disponibile, i moduli non possono funzionare!</string>
|
||||||
<string name="refresh">Ricarica</string>
|
<string name="refresh">Ricarica</string>
|
||||||
<string name="show_system_apps">Mostra app di sistema</string>
|
<string name="show_system_apps">Mostra app di sistema</string>
|
||||||
<string name="hide_system_apps">Nascondi app di sistema</string>
|
<string name="hide_system_apps">Nascondi app di sistema</string>
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
<string name="home_click_to_install">לחץ להתקנה</string>
|
<string name="home_click_to_install">לחץ להתקנה</string>
|
||||||
<string name="profile_selinux_rules">כללים</string>
|
<string name="profile_selinux_rules">כללים</string>
|
||||||
<string name="profile_groups">קבוצה</string>
|
<string name="profile_groups">קבוצה</string>
|
||||||
|
<string name="module_overlay_fs_not_available">שכבות-על לא זמינות, המודול לא יכול לעבוד!</string>
|
||||||
<string name="module">מודולים</string>
|
<string name="module">מודולים</string>
|
||||||
<string name="module_author">יוצר</string>
|
<string name="module_author">יוצר</string>
|
||||||
<string name="about">אודות</string>
|
<string name="about">אודות</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">アンインストールに失敗: %s</string>
|
<string name="module_uninstall_failed">アンインストールに失敗: %s</string>
|
||||||
<string name="module_version">バージョン</string>
|
<string name="module_version">バージョン</string>
|
||||||
<string name="module_author">制作者</string>
|
<string name="module_author">制作者</string>
|
||||||
|
<string name="module_overlay_fs_not_available">カーネルによって OverlayFS が無効になっているため、モジュールが利用できません。</string>
|
||||||
<string name="refresh">更新</string>
|
<string name="refresh">更新</string>
|
||||||
<string name="show_system_apps">システムアプリを表示</string>
|
<string name="show_system_apps">システムアプリを表示</string>
|
||||||
<string name="hide_system_apps">システムアプリを非表示</string>
|
<string name="hide_system_apps">システムアプリを非表示</string>
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
<string name="profile_namespace">ಮೌಂಟ್ ನೇಮ್ಸ್ಪೇಸ್</string>
|
<string name="profile_namespace">ಮೌಂಟ್ ನೇಮ್ಸ್ಪೇಸ್</string>
|
||||||
<string name="profile_selinux_rules">ನಿಯಮಗಳು</string>
|
<string name="profile_selinux_rules">ನಿಯಮಗಳು</string>
|
||||||
<string name="profile_groups">ಗುಂಪುಗಳು</string>
|
<string name="profile_groups">ಗುಂಪುಗಳು</string>
|
||||||
|
<string name="module_overlay_fs_not_available">ಓವರ್ಲೇಫ್ಗಳು ಲಭ್ಯವಿಲ್ಲ, ಮಾಡ್ಯೂಲ್ ಕಾರ್ಯನಿರ್ವಹಿಸುವುದಿಲ್ಲ!</string>
|
||||||
<string name="module">ಮಾಡ್ಯೂಲ್</string>
|
<string name="module">ಮಾಡ್ಯೂಲ್</string>
|
||||||
<string name="module_author">ಲೇಖಕ</string>
|
<string name="module_author">ಲೇಖಕ</string>
|
||||||
<string name="about">ಬಗ್ಗೆ</string>
|
<string name="about">ಬಗ್ಗೆ</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">모듈 삭제 실패: %s</string>
|
<string name="module_uninstall_failed">모듈 삭제 실패: %s</string>
|
||||||
<string name="module_version">버전</string>
|
<string name="module_version">버전</string>
|
||||||
<string name="module_author">제작자</string>
|
<string name="module_author">제작자</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs 사용 불가, 모듈을 사용할 수 없습니다!</string>
|
||||||
<string name="refresh">새로고침</string>
|
<string name="refresh">새로고침</string>
|
||||||
<string name="show_system_apps">시스템 앱 보이기</string>
|
<string name="show_system_apps">시스템 앱 보이기</string>
|
||||||
<string name="hide_system_apps">시스템 앱 숨기기</string>
|
<string name="hide_system_apps">시스템 앱 숨기기</string>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<string name="module_uninstall_success">%s išdiegtas</string>
|
<string name="module_uninstall_success">%s išdiegtas</string>
|
||||||
<string name="module_version">Versija</string>
|
<string name="module_version">Versija</string>
|
||||||
<string name="module_author">Autorius</string>
|
<string name="module_author">Autorius</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs nepasiekiamas, modulis negali veikti!</string>
|
||||||
<string name="show_system_apps">Rodyti sistemos programas</string>
|
<string name="show_system_apps">Rodyti sistemos programas</string>
|
||||||
<string name="hide_system_apps">Slėpti sistemos programas</string>
|
<string name="hide_system_apps">Slėpti sistemos programas</string>
|
||||||
<string name="send_log">Siųsti žurnalą</string>
|
<string name="send_log">Siųsti žurnalą</string>
|
||||||
|
|||||||
@@ -94,6 +94,7 @@
|
|||||||
<string name="module_install">Instalēt</string>
|
<string name="module_install">Instalēt</string>
|
||||||
<string name="module_uninstall_confirm">Vai tiešām vēlaties atinstalēt moduli %s?</string>
|
<string name="module_uninstall_confirm">Vai tiešām vēlaties atinstalēt moduli %s?</string>
|
||||||
<string name="module_version">Versija</string>
|
<string name="module_version">Versija</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs nav pieejams, modulis nevar darboties!</string>
|
||||||
<string name="safe_mode">Drošais režīms</string>
|
<string name="safe_mode">Drošais režīms</string>
|
||||||
<string name="module_magisk_conflict">Moduļi ir atspējoti, jo tie konfliktē ar Magisk!</string>
|
<string name="module_magisk_conflict">Moduļi ir atspējoti, jo tie konfliktē ar Magisk!</string>
|
||||||
<string name="home_support_content">KernelSU ir un vienmēr būs bezmaksas un atvērtā koda. Tomēr jūs varat parādīt mums, ka jums rūp, veicot ziedojumu.</string>
|
<string name="home_support_content">KernelSU ir un vienmēr būs bezmaksas un atvērtā koda. Tomēr jūs varat parādīt mums, ka jums rūp, veicot ziedojumu.</string>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
<string name="reboot_edl">EDL वर रीबूट करा</string>
|
<string name="reboot_edl">EDL वर रीबूट करा</string>
|
||||||
<string name="module_uninstall_confirm">तुमची खात्री आहे की तुम्ही मॉड्यूल %s विस्थापित करू इच्छिता\?</string>
|
<string name="module_uninstall_confirm">तुमची खात्री आहे की तुम्ही मॉड्यूल %s विस्थापित करू इच्छिता\?</string>
|
||||||
<string name="module_uninstall_failed">विस्थापित करण्यात अयशस्वी: %s</string>
|
<string name="module_uninstall_failed">विस्थापित करण्यात अयशस्वी: %s</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs उपलब्ध नाही, मॉड्यूल काम करू शकत नाही!</string>
|
||||||
<string name="show_system_apps">सिस्टम अॅप्स दाखवा</string>
|
<string name="show_system_apps">सिस्टम अॅप्स दाखवा</string>
|
||||||
<string name="reboot_bootloader">बूटलोडरवर रीबूट करा</string>
|
<string name="reboot_bootloader">बूटलोडरवर रीबूट करा</string>
|
||||||
<string name="module_uninstall_success">%s विस्थापित</string>
|
<string name="module_uninstall_success">%s विस्थापित</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">Mislukt om te verwijderen: %s</string>
|
<string name="module_uninstall_failed">Mislukt om te verwijderen: %s</string>
|
||||||
<string name="module_version">Versie</string>
|
<string name="module_version">Versie</string>
|
||||||
<string name="module_author">Auteur</string>
|
<string name="module_author">Auteur</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Modules zijn niet beschikbaar omdat OverlayFS door de kernel is uitgeschakeld.</string>
|
||||||
<string name="refresh">Vernieuwen</string>
|
<string name="refresh">Vernieuwen</string>
|
||||||
<string name="show_system_apps">Toon systeem apps</string>
|
<string name="show_system_apps">Toon systeem apps</string>
|
||||||
<string name="hide_system_apps">Verberg systeem apps</string>
|
<string name="hide_system_apps">Verberg systeem apps</string>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<string name="module_uninstall_failed">Nie udało się odinstalować:: %s</string>
|
<string name="module_uninstall_failed">Nie udało się odinstalować:: %s</string>
|
||||||
<string name="module_version">Wersja</string>
|
<string name="module_version">Wersja</string>
|
||||||
<string name="module_author">Autor</string>
|
<string name="module_author">Autor</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Moduły są niedostępne, ponieważ OverlayFS jest wyłączony przez jądro.</string>
|
||||||
<string name="refresh">Odśwież</string>
|
<string name="refresh">Odśwież</string>
|
||||||
<string name="show_system_apps">Pokaż aplikacje systemowe</string>
|
<string name="show_system_apps">Pokaż aplikacje systemowe</string>
|
||||||
<string name="hide_system_apps">Ukryj aplikacje systemowe</string>
|
<string name="hide_system_apps">Ukryj aplikacje systemowe</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">Falha ao desinstalar %s</string>
|
<string name="module_uninstall_failed">Falha ao desinstalar %s</string>
|
||||||
<string name="module_version">Versão</string>
|
<string name="module_version">Versão</string>
|
||||||
<string name="module_author">Autor</string>
|
<string name="module_author">Autor</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Os módulos estão indisponíveis porque OverlayFS está desabilitado pelo kernel!</string>
|
||||||
<string name="refresh">Atualizar</string>
|
<string name="refresh">Atualizar</string>
|
||||||
<string name="show_system_apps">Mostrar apps do sistema</string>
|
<string name="show_system_apps">Mostrar apps do sistema</string>
|
||||||
<string name="hide_system_apps">Ocultar apps do sistema</string>
|
<string name="hide_system_apps">Ocultar apps do sistema</string>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<string name="reboot_edl">Reiniciar em EDL</string>
|
<string name="reboot_edl">Reiniciar em EDL</string>
|
||||||
<string name="module_uninstall_confirm">Tem certeza de que deseja desinstalar o módulo %s\?</string>
|
<string name="module_uninstall_confirm">Tem certeza de que deseja desinstalar o módulo %s\?</string>
|
||||||
<string name="about">Sobre</string>
|
<string name="about">Sobre</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs não está disponível, o módulo não pode funcionar!</string>
|
||||||
<string name="send_log">Enviar log</string>
|
<string name="send_log">Enviar log</string>
|
||||||
<string name="module_uninstall_success">%s desinstalado</string>
|
<string name="module_uninstall_success">%s desinstalado</string>
|
||||||
<string name="show_system_apps">Mostrar aplicativos do sistema</string>
|
<string name="show_system_apps">Mostrar aplicativos do sistema</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">Dezinstalare eșuată: %s</string>
|
<string name="module_uninstall_failed">Dezinstalare eșuată: %s</string>
|
||||||
<string name="module_version">Versiune</string>
|
<string name="module_version">Versiune</string>
|
||||||
<string name="module_author">Autor</string>
|
<string name="module_author">Autor</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs nu este disponibil, modulul nu poate funcționa!</string>
|
||||||
<string name="refresh">Reîmprospătează</string>
|
<string name="refresh">Reîmprospătează</string>
|
||||||
<string name="show_system_apps">Arată aplicațiile de sistem</string>
|
<string name="show_system_apps">Arată aplicațiile de sistem</string>
|
||||||
<string name="hide_system_apps">Ascunde aplicațiile de sistem</string>
|
<string name="hide_system_apps">Ascunde aplicațiile de sistem</string>
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
<string name="module_uninstall_failed">Не удалось удалить: %s</string>
|
<string name="module_uninstall_failed">Не удалось удалить: %s</string>
|
||||||
<string name="module_version">Версия</string>
|
<string name="module_version">Версия</string>
|
||||||
<string name="module_author">Автор</string>
|
<string name="module_author">Автор</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Модули недоступны, так как OverlayFS отключен ядром.</string>
|
||||||
<string name="refresh">Обновить страницу</string>
|
<string name="refresh">Обновить страницу</string>
|
||||||
<string name="show_system_apps">Показать системные приложения</string>
|
<string name="show_system_apps">Показать системные приложения</string>
|
||||||
<string name="hide_system_apps">Скрыть системные приложения</string>
|
<string name="hide_system_apps">Скрыть системные приложения</string>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<string name="module_uninstall_confirm">Ste prepričani, da želite odstraniti modul %s?</string>
|
<string name="module_uninstall_confirm">Ste prepričani, da želite odstraniti modul %s?</string>
|
||||||
<string name="module_uninstall_success">%s je odmeščen</string>
|
<string name="module_uninstall_success">%s je odmeščen</string>
|
||||||
<string name="module_author">Avtor</string>
|
<string name="module_author">Avtor</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs ni na voljo, modul ne more delovati!</string>
|
||||||
<string name="hide_system_apps">Skrij prikaz sistemskih aplikacij</string>
|
<string name="hide_system_apps">Skrij prikaz sistemskih aplikacij</string>
|
||||||
<string name="send_log">Prijavite dnevnik</string>
|
<string name="send_log">Prijavite dnevnik</string>
|
||||||
<string name="home_learn_kernelsu">Naučite se KernelSU</string>
|
<string name="home_learn_kernelsu">Naučite se KernelSU</string>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
<string name="reboot_edl">รีบูตเข้าสู่โหมด EDL</string>
|
<string name="reboot_edl">รีบูตเข้าสู่โหมด EDL</string>
|
||||||
<string name="module_uninstall_success">%s ถอนการติดตั้งสำเร็จ</string>
|
<string name="module_uninstall_success">%s ถอนการติดตั้งสำเร็จ</string>
|
||||||
<string name="module_uninstall_failed">ล้มเหลวในการถอนการติดตั้ง: %s</string>
|
<string name="module_uninstall_failed">ล้มเหลวในการถอนการติดตั้ง: %s</string>
|
||||||
|
<string name="module_overlay_fs_not_available">โมดูลไม่สามารถใช้งานได้เนื่องจาก OverlayFS ถูกปิดใช้งานโดยเคอร์เนล</string>
|
||||||
<string name="module_uninstall_confirm">คุณแน่ใจว่าจะถอนการติดตั้งโมดูล %s หรือไม่\?</string>
|
<string name="module_uninstall_confirm">คุณแน่ใจว่าจะถอนการติดตั้งโมดูล %s หรือไม่\?</string>
|
||||||
<string name="module_author">ผู้สร้าง</string>
|
<string name="module_author">ผู้สร้าง</string>
|
||||||
<string name="module_version">เวอร์ชัน</string>
|
<string name="module_version">เวอร์ชัน</string>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<string name="module_uninstall_failed">Kaldırma başarısız: %s</string>
|
<string name="module_uninstall_failed">Kaldırma başarısız: %s</string>
|
||||||
<string name="module_version">Sürüm</string>
|
<string name="module_version">Sürüm</string>
|
||||||
<string name="module_author">Geliştirici</string>
|
<string name="module_author">Geliştirici</string>
|
||||||
|
<string name="module_overlay_fs_not_available">OverlayFS çekirdek tarafından devre dışı bırakıldığı için modüller kullanılamıyor.</string>
|
||||||
<string name="refresh">Yenile</string>
|
<string name="refresh">Yenile</string>
|
||||||
<string name="show_system_apps">Sistem uygulamalarını göster</string>
|
<string name="show_system_apps">Sistem uygulamalarını göster</string>
|
||||||
<string name="hide_system_apps">Sistem uygulamalarını gizle</string>
|
<string name="hide_system_apps">Sistem uygulamalarını gizle</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">Не вдалося видалити: %s</string>
|
<string name="module_uninstall_failed">Не вдалося видалити: %s</string>
|
||||||
<string name="module_version">Версія</string>
|
<string name="module_version">Версія</string>
|
||||||
<string name="module_author">Автор</string>
|
<string name="module_author">Автор</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs не доступний, модуль не може працювати!</string>
|
||||||
<string name="refresh">Освіжати(Оновити)</string>
|
<string name="refresh">Освіжати(Оновити)</string>
|
||||||
<string name="show_system_apps">Показати системні додатки</string>
|
<string name="show_system_apps">Показати системні додатки</string>
|
||||||
<string name="hide_system_apps">Сховати системні додатки</string>
|
<string name="hide_system_apps">Сховати системні додатки</string>
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
<string name="module_uninstall_failed">Lỗi khi gỡ cài đặt: %s</string>
|
<string name="module_uninstall_failed">Lỗi khi gỡ cài đặt: %s</string>
|
||||||
<string name="module_version">Phiên bản</string>
|
<string name="module_version">Phiên bản</string>
|
||||||
<string name="module_author">Tác giả</string>
|
<string name="module_author">Tác giả</string>
|
||||||
|
<string name="module_overlay_fs_not_available">overlayfs hiện không khả dụng, mô-đun không thể hoạt động!</string>
|
||||||
<string name="refresh">Làm mới</string>
|
<string name="refresh">Làm mới</string>
|
||||||
<string name="show_system_apps">Hiển thị ứng dụng hệ thống</string>
|
<string name="show_system_apps">Hiển thị ứng dụng hệ thống</string>
|
||||||
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
|
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
<string name="module_uninstall_failed">卸载失败: %s</string>
|
<string name="module_uninstall_failed">卸载失败: %s</string>
|
||||||
<string name="module_version">版本</string>
|
<string name="module_version">版本</string>
|
||||||
<string name="module_author">作者</string>
|
<string name="module_author">作者</string>
|
||||||
|
<string name="module_overlay_fs_not_available">OverlayFS被内核禁用,模块不可用。</string>
|
||||||
<string name="refresh">刷新</string>
|
<string name="refresh">刷新</string>
|
||||||
<string name="show_system_apps">显示系统应用</string>
|
<string name="show_system_apps">显示系统应用</string>
|
||||||
<string name="hide_system_apps">隐藏系统应用</string>
|
<string name="hide_system_apps">隐藏系统应用</string>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<string name="module_uninstall_failed">無法解除安裝:%s</string>
|
<string name="module_uninstall_failed">無法解除安裝:%s</string>
|
||||||
<string name="module_version">版本</string>
|
<string name="module_version">版本</string>
|
||||||
<string name="module_author">作者</string>
|
<string name="module_author">作者</string>
|
||||||
|
<string name="module_overlay_fs_not_available">OverlayFS 無法使用,模組無法正常運作!</string>
|
||||||
<string name="refresh">重新整理</string>
|
<string name="refresh">重新整理</string>
|
||||||
<string name="show_system_apps">顯示系統應用程式</string>
|
<string name="show_system_apps">顯示系統應用程式</string>
|
||||||
<string name="hide_system_apps">隱藏系統應用程式</string>
|
<string name="hide_system_apps">隱藏系統應用程式</string>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
<string name="module_uninstall_failed">無法解除安裝:%s</string>
|
<string name="module_uninstall_failed">無法解除安裝:%s</string>
|
||||||
<string name="module_version">版本</string>
|
<string name="module_version">版本</string>
|
||||||
<string name="module_author">作者</string>
|
<string name="module_author">作者</string>
|
||||||
|
<string name="module_overlay_fs_not_available">OverlayFS 無法使用,模組無法正常運作!</string>
|
||||||
<string name="refresh">重新整理</string>
|
<string name="refresh">重新整理</string>
|
||||||
<string name="show_system_apps">顯示系統應用程式</string>
|
<string name="show_system_apps">顯示系統應用程式</string>
|
||||||
<string name="hide_system_apps">隱藏系統應用程式</string>
|
<string name="hide_system_apps">隱藏系統應用程式</string>
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
<string name="module_uninstall_failed">Failed to uninstall: %s</string>
|
<string name="module_uninstall_failed">Failed to uninstall: %s</string>
|
||||||
<string name="module_version">Version</string>
|
<string name="module_version">Version</string>
|
||||||
<string name="module_author">Author</string>
|
<string name="module_author">Author</string>
|
||||||
|
<string name="module_overlay_fs_not_available">Modules are unavailable as OverlayFS is disabled by the kernel.</string>
|
||||||
<string name="refresh">Refresh</string>
|
<string name="refresh">Refresh</string>
|
||||||
<string name="show_system_apps">Show system apps</string>
|
<string name="show_system_apps">Show system apps</string>
|
||||||
<string name="hide_system_apps">Hide system apps</string>
|
<string name="hide_system_apps">Hide system apps</string>
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
<string name="module_magisk_conflict">Modules are unavailable due to a conflict with Magisk!</string>
|
<string name="module_magisk_conflict">Modules are unavailable due to a conflict with Magisk!</string>
|
||||||
<string name="home_next_kernelsu">🔥 Next Build</string>
|
<string name="home_next_kernelsu">🔥 Next Build</string>
|
||||||
<string name="home_next_kernelsu_announce">https://github.com/rifsxd/KernelSU</string>
|
<string name="home_next_kernelsu_announce">https://github.com/rifsxd/KernelSU</string>
|
||||||
<string name="home_next_kernelsu_body">Next modifications branch. Check it out on GitHub!</string>
|
<string name="home_next_kernelsu_body">Next modifications branch, Check it out!</string>
|
||||||
<string name="home_learn_kernelsu">Learn KernelSU</string>
|
<string name="home_learn_kernelsu">Learn KernelSU</string>
|
||||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||||
<string name="home_click_to_learn_kernelsu">Learn how to install KernelSU and use modules</string>
|
<string name="home_click_to_learn_kernelsu">Learn how to install KernelSU and use modules</string>
|
||||||
|
|||||||
48
userspace/ksud/Cargo.lock
generated
48
userspace/ksud/Cargo.lock
generated
@@ -29,7 +29,7 @@ version = "0.8.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
@@ -167,7 +167,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
"object",
|
"object",
|
||||||
@@ -223,6 +223,12 @@ dependencies = [
|
|||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -354,7 +360,7 @@ version = "1.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -499,7 +505,7 @@ version = "0.8.35"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -610,7 +616,7 @@ version = "0.2.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
@@ -649,6 +655,19 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
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",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
@@ -788,6 +807,7 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"extattr",
|
"extattr",
|
||||||
"getopts",
|
"getopts",
|
||||||
|
"hole-punch",
|
||||||
"humansize",
|
"humansize",
|
||||||
"is_executable",
|
"is_executable",
|
||||||
"java-properties",
|
"java-properties",
|
||||||
@@ -896,6 +916,16 @@ version = "2.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
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]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -1207,7 +1237,7 @@ version = "0.10.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
@@ -1218,7 +1248,7 @@ version = "0.10.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
@@ -1271,7 +1301,7 @@ version = "3.14.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 0.38.41",
|
"rustix 0.38.41",
|
||||||
@@ -1386,7 +1416,7 @@ version = "0.2.95"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ sha256 = "1"
|
|||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
tempfile = "3.14"
|
tempfile = "3.14"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
hole-punch = { git = "https://github.com/tiann/hole-punch" }
|
||||||
regex-lite = "0.1"
|
regex-lite = "0.1"
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
use anyhow::{Ok, Result};
|
use anyhow::{Ok, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use android_logger::Config;
|
use android_logger::Config;
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
|
|
||||||
use crate::defs::KSUD_VERBOSE_LOG_FILE;
|
|
||||||
use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, utils};
|
use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, utils};
|
||||||
|
|
||||||
/// KernelSU userspace cli
|
/// KernelSU userspace cli
|
||||||
@@ -16,9 +15,6 @@ use crate::{apk_sign, assets, debug, defs, init_event, ksucalls, module, utils};
|
|||||||
struct Args {
|
struct Args {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Commands,
|
command: Commands,
|
||||||
|
|
||||||
#[arg(short, long, default_value_t = cfg!(debug_assertions))]
|
|
||||||
verbose: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug)]
|
#[derive(clap::Subcommand, Debug)]
|
||||||
@@ -165,6 +161,17 @@ enum Debug {
|
|||||||
|
|
||||||
Mount,
|
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
|
/// For testing
|
||||||
Test,
|
Test,
|
||||||
}
|
}
|
||||||
@@ -224,6 +231,9 @@ enum Module {
|
|||||||
|
|
||||||
/// list all modules
|
/// list all modules
|
||||||
List,
|
List,
|
||||||
|
|
||||||
|
/// Shrink module image size
|
||||||
|
Shrink,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand, Debug)]
|
#[derive(clap::Subcommand, Debug)]
|
||||||
@@ -285,10 +295,6 @@ pub fn run() -> Result<()> {
|
|||||||
|
|
||||||
let cli = Args::parse();
|
let cli = Args::parse();
|
||||||
|
|
||||||
if !cli.verbose && !Path::new(KSUD_VERBOSE_LOG_FILE).exists() {
|
|
||||||
log::set_max_level(LevelFilter::Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("command: {:?}", cli.command);
|
log::info!("command: {:?}", cli.command);
|
||||||
|
|
||||||
let result = match cli.command {
|
let result = match cli.command {
|
||||||
@@ -308,6 +314,7 @@ pub fn run() -> Result<()> {
|
|||||||
Module::Disable { id } => module::disable_module(&id),
|
Module::Disable { id } => module::disable_module(&id),
|
||||||
Module::Action { id } => module::run_action(&id),
|
Module::Action { id } => module::run_action(&id),
|
||||||
Module::List => module::list_modules(),
|
Module::List => module::list_modules(),
|
||||||
|
Module::Shrink => module::shrink_ksu_images(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Commands::Install { magiskboot } => utils::install(magiskboot),
|
Commands::Install { magiskboot } => utils::install(magiskboot),
|
||||||
@@ -341,7 +348,15 @@ pub fn run() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Debug::Su { global_mnt } => crate::su::grant_root(global_mnt),
|
Debug::Su { global_mnt } => crate::su::grant_root(global_mnt),
|
||||||
Debug::Mount => init_event::mount_modules_systemlessly(),
|
Debug::Mount => init_event::mount_modules_systemlessly(defs::MODULE_DIR),
|
||||||
|
Debug::Xcp {
|
||||||
|
src,
|
||||||
|
dst,
|
||||||
|
punch_hole,
|
||||||
|
} => {
|
||||||
|
utils::copy_sparse_file(src, dst, punch_hole)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Debug::Test => assets::ensure_binaries(false),
|
Debug::Test => assets::ensure_binaries(false),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub const PROFILE_SELINUX_DIR: &str = concatcp!(PROFILE_DIR, "selinux/");
|
|||||||
pub const PROFILE_TEMPLATE_DIR: &str = concatcp!(PROFILE_DIR, "templates/");
|
pub const PROFILE_TEMPLATE_DIR: &str = concatcp!(PROFILE_DIR, "templates/");
|
||||||
|
|
||||||
pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc");
|
pub const KSURC_PATH: &str = concatcp!(WORKING_DIR, ".ksurc");
|
||||||
pub const KSU_MOUNT_SOURCE: &str = "KSU";
|
pub const KSU_OVERLAY_SOURCE: &str = "KSU";
|
||||||
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
|
pub const DAEMON_PATH: &str = concatcp!(ADB_DIR, "ksud");
|
||||||
pub const MAGISKBOOT_PATH: &str = concatcp!(BINARY_DIR, "magiskboot");
|
pub const MAGISKBOOT_PATH: &str = concatcp!(BINARY_DIR, "magiskboot");
|
||||||
|
|
||||||
@@ -18,11 +18,15 @@ pub const MAGISKBOOT_PATH: &str = concatcp!(BINARY_DIR, "magiskboot");
|
|||||||
pub const DAEMON_LINK_PATH: &str = concatcp!(BINARY_DIR, "ksud");
|
pub const DAEMON_LINK_PATH: &str = concatcp!(BINARY_DIR, "ksud");
|
||||||
|
|
||||||
pub const MODULE_DIR: &str = concatcp!(ADB_DIR, "modules/");
|
pub const MODULE_DIR: &str = concatcp!(ADB_DIR, "modules/");
|
||||||
|
pub const MODULE_IMG: &str = concatcp!(WORKING_DIR, "modules.img");
|
||||||
|
pub const MODULE_UPDATE_IMG: &str = concatcp!(WORKING_DIR, "modules_update.img");
|
||||||
|
|
||||||
|
pub const MODULE_UPDATE_TMP_IMG: &str = concatcp!(WORKING_DIR, "update_tmp.img");
|
||||||
|
|
||||||
// warning: this directory should not change, or you need to change the code in module_installer.sh!!!
|
// warning: this directory should not change, or you need to change the code in module_installer.sh!!!
|
||||||
pub const MODULE_UPDATE_DIR: &str = concatcp!(ADB_DIR, "modules_update/");
|
pub const MODULE_UPDATE_TMP_DIR: &str = concatcp!(ADB_DIR, "modules_update/");
|
||||||
|
|
||||||
pub const KSUD_VERBOSE_LOG_FILE: &str = concatcp!(ADB_DIR, "verbose");
|
pub const SYSTEM_RW_DIR: &str = concatcp!(MODULE_DIR, ".rw/");
|
||||||
|
|
||||||
pub const TEMP_DIR: &str = "/debug_ramdisk";
|
pub const TEMP_DIR: &str = "/debug_ramdisk";
|
||||||
pub const MODULE_WEB_DIR: &str = "webroot";
|
pub const MODULE_WEB_DIR: &str = "webroot";
|
||||||
@@ -31,7 +35,6 @@ pub const DISABLE_FILE_NAME: &str = "disable";
|
|||||||
pub const UPDATE_FILE_NAME: &str = "update";
|
pub const UPDATE_FILE_NAME: &str = "update";
|
||||||
pub const REMOVE_FILE_NAME: &str = "remove";
|
pub const REMOVE_FILE_NAME: &str = "remove";
|
||||||
pub const SKIP_MOUNT_FILE_NAME: &str = "skip_mount";
|
pub const SKIP_MOUNT_FILE_NAME: &str = "skip_mount";
|
||||||
pub const MAGIC_MOUNT_WORK_DIR: &str = concatcp!(TEMP_DIR, "/workdir");
|
|
||||||
|
|
||||||
pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE"));
|
pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE"));
|
||||||
pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME"));
|
pub const VERSION_NAME: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_NAME"));
|
||||||
|
|||||||
@@ -1,10 +1,100 @@
|
|||||||
use crate::defs::{KSU_MOUNT_SOURCE, TEMP_DIR};
|
use anyhow::{bail, Context, Result};
|
||||||
use crate::module::{handle_updated_modules, prune_modules};
|
|
||||||
use crate::{assets, defs, ksucalls, restorecon, utils};
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use rustix::fs::{mount, MountFlags};
|
use std::{collections::HashMap, path::Path};
|
||||||
use std::path::Path;
|
|
||||||
|
use crate::module::prune_modules;
|
||||||
|
use crate::{
|
||||||
|
assets, defs, ksucalls, mount, restorecon,
|
||||||
|
utils::{self, ensure_clean_dir},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn mount_partition(partition_name: &str, lowerdir: &Vec<String>) -> Result<()> {
|
||||||
|
if lowerdir.is_empty() {
|
||||||
|
warn!("partition: {partition_name} lowerdir is empty");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let partition = format!("/{partition_name}");
|
||||||
|
|
||||||
|
// if /partition is a symlink and linked to /system/partition, then we don't need to overlay it separately
|
||||||
|
if Path::new(&partition).read_link().is_ok() {
|
||||||
|
warn!("partition: {partition} is a symlink");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut workdir = None;
|
||||||
|
let mut upperdir = None;
|
||||||
|
let system_rw_dir = Path::new(defs::SYSTEM_RW_DIR);
|
||||||
|
if system_rw_dir.exists() {
|
||||||
|
workdir = Some(system_rw_dir.join(partition_name).join("workdir"));
|
||||||
|
upperdir = Some(system_rw_dir.join(partition_name).join("upperdir"));
|
||||||
|
}
|
||||||
|
|
||||||
|
mount::mount_overlay(&partition, lowerdir, workdir, upperdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount_modules_systemlessly(module_dir: &str) -> Result<()> {
|
||||||
|
// construct overlay mount params
|
||||||
|
let dir = std::fs::read_dir(module_dir);
|
||||||
|
let Ok(dir) = dir else {
|
||||||
|
bail!("open {} failed", defs::MODULE_DIR);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut system_lowerdir: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
let partition = vec!["vendor", "product", "system_ext", "odm", "oem"];
|
||||||
|
let mut partition_lowerdir: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
|
for ele in &partition {
|
||||||
|
partition_lowerdir.insert((*ele).to_string(), Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in dir.flatten() {
|
||||||
|
let module = entry.path();
|
||||||
|
if !module.is_dir() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let disabled = module.join(defs::DISABLE_FILE_NAME).exists();
|
||||||
|
if disabled {
|
||||||
|
info!("module: {} is disabled, ignore!", module.display());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let skip_mount = module.join(defs::SKIP_MOUNT_FILE_NAME).exists();
|
||||||
|
if skip_mount {
|
||||||
|
info!("module: {} skip_mount exist, skip!", module.display());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let module_system = Path::new(&module).join("system");
|
||||||
|
if module_system.is_dir() {
|
||||||
|
system_lowerdir.push(format!("{}", module_system.display()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for part in &partition {
|
||||||
|
// if /partition is a mountpoint, we would move it to $MODPATH/$partition when install
|
||||||
|
// otherwise it must be a symlink and we don't need to overlay!
|
||||||
|
let part_path = Path::new(&module).join(part);
|
||||||
|
if part_path.is_dir() {
|
||||||
|
if let Some(v) = partition_lowerdir.get_mut(*part) {
|
||||||
|
v.push(format!("{}", part_path.display()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mount /system first
|
||||||
|
if let Err(e) = mount_partition("system", &system_lowerdir) {
|
||||||
|
warn!("mount system failed: {:#}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mount other partitions
|
||||||
|
for (k, v) in partition_lowerdir {
|
||||||
|
if let Err(e) = mount_partition(&k, &v) {
|
||||||
|
warn!("mount {k} failed: {:#}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_post_data_fs() -> Result<()> {
|
pub fn on_post_data_fs() -> Result<()> {
|
||||||
ksucalls::report_post_fs_data();
|
ksucalls::report_post_fs_data();
|
||||||
@@ -21,9 +111,11 @@ pub fn on_post_data_fs() -> Result<()> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let safe_mode = utils::is_safe_mode();
|
let safe_mode = crate::utils::is_safe_mode();
|
||||||
|
|
||||||
if safe_mode {
|
if safe_mode {
|
||||||
|
// we should still mount modules.img to `/data/adb/modules` in safe mode
|
||||||
|
// becuase we may need to operate the module dir in safe mode
|
||||||
warn!("safe mode, skip common post-fs-data.d scripts");
|
warn!("safe mode, skip common post-fs-data.d scripts");
|
||||||
} else {
|
} else {
|
||||||
// Then exec common post-fs-data scripts
|
// Then exec common post-fs-data scripts
|
||||||
@@ -32,8 +124,43 @@ pub fn on_post_data_fs() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let module_update_img = defs::MODULE_UPDATE_IMG;
|
||||||
|
let module_img = defs::MODULE_IMG;
|
||||||
|
let module_dir = defs::MODULE_DIR;
|
||||||
|
let module_update_flag = Path::new(defs::WORKING_DIR).join(defs::UPDATE_FILE_NAME);
|
||||||
|
|
||||||
|
// modules.img is the default image
|
||||||
|
let mut target_update_img = &module_img;
|
||||||
|
|
||||||
|
// we should clean the module mount point if it exists
|
||||||
|
ensure_clean_dir(module_dir)?;
|
||||||
|
|
||||||
assets::ensure_binaries(true).with_context(|| "Failed to extract bin assets")?;
|
assets::ensure_binaries(true).with_context(|| "Failed to extract bin assets")?;
|
||||||
|
|
||||||
|
if Path::new(module_update_img).exists() {
|
||||||
|
if module_update_flag.exists() {
|
||||||
|
// if modules_update.img exists, and the the flag indicate this is an update
|
||||||
|
// this make sure that if the update failed, we will fallback to the old image
|
||||||
|
// if we boot succeed, we will rename the modules_update.img to modules.img #on_boot_complete
|
||||||
|
target_update_img = &module_update_img;
|
||||||
|
// And we should delete the flag immediately
|
||||||
|
std::fs::remove_file(module_update_flag)?;
|
||||||
|
} else {
|
||||||
|
// if modules_update.img exists, but the flag not exist, we should delete it
|
||||||
|
std::fs::remove_file(module_update_img)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Path::new(target_update_img).exists() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// we should always mount the module.img to module dir
|
||||||
|
// becuase we may need to operate the module dir in safe mode
|
||||||
|
info!("mount module image: {target_update_img} to {module_dir}");
|
||||||
|
mount::AutoMountExt4::try_new(target_update_img, module_dir, false)
|
||||||
|
.with_context(|| "mount module image failed".to_string())?;
|
||||||
|
|
||||||
// tell kernel that we've mount the module, so that it can do some optimization
|
// tell kernel that we've mount the module, so that it can do some optimization
|
||||||
ksucalls::report_module_mounted();
|
ksucalls::report_module_mounted();
|
||||||
|
|
||||||
@@ -50,10 +177,6 @@ pub fn on_post_data_fs() -> Result<()> {
|
|||||||
warn!("prune modules failed: {}", e);
|
warn!("prune modules failed: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = handle_updated_modules() {
|
|
||||||
warn!("handle updated modules failed: {}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = restorecon::restorecon() {
|
if let Err(e) = restorecon::restorecon() {
|
||||||
warn!("restorecon failed: {}", e);
|
warn!("restorecon failed: {}", e);
|
||||||
}
|
}
|
||||||
@@ -68,7 +191,7 @@ pub fn on_post_data_fs() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mount temp dir
|
// mount temp dir
|
||||||
if let Err(e) = mount(KSU_MOUNT_SOURCE, TEMP_DIR, "tmpfs", MountFlags::empty(), "") {
|
if let Err(e) = mount::mount_tmpfs(defs::TEMP_DIR) {
|
||||||
warn!("do temp dir mount failed: {}", e);
|
warn!("do temp dir mount failed: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,23 +206,15 @@ pub fn on_post_data_fs() -> Result<()> {
|
|||||||
warn!("load system.prop failed: {}", e);
|
warn!("load system.prop failed: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mount module systemlessly by magic mount
|
// mount module systemlessly by overlay
|
||||||
if let Err(e) = mount_modules_systemlessly() {
|
if let Err(e) = mount_modules_systemlessly(module_dir) {
|
||||||
warn!("do systemless mount failed: {}", e);
|
warn!("do systemless mount failed: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
run_stage("post-mount", true);
|
run_stage("post-mount", true);
|
||||||
|
|
||||||
Ok(())
|
std::env::set_current_dir("/").with_context(|| "failed to chdir to /")?;
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
pub fn mount_modules_systemlessly() -> Result<()> {
|
|
||||||
crate::magic_mount::magic_mount()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
pub fn mount_modules_systemlessly() -> Result<()> {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +249,17 @@ pub fn on_services() -> Result<()> {
|
|||||||
pub fn on_boot_completed() -> Result<()> {
|
pub fn on_boot_completed() -> Result<()> {
|
||||||
ksucalls::report_boot_complete();
|
ksucalls::report_boot_complete();
|
||||||
info!("on_boot_completed triggered!");
|
info!("on_boot_completed triggered!");
|
||||||
|
let module_update_img = Path::new(defs::MODULE_UPDATE_IMG);
|
||||||
|
let module_img = Path::new(defs::MODULE_IMG);
|
||||||
|
if module_update_img.exists() {
|
||||||
|
// this is a update and we successfully booted
|
||||||
|
if std::fs::rename(module_update_img, module_img).is_err() {
|
||||||
|
warn!("Failed to rename images, copy it now.",);
|
||||||
|
utils::copy_sparse_file(module_update_img, module_img, false)
|
||||||
|
.with_context(|| "Failed to copy images")?;
|
||||||
|
std::fs::remove_file(module_update_img).with_context(|| "Failed to remove image!")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
run_stage("boot-completed", false);
|
run_stage("boot-completed", false);
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ setup_flashable() {
|
|||||||
$BOOTMODE && return
|
$BOOTMODE && return
|
||||||
if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
|
if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
|
||||||
# We will have to manually find out OUTFD
|
# We will have to manually find out OUTFD
|
||||||
for FD in /proc/$$/fd/*; do
|
for FD in `ls /proc/$$/fd`; do
|
||||||
if readlink /proc/$$/fd/$FD | grep -q pipe; then
|
if readlink /proc/$$/fd/$FD | grep -q pipe; then
|
||||||
if ps | grep -v grep | grep -qE " 3 $FD |status_fd=$FD"; then
|
if ps | grep -v grep | grep -qE " 3 $FD |status_fd=$FD"; then
|
||||||
OUTFD=$FD
|
OUTFD=$FD
|
||||||
@@ -302,16 +302,18 @@ is_legacy_script() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handle_partition() {
|
handle_partition() {
|
||||||
PARTITION="$1"
|
# if /system/vendor is a symlink, we need to move it out of $MODPATH/system, otherwise it will be overlayed
|
||||||
REQUIRE_SYMLINK="$2"
|
# if /system/vendor is a normal directory, it is ok to overlay it and we don't need to overlay it separately.
|
||||||
if [ ! -e "$MODPATH/system/$PARTITION" ]; then
|
if [ ! -e $MODPATH/system/$1 ]; then
|
||||||
# no partition found
|
# no partition found
|
||||||
return;
|
return;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$REQUIRE_SYMLINK" = "false" ] || [ -L "/system/$PARTITION" ] && [ "$(readlink -f "/system/$PARTITION")" = "/$PARTITION" ]; then
|
if [ -L "/system/$1" ] && [ "$(readlink -f /system/$1)" = "/$1" ]; then
|
||||||
ui_print "- Handle partition /$PARTITION"
|
ui_print "- Handle partition /$1"
|
||||||
ln -sf "./system/$PARTITION" "$MODPATH/$PARTITION"
|
# we create a symlink if module want to access $MODPATH/system/$1
|
||||||
|
# but it doesn't always work(ie. write it in post-fs-data.sh would fail because it is readonly)
|
||||||
|
mv -f $MODPATH/system/$1 $MODPATH/$1 && ln -sf ../$1 $MODPATH/system/$1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,23 +391,22 @@ install_module() {
|
|||||||
[ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
|
[ -f $MODPATH/customize.sh ] && . $MODPATH/customize.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
handle_partition vendor true
|
|
||||||
handle_partition system_ext true
|
|
||||||
handle_partition product true
|
|
||||||
handle_partition odm false
|
|
||||||
|
|
||||||
# Handle replace folders
|
# Handle replace folders
|
||||||
for TARGET in $REPLACE; do
|
for TARGET in $REPLACE; do
|
||||||
ui_print "- Replace target: $TARGET"
|
ui_print "- Replace target: $TARGET"
|
||||||
mark_replace "$MODPATH$TARGET"
|
mark_replace $MODPATH$TARGET
|
||||||
done
|
done
|
||||||
|
|
||||||
# Handle remove files
|
# Handle remove files
|
||||||
for TARGET in $REMOVE; do
|
for TARGET in $REMOVE; do
|
||||||
ui_print "- Remove target: $TARGET"
|
ui_print "- Remove target: $TARGET"
|
||||||
mark_remove "$MODPATH$TARGET"
|
mark_remove $MODPATH$TARGET
|
||||||
done
|
done
|
||||||
|
|
||||||
|
handle_partition vendor
|
||||||
|
handle_partition system_ext
|
||||||
|
handle_partition product
|
||||||
|
|
||||||
if $BOOTMODE; then
|
if $BOOTMODE; then
|
||||||
mktouch $NVBASE/modules/$MODID/update
|
mktouch $NVBASE/modules/$MODID/update
|
||||||
rm -rf $NVBASE/modules/$MODID/remove 2>/dev/null
|
rm -rf $NVBASE/modules/$MODID/remove 2>/dev/null
|
||||||
|
|||||||
@@ -1,434 +0,0 @@
|
|||||||
use crate::defs::{
|
|
||||||
DISABLE_FILE_NAME, KSU_MOUNT_SOURCE, MAGIC_MOUNT_WORK_DIR, MODULE_DIR, SKIP_MOUNT_FILE_NAME,
|
|
||||||
};
|
|
||||||
use crate::magic_mount::NodeFileType::{Directory, RegularFile, Symlink, Whiteout};
|
|
||||||
use crate::restorecon::{lgetfilecon, lsetfilecon};
|
|
||||||
use crate::utils::ensure_dir_exists;
|
|
||||||
use anyhow::{bail, Context, Result};
|
|
||||||
use extattr::lgetxattr;
|
|
||||||
use rustix::fs::{
|
|
||||||
bind_mount, chmod, chown, mount, move_mount, unmount, Gid, MetadataExt, Mode, MountFlags,
|
|
||||||
MountPropagationFlags, Uid, UnmountFlags,
|
|
||||||
};
|
|
||||||
use rustix::mount::mount_change;
|
|
||||||
use rustix::path::Arg;
|
|
||||||
use std::cmp::PartialEq;
|
|
||||||
use std::collections::hash_map::Entry;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs;
|
|
||||||
use std::fs::{create_dir, create_dir_all, read_dir, read_link, DirEntry, FileType};
|
|
||||||
use std::os::unix::fs::{symlink, FileTypeExt};
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
const REPLACE_DIR_XATTR: &str = "trusted.overlay.opaque";
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
|
||||||
enum NodeFileType {
|
|
||||||
RegularFile,
|
|
||||||
Directory,
|
|
||||||
Symlink,
|
|
||||||
Whiteout,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeFileType {
|
|
||||||
fn from_file_type(file_type: FileType) -> Option<Self> {
|
|
||||||
if file_type.is_file() {
|
|
||||||
Some(RegularFile)
|
|
||||||
} else if file_type.is_dir() {
|
|
||||||
Some(Directory)
|
|
||||||
} else if file_type.is_symlink() {
|
|
||||||
Some(Symlink)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Node {
|
|
||||||
name: String,
|
|
||||||
file_type: NodeFileType,
|
|
||||||
children: HashMap<String, Node>,
|
|
||||||
// the module that owned this node
|
|
||||||
module_path: Option<PathBuf>,
|
|
||||||
replace: bool,
|
|
||||||
skip: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
fn collect_module_files<T: AsRef<Path>>(&mut self, module_dir: T) -> Result<bool> {
|
|
||||||
let dir = module_dir.as_ref();
|
|
||||||
let mut has_file = false;
|
|
||||||
for entry in dir.read_dir()?.flatten() {
|
|
||||||
let name = entry.file_name().to_string_lossy().to_string();
|
|
||||||
|
|
||||||
let node = match self.children.entry(name.clone()) {
|
|
||||||
Entry::Occupied(o) => Some(o.into_mut()),
|
|
||||||
Entry::Vacant(v) => Self::new_module(&name, &entry).map(|it| v.insert(it)),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(node) = node {
|
|
||||||
has_file |= if node.file_type == Directory {
|
|
||||||
node.collect_module_files(dir.join(&node.name))? || node.replace
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(has_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_root<T: ToString>(name: T) -> Self {
|
|
||||||
Node {
|
|
||||||
name: name.to_string(),
|
|
||||||
file_type: Directory,
|
|
||||||
children: Default::default(),
|
|
||||||
module_path: None,
|
|
||||||
replace: false,
|
|
||||||
skip: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_module<T: ToString>(name: T, entry: &DirEntry) -> Option<Self> {
|
|
||||||
if let Ok(metadata) = entry.metadata() {
|
|
||||||
let path = entry.path();
|
|
||||||
let file_type = if metadata.file_type().is_char_device() && metadata.rdev() == 0 {
|
|
||||||
Some(Whiteout)
|
|
||||||
} else {
|
|
||||||
NodeFileType::from_file_type(metadata.file_type())
|
|
||||||
};
|
|
||||||
if let Some(file_type) = file_type {
|
|
||||||
let mut replace = false;
|
|
||||||
if file_type == Directory {
|
|
||||||
if let Ok(v) = lgetxattr(&path, REPLACE_DIR_XATTR) {
|
|
||||||
if String::from_utf8_lossy(&v) == "y" {
|
|
||||||
replace = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Some(Node {
|
|
||||||
name: name.to_string(),
|
|
||||||
file_type,
|
|
||||||
children: Default::default(),
|
|
||||||
module_path: Some(path),
|
|
||||||
replace,
|
|
||||||
skip: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_module_files() -> Result<Option<Node>> {
|
|
||||||
let mut root = Node::new_root("");
|
|
||||||
let mut system = Node::new_root("system");
|
|
||||||
let module_root = Path::new(MODULE_DIR);
|
|
||||||
let mut has_file = false;
|
|
||||||
for entry in module_root.read_dir()?.flatten() {
|
|
||||||
if !entry.file_type()?.is_dir() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry.path().join(DISABLE_FILE_NAME).exists()
|
|
||||||
|| entry.path().join(SKIP_MOUNT_FILE_NAME).exists()
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mod_system = entry.path().join("system");
|
|
||||||
if !mod_system.is_dir() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::debug!("collecting {}", entry.path().display());
|
|
||||||
|
|
||||||
has_file |= system.collect_module_files(&mod_system)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_file {
|
|
||||||
for (partition, require_symlink) in [
|
|
||||||
("vendor", true),
|
|
||||||
("system_ext", true),
|
|
||||||
("product", true),
|
|
||||||
("odm", false),
|
|
||||||
] {
|
|
||||||
let path_of_root = Path::new("/").join(partition);
|
|
||||||
let path_of_system = Path::new("/system").join(partition);
|
|
||||||
if path_of_root.is_dir() && (!require_symlink || path_of_system.is_symlink()) {
|
|
||||||
let name = partition.to_string();
|
|
||||||
if let Some(node) = system.children.remove(&name) {
|
|
||||||
root.children.insert(name, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
root.children.insert("system".to_string(), system);
|
|
||||||
Ok(Some(root))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_symlink<Src: AsRef<Path>, Dst: AsRef<Path>>(src: Src, dst: Dst) -> Result<()> {
|
|
||||||
let src_symlink = read_link(src.as_ref())?;
|
|
||||||
symlink(&src_symlink, dst.as_ref())?;
|
|
||||||
lsetfilecon(dst.as_ref(), lgetfilecon(src.as_ref())?.as_str())?;
|
|
||||||
log::debug!(
|
|
||||||
"clone symlink {} -> {}({})",
|
|
||||||
dst.as_ref().display(),
|
|
||||||
dst.as_ref().display(),
|
|
||||||
src_symlink.display()
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mount_mirror<P: AsRef<Path>, WP: AsRef<Path>>(
|
|
||||||
path: P,
|
|
||||||
work_dir_path: WP,
|
|
||||||
entry: &DirEntry,
|
|
||||||
) -> Result<()> {
|
|
||||||
let path = path.as_ref().join(entry.file_name());
|
|
||||||
let work_dir_path = work_dir_path.as_ref().join(entry.file_name());
|
|
||||||
let file_type = entry.file_type()?;
|
|
||||||
|
|
||||||
if file_type.is_file() {
|
|
||||||
log::debug!(
|
|
||||||
"mount mirror file {} -> {}",
|
|
||||||
path.display(),
|
|
||||||
work_dir_path.display()
|
|
||||||
);
|
|
||||||
fs::File::create(&work_dir_path)?;
|
|
||||||
bind_mount(&path, &work_dir_path)?;
|
|
||||||
} else if file_type.is_dir() {
|
|
||||||
log::debug!(
|
|
||||||
"mount mirror dir {} -> {}",
|
|
||||||
path.display(),
|
|
||||||
work_dir_path.display()
|
|
||||||
);
|
|
||||||
create_dir(&work_dir_path)?;
|
|
||||||
let metadata = entry.metadata()?;
|
|
||||||
chmod(&work_dir_path, Mode::from_raw_mode(metadata.mode()))?;
|
|
||||||
unsafe {
|
|
||||||
chown(
|
|
||||||
&work_dir_path,
|
|
||||||
Some(Uid::from_raw(metadata.uid())),
|
|
||||||
Some(Gid::from_raw(metadata.gid())),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
lsetfilecon(&work_dir_path, lgetfilecon(&path)?.as_str())?;
|
|
||||||
for entry in read_dir(&path)?.flatten() {
|
|
||||||
mount_mirror(&path, &work_dir_path, &entry)?;
|
|
||||||
}
|
|
||||||
} else if file_type.is_symlink() {
|
|
||||||
log::debug!(
|
|
||||||
"create mirror symlink {} -> {}",
|
|
||||||
path.display(),
|
|
||||||
work_dir_path.display()
|
|
||||||
);
|
|
||||||
clone_symlink(&path, &work_dir_path)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_magic_mount<P: AsRef<Path>, WP: AsRef<Path>>(
|
|
||||||
path: P,
|
|
||||||
work_dir_path: WP,
|
|
||||||
current: Node,
|
|
||||||
has_tmpfs: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut current = current;
|
|
||||||
let path = path.as_ref().join(¤t.name);
|
|
||||||
let work_dir_path = work_dir_path.as_ref().join(¤t.name);
|
|
||||||
match current.file_type {
|
|
||||||
RegularFile => {
|
|
||||||
let target_path = if has_tmpfs {
|
|
||||||
fs::File::create(&work_dir_path)?;
|
|
||||||
&work_dir_path
|
|
||||||
} else {
|
|
||||||
&path
|
|
||||||
};
|
|
||||||
if let Some(module_path) = ¤t.module_path {
|
|
||||||
log::debug!(
|
|
||||||
"mount module file {} -> {}",
|
|
||||||
module_path.display(),
|
|
||||||
work_dir_path.display()
|
|
||||||
);
|
|
||||||
bind_mount(module_path, target_path)?;
|
|
||||||
} else {
|
|
||||||
bail!("cannot mount root file {}!", path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Symlink => {
|
|
||||||
if let Some(module_path) = ¤t.module_path {
|
|
||||||
log::debug!(
|
|
||||||
"create module symlink {} -> {}",
|
|
||||||
module_path.display(),
|
|
||||||
work_dir_path.display()
|
|
||||||
);
|
|
||||||
clone_symlink(module_path, &work_dir_path)?;
|
|
||||||
} else {
|
|
||||||
bail!("cannot mount root symlink {}!", path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Directory => {
|
|
||||||
let mut create_tmpfs = !has_tmpfs && current.replace && current.module_path.is_some();
|
|
||||||
if !has_tmpfs && !create_tmpfs {
|
|
||||||
for it in &mut current.children {
|
|
||||||
let (name, node) = it;
|
|
||||||
let real_path = path.join(name);
|
|
||||||
let need = match node.file_type {
|
|
||||||
Symlink => true,
|
|
||||||
Whiteout => real_path.exists(),
|
|
||||||
_ => {
|
|
||||||
if let Ok(metadata) = real_path.metadata() {
|
|
||||||
let file_type = NodeFileType::from_file_type(metadata.file_type())
|
|
||||||
.unwrap_or(Whiteout);
|
|
||||||
file_type != node.file_type || file_type == Symlink
|
|
||||||
} else {
|
|
||||||
// real path not exists
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if need {
|
|
||||||
if current.module_path.is_none() {
|
|
||||||
log::error!(
|
|
||||||
"cannot create tmpfs on {}, ignore: {name}",
|
|
||||||
path.display()
|
|
||||||
);
|
|
||||||
node.skip = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
create_tmpfs = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let has_tmpfs = has_tmpfs || create_tmpfs;
|
|
||||||
|
|
||||||
if has_tmpfs {
|
|
||||||
log::debug!(
|
|
||||||
"creating tmpfs skeleton for {} at {}",
|
|
||||||
path.display(),
|
|
||||||
work_dir_path.display()
|
|
||||||
);
|
|
||||||
create_dir_all(&work_dir_path)?;
|
|
||||||
let (metadata, path) = if path.exists() {
|
|
||||||
(path.metadata()?, &path)
|
|
||||||
} else if let Some(module_path) = ¤t.module_path {
|
|
||||||
(module_path.metadata()?, module_path)
|
|
||||||
} else {
|
|
||||||
bail!("cannot mount root dir {}!", path.display());
|
|
||||||
};
|
|
||||||
chmod(&work_dir_path, Mode::from_raw_mode(metadata.mode()))?;
|
|
||||||
unsafe {
|
|
||||||
chown(
|
|
||||||
&work_dir_path,
|
|
||||||
Some(Uid::from_raw(metadata.uid())),
|
|
||||||
Some(Gid::from_raw(metadata.gid())),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
lsetfilecon(&work_dir_path, lgetfilecon(path)?.as_str())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if create_tmpfs {
|
|
||||||
log::debug!(
|
|
||||||
"creating tmpfs for {} at {}",
|
|
||||||
path.display(),
|
|
||||||
work_dir_path.display()
|
|
||||||
);
|
|
||||||
bind_mount(&work_dir_path, &work_dir_path).context("bind self")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.exists() && !current.replace {
|
|
||||||
for entry in path.read_dir()?.flatten() {
|
|
||||||
let name = entry.file_name().to_string_lossy().to_string();
|
|
||||||
let result = if let Some(node) = current.children.remove(&name) {
|
|
||||||
if node.skip {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
do_magic_mount(&path, &work_dir_path, node, has_tmpfs)
|
|
||||||
.with_context(|| format!("magic mount {}/{name}", path.display()))
|
|
||||||
} else if has_tmpfs {
|
|
||||||
mount_mirror(&path, &work_dir_path, &entry)
|
|
||||||
.with_context(|| format!("mount mirror {}/{name}", path.display()))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = result {
|
|
||||||
if has_tmpfs {
|
|
||||||
return Err(e);
|
|
||||||
} else {
|
|
||||||
log::error!("mount child {}/{name} failed: {}", path.display(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if current.replace {
|
|
||||||
if current.module_path.is_none() {
|
|
||||||
bail!(
|
|
||||||
"dir {} is declared as replaced but it is root!",
|
|
||||||
path.display()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
log::debug!("dir {} is replaced", path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (name, node) in current.children.into_iter() {
|
|
||||||
if node.skip {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if let Err(e) = do_magic_mount(&path, &work_dir_path, node, has_tmpfs)
|
|
||||||
.with_context(|| format!("magic mount {}/{name}", path.display()))
|
|
||||||
{
|
|
||||||
if has_tmpfs {
|
|
||||||
return Err(e);
|
|
||||||
} else {
|
|
||||||
log::error!("mount child {}/{name} failed: {}", path.display(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if create_tmpfs {
|
|
||||||
log::debug!(
|
|
||||||
"moving tmpfs {} -> {}",
|
|
||||||
work_dir_path.display(),
|
|
||||||
path.display()
|
|
||||||
);
|
|
||||||
move_mount(&work_dir_path, &path).context("move self")?;
|
|
||||||
mount_change(&path, MountPropagationFlags::PRIVATE).context("make self private")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Whiteout => {
|
|
||||||
log::debug!("file {} is removed", path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn magic_mount() -> Result<()> {
|
|
||||||
if let Some(root) = collect_module_files()? {
|
|
||||||
log::debug!("collected: {:#?}", root);
|
|
||||||
let tmp_dir = PathBuf::from(MAGIC_MOUNT_WORK_DIR);
|
|
||||||
ensure_dir_exists(&tmp_dir)?;
|
|
||||||
mount(KSU_MOUNT_SOURCE, &tmp_dir, "tmpfs", MountFlags::empty(), "").context("mount tmp")?;
|
|
||||||
mount_change(&tmp_dir, MountPropagationFlags::PRIVATE).context("make tmp private")?;
|
|
||||||
let result = do_magic_mount("/", &tmp_dir, root, false);
|
|
||||||
if let Err(e) = unmount(&tmp_dir, UnmountFlags::DETACH) {
|
|
||||||
log::error!("failed to unmount tmp {}", e);
|
|
||||||
}
|
|
||||||
fs::remove_dir(tmp_dir).ok();
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
log::info!("no modules to mount, skipping!");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,9 +6,8 @@ mod debug;
|
|||||||
mod defs;
|
mod defs;
|
||||||
mod init_event;
|
mod init_event;
|
||||||
mod ksucalls;
|
mod ksucalls;
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
mod magic_mount;
|
|
||||||
mod module;
|
mod module;
|
||||||
|
mod mount;
|
||||||
mod profile;
|
mod profile;
|
||||||
mod restorecon;
|
mod restorecon;
|
||||||
mod sepolicy;
|
mod sepolicy;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#[allow(clippy::wildcard_imports)]
|
#[allow(clippy::wildcard_imports)]
|
||||||
use crate::utils::*;
|
use crate::utils::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
assets, defs, ksucalls,
|
assets, defs, ksucalls, mount,
|
||||||
restorecon::{restore_syscon, setsyscon},
|
restorecon::{restore_syscon, setsyscon},
|
||||||
sepolicy,
|
sepolicy, utils,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||||
@@ -12,21 +12,20 @@ use is_executable::is_executable;
|
|||||||
use java_properties::PropertiesIter;
|
use java_properties::PropertiesIter;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
use std::fs::{copy, rename};
|
use std::fs::OpenOptions;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
env::var as env_var,
|
env::var as env_var,
|
||||||
fs::{remove_dir_all, remove_file, set_permissions, File, Permissions},
|
fs::{remove_dir_all, remove_file, set_permissions, File, Permissions},
|
||||||
io::Cursor,
|
io::Cursor,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::Command,
|
process::{Command, Stdio},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
use zip_extensions::zip_extract_file_to_memory;
|
use zip_extensions::zip_extract_file_to_memory;
|
||||||
|
|
||||||
use crate::defs::{MODULE_DIR, MODULE_UPDATE_DIR, UPDATE_FILE_NAME};
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::{prelude::PermissionsExt, process::CommandExt};
|
use std::os::unix::{fs::MetadataExt, prelude::PermissionsExt, process::CommandExt};
|
||||||
|
|
||||||
const INSTALLER_CONTENT: &str = include_str!("./installer.sh");
|
const INSTALLER_CONTENT: &str = include_str!("./installer.sh");
|
||||||
const INSTALL_MODULE_SCRIPT: &str = concatcp!(
|
const INSTALL_MODULE_SCRIPT: &str = concatcp!(
|
||||||
@@ -76,34 +75,24 @@ fn ensure_boot_completed() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_module_state(module: &str, flag_file: &str, create: bool) -> Result<()> {
|
fn mark_update() -> Result<()> {
|
||||||
let module_state_file = Path::new(MODULE_DIR).join(module).join(flag_file);
|
ensure_file_exists(concatcp!(defs::WORKING_DIR, defs::UPDATE_FILE_NAME))
|
||||||
if create {
|
}
|
||||||
|
|
||||||
|
fn mark_module_state(module: &str, flag_file: &str, create_or_delete: bool) -> Result<()> {
|
||||||
|
let module_state_file = Path::new(defs::MODULE_DIR).join(module).join(flag_file);
|
||||||
|
if create_or_delete {
|
||||||
ensure_file_exists(module_state_file)
|
ensure_file_exists(module_state_file)
|
||||||
} else {
|
} else {
|
||||||
if module_state_file.exists() {
|
if module_state_file.exists() {
|
||||||
remove_file(module_state_file)?;
|
std::fs::remove_file(module_state_file)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
fn foreach_module(active_only: bool, mut f: impl FnMut(&Path) -> Result<()>) -> Result<()> {
|
||||||
enum ModuleType {
|
let modules_dir = Path::new(defs::MODULE_DIR);
|
||||||
All,
|
|
||||||
Active,
|
|
||||||
Updated,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn foreach_module(module_type: ModuleType, mut f: impl FnMut(&Path) -> Result<()>) -> Result<()> {
|
|
||||||
let modules_dir = Path::new(match module_type {
|
|
||||||
ModuleType::Updated => MODULE_UPDATE_DIR,
|
|
||||||
_ => defs::MODULE_DIR,
|
|
||||||
});
|
|
||||||
if !modules_dir.is_dir() {
|
|
||||||
warn!("{} is not a directory, skip", modules_dir.display());
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let dir = std::fs::read_dir(modules_dir)?;
|
let dir = std::fs::read_dir(modules_dir)?;
|
||||||
for entry in dir.flatten() {
|
for entry in dir.flatten() {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
@@ -112,11 +101,11 @@ fn foreach_module(module_type: ModuleType, mut f: impl FnMut(&Path) -> Result<()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if module_type == ModuleType::Active && path.join(defs::DISABLE_FILE_NAME).exists() {
|
if active_only && path.join(defs::DISABLE_FILE_NAME).exists() {
|
||||||
info!("{} is disabled, skip", path.display());
|
info!("{} is disabled, skip", path.display());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if module_type == ModuleType::Active && path.join(defs::REMOVE_FILE_NAME).exists() {
|
if active_only && path.join(defs::REMOVE_FILE_NAME).exists() {
|
||||||
warn!("{} is removed, skip", path.display());
|
warn!("{} is removed, skip", path.display());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -128,7 +117,27 @@ fn foreach_module(module_type: ModuleType, mut f: impl FnMut(&Path) -> Result<()
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn foreach_active_module(f: impl FnMut(&Path) -> Result<()>) -> Result<()> {
|
fn foreach_active_module(f: impl FnMut(&Path) -> Result<()>) -> Result<()> {
|
||||||
foreach_module(ModuleType::Active, f)
|
foreach_module(true, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_image(img: &str) -> Result<()> {
|
||||||
|
let result = Command::new("e2fsck")
|
||||||
|
.args(["-yf", img])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.status()
|
||||||
|
.with_context(|| format!("Failed to exec e2fsck {img}"))?;
|
||||||
|
let code = result.code();
|
||||||
|
// 0 or 1 is ok
|
||||||
|
// 0: no error
|
||||||
|
// 1: file system errors corrected
|
||||||
|
// https://man7.org/linux/man-pages/man8/e2fsck.8.html
|
||||||
|
// ensure!(
|
||||||
|
// code == Some(0) || code == Some(1),
|
||||||
|
// "Failed to check image, e2fsck exit code: {}",
|
||||||
|
// code.unwrap_or(-1)
|
||||||
|
// );
|
||||||
|
info!("e2fsck exit code: {}", code.unwrap_or(-1));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_sepolicy_rule() -> Result<()> {
|
pub fn load_sepolicy_rule() -> Result<()> {
|
||||||
@@ -247,149 +256,318 @@ pub fn load_system_prop() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn prune_modules() -> Result<()> {
|
pub fn prune_modules() -> Result<()> {
|
||||||
foreach_module(ModuleType::All, |module| {
|
foreach_module(false, |module| {
|
||||||
if module.join(defs::REMOVE_FILE_NAME).exists() {
|
remove_file(module.join(defs::UPDATE_FILE_NAME)).ok();
|
||||||
info!("remove module: {}", module.display());
|
|
||||||
|
|
||||||
let uninstaller = module.join("uninstall.sh");
|
if !module.join(defs::REMOVE_FILE_NAME).exists() {
|
||||||
if uninstaller.exists() {
|
return Ok(());
|
||||||
if let Err(e) = exec_script(uninstaller, true) {
|
|
||||||
warn!("Failed to exec uninstaller: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = remove_dir_all(module) {
|
|
||||||
warn!("Failed to remove {}: {}", module.display(), e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
remove_file(module.join(defs::UPDATE_FILE_NAME)).ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("remove module: {}", module.display());
|
||||||
|
|
||||||
|
let uninstaller = module.join("uninstall.sh");
|
||||||
|
if uninstaller.exists() {
|
||||||
|
if let Err(e) = exec_script(uninstaller, true) {
|
||||||
|
warn!("Failed to exec uninstaller: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = remove_dir_all(module) {
|
||||||
|
warn!("Failed to remove {}: {}", module.display(), e);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_updated_modules() -> Result<()> {
|
fn create_module_image(image: &str, image_size: u64, journal_size: u64) -> Result<()> {
|
||||||
let modules_root = Path::new(MODULE_DIR);
|
File::create(image)
|
||||||
foreach_module(ModuleType::Updated, |module| {
|
.context("Failed to create ext4 image file")?
|
||||||
if !module.is_dir() {
|
.set_len(image_size)
|
||||||
return Ok(());
|
.context("Failed to truncate ext4 image")?;
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(name) = module.file_name() {
|
// format the img to ext4 filesystem
|
||||||
let old_dir = modules_root.join(name);
|
let result = Command::new("mkfs.ext4")
|
||||||
if old_dir.exists() {
|
.arg("-J")
|
||||||
if let Err(e) = remove_dir_all(&old_dir) {
|
.arg(format!("size={journal_size}"))
|
||||||
log::error!("Failed to remove old {}: {}", old_dir.display(), e);
|
.arg(image)
|
||||||
}
|
.stdout(Stdio::piped())
|
||||||
}
|
.output()?;
|
||||||
if let Err(e) = rename(module, &old_dir) {
|
ensure!(
|
||||||
log::error!("Failed to move new module {}: {}", module.display(), e);
|
result.status.success(),
|
||||||
|
"Failed to format ext4 image: {}",
|
||||||
|
String::from_utf8(result.stderr).unwrap()
|
||||||
|
);
|
||||||
|
check_image(image)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn _install_module(zip: &str) -> Result<()> {
|
||||||
|
ensure_boot_completed()?;
|
||||||
|
|
||||||
|
// print banner
|
||||||
|
println!(include_str!("banner"));
|
||||||
|
|
||||||
|
assets::ensure_binaries(false).with_context(|| "Failed to extract assets")?;
|
||||||
|
|
||||||
|
// first check if workding dir is usable
|
||||||
|
ensure_dir_exists(defs::WORKING_DIR).with_context(|| "Failed to create working dir")?;
|
||||||
|
ensure_dir_exists(defs::BINARY_DIR).with_context(|| "Failed to create bin dir")?;
|
||||||
|
|
||||||
|
// read the module_id from zip, if faild if will return early.
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
let entry_path = PathBuf::from_str("module.prop")?;
|
||||||
|
let zip_path = PathBuf::from_str(zip)?;
|
||||||
|
let zip_path = zip_path.canonicalize()?;
|
||||||
|
zip_extract_file_to_memory(&zip_path, &entry_path, &mut buffer)?;
|
||||||
|
|
||||||
|
let mut module_prop = HashMap::new();
|
||||||
|
PropertiesIter::new_with_encoding(Cursor::new(buffer), encoding_rs::UTF_8).read_into(
|
||||||
|
|k, v| {
|
||||||
|
module_prop.insert(k, v);
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
info!("module prop: {:?}", module_prop);
|
||||||
|
|
||||||
|
let Some(module_id) = module_prop.get("id") else {
|
||||||
|
bail!("module id not found in module.prop!");
|
||||||
|
};
|
||||||
|
let module_id = module_id.trim();
|
||||||
|
|
||||||
|
let modules_img = Path::new(defs::MODULE_IMG);
|
||||||
|
let modules_update_img = Path::new(defs::MODULE_UPDATE_IMG);
|
||||||
|
let module_update_tmp_dir = defs::MODULE_UPDATE_TMP_DIR;
|
||||||
|
|
||||||
|
let modules_img_exist = modules_img.exists();
|
||||||
|
let modules_update_img_exist = modules_update_img.exists();
|
||||||
|
|
||||||
|
// prepare the tmp module img
|
||||||
|
let tmp_module_img = defs::MODULE_UPDATE_TMP_IMG;
|
||||||
|
let tmp_module_path = Path::new(tmp_module_img);
|
||||||
|
if tmp_module_path.exists() {
|
||||||
|
std::fs::remove_file(tmp_module_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let zip_uncompressed_size = get_zip_uncompressed_size(zip)?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"zip uncompressed size: {}",
|
||||||
|
humansize::format_size(zip_uncompressed_size, humansize::DECIMAL)
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("- Preparing image");
|
||||||
|
println!(
|
||||||
|
"- Module size: {}",
|
||||||
|
humansize::format_size(zip_uncompressed_size, humansize::DECIMAL)
|
||||||
|
);
|
||||||
|
|
||||||
|
let sparse_image_size = 1 << 34; // 16G
|
||||||
|
let journal_size = 8; // 8M
|
||||||
|
if !modules_img_exist && !modules_update_img_exist {
|
||||||
|
// if no modules and modules_update, it is brand new installation, we should create a new img
|
||||||
|
// create a tmp module img and mount it to modules_update
|
||||||
|
info!("Creating brand new module image");
|
||||||
|
create_module_image(tmp_module_img, sparse_image_size, journal_size)?;
|
||||||
|
} else if modules_update_img_exist {
|
||||||
|
// modules_update.img exists, we should use it as tmp img
|
||||||
|
info!("Using existing modules_update.img as tmp image");
|
||||||
|
utils::copy_sparse_file(modules_update_img, tmp_module_img, true).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Failed to copy {} to {}",
|
||||||
|
modules_update_img.display(),
|
||||||
|
tmp_module_img
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
// modules.img exists, we should use it as tmp img
|
||||||
|
info!("Using existing modules.img as tmp image");
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let blksize = std::fs::metadata(defs::MODULE_DIR)?.blksize();
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
let blksize = 0;
|
||||||
|
// legacy image, it's block size is 1024 with unlimited journal size
|
||||||
|
if blksize == 1024 {
|
||||||
|
println!("- Legacy image, migrating to new format, please be patient...");
|
||||||
|
create_module_image(tmp_module_img, sparse_image_size, journal_size)?;
|
||||||
|
let _dontdrop =
|
||||||
|
mount::AutoMountExt4::try_new(tmp_module_img, module_update_tmp_dir, true)
|
||||||
|
.with_context(|| format!("Failed to mount {tmp_module_img}"))?;
|
||||||
|
utils::copy_module_files(defs::MODULE_DIR, module_update_tmp_dir)
|
||||||
|
.with_context(|| "Failed to migrate module files".to_string())?;
|
||||||
|
} else {
|
||||||
|
utils::copy_sparse_file(modules_img, tmp_module_img, true)
|
||||||
|
.with_context(|| "Failed to copy module image".to_string())?;
|
||||||
|
|
||||||
|
if std::fs::metadata(tmp_module_img)?.len() < sparse_image_size {
|
||||||
|
// truncate the file to new size
|
||||||
|
OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.open(tmp_module_img)
|
||||||
|
.context("Failed to open ext4 image")?
|
||||||
|
.set_len(sparse_image_size)
|
||||||
|
.context("Failed to truncate ext4 image")?;
|
||||||
|
|
||||||
|
// resize the image to new size
|
||||||
|
check_image(tmp_module_img)?;
|
||||||
|
Command::new("resize2fs")
|
||||||
|
.arg(tmp_module_img)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.status()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
}
|
||||||
})?;
|
|
||||||
|
// ensure modules_update exists
|
||||||
|
ensure_dir_exists(module_update_tmp_dir)?;
|
||||||
|
|
||||||
|
// mount the modules_update.img to mountpoint
|
||||||
|
println!("- Mounting image");
|
||||||
|
|
||||||
|
let _dontdrop = mount::AutoMountExt4::try_new(tmp_module_img, module_update_tmp_dir, true)?;
|
||||||
|
|
||||||
|
info!("mounted {} to {}", tmp_module_img, module_update_tmp_dir);
|
||||||
|
|
||||||
|
setsyscon(module_update_tmp_dir)?;
|
||||||
|
|
||||||
|
let module_dir = format!("{module_update_tmp_dir}/{module_id}");
|
||||||
|
ensure_clean_dir(&module_dir)?;
|
||||||
|
info!("module dir: {}", module_dir);
|
||||||
|
|
||||||
|
// unzip the image and move it to modules_update/<id> dir
|
||||||
|
let file = File::open(zip)?;
|
||||||
|
let mut archive = zip::ZipArchive::new(file)?;
|
||||||
|
archive.extract(&module_dir)?;
|
||||||
|
|
||||||
|
// set permission and selinux context for $MOD/system
|
||||||
|
let module_system_dir = PathBuf::from(module_dir).join("system");
|
||||||
|
if module_system_dir.exists() {
|
||||||
|
#[cfg(unix)]
|
||||||
|
set_permissions(&module_system_dir, Permissions::from_mode(0o755))?;
|
||||||
|
restore_syscon(&module_system_dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
exec_install_script(zip)?;
|
||||||
|
|
||||||
|
info!("rename {tmp_module_img} to {}", defs::MODULE_UPDATE_IMG);
|
||||||
|
// all done, rename the tmp image to modules_update.img
|
||||||
|
if std::fs::rename(tmp_module_img, defs::MODULE_UPDATE_IMG).is_err() {
|
||||||
|
warn!("Rename image failed, try copy it.");
|
||||||
|
utils::copy_sparse_file(tmp_module_img, defs::MODULE_UPDATE_IMG, true)
|
||||||
|
.with_context(|| "Failed to copy image.".to_string())?;
|
||||||
|
let _ = std::fs::remove_file(tmp_module_img);
|
||||||
|
}
|
||||||
|
|
||||||
|
mark_update()?;
|
||||||
|
|
||||||
|
info!("Module install successfully!");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install_module(zip: &str) -> Result<()> {
|
pub fn install_module(zip: &str) -> Result<()> {
|
||||||
fn inner(zip: &str) -> Result<()> {
|
let result = _install_module(zip);
|
||||||
ensure_boot_completed()?;
|
|
||||||
|
|
||||||
// print banner
|
|
||||||
println!(include_str!("banner"));
|
|
||||||
|
|
||||||
assets::ensure_binaries(false).with_context(|| "Failed to extract assets")?;
|
|
||||||
|
|
||||||
// first check if working dir is usable
|
|
||||||
ensure_dir_exists(defs::WORKING_DIR).with_context(|| "Failed to create working dir")?;
|
|
||||||
ensure_dir_exists(defs::BINARY_DIR).with_context(|| "Failed to create bin dir")?;
|
|
||||||
|
|
||||||
// read the module_id from zip, if failed it will return early.
|
|
||||||
let mut buffer: Vec<u8> = Vec::new();
|
|
||||||
let entry_path = PathBuf::from_str("module.prop")?;
|
|
||||||
let zip_path = PathBuf::from_str(zip)?;
|
|
||||||
let zip_path = zip_path.canonicalize()?;
|
|
||||||
zip_extract_file_to_memory(&zip_path, &entry_path, &mut buffer)?;
|
|
||||||
|
|
||||||
let mut module_prop = HashMap::new();
|
|
||||||
PropertiesIter::new_with_encoding(Cursor::new(buffer), encoding_rs::UTF_8).read_into(
|
|
||||||
|k, v| {
|
|
||||||
module_prop.insert(k, v);
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
info!("module prop: {:?}", module_prop);
|
|
||||||
|
|
||||||
let Some(module_id) = module_prop.get("id") else {
|
|
||||||
bail!("module id not found in module.prop!");
|
|
||||||
};
|
|
||||||
let module_id = module_id.trim();
|
|
||||||
|
|
||||||
let zip_uncompressed_size = get_zip_uncompressed_size(zip)?;
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"zip uncompressed size: {}",
|
|
||||||
humansize::format_size(zip_uncompressed_size, humansize::DECIMAL)
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("- Preparing Zip");
|
|
||||||
println!(
|
|
||||||
"- Module size: {}",
|
|
||||||
humansize::format_size(zip_uncompressed_size, humansize::DECIMAL)
|
|
||||||
);
|
|
||||||
|
|
||||||
// ensure modules_update exists
|
|
||||||
ensure_dir_exists(MODULE_UPDATE_DIR)?;
|
|
||||||
setsyscon(MODULE_UPDATE_DIR)?;
|
|
||||||
|
|
||||||
let update_module_dir = Path::new(MODULE_UPDATE_DIR).join(module_id);
|
|
||||||
ensure_clean_dir(&update_module_dir)?;
|
|
||||||
info!("module dir: {}", update_module_dir.display());
|
|
||||||
|
|
||||||
let do_install = || -> Result<()> {
|
|
||||||
// unzip the image and move it to modules_update/<id> dir
|
|
||||||
let file = File::open(zip)?;
|
|
||||||
let mut archive = zip::ZipArchive::new(file)?;
|
|
||||||
archive.extract(&update_module_dir)?;
|
|
||||||
|
|
||||||
// set permission and selinux context for $MOD/system
|
|
||||||
let module_system_dir = update_module_dir.join("system");
|
|
||||||
if module_system_dir.exists() {
|
|
||||||
#[cfg(unix)]
|
|
||||||
set_permissions(&module_system_dir, Permissions::from_mode(0o755))?;
|
|
||||||
restore_syscon(&module_system_dir)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_install_script(zip)?;
|
|
||||||
|
|
||||||
let module_dir = Path::new(MODULE_DIR).join(module_id);
|
|
||||||
ensure_dir_exists(&module_dir)?;
|
|
||||||
copy(
|
|
||||||
update_module_dir.join("module.prop"),
|
|
||||||
module_dir.join("module.prop"),
|
|
||||||
)?;
|
|
||||||
ensure_file_exists(module_dir.join(UPDATE_FILE_NAME))?;
|
|
||||||
|
|
||||||
info!("Module install successfully!");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
let result = do_install();
|
|
||||||
if result.is_err() {
|
|
||||||
remove_dir_all(&update_module_dir).ok();
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
let result = inner(zip);
|
|
||||||
if let Err(ref e) = result {
|
if let Err(ref e) = result {
|
||||||
|
// error happened, do some cleanup!
|
||||||
|
let _ = std::fs::remove_file(defs::MODULE_UPDATE_TMP_IMG);
|
||||||
|
let _ = mount::umount_dir(defs::MODULE_UPDATE_TMP_DIR);
|
||||||
println!("- Error: {e}");
|
println!("- Error: {e}");
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_module<F>(update_dir: &str, id: &str, func: F) -> Result<()>
|
||||||
|
where
|
||||||
|
F: Fn(&str, &str) -> Result<()>,
|
||||||
|
{
|
||||||
|
ensure_boot_completed()?;
|
||||||
|
|
||||||
|
let modules_img = Path::new(defs::MODULE_IMG);
|
||||||
|
let modules_update_img = Path::new(defs::MODULE_UPDATE_IMG);
|
||||||
|
let modules_update_tmp_img = Path::new(defs::MODULE_UPDATE_TMP_IMG);
|
||||||
|
if !modules_update_img.exists() && !modules_img.exists() {
|
||||||
|
bail!("Please install module first!");
|
||||||
|
} else if modules_update_img.exists() {
|
||||||
|
info!(
|
||||||
|
"copy {} to {}",
|
||||||
|
modules_update_img.display(),
|
||||||
|
modules_update_tmp_img.display()
|
||||||
|
);
|
||||||
|
utils::copy_sparse_file(modules_update_img, modules_update_tmp_img, true)?;
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"copy {} to {}",
|
||||||
|
modules_img.display(),
|
||||||
|
modules_update_tmp_img.display()
|
||||||
|
);
|
||||||
|
utils::copy_sparse_file(modules_img, modules_update_tmp_img, true)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure modules_update dir exist
|
||||||
|
ensure_clean_dir(update_dir)?;
|
||||||
|
|
||||||
|
// mount the modules_update img
|
||||||
|
let _dontdrop = mount::AutoMountExt4::try_new(defs::MODULE_UPDATE_TMP_IMG, update_dir, true)?;
|
||||||
|
|
||||||
|
// call the operation func
|
||||||
|
let result = func(id, update_dir);
|
||||||
|
|
||||||
|
if let Err(e) = std::fs::rename(modules_update_tmp_img, defs::MODULE_UPDATE_IMG) {
|
||||||
|
warn!("Rename image failed: {e}, try copy it.");
|
||||||
|
utils::copy_sparse_file(modules_update_tmp_img, defs::MODULE_UPDATE_IMG, true)
|
||||||
|
.with_context(|| "Failed to copy image.".to_string())?;
|
||||||
|
let _ = std::fs::remove_file(modules_update_tmp_img);
|
||||||
|
}
|
||||||
|
|
||||||
|
mark_update()?;
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uninstall_module(id: &str) -> Result<()> {
|
pub fn uninstall_module(id: &str) -> Result<()> {
|
||||||
mark_module_state(id, defs::REMOVE_FILE_NAME, true)
|
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
|
||||||
|
let dir = Path::new(update_dir);
|
||||||
|
ensure!(dir.exists(), "No module installed");
|
||||||
|
|
||||||
|
// iterate the modules_update dir, find the module to be removed
|
||||||
|
let dir = std::fs::read_dir(dir)?;
|
||||||
|
for entry in dir.flatten() {
|
||||||
|
let path = entry.path();
|
||||||
|
let module_prop = path.join("module.prop");
|
||||||
|
if !module_prop.exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let content = std::fs::read(module_prop)?;
|
||||||
|
let mut module_id: String = String::new();
|
||||||
|
PropertiesIter::new_with_encoding(Cursor::new(content), encoding_rs::UTF_8).read_into(
|
||||||
|
|k, v| {
|
||||||
|
if k.eq("id") {
|
||||||
|
module_id = v;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
if module_id.eq(mid) {
|
||||||
|
let remove_file = path.join(defs::REMOVE_FILE_NAME);
|
||||||
|
File::create(remove_file).with_context(|| "Failed to create remove file.")?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// santity check
|
||||||
|
let target_module_path = format!("{update_dir}/{mid}");
|
||||||
|
let target_module = Path::new(&target_module_path);
|
||||||
|
if target_module.exists() {
|
||||||
|
let remove_file = target_module.join(defs::REMOVE_FILE_NAME);
|
||||||
|
if !remove_file.exists() {
|
||||||
|
File::create(remove_file).with_context(|| "Failed to create remove file.")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = mark_module_state(id, defs::REMOVE_FILE_NAME, true);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_action(id: &str) -> Result<()> {
|
pub fn run_action(id: &str) -> Result<()> {
|
||||||
@@ -397,12 +575,37 @@ pub fn run_action(id: &str) -> Result<()> {
|
|||||||
exec_script(&action_script_path, true)
|
exec_script(&action_script_path, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn _enable_module(module_dir: &str, mid: &str, enable: bool) -> Result<()> {
|
||||||
|
let src_module_path = format!("{module_dir}/{mid}");
|
||||||
|
let src_module = Path::new(&src_module_path);
|
||||||
|
ensure!(src_module.exists(), "module: {} not found!", mid);
|
||||||
|
|
||||||
|
let disable_path = src_module.join(defs::DISABLE_FILE_NAME);
|
||||||
|
if enable {
|
||||||
|
if disable_path.exists() {
|
||||||
|
std::fs::remove_file(&disable_path).with_context(|| {
|
||||||
|
format!("Failed to remove disable file: {}", &disable_path.display())
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ensure_file_exists(disable_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = mark_module_state(mid, defs::DISABLE_FILE_NAME, !enable);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn enable_module(id: &str) -> Result<()> {
|
pub fn enable_module(id: &str) -> Result<()> {
|
||||||
mark_module_state(id, defs::DISABLE_FILE_NAME, false)
|
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
|
||||||
|
_enable_module(update_dir, mid, true)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_module(id: &str) -> Result<()> {
|
pub fn disable_module(id: &str) -> Result<()> {
|
||||||
mark_module_state(id, defs::DISABLE_FILE_NAME, true)
|
update_module(defs::MODULE_UPDATE_TMP_DIR, id, |mid, update_dir| {
|
||||||
|
_enable_module(update_dir, mid, false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_all_modules() -> Result<()> {
|
pub fn disable_all_modules() -> Result<()> {
|
||||||
@@ -414,7 +617,8 @@ pub fn uninstall_all_modules() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mark_all_modules(flag_file: &str) -> Result<()> {
|
fn mark_all_modules(flag_file: &str) -> Result<()> {
|
||||||
let dir = std::fs::read_dir(MODULE_DIR)?;
|
// we assume the module dir is already mounted
|
||||||
|
let dir = std::fs::read_dir(defs::MODULE_DIR)?;
|
||||||
for entry in dir.flatten() {
|
for entry in dir.flatten() {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let flag = path.join(flag_file);
|
let flag = path.join(flag_file);
|
||||||
@@ -492,3 +696,21 @@ pub fn list_modules() -> Result<()> {
|
|||||||
println!("{}", serde_json::to_string_pretty(&modules)?);
|
println!("{}", serde_json::to_string_pretty(&modules)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shrink_image(img: &str) -> Result<()> {
|
||||||
|
check_image(img)?;
|
||||||
|
Command::new("resize2fs")
|
||||||
|
.arg("-M")
|
||||||
|
.arg(img)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.status()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shrink_ksu_images() -> Result<()> {
|
||||||
|
shrink_image(defs::MODULE_IMG)?;
|
||||||
|
if Path::new(defs::MODULE_UPDATE_IMG).exists() {
|
||||||
|
shrink_image(defs::MODULE_UPDATE_IMG)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
306
userspace/ksud/src/mount.rs
Normal file
306
userspace/ksud/src/mount.rs
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
use anyhow::{anyhow, bail, Ok, Result};
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
use anyhow::Context;
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
use rustix::{fd::AsFd, fs::CWD, mount::*};
|
||||||
|
|
||||||
|
use crate::defs::KSU_OVERLAY_SOURCE;
|
||||||
|
use log::{info, warn};
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
use procfs::process::Process;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub struct AutoMountExt4 {
|
||||||
|
target: String,
|
||||||
|
auto_umount: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoMountExt4 {
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn try_new(source: &str, target: &str, auto_umount: bool) -> Result<Self> {
|
||||||
|
mount_ext4(source, target)?;
|
||||||
|
Ok(Self {
|
||||||
|
target: target.to_string(),
|
||||||
|
auto_umount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
|
pub fn try_new(_src: &str, _mnt: &str, _auto_umount: bool) -> Result<Self> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn umount(&self) -> Result<()> {
|
||||||
|
unmount(self.target.as_str(), UnmountFlags::DETACH)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
impl Drop for AutoMountExt4 {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
log::info!(
|
||||||
|
"AutoMountExt4 drop: {}, auto_umount: {}",
|
||||||
|
self.target,
|
||||||
|
self.auto_umount
|
||||||
|
);
|
||||||
|
if self.auto_umount {
|
||||||
|
let _ = self.umount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn mount_ext4(source: impl AsRef<Path>, target: impl AsRef<Path>) -> Result<()> {
|
||||||
|
let new_loopback = loopdev::LoopControl::open()?
|
||||||
|
.next_free()
|
||||||
|
.with_context(|| "Failed to alloc loop")?;
|
||||||
|
new_loopback
|
||||||
|
.with()
|
||||||
|
.attach(source)
|
||||||
|
.with_context(|| "Failed to attach loop")?;
|
||||||
|
let lo = new_loopback.path().ok_or(anyhow!("no loop"))?;
|
||||||
|
let fs = fsopen("ext4", FsOpenFlags::FSOPEN_CLOEXEC)?;
|
||||||
|
let fs = fs.as_fd();
|
||||||
|
fsconfig_set_string(fs, "source", lo)?;
|
||||||
|
fsconfig_create(fs)?;
|
||||||
|
let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?;
|
||||||
|
move_mount(
|
||||||
|
mount.as_fd(),
|
||||||
|
"",
|
||||||
|
CWD,
|
||||||
|
target.as_ref(),
|
||||||
|
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn umount_dir(src: impl AsRef<Path>) -> Result<()> {
|
||||||
|
unmount(src.as_ref(), UnmountFlags::empty())
|
||||||
|
.with_context(|| format!("Failed to umount {}", src.as_ref().display()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn mount_overlayfs(
|
||||||
|
lower_dirs: &[String],
|
||||||
|
lowest: &str,
|
||||||
|
upperdir: Option<PathBuf>,
|
||||||
|
workdir: Option<PathBuf>,
|
||||||
|
dest: impl AsRef<Path>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let lowerdir_config = lower_dirs
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_ref())
|
||||||
|
.chain(std::iter::once(lowest))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(":");
|
||||||
|
info!(
|
||||||
|
"mount overlayfs on {:?}, lowerdir={}, upperdir={:?}, workdir={:?}",
|
||||||
|
dest.as_ref(),
|
||||||
|
lowerdir_config,
|
||||||
|
upperdir,
|
||||||
|
workdir
|
||||||
|
);
|
||||||
|
|
||||||
|
let upperdir = upperdir
|
||||||
|
.filter(|up| up.exists())
|
||||||
|
.map(|e| e.display().to_string());
|
||||||
|
let workdir = workdir
|
||||||
|
.filter(|wd| wd.exists())
|
||||||
|
.map(|e| e.display().to_string());
|
||||||
|
|
||||||
|
let result = (|| {
|
||||||
|
let fs = fsopen("overlay", FsOpenFlags::FSOPEN_CLOEXEC)?;
|
||||||
|
let fs = fs.as_fd();
|
||||||
|
fsconfig_set_string(fs, "lowerdir", &lowerdir_config)?;
|
||||||
|
if let (Some(upperdir), Some(workdir)) = (&upperdir, &workdir) {
|
||||||
|
fsconfig_set_string(fs, "upperdir", upperdir)?;
|
||||||
|
fsconfig_set_string(fs, "workdir", workdir)?;
|
||||||
|
}
|
||||||
|
fsconfig_set_string(fs, "source", KSU_OVERLAY_SOURCE)?;
|
||||||
|
fsconfig_create(fs)?;
|
||||||
|
let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?;
|
||||||
|
move_mount(
|
||||||
|
mount.as_fd(),
|
||||||
|
"",
|
||||||
|
CWD,
|
||||||
|
dest.as_ref(),
|
||||||
|
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
|
||||||
|
)
|
||||||
|
})();
|
||||||
|
|
||||||
|
if let Err(e) = result {
|
||||||
|
warn!("fsopen mount failed: {:#}, fallback to mount", e);
|
||||||
|
let mut data = format!("lowerdir={lowerdir_config}");
|
||||||
|
if let (Some(upperdir), Some(workdir)) = (upperdir, workdir) {
|
||||||
|
data = format!("{data},upperdir={upperdir},workdir={workdir}");
|
||||||
|
}
|
||||||
|
mount(
|
||||||
|
KSU_OVERLAY_SOURCE,
|
||||||
|
dest.as_ref(),
|
||||||
|
"overlay",
|
||||||
|
MountFlags::empty(),
|
||||||
|
data,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn mount_tmpfs(dest: impl AsRef<Path>) -> Result<()> {
|
||||||
|
info!("mount tmpfs on {}", dest.as_ref().display());
|
||||||
|
let fs = fsopen("tmpfs", FsOpenFlags::FSOPEN_CLOEXEC)?;
|
||||||
|
let fs = fs.as_fd();
|
||||||
|
fsconfig_set_string(fs, "source", KSU_OVERLAY_SOURCE)?;
|
||||||
|
fsconfig_create(fs)?;
|
||||||
|
let mount = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?;
|
||||||
|
move_mount(
|
||||||
|
mount.as_fd(),
|
||||||
|
"",
|
||||||
|
CWD,
|
||||||
|
dest.as_ref(),
|
||||||
|
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn bind_mount(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
||||||
|
info!(
|
||||||
|
"bind mount {} -> {}",
|
||||||
|
from.as_ref().display(),
|
||||||
|
to.as_ref().display()
|
||||||
|
);
|
||||||
|
let tree = open_tree(
|
||||||
|
CWD,
|
||||||
|
from.as_ref(),
|
||||||
|
OpenTreeFlags::OPEN_TREE_CLOEXEC
|
||||||
|
| OpenTreeFlags::OPEN_TREE_CLONE
|
||||||
|
| OpenTreeFlags::AT_RECURSIVE,
|
||||||
|
)?;
|
||||||
|
move_mount(
|
||||||
|
tree.as_fd(),
|
||||||
|
"",
|
||||||
|
CWD,
|
||||||
|
to.as_ref(),
|
||||||
|
MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
fn mount_overlay_child(
|
||||||
|
mount_point: &str,
|
||||||
|
relative: &String,
|
||||||
|
module_roots: &Vec<String>,
|
||||||
|
stock_root: &String,
|
||||||
|
) -> Result<()> {
|
||||||
|
if !module_roots
|
||||||
|
.iter()
|
||||||
|
.any(|lower| Path::new(&format!("{lower}{relative}")).exists())
|
||||||
|
{
|
||||||
|
return bind_mount(stock_root, mount_point);
|
||||||
|
}
|
||||||
|
if !Path::new(&stock_root).is_dir() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut lower_dirs: Vec<String> = vec![];
|
||||||
|
for lower in module_roots {
|
||||||
|
let lower_dir = format!("{lower}{relative}");
|
||||||
|
let path = Path::new(&lower_dir);
|
||||||
|
if path.is_dir() {
|
||||||
|
lower_dirs.push(lower_dir);
|
||||||
|
} else if path.exists() {
|
||||||
|
// stock root has been blocked by this file
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lower_dirs.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
// merge modules and stock
|
||||||
|
if let Err(e) = mount_overlayfs(&lower_dirs, stock_root, None, None, mount_point) {
|
||||||
|
warn!("failed: {:#}, fallback to bind mount", e);
|
||||||
|
bind_mount(stock_root, mount_point)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn mount_overlay(
|
||||||
|
root: &String,
|
||||||
|
module_roots: &Vec<String>,
|
||||||
|
workdir: Option<PathBuf>,
|
||||||
|
upperdir: Option<PathBuf>,
|
||||||
|
) -> Result<()> {
|
||||||
|
info!("mount overlay for {}", root);
|
||||||
|
std::env::set_current_dir(root).with_context(|| format!("failed to chdir to {root}"))?;
|
||||||
|
let stock_root = ".";
|
||||||
|
|
||||||
|
// collect child mounts before mounting the root
|
||||||
|
let mounts = Process::myself()?
|
||||||
|
.mountinfo()
|
||||||
|
.with_context(|| "get mountinfo")?;
|
||||||
|
let mut mount_seq = mounts
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.filter(|m| {
|
||||||
|
m.mount_point.starts_with(root) && !Path::new(&root).starts_with(&m.mount_point)
|
||||||
|
})
|
||||||
|
.map(|m| m.mount_point.to_str())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
mount_seq.sort();
|
||||||
|
mount_seq.dedup();
|
||||||
|
|
||||||
|
mount_overlayfs(module_roots, root, upperdir, workdir, root)
|
||||||
|
.with_context(|| "mount overlayfs for root failed")?;
|
||||||
|
for mount_point in mount_seq.iter() {
|
||||||
|
let Some(mount_point) = mount_point else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let relative = mount_point.replacen(root, "", 1);
|
||||||
|
let stock_root: String = format!("{stock_root}{relative}");
|
||||||
|
if !Path::new(&stock_root).exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Err(e) = mount_overlay_child(mount_point, &relative, module_roots, &stock_root) {
|
||||||
|
warn!(
|
||||||
|
"failed to mount overlay for child {}: {:#}, revert",
|
||||||
|
mount_point, e
|
||||||
|
);
|
||||||
|
umount_dir(root).with_context(|| format!("failed to revert {root}"))?;
|
||||||
|
bail!(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
|
pub fn mount_ext4(_src: &str, _target: &str, _autodrop: bool) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
|
pub fn umount_dir(_src: &str) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
|
pub fn mount_overlay(
|
||||||
|
_root: &String,
|
||||||
|
_module_roots: &Vec<String>,
|
||||||
|
_workdir: Option<PathBuf>,
|
||||||
|
_upperdir: Option<PathBuf>,
|
||||||
|
) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
|
pub fn mount_tmpfs(_dest: impl AsRef<Path>) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
@@ -61,11 +61,11 @@ pub fn restore_syscon<P: AsRef<Path>>(dir: P) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_modules_con<P: AsRef<Path>>(dir: P) -> Result<()> {
|
fn restore_syscon_if_unlabeled<P: AsRef<Path>>(dir: P) -> Result<()> {
|
||||||
for dir_entry in WalkDir::new(dir).parallelism(Serial) {
|
for dir_entry in WalkDir::new(dir).parallelism(Serial) {
|
||||||
if let Some(path) = dir_entry.ok().map(|dir_entry| dir_entry.path()) {
|
if let Some(path) = dir_entry.ok().map(|dir_entry| dir_entry.path()) {
|
||||||
if let Result::Ok(con) = lgetfilecon(&path) {
|
if let anyhow::Result::Ok(con) = lgetfilecon(&path) {
|
||||||
if con == ADB_CON || con == UNLABEL_CON || con.is_empty() {
|
if con == UNLABEL_CON || con.is_empty() {
|
||||||
lsetfilecon(&path, SYSTEM_CON)?;
|
lsetfilecon(&path, SYSTEM_CON)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,6 +76,6 @@ fn restore_modules_con<P: AsRef<Path>>(dir: P) -> Result<()> {
|
|||||||
|
|
||||||
pub fn restorecon() -> Result<()> {
|
pub fn restorecon() -> Result<()> {
|
||||||
lsetfilecon(defs::DAEMON_PATH, ADB_CON)?;
|
lsetfilecon(defs::DAEMON_PATH, ADB_CON)?;
|
||||||
restore_modules_con(defs::MODULE_DIR)?;
|
restore_syscon_if_unlabeled(defs::MODULE_DIR)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ use std::fs::{set_permissions, Permissions};
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::prelude::PermissionsExt;
|
use std::os::unix::prelude::PermissionsExt;
|
||||||
|
|
||||||
|
use hole_punch::*;
|
||||||
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
|
|
||||||
|
use jwalk::WalkDir;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
@@ -35,7 +39,7 @@ pub fn ensure_clean_dir(dir: impl AsRef<Path>) -> Result<()> {
|
|||||||
|
|
||||||
pub fn ensure_file_exists<T: AsRef<Path>>(file: T) -> Result<()> {
|
pub fn ensure_file_exists<T: AsRef<Path>>(file: T) -> Result<()> {
|
||||||
match File::options().write(true).create_new(true).open(&file) {
|
match File::options().write(true).create_new(true).open(&file) {
|
||||||
Result::Ok(_) => Ok(()),
|
std::result::Result::Ok(_) => Ok(()),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if err.kind() == AlreadyExists && file.as_ref().is_file() {
|
if err.kind() == AlreadyExists && file.as_ref().is_file() {
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -220,7 +224,9 @@ pub fn uninstall(magiskboot_path: Option<PathBuf>) -> Result<()> {
|
|||||||
println!("- Removing directories..");
|
println!("- Removing directories..");
|
||||||
std::fs::remove_dir_all(defs::WORKING_DIR).ok();
|
std::fs::remove_dir_all(defs::WORKING_DIR).ok();
|
||||||
std::fs::remove_file(defs::DAEMON_PATH).ok();
|
std::fs::remove_file(defs::DAEMON_PATH).ok();
|
||||||
|
crate::mount::umount_dir(defs::MODULE_DIR).ok();
|
||||||
std::fs::remove_dir_all(defs::MODULE_DIR).ok();
|
std::fs::remove_dir_all(defs::MODULE_DIR).ok();
|
||||||
|
std::fs::remove_dir_all(defs::MODULE_UPDATE_TMP_DIR).ok();
|
||||||
println!("- Restore boot image..");
|
println!("- Restore boot image..");
|
||||||
boot_patch::restore(None, magiskboot_path, true)?;
|
boot_patch::restore(None, magiskboot_path, true)?;
|
||||||
println!("- Uninstall KernelSU manager..");
|
println!("- Uninstall KernelSU manager..");
|
||||||
@@ -232,3 +238,153 @@ pub fn uninstall(magiskboot_path: Option<PathBuf>) -> Result<()> {
|
|||||||
Command::new("reboot").spawn()?;
|
Command::new("reboot").spawn()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: use libxcp to improve the speed if cross's MSRV is 1.70
|
||||||
|
pub fn copy_sparse_file<P: AsRef<Path>, Q: AsRef<Path>>(
|
||||||
|
src: P,
|
||||||
|
dst: Q,
|
||||||
|
punch_hole: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut src_file = File::open(src.as_ref())?;
|
||||||
|
let mut dst_file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(dst.as_ref())?;
|
||||||
|
|
||||||
|
dst_file.set_len(src_file.metadata()?.len())?;
|
||||||
|
|
||||||
|
let segments = src_file.scan_chunks()?;
|
||||||
|
for segment in segments {
|
||||||
|
if let SegmentType::Data = segment.segment_type {
|
||||||
|
let start = segment.start;
|
||||||
|
let end = segment.end;
|
||||||
|
|
||||||
|
src_file.seek(SeekFrom::Start(start))?;
|
||||||
|
dst_file.seek(SeekFrom::Start(start))?;
|
||||||
|
|
||||||
|
let mut buffer = [0; 4096];
|
||||||
|
let mut total_bytes_copied = 0;
|
||||||
|
|
||||||
|
while total_bytes_copied < end - start {
|
||||||
|
let bytes_to_read =
|
||||||
|
std::cmp::min(buffer.len() as u64, end - start - total_bytes_copied);
|
||||||
|
let bytes_read = src_file.read(&mut buffer[..bytes_to_read as usize])?;
|
||||||
|
|
||||||
|
if bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if punch_hole && buffer[..bytes_read].iter().all(|&x| x == 0) {
|
||||||
|
// all zero, don't copy it at all!
|
||||||
|
dst_file.seek(SeekFrom::Current(bytes_read as i64))?;
|
||||||
|
total_bytes_copied += bytes_read as u64;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dst_file.write_all(&buffer[..bytes_read])?;
|
||||||
|
total_bytes_copied += bytes_read as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
fn copy_xattrs(src_path: impl AsRef<Path>, dest_path: impl AsRef<Path>) -> Result<()> {
|
||||||
|
use rustix::path::Arg;
|
||||||
|
let std::result::Result::Ok(xattrs) = extattr::llistxattr(src_path.as_ref()) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
for xattr in xattrs {
|
||||||
|
let std::result::Result::Ok(value) = extattr::lgetxattr(src_path.as_ref(), &xattr) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
log::info!(
|
||||||
|
"Set {:?} xattr {} = {}",
|
||||||
|
dest_path.as_ref(),
|
||||||
|
xattr.to_string_lossy(),
|
||||||
|
value.to_string_lossy(),
|
||||||
|
);
|
||||||
|
if let Err(e) =
|
||||||
|
extattr::lsetxattr(dest_path.as_ref(), &xattr, &value, extattr::Flags::empty())
|
||||||
|
{
|
||||||
|
log::warn!("Failed to set xattr: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
pub fn copy_module_files(source: impl AsRef<Path>, destination: impl AsRef<Path>) -> Result<()> {
|
||||||
|
use rustix::fs::FileTypeExt;
|
||||||
|
use rustix::fs::MetadataExt;
|
||||||
|
|
||||||
|
for entry in WalkDir::new(source.as_ref()).into_iter() {
|
||||||
|
let entry = entry.context("Failed to access entry")?;
|
||||||
|
let source_path = entry.path();
|
||||||
|
let relative_path = source_path
|
||||||
|
.strip_prefix(source.as_ref())
|
||||||
|
.context("Failed to generate relative path")?;
|
||||||
|
let dest_path = destination.as_ref().join(relative_path);
|
||||||
|
|
||||||
|
if let Some(parent) = dest_path.parent() {
|
||||||
|
std::fs::create_dir_all(parent).context("Failed to create directory")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.file_type().is_file() {
|
||||||
|
std::fs::copy(&source_path, &dest_path).with_context(|| {
|
||||||
|
format!("Failed to copy file from {source_path:?} to {dest_path:?}",)
|
||||||
|
})?;
|
||||||
|
copy_xattrs(&source_path, &dest_path)?;
|
||||||
|
} else if entry.file_type().is_symlink() {
|
||||||
|
if dest_path.exists() {
|
||||||
|
std::fs::remove_file(&dest_path).context("Failed to remove file")?;
|
||||||
|
}
|
||||||
|
let target = std::fs::read_link(entry.path()).context("Failed to read symlink")?;
|
||||||
|
log::info!("Symlink: {:?} -> {:?}", dest_path, target);
|
||||||
|
std::os::unix::fs::symlink(target, &dest_path).context("Failed to create symlink")?;
|
||||||
|
copy_xattrs(&source_path, &dest_path)?;
|
||||||
|
} else if entry.file_type().is_dir() {
|
||||||
|
create_dir_all(&dest_path)?;
|
||||||
|
let metadata = std::fs::metadata(&source_path).context("Failed to read metadata")?;
|
||||||
|
std::fs::set_permissions(&dest_path, metadata.permissions())
|
||||||
|
.with_context(|| format!("Failed to set permissions for {dest_path:?}"))?;
|
||||||
|
copy_xattrs(&source_path, &dest_path)?;
|
||||||
|
} else if entry.file_type().is_char_device() {
|
||||||
|
if dest_path.exists() {
|
||||||
|
std::fs::remove_file(&dest_path).context("Failed to remove file")?;
|
||||||
|
}
|
||||||
|
let metadata = std::fs::metadata(&source_path).context("Failed to read metadata")?;
|
||||||
|
let mode = metadata.permissions().mode();
|
||||||
|
let dev = metadata.rdev();
|
||||||
|
if dev == 0 {
|
||||||
|
log::info!(
|
||||||
|
"Found a char device with major 0: {}",
|
||||||
|
entry.path().display()
|
||||||
|
);
|
||||||
|
rustix::fs::mknodat(
|
||||||
|
rustix::fs::CWD,
|
||||||
|
&dest_path,
|
||||||
|
rustix::fs::FileType::CharacterDevice,
|
||||||
|
mode.into(),
|
||||||
|
dev,
|
||||||
|
)
|
||||||
|
.with_context(|| format!("Failed to create device file at {dest_path:?}"))?;
|
||||||
|
copy_xattrs(&source_path, &dest_path)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::info!(
|
||||||
|
"Unknown file type: {:?}, {:?},",
|
||||||
|
entry.file_type(),
|
||||||
|
entry.path(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||||
|
pub fn copy_module_files(_source: impl AsRef<Path>, _destination: impl AsRef<Path>) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user