From 78019445c67c2a393660b8f7d73da6d29c76b994 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Sat, 20 Jun 2026 05:51:38 +0000 Subject: [PATCH 1/3] fix(install): write resolv.conf + hosts after fast-install The fast-install path (download latest__.meta4 -> extract our prebuilt rootfs) never wrote /etc/resolv.conf, so a fast-installed system booted with no DNS resolver (proot has no resolver daemon) -> no name resolution -> 'no internet'. Only the reset / advanced-reset bootstrap paths wrote it. Our standalone build script intentionally strips resolv.conf from the artifact (network config shouldn't be baked into a downloadable tarball; the app owns runtime network state). So the app must inject it on fast-install too. Mirror the reset path: write nameserver 1.1.1.1/8.8.8.8 and 127.0.0.1 localhost into the rootfs right after extraction, before the companion-data (maps/kiwix) steps that themselves need DNS. --- .../org/iiab/controller/DeployFragment.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/controller/app/src/main/java/org/iiab/controller/DeployFragment.java b/controller/app/src/main/java/org/iiab/controller/DeployFragment.java index d139ea5..533682a 100644 --- a/controller/app/src/main/java/org/iiab/controller/DeployFragment.java +++ b/controller/app/src/main/java/org/iiab/controller/DeployFragment.java @@ -1096,6 +1096,24 @@ public void onComplete(String destDir) { } } + // Inject DNS + hosts so the freshly fast-installed rootfs can + // resolve names (proot has no resolver daemon, and companion-data + // steps below need network). The build artifact ships WITHOUT + // resolv.conf on purpose; the app owns runtime network config. + try { + File resolvConf = new File(debianRootfs, "etc/resolv.conf"); + if (resolvConf.exists()) resolvConf.delete(); + try (java.io.FileOutputStream fos = new java.io.FileOutputStream(resolvConf)) { + fos.write("nameserver 1.1.1.1\nnameserver 8.8.8.8\n".getBytes()); + } + File hostsFile = new File(debianRootfs, "etc/hosts"); + try (java.io.FileOutputStream fosH = new java.io.FileOutputStream(hostsFile)) { + fosH.write("127.0.0.1 localhost\n".getBytes()); + } + } catch (Exception e) { + Log.w(TAG, "Failed to write resolv.conf/hosts after fast-install", e); + } + if (chkCompanionData.isChecked()) { editLocalVarsForMaps(debianRootfs, safeTier); android.content.SharedPreferences prefs = requireContext().getSharedPreferences(getString(R.string.pref_file_internal), Context.MODE_PRIVATE); From 138e3f2b9fc0fc1b4ee20b0e62b133c2b7167b6f Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Sat, 20 Jun 2026 05:57:01 +0000 Subject: [PATCH 2/3] docs: flag fast-install DNS write as tech-debt (not Clean Architecture) Mark the imperative resolv.conf/hosts write as a quick add inside the DeployFragment god-object, to be folded into the rootfs domain/data slice during the ongoing Clean Architecture strangler refactor. --- .../app/src/main/java/org/iiab/controller/DeployFragment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/controller/app/src/main/java/org/iiab/controller/DeployFragment.java b/controller/app/src/main/java/org/iiab/controller/DeployFragment.java index 533682a..fb5e4dd 100644 --- a/controller/app/src/main/java/org/iiab/controller/DeployFragment.java +++ b/controller/app/src/main/java/org/iiab/controller/DeployFragment.java @@ -1100,6 +1100,8 @@ public void onComplete(String destDir) { // resolve names (proot has no resolver daemon, and companion-data // steps below need network). The build artifact ships WITHOUT // resolv.conf on purpose; the app owns runtime network config. + // TODO [tech-debt]: quick imperative add inside DeployFragment (god-object). + // Not Clean Architecture; fold into the rootfs domain/data slice on refactor. try { File resolvConf = new File(debianRootfs, "etc/resolv.conf"); if (resolvConf.exists()) resolvConf.delete(); From a36c7b12fd1b9578e02e0b282631dea8fe5b279e Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Sat, 20 Jun 2026 06:17:59 +0000 Subject: [PATCH 3/3] fix(install): also write DNS on backup RESTORE; share one helper The reported 'no internet' repro uses the RESTORE path (import backup from external storage -> restore as internal backup -> boot), NOT fast-install. That path extracted the rootfs and only updated the UI; it never wrote /etc/resolv.conf, so the booted guest had no resolver -> name resolution failed (ping: Temporary failure in name resolution; /etc/resolv.conf absent). - Extract a single helper ensureRuntimeNetworkConfig(debianRootfs) that writes resolv.conf (1.1.1.1/8.8.8.8) + hosts, guarded by an etc/ isDirectory() check (logs instead of silently creating files in the wrong place) and logging. - Call it from BOTH fast-install and restore onComplete (restore targets installed-rootfs/iiab, matching the artifact layout). - Keeps the tech-debt note: imperative, belongs in the rootfs Clean-Arch slice. --- .../org/iiab/controller/DeployFragment.java | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/controller/app/src/main/java/org/iiab/controller/DeployFragment.java b/controller/app/src/main/java/org/iiab/controller/DeployFragment.java index fb5e4dd..37233a2 100644 --- a/controller/app/src/main/java/org/iiab/controller/DeployFragment.java +++ b/controller/app/src/main/java/org/iiab/controller/DeployFragment.java @@ -1096,25 +1096,9 @@ public void onComplete(String destDir) { } } - // Inject DNS + hosts so the freshly fast-installed rootfs can - // resolve names (proot has no resolver daemon, and companion-data - // steps below need network). The build artifact ships WITHOUT - // resolv.conf on purpose; the app owns runtime network config. - // TODO [tech-debt]: quick imperative add inside DeployFragment (god-object). - // Not Clean Architecture; fold into the rootfs domain/data slice on refactor. - try { - File resolvConf = new File(debianRootfs, "etc/resolv.conf"); - if (resolvConf.exists()) resolvConf.delete(); - try (java.io.FileOutputStream fos = new java.io.FileOutputStream(resolvConf)) { - fos.write("nameserver 1.1.1.1\nnameserver 8.8.8.8\n".getBytes()); - } - File hostsFile = new File(debianRootfs, "etc/hosts"); - try (java.io.FileOutputStream fosH = new java.io.FileOutputStream(hostsFile)) { - fosH.write("127.0.0.1 localhost\n".getBytes()); - } - } catch (Exception e) { - Log.w(TAG, "Failed to write resolv.conf/hosts after fast-install", e); - } + // Freshly fast-installed rootfs ships without resolv.conf; give it + // DNS before the companion-data (maps/kiwix) steps that need network. + ensureRuntimeNetworkConfig(debianRootfs); if (chkCompanionData.isChecked()) { editLocalVarsForMaps(debianRootfs, safeTier); @@ -2163,6 +2147,8 @@ private void refreshRestoreButtonLogic() { tarExtractor.startExtraction(requireContext(), backupFile.getAbsolutePath(), iiabRootDir.getAbsolutePath(), new TarExtractor.ExtractionListener() { @Override public void onComplete(String destDir) { + // Restored artifact ships without resolv.conf; the app owns runtime DNS. + ensureRuntimeNetworkConfig(new File(iiabRootDir, "installed-rootfs/iiab")); mainAct.runOnUiThread(() -> { isRestoring = false; disableSystemProtection(); @@ -2189,6 +2175,33 @@ public void onError(String error) { } } + // Runtime network config for the proot guest: proot has no resolver daemon, and our build + // artifact ships WITHOUT /etc/resolv.conf on purpose (the app is the single owner of runtime + // network state). Mirrors the reset path. Called from fast-install and restore. + // TODO [tech-debt]: imperative side-effect inside DeployFragment (god-object); not Clean + // Architecture. Fold into the rootfs domain/data slice during the strangler refactor. + private void ensureRuntimeNetworkConfig(File debianRootfs) { + try { + File etc = new File(debianRootfs, "etc"); + if (!etc.isDirectory()) { + Log.w(TAG, "ensureRuntimeNetworkConfig: missing " + etc.getAbsolutePath() + " - skipping DNS write"); + return; + } + File resolvConf = new File(etc, "resolv.conf"); + if (resolvConf.exists()) resolvConf.delete(); + try (java.io.FileOutputStream fos = new java.io.FileOutputStream(resolvConf)) { + fos.write("nameserver 1.1.1.1\nnameserver 8.8.8.8\n".getBytes()); + } + File hostsFile = new File(etc, "hosts"); + try (java.io.FileOutputStream fosH = new java.io.FileOutputStream(hostsFile)) { + fosH.write("127.0.0.1 localhost\n".getBytes()); + } + Log.i(TAG, "ensureRuntimeNetworkConfig: wrote resolv.conf + hosts under " + etc.getAbsolutePath()); + } catch (Exception e) { + Log.w(TAG, "ensureRuntimeNetworkConfig failed", e); + } + } + private void importBackupSafely(Uri sourceUri) { isImporting = true; updateDynamicButtons();