mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-4563: Support for searching for libraries inside a GFileSystem
This commit is contained in:
parent
9911db9828
commit
9f883022a4
7 changed files with 140 additions and 62 deletions
|
@ -366,6 +366,7 @@ icon.plugin.fsbrowser.import = images/famfamfam_silk_icons_v013/application_get.
|
||||||
icon.plugin.fsbrowser.ios = images/famfamfam_silk_icons_v013/phone.png
|
icon.plugin.fsbrowser.ios = images/famfamfam_silk_icons_v013/phone.png
|
||||||
icon.plugin.fsbrowser.open.all = images/famfamfam_silk_icons_v013/application_cascade.png
|
icon.plugin.fsbrowser.open.all = images/famfamfam_silk_icons_v013/application_cascade.png
|
||||||
icon.plugin.fsbrowser.list.mounted = downArrow.png
|
icon.plugin.fsbrowser.list.mounted = downArrow.png
|
||||||
|
icon.plugin.fsbrowser.library = images/imported_bookmark.gif
|
||||||
|
|
||||||
icon.base.util.fixed.bit.size.field = icon.pulldown
|
icon.base.util.fixed.bit.size.field = icon.pulldown
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,12 @@
|
||||||
using the Batch Import dialog.</P>
|
using the Batch Import dialog.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H3><A name="FSB_Add_To_Program"></A>Add To Program</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Adds the selected file into the currently active program.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3><A name="FSB_Export"></A>Export</H3>
|
<H3><A name="FSB_Export"></A>Export</H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
|
@ -104,6 +110,13 @@
|
||||||
local computer.</P>
|
local computer.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H3><A name="FSB_Add_Library_Search_Path"></A>Add Library Search Path</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>Adds the currently selected file or folder to the list of library search paths which
|
||||||
|
is used during program import.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3><A name="FSB_View_As_Image"></A>View As Image</H3>
|
<H3><A name="FSB_View_As_Image"></A>View As Image</H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
|
|
|
@ -15,9 +15,16 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.importer;
|
package ghidra.app.util.importer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.formats.gfilesystem.FileSystemService;
|
||||||
import ghidra.framework.Platform;
|
import ghidra.framework.Platform;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple class for managing the library search path
|
* A simple class for managing the library search path
|
||||||
|
@ -61,11 +68,39 @@ public class LibrarySearchPathManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of directories to search for libraries
|
* Returns a {@link List} of {@link FSRL}s to search for libraries
|
||||||
* @return a list of directories to search for libraries
|
* @param log The log
|
||||||
|
* @param monitor A cancellable monitor
|
||||||
|
* @return a {@link List} of {@link FSRL}s to search for libraries
|
||||||
|
* @throws CancelledException if the user cancelled the operation
|
||||||
*/
|
*/
|
||||||
public static List<String> getLibraryPathsList() {
|
public static List<FSRL> getLibraryFsrlList(MessageLog log, TaskMonitor monitor)
|
||||||
return new ArrayList<>(pathList);
|
throws CancelledException {
|
||||||
|
FileSystemService fsService = FileSystemService.getInstance();
|
||||||
|
List<FSRL> fsrlList = new ArrayList<>();
|
||||||
|
for (String path : pathList) {
|
||||||
|
monitor.checkCancelled();
|
||||||
|
path = path.trim();
|
||||||
|
FSRL fsrl = null;
|
||||||
|
try {
|
||||||
|
fsrl = FSRL.fromString(path);
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
try {
|
||||||
|
File f = new File(path);
|
||||||
|
if (f.exists() && f.isAbsolute()) {
|
||||||
|
fsrl = fsService.getLocalFSRL(f.getCanonicalFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e2) {
|
||||||
|
log.appendException(e2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fsrl != null) {
|
||||||
|
fsrlList.add(fsrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fsrlList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,9 +17,11 @@ package ghidra.app.util.opinion;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.InvalidPathException;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
@ -584,16 +586,13 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
if (!success) {
|
if (!success) {
|
||||||
release(loadedPrograms, consumer);
|
release(loadedPrograms, consumer);
|
||||||
}
|
}
|
||||||
for (FileSystemSearchPath fsSearchPath : localSearchPaths) {
|
Stream.of(customSearchPaths, localSearchPaths, systemSearchPaths)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.forEach(fsSearchPath -> {
|
||||||
if (!fsSearchPath.fsRef().isClosed()) {
|
if (!fsSearchPath.fsRef().isClosed()) {
|
||||||
fsSearchPath.fsRef().close();
|
fsSearchPath.fsRef().close();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
for (FileSystemSearchPath fsSearchPath : systemSearchPaths) {
|
|
||||||
if (!fsSearchPath.fsRef().isClosed()) {
|
|
||||||
fsSearchPath.fsRef().close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,9 +1060,10 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @return A {@link List} of priority-ordered custom {@link FileSystemSearchPath}s used to
|
* @return A {@link List} of priority-ordered custom {@link FileSystemSearchPath}s used to
|
||||||
* search for libraries
|
* search for libraries
|
||||||
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
protected List<FileSystemSearchPath> getCustomLibrarySearchPaths(ByteProvider provider,
|
protected List<FileSystemSearchPath> getCustomLibrarySearchPaths(ByteProvider provider,
|
||||||
List<Option> options, MessageLog log, TaskMonitor monitor) {
|
List<Option> options, MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1077,24 +1077,26 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @return A {@link List} of priority-ordered local {@link FileSystemSearchPath}s used to
|
* @return A {@link List} of priority-ordered local {@link FileSystemSearchPath}s used to
|
||||||
* search for libraries
|
* search for libraries
|
||||||
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
private List<FileSystemSearchPath> getLocalLibrarySearchPaths(ByteProvider provider,
|
private List<FileSystemSearchPath> getLocalLibrarySearchPaths(ByteProvider provider,
|
||||||
List<Option> options, MessageLog log, TaskMonitor monitor) {
|
List<Option> options, MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||||
|
if (!isLoadLocalLibraries(options) && !shouldSearchAllPaths(options)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
List<FileSystemSearchPath> result = new ArrayList<>();
|
List<FileSystemSearchPath> result = new ArrayList<>();
|
||||||
FileSystemService fsService = FileSystemService.getInstance();
|
FileSystemService fsService = FileSystemService.getInstance();
|
||||||
if (isLoadLocalLibraries(options) || shouldSearchAllPaths(options)) {
|
|
||||||
FSRL providerFsrl = provider.getFSRL();
|
FSRL providerFsrl = provider.getFSRL();
|
||||||
if (providerFsrl != null) {
|
if (providerFsrl != null) {
|
||||||
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
|
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
|
||||||
GFile parentFile = fileRef.file.getParentFile();
|
GFile parentFile = fileRef.file.getParentFile();
|
||||||
File f = new File(parentFile.getPath()); // File API will sanitize Windows-style paths
|
File f = new File(parentFile.getPath()); // File API will sanitize Windows-style paths
|
||||||
result.add(new FileSystemSearchPath(fileRef.fsRef, f.toPath()));
|
result.add(new FileSystemSearchPath(fileRef.fsRef.dup(), f.toPath()));
|
||||||
}
|
}
|
||||||
catch (IOException | CancelledException e) {
|
catch (IOException e) {
|
||||||
log.appendException(e);
|
log.appendException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1107,41 +1109,32 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* @param monitor A cancelable task monitor
|
* @param monitor A cancelable task monitor
|
||||||
* @return A {@link List} of priority-ordered system {@link FileSystemSearchPath}s used to
|
* @return A {@link List} of priority-ordered system {@link FileSystemSearchPath}s used to
|
||||||
* search for libraries
|
* search for libraries
|
||||||
|
* @throws CancelledException if the user cancelled the load
|
||||||
*/
|
*/
|
||||||
private List<FileSystemSearchPath> getSystemLibrarySearchPaths(List<Option> options,
|
private List<FileSystemSearchPath> getSystemLibrarySearchPaths(List<Option> options,
|
||||||
MessageLog log, TaskMonitor monitor) {
|
MessageLog log, TaskMonitor monitor) throws CancelledException {
|
||||||
List<FileSystemSearchPath> result = new ArrayList<>();
|
if (!isLoadSystemLibraries(options) && !shouldSearchAllPaths(options)) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
FileSystemService fsService = FileSystemService.getInstance();
|
FileSystemService fsService = FileSystemService.getInstance();
|
||||||
if (isLoadSystemLibraries(options) || shouldSearchAllPaths(options)) {
|
List<FileSystemSearchPath> result = new ArrayList<>();
|
||||||
List<Path> searchPaths = new ArrayList<>();
|
boolean success = false;
|
||||||
for (String str : LibrarySearchPathManager.getLibraryPathsList()) {
|
|
||||||
str = str.trim();
|
|
||||||
if (str.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
Path path = getCheckedPath(str).normalize();
|
for (FSRL fsrl : LibrarySearchPathManager.getLibraryFsrlList(log, monitor)) {
|
||||||
if (path.isAbsolute() && Files.exists(path)) {
|
try (RefdFile fileRef = fsService.getRefdFile(fsrl, monitor)) {
|
||||||
searchPaths.add(path);
|
File f = new File(fileRef.file.getPath()); // File API will sanitize Windows-style paths
|
||||||
|
result.add(new FileSystemSearchPath(fileRef.fsRef.dup(), f.toPath()));
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
log.appendMsg(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InvalidInputException e) {
|
success = true;
|
||||||
log.appendMsg("Skipping invalid system library search path: \"" + str + "\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Path searchPath : searchPaths) {
|
|
||||||
try {
|
|
||||||
FSRL searchFSRL =
|
|
||||||
fsService.getLocalFSRL(searchPath.toFile().getCanonicalFile());
|
|
||||||
FileSystemRef fsRef =
|
|
||||||
fsService.probeFileForFilesystem(searchFSRL, monitor, null);
|
|
||||||
if (fsRef != null) {
|
|
||||||
result.add(new FileSystemSearchPath(fsRef, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException | CancelledException e) {
|
|
||||||
log.appendException(e);
|
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
if (!success) {
|
||||||
|
result.forEach(fsSearchPath -> fsSearchPath.fsRef().close());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1205,7 +1198,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
|
||||||
* case-insensitive lookup may be allowed, and filename extensions may be optional.
|
* case-insensitive lookup may be allowed, and filename extensions may be optional.
|
||||||
*
|
*
|
||||||
* @param fs The {@link GFileSystem file system} to resolve in
|
* @param fs The {@link GFileSystem file system} to resolve in
|
||||||
* @param libraryParentPath The {@link Path} of the libraries parent directory, relative to the
|
* @param libraryParentPath The {@link Path} of the library's parent directory, relative to the
|
||||||
* given file system (could be null)
|
* given file system (could be null)
|
||||||
* @param libraryName The library name
|
* @param libraryName The library name
|
||||||
* @return The library resolved to an existing {@link FSRL}, or null if it did not resolve
|
* @return The library resolved to an existing {@link FSRL}, or null if it did not resolve
|
||||||
|
|
|
@ -40,6 +40,7 @@ import docking.widgets.tree.GTreeNode;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.app.services.TextEditorService;
|
import ghidra.app.services.TextEditorService;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.app.util.importer.LibrarySearchPathManager;
|
||||||
import ghidra.formats.gfilesystem.*;
|
import ghidra.formats.gfilesystem.*;
|
||||||
import ghidra.formats.gfilesystem.crypto.CachedPasswordProvider;
|
import ghidra.formats.gfilesystem.crypto.CachedPasswordProvider;
|
||||||
import ghidra.formats.gfilesystem.crypto.CryptoProviders;
|
import ghidra.formats.gfilesystem.crypto.CryptoProviders;
|
||||||
|
@ -83,6 +84,7 @@ class FSBActionManager {
|
||||||
DockingAction actionCollapse;
|
DockingAction actionCollapse;
|
||||||
DockingAction actionImportBatch;
|
DockingAction actionImportBatch;
|
||||||
DockingAction actionAddToProgram;
|
DockingAction actionAddToProgram;
|
||||||
|
DockingAction actionLibrarySearchPath;
|
||||||
DockingAction actionCloseFileSystem;
|
DockingAction actionCloseFileSystem;
|
||||||
DockingAction actionClearCachedPasswords;
|
DockingAction actionClearCachedPasswords;
|
||||||
/* end package visibility */
|
/* end package visibility */
|
||||||
|
@ -118,6 +120,7 @@ class FSBActionManager {
|
||||||
actions.add((actionImport = createImportAction()));
|
actions.add((actionImport = createImportAction()));
|
||||||
actions.add((actionImportBatch = createBatchImportAction()));
|
actions.add((actionImportBatch = createBatchImportAction()));
|
||||||
actions.add((actionAddToProgram = createAddToProgramAction()));
|
actions.add((actionAddToProgram = createAddToProgramAction()));
|
||||||
|
actions.add((actionLibrarySearchPath = createLibrarySearchPathAction()));
|
||||||
actions.add((actionOpenFileSystemNewWindow = createOpenFileSystemNewWindowAction()));
|
actions.add((actionOpenFileSystemNewWindow = createOpenFileSystemNewWindowAction()));
|
||||||
actions.add((actionOpenFileSystemNested = createOpenFileSystemNestedAction()));
|
actions.add((actionOpenFileSystemNested = createOpenFileSystemNestedAction()));
|
||||||
actions.add((actionOpenFileSystemChooser = createOpenNewFileSystemAction()));
|
actions.add((actionOpenFileSystemChooser = createOpenNewFileSystemAction()));
|
||||||
|
@ -516,11 +519,11 @@ class FSBActionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private DockingAction createGetInfoAction() {
|
private DockingAction createGetInfoAction() {
|
||||||
return new ActionBuilder("Get Info", plugin.getName())
|
return new ActionBuilder("FSB Get Info", plugin.getName())
|
||||||
.withContext(FSBActionContext.class)
|
.withContext(FSBActionContext.class)
|
||||||
.enabledWhen(ac -> ac.notBusy() && ac.getFSRL(true) != null)
|
.enabledWhen(ac -> ac.notBusy() && ac.getFSRL(true) != null)
|
||||||
.popupMenuPath("Get Info")
|
.popupMenuPath("Get Info")
|
||||||
.popupMenuGroup("A")
|
.popupMenuGroup("A", "A")
|
||||||
.popupMenuIcon(ImageManager.INFO)
|
.popupMenuIcon(ImageManager.INFO)
|
||||||
.description("Show information about a file")
|
.description("Show information about a file")
|
||||||
.onAction(
|
.onAction(
|
||||||
|
@ -742,6 +745,36 @@ class FSBActionManager {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DockingAction createLibrarySearchPathAction() {
|
||||||
|
return new ActionBuilder("FSB Add Library Search Path", plugin.getName())
|
||||||
|
.withContext(FSBActionContext.class)
|
||||||
|
.enabledWhen(ac -> ac.notBusy() && ac.getFSRL(true) != null)
|
||||||
|
.popupMenuPath("Add Library Search Path")
|
||||||
|
.popupMenuGroup("F", "D")
|
||||||
|
.popupMenuIcon(ImageManager.LIBRARY)
|
||||||
|
.description("Add file/folder to library search paths")
|
||||||
|
.onAction(ac -> {
|
||||||
|
try {
|
||||||
|
FSRL fsrl = ac.getFSRL(true);
|
||||||
|
LocalFileSystem localFs = fsService.getLocalFS();
|
||||||
|
String path = fsService.isLocal(fsrl) ? localFs.getLocalFile(fsrl).getPath()
|
||||||
|
: fsrl.toString();
|
||||||
|
if (LibrarySearchPathManager.addPath(path)) {
|
||||||
|
Msg.showInfo(this, gTree, "Add Library Search Path",
|
||||||
|
"Added '%s' to library search paths.".formatted(fsrl));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.showInfo(this, gTree, "Add Library Search Path",
|
||||||
|
"Library search path '%s' already exists.".formatted(fsrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Msg.showError(this, gTree, "Add Library Search Path", e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private DockingAction createClearCachedPasswordsAction() {
|
private DockingAction createClearCachedPasswordsAction() {
|
||||||
return new ActionBuilder("FSB Clear Cached Passwords", plugin.getName())
|
return new ActionBuilder("FSB Clear Cached Passwords", plugin.getName())
|
||||||
.withContext(FSBActionContext.class)
|
.withContext(FSBActionContext.class)
|
||||||
|
|
|
@ -61,5 +61,6 @@ public class ImageManager {
|
||||||
public final static Icon iOS = new GIcon("icon.plugin.fsbrowser.ios");
|
public final static Icon iOS = new GIcon("icon.plugin.fsbrowser.ios");
|
||||||
public final static Icon OPEN_ALL = new GIcon("icon.plugin.fsbrowser.open.all");
|
public final static Icon OPEN_ALL = new GIcon("icon.plugin.fsbrowser.open.all");
|
||||||
public final static Icon LIST_MOUNTED = new GIcon("icon.plugin.fsbrowser.list.mounted");
|
public final static Icon LIST_MOUNTED = new GIcon("icon.plugin.fsbrowser.list.mounted");
|
||||||
|
public final static Icon LIBRARY = new GIcon("icon.plugin.fsbrowser.library");
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,8 +278,10 @@ public class PathnameTablePanel extends JPanel {
|
||||||
pathName = "";
|
pathName = "";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
File file = new File(pathName);
|
int colonSlashSlash = pathName.indexOf("://");
|
||||||
fileExists = file.exists();
|
if (colonSlashSlash <= 0) { // Assume FSRL/URLs always exist
|
||||||
|
fileExists = new File(pathName).exists();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label.setText(pathName.toString());
|
label.setText(pathName.toString());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue