diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/ExtensionDetails.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/ExtensionDetails.java index 8f1ca6e9ce..703aad255d 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/ExtensionDetails.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/ExtensionDetails.java @@ -293,10 +293,8 @@ public class ExtensionDetails implements Comparable { // The remaining dirs are of the form: // /Ghidra/Extensions // /Ghidra/Extensions - return extDirs.stream() - .skip(1) - .anyMatch( - dir -> FileUtilities.isPathContainedWithin(dir.getFile(false), installDir)); + List remainingDirs = extDirs.subList(1, extDirs.size()); + return FileUtilities.startsWith(remainingDirs, installDir.getAbsolutePath()); } /** diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginDescription.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginDescription.java index db63e1d0f0..3962a16f75 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginDescription.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/PluginDescription.java @@ -168,10 +168,9 @@ public class PluginDescription implements Comparable { */ public boolean isInExtension() { String myPath = getSourceLocation(); - ResourceFile myLocation = new ResourceFile(myPath); ApplicationLayout layout = Application.getApplicationLayout(); List extDirs = layout.getExtensionInstallationDirs(); - return FileUtilities.isPathContainedWithin(extDirs, myLocation); + return FileUtilities.startsWith(extDirs, myPath); } /** diff --git a/Ghidra/Framework/Utility/src/main/java/generic/jar/ResourceFile.java b/Ghidra/Framework/Utility/src/main/java/generic/jar/ResourceFile.java index 5ccf6fc3f2..fd56ee934e 100644 --- a/Ghidra/Framework/Utility/src/main/java/generic/jar/ResourceFile.java +++ b/Ghidra/Framework/Utility/src/main/java/generic/jar/ResourceFile.java @@ -20,8 +20,6 @@ import java.net.*; import java.util.HashMap; import java.util.Map; -import utilities.util.FileUtilities; - /** * Class for representing file object regardless of whether they are actual files in the file system or * or files stored inside of a jar file. This class provides most all the same capabilities as the @@ -339,13 +337,4 @@ public class ResourceFile implements Comparable { public URI toURI() { return resource.toURI(); } - - /** - * Returns true if this file's path contains the entire path of the given file. - * @param otherFile the other file to check - * @return true if this file's path contains the entire path of the given file. - */ - public boolean containsPath(ResourceFile otherFile) { - return FileUtilities.isPathContainedWithin(getFile(false), otherFile.getFile(false)); - } } diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraApplicationLayout.java b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraApplicationLayout.java index 619213ae65..4b1cfeabff 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraApplicationLayout.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraApplicationLayout.java @@ -156,7 +156,7 @@ public class GhidraApplicationLayout extends ApplicationLayout { FileUtilities.forEachFile(extensionInstallDir, extensionDir -> { // Skip extensions in an application root directory... already found those. - if (FileUtilities.isPathContainedWithin(applicationRootDirs, extensionDir)) { + if (FileUtilities.startsWith(applicationRootDirs, extensionDir.getAbsolutePath())) { return; } diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java index d3032871dc..d5f5c9ef83 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java @@ -286,7 +286,7 @@ public class GhidraLauncher { for (GModule module : modules.values()) { ResourceFile moduleDir = module.getModuleRoot(); - if (!FileUtilities.isPathContainedWithin(extensionInstallationDirs, moduleDir)) { + if (!FileUtilities.startsWith(extensionInstallationDirs, moduleDir.getAbsolutePath())) { continue; // not an extension } diff --git a/Ghidra/Framework/Utility/src/main/java/utilities/util/FileUtilities.java b/Ghidra/Framework/Utility/src/main/java/utilities/util/FileUtilities.java index 43a513a0f8..96e483b8dc 100644 --- a/Ghidra/Framework/Utility/src/main/java/utilities/util/FileUtilities.java +++ b/Ghidra/Framework/Utility/src/main/java/utilities/util/FileUtilities.java @@ -842,12 +842,50 @@ public final class FileUtilities { } /** - * Returns true if the given potentialParentFile is the parent path of - * the given otherFile, or if the two file paths point to the same path. + * Tests if {@code otherPath} starts with {@code potentialParentPath}. The paths are + * {@link Path#normalize() normalized} before comparing. + * + * @param potentialParentPath The path that may be the parent + * @param otherPath The path that may be the child + * @return true if the normalized {@code otherPath} starts with the normalized + * {@code potentialParentPath} and the paths are {@link Paths#get valid}; otherwise false + */ + public static boolean startsWith(String potentialParentPath, String otherPath) { + try { + return Paths.get(otherPath) + .normalize() + .startsWith(Paths.get(potentialParentPath).normalize()); + } + catch (InvalidPathException e) { + return false; + } + } + + /** + * Tests if {@code otherPath} starts with any of the given {@code potentialParents}. The paths + * are {@link Path#normalize() normalized} before comparing. + * + * @param potentialParents The paths that may be the parent + * @param otherPath The path that may be the child + * @return boolean true if the normalized {@code otherPath} starts with any of the given + * normalized {@code potentialParents}s and the paths are {@link Paths#get valid}; otherwise + * false + */ + public static boolean startsWith(Collection potentialParents, String otherPath) { + return potentialParents.stream().anyMatch(p -> startsWith(p.getAbsolutePath(), otherPath)); + } + + /** + * Returns true if the given {@code potentialParentFile} is the parent path of + * the given {@code otherFile}, or if the two file paths point to the same path. + *

+ * NOTE: Both files are converted to their {@link File#getCanonicalPath() canonical form} prior + * to comparing their paths, which may have performance implications, particularly on Windows. * * @param potentialParentFile The file that may be the parent * @param otherFile The file that may be the child - * @return boolean true if otherFile's path is within potentialParentFile's path + * @return boolean true if {@code otherFile}'s canonical path is within + * {@code potentialParentFile}'s canonical path */ public static boolean isPathContainedWithin(File potentialParentFile, File otherFile) { try { @@ -869,17 +907,21 @@ public final class FileUtilities { } /** - * Returns true if any of the given potentialParents is the parent path of or has - * the same path as the given otherFile. + * Returns true if any of the given {@code potentialParents} is the parent path of or has + * the same path as the given {@code otherFile}. + *

+ * NOTE: All files are converted to their {@link File#getCanonicalPath() canonical form} prior + * to comparing their paths, which may have performance implications, particularly on Windows. * * @param potentialParents The files that may be the parent * @param otherFile The file that may be the child - * @return boolean true if otherFile's path is within any of the potentialParents' paths + * @return boolean true if {@code otherFile}'s canonical path is within any of the + * {@code potentialParents}' canonical paths */ public static boolean isPathContainedWithin(Collection potentialParents, ResourceFile otherFile) { - - return potentialParents.stream().anyMatch(parent -> parent.containsPath(otherFile)); + File f = otherFile.getFile(false); + return potentialParents.stream().anyMatch(p -> isPathContainedWithin(p.getFile(false), f)); } /** diff --git a/Ghidra/Framework/Utility/src/main/java/utility/module/ModuleUtilities.java b/Ghidra/Framework/Utility/src/main/java/utility/module/ModuleUtilities.java index 261175ea39..8b28d88da9 100644 --- a/Ghidra/Framework/Utility/src/main/java/utility/module/ModuleUtilities.java +++ b/Ghidra/Framework/Utility/src/main/java/utility/module/ModuleUtilities.java @@ -398,11 +398,11 @@ public class ModuleUtilities { * directory */ public static boolean isExternalModule(GModule module, ApplicationLayout layout) { - File moduleRootDir = module.getModuleRoot().getFile(false); + String moduleRootPath = module.getModuleRoot().getFile(false).getAbsolutePath(); return !layout.getApplicationRootDirs() .stream() - .map(dir -> dir.getParentFile().getFile(false)) - .anyMatch(dir -> FileUtilities.isPathContainedWithin(dir, moduleRootDir)); + .map(rootDir -> rootDir.getParentFile().getFile(false).getAbsolutePath()) + .anyMatch(appPath -> FileUtilities.startsWith(appPath, moduleRootPath)); } /**