GP-4563: Support for searching for libraries inside a GFileSystem

This commit is contained in:
Ryan Kurtz 2024-05-16 06:43:27 -04:00
parent 9911db9828
commit 9f883022a4
7 changed files with 140 additions and 62 deletions

View file

@ -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

View file

@ -90,6 +90,12 @@
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>
@ -104,6 +110,13 @@
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>
<BLOCKQUOTE>

View file

@ -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
@ -61,11 +68,39 @@ public class LibrarySearchPathManager {
}
/**
* 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;
}
/**

View file

@ -17,9 +17,11 @@ package ghidra.app.util.opinion;
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;
@ -584,16 +586,13 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
if (!success) {
release(loadedPrograms, consumer);
}
for (FileSystemSearchPath fsSearchPath : localSearchPaths) {
Stream.of(customSearchPaths, localSearchPaths, systemSearchPaths)
.flatMap(Collection::stream)
.forEach(fsSearchPath -> {
if (!fsSearchPath.fsRef().isClosed()) {
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
* @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();
}
@ -1077,24 +1077,26 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* @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()));
result.add(new FileSystemSearchPath(fileRef.fsRef.dup(), f.toPath()));
}
catch (IOException | CancelledException e) {
catch (IOException e) {
log.appendException(e);
}
}
}
return result;
}
@ -1107,41 +1109,32 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* @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;
}
List<FileSystemSearchPath> result = new ArrayList<>();
boolean success = false;
try {
Path path = getCheckedPath(str).normalize();
if (path.isAbsolute() && Files.exists(path)) {
searchPaths.add(path);
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 (IOException e) {
log.appendMsg(e.getMessage());
}
}
catch (InvalidInputException e) {
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);
success = true;
}
finally {
if (!success) {
result.forEach(fsSearchPath -> fsSearchPath.fsRef().close());
}
}
return result;
@ -1205,7 +1198,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* 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

View file

@ -40,6 +40,7 @@ import docking.widgets.tree.GTreeNode;
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;
@ -83,6 +84,7 @@ class FSBActionManager {
DockingAction actionCollapse;
DockingAction actionImportBatch;
DockingAction actionAddToProgram;
DockingAction actionLibrarySearchPath;
DockingAction actionCloseFileSystem;
DockingAction actionClearCachedPasswords;
/* end package visibility */
@ -118,6 +120,7 @@ class FSBActionManager {
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()));
@ -516,11 +519,11 @@ class FSBActionManager {
}
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(
@ -742,6 +745,36 @@ class FSBActionManager {
.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)

View file

@ -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
}

View file

@ -278,8 +278,10 @@ public class PathnameTablePanel extends JPanel {
pathName = "";
}
else {
File file = new File(pathName);
fileExists = file.exists();
int colonSlashSlash = pathName.indexOf("://");
if (colonSlashSlash <= 0) { // Assume FSRL/URLs always exist
fileExists = new File(pathName).exists();
}
}
label.setText(pathName.toString());