From 78e2ea086db13afeb75193891041d259d159fe23 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Mon, 8 Jan 2024 08:58:29 -0500 Subject: [PATCH] GP-1164: User cache directory now falls back to user temp directory location if it can't be created (Closes #6064) --- .../main/java/generic/test/AbstractGTest.java | 4 +- .../java/ghidra/framework/Application.java | 10 +-- .../framework/TestApplicationUtils.java | 4 +- .../application/ApplicationLayout.java | 31 ------- .../application/ApplicationUtilities.java | 81 +++++++++++++------ 5 files changed, 62 insertions(+), 68 deletions(-) diff --git a/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java b/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java index cb0fe7ceb8..f3450065e2 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java @@ -18,7 +18,7 @@ package generic.test; import static org.junit.Assert.*; import java.io.File; -import java.io.FileNotFoundException; +import java.io.IOException; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -116,7 +116,7 @@ public abstract class AbstractGTest { return new File(ApplicationUtilities.getDefaultUserTempDir("ghidra"), "test_" + UUID.randomUUID() + File.separator + "temp.data").getPath(); } - catch (FileNotFoundException e) { + catch (IOException e) { throw new AssertException(e); } } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/Application.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/Application.java index 7a4a35f266..d3dbcf7be1 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/Application.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/Application.java @@ -74,14 +74,6 @@ public class Application { private void initialize() { - // Create application's user directories - try { - layout.createUserDirs(); - } - catch (IOException e) { - throw new AssertException(e.getMessage()); - } - // Set headless property String isHeadless = Boolean.toString(configuration.isHeadless()); System.setProperty(SystemUtilities.HEADLESS_PROPERTY, isHeadless); @@ -683,7 +675,7 @@ public class Application { return app != null ? app.layout.getUserTempDir() : ApplicationUtilities.getDefaultUserTempDir("ghidra"); } - catch (FileNotFoundException e) { + catch (IOException e) { throw new AssertException(e); } } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/TestApplicationUtils.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/TestApplicationUtils.java index 8d86abe91d..b66df2a1cc 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/TestApplicationUtils.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/TestApplicationUtils.java @@ -16,7 +16,7 @@ package ghidra.framework; import java.io.File; -import java.io.FileNotFoundException; +import java.io.IOException; import java.util.List; import org.apache.commons.lang3.StringUtils; @@ -156,7 +156,7 @@ public class TestApplicationUtils { String name = reposContainer.getName(); return new File(tmpDir, name); } - catch (FileNotFoundException e) { + catch (IOException e) { throw new AssertException(e); } } diff --git a/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationLayout.java b/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationLayout.java index b41da57e14..17bf88b362 100644 --- a/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationLayout.java +++ b/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationLayout.java @@ -16,13 +16,11 @@ package utility.application; import java.io.File; -import java.io.IOException; import java.util.*; import generic.jar.ResourceFile; import ghidra.framework.ApplicationProperties; import ghidra.framework.GModule; -import utilities.util.FileUtilities; /** * The Application Layout base class defines the customizable elements of the application's @@ -152,35 +150,6 @@ public abstract class ApplicationLayout { return patchDir; } - /** - * Creates the application's user directories (or ensures they already exist). - * - * @throws IOException if there was a problem creating the application's user directories. - */ - public final void createUserDirs() throws IOException { - if (userTempDir != null) { - if (!FileUtilities.mkdirs(userTempDir)) { - throw new IOException("Failed to create user temp directory: " + userTempDir); - } - FileUtilities.setOwnerOnlyPermissions(userTempDir); - } - - if (userCacheDir != null) { - if (!FileUtilities.mkdirs(userCacheDir)) { - throw new IOException("Failed to create user cache directory: " + userCacheDir); - } - FileUtilities.setOwnerOnlyPermissions(userCacheDir); - } - - if (userSettingsDir != null) { - if (!FileUtilities.mkdirs(userSettingsDir)) { - throw new IOException( - "Failed to create user settings directory: " + userSettingsDir); - } - FileUtilities.setOwnerOnlyPermissions(userSettingsDir); - } - } - /** * Checks whether or not the application is using a "single jar" layout. Custom application * layouts that extend this class can override this method once they determine they are in diff --git a/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationUtilities.java b/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationUtilities.java index 072eca1a36..261b0ddcd3 100644 --- a/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationUtilities.java +++ b/Ghidra/Framework/Utility/src/main/java/utility/application/ApplicationUtilities.java @@ -155,77 +155,91 @@ public class ApplicationUtilities { /** * Gets the application's default user temp directory. *

- * NOTE: This method does not create the directory. + * NOTE: This method creates the directory if it does not exist. * * @param applicationName The application name. * @return The application's default user temp directory. The returned {@link File} will * represent an absolute path. * @throws FileNotFoundException if the absolute path of the user temp directory could not be * determined. + * @throws IOException if the user temp directory could not be created. */ - public static File getDefaultUserTempDir(String applicationName) throws FileNotFoundException { + public static File getDefaultUserTempDir(String applicationName) + throws FileNotFoundException, IOException { String appName = applicationName.toLowerCase(); // Look for Ghidra-specific system property File tempOverrideDir = getSystemPropertyFile(PROPERTY_TEMP_DIR, false); if (tempOverrideDir != null) { - return new File(tempOverrideDir, getUserSpecificDirName(tempOverrideDir, appName)); + return createDir( + new File(tempOverrideDir, getUserSpecificDirName(tempOverrideDir, appName))); } // Look for XDG environment variable File xdgRuntimeDir = getEnvFile(XdgUtils.XDG_RUNTIME_DIR, false); if (xdgRuntimeDir != null) { - return new File(xdgRuntimeDir, getUserSpecificDirName(xdgRuntimeDir, appName)); + return createDir( + new File(xdgRuntimeDir, getUserSpecificDirName(xdgRuntimeDir, appName))); } File javaTmpDir = getJavaTmpDir(); - return new File(javaTmpDir, getUserSpecificDirName(javaTmpDir, appName)); + return createDir(new File(getJavaTmpDir(), getUserSpecificDirName(javaTmpDir, appName))); } /** * Gets the application's default user cache directory. *

- * NOTE: This method does not create the directory. + * NOTE: This method creates the directory if it does not exist. * * @param applicationProperties The application properties. * @return The application's default user cache directory. The returned {@link File} will * represent an absolute path. * @throws FileNotFoundException if the absolute path of the user cache directory could not be * determined. + * @throws IOException if the user cache directory could not be created. */ public static File getDefaultUserCacheDir(ApplicationProperties applicationProperties) - throws FileNotFoundException { + throws FileNotFoundException, IOException { String appName = applicationProperties.getApplicationName().toLowerCase(); // Look for Ghidra-specific system property File cacheOverrideDir = getSystemPropertyFile(PROPERTY_CACHE_DIR, false); if (cacheOverrideDir != null) { - return new File(cacheOverrideDir, getUserSpecificDirName(cacheOverrideDir, appName)); + return createDir( + new File(cacheOverrideDir, getUserSpecificDirName(cacheOverrideDir, appName))); } // Look for XDG environment variable File xdgCacheHomeDir = getEnvFile(XdgUtils.XDG_CACHE_HOME, false); if (xdgCacheHomeDir != null) { - return new File(xdgCacheHomeDir, getUserSpecificDirName(xdgCacheHomeDir, appName)); + return createDir( + new File(xdgCacheHomeDir, getUserSpecificDirName(xdgCacheHomeDir, appName))); } // Use platform-specific default location String userDirName = SystemUtilities.getUserName() + "-" + appName; - return switch (OperatingSystem.CURRENT_OPERATING_SYSTEM) { - case WINDOWS -> new File(getEnvFile("LOCALAPPDATA", true), appName); - case LINUX -> new File("/var/tmp/" + userDirName); - case MAC_OS_X -> new File("/var/tmp/" + userDirName); - default -> throw new FileNotFoundException( - "Failed to find the user cache directory: Unsupported operating system."); - }; + + try { + return createDir(switch (OperatingSystem.CURRENT_OPERATING_SYSTEM) { + case WINDOWS -> new File(getEnvFile("LOCALAPPDATA", true), appName); + case LINUX -> new File("/var/tmp/" + userDirName); + case MAC_OS_X -> new File("/var/tmp/" + userDirName); + default -> throw new FileNotFoundException( + "Failed to find the user cache directory: Unsupported operating system."); + }); + } + catch (IOException e) { + // Failed to create desired cache directory...use temp directory instead + return getDefaultUserTempDir(applicationProperties.getApplicationName()); + } } /** * Gets the application's default user settings directory. *

- * NOTE: This method does not create the directory. + * NOTE: This method creates the directory if it does not exist. * * @param applicationProperties The application properties. * @param installationDirectory The application installation directory. @@ -233,9 +247,10 @@ public class ApplicationUtilities { * represent an absolute path. * @throws FileNotFoundException if the absolute path of the user settings directory could not * be determined. + * @throws IOException if the user settings directory could not be created. */ public static File getDefaultUserSettingsDir(ApplicationProperties applicationProperties, - ResourceFile installationDirectory) throws FileNotFoundException { + ResourceFile installationDirectory) throws FileNotFoundException, IOException { String appName = applicationProperties.getApplicationName().toLowerCase(); ApplicationIdentifier applicationIdentifier = @@ -250,26 +265,26 @@ public class ApplicationUtilities { // Look for Ghidra-specific system property File settingsOverrideDir = getSystemPropertyFile(PROPERTY_SETTINGS_DIR, false); if (settingsOverrideDir != null) { - return new File(settingsOverrideDir, - getUserSpecificDirName(settingsOverrideDir, appName) + "/" + versionedName); + return createDir(new File(settingsOverrideDir, + getUserSpecificDirName(settingsOverrideDir, appName) + "/" + versionedName)); } // Look for XDG environment variable File xdgConfigHomeDir = getEnvFile(XdgUtils.XDG_CONFIG_HOME, false); if (xdgConfigHomeDir != null) { - return new File(xdgConfigHomeDir, - getUserSpecificDirName(xdgConfigHomeDir, appName) + "/" + versionedName); + return createDir(new File(xdgConfigHomeDir, + getUserSpecificDirName(xdgConfigHomeDir, appName) + "/" + versionedName)); } File userHomeDir = getJavaUserHomeDir(); String versionedSubdir = appName + "/" + versionedName; - return switch (OperatingSystem.CURRENT_OPERATING_SYSTEM) { + return createDir(switch (OperatingSystem.CURRENT_OPERATING_SYSTEM) { case WINDOWS -> new File(getEnvFile("APPDATA", true), versionedSubdir); case LINUX -> new File(userHomeDir, ".config/" + versionedSubdir); case MAC_OS_X -> new File(userHomeDir, "Library/" + versionedSubdir); default -> throw new FileNotFoundException( "Failed to find the user settings directory: Unsupported operating system."); - }; + }); } /** @@ -407,4 +422,22 @@ public class ApplicationUtilities { } return userSpecificDirName; } + + /** + * Creates the given {@link File directory} if it does not exist, and sets its permissions to + * owner-only + * + * @param dir The directory to create + * @return The given directory + * @throws IOException if the directory failed to be created + */ + private static File createDir(File dir) throws IOException { + if (dir != null) { + if (!FileUtilities.mkdirs(dir)) { + throw new IOException("Failed to create directory: " + dir); + } + FileUtilities.setOwnerOnlyPermissions(dir); + } + return dir; + } }