diff --git a/app/src/main/java/com/droiddeck/launcher/runtime/BwrapSpawner.kt b/app/src/main/java/com/droiddeck/launcher/runtime/BwrapSpawner.kt index d3ab2b546..e8631c1ea 100644 --- a/app/src/main/java/com/droiddeck/launcher/runtime/BwrapSpawner.kt +++ b/app/src/main/java/com/droiddeck/launcher/runtime/BwrapSpawner.kt @@ -75,7 +75,10 @@ object BwrapSpawner { } } - class Plan(val root: File, val binds: List, val cwd: String, val argv: List, val env: Map) + class Plan( + val root: File, val binds: List, val cwd: String, val argv: List, val env: Map, + val envTool: String = "/usr/bin/env", + ) data class Bind(val host: String, val guest: String) { val spec: String get() = if (host == guest) host else "$host:$guest" @@ -179,12 +182,13 @@ object BwrapSpawner { val argv = request.getJSONArray("argv").let { a -> (0 until a.length()).map { a.getString(it) } } val env = LinkedHashMap() request.optJSONObject("env")?.let { e -> e.keys().forEach { k -> env[k] = e.getString(k) } } - if (binds.any { it.guest == "/dev" }) gpu(rootfs, root, env)?.forEach { addBind(it) } + val envTool = request.optString("envTool").takeIf { it.startsWith("/") } + if (envTool == null && binds.any { it.guest == "/dev" }) gpu(rootfs, root, env)?.forEach { addBind(it) } val libc = listOf("/usr/lib/aarch64-linux-gnu/libc.so.6", "/lib/aarch64-linux-gnu/libc.so.6").map { hostPath(binds, root.path, it) } - if (preloadFits(rootfs, libc.firstOrNull { File(it).isFile } ?: libc.first())) preload(session, rootfs, env).forEach { addBind(it) } + if (envTool == null && preloadFits(rootfs, libc.firstOrNull { File(it).isFile } ?: libc.first())) preload(session, rootfs, env).forEach { addBind(it) } zypakShims(binds, root, dir).forEach { addBind(it) } browserSandboxes(env) - return Plan(root, binds, request.optString("cwd", "/").ifEmpty { "/" }, argv, env) + return Plan(root, binds, request.optString("cwd", "/").ifEmpty { "/" }, argv, env, envTool ?: "/usr/bin/env") } private const val GPU_DIR = "/run/droiddeck-gpu" @@ -325,7 +329,7 @@ object BwrapSpawner { val verbose = plan.env["DROIDDECK_PROOT_VERBOSE"]?.toIntOrNull() if (verbose != null) { cmd.add(1, "-v"); cmd.add(2, verbose.toString()) } plan.binds.forEach { cmd.add("-b"); cmd.add(it.spec) } - cmd.add("/usr/bin/env") + cmd.add(plan.envTool) cmd.add("-i") plan.env.forEach { (k, v) -> cmd.add("$k=$v") } cmd.addAll(plan.argv) diff --git a/app/src/main/java/com/droiddeck/launcher/runtime/FlatpakManager.kt b/app/src/main/java/com/droiddeck/launcher/runtime/FlatpakManager.kt index c3dc83da5..eabcd67b8 100644 --- a/app/src/main/java/com/droiddeck/launcher/runtime/FlatpakManager.kt +++ b/app/src/main/java/com/droiddeck/launcher/runtime/FlatpakManager.kt @@ -19,7 +19,7 @@ object FlatpakManager { @Volatile var busy: String? = null private set - class App(val id: String, val name: String, val icon: File?, val branch: String, val summary: String?) + class App(val id: String, val name: String, val icon: File?, val branch: String, val summary: String?, val arch: String = "aarch64") fun ready(context: Context): Boolean { val root = LinuxRuntime.rootDir(context) @@ -33,24 +33,33 @@ object FlatpakManager { else -> "aarch64" } + internal fun arches(native: String = arch()): List = if (native == "aarch64") listOf("aarch64", "x86_64") else listOf(native) + + internal fun installedBranch(appDir: File, native: String = arch()): Pair? = + arches(native).firstNotNullOfOrNull { arch -> + File(appDir, arch).listFiles()?.firstOrNull { File(it, "active").exists() }?.let { arch to it } + } + + fun foreign(app: App, native: String = arch()): Boolean = app.arch != native + + fun launchArgs(app: App, native: String = arch()): List = if (foreign(app, native)) listOf("--arch=${app.arch}", app.id) else listOf(app.id) + fun installedApps(context: Context): List { val apps = File(LinuxRuntime.rootDir(context), "$USER_DIR/app") - val arch = arch() return apps.listFiles()?.mapNotNull { dir -> val id = dir.name - val archDir = File(dir, arch) - val branch = archDir.listFiles()?.firstOrNull { File(it, "active").exists() } ?: return@mapNotNull null + val (arch, branch) = installedBranch(dir) ?: return@mapNotNull null val active = File(branch, "active") val export = File(active, "export/share") val desktop = FileUtils.readString(File(export, "applications/$id.desktop")) val name = desktop?.let { entry(it, "Name") } ?: id.substringAfterLast('.') val summary = desktop?.let { entry(it, "Comment") } - App(id, name, icon(export, id), branch.name, summary) + App(id, name, icon(export, id), branch.name, summary, arch) }?.sortedBy { it.name.lowercase() } ?: emptyList() } fun isInstalled(context: Context, id: String): Boolean = - File(LinuxRuntime.rootDir(context), "$USER_DIR/app/$id/${arch()}").listFiles()?.any { File(it, "active").exists() } == true + installedBranch(File(LinuxRuntime.rootDir(context), "$USER_DIR/app/$id")) != null private fun entry(desktop: String, key: String): String? { var inMain = false diff --git a/app/src/main/java/com/droiddeck/launcher/runtime/UserApps.kt b/app/src/main/java/com/droiddeck/launcher/runtime/UserApps.kt index b7ca15a18..f40443e02 100644 --- a/app/src/main/java/com/droiddeck/launcher/runtime/UserApps.kt +++ b/app/src/main/java/com/droiddeck/launcher/runtime/UserApps.kt @@ -97,7 +97,8 @@ object UserApps { App( "flatpak:${app.id}", Kind.FLATPAK, readName(custom) ?: app.name, File(custom, "icon.png").takeIf { it.isFile } ?: app.icon ?: FlathubApi.iconUrl(app.id), - app.summary, FlatpakManager.LAUNCHER, listOf(app.id), + app.summary, FlatpakManager.LAUNCHER, FlatpakManager.launchArgs(app), + arch = app.arch.takeIf { FlatpakManager.foreign(app) }, ) } return (scripts + images + flatpaks).sortedBy { it.name.lowercase() } diff --git a/app/src/main/java/com/droiddeck/launcher/ui/FrontEndContent.kt b/app/src/main/java/com/droiddeck/launcher/ui/FrontEndContent.kt index 62c6a862e..1c4536fc0 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/FrontEndContent.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/FrontEndContent.kt @@ -465,7 +465,8 @@ private fun UserAppPage(app: UserApps.App, s: FrontEndState, a: FrontEndActions, val ctx = LocalContext.current val narrow = LocalNarrowPane.current var editing by rememberSaveable(app.key) { mutableStateOf(false) } - val x86 = (app.arch == "x86_64" || app.arch == "i386") && app.fex != com.droiddeck.launcher.runtime.LinuxFex.OFF + val x86 = (app.arch == "x86_64" || app.arch == "i386") && app.fex != com.droiddeck.launcher.runtime.LinuxFex.OFF && + app.kind != UserApps.Kind.FLATPAK val fexReady by produceState(null, app.key, x86, s.sessionRunning) { value = if (x86) kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) { com.droiddeck.launcher.runtime.LinuxFex.ready(ctx) } else true } diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 82a731f57..65093387c 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1892,7 +1892,7 @@ أعِدّ Flatpak أولاً جارٍ البدء خرج Flatpak بالحالة %1$d - تم بناء %1$s لمعمارية %2$s، لكن تطبيقات Flatpak هنا يجب أن تكون لمعمارية ARM64. استخدم ملف AppImage للتطبيق بدلًا من ذلك؛ يشغّل DroidDeck ملفات AppImage لمعمارية x86 عبر FEX. + تم بناء %1$s لمعمارية %2$s، ولا يستطيع DroidDeck تشغيلها. تحتاج تطبيقات Flatpak هنا إلى إصدار لمعمارية ARM64 أو x86_64. جارٍ تثبيت %1$s جارٍ تحديث %1$s جارٍ إزالة %1$s diff --git a/app/src/main/res/values-b+es+419/strings.xml b/app/src/main/res/values-b+es+419/strings.xml index 3a69ffa5c..eb759558a 100644 --- a/app/src/main/res/values-b+es+419/strings.xml +++ b/app/src/main/res/values-b+es+419/strings.xml @@ -1884,7 +1884,7 @@ Configura Flatpak primero Iniciando Flatpak terminó con el código %1$d - %1$s está compilado para %2$s, pero aquí las apps Flatpak deben ser para ARM64. Usa el AppImage de la app; DroidDeck ejecuta los AppImage x86 con FEX. + %1$s está compilado para %2$s, que DroidDeck no puede ejecutar. Aquí las apps Flatpak necesitan una versión para ARM64 o x86_64. Instalando %1$s Actualizando %1$s Quitando %1$s diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 598ce8af9..44b68fa1a 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1800,7 +1800,7 @@ Настрой Flatpak първо Стартиране Flatpak приключи със код %1$d - %1$s е създадено за %2$s, но приложенията Flatpak тук трябва да са за ARM64. Използвайте AppImage на приложението; DroidDeck изпълнява x86 AppImage чрез FEX. + %1$s е създадено за %2$s, което DroidDeck не може да изпълнява. Приложенията Flatpak тук трябва да са за ARM64 или x86_64. Инсталиране на %1$s Актуализиране на %1$s Премахване на %1$s diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 722c7640b..b3c982c97 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -1846,7 +1846,7 @@ Nejprve nastavte Flatpak Spouštění Flatpak skončil s návratovým kódem %1$d - %1$s je sestaveno pro %2$s, ale aplikace Flatpak zde musí být pro ARM64. Použijte AppImage aplikace; DroidDeck spouští x86 AppImage přes FEX. + %1$s je sestaveno pro %2$s, které DroidDeck neumí spustit. Aplikace Flatpak zde potřebují verzi pro ARM64 nebo x86_64. Instalování %1$s Aktualizování %1$s Odstraňování %1$s diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index f6ec5613e..89e0a5cb7 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -1800,7 +1800,7 @@ Opsæt Flatpak først Starter Flatpak afsluttede med status %1$d - %1$s er bygget til %2$s, men Flatpak-apps her skal være bygget til ARM64. Brug appens AppImage i stedet; DroidDeck kører x86-AppImages gennem FEX. + %1$s er bygget til %2$s, som DroidDeck ikke kan køre. Flatpak-apps her skal være bygget til ARM64 eller x86_64. Installerer %1$s Opdaterer %1$s Fjerner %1$s diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 62c85fd4d..1c62bbf5d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1792,7 +1792,7 @@ Richte zuerst Flatpak ein Wird gestartet Flatpak wurde mit Status %1$d beendet - %1$s wurde für %2$s gebaut, Flatpak-Apps müssen hier aber für ARM64 gebaut sein. Nutze stattdessen das AppImage der App; DroidDeck führt x86-AppImages über FEX aus. + %1$s wurde für %2$s gebaut, das DroidDeck nicht ausführen kann. Flatpak-Apps brauchen hier eine ARM64- oder x86_64-Version. %1$s wird installiert %1$s wird aktualisiert %1$s wird entfernt diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 1d831f4f3..d63807d43 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -1800,7 +1800,7 @@ Ρυθμίστε πρώτα το Flatpak Γίνεται εκκίνηση Το Flatpak έκλεισε με κατάσταση %1$d - Το %1$s είναι φτιαγμένο για %2$s, αλλά οι εφαρμογές Flatpak εδώ πρέπει να είναι για ARM64. Χρησιμοποιήστε το AppImage της εφαρμογής· το DroidDeck εκτελεί x86 AppImage μέσω FEX. + Το %1$s είναι φτιαγμένο για %2$s, που το DroidDeck δεν μπορεί να εκτελέσει. Οι εφαρμογές Flatpak εδώ χρειάζονται έκδοση για ARM64 ή x86_64. Εγκατάσταση %1$s Ενημέρωση %1$s Κατάργηση %1$s diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 296663e35..2e64f1a38 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1823,7 +1823,7 @@ Configura Flatpak primero Iniciando Flatpak terminó con el código %1$d - %1$s está compilado para %2$s, pero aquí las apps Flatpak deben ser para ARM64. Usa el AppImage de la app; DroidDeck ejecuta los AppImage x86 con FEX. + %1$s está compilado para %2$s, que DroidDeck no puede ejecutar. Aquí las apps Flatpak necesitan una versión para ARM64 o x86_64. Instalando %1$s Actualizando %1$s Quitando %1$s diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index fa5453918..230d5141f 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -1800,7 +1800,7 @@ Asenna Flatpak ensin Käynnistetään Flatpak sulkeutui tilakoodilla %1$d - %1$s on tehty alustalle %2$s, mutta Flatpak-sovellusten täytyy täällä olla ARM64-sovelluksia. Käytä sovelluksen AppImagea; DroidDeck ajaa x86-AppImaget FEXin kautta. + %1$s on tehty alustalle %2$s, jota DroidDeck ei pysty ajamaan. Flatpak-sovelluksista tarvitaan täällä ARM64- tai x86_64-versio. Asennetaan %1$s Päivitetään %1$s Poistetaan %1$s diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 152be9291..674db1ada 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1823,7 +1823,7 @@ Configurez d\'abord Flatpak Démarrage Flatpak s\'est arrêté avec le code %1$d - %1$s est compilée pour %2$s, mais ici les applis Flatpak doivent l\'être pour ARM64. Utilisez plutôt l\'AppImage de l\'appli ; DroidDeck exécute les AppImage x86 avec FEX. + %1$s est compilée pour %2$s, que DroidDeck ne peut pas exécuter. Ici, les applis Flatpak doivent exister en version ARM64 ou x86_64. Installation : %1$s Mise à jour : %1$s Suppression : %1$s diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 52cd825fe..fb5cc075c 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -1800,7 +1800,7 @@ पहले Flatpak सेट करें शुरू हो रहा है Flatpak status %1$d के साथ बंद हुआ - %1$s %2$s के लिए बना है, पर यहाँ Flatpak ऐप ARM64 के लिए बने होने चाहिए। इसके बजाय ऐप का AppImage इस्तेमाल करें; DroidDeck x86 AppImage को FEX से चलाता है। + %1$s %2$s के लिए बना है, जिसे DroidDeck नहीं चला सकता। यहाँ Flatpak ऐप ARM64 या x86_64 के लिए बने होने चाहिए। %1$s इंस्टॉल हो रहा है %1$s अपडेट हो रहा है %1$s हटाया जा रहा है diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index de4841b55..8587d876d 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1800,7 +1800,7 @@ Előbb állítsa be a Flatpakot Indítás A Flatpak %1$d állapotkóddal lépett ki - A(z) %1$s %2$s rendszerre készült, de az itteni Flatpak-alkalmazásoknak ARM64-re kell készülniük. Használd inkább az alkalmazás AppImage-ét; a DroidDeck az x86 AppImage-eket FEX-en keresztül futtatja. + A(z) %1$s %2$s rendszerre készült, ezt a DroidDeck nem tudja futtatni. Az itteni Flatpak-alkalmazásoknak ARM64- vagy x86_64-változat kell. %1$s telepítése %1$s frissítése %1$s eltávolítása diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 2a80cb98a..058bf9b70 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1777,7 +1777,7 @@ Siapkan Flatpak terlebih dahulu Memulai Flatpak keluar dengan status %1$d - %1$s dibuat untuk %2$s, tetapi aplikasi Flatpak di sini harus dibuat untuk ARM64. Gunakan AppImage aplikasi tersebut; DroidDeck menjalankan AppImage x86 melalui FEX. + %1$s dibuat untuk %2$s, yang tidak dapat dijalankan DroidDeck. Aplikasi Flatpak di sini memerlukan versi ARM64 atau x86_64. Menginstal %1$s Memperbarui %1$s Menghapus %1$s diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index a8a8742df..c76f361bf 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1815,7 +1815,7 @@ Configura prima Flatpak Avvio Flatpak è terminato con stato %1$d - %1$s è compilata per %2$s, ma qui le app Flatpak devono essere per ARM64. Usa invece l\'AppImage dell\'app; DroidDeck esegue le AppImage x86 tramite FEX. + %1$s è compilata per %2$s, che DroidDeck non può eseguire. Qui le app Flatpak richiedono una versione per ARM64 o x86_64. Installazione di %1$s Aggiornamento di %1$s Rimozione di %1$s diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index c559f74b7..b5034be43 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1748,7 +1748,7 @@ 先にFlatpakをセットアップしてください 開始中 Flatpakがステータス%1$dで終了しました - %1$s は %2$s 向けにビルドされていますが、ここでは Flatpak アプリは ARM64 向けである必要があります。代わりにアプリの AppImage を使ってください。DroidDeck は x86 の AppImage を FEX で実行します。 + %1$s は DroidDeck で実行できない %2$s 向けにビルドされています。ここでは Flatpak アプリに ARM64 または x86_64 版が必要です。 %1$sをインストール中 %1$sを更新中 %1$sを削除中 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 2fbb3c93f..a549545de 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -1777,7 +1777,7 @@ 먼저 Flatpak을 설정하세요 시작 중 Flatpak이 종료되었습니다(상태 %1$d) - %1$s은(는) %2$s용으로 빌드되었지만 여기서는 Flatpak 앱이 ARM64용이어야 합니다. 대신 앱의 AppImage를 사용하세요. DroidDeck은 x86 AppImage를 FEX로 실행합니다. + %1$s은(는) DroidDeck이 실행할 수 없는 %2$s용으로 빌드되었습니다. 여기서 Flatpak 앱은 ARM64 또는 x86_64 빌드여야 합니다. %1$s 설치 중 %1$s 업데이트 중 %1$s 제거 중 diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index 81909ac34..5f2aac9a1 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -1777,7 +1777,7 @@ Sediakan Flatpak dahulu Memulakan Flatpak keluar dengan status %1$d - %1$s dibina untuk %2$s, tetapi apl Flatpak di sini mesti dibina untuk ARM64. Gunakan AppImage apl itu; DroidDeck menjalankan AppImage x86 melalui FEX. + %1$s dibina untuk %2$s, yang tidak dapat dijalankan oleh DroidDeck. Apl Flatpak di sini memerlukan binaan ARM64 atau x86_64. Memasang %1$s Mengemas kini %1$s Mengalih keluar %1$s diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 765cb4e48..d8c49c02b 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -1800,7 +1800,7 @@ Sett opp Flatpak først Starter Flatpak avsluttet med status %1$d - %1$s er bygget for %2$s, men Flatpak-apper her må være bygget for ARM64. Bruk appens AppImage i stedet; DroidDeck kjører x86-AppImages gjennom FEX. + %1$s er bygget for %2$s, som DroidDeck ikke kan kjøre. Flatpak-apper her trenger en ARM64- eller x86_64-versjon. Installerer %1$s Oppdaterer %1$s Fjerner %1$s diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 3079c44bd..9fc6f5696 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1800,7 +1800,7 @@ Stel eerst Flatpak in Starten Flatpak is gestopt met status %1$d - %1$s is gebouwd voor %2$s, maar Flatpak-apps moeten hier voor ARM64 zijn gebouwd. Gebruik de AppImage van de app; DroidDeck draait x86-AppImages via FEX. + %1$s is gebouwd voor %2$s, wat DroidDeck niet kan draaien. Flatpak-apps hebben hier een ARM64- of x86_64-versie nodig. %1$s installeren %1$s updaten %1$s verwijderen diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 636b39d7f..dc4394c53 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1846,7 +1846,7 @@ Najpierw skonfiguruj Flatpak Rozpoczynanie Flatpak zakończył działanie z kodem %1$d - %1$s zbudowano dla %2$s, a aplikacje Flatpak muszą tu być zbudowane dla ARM64. Użyj AppImage tej aplikacji; DroidDeck uruchamia AppImage x86 przez FEX. + %1$s zbudowano dla %2$s, czego DroidDeck nie potrafi uruchomić. Aplikacje Flatpak potrzebują tu wersji dla ARM64 lub x86_64. Instalowanie %1$s Aktualizowanie %1$s Usuwanie %1$s diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 33d391c62..9a2925278 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -1815,7 +1815,7 @@ Configure primeiro o Flatpak A iniciar O Flatpak terminou com o código %1$d - %1$s foi compilado para %2$s, mas aqui as apps Flatpak têm de ser para ARM64. Use antes o AppImage da app; o DroidDeck executa AppImages x86 através do FEX. + %1$s foi compilado para %2$s, que o DroidDeck não consegue executar. Aqui as apps Flatpak precisam de uma versão para ARM64 ou x86_64. A instalar %1$s A atualizar %1$s A remover %1$s diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 9507ac50d..871aad5da 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1815,7 +1815,7 @@ Configure o Flatpak primeiro Iniciando O Flatpak terminou com o código %1$d - %1$s foi compilado para %2$s, mas aqui os apps Flatpak precisam ser para ARM64. Use o AppImage do app; o DroidDeck executa AppImages x86 pelo FEX. + %1$s foi compilado para %2$s, que o DroidDeck não consegue executar. Aqui os apps Flatpak precisam de uma versão para ARM64 ou x86_64. Instalando %1$s Atualizando %1$s Removendo %1$s diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 04ef1a701..b102e84b2 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1823,7 +1823,7 @@ Configurează mai întâi Flatpak Se pornește Flatpak s-a închis cu starea %1$d - %1$s este construită pentru %2$s, dar aici aplicațiile Flatpak trebuie să fie pentru ARM64. Folosește în schimb AppImage-ul aplicației; DroidDeck rulează AppImage-urile x86 prin FEX. + %1$s este construită pentru %2$s, pe care DroidDeck nu o poate rula. Aici aplicațiile Flatpak au nevoie de o versiune pentru ARM64 sau x86_64. Se instalează %1$s Se actualizează %1$s Se elimină %1$s diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index f1605e0af..ac5e41115 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1846,7 +1846,7 @@ Сначала настройте Flatpak Запуск Flatpak завершился с кодом %1$d - %1$s собрано для %2$s, а приложения Flatpak здесь должны быть собраны для ARM64. Используйте AppImage этого приложения: DroidDeck запускает x86 AppImage через FEX. + %1$s собрано для %2$s, а DroidDeck не может это запустить. Приложениям Flatpak здесь нужна сборка для ARM64 или x86_64. Установка %1$s Обновление %1$s Удаление %1$s diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 4d11c867b..de4b0b872 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -1800,7 +1800,7 @@ Konfigurera Flatpak först Startar Flatpak avslutades med status %1$d - %1$s är byggd för %2$s, men Flatpak-appar här måste vara byggda för ARM64. Använd appens AppImage i stället; DroidDeck kör x86-AppImages via FEX. + %1$s är byggd för %2$s, som DroidDeck inte kan köra. Flatpak-appar här behöver en ARM64- eller x86_64-version. Installerar %1$s Uppdaterar %1$s Tar bort %1$s diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 6e3ac5c29..62d0c590f 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -1777,7 +1777,7 @@ ตั้งค่า Flatpak ก่อน กำลังเริ่ม Flatpak ออกด้วยสถานะ %1$d - %1$s สร้างมาสำหรับ %2$s แต่แอป Flatpak ที่นี่ต้องสร้างสำหรับ ARM64 ให้ใช้ AppImage ของแอปแทน DroidDeck จะเรียกใช้ AppImage x86 ผ่าน FEX + %1$s สร้างมาสำหรับ %2$s ซึ่ง DroidDeck เรียกใช้ไม่ได้ แอป Flatpak ที่นี่ต้องเป็นเวอร์ชัน ARM64 หรือ x86_64 กำลังติดตั้ง %1$s กำลังอัปเดต %1$s กำลังลบ %1$s diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index a008247d7..641d59932 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1792,7 +1792,7 @@ Önce Flatpak\'i kurun Başlatılıyor Flatpak %1$d durum koduyla çıktı - %1$s %2$s için derlenmiş, ancak buradaki Flatpak uygulamaları ARM64 için derlenmiş olmalı. Bunun yerine uygulamanın AppImage dosyasını kullanın; DroidDeck x86 AppImage dosyalarını FEX ile çalıştırır. + %1$s, DroidDeck\'in çalıştıramadığı %2$s için derlenmiş. Buradaki Flatpak uygulamalarının ARM64 veya x86_64 sürümü olmalı. %1$s yükleniyor %1$s güncelleniyor %1$s kaldırılıyor diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a178dc4d3..f1049381c 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1846,7 +1846,7 @@ Спершу налаштуйте Flatpak Запуск Flatpak завершився з кодом %1$d - %1$s зібрано для %2$s, а застосунки Flatpak тут мають бути зібрані для ARM64. Використайте AppImage цього застосунку: DroidDeck запускає x86 AppImage через FEX. + %1$s зібрано для %2$s, а DroidDeck не може це запустити. Застосункам Flatpak тут потрібна збірка для ARM64 або x86_64. Встановлення %1$s Оновлення %1$s Видалення %1$s diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 02c4ed55a..8df9cf14b 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -1777,7 +1777,7 @@ Thiết lập Flatpak trước Đang khởi động Flatpak đã thoát với mã %1$d - %1$s được dựng cho %2$s, nhưng ứng dụng Flatpak ở đây phải dựng cho ARM64. Hãy dùng bản AppImage của ứng dụng; DroidDeck chạy AppImage x86 qua FEX. + %1$s được dựng cho %2$s, DroidDeck không chạy được. Ứng dụng Flatpak ở đây cần bản ARM64 hoặc x86_64. Đang cài %1$s Đang cập nhật %1$s Đang gỡ %1$s diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 8cca0454f..ee1613baf 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1825,7 +1825,7 @@ 请先配置 Flatpak 启动中 Flatpak 已退出,状态码 %1$d - %1$s 是为 %2$s 构建的,但这里的 Flatpak 应用必须为 ARM64 构建。请改用该应用的 AppImage;DroidDeck 会通过 FEX 运行 x86 AppImage。 + %1$s 是为 %2$s 构建的,DroidDeck 无法运行。这里的 Flatpak 应用需要 ARM64 或 x86_64 版本。 正在安装 %1$s 正在更新 %1$s 正在移除 %1$s diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 9662dab64..f76ebf2aa 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -1748,7 +1748,7 @@ 請先設定 Flatpak 正在開始 Flatpak 已結束,狀態為 %1$d - %1$s 是為 %2$s 建置的,但這裡的 Flatpak 應用程式必須為 ARM64 建置。請改用該應用程式的 AppImage;DroidDeck 會透過 FEX 執行 x86 AppImage。 + %1$s 是為 %2$s 建置的,DroidDeck 無法執行。這裡的 Flatpak 應用程式需要 ARM64 或 x86_64 版本。 正在安裝 %1$s 正在更新 %1$s 正在移除 %1$s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index ee7925bed..ee7521ac4 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1748,7 +1748,7 @@ 請先設定 Flatpak 正在啟動 Flatpak 已結束,狀態碼 %1$d - %1$s 是為 %2$s 建置的,但這裡的 Flatpak 應用程式必須為 ARM64 建置。請改用該應用程式的 AppImage;DroidDeck 會透過 FEX 執行 x86 AppImage。 + %1$s 是為 %2$s 建置的,DroidDeck 無法執行。這裡的 Flatpak 應用程式需要 ARM64 或 x86_64 版本。 正在安裝 %1$s 正在更新 %1$s 正在移除 %1$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd9ceabf4..e2da36712 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1904,7 +1904,7 @@ Set up Flatpak first Starting Flatpak exited with status %1$d - %1$s is built for %2$s, but Flatpak apps here must be built for ARM64. Use the app\'s AppImage instead; DroidDeck runs x86 AppImages through FEX. + %1$s is built for %2$s, which DroidDeck can\'t run. Flatpak apps here need an ARM64 or x86_64 build. Installing %1$s Updating %1$s Removing %1$s diff --git a/app/src/test/java/com/droiddeck/launcher/runtime/BwrapSpawnerTest.kt b/app/src/test/java/com/droiddeck/launcher/runtime/BwrapSpawnerTest.kt index befb45605..a71063bf3 100644 --- a/app/src/test/java/com/droiddeck/launcher/runtime/BwrapSpawnerTest.kt +++ b/app/src/test/java/com/droiddeck/launcher/runtime/BwrapSpawnerTest.kt @@ -119,6 +119,27 @@ class BwrapSpawnerPlanTest { assertEquals("org.example.Game", plan.env["FLATPAK_ID"]) } + @Test fun sandboxesStartThroughTheirOwnEnvByDefault() { + val plan = BwrapSpawner.plan(RuntimeEnvironment.getApplication(), request(op("t" to "dir", "dest" to "/tmp")), dir) + assertEquals("/usr/bin/env", plan.envTool) + } + + @Test fun fexSandboxesStartThroughTheEnvTheyBringAndSkipArmPreloads() { + val context = RuntimeEnvironment.getApplication() + val rootfs = LinuxRuntime.rootDir(context) + val lib = File(rootfs, "usr/local/lib/libblsession.so").apply { parentFile?.mkdirs(); writeText("") } + val preload = File(rootfs, "etc/ld.so.preload").apply { parentFile?.mkdirs(); writeText("/usr/local/lib/libblsession.so\n") } + try { + val plan = BwrapSpawner.plan(context, request(op("t" to "dir", "dest" to "/tmp")) + .put("envTool", "/run/droiddeck-fex/bin/env"), dir) + assertEquals("/run/droiddeck-fex/bin/env", plan.envTool) + assertFalse(plan.env.containsKey("LD_PRELOAD")) + assertFalse(plan.binds.any { it.guest.startsWith("/run/droiddeck-preload") }) + } finally { + listOf(lib, preload).forEach { it.delete() } + } + } + @Test fun sandboxedAppsSeeTheSessionsNetworkLink() { val context = RuntimeEnvironment.getApplication() val rootfs = LinuxRuntime.rootDir(context) diff --git a/app/src/test/java/com/droiddeck/launcher/runtime/FlatpakManagerArchTest.kt b/app/src/test/java/com/droiddeck/launcher/runtime/FlatpakManagerArchTest.kt new file mode 100644 index 000000000..a7cd04c46 --- /dev/null +++ b/app/src/test/java/com/droiddeck/launcher/runtime/FlatpakManagerArchTest.kt @@ -0,0 +1,55 @@ +package com.droiddeck.launcher.runtime + +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import java.io.File +import java.nio.file.Files + +class FlatpakManagerArchTest { + private lateinit var dir: File + + @Before fun setUp() { + dir = Files.createTempDirectory("flatpak-arch").toFile() + } + + @After fun tearDown() { + dir.deleteRecursively() + } + + private fun install(arch: String, branch: String) { + File(dir, "$arch/$branch/active").mkdirs() + } + + @Test fun armDevicesAlsoListX86Builds() { + assertEquals(listOf("aarch64", "x86_64"), FlatpakManager.arches("aarch64")) + assertEquals(listOf("x86_64"), FlatpakManager.arches("x86_64")) + } + + @Test fun anX86OnlyInstallIsFound() { + install("x86_64", "master") + val (arch, branch) = FlatpakManager.installedBranch(dir, "aarch64")!! + assertEquals("x86_64", arch) + assertEquals("master", branch.name) + } + + @Test fun theNativeBuildWinsWhenBothAreInstalled() { + install("x86_64", "master") + install("aarch64", "stable") + assertEquals("aarch64", FlatpakManager.installedBranch(dir, "aarch64")!!.first) + } + + @Test fun aBranchWithoutAnActiveDeployIsNotInstalled() { + File(dir, "x86_64/master").mkdirs() + assertNull(FlatpakManager.installedBranch(dir, "aarch64")) + } + + @Test fun x86BuildsOnArmLaunchWithTheirArch() { + val x86 = FlatpakManager.App("io.github.ebkr.r2modman", "r2modman", null, "master", null, "x86_64") + val arm = FlatpakManager.App("org.example.App", "App", null, "stable", null, "aarch64") + assertEquals(listOf("--arch=x86_64", "io.github.ebkr.r2modman"), FlatpakManager.launchArgs(x86, "aarch64")) + assertEquals(listOf("org.example.App"), FlatpakManager.launchArgs(arm, "aarch64")) + } +} diff --git a/tools/linuxfs/overlay/usr/local/bin/droiddeck-bwrap b/tools/linuxfs/overlay/usr/local/bin/droiddeck-bwrap index d209ae16d..59bf0baf2 100644 --- a/tools/linuxfs/overlay/usr/local/bin/droiddeck-bwrap +++ b/tools/linuxfs/overlay/usr/local/bin/droiddeck-bwrap @@ -6,6 +6,7 @@ import os import signal import socket import struct +import subprocess import sys SOCKET_FILE = "/etc/droiddeck/bwrap-socket" @@ -23,6 +24,11 @@ def socket_name(): SOCKET = socket_name() +FEX_DIR = "/run/droiddeck-fex" +OWN_FEX = "/usr/local" +X86_MACHINES = (3, 62) +ARM_LIBS = "/usr/lib/aarch64-linux-gnu" +ARM_LOADER = "/usr/lib/ld-linux-aarch64.so.1" VERSION = "bubblewrap 0.11.0 (droiddeck)" NO_ARG = { @@ -91,6 +97,7 @@ class Spec: self.lock_files = [] self.command = [] self.argv0 = None + self.env_tool = None def parse(argv): @@ -217,6 +224,134 @@ def host_path(s, path): return path +def sandbox_link(s, path): + for op in reversed(s.ops): + if op.get("dest", "").rstrip("/") != path: + continue + if op["t"] == "symlink": + return op["target"] + if op["t"] == "bind": + return None + break + here = sandbox_source(s, path) + return os.readlink(here) if os.path.islink(here) else None + + +def sandbox_source(s, path): + for op in reversed(s.ops): + if op["t"] != "bind": + continue + dest = op["dest"].rstrip("/") + if path == dest or path.startswith(dest + "/"): + return real(op["src"]).rstrip("/") + path[len(dest):] + return path + + +def sandbox_file(s, path): + parts = [p for p in path.split("/") if p] + done = "/" + hops = 0 + i = 0 + while i < len(parts): + step = os.path.join(done, parts[i]) + link = sandbox_link(s, step) + if link is None: + done = step + i += 1 + continue + hops += 1 + if hops > 40: + return None + base = "/" if link.startswith("/") else done + parts = [p for p in os.path.normpath(os.path.join(base, link)).split("/") if p] + parts[i + 1:] + done = "/" + i = 0 + here = sandbox_source(s, done) + return here if os.path.isfile(here) else None + + +def program_path(s, program): + if "/" in program: + return os.path.normpath(os.path.join(s.cwd or "/", program)) + for folder in s.env.get("PATH", "/usr/bin:/bin").split(":"): + if not folder: + continue + candidate = os.path.normpath(os.path.join(s.cwd or "/", folder, program)) + if sandbox_file(s, candidate): + return candidate + return None + + +def machine(s, path, depth=0): + here = sandbox_file(s, path) + if here is None: + return None + try: + with open(here, "rb") as f: + head = f.read(256) + except OSError: + return None + if head[:4] == b"\x7fELF" and len(head) >= 20: + return struct.unpack("H", head[18:20])[0] + if head[:2] == b"#!" and depth < 4: + words = head[2:].split(b"\n")[0].split() + if words: + return machine(s, words[0].decode("utf-8", "replace"), depth + 1) + return None + + +def own_fex(): + return all(os.access(os.path.join(OWN_FEX, "bin", name), os.X_OK) for name in ("FEX", "FEXServer")) \ + and os.path.isfile(os.path.join(OWN_FEX, "lib", "libxxhash.so.0")) + + +def fex_ready(): + if own_fex(): + return True + try: + subprocess.call([os.path.join(OWN_FEX, "bin", "droiddeck-fex-update")], stdout=sys.stderr) + except OSError: + return False + return own_fex() + + +def start_fex_server(): + try: + subprocess.Popen([os.path.join(OWN_FEX, "bin", "FEXServer"), "-p", "30"], stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True) + except OSError as e: + sys.stderr.write("bwrap: could not start FEXServer: %s\n" % e) + + +def through_fex(s): + program = program_path(s, s.command[0]) + if program is None or machine(s, program) not in X86_MACHINES: + return False + if not fex_ready(): + sys.stderr.write("bwrap: %s is an x86 program and FEX is not installed\n" % program) + return False + for src, dest in ((os.path.join(OWN_FEX, "bin", "FEX"), FEX_DIR + "/bin/FEX"), + (os.path.join(OWN_FEX, "bin", "FEXServer"), FEX_DIR + "/bin/FEXServer"), + (os.path.join(OWN_FEX, "lib", "libxxhash.so.0"), FEX_DIR + "/lib/libxxhash.so.0"), + ("/usr/bin/env", FEX_DIR + "/bin/env"), + (ARM_LIBS, ARM_LIBS), + (real(ARM_LOADER), ARM_LOADER)): + s.ops.append({"t": "bind", "src": src, "dest": dest, "try": False, "ro": True}) + data = s.env.get("XDG_CACHE_HOME") or "/tmp" + s.env["FEX_APP_CONFIG_LOCATION"] = FEX_DIR + "/config/" + s.env["FEX_APP_DATA_LOCATION"] = data + "/droiddeck-fex/" + s.env["FEX_APP_CACHE_LOCATION"] = data + "/droiddeck-fex/cache/" + for name in ("FEX_ROOTFS", "FEX_THUNKHOSTLIBS", "FEX_THUNKGUESTLIBS", "FEX_THUNKCONFIG", "FEX_PORTABLE", + "GLIBC_TUNABLES", "MESA_LOADER_DRIVER_OVERRIDE", "GALLIUM_DRIVER", "VK_ICD_FILENAMES", + "VK_DRIVER_FILES"): + s.env.pop(name, None) + s.command = [FEX_DIR + "/bin/FEX", program] + s.command[1:] + s.argv0 = None + s.env_tool = FEX_DIR + "/bin/env" + start_fex_server() + return True + + def hold_locks(s): for path in s.lock_files: try: @@ -290,6 +425,8 @@ def run_by_app(s): if s.argv0: argv[0] = s.argv0 request = {"argv": argv, "env": s.env, "cwd": s.cwd or "/", "ops": ops} + if s.env_tool: + request["envTool"] = s.env_tool sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: sock.connect(SOCKET) @@ -333,8 +470,9 @@ def main(): hold_locks(s) if remaps_nothing(s): run_here(s) - else: - run_by_app(s) + return + through_fex(s) + run_by_app(s) if __name__ == "__main__": diff --git a/tools/linuxfs/overlay/usr/local/bin/droiddeck-flatpak b/tools/linuxfs/overlay/usr/local/bin/droiddeck-flatpak index 0ca582d6f..15f028fd4 100644 --- a/tools/linuxfs/overlay/usr/local/bin/droiddeck-flatpak +++ b/tools/linuxfs/overlay/usr/local/bin/droiddeck-flatpak @@ -109,21 +109,31 @@ def run(t, state, report_done=True): return 0 +def arches(): + native = Flatpak.get_default_arch() + return [native, "x86_64"] if native == "aarch64" else [native] + + def install(app_id): inst = installation() - arch = Flatpak.get_default_arch() if app_ref(inst, app_id) is not None: emit(e="done", note="already installed") return 0 - branch, last = None, None - for candidate in ("stable", "master"): - try: - branch = inst.fetch_remote_ref_sync(REMOTE, Flatpak.RefKind.APP, app_id, arch, candidate, None).get_branch() + arch, branch, last = None, None, None + for candidate_arch in arches(): + for candidate in ("stable", "master"): + try: + branch = inst.fetch_remote_ref_sync(REMOTE, Flatpak.RefKind.APP, app_id, candidate_arch, candidate, + None).get_branch() + arch = candidate_arch + break + except GLib.Error as err: + last = err + if branch is not None: break - except GLib.Error as err: - last = err if branch is None: - emit(e="error", message="%s is not on Flathub for %s (%s)" % (app_id, arch, last.message if last else "no branch")) + emit(e="error", message="%s is not on Flathub for %s (%s)" % (app_id, " or ".join(arches()), + last.message if last else "no branch")) return 1 t, state = transaction(inst) t.add_install(REMOTE, "app/%s/%s/%s" % (app_id, arch, branch), None) @@ -146,9 +156,9 @@ def install_bundle(path): except GLib.Error as err: emit(e="error", message="%s is not a Flatpak bundle (%s)" % (os.path.basename(path), err.message)) return 1 - if ref.get_arch() != Flatpak.get_default_arch(): + if ref.get_arch() not in arches(): emit(e="error", code="arch", id=app_id, arch=ref.get_arch(), - message="%s is built for %s, not %s" % (app_id, ref.get_arch(), Flatpak.get_default_arch())) + message="%s is built for %s, not %s" % (app_id, ref.get_arch(), " or ".join(arches()))) return 1 if app_ref(inst, app_id) is not None: emit(e="done", id=app_id, note="already installed") diff --git a/tools/proot/PATCHES.md b/tools/proot/PATCHES.md index c5924c0fe..dbf1b959b 100644 --- a/tools/proot/PATCHES.md +++ b/tools/proot/PATCHES.md @@ -127,6 +127,11 @@ Prototype (inert unless `PROOT_FASTPATH` is set; see `docs/development/proot-per IDs wrap (32768 on many Android kernels) a new child can get that ID, and PRoot aborted on the assertion that a new child has no state, ending the whole session. Chromium-based programs such as Steam's client do this. PRoot now retires the stale entry and sets the new child up from scratch. +- `0024-execveat-absolute-path.patch` - `execveat` with an absolute path and neither `AT_EMPTY_PATH` + nor `AT_SYMLINK_NOFOLLOW` runs as `execve`. The kernel ignores the directory descriptor for such a + path, and PRoot refused the call unless the descriptor was `AT_FDCWD`. FEX starts every child of an + emulated program with `execveat(fd, "/proc/self/exe")`, so x86 programs in a Flatpak sandbox, which + does not carry the session's preloads, could not start other programs. Old kernel fixes: diff --git a/tools/proot/patches/0024-execveat-absolute-path.patch b/tools/proot/patches/0024-execveat-absolute-path.patch new file mode 100644 index 000000000..a3b7038ca --- /dev/null +++ b/tools/proot/patches/0024-execveat-absolute-path.patch @@ -0,0 +1,44 @@ +From 968f450754f97907d72a756add5010f6be746b6a Mon Sep 17 00:00:00 2001 +From: maxjivi05 +Date: Fri, 30 Oct 2026 20:21:28 -0400 +Subject: [PATCH] syscall: run an execveat with an absolute path like execve + +The kernel ignores the directory descriptor of execveat when the path is +absolute, so such a call is an execve with flags. FEX runs every child +of an emulated program through execveat(fd, "/proc/self/exe"), which +proot refused unless the descriptor was AT_FDCWD. +--- + src/syscall/enter.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/src/syscall/enter.c b/src/syscall/enter.c +index f8ce2e7..98241bd 100644 +--- a/src/syscall/enter.c ++++ b/src/syscall/enter.c +@@ -129,8 +129,13 @@ int translate_syscall_enter(Tracee *tracee) + status = translate_execve_enter(tracee); + break; + +- case PR_execveat: +- if ((int) peek_reg(tracee, CURRENT, SYSARG_1) == AT_FDCWD) { ++ case PR_execveat: { ++ word_t flags = peek_reg(tracee, CURRENT, SYSARG_5); ++ char first = '\0'; ++ bool absolute = (flags & (AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)) == 0 ++ && read_data(tracee, &first, peek_reg(tracee, CURRENT, SYSARG_2), 1) == 0 ++ && first == '/'; ++ if ((int) peek_reg(tracee, CURRENT, SYSARG_1) == AT_FDCWD || absolute) { + set_sysnum(tracee, PR_execve); + poke_reg(tracee, SYSARG_1, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_3)); +@@ -142,6 +147,7 @@ int translate_syscall_enter(Tracee *tracee) + } + status = translate_execve_enter(tracee); + break; ++ } + + case PR_ptrace: + status = translate_ptrace_enter(tracee); +-- +2.43.0 + diff --git a/tools/tests/test_bwrap_fex.py b/tools/tests/test_bwrap_fex.py new file mode 100644 index 000000000..2fc646333 --- /dev/null +++ b/tools/tests/test_bwrap_fex.py @@ -0,0 +1,98 @@ +import os +import runpy +import shutil +import struct +import tempfile +import unittest +from pathlib import Path + +BWRAP = Path(__file__).resolve().parents[1] / "linuxfs/overlay/usr/local/bin/droiddeck-bwrap" + + +def elf(path, machine): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(b"\x7fELF\x02\x01\x01" + bytes(11) + struct.pack("