Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4e5f2c7

Browse files
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.
1 parent 3373a1f commit 4e5f2c7

File tree

2 files changed

+89
-30
lines changed

2 files changed

+89
-30
lines changed

app/src/main/java/com/termux/app/TermuxInstaller.java

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
import java.util.zip.ZipEntry;
3232
import java.util.zip.ZipInputStream;
3333

34+
import static com.termux.shared.termux.TermuxConstants.TERMUX_PREFIX_DIR;
35+
import static com.termux.shared.termux.TermuxConstants.TERMUX_PREFIX_DIR_PATH;
36+
import static com.termux.shared.termux.TermuxConstants.TERMUX_STAGING_PREFIX_DIR;
37+
import static com.termux.shared.termux.TermuxConstants.TERMUX_STAGING_PREFIX_DIR_PATH;
38+
3439
/**
3540
* Install the Termux bootstrap packages if necessary by following the below steps:
3641
* <p/>
@@ -67,7 +72,7 @@ static void setupBootstrapIfNeeded(final Activity activity, final Runnable whenD
6772
// Termux can only be run as the primary user (device owner) since only that
6873
// account has the expected file system paths. Verify that:
6974
if (!PackageUtils.isCurrentUserThePrimaryUser(activity)) {
70-
bootstrapErrorMessage = activity.getString(R.string.bootstrap_error_not_primary_user_message, MarkdownUtils.getMarkdownCodeForString(TermuxConstants.TERMUX_PREFIX_DIR_PATH, false));
75+
bootstrapErrorMessage = activity.getString(R.string.bootstrap_error_not_primary_user_message, MarkdownUtils.getMarkdownCodeForString(TERMUX_PREFIX_DIR_PATH, false));
7176
Logger.logError(LOG_TAG, "isFilesDirectoryAccessible: " + isFilesDirectoryAccessible);
7277
Logger.logError(LOG_TAG, bootstrapErrorMessage);
7378
sendBootstrapCrashReportNotification(activity, bootstrapErrorMessage);
@@ -87,21 +92,18 @@ static void setupBootstrapIfNeeded(final Activity activity, final Runnable whenD
8792
return;
8893
}
8994

90-
final String PREFIX_FILE_PATH = TermuxConstants.TERMUX_PREFIX_DIR_PATH;
91-
final File PREFIX_FILE = TermuxConstants.TERMUX_PREFIX_DIR;
92-
9395
// If prefix directory exists, even if its a symlink to a valid directory and symlink is not broken/dangling
94-
if (FileUtils.directoryFileExists(PREFIX_FILE_PATH, true)) {
95-
File[] PREFIX_FILE_LIST = PREFIX_FILE.listFiles();
96+
if (FileUtils.directoryFileExists(TERMUX_PREFIX_DIR_PATH, true)) {
97+
File[] PREFIX_FILE_LIST = TERMUX_PREFIX_DIR.listFiles();
9698
// If prefix directory is empty or only contains the tmp directory
9799
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()))) {
98-
Logger.logInfo(LOG_TAG, "The prefix directory \"" + PREFIX_FILE_PATH + "\" exists but is empty or only contains the tmp directory.");
100+
Logger.logInfo(LOG_TAG, "The termux prefix directory \"" + TERMUX_PREFIX_DIR_PATH + "\" exists but is empty or only contains the tmp directory.");
99101
} else {
100102
whenDone.run();
101103
return;
102104
}
103-
} else if (FileUtils.fileExists(PREFIX_FILE_PATH, false)) {
104-
Logger.logInfo(LOG_TAG, "The prefix directory \"" + PREFIX_FILE_PATH + "\" does not exist but another file exists at its destination.");
105+
} else if (FileUtils.fileExists(TERMUX_PREFIX_DIR_PATH, false)) {
106+
Logger.logInfo(LOG_TAG, "The termux prefix directory \"" + TERMUX_PREFIX_DIR_PATH + "\" does not exist but another file exists at its destination.");
105107
}
106108

107109
final ProgressDialog progress = ProgressDialog.show(activity, null, activity.getString(R.string.bootstrap_installer_body), true, false);
@@ -113,24 +115,35 @@ public void run() {
113115

114116
Error error;
115117

116-
final String STAGING_PREFIX_PATH = TermuxConstants.TERMUX_STAGING_PREFIX_DIR_PATH;
117-
final File STAGING_PREFIX_FILE = new File(STAGING_PREFIX_PATH);
118-
119118
// Delete prefix staging directory or any file at its destination
120-
error = FileUtils.deleteFile("prefix staging directory", STAGING_PREFIX_PATH, true);
119+
error = FileUtils.deleteFile("termux prefix staging directory", TERMUX_STAGING_PREFIX_DIR_PATH, true);
121120
if (error != null) {
122-
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Error.getErrorMarkdownString(error));
121+
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
123122
return;
124123
}
125124

126125
// Delete prefix directory or any file at its destination
127-
error = FileUtils.deleteFile("prefix directory", PREFIX_FILE_PATH, true);
126+
error = FileUtils.deleteFile("termux prefix directory", TERMUX_PREFIX_DIR_PATH, true);
127+
if (error != null) {
128+
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
129+
return;
130+
}
131+
132+
// Create prefix staging directory if it does not already exist and set required permissions
133+
error = TermuxFileUtils.isTermuxPrefixStagingDirectoryAccessible(true, true);
134+
if (error != null) {
135+
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
136+
return;
137+
}
138+
139+
// Create prefix directory if it does not already exist and set required permissions
140+
error = TermuxFileUtils.isTermuxPrefixDirectoryAccessible(true, true);
128141
if (error != null) {
129-
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Error.getErrorMarkdownString(error));
142+
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
130143
return;
131144
}
132145

133-
Logger.logInfo(LOG_TAG, "Extracting bootstrap zip to prefix staging directory \"" + STAGING_PREFIX_PATH + "\".");
146+
Logger.logInfo(LOG_TAG, "Extracting bootstrap zip to prefix staging directory \"" + TERMUX_STAGING_PREFIX_DIR_PATH + "\".");
134147

135148
final byte[] buffer = new byte[8096];
136149
final List<Pair<String, String>> symlinks = new ArrayList<>(50);
@@ -147,23 +160,23 @@ public void run() {
147160
if (parts.length != 2)
148161
throw new RuntimeException("Malformed symlink line: " + line);
149162
String oldPath = parts[0];
150-
String newPath = STAGING_PREFIX_PATH + "/" + parts[1];
163+
String newPath = TERMUX_STAGING_PREFIX_DIR_PATH + "/" + parts[1];
151164
symlinks.add(Pair.create(oldPath, newPath));
152165

153166
error = ensureDirectoryExists(new File(newPath).getParentFile());
154167
if (error != null) {
155-
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Error.getErrorMarkdownString(error));
168+
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
156169
return;
157170
}
158171
}
159172
} else {
160173
String zipEntryName = zipEntry.getName();
161-
File targetFile = new File(STAGING_PREFIX_PATH, zipEntryName);
174+
File targetFile = new File(TERMUX_STAGING_PREFIX_DIR_PATH, zipEntryName);
162175
boolean isDirectory = zipEntry.isDirectory();
163176

164177
error = ensureDirectoryExists(isDirectory ? targetFile : targetFile.getParentFile());
165178
if (error != null) {
166-
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Error.getErrorMarkdownString(error));
179+
showBootstrapErrorDialog(activity, whenDone, Error.getErrorMarkdownString(error));
167180
return;
168181
}
169182

@@ -189,17 +202,17 @@ public void run() {
189202
Os.symlink(symlink.first, symlink.second);
190203
}
191204

192-
Logger.logInfo(LOG_TAG, "Moving prefix staging to prefix directory.");
205+
Logger.logInfo(LOG_TAG, "Moving termux prefix staging to prefix directory.");
193206

194-
if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) {
195-
throw new RuntimeException("Moving prefix staging to prefix directory failed");
207+
if (!TERMUX_STAGING_PREFIX_DIR.renameTo(TERMUX_PREFIX_DIR)) {
208+
throw new RuntimeException("Moving termux prefix staging to prefix directory failed");
196209
}
197210

198211
Logger.logInfo(LOG_TAG, "Bootstrap packages installed successfully.");
199212
activity.runOnUiThread(whenDone);
200213

201214
} catch (final Exception e) {
202-
showBootstrapErrorDialog(activity, PREFIX_FILE_PATH, whenDone, Logger.getStackTracesMarkdownString(null, Logger.getStackTracesStringArray(e)));
215+
showBootstrapErrorDialog(activity, whenDone, Logger.getStackTracesMarkdownString(null, Logger.getStackTracesStringArray(e)));
203216

204217
} finally {
205218
activity.runOnUiThread(() -> {
@@ -214,7 +227,7 @@ public void run() {
214227
}.start();
215228
}
216229

217-
public static void showBootstrapErrorDialog(Activity activity, String PREFIX_FILE_PATH, Runnable whenDone, String message) {
230+
public static void showBootstrapErrorDialog(Activity activity, Runnable whenDone, String message) {
218231
Logger.logErrorExtended(LOG_TAG, "Bootstrap Error:\n" + message);
219232

220233
// Send a notification with the exception so that the user knows why bootstrap setup failed
@@ -229,7 +242,7 @@ public static void showBootstrapErrorDialog(Activity activity, String PREFIX_FIL
229242
})
230243
.setPositiveButton(R.string.bootstrap_error_try_again, (dialog, which) -> {
231244
dialog.dismiss();
232-
FileUtils.deleteFile("prefix directory", PREFIX_FILE_PATH, true);
245+
FileUtils.deleteFile("termux prefix directory", TERMUX_PREFIX_DIR_PATH, true);
233246
TermuxInstaller.setupBootstrapIfNeeded(activity, whenDone);
234247
}).show();
235248
} catch (WindowManager.BadTokenException e1) {

termux-shared/src/main/java/com/termux/shared/file/TermuxFileUtils.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.termux.shared.markdown.MarkdownUtils;
1010
import com.termux.shared.models.ExecutionCommand;
1111
import com.termux.shared.models.errors.Error;
12+
import com.termux.shared.models.errors.FileUtilsErrno;
1213
import com.termux.shared.shell.TermuxShellEnvironmentClient;
1314
import com.termux.shared.shell.TermuxTask;
1415
import com.termux.shared.termux.AndroidUtils;
@@ -134,7 +135,9 @@ public static Error validateDirectoryFileExistenceAndPermissions(String label, f
134135
}
135136

136137
/**
137-
* Validate the existence and permissions of {@link TermuxConstants#TERMUX_FILES_DIR_PATH}.
138+
* Validate if {@link TermuxConstants#TERMUX_FILES_DIR_PATH} exists and has
139+
* {@link FileUtils#APP_WORKING_DIRECTORY_PERMISSIONS} permissions.
140+
*
138141
* This is required because binaries compiled for termux are hard coded with
139142
* {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} and the path must be accessible.
140143
*
@@ -216,14 +219,57 @@ public static Error isTermuxFilesDirectoryAccessible(@NonNull final Context cont
216219
if (createDirectoryIfMissing)
217220
context.getFilesDir();
218221

222+
if (!FileUtils.directoryFileExists(TermuxConstants.TERMUX_FILES_DIR_PATH, true))
223+
return FileUtilsErrno.ERRNO_FILE_NOT_FOUND_AT_PATH.getError("termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH);
224+
219225
if (setMissingPermissions)
220-
FileUtils.setMissingFilePermissions("Termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH,
226+
FileUtils.setMissingFilePermissions("termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH,
221227
FileUtils.APP_WORKING_DIRECTORY_PERMISSIONS);
222228

223-
return FileUtils.checkMissingFilePermissions("Termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH,
229+
return FileUtils.checkMissingFilePermissions("termux files directory", TermuxConstants.TERMUX_FILES_DIR_PATH,
224230
FileUtils.APP_WORKING_DIRECTORY_PERMISSIONS, false);
225231
}
226232

233+
/**
234+
* Validate if {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} exists and has
235+
* {@link FileUtils#APP_WORKING_DIRECTORY_PERMISSIONS} permissions.
236+
* .
237+
*
238+
* The {@link TermuxConstants#TERMUX_PREFIX_DIR_PATH} directory would not exist if termux has
239+
* not been installed or the bootstrap setup has not been run or if it was deleted by the user.
240+
*
241+
* @param createDirectoryIfMissing The {@code boolean} that decides if directory file
242+
* should be created if its missing.
243+
* @param setMissingPermissions The {@code boolean} that decides if permissions are to be
244+
* automatically set.
245+
* @return Returns the {@code error} if path is not a directory file, failed to create it,
246+
* or validating permissions failed, otherwise {@code null}.
247+
*/
248+
public static Error isTermuxPrefixDirectoryAccessible(boolean createDirectoryIfMissing, boolean setMissingPermissions) {
249+
return FileUtils.validateDirectoryFileExistenceAndPermissions("termux prefix directory", TermuxConstants.TERMUX_PREFIX_DIR_PATH,
250+
null, createDirectoryIfMissing,
251+
FileUtils.APP_WORKING_DIRECTORY_PERMISSIONS, setMissingPermissions, true,
252+
false, false);
253+
}
254+
255+
/**
256+
* Validate if {@link TermuxConstants#TERMUX_STAGING_PREFIX_DIR_PATH} exists and has
257+
* {@link FileUtils#APP_WORKING_DIRECTORY_PERMISSIONS} permissions.
258+
*
259+
* @param createDirectoryIfMissing The {@code boolean} that decides if directory file
260+
* should be created if its missing.
261+
* @param setMissingPermissions The {@code boolean} that decides if permissions are to be
262+
* automatically set.
263+
* @return Returns the {@code error} if path is not a directory file, failed to create it,
264+
* or validating permissions failed, otherwise {@code null}.
265+
*/
266+
public static Error isTermuxPrefixStagingDirectoryAccessible(boolean createDirectoryIfMissing, boolean setMissingPermissions) {
267+
return FileUtils.validateDirectoryFileExistenceAndPermissions("termux prefix staging directory", TermuxConstants.TERMUX_STAGING_PREFIX_DIR_PATH,
268+
null, createDirectoryIfMissing,
269+
FileUtils.APP_WORKING_DIRECTORY_PERMISSIONS, setMissingPermissions, true,
270+
false, false);
271+
}
272+
227273
/**
228274
* Get a markdown {@link String} for stat output for various Termux app files paths.
229275
*

0 commit comments

Comments
 (0)