mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Merge remote-tracking branch 'origin/GP-5825_dev747368_refactor_localfilesystemsub_out'
This commit is contained in:
commit
12a8db6195
12 changed files with 173 additions and 532 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<File> parts = LocalFileSystem.getFilePathParts(f); // [/subdir/subroot/file, /subdir/subroot, /subdir, /]
|
||||
GFile current = rootDir;
|
||||
for (int i = parts.size() - 2; i >= 0; i--) {
|
||||
|
|
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<GFile> 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<GFile> 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<String> 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<File> 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<File> 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);
|
||||
}
|
||||
}
|
|
@ -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<Icon> 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) {
|
||||
|
|
|
@ -141,7 +141,7 @@ public class FSBIcons {
|
|||
return buildIcon(DEFAULT_ICON, overlays);
|
||||
}
|
||||
|
||||
private Icon buildIcon(Icon base, List<Icon> overlays) {
|
||||
public static Icon buildIcon(Icon base, List<Icon> overlays) {
|
||||
if (overlays == null || overlays.isEmpty()) {
|
||||
return base;
|
||||
}
|
||||
|
|
|
@ -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<GTreeNode> 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<GFile> pathParts = splitGFilePath(file);
|
||||
List<GFile> 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<GFile> 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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue