diff --git a/app/src/main/java/com/termux/app/TermuxInstaller.java b/app/src/main/java/com/termux/app/TermuxInstaller.java
index b8f6def5..763d1670 100644
--- a/app/src/main/java/com/termux/app/TermuxInstaller.java
+++ b/app/src/main/java/com/termux/app/TermuxInstaller.java
@@ -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:
*
@@ -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> 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) {
diff --git a/termux-shared/src/main/java/com/termux/shared/file/TermuxFileUtils.java b/termux-shared/src/main/java/com/termux/shared/file/TermuxFileUtils.java
index 67d5e4e8..61e60105 100644
--- a/termux-shared/src/main/java/com/termux/shared/file/TermuxFileUtils.java
+++ b/termux-shared/src/main/java/com/termux/shared/file/TermuxFileUtils.java
@@ -9,6 +9,7 @@ import com.termux.shared.logger.Logger;
import com.termux.shared.markdown.MarkdownUtils;
import com.termux.shared.models.ExecutionCommand;
import com.termux.shared.models.errors.Error;
+import com.termux.shared.models.errors.FileUtilsErrno;
import com.termux.shared.shell.TermuxShellEnvironmentClient;
import com.termux.shared.shell.TermuxTask;
import com.termux.shared.termux.AndroidUtils;
@@ -134,7 +135,9 @@ public class TermuxFileUtils {
}
/**
- * Validate the existence and permissions of {@link TermuxConstants#TERMUX_FILES_DIR_PATH}.
+ * Validate if {@link TermuxConstants#TERMUX_FILES_DIR_PATH} exists and has
+ * {@link FileUtils#APP_WORKING_DIRECTORY_PERMISSIONS} permissions.
+ *
* This is required because binaries compiled for termux are hard coded with
* {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} and the path must be accessible.
*
@@ -216,14 +219,57 @@ public class TermuxFileUtils {
if (createDirectoryIfMissing)
context.getFilesDir();
+ if (!FileUtils.directoryFileExists(TermuxConstants.TERMUX_FILES_DIR_PATH, true))
+ return FileUtilsErrno.ERRNO_FILE_NOT_FOUND_AT_PATH.getError("termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH);
+
if (setMissingPermissions)
- FileUtils.setMissingFilePermissions("Termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH,
+ FileUtils.setMissingFilePermissions("termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH,
FileUtils.APP_WORKING_DIRECTORY_PERMISSIONS);
- return FileUtils.checkMissingFilePermissions("Termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH,
+ return FileUtils.checkMissingFilePermissions("termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH,
FileUtils.APP_WORKING_DIRECTORY_PERMISSIONS, false);
}
+ /**
+ * Validate if {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} exists and has
+ * {@link FileUtils#APP_WORKING_DIRECTORY_PERMISSIONS} permissions.
+ * .
+ *
+ * The {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} directory would not exist if termux has
+ * not been installed or the bootstrap setup has not been run or if it was deleted by the user.
+ *
+ * @param createDirectoryIfMissing The {@code boolean} that decides if directory file
+ * should be created if its missing.
+ * @param setMissingPermissions The {@code boolean} that decides if permissions are to be
+ * automatically set.
+ * @return Returns the {@code error} if path is not a directory file, failed to create it,
+ * or validating permissions failed, otherwise {@code null}.
+ */
+ public static Error isTermuxPrefixDirectoryAccessible(boolean createDirectoryIfMissing, boolean setMissingPermissions) {
+ return FileUtils.validateDirectoryFileExistenceAndPermissions("termux prefix directory", TermuxConstants.TERMUX_PREFIX_DIR_PATH,
+ null, createDirectoryIfMissing,
+ FileUtils.APP_WORKING_DIRECTORY_PERMISSIONS, setMissingPermissions, true,
+ false, false);
+ }
+
+ /**
+ * Validate if {@link TermuxConstants#TERMUX_STAGING_PREFIX_DIR_PATH} exists and has
+ * {@link FileUtils#APP_WORKING_DIRECTORY_PERMISSIONS} permissions.
+ *
+ * @param createDirectoryIfMissing The {@code boolean} that decides if directory file
+ * should be created if its missing.
+ * @param setMissingPermissions The {@code boolean} that decides if permissions are to be
+ * automatically set.
+ * @return Returns the {@code error} if path is not a directory file, failed to create it,
+ * or validating permissions failed, otherwise {@code null}.
+ */
+ public static Error isTermuxPrefixStagingDirectoryAccessible(boolean createDirectoryIfMissing, boolean setMissingPermissions) {
+ return FileUtils.validateDirectoryFileExistenceAndPermissions("termux prefix staging directory", TermuxConstants.TERMUX_STAGING_PREFIX_DIR_PATH,
+ null, createDirectoryIfMissing,
+ FileUtils.APP_WORKING_DIRECTORY_PERMISSIONS, setMissingPermissions, true,
+ false, false);
+ }
+
/**
* Get a markdown {@link String} for stat output for various Termux app files paths.
*