From d3d8aebd1cdf808ea1a177fa443208261cb8e396 Mon Sep 17 00:00:00 2001 From: Leonid Plyushch Date: Mon, 25 Feb 2019 14:06:37 +0200 Subject: [PATCH] installer: use offline bootstraps --- app/build.gradle | 7 ++ .../java/com/termux/app/TermuxInstaller.java | 99 ++++++++++++------- 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8bdc6851..58eff664 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,3 +35,10 @@ android { dependencies { testImplementation 'junit:junit:4.12' } + +preBuild.doFirst { + assert file("./src/main/assets/bootstrap-aarch64").exists() + assert file("./src/main/assets/bootstrap-arm").exists() + assert file("./src/main/assets/bootstrap-i686").exists() + assert file("./src/main/assets/bootstrap-x86_64").exists() +} diff --git a/app/src/main/java/com/termux/app/TermuxInstaller.java b/app/src/main/java/com/termux/app/TermuxInstaller.java index f1818650..68dfd8dc 100644 --- a/app/src/main/java/com/termux/app/TermuxInstaller.java +++ b/app/src/main/java/com/termux/app/TermuxInstaller.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; +import android.content.res.AssetManager; import android.os.Build; import android.os.Environment; import android.os.UserManager; @@ -79,52 +80,74 @@ final class TermuxInstaller { deleteFolder(STAGING_PREFIX_FILE); } - final byte[] buffer = new byte[8096]; - final List> symlinks = new ArrayList<>(50); + final byte[] buffer = new byte[16384]; + final List> symlinks = new ArrayList<>(128); + final List executables = new ArrayList<>(128); - final URL zipUrl = determineZipUrl(); - try (ZipInputStream zipInput = new ZipInputStream(zipUrl.openStream())) { + final String bootstrapArchiveName = determineZipName(); + AssetManager assetManager = activity.getAssets(); + + try (ZipInputStream zipInput = new ZipInputStream(assetManager.open(bootstrapArchiveName))) { ZipEntry zipEntry; while ((zipEntry = zipInput.getNextEntry()) != null) { - if (zipEntry.getName().equals("SYMLINKS.txt")) { - BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput)); - String line; - while ((line = symlinksReader.readLine()) != null) { - String[] parts = line.split("←"); - if (parts.length != 2) - throw new RuntimeException("Malformed symlink line: " + line); - String oldPath = parts[0]; - String newPath = STAGING_PREFIX_PATH + "/" + parts[1]; - symlinks.add(Pair.create(oldPath, newPath)); - - ensureDirectoryExists(new File(newPath).getParentFile()); + switch (zipEntry.getName()) { + case "EXECUTABLES.txt": { + BufferedReader executablesReader = new BufferedReader(new InputStreamReader(zipInput)); + String line; + while ((line = executablesReader.readLine()) != null) { + executables.add(line); + } + break; } - } else { - String zipEntryName = zipEntry.getName(); - File targetFile = new File(STAGING_PREFIX_PATH, zipEntryName); - boolean isDirectory = zipEntry.isDirectory(); - - ensureDirectoryExists(isDirectory ? targetFile : targetFile.getParentFile()); - - if (!isDirectory) { - try (FileOutputStream outStream = new FileOutputStream(targetFile)) { - int readBytes; - while ((readBytes = zipInput.read(buffer)) != -1) - outStream.write(buffer, 0, readBytes); + case "SYMLINKS.txt": { + BufferedReader symlinksReader = new BufferedReader(new InputStreamReader(zipInput)); + String line; + while ((line = symlinksReader.readLine()) != null) { + String[] parts = line.split("←"); + if (parts.length != 2) + throw new RuntimeException("Malformed symlink line: " + line); + String oldPath = parts[0]; + String newPath = STAGING_PREFIX_PATH + "/" + parts[1]; + symlinks.add(Pair.create(oldPath, newPath)); + ensureDirectoryExists(new File(newPath).getParentFile()); } - if (zipEntryName.startsWith("bin/") || zipEntryName.startsWith("libexec") || zipEntryName.startsWith("lib/apt/methods")) { - //noinspection OctalInteger - Os.chmod(targetFile.getAbsolutePath(), 0700); + break; + } + default: { + String zipEntryName = zipEntry.getName(); + File targetFile = new File(STAGING_PREFIX_PATH, zipEntryName); + boolean isDirectory = zipEntry.isDirectory(); + + ensureDirectoryExists(isDirectory ? targetFile : targetFile.getParentFile()); + + if (!isDirectory) { + try (FileOutputStream outStream = new FileOutputStream(targetFile)) { + int readBytes; + while ((readBytes = zipInput.read(buffer)) != -1) + outStream.write(buffer, 0, readBytes); + } } + break; } } } } - if (symlinks.isEmpty()) - throw new RuntimeException("No SYMLINKS.txt encountered"); - for (Pair symlink : symlinks) { - Os.symlink(symlink.first, symlink.second); + if (!executables.isEmpty()) { + for (String executable : executables) { + //noinspection OctalInteger + Os.chmod(STAGING_PREFIX_PATH + "/" + executable, 0700); + } + } else { + throw new RuntimeException("Installer: no EXECUTABLES.txt found while extracting environment archive."); + } + + if (!symlinks.isEmpty()) { + for (Pair symlink : symlinks) { + Os.symlink(symlink.first, symlink.second); + } + } else { + throw new RuntimeException("Installer: no SYMLINKS.txt found while extracting environment archive."); } if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) { @@ -167,10 +190,10 @@ final class TermuxInstaller { } } - /** Get bootstrap zip url for this systems cpu architecture. */ - private static URL determineZipUrl() throws MalformedURLException { + /** Get bootstrap zip file name for this systems cpu architecture. */ + private static String determineZipName() { String archName = determineTermuxArchName(); - return new URL("https://termux.net/bootstrap/bootstrap-" + archName + ".zip"); + return "bootstrap-" + archName; } private static String determineTermuxArchName() {