mirror of
https://github.com/fankes/termux-app.git
synced 2025-09-06 10:45:23 +08:00
Changed/Fixed: Ensure bootstrap installation creates prefix and prefix staging directory before extraction
We manually create the parent directories first so that bootstrap failures are detected early on instead of some sub directory during extraction. Also fixed issue where `TermuxFileUtils.isTermuxFilesDirectoryAccessible()` would not check if a directory file actually existed at TERMUX_FILES_DIR_PATH and may set permissions for a non-directory file at the path. The `TermuxInstaller` was testing if `TERMUX_PREFIX_DIR_PATH` existed later on so check wasn't necessary but function may be called from elsewhere too. Also removed legacy `PREFIX_FILE*` and `STAGING_PREFIX_FILE*` local constants and use the ones provided by `TermuxConstants` directly.
This commit is contained in:
@@ -31,6 +31,11 @@ import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import static com.termux.shared.termux.TermuxConstants.TERMUX_PREFIX_DIR;
|
||||
import static com.termux.shared.termux.TermuxConstants.TERMUX_PREFIX_DIR_PATH;
|
||||
import static com.termux.shared.termux.TermuxConstants.TERMUX_STAGING_PREFIX_DIR;
|
||||
import static com.termux.shared.termux.TermuxConstants.TERMUX_STAGING_PREFIX_DIR_PATH;
|
||||
|
||||
/**
|
||||
* Install the Termux bootstrap packages if necessary by following the below steps:
|
||||
* <p/>
|
||||
@@ -67,7 +72,7 @@ final class TermuxInstaller {
|
||||
// Termux can only be run as the primary user (device owner) since only that
|
||||
// account has the expected file system paths. Verify that:
|
||||
if (!PackageUtils.isCurrentUserThePrimaryUser(activity)) {
|
||||
bootstrapErrorMessage = activity.getString(R.string.bootstrap_error_not_primary_user_message, MarkdownUtils.getMarkdownCodeForString(TermuxConstants.TERMUX_PREFIX_DIR_PATH, false));
|
||||
bootstrapErrorMessage = activity.getString(R.string.bootstrap_error_not_primary_user_message, MarkdownUtils.getMarkdownCodeForString(TERMUX_PREFIX_DIR_PATH, false));
|
||||
Logger.logError(LOG_TAG, "isFilesDirectoryAccessible: " + isFilesDirectoryAccessible);
|
||||
Logger.logError(LOG_TAG, bootstrapErrorMessage);
|
||||
sendBootstrapCrashReportNotification(activity, bootstrapErrorMessage);
|
||||
@@ -87,21 +92,18 @@ final class TermuxInstaller {
|
||||
return;
|
||||
}
|
||||
|
||||
final String PREFIX_FILE_PATH = TermuxConstants.TERMUX_PREFIX_DIR_PATH;
|
||||
final File PREFIX_FILE = TermuxConstants.TERMUX_PREFIX_DIR;
|
||||
|
||||
// If prefix directory exists, even if its a symlink to a valid directory and symlink is not broken/dangling
|
||||
if (FileUtils.directoryFileExists(PREFIX_FILE_PATH, true)) {
|
||||
File[] PREFIX_FILE_LIST = PREFIX_FILE.listFiles();
|
||||
if (FileUtils.directoryFileExists(TERMUX_PREFIX_DIR_PATH, true)) {
|
||||
File[] PREFIX_FILE_LIST = TERMUX_PREFIX_DIR.listFiles();
|
||||
// If prefix directory is empty or only contains the tmp directory
|
||||
if(PREFIX_FILE_LIST == null || PREFIX_FILE_LIST.length == 0 || (PREFIX_FILE_LIST.length == 1 && TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH.equals(PREFIX_FILE_LIST[0].getAbsolutePath()))) {
|
||||
Logger.logInfo(LOG_TAG, "The prefix directory \"" + PREFIX_FILE_PATH + "\" exists but is empty or only contains the tmp directory.");
|
||||
Logger.logInfo(LOG_TAG, "The termux prefix directory \"" + TERMUX_PREFIX_DIR_PATH + "\" exists but is empty or only contains the tmp directory.");
|
||||
} else {
|
||||
whenDone.run();
|
||||
return;
|
||||
}
|
||||
} else if (FileUtils.fileExists(PREFIX_FILE_PATH, false)) {
|
||||
Logger.logInfo(LOG_TAG, "The prefix directory \"" + PREFIX_FILE_PATH + "\" does not exist but another file exists at its destination.");
|
||||
} else if (FileUtils.fileExists(TERMUX_PREFIX_DIR_PATH, false)) {
|
||||
Logger.logInfo(LOG_TAG, "The termux prefix directory \"" + TERMUX_PREFIX_DIR_PATH + "\" does not exist but another file exists at its destination.");
|
||||
}
|
||||
|
||||
final ProgressDialog progress = ProgressDialog.show(activity, null, activity.getString(R.string.bootstrap_installer_body), true, false);
|
||||
@@ -113,24 +115,35 @@ final class TermuxInstaller {
|
||||
|
||||
Error error;
|
||||
|
||||
final String STAGING_PREFIX_PATH = TermuxConstants.TERMUX_STAGING_PREFIX_DIR_PATH;
|
||||
final File STAGING_PREFIX_FILE = new File(STAGING_PREFIX_PATH);
|
||||
|
||||
// Delete prefix staging directory or any file at its destination
|
||||
error = FileUtils.deleteFile("prefix staging directory", STAGING_PREFIX_PATH, true);
|
||||
error = FileUtils.deleteFile("termux prefix staging directory", TERMUX_STAGING_PREFIX_DIR_PATH, true);
|
||||
if (error != null) {
|
||||
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Error.getErrorMarkdownString(error));
|
||||
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete prefix directory or any file at its destination
|
||||
error = FileUtils.deleteFile("prefix directory", PREFIX_FILE_PATH, true);
|
||||
error = FileUtils.deleteFile("termux prefix directory", TERMUX_PREFIX_DIR_PATH, true);
|
||||
if (error != null) {
|
||||
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Error.getErrorMarkdownString(error));
|
||||
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.logInfo(LOG_TAG, "Extracting bootstrap zip to prefix staging directory \"" + STAGING_PREFIX_PATH + "\".");
|
||||
// Create prefix staging directory if it does not already exist and set required permissions
|
||||
error = TermuxFileUtils.isTermuxPrefixStagingDirectoryAccessible(true, true);
|
||||
if (error != null) {
|
||||
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create prefix directory if it does not already exist and set required permissions
|
||||
error = TermuxFileUtils.isTermuxPrefixDirectoryAccessible(true, true);
|
||||
if (error != null) {
|
||||
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.logInfo(LOG_TAG, "Extracting bootstrap zip to prefix staging directory \"" + TERMUX_STAGING_PREFIX_DIR_PATH + "\".");
|
||||
|
||||
final byte[] buffer = new byte[8096];
|
||||
final List<Pair<String, String>> symlinks = new ArrayList<>(50);
|
||||
@@ -147,23 +160,23 @@ final class TermuxInstaller {
|
||||
if (parts.length != 2)
|
||||
throw new RuntimeException("Malformed symlink line: " + line);
|
||||
String oldPath = parts[0];
|
||||
String newPath = STAGING_PREFIX_PATH + "/" + parts[1];
|
||||
String newPath = TERMUX_STAGING_PREFIX_DIR_PATH + "/" + parts[1];
|
||||
symlinks.add(Pair.create(oldPath, newPath));
|
||||
|
||||
error = ensureDirectoryExists(new File(newPath).getParentFile());
|
||||
if (error != null) {
|
||||
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Error.getErrorMarkdownString(error));
|
||||
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String zipEntryName = zipEntry.getName();
|
||||
File targetFile = new File(STAGING_PREFIX_PATH, zipEntryName);
|
||||
File targetFile = new File(TERMUX_STAGING_PREFIX_DIR_PATH, zipEntryName);
|
||||
boolean isDirectory = zipEntry.isDirectory();
|
||||
|
||||
error = ensureDirectoryExists(isDirectory ? targetFile : targetFile.getParentFile());
|
||||
if (error != null) {
|
||||
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Error.getErrorMarkdownString(error));
|
||||
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,17 +202,17 @@ final class TermuxInstaller {
|
||||
Os.symlink(symlink.first, symlink.second);
|
||||
}
|
||||
|
||||
Logger.logInfo(LOG_TAG, "Moving prefix staging to prefix directory.");
|
||||
Logger.logInfo(LOG_TAG, "Moving termux prefix staging to prefix directory.");
|
||||
|
||||
if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) {
|
||||
throw new RuntimeException("Moving prefix staging to prefix directory failed");
|
||||
if (!TERMUX_STAGING_PREFIX_DIR.renameTo(TERMUX_PREFIX_DIR)) {
|
||||
throw new RuntimeException("Moving termux prefix staging to prefix directory failed");
|
||||
}
|
||||
|
||||
Logger.logInfo(LOG_TAG, "Bootstrap packages installed successfully.");
|
||||
activity.runOnUiThread(whenDone);
|
||||
|
||||
} catch (final Exception e) {
|
||||
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Logger.getStackTracesMarkdownString(null, Logger.getStackTracesStringArray(e)));
|
||||
showBootstrapErrorDialog(activity, whenDone, Logger.getStackTracesMarkdownString(null, Logger.getStackTracesStringArray(e)));
|
||||
|
||||
} finally {
|
||||
activity.runOnUiThread(() -> {
|
||||
@@ -214,7 +227,7 @@ final class TermuxInstaller {
|
||||
}.start();
|
||||
}
|
||||
|
||||
public static void showBootstrapErrorDialog(Activity activity, String PREFIX_FILE_PATH, Runnable whenDone, String message) {
|
||||
public static void showBootstrapErrorDialog(Activity activity, Runnable whenDone, String message) {
|
||||
Logger.logErrorExtended(LOG_TAG, "Bootstrap Error:\n" + message);
|
||||
|
||||
// Send a notification with the exception so that the user knows why bootstrap setup failed
|
||||
@@ -229,7 +242,7 @@ final class TermuxInstaller {
|
||||
})
|
||||
.setPositiveButton(R.string.bootstrap_error_try_again, (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
FileUtils.deleteFile("prefix directory", PREFIX_FILE_PATH, true);
|
||||
FileUtils.deleteFile("termux prefix directory", TERMUX_PREFIX_DIR_PATH, true);
|
||||
TermuxInstaller.setupBootstrapIfNeeded(activity, whenDone);
|
||||
}).show();
|
||||
} catch (WindowManager.BadTokenException e1) {
|
||||
|
Reference in New Issue
Block a user