GP-1164: Reorganizing Ghidra's user settings/cache/temp directories to support XDG

This commit is contained in:
Ryan Kurtz 2023-12-19 08:22:40 -05:00
parent 8bfcb02166
commit 3c30ada14c
50 changed files with 919 additions and 222 deletions

View File

@ -75,6 +75,9 @@ public class GhidraRun implements GhidraLaunchable {
log = LogManager.getLogger(GhidraRun.class);
log.info("User " + SystemUtilities.getUserName() + " started Ghidra.");
log.info("User settings directory: " + Application.getUserSettingsDirectory());
log.info("User temp directory: " + Application.getUserTempDirectory());
log.info("User cache directory: " + Application.getUserCacheDirectory());
initializeTooltips();

View File

@ -40,6 +40,7 @@ import ghidra.app.context.*;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.services.GoToService;
import ghidra.framework.Application;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
@ -190,7 +191,7 @@ public class ShowInstructionInfoPlugin extends ProgramPlugin {
private File writeWrapperFile(URL fileURL) throws IOException {
File f;
if (manualWrapperFiles.size() < MAX_MANUAL_WRAPPER_FILE_COUNT) {
f = File.createTempFile("pdfView", ".html");
f = Application.createTempFile("pdfView", ".html");
f.deleteOnExit();
}
else {

View File

@ -24,6 +24,7 @@ import generic.theme.GThemeDefaults.Colors;
import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.cparser.CPP.PreProcessor;
import ghidra.framework.Application;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.store.LockException;
import ghidra.program.model.data.*;
@ -535,7 +536,7 @@ public class CParserUtils {
String fName = dtMgr.getName();
// make a path to tmpdir with name of data type manager
String path = System.getProperty("java.io.tmpdir") + File.pathSeparator + fName;
String path = new File(Application.getUserTempDirectory(), fName).getAbsolutePath();
// if file data type manager, use path to .gdt file
if (dtMgr instanceof FileDataTypeManager) {
path = ((FileDataTypeManager) dtMgr).getPath();

View File

@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.*;
import ghidra.framework.Application;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
@ -166,7 +167,7 @@ public class OriginalFileExporter extends Exporter {
// Write source program's file bytes to a temp file.
// This is done to ensure a random access write failure doesn't corrupt a file the user
// might be overwriting.
File tempFile = File.createTempFile("ghidra_export_", null);
File tempFile = Application.createTempFile("ghidra_export_", null);
try (OutputStream out = new FileOutputStream(tempFile, false)) {
FileUtilities.copyStreamToStream(new FileBytesInputStream(fileBytes, true), out,
monitor);

View File

@ -36,6 +36,7 @@ import ghidra.app.util.bin.format.elf.extend.ElfLoadAdapter;
import ghidra.app.util.bin.format.elf.info.ElfInfoProducer;
import ghidra.app.util.bin.format.elf.relocation.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.Application;
import ghidra.framework.options.Options;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.FileBytes;
@ -1606,7 +1607,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
Address debugDataAddr = findLoadAddress(debugDataSection, 0);
if (debugDataAddr != null) {
try {
File tmpFile = File.createTempFile("ghidra_gnu_debugdata", null);
File tmpFile = Application.createTempFile("ghidra_gnu_debugdata", null);
try (ByteProviderWrapper compressedDebugDataBP = new ByteProviderWrapper(
new MemoryByteProvider(memory, debugDataAddr), 0, debugDataSection.getSize());
XZCompressorInputStream xzIS =

View File

@ -25,6 +25,7 @@ import db.DBHandle;
import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.Application;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.Project;
import ghidra.framework.store.db.PackedDatabase;
@ -138,7 +139,7 @@ public class GdtLoader implements Loader {
private static File createTmpFile(ByteProvider provider, TaskMonitor monitor)
throws IOException {
File tmpFile = File.createTempFile("ghidra_gdt_loader", null);
File tmpFile = Application.createTempFile("ghidra_gdt_loader", null);
try (InputStream is = provider.getInputStream(0);
FileOutputStream fos = new FileOutputStream(tmpFile)) {
FileUtilities.copyStreamToStream(is, fos, monitor);

View File

@ -25,6 +25,7 @@ import db.DBHandle;
import ghidra.app.util.Option;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.Application;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.Project;
import ghidra.framework.store.db.PackedDatabase;
@ -148,7 +149,7 @@ public class GzfLoader implements Loader {
private static File createTmpFile(ByteProvider provider, TaskMonitor monitor)
throws IOException {
File tmpFile = File.createTempFile("ghidra_gzf_loader", null);
File tmpFile = Application.createTempFile("ghidra_gzf_loader", null);
try (InputStream is = provider.getInputStream(0);
FileOutputStream fos = new FileOutputStream(tmpFile)) {
FileUtilities.copyStreamToStream(is, fos, monitor);

View File

@ -577,7 +577,7 @@ public class FileSystemService {
public File createPlaintextTempFile(ByteProvider provider, String filenamePrefix,
TaskMonitor monitor) throws IOException {
File tmpFile =
File.createTempFile(filenamePrefix, Long.toString(System.currentTimeMillis()));
Application.createTempFile(filenamePrefix, Long.toString(System.currentTimeMillis()));
monitor.setMessage("Copying " + provider.getName() + " to temp file");
monitor.initialize(provider.length());
try {

View File

@ -20,6 +20,7 @@ import java.util.Comparator;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
import ghidra.framework.Application;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
@ -138,7 +139,7 @@ public abstract class GFileSystemBase implements GFileSystem {
protected void debug(byte[] bytes, String fileName) {
try {
if (SystemUtilities.isInDevelopmentMode()) {
File file = File.createTempFile(fileName, ".ghidra.tmp");
File file = Application.createTempFile(fileName, ".ghidra.tmp");
OutputStream out = new FileOutputStream(file);
try {
out.write(bytes);

View File

@ -28,8 +28,7 @@ import ghidra.app.util.importer.AutoImporter;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.LoadException;
import ghidra.app.util.opinion.LoadResults;
import ghidra.framework.Application;
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
import ghidra.framework.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.*;
@ -38,6 +37,7 @@ import ghidra.program.util.DefaultLanguageService;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import utility.application.ApplicationLayout;
import utility.application.ApplicationUtilities;
/**
* Wrapper for Ghidra code to find images (and maybe other artifacts later) in a program
@ -122,8 +122,9 @@ public class ProgramExaminer {
if (!Application.isInitialized()) {
ApplicationLayout layout;
try {
layout =
new GhidraTestApplicationLayout(new File(System.getProperty("java.io.tmpdir")));
layout = new GhidraTestApplicationLayout(ApplicationUtilities.getDefaultUserTempDir(
new ApplicationProperties(ApplicationUtilities.findDefaultApplicationRootDirs())
.getApplicationName()));
}
catch (IOException e) {
throw new GhidraException(e);
@ -184,8 +185,7 @@ public class ProgramExaminer {
int txID = program.startTransaction("find images");
try {
EmbeddedMediaAnalyzer imageAnalyzer = new EmbeddedMediaAnalyzer();
imageAnalyzer.added(program, program.getMemory(), TaskMonitor.DUMMY,
messageLog);
imageAnalyzer.added(program, program.getMemory(), TaskMonitor.DUMMY, messageLog);
}
catch (CancelledException e) {
// using Dummy, can't happen

View File

@ -428,7 +428,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
// return; // already enabled by batch test environment
// }
//
// String tmpDir = System.getProperty("java.io.tmpdir");
// File tmpDir = Application.getUserTempDirectory();
// File cacheDir = new File(tmpDir, "EmulatorDBTestCache");
// if (cacheDir.exists() && !FileUtilities.deleteDir(cacheDir)) {
// Msg.warn(ProcessorEmulatorTestAdapter.class,

View File

@ -704,7 +704,7 @@ public class GhidraJarBuilder implements GhidraLaunchable {
}
public void close() throws IOException {
File tempFile = File.createTempFile("jarBuilder", "treeIDX");
File tempFile = Application.createTempFile("jarBuilder", "treeIDX");
classTree.trim();
classTree.saveFile(tempFile);
try {

View File

@ -54,6 +54,7 @@ import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.datamgr.util.DataTypeChooserDialog;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.services.ProgramManager;
import ghidra.framework.Application;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.data.ProgramDataTypeManager;
@ -723,7 +724,7 @@ public class DataTypeSelectionDialogTest extends AbstractGhidraHeadedIntegration
DataTypeManagerHandler dataTypeManagerHandler = plugin.getDataTypeManagerHandler();
File tempArchiveFile;
try {
tempArchiveFile = File.createTempFile("TestFileArchive", ".gdt");
tempArchiveFile = Application.createTempFile("TestFileArchive", ".gdt");
}
catch (IOException e) {
e.printStackTrace();

View File

@ -16,7 +16,8 @@
package db;
import java.awt.*;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import javax.swing.*;
@ -240,7 +241,7 @@ public class DbViewer extends JFrame {
return stats;
}
public static void main(String[] args) throws FileNotFoundException {
public static void main(String[] args) throws IOException {
ApplicationLayout layout = new GenericApplicationLayout("DB Viewer", "1.0");
DockingApplicationConfiguration configuration = new DockingApplicationConfiguration();
configuration.setShowSplashScreen(false);

View File

@ -27,6 +27,7 @@ import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.fileinfo.FileAttribute;
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
import ghidra.framework.Application;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import utilities.util.FileUtilities;
@ -76,7 +77,7 @@ public class JavaClassDecompilerFileSystem implements GFileSystem {
throws CancelledException, IOException {
File tempDir = null;
try {
tempDir = FileUtilities.createTempDirectory("JavaClassDecompilerFileSystem");
tempDir = new File(Application.getUserTempDirectory(), "JavaClassDecompilerFileSystem");
File tempClassFile = new File(tempDir, containerFSRL.getName());
FSUtilities.copyByteProviderToFile(provider, tempClassFile, monitor);

View File

@ -15,7 +15,6 @@
*/
package ghidra.server.remote;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
@ -34,10 +33,10 @@ public class GhidraServerApplicationLayout extends ApplicationLayout {
/**
* Constructs a new Ghidra server application layout object.
*
* @throws FileNotFoundException if there was a problem getting a user directory.
* @throws IOException if there was a problem getting the application properties.
* @throws IOException if there was a problem getting a user directory or the application
* properties.
*/
public GhidraServerApplicationLayout() throws FileNotFoundException, IOException {
public GhidraServerApplicationLayout() throws IOException {
// Application root directories
applicationRootDirs = ApplicationUtilities.findDefaultApplicationRootDirs();
@ -56,7 +55,8 @@ public class GhidraServerApplicationLayout extends ApplicationLayout {
extensionInstallationDirs = Collections.emptyList();
// User directories (don't let anything use the user home directory...there may not be one)
userTempDir = ApplicationUtilities.getDefaultUserTempDir(applicationProperties);
userTempDir =
ApplicationUtilities.getDefaultUserTempDir(applicationProperties.getApplicationName());
// Modules - required to find module data files
modules = ModuleUtilities.findModules(applicationRootDirs,

View File

@ -21,8 +21,7 @@ import java.util.*;
import db.DBChangeSet;
import db.DBHandle;
import db.buffers.LocalBufferFile.BufferFileFilter;
import ghidra.framework.ShutdownHookRegistry;
import ghidra.framework.ShutdownPriority;
import ghidra.framework.*;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.datastruct.ObjectArray;
@ -2049,7 +2048,7 @@ public class BufferMgr {
}
public static void cleanupOldCacheFiles() {
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
File tmpDir = Application.getUserTempDirectory();
File[] cacheFiles =
tmpDir.listFiles(new BufferFileFilter(CACHE_FILE_PREFIX, CACHE_FILE_EXT));
if (cacheFiles == null) {

View File

@ -18,6 +18,7 @@ package db.buffers;
import java.io.*;
import java.util.*;
import ghidra.framework.Application;
import ghidra.util.BigEndianDataConverter;
import ghidra.util.Msg;
import ghidra.util.datastruct.IntSet;
@ -197,8 +198,7 @@ public class LocalBufferFile implements BufferFile {
this.blockSize = bufferSize + BUFFER_PREFIX_SIZE;
this.readOnly = false;
this.temporary = true;
file = File.createTempFile(tmpPrefix, tmpExtension);
// file.deleteOnExit();
file = Application.createTempFile(tmpPrefix, tmpExtension);
raf = new RandomAccessFile(file, "rw");
}

View File

@ -24,6 +24,7 @@ import java.util.Random;
import org.junit.Assert;
import db.buffers.*;
import ghidra.framework.Application;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -815,7 +816,7 @@ public class DBTestUtils {
public static DBHandle cloneDbHandle(DBHandle dbh) throws IOException {
try {
File tmpFile = File.createTempFile("tmp", ".db");
File tmpFile = Application.createTempFile("tmp", ".db");
tmpFile.delete();
LocalBufferFile bf = new LocalBufferFile(tmpFile, dbh.getBufferSize());

View File

@ -24,6 +24,7 @@ import db.Database;
import db.buffers.BufferFileManager;
import db.buffers.LocalManagedBufferFile;
import generic.jar.ResourceFile;
import ghidra.framework.Application;
import ghidra.framework.store.FolderItem;
import ghidra.framework.store.db.PackedDatabaseCache.CachedDB;
import ghidra.framework.store.local.*;
@ -32,7 +33,6 @@ import ghidra.util.datastruct.WeakDataStructureFactory;
import ghidra.util.datastruct.WeakSet;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
import utilities.util.FileUtilities;
/**
@ -364,7 +364,7 @@ public class PackedDatabase extends Database {
*/
private static File createDBDir() throws IOException {
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
File tmpDir = Application.getUserTempDirectory();
int tries = 0;
while (tries++ < 10) {
File dir = new File(tmpDir, TEMPDB_DIR_PREFIX + getRandomString() + TEMPDB_DIR_EXT);
@ -620,7 +620,7 @@ public class PackedDatabase extends Database {
InputStream itemIn = null;
File tmpFile = null;
try {
tmpFile = File.createTempFile("pack", ".tmp");
tmpFile = Application.createTempFile("pack", ".tmp");
tmpFile.delete();
dbh.saveAs(tmpFile, false, monitor);
itemIn = new BufferedInputStream(new FileInputStream(tmpFile));
@ -838,7 +838,7 @@ public class PackedDatabase extends Database {
*/
public static void cleanupOldTempDatabases() {
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
File tmpDir = Application.getUserTempDirectory();
File[] tempDbs = tmpDir.listFiles((FileFilter) file -> {
String name = file.getName();
if (file.isDirectory()) {

View File

@ -23,6 +23,7 @@ import org.apache.logging.log4j.Logger;
import db.DBHandle;
import db.Database;
import db.buffers.*;
import ghidra.framework.Application;
import ghidra.framework.store.local.ItemSerializer;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
@ -417,7 +418,8 @@ public class VersionedDatabase extends Database {
else {
BufferFile bf = openBufferFile(version, -1);
try {
File tmpFile = File.createTempFile("ghidra", LocalBufferFile.TEMP_FILE_EXT);
File tmpFile =
Application.createTempFile("ghidra", LocalBufferFile.TEMP_FILE_EXT);
tmpFile.delete();
BufferFile tmpBf = new LocalBufferFile(tmpFile, bf.getBufferSize());
boolean success = false;
@ -425,18 +427,10 @@ public class VersionedDatabase extends Database {
LocalBufferFile.copyFile(bf, tmpBf, null, monitor);
tmpBf.close();
InputStream itemIn = new FileInputStream(tmpFile);
try {
try (InputStream itemIn = new FileInputStream(tmpFile)) {
ItemSerializer.outputItem(name, contentType, filetype, tmpFile.length(),
itemIn, outputFile, monitor);
}
finally {
try {
itemIn.close();
}
catch (IOException e) {
}
}
success = true;
}
finally {

View File

@ -18,6 +18,7 @@ package ghidra.framework.store.remote;
import java.io.*;
import db.buffers.*;
import ghidra.framework.Application;
import ghidra.framework.client.RepositoryAdapter;
import ghidra.framework.remote.RepositoryItem;
import ghidra.framework.store.DatabaseItem;
@ -101,25 +102,17 @@ public class RemoteDatabaseItem extends RemoteFolderItem implements DatabaseItem
BufferFile bf = repository.openDatabase(parentPath, itemName, version, -1);
try {
File tmpFile = File.createTempFile("ghidra", LocalBufferFile.TEMP_FILE_EXT);
File tmpFile = Application.createTempFile("ghidra", LocalBufferFile.TEMP_FILE_EXT);
tmpFile.delete();
BufferFile tmpBf = new LocalBufferFile(tmpFile, bf.getBufferSize());
try {
LocalBufferFile.copyFile(bf, tmpBf, null, monitor);
tmpBf.close();
InputStream itemIn = new FileInputStream(tmpFile);
try {
try (InputStream itemIn = new FileInputStream(tmpFile)) {
ItemSerializer.outputItem(getName(), getContentType(), DATABASE_FILE_TYPE,
tmpFile.length(), itemIn, outputFile, monitor);
}
finally {
try {
itemIn.close();
}
catch (IOException e) {
}
}
}
finally {
tmpBf.close();

View File

@ -16,7 +16,7 @@
package generic.application;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
@ -59,9 +59,9 @@ public class GenericApplicationLayout extends ApplicationLayout {
*
* @param name The name of the application.
* @param version The version of the application.
* @throws FileNotFoundException if there was a problem getting a user directory.
* @throws IOException if there was a problem getting a user directory.
*/
public GenericApplicationLayout(String name, String version) throws FileNotFoundException {
public GenericApplicationLayout(String name, String version) throws IOException {
this(new ApplicationProperties(name, version, NO_RELEASE_NAME));
}
@ -70,10 +70,10 @@ public class GenericApplicationLayout extends ApplicationLayout {
* properties. The default Ghidra application root directory(s) will be used.
*
* @param applicationProperties The properties object that will be read system properties.
* @throws FileNotFoundException if there was a problem getting a user directory.
* @throws IOException if there was a problem getting a user directory.
*/
public GenericApplicationLayout(ApplicationProperties applicationProperties)
throws FileNotFoundException {
throws IOException {
this(getDefaultApplicationRootDirs(), applicationProperties);
}
@ -85,10 +85,10 @@ public class GenericApplicationLayout extends ApplicationLayout {
* used to identify modules and resources. The first entry will be treated as the
* installation root.
* @param applicationProperties The properties object that will be read system properties.
* @throws FileNotFoundException if there was a problem getting a user directory.
* @throws IOException if there was a problem getting a user directory.
*/
public GenericApplicationLayout(Collection<ResourceFile> applicationRootDirs,
ApplicationProperties applicationProperties) throws FileNotFoundException {
ApplicationProperties applicationProperties) throws IOException {
this.applicationProperties = Objects.requireNonNull(applicationProperties);
this.applicationRootDirs = applicationRootDirs;
@ -117,7 +117,8 @@ public class GenericApplicationLayout extends ApplicationLayout {
modules = Collections.unmodifiableMap(allModules);
// User directories
userTempDir = ApplicationUtilities.getDefaultUserTempDir(applicationProperties);
userTempDir =
ApplicationUtilities.getDefaultUserTempDir(applicationProperties.getApplicationName());
userSettingsDir = ApplicationUtilities.getDefaultUserSettingsDir(applicationProperties,
applicationInstallationDir);

View File

@ -18,6 +18,7 @@ package generic.test;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -35,6 +36,7 @@ import ghidra.util.SystemUtilities;
import ghidra.util.UniversalIdGenerator;
import ghidra.util.exception.AssertException;
import junit.framework.AssertionFailedError;
import utility.application.ApplicationUtilities;
/**
* A root for system tests that provides known system information.
@ -110,16 +112,21 @@ public abstract class AbstractGTest {
// In batch mode we rely on the fact that the test environment has been setup with a
// custom temp directory.
//
return System.getProperty("java.io.tmpdir") + File.separator + "Ghidra_test_" +
UUID.randomUUID() + File.separator + "temp.data";
try {
return new File(ApplicationUtilities.getDefaultUserTempDir("ghidra"),
"test_" + UUID.randomUUID() + File.separator + "temp.data").getPath();
}
catch (FileNotFoundException e) {
throw new AssertException(e);
}
}
private static String buildDevelopmentDirectoryPath() {
//
// Create a unique name based upon the repo from which we are running.
//
File tempDir = TestApplicationUtils.getUniqueTempFolder();
return tempDir.getAbsolutePath();
File tempDir = TestApplicationUtils.getUniqueTempDir();
return tempDir.getPath();
}
public static String getTestDirectoryPath() {

View File

@ -809,10 +809,10 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a file in the Java temp directory using the given name as a
* Creates a file in the Application temp directory using the given name as a
* prefix and the given suffix. The final filename will also include the
* current test name, as well as any data added by
* {@link File#createTempFile(String, String)}. The file suffix will be
* {@link File#createTempFile(String, String, File)}. The file suffix will be
* <code>.tmp</code>
* <p>
* The file will be marked to delete on JVM exit. This will not work if the
@ -830,10 +830,10 @@ public abstract class AbstractGenericTest extends AbstractGTest {
}
/**
* Creates a file in the Java temp directory using the given name as a
* Creates a file in the Application temp directory using the given name as a
* prefix and the given suffix. The final filename will also include the
* current test name, as well as any data added by
* {@link File#createTempFile(String, String)}.
* {@link File#createTempFile(String, String, File)}.
* <p>
* The file will be marked to delete on JVM exit. This will not work if the
* JVM is taken down the hard way, as when pressing the stop button in

View File

@ -29,6 +29,7 @@ import util.CollectionUtils;
import utilities.util.FileUtilities;
import utilities.util.reflection.ReflectionUtilities;
import utility.application.ApplicationLayout;
import utility.application.ApplicationUtilities;
import utility.module.ModuleUtilities;
/**
@ -667,7 +668,6 @@ public class Application {
/**
* Returns the temporary directory specific to the user and the application.
* Directory has name of &lt;username&gt;-&lt;appname&gt;
* This directory may be removed at system reboot or during periodic
* system cleanup of unused temp files.
* This directory is specific to the application name but not the version.
@ -677,15 +677,22 @@ public class Application {
* @return temp directory
*/
public static File getUserTempDirectory() {
checkAppInitialized();
return app.layout.getUserTempDir();
try {
// 'app' will be null when the application has not been initialized yet. In this case,
// we provide the default user temp directory.
return app != null ? app.layout.getUserTempDir()
: ApplicationUtilities.getDefaultUserTempDir("ghidra");
}
catch (FileNotFoundException e) {
throw new AssertException(e);
}
}
/**
* Returns the cache directory specific to the user and the application.
* The intention is for directory contents to be preserved, however the
* specific location is platform specific and contents may be removed when
* not in use and may in fact be the same directory the user temp directory.
* not in use.
* This directory is specific to the application name but not the version.
* Resources stored within this directory should utilize some
* form of access locking and/or unique naming.
@ -696,6 +703,24 @@ public class Application {
return app.layout.getUserCacheDir();
}
/**
* Creates a new empty file in the Application's temp directory, using the given prefix and
* suffix strings to generate its name.
*
* @param prefix The prefix string to be used in generating the file's name; must be at least
* three characters long
* @param suffix The suffix string to be used in generating the file's name; may be
* {@code null}, in which case the suffix {@code ".tmp"} will be used
* @return A {@link File} denoting a newly-created empty file
* @throws IllegalArgumentException If the {@code prefix} argument contains fewer than three
* characters
* @throws IOException If a file could not be created
* @see File#createTempFile(String, String, File)
*/
public static File createTempFile(String prefix, String suffix) throws IOException {
return File.createTempFile(prefix, suffix, getUserTempDirectory());
}
/**
* Returns a collection of all the module root directories. A module root directory is
* the top-level directory of a module.

View File

@ -15,8 +15,7 @@
*/
package ghidra.framework;
import java.io.File;
import java.io.FileFilter;
import java.io.*;
import java.util.*;
import org.apache.logging.log4j.LogManager;
@ -24,6 +23,8 @@ import org.apache.logging.log4j.Logger;
import ghidra.framework.preferences.Preferences;
import util.CollectionUtils;
import utility.application.ApplicationLayout;
import utility.application.ApplicationUtilities;
public class GenericRunInfo {
@ -42,10 +43,23 @@ public class GenericRunInfo {
* <b>Note: </b>This method ignores Test directories
*/
private static List<File> getUserSettingsDirsByTime() {
File userDataDirectory = Application.getUserSettingsDirectory();
File userDataDirParentFile = userDataDirectory.getParentFile();
ApplicationLayout layout = Application.getApplicationLayout();
File userSettingsDirectory = Application.getUserSettingsDirectory();
List<File> appDirs = collectAllApplicationDirectories(userDataDirParentFile);
List<File> appDirs =
collectAllApplicationDirectories(userSettingsDirectory.getParentFile());
// Search "legacy" user setting directory locations in case the user has upgraded from an
// older version
try {
File legacyUserSettingsDirectory = ApplicationUtilities.getLegacyUserSettingsDir(
layout.getApplicationProperties(), layout.getApplicationInstallationDir());
appDirs.addAll(
collectAllApplicationDirectories(legacyUserSettingsDirectory.getParentFile()));
}
catch (FileNotFoundException e) {
// ignore
}
Comparator<File> modifyTimeComparator = (f1, f2) -> {

View File

@ -16,6 +16,7 @@
package ghidra.framework;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
@ -24,6 +25,7 @@ import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException;
import utilities.util.FileUtilities;
import utility.application.ApplicationUtilities;
import utility.module.ModuleUtilities;
public class TestApplicationUtils {
@ -127,13 +129,13 @@ public class TestApplicationUtils {
}
/**
* Creates a folder that is unique for the current installation. This allows clients to
* Creates a directory that is unique for the current installation. This allows clients to
* have multiple clones (for development mode) or multiple installations (for release mode)
* on their machine, running tests from each repo simultaneously.
*
* @return a folder that is unique for the current installation
* @return an absolute form directory that is unique for the current installation
*/
public static File getUniqueTempFolder() {
public static File getUniqueTempDir() {
//
// Create a unique name based upon the repo from which we are running.
@ -144,14 +146,18 @@ public class TestApplicationUtils {
reposContainer = installDir;
}
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
String tempName = tmpDir.getName();
try {
File tmpDir = ApplicationUtilities.getDefaultUserTempDir("ghidra");
//
// The container name makes this name unique across multiple Eclipses; the system temp
// name makes this name unique across multiple runs from the same Eclipse
//
String name = reposContainer.getName() + tempName;
return new File(tmpDir, name);
//
// The container name makes this name unique across multiple Eclipses; the system temp
// name makes this name unique across multiple runs from the same Eclipse
//
String name = reposContainer.getName();
return new File(tmpDir, name);
}
catch (FileNotFoundException e) {
throw new AssertException(e);
}
}
}

View File

@ -15,8 +15,8 @@
*/
package utilities.util;
import static generic.test.AbstractGTest.assertListEqualsArrayOrdered;
import static org.hamcrest.CoreMatchers.equalTo;
import static generic.test.AbstractGTest.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.io.*;
@ -30,8 +30,7 @@ import org.junit.Test;
import generic.jar.ResourceFile;
import generic.test.AbstractGenericTest;
import ghidra.framework.OperatingSystem;
import ghidra.framework.Platform;
import ghidra.framework.*;
import utilities.util.FileResolutionResult.FileResolutionStatus;
public class FileUtilitiesTest {
@ -198,11 +197,11 @@ public class FileUtilitiesTest {
@Test
public void copyFile_ResourceFile_To_ResourceFile() throws Exception {
File from = File.createTempFile("from.file", ".txt");
File from = Application.createTempFile("from.file", ".txt");
FileUtilities.writeLinesToFile(from, Arrays.asList("From file contents"));
from.deleteOnExit();
File to = File.createTempFile("to.file", ".txt");
File to = Application.createTempFile("to.file", ".txt");
to.deleteOnExit();
FileUtilities.writeLinesToFile(to, Arrays.asList("To file contents"));
@ -222,7 +221,7 @@ public class FileUtilitiesTest {
}
};
File to = File.createTempFile("to.file", ".txt");
File to = Application.createTempFile("to.file", ".txt");
to.deleteOnExit();
// should fail
@ -232,7 +231,7 @@ public class FileUtilitiesTest {
@Test(expected = IOException.class)
public void copyFile_ExceptionFromOutputStream() throws Exception {
File from = File.createTempFile("from.file", ".txt");
File from = Application.createTempFile("from.file", ".txt");
from.deleteOnExit();
ResourceFile to = new ResourceFile(new File("/to.from.file")) {

View File

@ -25,6 +25,7 @@ import javax.swing.Icon;
import javax.swing.ImageIcon;
import generic.util.image.ImageUtils;
import ghidra.framework.Application;
import ghidra.util.Msg;
/**
@ -94,7 +95,7 @@ public class IconProvider {
}
try {
File imageFile = File.createTempFile("temp.help.icon", null);
File imageFile = Application.createTempFile("temp.help.icon", null);
imageFile.deleteOnExit(); // don't let this linger
ImageIcon imageIcon = ResourceManager.getImageIcon(icon);
ImageUtils.writeFile(imageIcon.getImage(), imageFile);

View File

@ -25,6 +25,7 @@ import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.ParserAdapter;
import ghidra.framework.Application;
import ghidra.util.xml.XmlUtilities;
/**
@ -174,7 +175,7 @@ public class TOCConverter {
* @throws IOException
*/
private File createTempTOCFile() throws IOException {
File tempFile = File.createTempFile("toc", ".xml");
File tempFile = Application.createTempFile("toc", ".xml");
PrintWriter out = new PrintWriter(new FileOutputStream(tempFile));
BufferedReader reader = new BufferedReader(new FileReader(sourceFilename));

View File

@ -21,6 +21,7 @@ import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import ghidra.framework.Application;
import ghidra.framework.protocol.ghidra.GhidraURL;
/**
@ -67,7 +68,7 @@ public class ProjectLocator {
}
this.name = name;
if (StringUtils.isBlank(path)) {
path = System.getProperty("java.io.tmpdir");
path = Application.getUserTempDirectory().getAbsolutePath();
}
this.location = checkAbsolutePath(path);
url = GhidraURL.makeURL(location, name);

View File

@ -22,6 +22,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import ghidra.framework.Application;
import ghidra.framework.client.NotConnectedException;
import ghidra.framework.client.RepositoryAdapter;
import ghidra.framework.model.ProjectLocator;
@ -172,7 +173,7 @@ public class TransientProjectManager {
private TransientProjectData createTransientProject(RepositoryAdapter repository,
RepositoryInfo repositoryInfo) throws IOException {
File tmp = File.createTempFile("ghidraPrj", "");
File tmp = Application.createTempFile("ghidraPrj", "");
tmp.delete();
ProjectLocator tmpProjectLocation = new TransientProjectStorageLocator(

View File

@ -25,6 +25,7 @@ import org.junit.Before;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.framework.Application;
import ghidra.framework.OperatingSystem;
import ghidra.framework.protocol.ghidra.Handler;
@ -120,7 +121,7 @@ public class ProjectLocatorTest extends AbstractGenericTest {
@Test
public void testTempPath() throws MalformedURLException {
String tmpPath = System.getProperty("java.io.tmpdir").replace("\\", "/");
String tmpPath = Application.getUserTempDirectory().getAbsolutePath().replace("\\", "/");
if (!tmpPath.startsWith("/")) {
tmpPath = "/" + tmpPath;
}

View File

@ -215,7 +215,7 @@ public class SleighLanguageVolatilityTest extends AbstractGenericTest {
public ResourceFile createCustomPspecFile(String name, String content) {
File newPspecFile = null;
try {
newPspecFile = File.createTempFile(name, ".pspec");
newPspecFile = Application.createTempFile(name, ".pspec");
BufferedWriter bw = new BufferedWriter(new FileWriter(newPspecFile));
bw.write(content);
bw.close();
@ -239,7 +239,7 @@ public class SleighLanguageVolatilityTest extends AbstractGenericTest {
}
try {
File editedPspecFile = File.createTempFile(name, ".ldefs");
File editedPspecFile = Application.createTempFile(name, ".ldefs");
BufferedReader br = new BufferedReader(new FileReader(originalLdefFile.getFile(false)));
BufferedWriter bw = new BufferedWriter(new FileWriter(editedPspecFile));
String s;

View File

@ -15,7 +15,8 @@
*/
package ghidra;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import generic.jar.ResourceFile;
@ -36,12 +37,10 @@ public class GhidraApplicationLayout extends ApplicationLayout {
/**
* Constructs a new Ghidra application layout object.
*
* @throws FileNotFoundException if there was a problem getting a user
* directory.
* @throws IOException if there was a problem getting the application
* properties or modules.
* @throws IOException if there was a problem getting a user directory or the application
* properties or modules.
*/
public GhidraApplicationLayout() throws FileNotFoundException, IOException {
public GhidraApplicationLayout() throws IOException {
// Application root directories
applicationRootDirs = findGhidraApplicationRootDirs();
@ -53,7 +52,8 @@ public class GhidraApplicationLayout extends ApplicationLayout {
applicationInstallationDir = findGhidraApplicationInstallationDir();
// User directories
userTempDir = ApplicationUtilities.getDefaultUserTempDir(getApplicationProperties());
userTempDir = ApplicationUtilities
.getDefaultUserTempDir(getApplicationProperties().getApplicationName());
userCacheDir = ApplicationUtilities.getDefaultUserCacheDir(getApplicationProperties());
userSettingsDir = ApplicationUtilities.getDefaultUserSettingsDir(getApplicationProperties(),
getApplicationInstallationDir());
@ -77,13 +77,10 @@ public class GhidraApplicationLayout extends ApplicationLayout {
* (like the Eclipse GhidraDevPlugin).
*
* @param applicationInstallationDir The application installation directory.
* @throws FileNotFoundException if there was a problem getting a user
* directory.
* @throws IOException if there was a problem getting the application
* properties.
* @throws IOException if there was a problem getting a user directory or the application
* properties.
*/
public GhidraApplicationLayout(File applicationInstallationDir)
throws FileNotFoundException, IOException {
public GhidraApplicationLayout(File applicationInstallationDir) throws IOException {
// Application installation directory
this.applicationInstallationDir = new ResourceFile(applicationInstallationDir);
@ -96,7 +93,8 @@ public class GhidraApplicationLayout extends ApplicationLayout {
applicationProperties = new ApplicationProperties(applicationRootDirs);
// User directories
userTempDir = ApplicationUtilities.getDefaultUserTempDir(getApplicationProperties());
userTempDir = ApplicationUtilities
.getDefaultUserTempDir(getApplicationProperties().getApplicationName());
userCacheDir = ApplicationUtilities.getDefaultUserCacheDir(getApplicationProperties());
userSettingsDir = ApplicationUtilities.getDefaultUserSettingsDir(getApplicationProperties(),
getApplicationInstallationDir());

View File

@ -140,7 +140,7 @@ public class SystemUtilities {
}
/**
* Gets the boolean value of the system property by the given name. If the property is
* Gets the boolean value of the system property by the given name. If the property is
* not set, the defaultValue is returned. If the value is set, then it will be passed
* into {@link Boolean#parseBoolean(String)}.
*

View File

@ -1189,29 +1189,6 @@ public final class FileUtilities {
return formatter.format((length / 1000000f)) + "MB";
}
/**
* Creates a temporary directory using the given prefix
* @param prefix the prefix
* @return the temp file
*/
public static File createTempDirectory(String prefix) {
try {
File temp = File.createTempFile(prefix, Long.toString(System.currentTimeMillis()));
if (!temp.delete()) {
throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
}
if (!createDir(temp)) {
throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
}
return temp;
}
catch (IOException e) {
Msg.error(FileUtilities.class, "Error creating temporary directory", e);
}
return null;
}
/**
* Sets the given file (or directory) to readable and writable by only the owner.
*

View File

@ -0,0 +1,310 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utility.application;
import static utility.application.ApplicationUtilities.*;
import static utility.application.XdgUtils.*;
import java.io.File;
import java.util.*;
import generic.jar.ResourceFile;
import ghidra.GhidraApplicationLayout;
import ghidra.GhidraLaunchable;
import ghidra.framework.ApplicationProperties;
import ghidra.framework.OperatingSystem;
import ghidra.util.SystemUtilities;
import utilities.util.FileUtilities;
/**
* Interactive utility to discover and delete artifacts that Ghidra lays down on the filesystem
*/
public class AppCleaner implements GhidraLaunchable {
/**
* Launches the {@link AppCleaner}
*
* @param layout The application layout to use for the launch
* @param args One argument is expected: the name of the application to clean. All other
* arguments are ignored.
* @throws Exception if there was a problem with the launch
*/
@Override
public void launch(GhidraApplicationLayout layout, String[] args) throws Exception {
if (args.length != 1) {
System.out.println("Expected 1 argument but got " + args.length);
System.exit(1);
}
String appName = args[0];
System.out.println("\nDiscovering " + appName + " artifact directories....");
// Discover directories
Set<File> discoveredSet = new LinkedHashSet<>();
discoveredSet.addAll(findSettingsDirs(appName, layout));
discoveredSet.addAll(findCacheDirs(appName, layout));
discoveredSet.addAll(findTempDirs(appName, layout));
List<File> discoveredDirs = new ArrayList<>(discoveredSet);
// Exit if we didn't discover any directories
if (discoveredDirs.isEmpty()) {
System.out.println("NONE FOUND");
return;
}
// Output discovered directories and prompt user
File potentialParentDir = null;
for (int i = 0; i < discoveredDirs.size(); i++) {
File d = discoveredDirs.get(i);
File parentDir = d.getParentFile();
boolean indent = parentDir.equals(potentialParentDir);
System.out.println("%2d)%s %s".formatted(i + 1, indent ? " " : "", d));
if (!indent) {
potentialParentDir = d;
}
}
System.out.println("*) All");
System.out.println("0) Exit");
System.out.print("Enter a directory to delete: ");
// Get user choice and delete
String choice = null;
try (Scanner scanner = new Scanner(System.in)){
List<File> failures = new ArrayList<>();
choice = scanner.nextLine().trim();
switch (choice) {
case "0":
System.out.println("Exiting...");
return;
case "*":
for (File dir : discoveredDirs) {
if (dir.isDirectory()) {
if (!FileUtilities.deleteDir(dir)) {
failures.add(dir);
}
}
}
break;
default:
File dir = discoveredDirs.get(Integer.parseInt(choice) - 1);
if (!FileUtilities.deleteDir(dir)) {
failures.add(dir);
}
}
System.out.println(failures.isEmpty() ? "SUCCESS" : "Failed to delete:");
failures.forEach(dir -> System.out.println(" " + dir));
}
catch (NoSuchElementException e) {
// User likely hit ctrl+c to exit
}
catch (NumberFormatException | IndexOutOfBoundsException e) {
System.out.println("Invalid entry: \"" + choice + "\"");
}
}
/**
* Finds user settings directories
*
* @param appName The name of the application
* @param layout The layout
* @return A {@link Set} of discovered user settings directories, ordered such that
* parent directories are directly followed by their subdirectories, if applicable
* @see ApplicationUtilities#getDefaultUserSettingsDir(ApplicationProperties, ResourceFile)
* @see ApplicationUtilities#getLegacyUserSettingsDir(ApplicationProperties, ResourceFile)
*/
private Set<File> findSettingsDirs(String appName, ApplicationLayout layout) {
Set<File> discoveredDirs = new LinkedHashSet<>();
appName = appName.toLowerCase();
String userNameAndAppName = SystemUtilities.getUserName() + "-" + appName;
// Legacy default settings directory
getDirFromProperty("user.home", "." + appName).ifPresent(dir -> {
discoveredDirs.add(dir);
discoveredDirs.addAll(getSubdirs(dir));
});
// Current default settings directory
File settingsDir = layout.getUserSettingsDir();
File settingsParentDir = settingsDir.getParentFile();
if (settingsParentDir != null && (settingsParentDir.getName().equals(appName) ||
settingsParentDir.getName().equals(userNameAndAppName))) {
discoveredDirs.add(settingsParentDir);
discoveredDirs.addAll(getSubdirs(settingsParentDir));
}
// Application system property override (likely not set for AppCleaner)
getDirFromProperty(PROPERTY_SETTINGS_DIR, appName).ifPresent(dir -> {
discoveredDirs.add(dir);
discoveredDirs.addAll(getSubdirs(dir));
});
getDirFromProperty(PROPERTY_SETTINGS_DIR, userNameAndAppName).ifPresent(dir -> {
discoveredDirs.add(dir);
discoveredDirs.addAll(getSubdirs(dir));
});
// XDG environment variable override
getDirFromEnv(XDG_CONFIG_HOME, appName).ifPresent(dir -> {
discoveredDirs.add(dir);
discoveredDirs.addAll(getSubdirs(dir));
});
getDirFromEnv(XDG_CONFIG_HOME, userNameAndAppName).ifPresent(dir -> {
discoveredDirs.add(dir);
discoveredDirs.addAll(getSubdirs(dir));
});
return discoveredDirs;
}
/**
* Finds user cache directories
*
* @param appName The name of the application
* @param layout The layout
* @return A {@link Set} of discovered user cache directories, ordered such that
* parent directories are directly followed by their subdirectories, if applicable
* @see ApplicationUtilities#getDefaultUserCacheDir(ApplicationProperties)
*/
private Set<File> findCacheDirs(String appName, ApplicationLayout layout) {
Set<File> discoveredDirs = new LinkedHashSet<>();
// Legacy cache directories
if (OperatingSystem.CURRENT_OPERATING_SYSTEM.equals(OperatingSystem.WINDOWS)) {
getDirFromEnv("LOCALAPPDATA", appName).ifPresent(discoveredDirs::add);
}
else {
String legacyName = SystemUtilities.getUserName() + "-" + appName;
getDirFromProperty("java.io.tmpdir", legacyName).ifPresent(discoveredDirs::add);
}
// Newer cache directories always use a lowercase application name
appName = appName.toLowerCase();
String userNameAndAppName = SystemUtilities.getUserName() + "-" + appName;
// Current cache directories
File cacheDir = layout.getUserCacheDir();
if (cacheDir != null && cacheDir.isDirectory()) {
discoveredDirs.add(cacheDir);
}
// Application system property override (likely not set for AppCleaner)
getDirFromProperty(PROPERTY_CACHE_DIR, appName).ifPresent(discoveredDirs::add);
getDirFromProperty(PROPERTY_CACHE_DIR, userNameAndAppName).ifPresent(discoveredDirs::add);
// XDG environment variable override
getDirFromEnv(XDG_CACHE_HOME, appName).ifPresent(discoveredDirs::add);
getDirFromEnv(XDG_CACHE_HOME, userNameAndAppName).ifPresent(discoveredDirs::add);
return discoveredDirs;
}
/**
* Finds user temp directories
*
* @param appName The name of the application
* @param layout The layout
* @return A {@link Set} of discovered user temp directories, ordered such that
* parent directories are directly followed by their subdirectories, if applicable
* @see ApplicationUtilities#getDefaultUserTempDir(String)
*/
private Set<File> findTempDirs(String appName, ApplicationLayout layout) {
Set<File> discoveredDirs = new LinkedHashSet<>();
// Legacy temp directories
String legacyName = SystemUtilities.getUserName() + "-" + appName;
if (OperatingSystem.CURRENT_OPERATING_SYSTEM.equals(OperatingSystem.WINDOWS)) {
getDirFromEnv("TEMP", legacyName).ifPresent(discoveredDirs::add);
}
else {
getDirFromProperty("java.io.tmpdir", legacyName).ifPresent(discoveredDirs::add);
}
// Newer temp directories always use a lowercase application name
appName = appName.toLowerCase();
String userNameAndAppName = SystemUtilities.getUserName() + "-" + appName;
// Current temp directories
File tempDir = layout.getUserTempDir();
if (tempDir != null && tempDir.isDirectory()) {
discoveredDirs.add(tempDir);
}
// Application system property override (likely not set for AppCleaner)
getDirFromProperty(PROPERTY_TEMP_DIR, appName).ifPresent(discoveredDirs::add);
getDirFromProperty(PROPERTY_TEMP_DIR, userNameAndAppName).ifPresent(discoveredDirs::add);
// XDG environment variable override
getDirFromEnv(XDG_RUNTIME_DIR, appName).ifPresent(discoveredDirs::add);
getDirFromEnv(XDG_RUNTIME_DIR, userNameAndAppName).ifPresent(discoveredDirs::add);
return discoveredDirs;
}
/**
* Gets the subdirectory of the given name found within the directory specified by the given
* system property
*
* @param propertyName The name of the system property
* @param subdirName The name of the subdirectory within the directory specified by the given
* system property
* @return The subdirectory of the given name found within the directory specified by the given
* systemProperty
*/
private Optional<File> getDirFromProperty(String propertyName, String subdirName) {
String path = System.getProperty(propertyName, "").trim();
if (!path.isEmpty()) {
File dir = new File(path, subdirName);
if (dir.isDirectory()) {
return Optional.of(dir);
}
}
return Optional.empty();
}
/**
* Gets the subdirectory of the given name found within the directory specified by the given
* environment variable
*
* @param envName The name of the environment variable
* @param subdirName The name of the subdirectory within the directory specified by the given
* environment variable
* @return The subdirectory of the given name found within the directory specified by the given
* environment variable
*/
private Optional<File> getDirFromEnv(String envName, String subdirName) {
String path = System.getenv(envName);
if (path != null && !path.isBlank()) {
File dir = new File(path, subdirName);
if (dir.isDirectory()) {
return Optional.of(dir);
}
}
return Optional.empty();
}
/**
* Gets the direct sub-directories of the given directory (non-recursive)
*
* @param dir The directory to get the sub-directories of
* @return The direct sub-directories of the given directory
*/
private List<File> getSubdirs(File dir) {
File[] listing = dir.listFiles(File::isDirectory);
return listing != null ? Arrays.asList(listing) : List.of();
}
}

View File

@ -15,11 +15,11 @@
*/
package utility.application;
import ghidra.framework.PluggableServiceRegistry;
import java.io.File;
import java.io.IOException;
import utilities.util.FileUtilities;
import ghidra.framework.PluggableServiceRegistry;
import ghidra.util.Msg;
public class ApplicationSettings {
static {
@ -46,6 +46,13 @@ public class ApplicationSettings {
* application version.
*/
protected File doGetUserApplicationSettingsDirectory() {
return FileUtilities.createTempDirectory("application.settings_");
try {
return ApplicationUtilities.getDefaultUserTempDir("application.settings");
}
catch (IOException e) {
Msg.error(ApplicationSettings.class, "Error creating application.settings directory",
e);
return null;
}
}
}

View File

@ -23,12 +23,28 @@ import generic.jar.ResourceFile;
import ghidra.framework.*;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import utilities.util.FileUtilities;
/**
* Utility class for default application things.
*/
public class ApplicationUtilities {
/**
* Name of system property used to override the location of the user temporary directory
*/
public static final String PROPERTY_TEMP_DIR = "application.tempdir";
/**
* Name of system property used to override the location of the user cache directory
*/
public static final String PROPERTY_CACHE_DIR = "application.cachedir";
/**
* Name of system property used to override the location of the user settings directory
*/
public static final String PROPERTY_SETTINGS_DIR = "application.settingsdir";
/**
* Searches for default application root directories.
*
@ -137,85 +153,145 @@ public class ApplicationUtilities {
}
/**
* Gets the default application's user temp directory.
* Gets the application's default user temp directory.
* <p>
* NOTE: This method does not create the directory.
*
* @param applicationProperties The application properties.
* @return The default application's user temp directory.
* @throws FileNotFoundException if the user temp directory could not be determined.
* @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.
*/
public static File getDefaultUserTempDir(ApplicationProperties applicationProperties)
throws FileNotFoundException {
String tmpdir = System.getProperty("java.io.tmpdir");
if (tmpdir == null || tmpdir.isEmpty()) {
throw new FileNotFoundException("System property \"java.io.tmpdir\" is not set!");
public static File getDefaultUserTempDir(String applicationName) throws FileNotFoundException {
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 new File(tmpdir,
SystemUtilities.getUserName() + "-" + applicationProperties.getApplicationName());
// Look for XDG environment variable
File xdgRuntimeDir = getEnvFile(XdgUtils.XDG_RUNTIME_DIR, false);
if (xdgRuntimeDir != null) {
return new File(xdgRuntimeDir, getUserSpecificDirName(xdgRuntimeDir, appName));
}
File javaTmpDir = getJavaTmpDir();
return new File(javaTmpDir, getUserSpecificDirName(javaTmpDir, appName));
}
/**
* Gets the default application's user cache directory.
* Gets the application's default user cache directory.
* <p>
* NOTE: This method does not create the directory.
*
* @param applicationProperties The application properties.
* @return The default application's user cache directory.
* @throws FileNotFoundException if the user cache directory could not be determined.
* @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.
*/
public static File getDefaultUserCacheDir(ApplicationProperties applicationProperties)
throws FileNotFoundException {
// Look for preset cache directory
String cachedir = System.getProperty("application.cachedir", "").trim();
if (!cachedir.isEmpty()) {
return new File(cachedir,
SystemUtilities.getUserName() + "-" + applicationProperties.getApplicationName());
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));
}
// Handle Windows specially
if (OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS) {
File localAppDataDir = null;
String localAppDataDirPath = System.getenv("LOCALAPPDATA"); // e.g., /Users/myname/AppData/Local
if (localAppDataDirPath != null && !localAppDataDirPath.isEmpty()) {
localAppDataDir = new File(localAppDataDirPath);
}
else {
String userHome = System.getProperty("user.home");
if (userHome != null) {
localAppDataDir = new File(userHome, "AppData\\Local");
if (!localAppDataDir.isDirectory()) {
localAppDataDir = new File(userHome, "Local Settings");
}
}
}
if (localAppDataDir != null && localAppDataDir.isDirectory()) {
return new File(localAppDataDir, applicationProperties.getApplicationName());
}
// Look for XDG environment variable
File xdgCacheHomeDir = getEnvFile(XdgUtils.XDG_CACHE_HOME, false);
if (xdgCacheHomeDir != null) {
return new File(xdgCacheHomeDir, getUserSpecificDirName(xdgCacheHomeDir, appName));
}
// Use user temp directory if platform specific scheme does not exist above or it failed
return getDefaultUserTempDir(applicationProperties);
// 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.");
};
}
/**
* Gets the default application's user settings directory.
* Gets the application's default user settings directory.
* <p>
* NOTE: This method does not create the directory.
*
* @param applicationProperties The application properties.
* @param installationDirectory The application installation directory.
* @return The application's user settings directory.
* @throws FileNotFoundException if the user settings directory could not be determined.
* @return The application's default user settings directory. The returned {@link File} will
* represent an absolute path.
* @throws FileNotFoundException if the absolute path of the user settings directory could not
* be determined.
*/
public static File getDefaultUserSettingsDir(ApplicationProperties applicationProperties,
ResourceFile installationDirectory) throws FileNotFoundException {
String homedir = System.getProperty("user.home");
if (homedir == null || homedir.isEmpty()) {
throw new FileNotFoundException("System property \"user.home\" is not set!");
String appName = applicationProperties.getApplicationName().toLowerCase();
ApplicationIdentifier applicationIdentifier =
new ApplicationIdentifier(applicationProperties);
String versionedName = applicationIdentifier.toString();
if (SystemUtilities.isInDevelopmentMode()) {
// Add the application's installation directory name to this variable, so that each
// branch's project user directory is unique.
versionedName += "_location_" + installationDirectory.getName();
}
// Look for Ghidra-specific system property
File settingsOverrideDir = getSystemPropertyFile(PROPERTY_SETTINGS_DIR, false);
if (settingsOverrideDir != null) {
return 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);
}
File userHomeDir = getJavaUserHomeDir();
String versionedSubdir = appName + "/" + versionedName;
return 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.");
};
}
/**
* Gets the application's legacy (pre-Ghida 11.1) user settings directory.
* <p>
* NOTE: This method does not create the directory.
*
* @param applicationProperties The application properties.
* @param installationDirectory The application installation directory.
* @return The application's legacy user settings directory. The returned {@link File} will
* represent an absolute path.
* @throws FileNotFoundException if the absolute path of the legacy user settings directory
* could not be determined.
*/
public static File getLegacyUserSettingsDir(ApplicationProperties applicationProperties,
ResourceFile installationDirectory) throws FileNotFoundException {
ApplicationIdentifier applicationIdentifier =
new ApplicationIdentifier(applicationProperties);
File userSettingsParentDir =
new File(homedir, "." + applicationIdentifier.getApplicationName());
new File(getJavaUserHomeDir(), "." + applicationIdentifier.getApplicationName());
String userSettingsDirName = "." + applicationIdentifier;
@ -227,4 +303,108 @@ public class ApplicationUtilities {
return new File(userSettingsParentDir, userSettingsDirName);
}
/**
* Gets Java's temporary directory in absolute form
*
* @return Java's temporary directory in absolute form
* @throws FileNotFoundException if Java's temporary directory is not defined or it is not an
* absolute path
*/
private static File getJavaTmpDir() throws FileNotFoundException {
return getSystemPropertyFile("java.io.tmpdir", true);
}
/**
* Gets Java's user home directory in absolute form
*
* @return Java's user home directory in absolute form
* @throws FileNotFoundException if Java's user home directory is not defined or it is not an
* absolute path
*/
private static File getJavaUserHomeDir() throws FileNotFoundException {
return getSystemPropertyFile("user.home", true);
}
/**
* Gets the absolute form {@link File} value of the system property by the given name
*
* @param name The system property name
* @param required True if given system property is required to be set; otherwise, false
* @return The absolute form {@link File} value of the system property by the given name, or
* null if it isn't set
* @throws FileNotFoundException if the property value was not an absolute path, or if it is
* required and not set
*/
private static File getSystemPropertyFile(String name, boolean required)
throws FileNotFoundException {
String path = System.getProperty(name);
if (path == null || path.isBlank()) {
if (required) {
throw new FileNotFoundException(
"Required system property \"%s\" is not set!".formatted(name));
}
return null;
}
path = path.trim();
File file = new File(path);
if (!file.isAbsolute()) {
throw new FileNotFoundException(
"System property \"%s\" is not an absolute path: \"%s\"".formatted(name, path));
}
return file;
}
/**
* Gets the absolute form {@link File} value of the environment variable by the given name
*
* @param name The environment variable name
* @param required True if the given environment variable is required to be set; otherwise,
* false
* @return The absolute form {@link File} value of the environment variable by the given name,
* or null if it isn't set
* @throws FileNotFoundException if the property value was not an absolute path, or if it is
* required and not set
*/
private static File getEnvFile(String name, boolean required) throws FileNotFoundException {
String path = System.getenv(name);
if (path == null || path.isBlank()) {
if (required) {
throw new FileNotFoundException(
"Required environment variable \"%s\" is not set!".formatted(name));
}
return null;
}
path = path.trim();
File file = new File(path);
if (!file.isAbsolute()) {
throw new FileNotFoundException(
"Environment variable \"%s\" is not an absolute path: \"%s\"".formatted(name,
path));
}
return file;
}
/**
* Gets a directory name that can be used to create a user-specific sub-directory in
* {@code parentDir}. If the {@code parentDir} is contained within the user's home directory,
* the given {@code appName} can simply be used since it will live in a user-specific location.
* Otherwise, the user's name will get prepended to the {@code appName} so it does not collide
* with other users' directories in the shared directory space.
* @param parentDir The parent directory where we'd like to create a user-specific sub-directory
* @param appName The application name
* @return A directory name that can be used to create a user-specific sub-directory in
* {@code parentDir}.
* @throws FileNotFoundException if Java's user home directory is not defined or it is not an
* absolute path
*/
private static String getUserSpecificDirName(File parentDir, String appName)
throws FileNotFoundException {
String userSpecificDirName = appName;
if (!FileUtilities.isPathContainedWithin(getJavaUserHomeDir(), parentDir)) {
userSpecificDirName = SystemUtilities.getUserName() + "-" + appName;
}
return userSpecificDirName;
}
}

View File

@ -15,7 +15,7 @@
*/
package utility.application;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@ -32,9 +32,9 @@ public class DummyApplicationLayout extends ApplicationLayout {
/**
* Constructs a new dummy application layout object.
* @param name the application name
* @throws FileNotFoundException if there was a problem getting a user directory.
* @throws IOException if there was a problem getting a user directory.
*/
public DummyApplicationLayout(String name) throws FileNotFoundException {
public DummyApplicationLayout(String name) throws IOException {
// Application properties
applicationProperties = new ApplicationProperties(name);
@ -48,7 +48,8 @@ public class DummyApplicationLayout extends ApplicationLayout {
applicationRootDirs.add(cwd);
// User directories
userTempDir = ApplicationUtilities.getDefaultUserTempDir(applicationProperties);
userTempDir =
ApplicationUtilities.getDefaultUserTempDir(applicationProperties.getApplicationName());
extensionInstallationDirs = Collections.emptyList();
}

View File

@ -0,0 +1,76 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utility.application;
/**
* Class to support the "XDG Base Directory Specification"
* <p>
* Based off version 0.8
*
* @see <a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-0.8.html">basedir-spec-0.8.html</a>
*/
public class XdgUtils {
/**
* $XDG_DATA_HOME defines the base directory relative to which user-specific data files should
* be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to
* $HOME/.local/share should be used.
*/
public static final String XDG_DATA_HOME = "XDG_DATA_HOME";
/**
* $XDG_CONFIG_HOME defines the base directory relative to which user-specific configuration
* files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to
* $HOME/.config should be used.
*/
public static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
/**
* $XDG_STATE_HOME defines the base directory relative to which user-specific state files should
* be stored. If $XDG_STATE_HOME is either not set or empty, a default equal to
* $HOME/.local/state should be used.
*/
public static final String XDG_STATE_HOME = "XDG_STATE_HOME";
/**
* $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data
* files in addition to the $XDG_DATA_HOME base directory. The directories in $XDG_DATA_DIRS
* should be separated with a colon ':'.
*/
public static final String XDG_DATA_DIRS = "XDG_DATA_DIRS";
/**
* $XDG_CONFIG_DIRS defines the preference-ordered set of base directories to search for
* configuration files in addition to the $XDG_CONFIG_HOME base directory. The directories in
* $XDG_CONFIG_DIRS should be separated with a colon ':'.
*/
public static final String XDG_CONFIG_DIRS = "XDG_CONFIG_DIRS";
/**
* $XDG_CACHE_HOME defines the base directory relative to which user-specific non-essential
* data files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal
* to $HOME/.cache should be used.
*/
public static final String XDG_CACHE_HOME = "XDG_CACHE_HOME";
/**
* $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential
* runtime files and other file objects (such as sockets, named pipes, ...) should be stored.
* The directory MUST be owned by the user, and he MUST be the only one having read and write
* access to it. Its Unix access mode MUST be 0700.
*/
public static final String XDG_RUNTIME_DIR = "XDG_RUNTIME_DIR";
}

View File

@ -83,17 +83,47 @@ VMARGS_WINDOWS=-Dlog4j.skipJansi=true
# Ghidra does not use class data sharing, so explicitly turn it off to avoid the warning.
VMARGS=-Xshare:off
# Persistent cache directory used by the application. This directory will be used to store
# persistent application caches for all users. The default location for Mac/Linux is the same as
# specified by java.io.tmpdir property. The default location for Windows corresponds to the
# application local settings directory for the user (e.g., %LOCALAPPDATA%). If you wish to use a
# directory with more storage or avoid system cleanups, it may be desirable to override the default
# location.
# Settings directory used by the application to store application settings and data that persist
# between application sessions, system reboots, and periodic system cleanup. Overridden values
# are required to be absolute paths. The current user name may be incorporated into the settings
# directory's name if the settings directory lives outside of the user's home directory. The
# settings directory will be selected based on the following rules, in order of precedence:
# 1. System.getProperty("application.settingsdir")/[user-]<application>/<application>_<version>
# 2. System.getenv("XDG_CONFIG_HOME")/[user-]<application>/<application>_<version>
# 3. A platform specific default location:
# - Windows: %APPDATA%\<application>\<application>_<version>
# - Linux: $HOME/.config/<application>/<application>_<version>
# - macOS: $HOME/Library/<application>/<application>_<version>
#VMARGS=-Dapplication.settingsdir=
# Cache directory used by the application to store cached application data that ideally will persist
# between application sessions and system reboots, but is not required to do so. Files stored in
# the cache directory may be numerous and/or large. Overridden values are required to be absolute
# paths. The current user name may be incorporated into the cache directory's name if the cache
# directory lives outside of the user's home directory. The cache directory will be selected based
# on the following rules, in order of precedence
# 1. System.getProperty("application.cachedir")/[user-]<application>
# 2. System.getenv("XDG_CACHE_HOME")/[user-]<application>
# 3. A platform specific default location:
# - Windows: %LOCALAPPDATA%\<application>
# - Linux: /var/tmp/<user>-<application>
# - macOS: /var/tmp/<user>-<application>
#VMARGS=-Dapplication.cachedir=
# Temporary directory used by the application. This directory will be used for all temporary files
# and may also be used for the persistent user cache directory <java.io.tmpdir>/<username>-Ghidra.
# The specified directory must exist and have appropriate read/write/execute permissions
# Temporary directory used by the application to store short-lived files that are not required to
# persist between application sessions. Overridden values are required to be absolute paths. The
# current user name may be incorporated into the temporary directory's name if the temporary
# directory lives outside of the user's home directory. The temporary directory will be selected
# based on the following rules, in order of precedence:
# 1. System.getProperty("application.tempdir")/[user-]<application>
# 2. System.getenv("XDG_RUNTIME_DIR")/[user-]<application>
# 3. System.getProperty("java.io.tmpdir")/[user-]<application>
# Unless overridden below, the "java.io.tmpdir" system property typically defaults to the following
# platform specific locations:
# - Windows: %TEMP%
# - Linux: /tmp
# - macOS: $TMPDIR
#VMARGS=-Dapplication.tempdir=
#VMARGS=-Djava.io.tmpdir=
# Disable alternating row colors in tables

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
#---------------------------------------------------------------------------------------------------
# Ghidra-Clean
# An interactive utility to discover and delete artifacts that Ghidra lays down on the filesystem.
#---------------------------------------------------------------------------------------------------
# Maximum heap memory may be changed if default is inadequate. This will generally be up to 1/4 of
# the physical memory available to the OS. Uncomment MAXMEM setting if non-default value is needed.
#MAXMEM=1G
VMARG_LIST="-Djava.awt.headless=true "
# Resolve symbolic link if present and get the directory this script lives in.
# NOTE: "readlink -f" is best but works on Linux only, "readlink" will only work if your PWD
# contains the link you are calling (which is the best we can do on macOS), and the "echo" is the
# fallback, which doesn't attempt to do anything with links.
SCRIPT_FILE="$(readlink -f "$0" 2>/dev/null || readlink "$0" 2>/dev/null || echo "$0")"
SCRIPT_DIR="${SCRIPT_FILE%/*}"
"${SCRIPT_DIR}"/launch.sh fg jre Ghidra-Clean "$MAXMEM" "$VMARG_LIST" utility.application.AppCleaner Ghidra

View File

@ -0,0 +1,9 @@
:: Ghidra-Clean
:: An interactive utility to discover and delete artifacts that Ghidra lays down on the filesystem.
@echo off
setlocal
set VMARG_LIST=-Djava.awt.headless=true
call "%~dp0launch.bat" fg jdk Ghidra-Clean "" "" utility.application.AppCleaner Ghidra

View File

@ -21,6 +21,7 @@ Linux/support/buildGhidraJar||GHIDRA||||END|
Linux/support/buildNatives||GHIDRA||||END|
Linux/support/convertStorage||GHIDRA||||END|
Linux/support/gdbGADPServerRun||GHIDRA||||END|
Linux/support/ghidraClean||GHIDRA||||END|
Linux/support/ghidraDebug||GHIDRA||||END|
Linux/support/pythonRun||GHIDRA||||END|
Linux/support/sleigh||GHIDRA||||END|
@ -40,6 +41,7 @@ Windows/support/createPdbXmlFiles.bat||GHIDRA||||END|
Windows/support/dbgengGADPServerRun.bat||GHIDRA||||END|
Windows/support/dbgmodelGADPServerRun.bat||GHIDRA||||END|
Windows/support/ghidra.ico||GHIDRA||||END|
Windows/support/ghidraClean.bat||GHIDRA||||END|
Windows/support/ghidraDebug.bat||GHIDRA||||END|
Windows/support/launch.bat||GHIDRA||||END|
Windows/support/pythonRun.bat||GHIDRA||||END|

View File

@ -35,6 +35,7 @@ import docking.wizard.WizardManager;
import docking.wizard.WizardPanel;
import generic.theme.GThemeDefaults.Colors;
import ghidra.app.plugin.core.archive.RestoreDialog;
import ghidra.framework.Application;
import ghidra.framework.data.DefaultProjectData;
import ghidra.framework.data.GhidraFileData;
import ghidra.framework.main.*;
@ -56,7 +57,6 @@ import resources.MultiIcon;
public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator {
private static final String OTHER_PROJECT = "Other_Project";
private final static String TEMP_DIR = System.getProperty("java.io.tmpdir");
Icon icon = (Icon) getInstanceField("CONVERT_ICON", ProjectInfoDialog.class);
public FrontEndPluginScreenShots() {
@ -659,6 +659,7 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator {
@Test
public void testViewOtherProjects()
throws IOException, LockException, InvalidNameException, CancelledException {
String TEMP_DIR = Application.getUserTempDirectory().getAbsolutePath();
Project project = env.getProject();
program = env.getProgram("WinHelloCPP.exe");
@ -692,6 +693,7 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator {
@Test
public void testLinkOtherProject()
throws IOException, LockException, InvalidNameException, CancelledException {
String TEMP_DIR = Application.getUserTempDirectory().getAbsolutePath();
Project project = env.getProject();
program = env.getProgram("WinHelloCPP.exe");

View File

@ -30,6 +30,7 @@ import javax.rmi.ssl.SslRMIClientSocketFactory;
import org.apache.commons.lang3.RandomStringUtils;
import generic.test.*;
import ghidra.framework.Application;
import ghidra.framework.client.*;
import ghidra.framework.data.ContentHandler;
import ghidra.framework.data.DomainObjectAdapter;
@ -347,7 +348,7 @@ public class ServerTestUtil {
private static synchronized File getPkiTestDirectory() {
if (testPkiDirectory == null) {
testPkiDirectory = new File(System.getProperty("java.io.tmpdir"), "test-pki");
testPkiDirectory = new File(Application.getUserTempDirectory(), "test-pki");
FileUtilities.deleteDir(testPkiDirectory);
testPkiDirectory.mkdirs();

View File

@ -359,6 +359,23 @@ public class JavaConfig {
*/
private void initJavaHomeSaveFile(File installDir) throws FileNotFoundException {
boolean isDev = new File(installDir, "build.gradle").isFile();
String appName = applicationName.toLowerCase();
String userSettingsDirName = appName + "_" + applicationVersion + "_" +
applicationReleaseName.replaceAll("\\s", "").toUpperCase();
if (isDev) {
userSettingsDirName += "_location_" + installDir.getParentFile().getName();
}
File userSettingsDir = null;
// Look for XDG environment variable
String xdgConfigHomeDirStr = System.getenv("XDG_CONFIG_HOME");
if (xdgConfigHomeDirStr != null && !xdgConfigHomeDirStr.isEmpty()) {
userSettingsDir = new File(xdgConfigHomeDirStr, appName + "/" + userSettingsDirName);
javaHomeSaveFile = new File(userSettingsDir, JAVA_HOME_SAVE_NAME);
return;
}
// Ensure there is a user home directory (there definitely should be)
String userHomeDirPath = System.getProperty("user.home");
@ -370,18 +387,27 @@ public class JavaConfig {
throw new FileNotFoundException("User home directory does not exist: " + userHomeDir);
}
// Get the java home save file from user home directory (it might not exist yet).
File userSettingsParentDir =
new File(userHomeDir, "." + applicationName.replaceAll("\\s", "").toLowerCase());
String userSettingsDirName = userSettingsParentDir.getName() + "_" + applicationVersion +
"_" + applicationReleaseName.replaceAll("\\s", "").toUpperCase();
if (isDev) {
userSettingsDirName += "_location_" + installDir.getParentFile().getName();
switch (JavaFinder.getCurrentPlatform()) {
case WINDOWS:
String localAppDataDirPath = System.getenv("APPDATA");
if (localAppDataDirPath == null || localAppDataDirPath.trim().isEmpty()) {
throw new FileNotFoundException("\"APPDATA\" environment variable is not set");
}
userSettingsDir =
new File(localAppDataDirPath, appName + "/" + userSettingsDirName);
break;
case LINUX:
userSettingsDir =
new File(userHomeDir, ".config/" + appName + "/" + userSettingsDirName);
break;
case MACOS:
userSettingsDir =
new File(userHomeDir, "Library/" + appName + "/" + userSettingsDirName);
break;
default:
throw new FileNotFoundException(
"Failed to find the user settings directory: Unsupported operating system.");
}
File userSettingsDir = new File(userSettingsParentDir, userSettingsDirName);
javaHomeSaveFile = new File(userSettingsDir, JAVA_HOME_SAVE_NAME);
}