Skip to content

Commit

Permalink
GP-4563: Support for searching for libraries inside a GFileSystem
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanmkurtz committed May 16, 2024
1 parent 9911db9 commit 9f88302
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 62 deletions.
1 change: 1 addition & 0 deletions Ghidra/Features/Base/data/base.icons.theme.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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.open.all = images/famfamfam_silk_icons_v013/application_cascade.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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ <H3><A name="FSB_Import_Batch"></A>Batch Import</H3>
using the Batch Import dialog.</P>
</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>

<BLOCKQUOTE>
Expand All @@ -103,6 +109,13 @@ <H3><A name="FSB_Export_All"></A>Export All</H3>
<P>Recursively copies the contents of a selected folder to a directory you select on your
local computer.</P>
</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>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@
*/
package ghidra.app.util.importer;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.*;

import ghidra.formats.gfilesystem.FSRL;
import ghidra.formats.gfilesystem.FileSystemService;
import ghidra.framework.Platform;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;

/**
* A simple class for managing the library search path
Expand Down Expand Up @@ -61,11 +68,39 @@ public static String[] getLibraryPaths() {
}

/**
* Returns an array of directories to search for libraries
* @return a list of directories to search for libraries
* Returns a {@link List} of {@link FSRL}s 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() {
return new ArrayList<>(pathList);
public static List<FSRL> getLibraryFsrlList(MessageLog log, TaskMonitor monitor)
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;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ObjectUtils;
Expand Down Expand Up @@ -584,16 +586,13 @@ else if (!customSearchPaths.isEmpty() || !localSearchPaths.isEmpty() ||
if (!success) {
release(loadedPrograms, consumer);
}
for (FileSystemSearchPath fsSearchPath : localSearchPaths) {
if (!fsSearchPath.fsRef().isClosed()) {
fsSearchPath.fsRef().close();
}
}
for (FileSystemSearchPath fsSearchPath : systemSearchPaths) {
if (!fsSearchPath.fsRef().isClosed()) {
fsSearchPath.fsRef().close();
}
}
Stream.of(customSearchPaths, localSearchPaths, systemSearchPaths)
.flatMap(Collection::stream)
.forEach(fsSearchPath -> {
if (!fsSearchPath.fsRef().isClosed()) {
fsSearchPath.fsRef().close();
}
});
}
}

Expand Down Expand Up @@ -1061,9 +1060,10 @@ protected record FileSystemSearchPath(FileSystemRef fsRef, Path fsPath) {}
* @param monitor A cancelable task monitor
* @return A {@link List} of priority-ordered custom {@link FileSystemSearchPath}s used to
* search for libraries
* @throws CancelledException if the user cancelled the load
*/
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();
}

Expand All @@ -1077,22 +1077,24 @@ protected List<FileSystemSearchPath> getCustomLibrarySearchPaths(ByteProvider pr
* @param monitor A cancelable task monitor
* @return A {@link List} of priority-ordered local {@link FileSystemSearchPath}s used to
* search for libraries
* @throws CancelledException if the user cancelled the load
*/
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<>();
FileSystemService fsService = FileSystemService.getInstance();
if (isLoadLocalLibraries(options) || shouldSearchAllPaths(options)) {
FSRL providerFsrl = provider.getFSRL();
if (providerFsrl != null) {
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
GFile parentFile = fileRef.file.getParentFile();
File f = new File(parentFile.getPath()); // File API will sanitize Windows-style paths
result.add(new FileSystemSearchPath(fileRef.fsRef, f.toPath()));
}
catch (IOException | CancelledException e) {
log.appendException(e);
}
FSRL providerFsrl = provider.getFSRL();
if (providerFsrl != null) {
try (RefdFile fileRef = fsService.getRefdFile(providerFsrl, monitor)) {
GFile parentFile = fileRef.file.getParentFile();
File f = new File(parentFile.getPath()); // File API will sanitize Windows-style paths
result.add(new FileSystemSearchPath(fileRef.fsRef.dup(), f.toPath()));
}
catch (IOException e) {
log.appendException(e);
}
}
return result;
Expand All @@ -1107,41 +1109,32 @@ private List<FileSystemSearchPath> getLocalLibrarySearchPaths(ByteProvider provi
* @param monitor A cancelable task monitor
* @return A {@link List} of priority-ordered system {@link FileSystemSearchPath}s used to
* search for libraries
* @throws CancelledException if the user cancelled the load
*/
private List<FileSystemSearchPath> getSystemLibrarySearchPaths(List<Option> options,
MessageLog log, TaskMonitor monitor) {
List<FileSystemSearchPath> result = new ArrayList<>();
MessageLog log, TaskMonitor monitor) throws CancelledException {
if (!isLoadSystemLibraries(options) && !shouldSearchAllPaths(options)) {
return List.of();
}

FileSystemService fsService = FileSystemService.getInstance();
if (isLoadSystemLibraries(options) || shouldSearchAllPaths(options)) {
List<Path> searchPaths = new ArrayList<>();
for (String str : LibrarySearchPathManager.getLibraryPathsList()) {
str = str.trim();
if (str.isEmpty()) {
continue;
}
try {
Path path = getCheckedPath(str).normalize();
if (path.isAbsolute() && Files.exists(path)) {
searchPaths.add(path);
}
List<FileSystemSearchPath> result = new ArrayList<>();
boolean success = false;
try {
for (FSRL fsrl : LibrarySearchPathManager.getLibraryFsrlList(log, monitor)) {
try (RefdFile fileRef = fsService.getRefdFile(fsrl, monitor)) {
File f = new File(fileRef.file.getPath()); // File API will sanitize Windows-style paths
result.add(new FileSystemSearchPath(fileRef.fsRef.dup(), f.toPath()));
}
catch (InvalidInputException e) {
log.appendMsg("Skipping invalid system library search path: \"" + str + "\"");
catch (IOException e) {
log.appendMsg(e.getMessage());
}
}
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);
}
success = true;
}
finally {
if (!success) {
result.forEach(fsSearchPath -> fsSearchPath.fsRef().close());
}
}
return result;
Expand Down Expand Up @@ -1205,7 +1198,7 @@ protected LoadSpec matchSupportedLoadSpec(LoadSpec desiredLoadSpec, ByteProvider
* case-insensitive lookup may be allowed, and filename extensions may be optional.
*
* @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)
* @param libraryName The library name
* @return The library resolved to an existing {@link FSRL}, or null if it did not resolve
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import ghidra.app.services.ProgramManager;
import ghidra.app.services.TextEditorService;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.importer.LibrarySearchPathManager;
import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.crypto.CachedPasswordProvider;
import ghidra.formats.gfilesystem.crypto.CryptoProviders;
Expand Down Expand Up @@ -83,6 +84,7 @@ class FSBActionManager {
DockingAction actionCollapse;
DockingAction actionImportBatch;
DockingAction actionAddToProgram;
DockingAction actionLibrarySearchPath;
DockingAction actionCloseFileSystem;
DockingAction actionClearCachedPasswords;
/* end package visibility */
Expand Down Expand Up @@ -118,6 +120,7 @@ private void createActions() {
actions.add((actionImport = createImportAction()));
actions.add((actionImportBatch = createBatchImportAction()));
actions.add((actionAddToProgram = createAddToProgramAction()));
actions.add((actionLibrarySearchPath = createLibrarySearchPathAction()));
actions.add((actionOpenFileSystemNewWindow = createOpenFileSystemNewWindowAction()));
actions.add((actionOpenFileSystemNested = createOpenFileSystemNestedAction()));
actions.add((actionOpenFileSystemChooser = createOpenNewFileSystemAction()));
Expand Down Expand Up @@ -516,11 +519,11 @@ private DockingAction createCollapseAllAction() {
}

private DockingAction createGetInfoAction() {
return new ActionBuilder("Get Info", plugin.getName())
return new ActionBuilder("FSB Get Info", plugin.getName())
.withContext(FSBActionContext.class)
.enabledWhen(ac -> ac.notBusy() && ac.getFSRL(true) != null)
.popupMenuPath("Get Info")
.popupMenuGroup("A")
.popupMenuGroup("A", "A")
.popupMenuIcon(ImageManager.INFO)
.description("Show information about a file")
.onAction(
Expand Down Expand Up @@ -742,6 +745,36 @@ private DockingAction createAddToProgramAction() {
.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() {
return new ActionBuilder("FSB Clear Cached Passwords", plugin.getName())
.withContext(FSBActionContext.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@ public class ImageManager {
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 LIST_MOUNTED = new GIcon("icon.plugin.fsbrowser.list.mounted");
public final static Icon LIBRARY = new GIcon("icon.plugin.fsbrowser.library");
//@formatter:on
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,10 @@ public Component getTableCellRendererComponent(GTableCellRenderingData data) {
pathName = "";
}
else {
File file = new File(pathName);
fileExists = file.exists();
int colonSlashSlash = pathName.indexOf(":https://");
if (colonSlashSlash <= 0) { // Assume FSRL/URLs always exist
fileExists = new File(pathName).exists();
}
}

label.setText(pathName.toString());
Expand Down

0 comments on commit 9f88302

Please sign in to comment.