mirror of https://github.com/termux/termux-app
Fixed: Fix bootstrap not installing on app install
Previously, bootstrap was only installed if `$PREFIX` didn't exist, was empty or only had `$PREFIX/tmp`. But now with 03e1d14e
, `$PREFIX/etc/termux/termux.env` was also created at app startup before bootstrap check was made, hence it was being assumed that bootstrap was already installed.
Now, bootstrap will be installed even if `$PREFIX/tmp`, `$PREFIX/etc/termux/termux.env.tmp` or `$PREFIX/etc/termux/termux.env` exist but no other files do.
Closes #2844
This commit is contained in:
parent
82b1580312
commit
a2df7d791a
|
@ -21,6 +21,7 @@ import com.termux.shared.errors.Error;
|
|||
import com.termux.shared.android.PackageUtils;
|
||||
import com.termux.shared.termux.TermuxConstants;
|
||||
import com.termux.shared.termux.TermuxUtils;
|
||||
import com.termux.shared.termux.shell.command.environment.TermuxShellEnvironment;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -103,10 +104,8 @@ final class TermuxInstaller {
|
|||
|
||||
// If prefix directory exists, even if its a symlink to a valid directory and symlink is not broken/dangling
|
||||
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 termux prefix directory \"" + TERMUX_PREFIX_DIR_PATH + "\" exists but is empty or only contains the tmp directory.");
|
||||
if (TermuxFileUtils.isTermuxPrefixDirectoryEmpty()) {
|
||||
Logger.logInfo(LOG_TAG, "The termux prefix directory \"" + TERMUX_PREFIX_DIR_PATH + "\" exists but is empty or only contains specific unimportant files.");
|
||||
} else {
|
||||
whenDone.run();
|
||||
return;
|
||||
|
@ -218,6 +217,10 @@ final class TermuxInstaller {
|
|||
}
|
||||
|
||||
Logger.logInfo(LOG_TAG, "Bootstrap packages installed successfully.");
|
||||
|
||||
// Recreate env file since termux prefix was wiped earlier
|
||||
TermuxShellEnvironment.writeEnvironmentToFile(activity);
|
||||
|
||||
activity.runOnUiThread(whenDone);
|
||||
|
||||
} catch (final Exception e) {
|
||||
|
|
|
@ -110,4 +110,9 @@ public class Errno {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean equalsErrorTypeAndCode(Error error) {
|
||||
if (error == null) return false;
|
||||
return type.equals(error.getType()) && code == error.getCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.io.Serializable;
|
|||
import java.nio.charset.Charset;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
@ -185,6 +186,118 @@ public class FileUtils {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Validate that directory is empty or contains only files in {@code ignoredSubFilePaths}.
|
||||
*
|
||||
* If parent path of an ignored file exists, but ignored file itself does not exist, then directory
|
||||
* is not considered empty.
|
||||
*
|
||||
* @param label The optional label for directory to check. This can optionally be {@code null}.
|
||||
* @param filePath The {@code path} for directory to check.
|
||||
* @param ignoredSubFilePaths The list of absolute file paths under {@code filePath} dir.
|
||||
* Validation is done for the paths.
|
||||
* @param ignoreNonExistentFile The {@code boolean} that decides if it should be considered an
|
||||
* error if file to be checked doesn't exist.
|
||||
* @return Returns {@code null} if directory is empty or contains only files in {@code ignoredSubFilePaths}.
|
||||
* Returns {@code FileUtilsErrno#ERRNO_NON_EMPTY_DIRECTORY_FILE} if a file was found that did not
|
||||
* exist in the {@code ignoredSubFilePaths}, otherwise returns an appropriate {@code error} if
|
||||
* checking was not successful.
|
||||
*/
|
||||
public static Error validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(String label, String filePath,
|
||||
final List<String> ignoredSubFilePaths,
|
||||
final boolean ignoreNonExistentFile) {
|
||||
label = (label == null || label.isEmpty() ? "" : label + " ");
|
||||
if (filePath == null || filePath.isEmpty()) return FunctionErrno.ERRNO_NULL_OR_EMPTY_PARAMETER.getError(label + "file path", "isDirectoryFileEmptyOrOnlyContainsSpecificFiles");
|
||||
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
FileType fileType = getFileType(filePath, false);
|
||||
|
||||
// If file exists but not a directory file
|
||||
if (fileType != FileType.NO_EXIST && fileType != FileType.DIRECTORY) {
|
||||
return FileUtilsErrno.ERRNO_NON_DIRECTORY_FILE_FOUND.getError(label + "directory", filePath).setLabel(label + "directory");
|
||||
}
|
||||
|
||||
// If file does not exist
|
||||
if (fileType == FileType.NO_EXIST) {
|
||||
// If checking is to be ignored if file does not exist
|
||||
if (ignoreNonExistentFile)
|
||||
return null;
|
||||
else {
|
||||
label += "directory to check if is empty or only contains specific files";
|
||||
return FileUtilsErrno.ERRNO_FILE_NOT_FOUND_AT_PATH.getError(label, filePath).setLabel(label);
|
||||
}
|
||||
}
|
||||
|
||||
File[] subFiles = file.listFiles();
|
||||
if (subFiles == null || subFiles.length == 0)
|
||||
return null;
|
||||
|
||||
// If sub files exists but no file should be ignored
|
||||
if (ignoredSubFilePaths == null || ignoredSubFilePaths.size() == 0)
|
||||
return FileUtilsErrno.ERRNO_NON_EMPTY_DIRECTORY_FILE.getError(label, filePath);
|
||||
|
||||
// If a sub file does not exist in ignored file path
|
||||
if (nonIgnoredSubFileExists(subFiles, ignoredSubFilePaths)) {
|
||||
return FileUtilsErrno.ERRNO_NON_EMPTY_DIRECTORY_FILE.getError(label, filePath);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
return FileUtilsErrno.ERRNO_VALIDATE_DIRECTORY_EMPTY_OR_ONLY_CONTAINS_SPECIFIC_FILES_FAILED_WITH_EXCEPTION.getError(e, label + "directory", filePath, e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if {@code subFiles} contains contains a file not in {@code ignoredSubFilePaths}.
|
||||
*
|
||||
* If parent path of an ignored file exists, but ignored file itself does not exist, then directory
|
||||
* is not considered empty.
|
||||
*
|
||||
* This function should ideally not be called by itself but through
|
||||
* {@link #validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(String, String, List, boolean)}.
|
||||
*
|
||||
* @param subFiles The list of files of a directory to check.
|
||||
* @param ignoredSubFilePaths The list of absolute file paths under {@code filePath} dir.
|
||||
* Validation is done for the paths.
|
||||
* @return Returns {@code true} if a file was found that did not exist in the {@code ignoredSubFilePaths},
|
||||
* otherwise {@code false}.
|
||||
*/
|
||||
public static boolean nonIgnoredSubFileExists(File[] subFiles, @NonNull List<String> ignoredSubFilePaths) {
|
||||
if (subFiles == null || subFiles.length == 0) return false;
|
||||
|
||||
String subFilePath;
|
||||
for (File subFile : subFiles) {
|
||||
subFilePath = subFile.getAbsolutePath();
|
||||
// If sub file does not exist in ignored sub file paths
|
||||
if (!ignoredSubFilePaths.contains(subFilePath)) {
|
||||
boolean isParentPath = false;
|
||||
for (String ignoredSubFilePath : ignoredSubFilePaths) {
|
||||
if (ignoredSubFilePath.startsWith(subFilePath + "/") && fileExists(ignoredSubFilePath, false)) {
|
||||
isParentPath = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If sub file is not a parent of any existing ignored sub file paths
|
||||
if (!isParentPath) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (getFileType(subFilePath, false) == FileType.DIRECTORY) {
|
||||
// If non ignored sub file found, then early exit, otherwise continue looking
|
||||
if (nonIgnoredSubFileExists(subFile.listFiles(), ignoredSubFilePaths))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a regular file exists at {@code filePath}.
|
||||
*
|
||||
|
|
|
@ -34,9 +34,11 @@ public class FileUtilsErrno extends Errno {
|
|||
public static final Errno ERRNO_NON_SYMLINK_FILE_FOUND_SHORT = new Errno(TYPE, 157, "Non-symlink file found at %1$s path.");
|
||||
|
||||
public static final Errno ERRNO_FILE_NOT_AN_ALLOWED_FILE_TYPE = new Errno(TYPE, 158, "The %1$s found at path \"%2$s\" of type \"%3$s\" is not one of allowed file types \"%4$s\".");
|
||||
public static final Errno ERRNO_NON_EMPTY_DIRECTORY_FILE = new Errno(TYPE, 159, "The %1$s directory at path \"%2$s\" is not empty.");
|
||||
|
||||
public static final Errno ERRNO_VALIDATE_FILE_EXISTENCE_AND_PERMISSIONS_FAILED_WITH_EXCEPTION = new Errno(TYPE, 159, "Validating file existence and permissions of %1$s at path \"%2$s\" failed.\nException: %3$s");
|
||||
public static final Errno ERRNO_VALIDATE_DIRECTORY_EXISTENCE_AND_PERMISSIONS_FAILED_WITH_EXCEPTION = new Errno(TYPE, 160, "Validating directory existence and permissions of %1$s at path \"%2$s\" failed.\nException: %3$s");
|
||||
public static final Errno ERRNO_VALIDATE_FILE_EXISTENCE_AND_PERMISSIONS_FAILED_WITH_EXCEPTION = new Errno(TYPE, 160, "Validating file existence and permissions of %1$s at path \"%2$s\" failed.\nException: %3$s");
|
||||
public static final Errno ERRNO_VALIDATE_DIRECTORY_EXISTENCE_AND_PERMISSIONS_FAILED_WITH_EXCEPTION = new Errno(TYPE, 161, "Validating directory existence and permissions of %1$s at path \"%2$s\" failed.\nException: %3$s");
|
||||
public static final Errno ERRNO_VALIDATE_DIRECTORY_EMPTY_OR_ONLY_CONTAINS_SPECIFIC_FILES_FAILED_WITH_EXCEPTION = new Errno(TYPE, 162, "Validating directory is empty or only contains specific files of %1$s at path \"%2$s\" failed.\nException: %3$s");
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,12 +4,16 @@ import android.content.Context;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.termux.shared.errors.Errno;
|
||||
import com.termux.shared.file.FileUtils;
|
||||
import com.termux.shared.file.FileUtilsErrno;
|
||||
import com.termux.shared.logger.Logger;
|
||||
import com.termux.shared.errors.Error;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class FileUtilsTests {
|
||||
|
||||
|
@ -68,6 +72,15 @@ public class FileUtilsTests {
|
|||
String dir1__sub_dir1_label = "dir1/sub_dir1";
|
||||
String dir1__sub_dir1_path = dir1_path + "/sub_dir1";
|
||||
|
||||
String dir1__sub_dir2_label = "dir1/sub_dir2";
|
||||
String dir1__sub_dir2_path = dir1_path + "/sub_dir2";
|
||||
|
||||
String dir1__sub_dir3_label = "dir1/sub_dir3";
|
||||
String dir1__sub_dir3_path = dir1_path + "/sub_dir3";
|
||||
|
||||
String dir1__sub_dir3__sub_reg1_label = "dir1/sub_dir3/sub_reg1";
|
||||
String dir1__sub_dir3__sub_reg1_path = dir1__sub_dir3_path + "/sub_reg1";
|
||||
|
||||
String dir1__sub_reg1_label = "dir1/sub_reg1";
|
||||
String dir1__sub_reg1_path = dir1_path + "/sub_reg1";
|
||||
|
||||
|
@ -274,6 +287,72 @@ public class FileUtilsTests {
|
|||
if (FileUtils.fileExists(path, false))
|
||||
throwException("The " + label + " regular file still exist after deletion");
|
||||
|
||||
|
||||
List<String> ignoredSubFilePaths = Arrays.asList(dir1__sub_dir2_path, dir1__sub_dir3__sub_reg1_path);
|
||||
|
||||
// Create dir1 directory file
|
||||
error = FileUtils.createDirectoryFile(dir1_label, dir1_path);
|
||||
assertEqual("Failed to create " + dir1_label + " directory file", null, error);
|
||||
|
||||
// Test empty dir
|
||||
error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
|
||||
assertEqual("Failed to validate if " + dir1_label + " directory file is empty", null, error);
|
||||
|
||||
|
||||
// Create dir1/sub_dir3 directory file
|
||||
label = dir1__sub_dir3_label; path = dir1__sub_dir3_path;
|
||||
error = FileUtils.createDirectoryFile(label, path);
|
||||
assertEqual("Failed to create " + label + " directory file", null, error);
|
||||
if (!FileUtils.directoryFileExists(path, false))
|
||||
throwException("The " + label + " directory file does not exist as expected after creation");
|
||||
|
||||
// Test parent dir existing of non existing ignored regular file
|
||||
error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
|
||||
assertErrnoEqual("Failed to validate if " + dir1_label + " directory file is empty with parent dir existing of non existing ignored regular file", FileUtilsErrno.ERRNO_NON_EMPTY_DIRECTORY_FILE, error);
|
||||
|
||||
|
||||
// Write "line1" to dir1/sub_dir3/sub_reg1 regular file
|
||||
label = dir1__sub_dir3__sub_reg1_label; path = dir1__sub_dir3__sub_reg1_path;
|
||||
error = FileUtils.writeTextToFile(label, path, Charset.defaultCharset(), "line1", false);
|
||||
assertEqual("Failed to write string to " + label + " file with append mode false", null, error);
|
||||
if (!FileUtils.regularFileExists(path, false))
|
||||
throwException("The " + label + " file does not exist as expected after writing to it with append mode false");
|
||||
|
||||
// Test ignored regular file existing
|
||||
error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
|
||||
assertEqual("Failed to validate if " + dir1_label + " directory file is empty with ignored regular file existing", null, error);
|
||||
|
||||
|
||||
// Create dir1/sub_dir2 directory file
|
||||
label = dir1__sub_dir2_label; path = dir1__sub_dir2_path;
|
||||
error = FileUtils.createDirectoryFile(label, path);
|
||||
assertEqual("Failed to create " + label + " directory file", null, error);
|
||||
if (!FileUtils.directoryFileExists(path, false))
|
||||
throwException("The " + label + " directory file does not exist as expected after creation");
|
||||
|
||||
// Test ignored dir file existing
|
||||
error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
|
||||
assertEqual("Failed to validate if " + dir1_label + " directory file is empty with ignored dir file existing", null, error);
|
||||
|
||||
|
||||
// Create dir1/sub_dir1 directory file
|
||||
label = dir1__sub_dir1_label; path = dir1__sub_dir1_path;
|
||||
error = FileUtils.createDirectoryFile(label, path);
|
||||
assertEqual("Failed to create " + label + " directory file", null, error);
|
||||
if (!FileUtils.directoryFileExists(path, false))
|
||||
throwException("The " + label + " directory file does not exist as expected after creation");
|
||||
|
||||
// Test non ignored dir file existing
|
||||
error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles(dir1_label, dir1_path, ignoredSubFilePaths, false);
|
||||
assertErrnoEqual("Failed to validate if " + dir1_label + " directory file is empty with non ignored dir file existing", FileUtilsErrno.ERRNO_NON_EMPTY_DIRECTORY_FILE, error);
|
||||
|
||||
|
||||
// Delete dir1 directory file
|
||||
label = dir1_label; path = dir1_path;
|
||||
error = FileUtils.deleteDirectoryFile(label, path, false);
|
||||
assertEqual("Failed to delete " + label + " directory file", null, error);
|
||||
|
||||
|
||||
FileUtils.getFileType("/dev/ptmx", false);
|
||||
FileUtils.getFileType("/dev/null", false);
|
||||
}
|
||||
|
@ -299,6 +378,13 @@ public class FileUtilsTests {
|
|||
return isEquals(expected, actual);
|
||||
}
|
||||
|
||||
public static void assertErrnoEqual(@NonNull final String message, final Errno expected, final Error actual) throws Exception {
|
||||
if ((expected == null && actual != null) || (expected != null && !expected.equalsErrorTypeAndCode(actual)))
|
||||
throwException(message + "\nexpected: \"" + expected + "\"\nactual: \"" + actual + "\"\nFull Error:\n" + (actual != null ? actual.toString() : ""));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static boolean isEquals(String expected, String actual) {
|
||||
return expected.equals(actual);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import java.util.Formatter;
|
|||
import java.util.List;
|
||||
|
||||
/*
|
||||
* Version: v0.51.0
|
||||
* Version: v0.52.0
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Changelog
|
||||
|
@ -274,6 +274,9 @@ import java.util.List;
|
|||
*
|
||||
* - 0.51.0 (2022-06-13)
|
||||
* - Added `TERMUX_APP.FILE_SHARE_RECEIVER_ACTIVITY_CLASS_NAME` and `TERMUX_APP.FILE_VIEW_RECEIVER_ACTIVITY_CLASS_NAME`.
|
||||
*
|
||||
* - 0.52.0 (2022-06-18)
|
||||
* - Added `TERMUX_PREFIX_DIR_IGNORED_SUB_FILES_PATHS_TO_CONSIDER_AS_EMPTY`.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -681,6 +684,11 @@ public final class TermuxConstants {
|
|||
public static final File TERMUX_APPS_DIR = new File(TERMUX_APPS_DIR_PATH);
|
||||
|
||||
|
||||
/** Termux app $PREFIX directory path ignored sub file paths to consider it empty */
|
||||
public static final List<String> TERMUX_PREFIX_DIR_IGNORED_SUB_FILES_PATHS_TO_CONSIDER_AS_EMPTY = Arrays.asList(
|
||||
TermuxConstants.TERMUX_TMP_PREFIX_DIR_PATH, TermuxConstants.TERMUX_ENV_TEMP_FILE_PATH, TermuxConstants.TERMUX_ENV_FILE_PATH);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Termux app and plugin preferences and properties file paths.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.termux.shared.termux.file;
|
||||
|
||||
import static com.termux.shared.termux.TermuxConstants.TERMUX_PREFIX_DIR_PATH;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
|
||||
|
@ -325,6 +327,21 @@ public class TermuxFileUtils {
|
|||
false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} doesn't exist, is empty or only contains
|
||||
* files in {@link TermuxConstants#TERMUX_PREFIX_DIR_IGNORED_SUB_FILES_PATHS_TO_CONSIDER_AS_EMPTY}.
|
||||
*/
|
||||
public static boolean isTermuxPrefixDirectoryEmpty() {
|
||||
Error error = FileUtils.validateDirectoryFileEmptyOrOnlyContainsSpecificFiles("termux prefix",
|
||||
TERMUX_PREFIX_DIR_PATH, TermuxConstants.TERMUX_PREFIX_DIR_IGNORED_SUB_FILES_PATHS_TO_CONSIDER_AS_EMPTY, true);
|
||||
if (error == null)
|
||||
return true;
|
||||
|
||||
if (!FileUtilsErrno.ERRNO_NON_EMPTY_DIRECTORY_FILE.equalsErrorTypeAndCode(error))
|
||||
Logger.logErrorExtended(LOG_TAG, "Failed to check if termux prefix directory is empty:\n" + error.getErrorLogString());
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a markdown {@link String} for stat output for various Termux app files paths.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue