diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractLibrarySupportLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractLibrarySupportLoader.java
index 07d22f2e12..6bc3daf4ff 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractLibrarySupportLoader.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/AbstractLibrarySupportLoader.java
@@ -15,9 +15,7 @@
*/
package ghidra.app.util.opinion;
-import java.io.File;
import java.io.IOException;
-import java.nio.file.Path;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -712,7 +710,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
Program libraryProgram = null;
String simpleLibraryName = FilenameUtils.getName(library);
- boolean isAbsolute = new File(library).isAbsolute();
+ boolean isAbsolute = isAbsoluteLibraryPath(library);
boolean success = false;
try {
@@ -847,13 +845,14 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
try {
for (LibrarySearchPath searchPath : searchPaths) {
monitor.checkCancelled();
- String fullLibraryPath = joinPaths(searchPath.relativeFsPath(), library);
+ String fullLibraryPath =
+ FSUtilities.appendPath(searchPath.relativeFsPath(), library);
GFileSystem fs = searchPath.fsRef().getFilesystem();
FSRL fsrl = resolveLibraryFile(fs, fullLibraryPath);
Optional.ofNullable(fsrl).ifPresent(results::add);
}
- if (results.isEmpty() && new File(library).isAbsolute()) {
+ if (results.isEmpty() && isAbsoluteLibraryPath(library)) {
LocalFileSystem localFS = FileSystemService.getInstance().getLocalFS();
FSRL fsrl = resolveLibraryFile(localFS, library);
Optional.ofNullable(fsrl).ifPresent(results::add);
@@ -1054,7 +1053,7 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
* A library search path
*
* @param fsRef The root {@link FileSystemRef}
- * @param relativeFsPath A {@link Path} relative to the root of the file system, or null for the
+ * @param relativeFsPath string path, relative to the root of the file system, or null for the
* root
*/
protected record LibrarySearchPath(FileSystemRef fsRef, String relativeFsPath) {}
@@ -1122,28 +1121,14 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
continue;
}
- if (fsService.isLocal(fsrl)) {
- try {
- FileSystemRef fileRef =
- fsService.probeFileForFilesystem(fsrl, monitor, null);
- if (fileRef != null) {
- result.add(new LibrarySearchPath(fileRef, null));
- }
- }
- catch (IOException e) {
- log.appendMsg(e.getMessage());
+ try (RefdFile fileRef = fsService.getRefdFile(fsrl, monitor)) {
+ if (fileRef != null) {
+ result.add(
+ new LibrarySearchPath(fileRef.fsRef.dup(), fileRef.file.getPath()));
}
}
- else {
- try (RefdFile fileRef = fsService.getRefdFile(fsrl, monitor)) {
- if (fileRef != null) {
- File f = new File(fileRef.file.getPath()); // File API will sanitize Windows-style paths
- result.add(new LibrarySearchPath(fileRef.fsRef.dup(), f.getPath()));
- }
- }
- catch (IOException e) {
- log.appendMsg(e.getMessage());
- }
+ catch (IOException e) {
+ log.appendMsg(e.getMessage());
}
}
success = true;
@@ -1242,4 +1227,14 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
? String.CASE_INSENSITIVE_ORDER
: (s1, s2) -> s1.compareTo(s2);
}
+
+ /**
+ * Performs a platform-independent test to see if the given path is absolute
+ *
+ * @param path The path to test
+ * @return True if the given path is absolute; otherwise, false
+ */
+ private boolean isAbsoluteLibraryPath(String path) {
+ return FilenameUtils.getPrefixLength(path) > 0;
+ }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemService.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemService.java
index a8c1d57c72..a7afa5919c 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemService.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemService.java
@@ -709,13 +709,6 @@ public class FileSystemService {
if (ref != null) {
return ref;
}
-
- GFileSystem subdirFS = probeForLocalSubDirFilesystem(containerFSRL);
- if (subdirFS != null) {
- ref = subdirFS.getRefManager().create();
- fsInstanceManager.add(subdirFS);
- return ref;
- }
}
// Normal case, probe the container file and create a filesystem instance.
@@ -748,18 +741,6 @@ public class FileSystemService {
return null;
}
- private GFileSystem probeForLocalSubDirFilesystem(FSRL containerFSRL) {
- if (localFS.isLocalSubdir(containerFSRL)) {
- try {
- return localFS.getSubFileSystem(containerFSRL);
- }
- catch (IOException e) {
- Msg.error(this, "Problem when probing for local directory: ", e);
- }
- }
- return null;
- }
-
/**
* Mount a specific file system (by class) using a specified container file.
*
@@ -815,11 +796,6 @@ public class FileSystemService {
public GFileSystem openFileSystemContainer(FSRL containerFSRL, TaskMonitor monitor)
throws CancelledException, IOException {
- GFileSystem subdirFS = probeForLocalSubDirFilesystem(containerFSRL);
- if (subdirFS != null) {
- return subdirFS;
- }
-
ByteProvider byteProvider = getByteProvider(containerFSRL, true, monitor);
return fsFactoryMgr.probe(byteProvider, this, null, FileSystemInfo.PRIORITY_LOWEST,
monitor);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileLocal.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileLocal.java
deleted file mode 100644
index 8a7f77b121..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileLocal.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/* ###
- * 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.formats.gfilesystem;
-
-import java.io.File;
-import java.util.Objects;
-
-/**
- * {@link GFile} implementation that refers to a real java.io.File on the local
- * file system.
- *
- * This implementation keeps track of the FSRL and GFile path separately so that
- * they can be different, as is the case with LocalFileSystemSub files that
- * have real FSRLs but fake relative paths.
- */
-public class GFileLocal implements GFile {
-
- private GFileSystem fs;
- private FSRL fsrl;
- private String path;
- private File f;
- private GFile parent;
-
- /**
- * Create new GFileLocal instance.
- *
- * @param f {@link File} on the local filesystem
- * @param path String path (including filename) of this instance
- * @param fsrl {@link FSRL} of this instance
- * @param fs {@link GFileSystem} that created this file.
- * @param parent Parent directory that contains this file, or null if parent is root.
- */
- public GFileLocal(File f, String path, FSRL fsrl, GFileSystem fs, GFile parent) {
- this.fs = fs;
- this.fsrl = fsrl;
- this.path = path;
- this.f = f;
- this.parent = parent;
- }
-
- @Override
- public GFileSystem getFilesystem() {
- return fs;
- }
-
- @Override
- public FSRL getFSRL() {
- return fsrl;
- }
-
- @Override
- public GFile getParentFile() {
- return parent;
- }
-
- @Override
- public String getPath() {
- return path;
- }
-
- @Override
- public String getName() {
- return fsrl.getName();
- }
-
- @Override
- public boolean isDirectory() {
- return f.isDirectory();
- }
-
- @Override
- public long getLength() {
- return f.length();
- }
-
- public File getLocalFile() {
- return f;
- }
-
- @Override
- public String toString() {
- return "Local " + f.toString() + " with path " + path;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(f, fs, fsrl, parent, path);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof GFileLocal)) {
- return false;
- }
- GFileLocal other = (GFileLocal) obj;
- return Objects.equals(f, other.f) && Objects.equals(fs, other.fs) &&
- Objects.equals(fsrl, other.fsrl) && Objects.equals(parent, other.parent) &&
- Objects.equals(path, other.path);
- }
-
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystem.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystem.java
index ac7a9bcd3a..00b110f2c3 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystem.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystem.java
@@ -74,21 +74,6 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
return fsFSRL.equals(fsrl.getFS());
}
- /**
- * Creates a new file system instance that is a sub-view limited to the specified directory.
- *
- * @param fsrl {@link FSRL} that must be a directory in this local filesystem
- * @return new {@link LocalFileSystemSub} instance
- * @throws IOException if bad FSRL
- */
- public LocalFileSystemSub getSubFileSystem(FSRL fsrl) throws IOException {
- if (isLocalSubdir(fsrl)) {
- File localDir = getLocalFile(fsrl);
- return new LocalFileSystemSub(localDir, this);
- }
- return null;
- }
-
/**
* Returns true if the {@link FSRL} is a local filesystem subdirectory.
*
@@ -136,7 +121,7 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
return fsFSRL.withPath(fsrlPath);
}
- private GFile getGFile(File f) {
+ public GFile getGFile(File f) {
List parts = LocalFileSystem.getFilePathParts(f); // [/subdir/subroot/file, /subdir/subroot, /subdir, /]
GFile current = rootDir;
for (int i = parts.size() - 2; i >= 0; i--) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystemSub.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystemSub.java
deleted file mode 100644
index 745d78d4a5..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystemSub.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/* ###
- * 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.formats.gfilesystem;
-
-import java.io.*;
-import java.util.*;
-
-import ghidra.app.util.bin.ByteProvider;
-import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
-import ghidra.util.exception.CancelledException;
-import ghidra.util.task.TaskMonitor;
-
-/**
- * A {@link GFileSystem} interface to a part of the user's local / native file system.
- *
- * This class is a sub-view of the {@link LocalFileSystem}, and returns hybrid GFile objects
- * that have fully specified FSRL paths that are valid in the Root filesystem, but relative
- * GFile paths.
- *
- * This class's name doesn't end with "FileSystem" to ensure it will not be auto-discovered
- * by the FileSystemFactoryMgr.
- *
- */
-public class LocalFileSystemSub implements GFileSystem, GFileHashProvider {
- private final FSRLRoot fsFSRL;
- private final LocalFileSystem rootFS;
- private File localfsRootDir;
- private FileSystemRefManager refManager = new FileSystemRefManager(this);
- private GFileLocal rootGFile;
-
- public LocalFileSystemSub(File rootDir, LocalFileSystem rootFS) throws IOException {
- this.rootFS = rootFS;
- this.localfsRootDir = rootDir.getCanonicalFile();
-
- FSRL containerFSRL = rootFS.getLocalFSRL(localfsRootDir);
- this.fsFSRL = FSRLRoot.nestedFS(containerFSRL, rootFS.getFSRL().getProtocol());
- this.rootGFile = new GFileLocal(localfsRootDir, "/", containerFSRL, this, null);
- }
-
- @Override
- public String getType() {
- return rootFS.getType();
- }
-
- @Override
- public String getDescription() {
- return "Local filesystem subdirectory";
- }
-
- @Override
- public void close() {
- refManager.onClose();
- localfsRootDir = null;
- }
-
- @Override
- public boolean isClosed() {
- return localfsRootDir == null;
- }
-
- @Override
- public boolean isStatic() {
- return false;
- }
-
- private File getFileFromGFile(GFile gf) throws IOException {
- if (gf == null) {
- return localfsRootDir;
- }
- if (!(gf instanceof GFileLocal)) {
- throw new IOException("Unexpected GFile class: " + gf.getClass());
- }
- return ((GFileLocal) gf).getLocalFile();
- }
-
- @Override
- public List getListing(GFile directory) throws IOException {
- if (directory == null) {
- directory = rootGFile;
- }
- if (!directory.isDirectory()) {
- return List.of();
- }
- File localDir = getFileFromGFile(directory);
- if (FSUtilities.isSymlink(localDir)) {
- return List.of();
- }
-
- File[] localFiles = localDir.listFiles();
-
- if (localFiles == null) {
- return List.of();
- }
-
- List tmp = new ArrayList<>(localFiles.length);
- FSRL dirFSRL = directory.getFSRL();
- String relPath = directory.getPath(); // this is the clean relative path assigned to the dir GFile earlier
-
- for (File f : localFiles) {
- boolean isSymlink = FSUtilities.isSymlink(f); // check this manually to allow broken symlinks to appear in listing
- if (!(isSymlink || f.isFile() || f.isDirectory())) {
- // skip non-file things
- continue;
- }
- // construct a GFile with split personality... a relative GFile pathname but
- // an absolute FSRL path
- String name = f.getName();
- GFileLocal gf = new GFileLocal(f, FSUtilities.appendPath(relPath, name),
- dirFSRL.appendPath(name), this, directory);
- tmp.add(gf);
- }
- return tmp;
- }
-
- @Override
- public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
- try {
- File localFile = getFileFromGFile(file);
- return rootFS.getFileAttributes(localFile);
- }
- catch (IOException e) {
- // fail and return empty
- }
- return FileAttributes.EMPTY;
- }
-
- @Override
- public String getName() {
- return "Subdir " + localfsRootDir.getPath();
- }
-
- @Override
- public FSRLRoot getFSRL() {
- return fsFSRL;
- }
-
- @Override
- public GFile getRootDir() {
- return rootGFile;
- }
-
- @Override
- public GFile lookup(String path) throws IOException {
- return lookup(path, null);
- }
-
- @Override
- public GFile lookup(String path, Comparator nameComp) throws IOException {
- File f = LocalFileSystem.lookupFile(localfsRootDir, path, nameComp);
- if (f == null) {
- return null;
- }
- GFile result = getGFile(f);
- return result;
- }
-
- private GFile getGFile(File f) throws IOException {
- List parts = LocalFileSystem.getFilePathParts(f); // [/subdir/subroot/file, /subdir/subroot, /subdir, /]
- int rootDirIndex = findRootDirIndex(parts);
- if (rootDirIndex < 0) {
- throw new IOException("Invalid directory " + f);
- }
- GFile current = rootGFile;
- for (int i = rootDirIndex - 1; i >= 0; i--) {
- File part = parts.get(i);
- FSRL childFSRL = current.getFSRL().appendPath(part.getName());
- String childPath = FSUtilities.appendPath(current.getPath(), part.getName());
- current = new GFileLocal(part, childPath, childFSRL, this, current);
- }
- return current;
- }
-
- private int findRootDirIndex(List dirList) {
- for (int i = 0; i < dirList.size(); i++) {
- if (localfsRootDir.equals(dirList.get(i))) {
- return i;
- }
- }
- return -1;
- }
-
- @Override
- public InputStream getInputStream(GFile file, TaskMonitor monitor)
- throws IOException, CancelledException {
- return rootFS.getInputStream(file.getFSRL(), monitor);
- }
-
- @Override
- public FileSystemRefManager getRefManager() {
- return refManager;
- }
-
- @Override
- public String toString() {
- return getName();
- }
-
- @Override
- public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
- throws IOException, CancelledException {
- return rootFS.getByteProvider(file.getFSRL(), monitor);
- }
-
- @Override
- public String getMD5Hash(GFile file, boolean required, TaskMonitor monitor)
- throws CancelledException, IOException {
- return rootFS.getMD5Hash(file.getFSRL(), required, monitor);
- }
-
- @Override
- public GFile resolveSymlinks(GFile file) throws IOException {
- File f = getFileFromGFile(file);
- File canonicalFile = f.getCanonicalFile();
- if (f.equals(canonicalFile)) {
- return file;
- }
- return getGFile(canonicalFile);
- }
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBComponentProvider.java
index 5cfd1606d6..2b8eb4a1c4 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBComponentProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBComponentProvider.java
@@ -19,6 +19,7 @@ import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
import java.awt.Component;
import java.awt.event.MouseEvent;
+import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -42,6 +43,7 @@ import ghidra.app.services.ProgramManager;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
+import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.plugin.importer.ImporterUtilities;
@@ -81,20 +83,20 @@ public class FSBComponentProvider extends ComponentProviderAdapter
* ownership of the passed-in {@link FileSystemRef fsRef}.
*
* @param plugin parent plugin
- * @param fsRef {@link FileSystemRef} to a {@link GFileSystem}.
+ * @param rootDir {@link FileSystemRef} to a {@link GFileSystem} and a {@link GFile} root
*/
- public FSBComponentProvider(FileSystemBrowserPlugin plugin, FileSystemRef fsRef) {
- super(plugin.getTool(), getDescriptiveFSName(fsRef.getFilesystem()), plugin.getName());
+ public FSBComponentProvider(FileSystemBrowserPlugin plugin, RefdFile rootDir) {
+ super(plugin.getTool(), getDescriptiveFSName(rootDir), plugin.getName());
this.plugin = plugin;
- this.rootNode = new FSBRootNode(fsRef);
+ this.rootNode = new FSBRootNode(rootDir);
this.pm = plugin.getTool().getService(ProgramManager.class);
setTransient();
- setIcon(getFSIcon(fsRef.getFilesystem(), true, fsbIcons));
+ setIcon(getFSIcon(rootDir.fsRef.getFilesystem(), true, fsbIcons));
initTree();
- fsRef.getFilesystem().getRefManager().addListener(this);
+ rootDir.fsRef.getFilesystem().getRefManager().addListener(this);
initFileHandlers();
setHelpLocation(
@@ -150,8 +152,12 @@ public class FSBComponentProvider extends ComponentProviderAdapter
if (value instanceof FSBRootNode) {
// do nothing
}
- else if (value instanceof FSBDirNode) {
- // do nothing special, but exclude FSBFileNode
+ else if (value instanceof FSBDirNode dirNode) {
+ Icon currentIcon = getIcon();
+ Icon newIcon = dirNode.isSymlink()
+ ? FSBIcons.buildIcon(currentIcon, List.of(FSBIcons.LINK_OVERLAY_ICON))
+ : currentIcon;
+ setIcon(newIcon);
}
else if (value instanceof FSBFileNode fileNode) {
renderFile(fileNode, selected);
@@ -367,6 +373,9 @@ public class FSBComponentProvider extends ComponentProviderAdapter
}
public boolean ensureFileAccessable(FSRL fsrl, FSBNode node, TaskMonitor monitor) {
+ if (node instanceof FSBDirNode) {
+ return true;
+ }
FSBFileNode fileNode = (node instanceof FSBFileNode) ? (FSBFileNode) node : null;
@@ -399,9 +408,16 @@ public class FSBComponentProvider extends ComponentProviderAdapter
}
public boolean openFileSystem(FSBNode node, boolean nested) {
+ if (node instanceof FSBDirNode dirNode) {
+ plugin.createNewFileSystemBrowser(dirNode.getFSBRootNode().getFSRef().dup(),
+ dirNode.file, true);
+ return true;
+ }
+
if (!(node instanceof FSBFileNode fileNode) || fileNode.getFSRL() == null) {
return false;
}
+
FSRL fsrl = fileNode.getFSRL();
gTree.runTask(monitor -> {
if (!ensureFileAccessable(fsrl, fileNode, monitor)) {
@@ -437,7 +453,8 @@ public class FSBComponentProvider extends ComponentProviderAdapter
return;
}
- FSBRootNode nestedRootNode = new FSBRootNode(ref, modelFileNode);
+ RefdFile refdRootDir = new RefdFile(ref, ref.getFilesystem().getRootDir());
+ FSBRootNode nestedRootNode = new FSBRootNode(refdRootDir, modelFileNode);
int indexInParent = modelFileNode.getIndexInParent();
GTreeNode parent = modelFileNode.getParent();
@@ -453,7 +470,7 @@ public class FSBComponentProvider extends ComponentProviderAdapter
contextChanged();
}
else {
- plugin.createNewFileSystemBrowser(ref, true);
+ plugin.createNewFileSystemBrowser(ref, null, true);
}
});
return true;
@@ -577,6 +594,18 @@ public class FSBComponentProvider extends ComponentProviderAdapter
if (destNode != null) {
Swing.runLater(() -> gTree.setSelectedNodes(destNode));
}
+ else {
+ String msg = "Failed to go to %s (%s)".formatted(fileNode.symlinkDest,
+ destFile.getPath());
+ // front end tool doesn't show message when using setStatusInfo, but
+ // does display Msg.warn messages
+ if (tool instanceof FrontEndTool) {
+ Msg.warn(this, msg);
+ }
+ else {
+ tool.setStatusInfo(msg);
+ }
+ }
});
return;
}
@@ -592,15 +621,23 @@ public class FSBComponentProvider extends ComponentProviderAdapter
}
- static String getDescriptiveFSName(GFileSystem fs) {
- return fs instanceof LocalFileSystem ? "My Computer" : fs.getName();
+ static String getDescriptiveFSName(RefdFile rootFile) {
+ GFileSystem fs = rootFile.fsRef.getFilesystem();
+ GFile file = rootFile.file;
+ boolean isRootDir = file.getParentFile() == null;
+ if (fs instanceof LocalFileSystem) {
+ // use [new File(fsrl.path).getPath()] to transform fsrl formatted path back into
+ // current jvm OS specific string format
+ return isRootDir ? "My Computer" : new File(file.getPath()).getPath();
+ }
+ return fs.getName() + (!isRootDir ? " - " + file.getPath() : "");
}
static Icon getFSIcon(GFileSystem fs, boolean isRootNode, FSBIcons fsbIcons) {
List overlays = !isRootNode ? List.of(FSBIcons.FILESYSTEM_OVERLAY_ICON) : List.of();
FSRL container = fs.getFSRL().getContainer();
String containerName = container != null ? container.getName() : "/";
- Icon image = fs instanceof LocalFileSystem || fs instanceof LocalFileSystemSub
+ Icon image = fs instanceof LocalFileSystem
? FSBIcons.MY_COMPUTER
: fsbIcons.getIcon(containerName, overlays);
if (image == FSBIcons.DEFAULT_ICON) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBIcons.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBIcons.java
index 71d9badce0..187dbbd975 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBIcons.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBIcons.java
@@ -141,7 +141,7 @@ public class FSBIcons {
return buildIcon(DEFAULT_ICON, overlays);
}
- private Icon buildIcon(Icon base, List overlays) {
+ public static Icon buildIcon(Icon base, List overlays) {
if (overlays == null || overlays.isEmpty()) {
return base;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBRootNode.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBRootNode.java
index 3a7c3600c4..fded6f8da6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBRootNode.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FSBRootNode.java
@@ -38,30 +38,30 @@ import ghidra.util.task.TaskMonitor;
*/
public class FSBRootNode extends FSBNode {
- private FileSystemRef fsRef;
+ private RefdFile rootDir; // do not use RefdFile.close(), the FsRef will be extracted and closed manually
private FSBFileNode prevNode;
private FSBRootNode modelNode;
private boolean cryptoStatusUpdated;
private Icon icon;
- FSBRootNode(FileSystemRef fsRef) {
- this(fsRef, null);
+ FSBRootNode(RefdFile rootDir) {
+ this(rootDir, null);
}
- FSBRootNode(FileSystemRef fsRef, FSBFileNode prevNode) {
- super(FSBComponentProvider.getDescriptiveFSName(fsRef.getFilesystem()));
+ FSBRootNode(RefdFile rootDir, FSBFileNode prevNode) {
+ super(FSBComponentProvider.getDescriptiveFSName(rootDir));
- this.fsRef = fsRef;
+ this.rootDir = rootDir;
this.prevNode = prevNode;
this.modelNode = this;
- this.icon = FSBComponentProvider.getFSIcon(fsRef.getFilesystem(), prevNode == null,
+ this.icon = FSBComponentProvider.getFSIcon(rootDir.fsRef.getFilesystem(), prevNode == null,
FSBIcons.getInstance());
}
@Override
public GTreeNode clone() throws CloneNotSupportedException {
FSBRootNode clone = (FSBRootNode) super.clone();
- clone.fsRef = null; // stomp on the clone's fsRef to force it to use modelNode's fsRef
+ clone.rootDir = null; // stomp on the clone's fsRef to force it to use modelNode's fsRef
return clone;
}
@@ -103,19 +103,19 @@ public class FSBRootNode extends FSBNode {
@Override
public GFile getGFile() {
- return fsRef.getFilesystem().getRootDir();
+ return rootDir.file;
}
public FileSystemRef getFSRef() {
- return modelNode.fsRef;
+ return modelNode.rootDir.fsRef;
}
private void releaseFSRefIfModelNode() {
if (this != modelNode) {
return;
}
- FileSystemService.getInstance().releaseFileSystemImmediate(fsRef);
- fsRef = null;
+ FileSystemService.getInstance().releaseFileSystemImmediate(rootDir.fsRef);
+ rootDir = null;
}
@Override
@@ -142,10 +142,9 @@ public class FSBRootNode extends FSBNode {
@Override
public List generateChildren(TaskMonitor monitor) throws CancelledException {
- if (fsRef != null) {
+ if (rootDir != null) {
try {
- return FSBNode.createNodesFromFileList(fsRef.getFilesystem().getListing(null),
- monitor);
+ return FSBNode.createNodesFromFileList(rootDir.file.getListing(), monitor);
}
catch (IOException e) {
FSUtilities.displayException(this, null, "Error Opening File System",
@@ -157,15 +156,18 @@ public class FSBRootNode extends FSBNode {
@Override
public FSRL getFSRL() {
- return modelNode != null && modelNode.fsRef != null
- ? modelNode.fsRef.getFilesystem().getFSRL()
+ return modelNode != null && modelNode.rootDir != null
+ ? modelNode.rootDir.file.getFSRL()
: null;
}
public FSBNode getGFileFSBNode(GFile file, TaskMonitor monitor) {
List pathParts = splitGFilePath(file);
+ List rootPathParts = splitGFilePath(rootDir.file);
+ // TODO: ensure pathParts has a prefix that equals rootPathParts
FSBNode fileNode = this;
- for (int i = 1 /* skip root */; fileNode != null && i < pathParts.size(); i++) {
+ for (int i = rootPathParts.size() /* skip root */; fileNode != null &&
+ i < pathParts.size(); i++) {
try {
fileNode = fileNode.findMatchingNode(pathParts.get(i), monitor);
}
@@ -177,10 +179,11 @@ public class FSBRootNode extends FSBNode {
}
public FSRL getContainer() {
- // use the rootDir's FSRL to sidestep issue with LocalFileSystemSub's non-standard fsFSRL
- return fsRef != null
- ? fsRef.getFilesystem().getRootDir().getFSRL().getFS().getContainer()
- : null;
+ // allows the import of the file container of a filesystem image
+ if ( rootDir != null && rootDir.file.getParentFile() == null ) {
+ return rootDir.fsRef.getFilesystem().getFSRL().getContainer();
+ }
+ return null;
}
private List splitGFilePath(GFile f) {
@@ -193,8 +196,8 @@ public class FSBRootNode extends FSBNode {
}
public FSRL getProgramProviderFSRL(FSRL fsrl) {
- if (fsRef != null) {
- GFileSystem fs = fsRef.getFilesystem();
+ if (rootDir != null) {
+ GFileSystem fs = rootDir.fsRef.getFilesystem();
if (fs instanceof GFileSystemProgramProvider programProviderFS) {
try {
GFile gfile = fs.lookup(fsrl.getPath());
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FileSystemBrowserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FileSystemBrowserPlugin.java
index 3ff4a20cbb..a6b6a1cedb 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FileSystemBrowserPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/FileSystemBrowserPlugin.java
@@ -140,22 +140,28 @@ public class FileSystemBrowserPlugin extends Plugin
* method).
*
* @param fsRef {@link FileSystemRef} of open {@link GFileSystem}
+ * @param rootDir directory to use as the root of the filesystem's tree, or {@code null} to
+ * specify the filesystem's actual rootdir
* @param show boolean true if the new browser component should be shown
*/
- public void createNewFileSystemBrowser(FileSystemRef fsRef, boolean show) {
- Swing.runIfSwingOrRunLater(() -> doCreateNewFileSystemBrowser(fsRef, show));
+ public void createNewFileSystemBrowser(FileSystemRef fsRef, GFile rootDir, boolean show) {
+ if (rootDir == null) {
+ rootDir = fsRef.getFilesystem().getRootDir();
+ }
+ RefdFile refdRootDir = new RefdFile(fsRef, rootDir);
+ Swing.runIfSwingOrRunLater(() -> doCreateNewFileSystemBrowser(refdRootDir, show));
}
- private void doCreateNewFileSystemBrowser(FileSystemRef fsRef, boolean show) {
- FSRLRoot fsFSRL = fsRef.getFilesystem().getFSRL();
- FSBComponentProvider provider = currentBrowsers.get(fsFSRL);
+ private void doCreateNewFileSystemBrowser(RefdFile rootFile, boolean show) {
+ FSRL rootFSRL = rootFile.file.getFSRL();
+ FSBComponentProvider provider = currentBrowsers.get(rootFSRL);
if (provider != null) {
- Msg.info(this, "Filesystem browser already open for " + fsFSRL);
- fsRef.close();
+ Msg.info(this, "Filesystem browser already open for " + rootFSRL);
+ FSUtilities.uncheckedClose(rootFile, null);
}
else {
- provider = new FSBComponentProvider(this, fsRef);
- currentBrowsers.put(fsFSRL, provider);
+ provider = new FSBComponentProvider(this, rootFile);
+ currentBrowsers.put(rootFSRL, provider);
getTool().addComponentProvider(provider, false);
provider.afterAddedToTool();
provider.contextChanged();
@@ -201,32 +207,6 @@ public class FileSystemBrowserPlugin extends Plugin
chooserOpen.setLastDirectoryPreference(LAST_FS_DIR);
}
- /**
- * Worker function for doOpenFilesystem, meant to be called in a task thread.
- *
- * @param containerFSRL {@link FSRL} of the container to open
- * @param parent parent {@link Component} for error dialogs, null ok
- * @param monitor {@link TaskMonitor} to watch and update.
- */
- private void doOpenFilesystem(FSRL containerFSRL, Component parent, TaskMonitor monitor) {
- try {
- monitor.setMessage("Probing " + containerFSRL.getName() + " for filesystems");
- FileSystemRef ref = fsService().probeFileForFilesystem(containerFSRL, monitor,
- FileSystemProbeConflictResolver.GUI_PICKER);
- if (ref == null) {
- Msg.showWarn(this, parent, "Open Filesystem",
- "No filesystem provider for " + containerFSRL.getName());
- return;
- }
-
- createNewFileSystemBrowser(ref, true);
- }
- catch (IOException | CancelledException e) {
- FSUtilities.displayException(this, parent, "Open Filesystem Error",
- "Error opening filesystem for " + containerFSRL.getName(), e);
- }
- }
-
/**
* Prompts the user to pick a file system container file to open using a local
* filesystem browser and then displays that filesystem in a new fsb browser.
@@ -255,12 +235,45 @@ public class FileSystemBrowserPlugin extends Plugin
return;
}
- FSRL containerFSRL = fsService().getLocalFSRL(file);
+ LocalFileSystem localFS = fsService().getLocalFS();
+ if (file.isDirectory()) {
+ createNewFileSystemBrowser(localFS.getRefManager().create(), localFS.getGFile(file),
+ true);
+ return;
+ }
+
TaskLauncher.launchModal("Open File System", (monitor) -> {
+ FSRL containerFSRL = localFS.getLocalFSRL(file);
doOpenFilesystem(containerFSRL, parent, monitor);
});
}
+ /**
+ * Worker function for doOpenFilesystem, meant to be called in a task thread.
+ *
+ * @param containerFSRL {@link FSRL} of the container to open
+ * @param parent parent {@link Component} for error dialogs, null ok
+ * @param monitor {@link TaskMonitor} to watch and update.
+ */
+ private void doOpenFilesystem(FSRL containerFSRL, Component parent, TaskMonitor monitor) {
+ try {
+ monitor.setMessage("Probing " + containerFSRL.getName() + " for filesystems");
+ FileSystemRef ref = fsService().probeFileForFilesystem(containerFSRL, monitor,
+ FileSystemProbeConflictResolver.GUI_PICKER);
+ if (ref == null) {
+ Msg.showWarn(this, parent, "Open Filesystem",
+ "No filesystem provider for " + containerFSRL.getName());
+ return;
+ }
+
+ createNewFileSystemBrowser(ref, null, true);
+ }
+ catch (IOException | CancelledException e) {
+ FSUtilities.displayException(this, parent, "Open Filesystem Error",
+ "Error opening filesystem for " + containerFSRL.getName(), e);
+ }
+ }
+
private FileSystemService fsService() {
// use a delayed initialization so we don't force the FileSystemService to initialize
if (fsService == null) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/filehandlers/ListMountedFSBFileHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/filehandlers/ListMountedFSBFileHandler.java
index 1961485c41..6d78533eb8 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/filehandlers/ListMountedFSBFileHandler.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/filehandlers/ListMountedFSBFileHandler.java
@@ -4,9 +4,9 @@
* 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.
@@ -52,7 +52,9 @@ public class ListMountedFSBFileHandler implements FSBFileHandler {
FileSystemRef fsRef;
if (fsFSRL != null &&
(fsRef = context.fsService().getMountedFilesystem(fsFSRL)) != null) {
- context.fsbComponent().getPlugin().createNewFileSystemBrowser(fsRef, true);
+ context.fsbComponent()
+ .getPlugin()
+ .createNewFileSystemBrowser(fsRef, null, true);
}
})
.build());
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/filehandlers/OpenFsFSBFileHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/filehandlers/OpenFsFSBFileHandler.java
index a4ea7c07aa..f626b9ab61 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/filehandlers/OpenFsFSBFileHandler.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/fsbrowser/filehandlers/OpenFsFSBFileHandler.java
@@ -19,7 +19,6 @@ import java.util.List;
import docking.action.DockingAction;
import docking.action.builder.ActionBuilder;
-import ghidra.formats.gfilesystem.FileSystemRef;
import ghidra.formats.gfilesystem.FileSystemService;
import ghidra.plugins.fsbrowser.*;
@@ -29,6 +28,7 @@ public class OpenFsFSBFileHandler implements FSBFileHandler {
public static final String FSB_OPEN_FILE_SYSTEM_CHOOSER = "FSB Open File System Chooser";
public static final String FSB_OPEN_FILE_SYSTEM_IN_NEW_WINDOW =
"FSB Open File System In New Window";
+ public static final String FSB_OPEN_DIR_IN_NEW_WINDOW = "FSB Open Directory In New Window";
public static final String FSB_OPEN_FILE_SYSTEM_NESTED = "FSB Open File System Nested";
private FSBFileHandlerContext context;
@@ -59,7 +59,18 @@ public class OpenFsFSBFileHandler implements FSBFileHandler {
ac.getSelectedNode() instanceof FSBFileNode fileNode && fileNode.isLeaf() &&
!fileNode.isSymlink())
.popupMenuIcon(FSBIcons.OPEN_FILE_SYSTEM)
- .popupMenuPath("Open File System in new window")
+ .popupMenuPath("Open File System [new window]")
+ .popupMenuGroup("C")
+ .onAction(
+ ac -> ac.getComponentProvider().openFileSystem(ac.getSelectedNode(), false))
+ .build(),
+
+ new ActionBuilder(FSB_OPEN_DIR_IN_NEW_WINDOW, context.plugin().getName())
+ .withContext(FSBActionContext.class)
+ .enabledWhen(ac -> ac.notBusy() &&
+ ac.getSelectedNode() instanceof FSBDirNode dirNode && !dirNode.isSymlink())
+ .popupMenuIcon(FSBIcons.OPEN_FILE_SYSTEM)
+ .popupMenuPath("Open Directory [new window]")
.popupMenuGroup("C")
.onAction(
ac -> ac.getComponentProvider().openFileSystem(ac.getSelectedNode(), false))
@@ -72,9 +83,9 @@ public class OpenFsFSBFileHandler implements FSBFileHandler {
.toolBarGroup("B")
.onAction(ac -> {
FileSystemService fsService = context.fsService();
- FileSystemRef fsRef =
- fsService.getMountedFilesystem(fsService.getLocalFS().getFSRL());
- context.plugin().createNewFileSystemBrowser(fsRef, true);
+ context.plugin()
+ .createNewFileSystemBrowser(
+ fsService.getLocalFS().getRefManager().create(), null, true);
})
.build(),
@@ -84,8 +95,7 @@ public class OpenFsFSBFileHandler implements FSBFileHandler {
.toolBarIcon(FSBIcons.OPEN_FILE_SYSTEM)
.toolBarGroup("B")
.onAction(ac -> context.plugin().openFileSystem())
- .build()
- );
+ .build());
}
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/formats/gfilesystem/LocalGFileSystemTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/formats/gfilesystem/LocalGFileSystemTest.java
index 8341eeda7d..bbe80da68c 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/formats/gfilesystem/LocalGFileSystemTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/formats/gfilesystem/LocalGFileSystemTest.java
@@ -4,9 +4,9 @@
* 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.
@@ -57,35 +57,4 @@ public class LocalGFileSystemTest {
}
- @Test
- public void testSubFSLookup() throws IOException {
- File subworkdir = new File(workDir, "sub/Sub2/SUB3");
- subworkdir.mkdirs();
-
- File f = File.createTempFile("testfile", null, subworkdir);
-
- try (LocalFileSystemSub subFS =
- new LocalFileSystemSub(workDir, localFS)) {
- GFile gfile = subFS.lookup("/sub/Sub2/SUB3/" + f.getName());
- assertNotNull(gfile);
- assertEquals(FSUtilities.normalizeNativePath(f.getPath()), gfile.getFSRL().getPath());
- assertEquals("/sub/Sub2/SUB3/" + f.getName(), gfile.getPath());
-
- GFile rootDir = subFS.lookup("/");
- assertNotNull(rootDir);
- assertEquals("/", rootDir.getPath());
- assertEquals(FSUtilities.normalizeNativePath(workDir.getPath()),
- rootDir.getFSRL().getPath());
-
- rootDir = subFS.lookup(null);
- assertNotNull(rootDir);
- assertEquals("/", rootDir.getPath());
- assertEquals(FSUtilities.normalizeNativePath(workDir.getPath()),
- rootDir.getFSRL().getPath());
-
- GFile baseDir = subFS.lookup("/sub");
- assertNotNull(baseDir);
- }
- }
-
}