mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
Candidate release of source code.
This commit is contained in:
parent
db81e6b3b0
commit
79d8f164f8
12449 changed files with 2800756 additions and 16 deletions
|
@ -0,0 +1,171 @@
|
|||
/* ###
|
||||
* 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 java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
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
|
||||
* directory structure. Create a subclass to define a custom layout.
|
||||
* <p>
|
||||
* If a layout changes in a significant way, the
|
||||
* {@link ApplicationProperties#APPLICATION_LAYOUT_VERSION_PROPERTY} should be incremented so
|
||||
* external things like Eclipse GhidraDev know to look in different places for things.
|
||||
*/
|
||||
public abstract class ApplicationLayout {
|
||||
|
||||
protected ApplicationProperties applicationProperties;
|
||||
protected Collection<ResourceFile> applicationRootDirs;
|
||||
protected ResourceFile applicationInstallationDir;
|
||||
protected Map<String, GModule> modules;
|
||||
protected File userTempDir;
|
||||
protected File userCacheDir;
|
||||
protected File userSettingsDir;
|
||||
protected ResourceFile extensionArchiveDir;
|
||||
protected ResourceFile extensionInstallationDir;
|
||||
|
||||
/**
|
||||
* Gets the application properties from the application layout
|
||||
*
|
||||
* @return The application properties. Should never be null.
|
||||
*/
|
||||
public final ApplicationProperties getApplicationProperties() {
|
||||
return applicationProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application root directories from the application layout.
|
||||
*
|
||||
* @return A collection of application root directories (or null if not set).
|
||||
*/
|
||||
public final Collection<ResourceFile> getApplicationRootDirs() {
|
||||
return applicationRootDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application installation directory from the application layout.
|
||||
*
|
||||
* @return The application installation directory (or null if not set).
|
||||
*/
|
||||
public final ResourceFile getApplicationInstallationDir() {
|
||||
return applicationInstallationDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application's modules from the application layout.
|
||||
*
|
||||
* @return The application's modules as a map (mapping module name to module for convenience).
|
||||
*/
|
||||
public final Map<String, GModule> getModules() {
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user temp directory from the application layout.
|
||||
*
|
||||
* @return The user temp directory (or null if not set).
|
||||
*/
|
||||
public final File getUserTempDir() {
|
||||
return userTempDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user cache directory from the application layout.
|
||||
*
|
||||
* @return The user cache directory (or null if not set).
|
||||
*/
|
||||
public final File getUserCacheDir() {
|
||||
return userCacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user settings directory from the application layout.
|
||||
*
|
||||
* @return The user settings directory (or null if not set).
|
||||
*/
|
||||
public final File getUserSettingsDir() {
|
||||
return userSettingsDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory where archived Ghidra Extensions are stored.
|
||||
*
|
||||
* @return The Ghidra Extensions archive directory. Could be null if the
|
||||
* {@link ApplicationLayout} does not support Ghidra Extensions.
|
||||
*
|
||||
*/
|
||||
public final ResourceFile getExtensionArchiveDir() {
|
||||
return extensionArchiveDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Ghidra Extensions installation folder.
|
||||
*
|
||||
* @return The Ghidra Extensions installation directory. Could be null if the
|
||||
* {@link ApplicationLayout} does not support Ghidra Extensions.
|
||||
*/
|
||||
public final ResourceFile getExtensionInstallationDir() {
|
||||
return extensionInstallationDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* single jar mode.
|
||||
*
|
||||
* @return true if the application is using a "single jar" layout; otherwise, false.
|
||||
*/
|
||||
public boolean inSingleJarMode() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* ###
|
||||
* 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 ghidra.framework.PluggableServiceRegistry;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
public class ApplicationSettings {
|
||||
static {
|
||||
PluggableServiceRegistry.registerPluggableService(ApplicationSettings.class,
|
||||
new ApplicationSettings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory into which application settings are stored per user, per
|
||||
* application version.
|
||||
* @return the directory into which application settings are stored per user, per
|
||||
* application version.
|
||||
*/
|
||||
public static File getUserApplicationSettingsDirectory() {
|
||||
ApplicationSettings impl =
|
||||
PluggableServiceRegistry.getPluggableService(ApplicationSettings.class);
|
||||
return impl.doGetUserApplicationSettingsDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Aha! This is where any potential subclasses can update the returned value.
|
||||
*
|
||||
* @return the directory into which application settings are stored per user, per
|
||||
* application version.
|
||||
*/
|
||||
protected File doGetUserApplicationSettingsDirectory() {
|
||||
return FileUtilities.createTempDirectory("application.settings_");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
/* ###
|
||||
* 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 java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.framework.ApplicationProperties;
|
||||
import ghidra.framework.OperatingSystem;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
/**
|
||||
* Utility class for default application things.
|
||||
*/
|
||||
public class ApplicationUtilities {
|
||||
|
||||
/**
|
||||
* Searches for default application root directories.
|
||||
*
|
||||
* @return A collection of discovered application root directories (could be empty).
|
||||
*/
|
||||
public static Collection<ResourceFile> findDefaultApplicationRootDirs() {
|
||||
Collection<ResourceFile> applicationRootDirs = new ArrayList<>();
|
||||
ResourceFile applicationRootDir = findPrimaryApplicationRootDir();
|
||||
if (applicationRootDir != null) {
|
||||
applicationRootDirs.add(applicationRootDir);
|
||||
if (SystemUtilities.isInTestingMode() || SystemUtilities.isInDevelopmentMode()) {
|
||||
applicationRootDirs.addAll(
|
||||
findApplicationRootDirsFromRepoConfig(applicationRootDir));
|
||||
}
|
||||
}
|
||||
return applicationRootDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the primary application root directory from the classpath. The primary application
|
||||
* root directory must contain an application.properties file. No other application root
|
||||
* directories may contain an application.properties file.
|
||||
*
|
||||
* @return The primary application root directory, or null if it could not be found.
|
||||
*/
|
||||
private static ResourceFile findPrimaryApplicationRootDir() {
|
||||
String[] classpath = System.getProperty("java.class.path").split(File.pathSeparator);
|
||||
for (String pathEntry : classpath) {
|
||||
try {
|
||||
ResourceFile pathFile = new ResourceFile(new File(pathEntry).getCanonicalPath());
|
||||
while (pathFile != null && pathFile.exists()) {
|
||||
if (new ResourceFile(pathFile, ApplicationProperties.PROPERTY_FILE).exists()) {
|
||||
return pathFile;
|
||||
}
|
||||
pathFile = pathFile.getParentFile();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(ApplicationUtilities.class, "Invalid class path entry: " + pathEntry,
|
||||
e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all application root directories defined in the repository config file.
|
||||
*
|
||||
* @param primaryApplicationRootDir The primary application root directory that may contain the
|
||||
* repository config file one directory up.
|
||||
* @return A collection of defined application repository root directories.
|
||||
*/
|
||||
private static Collection<ResourceFile> findApplicationRootDirsFromRepoConfig(
|
||||
ResourceFile primaryApplicationRootDir) {
|
||||
Collection<ResourceFile> repoApplicationRootDirs = new ArrayList<>();
|
||||
ResourceFile repoConfigFile =
|
||||
new ResourceFile(primaryApplicationRootDir.getParentFile(), "ghidra.repos.config");
|
||||
if (repoConfigFile.isFile()) {
|
||||
try (BufferedReader reader =
|
||||
new BufferedReader(new FileReader(repoConfigFile.getFile(false)))) {
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty() || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
ResourceFile potentialApplicationRootDir =
|
||||
new ResourceFile(repoConfigFile.getParentFile().getParentFile(),
|
||||
line + File.separator + "Ghidra");
|
||||
if (potentialApplicationRootDir.isDirectory()) {
|
||||
repoApplicationRootDirs.add(potentialApplicationRootDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(ApplicationUtilities.class, "Failed to read: " + repoConfigFile);
|
||||
}
|
||||
}
|
||||
return repoApplicationRootDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default application's user temp 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.
|
||||
*/
|
||||
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!");
|
||||
}
|
||||
return new File(tmpdir,
|
||||
SystemUtilities.getUserName() + "-" + applicationProperties.getApplicationName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default application's user cache 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.
|
||||
*/
|
||||
public static File getDefaultUserCacheDir(ApplicationProperties applicationProperties)
|
||||
throws FileNotFoundException {
|
||||
|
||||
// Look for preset cache directory
|
||||
String cachedir = System.getProperty("application.cachedir");
|
||||
if (cachedir != null && !cachedir.isEmpty()) {
|
||||
return new File(cachedir,
|
||||
SystemUtilities.getUserName() + "-" + applicationProperties.getApplicationName());
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
// Use user temp directory if platform specific scheme does not exist above or it failed
|
||||
return getDefaultUserTempDir(applicationProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default application's user settings 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.
|
||||
*/
|
||||
public static File getDefaultUserSettingsDir(ApplicationProperties applicationProperties,
|
||||
ResourceFile installationDirectory) throws FileNotFoundException {
|
||||
|
||||
String userSettingsDir = System.getProperty("user.home");
|
||||
if (userSettingsDir == null || userSettingsDir.isEmpty()) {
|
||||
throw new FileNotFoundException("System property \"user.home\" is not set!");
|
||||
}
|
||||
|
||||
String prefix =
|
||||
"." + applicationProperties.getApplicationName().replaceAll("\\s", "").toLowerCase();
|
||||
|
||||
File applicationParentDir = new File(userSettingsDir, prefix);
|
||||
String suffix = applicationProperties.getApplicationVersion();
|
||||
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
// Add the appication's installation directory name to this variable, so that each
|
||||
// branch's project user directory is unique.
|
||||
suffix += "_location_" + installationDirectory.getName();
|
||||
}
|
||||
|
||||
return new File(applicationParentDir, prefix + "-" + suffix);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/* ###
|
||||
* 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 java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.framework.ApplicationProperties;
|
||||
|
||||
/**
|
||||
* The dummy application layout defines the customizable elements of a dummy application's
|
||||
* directory structure. A dummy application only has a name, an installation/root dir, and
|
||||
* a user temp directory.
|
||||
*/
|
||||
public class DummyApplicationLayout extends ApplicationLayout {
|
||||
|
||||
/**
|
||||
* Constructs a new dummy application layout object.
|
||||
*
|
||||
* @throws FileNotFoundException if there was a problem getting a user directory.
|
||||
*/
|
||||
public DummyApplicationLayout(String name) throws FileNotFoundException {
|
||||
|
||||
// Application properties
|
||||
applicationProperties = new ApplicationProperties(name);
|
||||
|
||||
// Application installation directory
|
||||
ResourceFile cwd = new ResourceFile(System.getProperty("user.dir"));
|
||||
applicationInstallationDir = cwd;
|
||||
|
||||
// Application root directories
|
||||
applicationRootDirs = new ArrayList<>();
|
||||
applicationRootDirs.add(cwd);
|
||||
|
||||
// User directories
|
||||
userTempDir = ApplicationUtilities.getDefaultUserTempDir(applicationProperties);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/* ###
|
||||
* 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.module;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
public class ModuleManifestFile {
|
||||
public final static String MODULE_MANIFEST_FILE_NAME = "Module.manifest";
|
||||
|
||||
private static final String NAME_IDENTIFIER = "MODULE NAME:";
|
||||
private static final String DEPENDENCY_IDENTIFIER = "MODULE DEPENDENCY:";
|
||||
private static final String MODULE_FILE_LICENSE = "MODULE FILE LICENSE:";
|
||||
private static final String EXCLUDE_FROM_GHIDRA_JAR = "EXCLUDE FROM GHIDRA JAR";
|
||||
private static final String DATA_SEARCH_IGNORE_DIR = "DATA SEARCH IGNORE DIR:";
|
||||
private static final String MODULE_DIR_IDENTIFIER = "MODULE DIR:";
|
||||
// private static final String EXTENSION_SUFFIX = "EXTENSION SUFFIX:";
|
||||
// private static final String REQUIRES_CLASS_SEARCH = "REQUIRES CLASS SEARCH:";
|
||||
// private static final String OWNER_IDENTIFIER = "MODULE DIR:";
|
||||
// private static final String RSRC_IDENTIFIER = "MODULE DIR:";
|
||||
// private static final String DATA_IDENTIFIER = "MODULE DIR:";
|
||||
// private static final String PLAF_IDENTIFIER = "MODULE DIR:";
|
||||
|
||||
private static final String COMMENT_IDENTIFIER = "#";
|
||||
|
||||
private String moduleName;
|
||||
private boolean excludeFromGhidraJar;
|
||||
private Map<String, String> fileIPMap = new HashMap<String, String>();
|
||||
|
||||
private Set<String> dataSearchIgnoreDirs = new HashSet<String>();
|
||||
|
||||
public ModuleManifestFile(File moduleRootDir) throws IOException {
|
||||
this(new ResourceFile(moduleRootDir));
|
||||
}
|
||||
|
||||
public ModuleManifestFile(ResourceFile moduleRootDir) throws IOException {
|
||||
ResourceFile file = new ResourceFile(moduleRootDir, MODULE_MANIFEST_FILE_NAME);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException("Missing module manifest file:" +
|
||||
file.getAbsolutePath());
|
||||
}
|
||||
|
||||
List<String> lines = FileUtilities.getLines(file);
|
||||
|
||||
int lineNumber = 1;
|
||||
for (String line : lines) {
|
||||
processLine(file, line, lineNumber++);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasModuleManifest(File moduleRootDir) {
|
||||
File file = new File(moduleRootDir, MODULE_MANIFEST_FILE_NAME);
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
public boolean excludeFromGhidraJar() {
|
||||
return excludeFromGhidraJar;
|
||||
}
|
||||
|
||||
public Map<String, String> getModuleFileIPs() {
|
||||
return Collections.unmodifiableMap(fileIPMap);
|
||||
}
|
||||
|
||||
private void processLine(ResourceFile file, String configLine, int lineNumber)
|
||||
throws IOException {
|
||||
String trimmedLine = configLine.trim();
|
||||
if (trimmedLine.length() == 0) {
|
||||
return; // ignore empty lines.
|
||||
}
|
||||
|
||||
else if (trimmedLine.startsWith(NAME_IDENTIFIER)) {
|
||||
processNameLine(trimmedLine);
|
||||
}
|
||||
else if (trimmedLine.startsWith(DEPENDENCY_IDENTIFIER)) {
|
||||
// ignore for now
|
||||
}
|
||||
else if (trimmedLine.startsWith(EXCLUDE_FROM_GHIDRA_JAR)) {
|
||||
excludeFromGhidraJar = true;
|
||||
}
|
||||
else if (trimmedLine.startsWith(MODULE_FILE_LICENSE)) {
|
||||
processModuleFileLicense(trimmedLine);
|
||||
}
|
||||
else if (trimmedLine.startsWith(COMMENT_IDENTIFIER)) {
|
||||
// this is a comment line--ignore!
|
||||
}
|
||||
else if (trimmedLine.startsWith(DATA_SEARCH_IGNORE_DIR)) {
|
||||
processDataSearchIgnoreDir(trimmedLine);
|
||||
}
|
||||
else if (trimmedLine.startsWith(MODULE_DIR_IDENTIFIER)) {
|
||||
// do nothing for now
|
||||
}
|
||||
else {
|
||||
String message =
|
||||
"Module manifest file error on line " + (lineNumber + 1) + " of file: " + file +
|
||||
"\n\t-> Invalid line encountered: " + trimmedLine;
|
||||
Msg.debug(this, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void processDataSearchIgnoreDir(String trimmedLine) {
|
||||
String ignoreDirName = trimmedLine.substring(DATA_SEARCH_IGNORE_DIR.length()).trim();
|
||||
dataSearchIgnoreDirs.add(ignoreDirName);
|
||||
}
|
||||
|
||||
private void processModuleFileLicense(String line) throws IOException {
|
||||
String fileAndIPLine = line.substring(MODULE_FILE_LICENSE.length()).trim();
|
||||
int firstSpace = fileAndIPLine.indexOf(' ');
|
||||
if (firstSpace < 0) {
|
||||
fileIPFail(line); // error
|
||||
}
|
||||
|
||||
String filename = fileAndIPLine.substring(0, firstSpace);
|
||||
String IP = fileAndIPLine.substring(firstSpace + 1);
|
||||
fileIPMap.put(filename, IP);
|
||||
}
|
||||
|
||||
private void fileIPFail(String line) throws IOException {
|
||||
throw new IOException("Invalid Module.manifest entry for identifier \"" +
|
||||
MODULE_FILE_LICENSE + "\".\nThis line requires two parts: 1) " +
|
||||
"the module-relative file path and filename, and 2) the IP of " +
|
||||
"that file.\n Found: " + line);
|
||||
}
|
||||
|
||||
private void processNameLine(String line) {
|
||||
moduleName = line.substring(NAME_IDENTIFIER.length()).trim();
|
||||
}
|
||||
|
||||
public String getModuleName() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
public Set<String> getDataSearchIgnoreDirs() {
|
||||
return dataSearchIgnoreDirs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
/* ###
|
||||
* 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.module;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.framework.GModule;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
/**
|
||||
* Utility methods for module related things.
|
||||
*/
|
||||
public class ModuleUtilities {
|
||||
|
||||
public static final String MANIFEST_FILE_NAME = "Module.manifest";
|
||||
public static final String MANIFEST_FILE_NAME_UNINSTALLED = "Module.manifest.uninstalled";
|
||||
public static final String MODULE_LIST = "MODULE_LIST";
|
||||
|
||||
/**
|
||||
* How many directories deep to look for module directories, starting from an application root
|
||||
* directory. For example, 3 would pick up modules as deep as: root/category/category/module
|
||||
*/
|
||||
private static final int MAX_MODULE_DEPTH = 3;
|
||||
|
||||
/**
|
||||
* Checks if the given directory is a module.
|
||||
*
|
||||
* @param dir the directory to check.
|
||||
* @return true if the given directory is a module
|
||||
*/
|
||||
public static boolean isModuleDirectory(ResourceFile dir) {
|
||||
return new ResourceFile(dir, MANIFEST_FILE_NAME).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given path is a module root directory.
|
||||
*
|
||||
* @param path the path to check
|
||||
* @return true if the given path is a module root directory.
|
||||
*/
|
||||
public static boolean isModuleDirectory(Path path) {
|
||||
File file = path.toFile();
|
||||
return new File(file, MANIFEST_FILE_NAME).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the given root directory for module root directories. Adds any discovered module
|
||||
* root directories to the given collection.
|
||||
*
|
||||
* @param rootDir The directory to start looking for module root directories in.
|
||||
* @param moduleRootDirs A collection to add discovered module root directories to.
|
||||
* @return The given collection with any discovered modules added.
|
||||
*/
|
||||
public static Collection<ResourceFile> findModuleRootDirectories(ResourceFile rootDir,
|
||||
Collection<ResourceFile> moduleRootDirs) {
|
||||
// look for any external GPL modules
|
||||
findModuleRootDirectoriesHelper(new ResourceFile(rootDir, "../GPL"), moduleRootDirs,
|
||||
MAX_MODULE_DEPTH);
|
||||
return findModuleRootDirectoriesHelper(rootDir, moduleRootDirs, MAX_MODULE_DEPTH);
|
||||
}
|
||||
|
||||
private static Collection<ResourceFile> findModuleRootDirectoriesHelper(ResourceFile rootDir,
|
||||
Collection<ResourceFile> moduleRootDirs, int remainingDepth) {
|
||||
if (!rootDir.exists() || remainingDepth <= 0) {
|
||||
return moduleRootDirs;
|
||||
}
|
||||
for (ResourceFile subDir : rootDir.listFiles(ResourceFile::isDirectory)) {
|
||||
if ("build".equals(subDir.getName())) {
|
||||
continue; // ignore all "build" directories
|
||||
}
|
||||
if (ModuleUtilities.isModuleDirectory(subDir)) {
|
||||
moduleRootDirs.add(subDir);
|
||||
}
|
||||
else {
|
||||
findModuleRootDirectoriesHelper(subDir, moduleRootDirs, remainingDepth - 1);
|
||||
}
|
||||
}
|
||||
return moduleRootDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the given root directories for module root directories. Adds any discovered module
|
||||
* root directories to the given collection.
|
||||
*
|
||||
* @param rootDirs The directories to look for module root directories in.
|
||||
* @param moduleRootDirs A collection to add discovered module root directories to.
|
||||
* @return The given collection with any discovered modules added.
|
||||
*/
|
||||
public static Collection<ResourceFile> findModuleRootDirectories(
|
||||
Collection<ResourceFile> rootDirs, Collection<ResourceFile> moduleRootDirs) {
|
||||
for (ResourceFile rootDir : rootDirs) {
|
||||
findModuleRootDirectories(rootDir, moduleRootDirs);
|
||||
}
|
||||
return moduleRootDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the given jar root directory for module root directories. Uses a "module list"
|
||||
* file to locate the module root directories. Adds any discovered module root directories
|
||||
* to the given collection.
|
||||
*
|
||||
* @param rootDir The jar directory to start looking for module root directories in.
|
||||
* @param moduleRootDirs A collection to add discovered module root directories to.
|
||||
* @return The given collection with any discovered modules added.
|
||||
* @throws IOException if there was a problem reading the module list file.
|
||||
*/
|
||||
public static Collection<ResourceFile> findJarModuleRootDirectories(ResourceFile rootDir,
|
||||
Collection<ResourceFile> moduleRootDirs) throws IOException {
|
||||
ResourceFile moduleListFile = new ResourceFile(rootDir, MODULE_LIST);
|
||||
for (String relativeModulePath : FileUtilities.getLines(moduleListFile)) {
|
||||
moduleRootDirs.add(new ResourceFile(rootDir, relativeModulePath));
|
||||
}
|
||||
return moduleRootDirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for modules in a given collection of module root directories.
|
||||
*
|
||||
* @param appRootDirs The collection of application root directories associated with the the given
|
||||
* list of module root directories.
|
||||
* @param moduleRootDirs A collection of module root directories to search for modules in.
|
||||
* @return The discovered modules as a map (mapping module name to module for convenience).
|
||||
*/
|
||||
public static Map<String, GModule> findModules(Collection<ResourceFile> appRootDirs,
|
||||
Collection<ResourceFile> moduleRootDirs) {
|
||||
|
||||
Map<String, GModule> map = new TreeMap<>();
|
||||
|
||||
for (ResourceFile moduleRoot : moduleRootDirs) {
|
||||
GModule gModule = new GModule(appRootDirs, moduleRoot);
|
||||
if (map.put(moduleRoot.getName(), gModule) != null) {
|
||||
StringBuilder collided = new StringBuilder();
|
||||
for (ResourceFile collideRoot : moduleRootDirs) {
|
||||
if (moduleRoot.getName().equals(collideRoot.getName())) {
|
||||
collided.append("\n");
|
||||
collided.append(collideRoot.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
throw new AssertException(
|
||||
"Multiple modules collided with same name: " + moduleRoot.getName() + collided);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "lib" directories from the given modules.
|
||||
*
|
||||
* @param modules The modules to get the lib directories of.
|
||||
* @return A collection of lib directories from the given modules.
|
||||
*/
|
||||
public static Collection<ResourceFile> getModuleLibDirectories(Map<String, GModule> modules) {
|
||||
List<ResourceFile> libraryDirectories = new ArrayList<>();
|
||||
for (GModule module : modules.values()) {
|
||||
module.collectExistingModuleDirs(libraryDirectories, "lib");
|
||||
|
||||
// In testing mode, we run out of an intermediate build state...the module jars
|
||||
// live in a build/libs directory. We only want to look in here when testing because
|
||||
// other run modes (such as a Ghidra release launched from a user's Eclipse) may contain
|
||||
// build remnants that could cause problems if discovered.
|
||||
if (SystemUtilities.isInTestingMode()) {
|
||||
module.collectExistingModuleDirs(libraryDirectories, "libs");
|
||||
}
|
||||
}
|
||||
return libraryDirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "bin" directories from the given modules.
|
||||
*
|
||||
* @param modules The modules to get the bin directories of.
|
||||
* @return A collection of bin directories from the given modules.
|
||||
*/
|
||||
public static Collection<ResourceFile> getModuleBinDirectories(Map<String, GModule> modules) {
|
||||
List<ResourceFile> binDirectories = new ArrayList<>();
|
||||
for (GModule module : modules.values()) {
|
||||
module.collectExistingModuleDirs(binDirectories, "bin/main");
|
||||
}
|
||||
return binDirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given path is parented by a module root directory.
|
||||
* <p>
|
||||
* For example, given a module path of <tt>/some/dir/features/cool_module/</tt>, then this
|
||||
* method will return true for these paths:
|
||||
* <br>
|
||||
* <br>
|
||||
* <tt>/some/dir/features/cool_module</tt><br>
|
||||
* <tt>/some/dir/features/cool_module/some/child/dir</tt>
|
||||
* <br>
|
||||
* <br>and false for these paths:
|
||||
* <br>
|
||||
* <br>
|
||||
* <tt>/some/random/path</tt><br>
|
||||
* <tt>/some/dir/features/</tt>
|
||||
*
|
||||
* @param pathName the path name to check
|
||||
* @return true if the given path is parented by a module root directory.
|
||||
* @see #isModuleDirectory(Path)
|
||||
*/
|
||||
public static boolean isInModule(String pathName) {
|
||||
return getModule(pathName) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the module containing the given path string, if it is parented by a
|
||||
* module root directory.
|
||||
* <p>
|
||||
* For example, given a module path of <tt>/some/dir/features/cool_module/</tt>, then this
|
||||
* method will return that module path, given these paths:
|
||||
* <br>
|
||||
* <br>
|
||||
* <tt>/some/dir/features/cool_module</tt><br>
|
||||
* <tt>/some/dir/features/cool_module/some/child/dir</tt>
|
||||
* <br>
|
||||
* <br>and null for these paths:
|
||||
* <br>
|
||||
* <br>
|
||||
* <tt>/some/random/path</tt><br>
|
||||
* <tt>/some/dir/features/</tt>
|
||||
*
|
||||
* @param pathName the path name to check
|
||||
* @return the module root directory; null if the path is not in a module
|
||||
* @see #isModuleDirectory(Path)
|
||||
*/
|
||||
public static Path getModule(String pathName) {
|
||||
Path path = toPath(pathName);
|
||||
while (path != null) {
|
||||
if (isModuleDirectory(path)) {
|
||||
return path;
|
||||
}
|
||||
path = path.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Path toPath(String pathname) {
|
||||
try {
|
||||
return Paths.get(pathname);
|
||||
}
|
||||
catch (InvalidPathException e) {
|
||||
Msg.trace(ModuleUtilities.class, "Invalid path: " + pathname);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file that is the root folder of the repository containing the given file. 'Root'
|
||||
* here means a folder that contains a repository folder. As an example, given a repo
|
||||
* structure of:
|
||||
*
|
||||
* <p><tt>/userdir/repoRoot/repoDir/.git</tt><br>
|
||||
*
|
||||
* <p>then this method, given will produce the following results (input -> output):<br>
|
||||
*
|
||||
* <p><tt>/userdir/repoRoot/repoDir/.git -> /userdir/repoRoot</tt>
|
||||
* <br><tt>/userdir/repoRoot/repoDir -> /userdir/repoRoot</tt>
|
||||
* <br><tt>/userdir/repoRoot -> /userdir/repoRoot</tt>
|
||||
*
|
||||
*
|
||||
* @param f the child file of the desired repo
|
||||
* @return a file that is the root folder of the repository containing the given file; null
|
||||
* if the given file is not under a repo directory or itself a repo root
|
||||
*/
|
||||
public static File findRepoRoot(File f) {
|
||||
if (f == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File repoDir = findRepo(f);
|
||||
if (repoDir != null) {
|
||||
return repoDir.getParentFile();
|
||||
}
|
||||
|
||||
// one last check to see if the given file is actually itself a repo root
|
||||
File[] children = f.listFiles(file -> file.isDirectory());
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
File childRepo = findRepo(child);
|
||||
if (childRepo != null) {
|
||||
return childRepo.getParentFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file that is the repository folder containing the given file. As an example,
|
||||
* given a repo structure of:
|
||||
*
|
||||
* <p><tt>/userdir/repoRoot/repoDir/.git</tt><br>
|
||||
*
|
||||
* <p>then this method, given will produce the following results (input -> output):<br>
|
||||
*
|
||||
* <p><tt>/userdir/repoRoot/repoDir/.git -> /userdir/repoRoot/repoDir</tt>
|
||||
* <br><tt>/userdir/repoRoot/repoDir -> /userdir/repoRoot/repoDir</tt>
|
||||
*
|
||||
* @param f the child file of the desired repo
|
||||
* @return a file that is the repo folder of the repository containing the given file; null
|
||||
* if the given file is not under a repo directory
|
||||
*/
|
||||
public static File findRepo(File f) {
|
||||
if (f == null) {
|
||||
return null;
|
||||
}
|
||||
File testGit = new File(f, ".git");
|
||||
if (testGit.exists()) {
|
||||
return f;
|
||||
}
|
||||
return findRepo(f.getParentFile());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue