GT-3547 - Patch dir fix to allow loading of extension points

This commit is contained in:
dragonmacher 2020-02-14 17:08:21 -05:00
parent 3c8f2bdeff
commit 3dced733df
9 changed files with 175 additions and 49 deletions

View file

@ -58,15 +58,15 @@ public class ClassFinder {
if ((lcPath.endsWith(".jar") || lcPath.endsWith(".zip")) && file.exists()) {
if (ClassJar.ignoreJar(lcPath)) {
log.trace("Ignoring jar file: " + path);
log.trace("Ignoring jar file: {}", path);
continue;
}
log.trace("Searching jar file: " + path);
log.trace("Searching jar file: {}", path);
classJars.add(new ClassJar(path, monitor));
}
else if (file.isDirectory()) {
log.trace("Searching classpath directory: " + path);
log.trace("Searching classpath directory: {}", path);
classDirs.add(new ClassDir(path, monitor));
}
}
@ -107,7 +107,6 @@ public class ClassFinder {
return n1.compareTo(n2);
});
return classList;
}

View file

@ -17,17 +17,23 @@ package ghidra.util.classfinder;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.Enumeration;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FilenameUtils;
import generic.jar.ResourceFile;
import ghidra.framework.Application;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import utility.application.ApplicationLayout;
class ClassJar {
class ClassJar extends ClassLocation {
/**
* Pattern for matching jar files in a module lib dir
@ -36,24 +42,35 @@ class ClassJar {
* <tt>build/libs</tt>, ending in <tt>.jar</tt> (non-capturing) and then
* grab that dir's parent and the name of the jar file.
*/
private static Pattern ANY_MODULE_LIB_JAR_FILE_PATTERN =
private static final Pattern ANY_MODULE_LIB_JAR_FILE_PATTERN =
Pattern.compile(".*/(.*)/(?:lib|build/libs)/(.+).jar");
private static final String PATCH_DIR_PATH_FORWARD_SLASHED = getPatchDirPath();
private static String getPatchDirPath() {
ApplicationLayout layout = Application.getApplicationLayout();
ResourceFile installDir = layout.getApplicationInstallationDir();
ResourceFile patchDir = new ResourceFile(installDir, "Ghidra/patch");
String patchPath = patchDir.getAbsolutePath();
String forwardSlashed = patchPath.replaceAll("\\\\", "/");
return forwardSlashed;
}
private String path;
private Set<String> classNameList = new HashSet<>();
private Set<Class<?>> classes = new HashSet<>();
ClassJar(String path, TaskMonitor monitor) throws CancelledException {
this.path = path;
scan(monitor);
scanJar(monitor);
}
@Override
void getClasses(Set<Class<?>> set, TaskMonitor monitor) {
checkForDuplicates(set);
set.addAll(classes);
}
private void scan(TaskMonitor monitor) throws CancelledException {
private void scanJar(TaskMonitor monitor) throws CancelledException {
File file = new File(path);
@ -88,25 +105,39 @@ class ClassJar {
if (pathName.contains("ExternalLibraries")) {
return true;
}
//
// Production Mode - allow users to enter code in the 'patch' directory
//
String forwardSlashedPathName = pathName.replaceAll("\\\\", "/");
if (isPatchJar(forwardSlashedPathName)) {
return false;
}
//
// Production Mode - In production, only module lib jar files are scanned.
//
if (isModuleDependencyJar(pathName)) {
if (isModuleDependencyJar(forwardSlashedPathName)) {
return false;
}
return true;
}
static boolean isModuleDependencyJar(String pathName) {
// Note: the path is expected to be using forward slashes
private static boolean isPatchJar(String pathName) {
String jarDirectory = FilenameUtils.getFullPathNoEndSeparator(pathName);
return jarDirectory.equalsIgnoreCase(PATCH_DIR_PATH_FORWARD_SLASHED);
}
// Note: the path is expected to be using forward slashes
private static boolean isModuleDependencyJar(String pathName) {
if (ClassSearcher.SEARCH_ALL_JARS) {
return true; // this will search all jar files
}
String forwardSlashed = pathName.replaceAll("\\\\", "/");
Matcher matcher = ANY_MODULE_LIB_JAR_FILE_PATTERN.matcher(forwardSlashed);
// Note: the path is expected to be using forward slashes
Matcher matcher = ANY_MODULE_LIB_JAR_FILE_PATTERN.matcher(pathName);
if (!matcher.matches()) {
return false;
}
@ -121,15 +152,14 @@ class ClassJar {
private void processClassFiles(JarEntry entry) {
String name = entry.getName();
if (!name.endsWith(".class")) {
if (!name.endsWith(CLASS_EXT)) {
return;
}
name = name.substring(0, name.indexOf(".class"));
name = name.substring(0, name.indexOf(CLASS_EXT));
name = name.replace('/', '.');
Class<?> c = ClassFinder.loadExtensionPoint(path, name);
if (c != null) {
classNameList.add(name);
classes.add(c);
}
}

View file

@ -0,0 +1,56 @@
/* ###
* 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 ghidra.util.classfinder;
import java.util.HashSet;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a place from which {@link Class}s can be obtained
*/
abstract class ClassLocation {
protected static final String CLASS_EXT = ".class";
final Logger log = LogManager.getLogger(getClass());
protected Set<Class<?>> classes = new HashSet<>();
abstract void getClasses(Set<Class<?>> set, TaskMonitor monitor) throws CancelledException;
void checkForDuplicates(Set<Class<?>> existingClasses) {
if (!log.isTraceEnabled()) {
return;
}
for (Class<?> c : classes) {
if (existingClasses.contains(c)) {
Module module = c.getModule();
module.toString();
log.trace("Attempting to load the same class twice: {}. " +
"Keeping loaded class ; ignoring class from {}", c, this);
return;
}
}
}
}

View file

@ -23,12 +23,11 @@ import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
class ClassPackage {
class ClassPackage extends ClassLocation {
private static final FileFilter CLASS_FILTER =
pathname -> pathname.getName().endsWith(".class");
pathname -> pathname.getName().endsWith(CLASS_EXT);
private Set<Class<?>> classes = new HashSet<>();
private Set<ClassPackage> children = new HashSet<>();
private File rootDir;
private File packageDir;
@ -88,7 +87,11 @@ class ClassPackage {
return new File(lRootDir, lPackageName.replace('.', File.separatorChar));
}
@Override
void getClasses(Set<Class<?>> set, TaskMonitor monitor) throws CancelledException {
checkForDuplicates(set);
set.addAll(classes);
Iterator<ClassPackage> it = children.iterator();

View file

@ -248,7 +248,7 @@ public class ClassSearcher {
monitor.setMessage("Loading classes...");
extensionPoints = searcher.getClasses(monitor);
log.trace("Found extension classes: " + extensionPoints);
log.trace("Found extension classes {}", extensionPoints);
if (extensionPoints.isEmpty()) {
throw new AssertException("Unable to location extension points!");
}
@ -307,7 +307,6 @@ public class ClassSearcher {
}
private static void loadExtensionClassesFromJar() {
// there will only be one root in jar file mode!
ResourceFile appRoot = Application.getApplicationRootDirectory();
ResourceFile extensionClassesFile = new ResourceFile(appRoot, "EXTENSION_POINT_CLASSES");
try {
@ -326,7 +325,8 @@ public class ClassSearcher {
}
catch (IOException e) {
throw new AssertException("Got unexpected IOException ", e);
throw new AssertException("Unexpected IOException reading extension class file " +
extensionClassesFile, e);
}
}
@ -338,7 +338,7 @@ public class ClassSearcher {
throw new AssertException("Could not find modules for Class Searcher!");
}
log.trace("Scanning module root directories: " + moduleRootDirectories);
log.trace("Scanning module root directories: {}", moduleRootDirectories);
for (ResourceFile moduleRoot : moduleRootDirectories) {
ResourceFile file = new ResourceFile(moduleRoot, "data/ExtensionPoint.manifest");
@ -361,7 +361,7 @@ public class ClassSearcher {
}
buffy.append(')');
extensionPointSuffixPattern = Pattern.compile(buffy.toString());
log.trace("Using extension point pattern: " + extensionPointSuffixPattern);
log.trace("Using extension point pattern: {}", extensionPointSuffixPattern);
}
static boolean isExtensionPointName(String name) {