mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-16 15:40:14 +00:00
GP-3654 refactor GFilesystem, add abstract base class, filename case
Add AbstractFileSystem to reduce duplicate boilerplate. Start to add support for file name/path case (in)sensitive comparisons.
This commit is contained in:
parent
b0e0c7372a
commit
1b1fbf3baa
@ -0,0 +1,78 @@
|
||||
/* ###
|
||||
* 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.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default implementation of base file system functionality.
|
||||
*
|
||||
* @param <METADATATYPE> the type of objects that will be stored in the FileSystemIndexHelper
|
||||
*/
|
||||
public abstract class AbstractFileSystem<METADATATYPE> implements GFileSystem {
|
||||
protected final FileSystemService fsService;
|
||||
protected final FSRLRoot fsFSRL;
|
||||
protected FileSystemIndexHelper<METADATATYPE> fsIndex;
|
||||
protected FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
|
||||
/**
|
||||
* Initializes the fields for this abstract implementation of a file system.
|
||||
*
|
||||
* @param fsFSRL {@link FSRLRoot} of this file system
|
||||
* @param fsService reference to the {@link FileSystemService} instance
|
||||
*/
|
||||
protected AbstractFileSystem(FSRLRoot fsFSRL, FileSystemService fsService) {
|
||||
this.fsService = fsService;
|
||||
this.fsFSRL = fsFSRL;
|
||||
this.fsIndex = new FileSystemIndexHelper<>(this, fsFSRL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsFSRL.getContainer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsFSRL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return refManager;
|
||||
}
|
||||
|
||||
protected Comparator<String> getFilenameComparator() {
|
||||
return null; // null will cause exact matches in the fsIndex.lookup()
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) {
|
||||
return fsIndex.lookup(null, path, getFilenameComparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) {
|
||||
return fsIndex.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileCount() {
|
||||
return fsIndex.getFileCount();
|
||||
}
|
||||
|
||||
}
|
@ -160,15 +160,32 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
* @return {@link GFile} instance or null if no file was added to the index at that path
|
||||
*/
|
||||
public synchronized GFile lookup(String path) {
|
||||
return lookup(null, path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirror's {@link GFileSystem#lookup(String)} interface, with additional parameters to
|
||||
* control the lookup.
|
||||
*
|
||||
* @param baseDir optional starting directory to perform lookup
|
||||
* @param path path and filename of a file to find
|
||||
* @param nameComp optional {@link Comparator} that compares file names. Suggested values are
|
||||
* {@code String::compareTo} or {@code String::compareToIgnoreCase} or {@code null} (also exact).
|
||||
* @return {@link GFile} instance or null if no file was added to the index at that path
|
||||
*/
|
||||
public synchronized GFile lookup(GFile baseDir, String path, Comparator<String> nameComp) {
|
||||
String[] nameparts = (path != null ? path : "").split("/");
|
||||
GFile parent = lookupParent(nameparts);
|
||||
GFile parent = lookupParent(baseDir, nameparts, false, nameComp);
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
String name = (nameparts.length > 0) ? nameparts[nameparts.length - 1] : null;
|
||||
if (name == null || name.isEmpty()) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
Map<String, FileData<METADATATYPE>> dirListing = getDirectoryContents(parent, false);
|
||||
FileData<METADATATYPE> fileData = (dirListing != null) ? dirListing.get(name) : null;
|
||||
FileData<METADATATYPE> fileData =
|
||||
lookupFileInDir(getDirectoryContents(parent, false), name, nameComp);
|
||||
return (fileData != null) ? fileData.file : null;
|
||||
}
|
||||
|
||||
@ -197,7 +214,7 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
long length, METADATATYPE metadata) {
|
||||
|
||||
String[] nameparts = path.replaceAll("[\\\\]", "/").split("/");
|
||||
GFile parent = lookupParent(nameparts);
|
||||
GFile parent = lookupParent(rootDir, nameparts, true, null);
|
||||
|
||||
String lastpart = nameparts[nameparts.length - 1];
|
||||
FileData<METADATATYPE> fileData =
|
||||
@ -301,19 +318,27 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
* Walks a list of names of directories in nameparts (stopping prior to the last element)
|
||||
* starting at the root of the filesystem and returns the final directory.
|
||||
* <p>
|
||||
* Directories in a path that have not been encountered before (ie. a file's path references a directory
|
||||
* that hasn't been mentioned yet as its own file entry) will have a stub entry GFile created for them.
|
||||
* Directories in a path that have not been encountered before (ie. a file's path references
|
||||
* a directory that hasn't been mentioned yet as its own file entry) will have a stub entry
|
||||
* GFile created for them if createIfMissing is true.
|
||||
* <p>
|
||||
* Superfluous slashes in the original filename (ie. name/sub//subafter_extra_slash) will
|
||||
* be represented as empty string elements in the nameparts array and will be skipped
|
||||
* as if they were not there.
|
||||
* <p>
|
||||
* @param nameparts
|
||||
* @return
|
||||
* @param baseDir optional starting directory to perform lookups
|
||||
* @param nameparts String[] containing the elements of a path
|
||||
* @param createIfMissing boolean flag, if true missing elements will have stub entries created
|
||||
* for them
|
||||
* @param nameComp optional comparator that will compare names, usually case-sensitive vs case
|
||||
* insensitive
|
||||
* @return GFile that represents the parent directory, or null if in read-only mode and not
|
||||
* found
|
||||
*/
|
||||
protected GFile lookupParent(String[] nameparts) {
|
||||
protected GFile lookupParent(GFile baseDir, String[] nameparts, boolean createIfMissing,
|
||||
Comparator<String> nameComp) {
|
||||
|
||||
GFile currentDir = rootDir;
|
||||
GFile currentDir = Objects.requireNonNullElse(baseDir, rootDir);
|
||||
for (int i = 0; i < nameparts.length - 1; i++) {
|
||||
Map<String, FileData<METADATATYPE>> currentDirContents =
|
||||
getDirectoryContents(currentDir, true);
|
||||
@ -321,8 +346,11 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
FileData<METADATATYPE> fileData = currentDirContents.get(name);
|
||||
FileData<METADATATYPE> fileData = lookupFileInDir(currentDirContents, name, nameComp);
|
||||
if (fileData == null) {
|
||||
if (!createIfMissing) {
|
||||
return null;
|
||||
}
|
||||
fileData = doStoreMissingDir(name, currentDir);
|
||||
}
|
||||
currentDir = fileData.file;
|
||||
@ -331,6 +359,30 @@ public class FileSystemIndexHelper<METADATATYPE> {
|
||||
return currentDir;
|
||||
}
|
||||
|
||||
protected FileData<METADATATYPE> lookupFileInDir(
|
||||
Map<String, FileData<METADATATYPE>> dirContents, String filename,
|
||||
Comparator<String> nameComp) {
|
||||
if (dirContents == null) {
|
||||
return null;
|
||||
}
|
||||
if (nameComp == null) {
|
||||
// exact match
|
||||
return dirContents.get(filename);
|
||||
}
|
||||
List<FileData<METADATATYPE>> candidateFiles = new ArrayList<>();
|
||||
for (FileData<METADATATYPE> fd : dirContents.values()) {
|
||||
if (nameComp.compare(filename, fd.file.getName()) == 0) {
|
||||
if (fd.file.getName().equals(filename)) {
|
||||
return fd;
|
||||
}
|
||||
candidateFiles.add(fd);
|
||||
}
|
||||
}
|
||||
Collections.sort(candidateFiles,
|
||||
(f1, f2) -> f1.file.getName().compareTo(f2.file.getName()));
|
||||
return !candidateFiles.isEmpty() ? candidateFiles.get(0) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GFile instance, using per-filesystem custom logic.
|
||||
* <p>
|
||||
|
@ -89,7 +89,8 @@ public class GFileImpl implements GFile {
|
||||
parent = fromFilename(fileSystem, parent, split[i], true, -1, null);
|
||||
}
|
||||
if (fsrl == null) {
|
||||
fsrl = getFSRLFromParent(fileSystem, parent, split[split.length - 1]);
|
||||
String filename = split.length > 0 ? split[split.length - 1] : "/";
|
||||
fsrl = getFSRLFromParent(fileSystem, parent, filename);
|
||||
}
|
||||
return new GFileImpl(fileSystem, parent, isDirectory, length, fsrl);
|
||||
}
|
||||
|
@ -119,7 +119,8 @@ public interface GFileSystem extends Closeable, ExtensionPoint {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a {@link GFile} from this filesystem based on its full path and filename.
|
||||
* Retrieves a {@link GFile} from this filesystem based on its full path and filename, using
|
||||
* this filesystem's default name comparison logic (eg. case sensitive vs insensitive).
|
||||
* <p>
|
||||
* @param path string path and filename of a file located in this filesystem. Use
|
||||
* {@code null} or "/" to retrieve the root directory
|
||||
|
@ -16,6 +16,7 @@
|
||||
package ghidra.formats.gfilesystem;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
@ -151,12 +152,24 @@ public abstract class GFileSystemBase implements GFileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to specify a file-system specific name comparator.
|
||||
*
|
||||
* @return {@link Comparator} such as {@link String#compareTo(String)} or
|
||||
* {@link String#compareToIgnoreCase(String)}
|
||||
*/
|
||||
protected Comparator<String> getFilenameComparator() {
|
||||
return String::compareTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
if (path == null || path.equals("/")) {
|
||||
return root;
|
||||
}
|
||||
GFile current = null;
|
||||
Comparator<String> nameComp = getFilenameComparator();
|
||||
|
||||
GFile current = root;
|
||||
String[] parts = path.split("/");
|
||||
partloop: for (String part : parts) {
|
||||
if (part.isEmpty()) {
|
||||
@ -164,7 +177,7 @@ public abstract class GFileSystemBase implements GFileSystem {
|
||||
}
|
||||
List<GFile> listing = getListing(current);
|
||||
for (GFile gf : listing) {
|
||||
if (part.equals(gf.getName())) {
|
||||
if (nameComp.compare(part, gf.getName()) == 0) {
|
||||
current = gf;
|
||||
continue partloop;
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.map.ReferenceMap;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.FileByteProvider;
|
||||
@ -30,6 +29,7 @@ import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
|
||||
import ghidra.formats.gfilesystem.factory.GFileSystemFactory;
|
||||
import ghidra.formats.gfilesystem.factory.GFileSystemFactoryIgnore;
|
||||
import ghidra.formats.gfilesystem.fileinfo.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@ -122,10 +122,7 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
|
||||
* @return The {@link FSRL}
|
||||
*/
|
||||
public FSRL getLocalFSRL(File f) {
|
||||
// We prepend a "/" to ensure that Windows-style paths (i.e. C:\) start with a "/". For
|
||||
// unix-style paths, this redundant "/" will be dropped.
|
||||
return fsFSRL.withPath(
|
||||
FSUtilities.appendPath("/", FilenameUtils.separatorsToUnix(f.getAbsolutePath())));
|
||||
return fsFSRL.withPath(FSUtilities.normalizeNativePath(f.getAbsolutePath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -149,8 +146,8 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
|
||||
|
||||
if (directory == null) {
|
||||
for (File f : File.listRoots()) {
|
||||
results.add(GFileImpl.fromFSRL(this, null, fsFSRL.withPath(f.getName()),
|
||||
f.isDirectory(), -1));
|
||||
FSRL rootElemFSRL = fsFSRL.withPath(FSUtilities.normalizeNativePath(f.getName()));
|
||||
results.add(GFileImpl.fromFSRL(this, null, rootElemFSRL, f.isDirectory(), -1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -166,8 +163,9 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
|
||||
|
||||
for (File f : files) {
|
||||
if (f.isFile() || f.isDirectory()) {
|
||||
results.add(GFileImpl.fromFSRL(this, directory,
|
||||
directory.getFSRL().appendPath(f.getName()), f.isDirectory(), f.length()));
|
||||
FSRL newFileFSRL = directory.getFSRL().appendPath(f.getName());
|
||||
results.add(GFileImpl.fromFSRL(this, directory, newFileFSRL, f.isDirectory(),
|
||||
f.length()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,11 +224,10 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFileImpl lookup(String path) throws IOException {
|
||||
File f = new File(path);
|
||||
GFileImpl gf = GFileImpl.fromPathString(this, FilenameUtils.separatorsToUnix(f.getPath()),
|
||||
null, f.isDirectory(), f.length());
|
||||
return gf;
|
||||
public GFile lookup(String path) throws IOException {
|
||||
File f = lookupFile(null, path, null);
|
||||
return f != null ? GFileImpl.fromPathString(this,
|
||||
FSUtilities.normalizeNativePath(f.getPath()), null, f.isDirectory(), f.length()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -292,54 +289,121 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
private static class FileFingerprintRec {
|
||||
final String path;
|
||||
final long timestamp;
|
||||
final long length;
|
||||
private record FileFingerprintRec(String path, long timestamp, long length) {
|
||||
}
|
||||
|
||||
FileFingerprintRec(String path, long timestamp, long length) {
|
||||
this.path = path;
|
||||
this.timestamp = timestamp;
|
||||
this.length = length;
|
||||
//--------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Looks up a file, by its string path, using a custom comparator.
|
||||
* <p>
|
||||
* If any element of the path, or the filename are not found, returns a null.
|
||||
* <p>
|
||||
* A null custom comparator avoids testing each element of the directory path and instead
|
||||
* relies on the native local file system's name matching.
|
||||
*
|
||||
* @param baseDir optional directory to start lookup at
|
||||
* @param path String path
|
||||
* @param nameComp optional {@link Comparator} that will compare filenames, or {@code null}
|
||||
* to use native local file system lookup (eg. case-insensitive on windows)
|
||||
* @return File that points to the requested path, or null if file was not present on the
|
||||
* local filesystem (because it doesn't exist, or the name comparison function rejected it)
|
||||
*/
|
||||
public static File lookupFile(File baseDir, String path, Comparator<String> nameComp) {
|
||||
// TODO: if path is in unc format "//server/share/path", linux jvm's will normalize the
|
||||
// leading double slashes to a single "/". Should the path be rejected immediately in a
|
||||
// non-windows jvm?
|
||||
path = Objects.requireNonNullElse(path, "/");
|
||||
File f = new File(baseDir, path); // null baseDir is okay
|
||||
if (!f.isAbsolute()) {
|
||||
Msg.debug(LocalFileSystem.class,
|
||||
"Non-absolute path encountered in LocalFileSystem lookup: " + path);
|
||||
// TODO: this would be better to throw an exception, but because some relative filenames
|
||||
// have leaked into some FSRLs, resolving those paths (even if it produces an incorrect
|
||||
// result) seems preferable.
|
||||
f = f.getAbsoluteFile();
|
||||
}
|
||||
try {
|
||||
if (nameComp == null || f.getParentFile() == null) {
|
||||
// If not using a comparator, or if the requested path is a
|
||||
// root element (eg "/", or "c:\\"), don't do per-directory-path lookups.
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (int) (length ^ (length >>> 32));
|
||||
result = prime * result + ((path == null) ? 0 : path.hashCode());
|
||||
result = prime * result + (int) (timestamp ^ (timestamp >>> 32));
|
||||
return result;
|
||||
}
|
||||
// On windows, getCanonicalFile() will return a corrected path using the case of
|
||||
// the file element on the file system (eg. "c:/users" -> "c:/Users"), if the
|
||||
// element exists.
|
||||
return f.exists() ? f.getCanonicalFile() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof FileFingerprintRec)) {
|
||||
return false;
|
||||
}
|
||||
FileFingerprintRec other = (FileFingerprintRec) obj;
|
||||
if (length != other.length) {
|
||||
return false;
|
||||
}
|
||||
if (path == null) {
|
||||
if (other.path != null) {
|
||||
return false;
|
||||
if (f.exists()) {
|
||||
// try to short-cut by comparing the entire path string
|
||||
File canonicalFile = f.getCanonicalFile();
|
||||
if (nameComp.compare(path,
|
||||
FSUtilities.normalizeNativePath((canonicalFile.getPath()))) == 0) {
|
||||
return canonicalFile;
|
||||
}
|
||||
}
|
||||
else if (!path.equals(other.path)) {
|
||||
return false;
|
||||
|
||||
// For path "/subdir/file", pathParts will contain, in reverse order:
|
||||
// [/subdir/file, /subdir, /]
|
||||
// The root element ("/", or "c:/") will never be subjected to the name comparator
|
||||
// The case of each element will be what was specified in the path parameter.
|
||||
// Lookup each element in its parent directory, using the comparator to find the file
|
||||
// in the full listing of each directory.
|
||||
// If requested path has "." and ".." elements, findInDir() will not find them,
|
||||
// avoiding path traversal issues.
|
||||
// TODO: shouldn't use findInDir on the server and share parts of a UNC path "//server/share"
|
||||
List<File> pathParts = getFilePathParts(f);
|
||||
|
||||
for (int i = pathParts.size() - 2 /*skip root ele*/; i >= 0; i--) {
|
||||
File parentDir = pathParts.get(i + 1);
|
||||
File part = pathParts.get(i);
|
||||
File foundFile = findInDir(parentDir, part.getName(), nameComp);
|
||||
if (foundFile == null) {
|
||||
return null;
|
||||
}
|
||||
pathParts.set(i, foundFile);
|
||||
}
|
||||
if (timestamp != other.timestamp) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return pathParts.get(0);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.warn(LocalFileSystem.class, "Error resolving path: " + path, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static File findInDir(File dir, String name, Comparator<String> nameComp) {
|
||||
// Searches for "name" in the list of files found in the directory.
|
||||
// Because a case-insensitive comparator could match on several files in the same directory,
|
||||
// query for all the files before picking a match: either an exact string match, or
|
||||
// if there are several candidates, the first in the list after sorting.
|
||||
File[] files = dir.listFiles();
|
||||
List<File> candidateMatches = new ArrayList<>();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
String foundFilename = f.getName();
|
||||
if (nameComp.compare(name, foundFilename) == 0) {
|
||||
if (name.equals(foundFilename)) {
|
||||
return f;
|
||||
}
|
||||
candidateMatches.add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(candidateMatches);
|
||||
return !candidateMatches.isEmpty() ? candidateMatches.get(0) : null;
|
||||
}
|
||||
|
||||
static List<File> getFilePathParts(File f) {
|
||||
// return a list of the parts of the specified file:
|
||||
// "/subdir/file" -> "/subidr/file", "/subdir", "/"
|
||||
// "c:/subdir/file" -> "c:/subdir/file", "c:/subdir", "c:/"
|
||||
// "//uncserver/share/path" -> "//uncserver/share/path", "//uncserver/share", "//uncserver", "//"
|
||||
// (windows jvm only, unix jvm will normalize a path's leading "//" to be "/"
|
||||
List<File> results = new ArrayList<File>();
|
||||
while (f != null) {
|
||||
results.add(f);
|
||||
f = f.getParentFile();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
@ -152,25 +150,39 @@ public class LocalFileSystemSub implements GFileSystem, GFileHashProvider {
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
path = StringUtils.defaultString(path, "/");
|
||||
|
||||
// Create a new GFile instance with a FSRL based on the RootFS (and not this FS),
|
||||
File curFile = localfsRootDir;
|
||||
GFileLocal result = rootGFile;
|
||||
|
||||
String[] parts = path.split("/");
|
||||
for (String name : parts) {
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
curFile = new File(curFile, name);
|
||||
FSRL fsrl = result.getFSRL().appendPath(name);
|
||||
String relPath = FSUtilities.appendPath(result.getPath(), name);
|
||||
result = new GFileLocal(curFile, relPath, fsrl, this, result);
|
||||
File f = LocalFileSystem.lookupFile(localfsRootDir, path, null);
|
||||
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 {
|
||||
|
@ -49,7 +49,7 @@ public class SingleFileSystemIndexHelper {
|
||||
// used as the owner of the new GFileImpl instances.
|
||||
this.rootDir = GFileImpl.fromFSRL(fs, null, fsFSRL.withPath("/"), true, -1);
|
||||
this.payloadFile = GFileImpl.fromFSRL(fs, rootDir,
|
||||
rootDir.getFSRL().withPath(payloadFilename).withMD5(payloadMD5), false, length);
|
||||
rootDir.getFSRL().appendPath(payloadFilename).withMD5(payloadMD5), false, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -126,28 +126,43 @@ public class SingleFileSystemIndexHelper {
|
||||
if (isClosed()) {
|
||||
throw new IOException("Invalid state, index already closed");
|
||||
}
|
||||
if (directory == null || rootDir.equals(directory)) {
|
||||
return Arrays.asList(payloadFile);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return directory == null || rootDir.equals(directory) ? List.of(payloadFile) : List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirror's {@link GFileSystem#lookup(String)} interface.
|
||||
*
|
||||
*
|
||||
* @param path path and filename of a file to find (either "/" for root or the payload file's
|
||||
* path).
|
||||
* @return {@link GFile} instance or null if requested path is not the same as the payload file.
|
||||
*/
|
||||
public GFile lookup(String path) {
|
||||
return lookup(null, path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirror's {@link GFileSystem#lookup(String)} interface.
|
||||
*
|
||||
* @param baseDir starting directory
|
||||
* @param path path and filename of a file to find (either "/" for root or the payload file's
|
||||
* path).
|
||||
* @param nameComp optional {@link Comparator} that compares file names. Suggested values are
|
||||
* {@code String::compareTo} or {@code String::compareToIgnoreCase} or {@code null}.
|
||||
* @return {@link GFile} instance or null if requested path is not the same as
|
||||
* the payload file.
|
||||
*/
|
||||
public GFile lookup(String path) {
|
||||
public GFile lookup(GFile baseDir, String path, Comparator<String> nameComp) {
|
||||
if (baseDir != null && !baseDir.equals(rootDir)) {
|
||||
return null;
|
||||
}
|
||||
if (path == null || path.equals("/")) {
|
||||
return rootDir;
|
||||
}
|
||||
else if (path.equals(payloadFile.getFSRL().getPath())) {
|
||||
return payloadFile;
|
||||
}
|
||||
return null;
|
||||
nameComp = Objects.requireNonNullElse(nameComp, String::compareTo);
|
||||
// compare the FSRL path ("/payloadname") as well just the payloadname (to be compatible
|
||||
// with existing data that have malformed fsrls without a leading slash in the path)
|
||||
return nameComp.compare(path, payloadFile.getFSRL().getPath()) == 0 ||
|
||||
nameComp.compare(path, payloadFile.getFSRL().getName()) == 0 ? payloadFile : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,91 @@
|
||||
/* ###
|
||||
* 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 static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
|
||||
public class LocalGFileSystemTest {
|
||||
|
||||
private File testTempDir;
|
||||
private FileSystemService fsService;
|
||||
private File workDir;
|
||||
private LocalFileSystem localFS;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
testTempDir = AbstractGenericTest.createTempDirectory("localfs_test");
|
||||
fsService = new FileSystemService(new File(testTempDir, "cache"));
|
||||
workDir = new File(testTempDir, "work");
|
||||
workDir.mkdirs();
|
||||
localFS = fsService.getLocalFS();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasePathLookups() throws IOException {
|
||||
File subworkdir = new File(workDir, "sub/Sub2/SUB3");
|
||||
subworkdir.mkdirs();
|
||||
|
||||
GFile sub = localFS.lookup(FSUtilities.normalizeNativePath(workDir.getPath() + "/sub"));
|
||||
assertNotNull(sub);
|
||||
|
||||
GFile rootNull = localFS.lookup(null);
|
||||
assertNotNull(rootNull);
|
||||
|
||||
GFile rootSlash = localFS.lookup("/");
|
||||
assertNotNull(rootSlash);
|
||||
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -19,7 +19,6 @@ import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.bin.ByteProviderWrapper;
|
||||
@ -32,18 +31,13 @@ import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@FileSystemInfo(type = "coff", description = "COFF Archive", factory = CoffArchiveFileSystemFactory.class)
|
||||
public class CoffArchiveFileSystem implements GFileSystem {
|
||||
|
||||
private final FSRLRoot fsFSRL;
|
||||
private FileSystemIndexHelper<CoffArchiveMemberHeader> fsih;
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
public class CoffArchiveFileSystem extends AbstractFileSystem<CoffArchiveMemberHeader> {
|
||||
|
||||
private ByteProvider provider;
|
||||
|
||||
public CoffArchiveFileSystem(FSRLRoot fsFSRL, ByteProvider provider) {
|
||||
this.fsFSRL = fsFSRL;
|
||||
super(fsFSRL, FileSystemService.getInstance());
|
||||
this.provider = provider;
|
||||
this.fsih = new FileSystemIndexHelper<>(this, fsFSRL);
|
||||
}
|
||||
|
||||
public void mount(TaskMonitor monitor) throws IOException {
|
||||
@ -56,7 +50,7 @@ public class CoffArchiveFileSystem implements GFileSystem {
|
||||
String name = camh.getName().replace('\\', '/');//replace stupid windows backslashes.
|
||||
monitor.setMessage(name);
|
||||
|
||||
fsih.storeFile(name, fsih.getFileCount(), false, camh.getSize(), camh);
|
||||
fsIndex.storeFile(name, fsIndex.getFileCount(), false, camh.getSize(), camh);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,19 +59,9 @@ public class CoffArchiveFileSystem implements GFileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsFSRL.getContainer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsFSRL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) {
|
||||
CoffArchiveMemberHeader entry = fsih.getMetadata(file);
|
||||
CoffArchiveMemberHeader entry = fsIndex.getMetadata(file);
|
||||
return (entry != null && entry.isCOFF())
|
||||
? new ByteProviderWrapper(provider, entry.getPayloadOffset(), entry.getSize(),
|
||||
file.getFSRL())
|
||||
@ -91,7 +75,7 @@ public class CoffArchiveFileSystem implements GFileSystem {
|
||||
provider.close();
|
||||
provider = null;
|
||||
}
|
||||
fsih.clear();
|
||||
fsIndex.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,14 +83,9 @@ public class CoffArchiveFileSystem implements GFileSystem {
|
||||
return provider == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileCount() {
|
||||
return fsih.getFileCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
CoffArchiveMemberHeader entry = fsih.getMetadata(file);
|
||||
CoffArchiveMemberHeader entry = fsIndex.getMetadata(file);
|
||||
FileAttributes result = new FileAttributes();
|
||||
if (entry != null) {
|
||||
result.add(NAME_ATTR, entry.getName());
|
||||
@ -118,19 +97,4 @@ public class CoffArchiveFileSystem implements GFileSystem {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsih.lookup(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
return fsih.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return refManager;
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package ghidra.file.formats.cpio;
|
||||
import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.compress.archivers.cpio.CpioArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
|
||||
@ -29,37 +28,28 @@ import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
|
||||
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
@FileSystemInfo(type = "cpio", description = "CPIO", factory = CpioFileSystemFactory.class)
|
||||
public class CpioFileSystem implements GFileSystem {
|
||||
private FileSystemService fsService;
|
||||
private FSRLRoot fsFSRL;
|
||||
private FileSystemIndexHelper<CpioArchiveEntry> fsIndex;
|
||||
private FileSystemRefManager fsRefManager = new FileSystemRefManager(this);
|
||||
public class CpioFileSystem extends AbstractFileSystem<CpioArchiveEntry> {
|
||||
private ByteProvider provider;
|
||||
|
||||
|
||||
public CpioFileSystem(FSRLRoot fsFSRL, ByteProvider provider, FileSystemService fsService,
|
||||
TaskMonitor monitor)
|
||||
throws IOException {
|
||||
TaskMonitor monitor) throws IOException {
|
||||
super(fsFSRL, fsService);
|
||||
|
||||
monitor.setMessage("Opening CPIO...");
|
||||
this.fsService = fsService;
|
||||
this.fsFSRL = fsFSRL;
|
||||
this.provider = provider;
|
||||
this.fsIndex = new FileSystemIndexHelper<>(this, fsFSRL);
|
||||
|
||||
try (CpioArchiveInputStream cpioInputStream =
|
||||
new CpioArchiveInputStream(provider.getInputStream(0))) {
|
||||
CpioArchiveEntry entry;
|
||||
int fileNum = 0;
|
||||
while ((entry = cpioInputStream.getNextCPIOEntry()) != null) {
|
||||
FileUtilities.copyStreamToStream(cpioInputStream, OutputStream.nullOutputStream(),
|
||||
monitor);
|
||||
FSUtilities.streamCopy(cpioInputStream, OutputStream.nullOutputStream(), monitor);
|
||||
|
||||
monitor.setMessage(entry.getName());
|
||||
fsIndex.storeFile(entry.getName(), fileNum++, entry.isDirectory(),
|
||||
entry.getSize(), entry);
|
||||
fsIndex.storeFile(entry.getName(), fileNum++, entry.isDirectory(), entry.getSize(),
|
||||
entry);
|
||||
}
|
||||
}
|
||||
catch (EOFException e) {
|
||||
@ -75,7 +65,7 @@ public class CpioFileSystem implements GFileSystem {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
fsRefManager.onClose();
|
||||
refManager.onClose();
|
||||
fsIndex.clear();
|
||||
if (provider != null) {
|
||||
provider.close();
|
||||
@ -83,36 +73,11 @@ public class CpioFileSystem implements GFileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsFSRL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsFSRL.getContainer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return provider == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return fsRefManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
return fsIndex.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsIndex.lookup(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
FileAttributes result = new FileAttributes();
|
||||
|
@ -20,7 +20,8 @@ import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.BitSet;
|
||||
import java.util.Date;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
@ -33,13 +34,10 @@ import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@FileSystemInfo(type = "ext4", description = "EXT4", factory = Ext4FileSystemFactory.class)
|
||||
public class Ext4FileSystem implements GFileSystem {
|
||||
public class Ext4FileSystem extends AbstractFileSystem<Ext4File> {
|
||||
|
||||
public static final Charset EXT4_DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
private FileSystemIndexHelper<Ext4File> fsih;
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
private FSRLRoot fsrl;
|
||||
private int blockSize;
|
||||
private ByteProvider provider;
|
||||
private String volumeName;
|
||||
@ -47,8 +45,7 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
private Ext4SuperBlock superBlock;
|
||||
|
||||
public Ext4FileSystem(FSRLRoot fsrl, ByteProvider provider) {
|
||||
this.fsrl = fsrl;
|
||||
this.fsih = new FileSystemIndexHelper<>(this, fsrl);
|
||||
super(fsrl, FileSystemService.getInstance());
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@ -95,8 +92,8 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
monitor.initialize(usedInodeCount);
|
||||
|
||||
BitSet processedInodes = new BitSet(inodes.length);
|
||||
processDirectory(inodes[Ext4Constants.EXT4_INODE_INDEX_ROOTDIR], fsih.getRootDir(), inodes,
|
||||
processedInodes, monitor);
|
||||
processDirectory(inodes[Ext4Constants.EXT4_INODE_INDEX_ROOTDIR], fsIndex.getRootDir(),
|
||||
inodes, processedInodes, monitor);
|
||||
checkUnprocessedInodes(inodes, processedInodes);
|
||||
}
|
||||
|
||||
@ -160,8 +157,8 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
return;
|
||||
}
|
||||
|
||||
GFile gfile = fsih.storeFileWithParent(name, parentDir, -1, inode.isDir(), inode.getSize(),
|
||||
new Ext4File(name, inode));
|
||||
GFile gfile = fsIndex.storeFileWithParent(name, parentDir, -1, inode.isDir(),
|
||||
inode.getSize(), new Ext4File(name, inode));
|
||||
if (processedInodes.get(inodeNumber)) {
|
||||
// this inode was already seen and handled earlier. adding a second filename to the fsih is
|
||||
// okay, but don't try to process as a directory, which shouldn't normally be possible
|
||||
@ -174,21 +171,11 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileCount() {
|
||||
return fsih.getFileCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
return fsih.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
FileAttributes result = new FileAttributes();
|
||||
|
||||
Ext4File ext4File = fsih.getMetadata(file);
|
||||
Ext4File ext4File = fsIndex.getMetadata(file);
|
||||
if (ext4File != null) {
|
||||
Ext4Inode inode = ext4File.getInode();
|
||||
result.add(NAME_ATTR, ext4File.getName());
|
||||
@ -237,7 +224,7 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
throw new IOException(
|
||||
"Symlink too long: " + file.getPath() + ", " + symlinkDebugPath);
|
||||
}
|
||||
Ext4File extFile = fsih.getMetadata(currentFile);
|
||||
Ext4File extFile = fsIndex.getMetadata(currentFile);
|
||||
if (extFile == null) {
|
||||
throw new IOException("Missing Ext4 metadata for " + currentFile.getPath());
|
||||
}
|
||||
@ -252,6 +239,7 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
if (currentFile.getParentFile() == null) {
|
||||
throw new IOException("No parent file for " + currentFile);
|
||||
}
|
||||
// TODO: doesn't handle "../../" traversal yet
|
||||
symlinkDestPath =
|
||||
FSUtilities.appendPath(currentFile.getParentFile().getPath(),
|
||||
symlinkDestPath);
|
||||
@ -273,7 +261,7 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
}
|
||||
|
||||
private Ext4Inode getInodeFor(GFile file, TaskMonitor monitor) throws IOException {
|
||||
Ext4File extFile = fsih.getMetadata(file);
|
||||
Ext4File extFile = fsIndex.getMetadata(file);
|
||||
if (extFile == null) {
|
||||
return null;
|
||||
}
|
||||
@ -362,17 +350,12 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
refManager.onClose();
|
||||
provider.close();
|
||||
provider = null;
|
||||
fsih.clear();
|
||||
fsIndex.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsrl.getContainer().getName() + " - " + volumeName + " - " + uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsrl;
|
||||
return "%s - %s - %s".formatted(fsFSRL.getContainer().getName(), volumeName, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -380,14 +363,4 @@ public class Ext4FileSystem implements GFileSystem {
|
||||
return provider == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return refManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsih.lookup(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ public class GZipFileSystem implements GFileSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
public GFile lookup(String path) {
|
||||
return fsIndex.lookup(path);
|
||||
}
|
||||
|
||||
|
@ -45,49 +45,46 @@ import ghidra.util.task.*;
|
||||
* restarted.
|
||||
*/
|
||||
@FileSystemInfo(type = "dmg", description = "iOS Disk Image (DMG)", factory = DmgClientFileSystemFactory.class)
|
||||
public class DmgClientFileSystem implements GFileSystem {
|
||||
public class DmgClientFileSystem extends AbstractFileSystem<Object> {
|
||||
|
||||
private final FSRLRoot fsrl;
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
private FileSystemIndexHelper<Object> fsih;
|
||||
private File decryptedDmgFile;
|
||||
private boolean deleteFileWhenDone;
|
||||
private DmgServerProcessManager processManager;
|
||||
private CancelledListener listener = () -> processManager.interruptCmd();
|
||||
private FileSystemService fsService;
|
||||
|
||||
/**
|
||||
* Creates a {@link DmgClientFileSystem} instance, using a decrypted dmg file and
|
||||
* the filesystem's {@link FSRLRoot}.
|
||||
*
|
||||
* @param decryptedDmgFile path to a decrypted DMG file. The DmgClientFileSystemFactory
|
||||
* takes care of decrypting for us.
|
||||
* @param fsrl {@link FSRLRoot} of this filesystem.
|
||||
* takes care of decrypting for us
|
||||
* @param deleteFileWhenDone boolean flag, if true, the container file will be deleted when
|
||||
* the filesystem is closed
|
||||
* @param fsrl {@link FSRLRoot} of this filesystem
|
||||
* @param fsService {@link FileSystemService} reference
|
||||
*/
|
||||
public DmgClientFileSystem(File decryptedDmgFile, boolean deleteFileWhenDone, FSRLRoot fsrl,
|
||||
FileSystemService fsService) {
|
||||
this.fsrl = fsrl;
|
||||
this.fsih = new FileSystemIndexHelper<>(this, fsrl);
|
||||
super(fsrl, fsService);
|
||||
this.decryptedDmgFile = decryptedDmgFile;
|
||||
this.deleteFileWhenDone = deleteFileWhenDone;
|
||||
this.fsService = fsService;
|
||||
}
|
||||
|
||||
public void mount(TaskMonitor monitor) throws CancelledException, IOException {
|
||||
processManager =
|
||||
new DmgServerProcessManager(decryptedDmgFile, fsrl.getContainer().getName());
|
||||
new DmgServerProcessManager(decryptedDmgFile, fsFSRL.getContainer().getName());
|
||||
|
||||
monitor.addCancelledListener(listener);
|
||||
try {
|
||||
UnknownProgressWrappingTaskMonitor upwtm =
|
||||
new UnknownProgressWrappingTaskMonitor(monitor, 1);
|
||||
recurseDirectories(fsih.getRootDir(), upwtm);
|
||||
recurseDirectories(fsIndex.getRootDir(), upwtm);
|
||||
}
|
||||
finally {
|
||||
monitor.removeCancelledListener(listener);
|
||||
}
|
||||
Msg.info(this,
|
||||
"Indexed " + fsih.getFileCount() + " files in " + fsrl.getContainer().getName());
|
||||
Msg.info(this, "Indexed %d files in %s".formatted(fsIndex.getFileCount(),
|
||||
fsFSRL.getContainer().getName()));
|
||||
|
||||
}
|
||||
|
||||
@ -98,34 +95,19 @@ public class DmgClientFileSystem implements GFileSystem {
|
||||
processManager.close();
|
||||
processManager = null;
|
||||
|
||||
fsih.clear();
|
||||
fsih = null;
|
||||
fsIndex.clear();
|
||||
fsIndex = null;
|
||||
if (deleteFileWhenDone) {
|
||||
Msg.info(this, "Deleting DMG temp file:" + decryptedDmgFile);
|
||||
decryptedDmgFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsrl.getContainer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return processManager == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return refManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
@ -162,7 +144,7 @@ public class DmgClientFileSystem implements GFileSystem {
|
||||
monitor.incrementProgress(1);
|
||||
|
||||
// throw away the gfileimpl from getrawlisting(), create new gfile in rafi
|
||||
GFile newF = fsih.storeFileWithParent(f.getName(), dir, -1, f.isDirectory(),
|
||||
GFile newF = fsIndex.storeFileWithParent(f.getName(), dir, -1, f.isDirectory(),
|
||||
f.getLength(), null);
|
||||
if (newF.isDirectory()) {
|
||||
recurseDirectories(newF, monitor);
|
||||
@ -190,21 +172,6 @@ public class DmgClientFileSystem implements GFileSystem {
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsih.lookup(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
return fsih.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileCount() {
|
||||
return fsih.getFileCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
monitor.addCancelledListener(listener);
|
||||
|
@ -33,7 +33,7 @@ import ghidra.util.task.TaskMonitor;
|
||||
//@formatter:on
|
||||
public class Img2FileSystem implements GFileSystem {
|
||||
|
||||
private FSRLRoot fsFSRL;
|
||||
private final FSRLRoot fsFSRL;
|
||||
private SingleFileSystemIndexHelper fsIndexHelper;
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
private ByteProvider provider;
|
||||
@ -102,7 +102,7 @@ public class Img2FileSystem implements GFileSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
public GFile lookup(String path) {
|
||||
return fsIndexHelper.lookup(path);
|
||||
}
|
||||
|
||||
|
@ -30,20 +30,15 @@ import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@FileSystemInfo(type = "img3", description = "iOS " +
|
||||
Img3Constants.IMG3_SIGNATURE, factory = Img3FileSystemFactory.class)
|
||||
public class Img3FileSystem implements GFileSystem {
|
||||
public class Img3FileSystem extends AbstractFileSystem<DataTag> {
|
||||
|
||||
private FSRLRoot fsFSRL;
|
||||
private FileSystemRefManager fsRefManager = new FileSystemRefManager(this);
|
||||
private FileSystemIndexHelper<DataTag> fsIndexHelper;
|
||||
private ByteProvider provider;
|
||||
private FileSystemService fsService;
|
||||
|
||||
public Img3FileSystem(FSRLRoot fsFSRL, ByteProvider provider, FileSystemService fsService,
|
||||
TaskMonitor monitor) throws IOException {
|
||||
this.fsFSRL = fsFSRL;
|
||||
this.fsIndexHelper = new FileSystemIndexHelper<>(this, fsFSRL);
|
||||
super(fsFSRL, fsService);
|
||||
|
||||
this.provider = provider;
|
||||
this.fsService = fsService;
|
||||
|
||||
monitor.setMessage("Opening IMG3...");
|
||||
Img3 header = new Img3(provider);
|
||||
@ -61,7 +56,7 @@ public class Img3FileSystem implements GFileSystem {
|
||||
|
||||
DataTag dataTag = tags.get(i);
|
||||
String filename = getDataTagFilename(dataTag, i, tags.size() > 1);
|
||||
fsIndexHelper.storeFileWithParent(filename, fsIndexHelper.getRootDir(), i, false,
|
||||
fsIndex.storeFileWithParent(filename, fsIndex.getRootDir(), i, false,
|
||||
dataTag.getTotalLength(), dataTag);
|
||||
}
|
||||
}
|
||||
@ -73,8 +68,8 @@ public class Img3FileSystem implements GFileSystem {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
fsRefManager.onClose();
|
||||
fsIndexHelper.clear();
|
||||
refManager.onClose();
|
||||
fsIndex.clear();
|
||||
if (provider != null) {
|
||||
provider.close();
|
||||
provider = null;
|
||||
@ -90,7 +85,7 @@ public class Img3FileSystem implements GFileSystem {
|
||||
"Unable to decrypt IMG3 data because IMG3 crypto keys are specific to the container it is embedded in and this IMG3 was not in a container");
|
||||
}
|
||||
|
||||
DataTag dataTag = fsIndexHelper.getMetadata(file);
|
||||
DataTag dataTag = fsIndex.getMetadata(file);
|
||||
if (dataTag == null) {
|
||||
throw new IOException("Unknown file: " + file);
|
||||
}
|
||||
@ -106,34 +101,9 @@ public class Img3FileSystem implements GFileSystem {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) {
|
||||
return fsIndexHelper.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsFSRL.getContainer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsFSRL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return provider == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return fsRefManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsIndexHelper.lookup(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,14 +39,15 @@ import utilities.util.FileUtilities;
|
||||
@FileSystemInfo(type = "javaclass", description = "Java Class Decompiler", factory = JavaClassDecompilerFileSystemFactory.class, priority = FileSystemInfo.PRIORITY_LOW)
|
||||
public class JavaClassDecompilerFileSystem implements GFileSystem {
|
||||
|
||||
private FSRLRoot fsFSRL;
|
||||
private final FSRLRoot fsFSRL;
|
||||
private final FileSystemService fsService;
|
||||
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
private SingleFileSystemIndexHelper fsIndexHelper;
|
||||
private ByteProvider provider;
|
||||
private FSRL containerFSRL;
|
||||
private String className;
|
||||
private String javaSrcFilename;
|
||||
private FileSystemService fsService;
|
||||
|
||||
public JavaClassDecompilerFileSystem(FSRLRoot fsFSRL, ByteProvider provider,
|
||||
FileSystemService fsService, TaskMonitor monitor)
|
||||
@ -128,7 +129,7 @@ public class JavaClassDecompilerFileSystem implements GFileSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
public GFile lookup(String path) {
|
||||
return fsIndexHelper.lookup(path);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@ import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.omf.OmfFileHeader;
|
||||
@ -30,18 +29,13 @@ import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@FileSystemInfo(type = "omf", description = "OMF Archive", factory = OmfArchiveFileSystemFactory.class)
|
||||
public class OmfArchiveFileSystem implements GFileSystem {
|
||||
|
||||
private final FSRLRoot fsFSRL;
|
||||
private FileSystemIndexHelper<OmfLibraryRecord.MemberHeader> fsih;
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
public class OmfArchiveFileSystem extends AbstractFileSystem<OmfLibraryRecord.MemberHeader> {
|
||||
|
||||
private ByteProvider provider;
|
||||
|
||||
public OmfArchiveFileSystem(FSRLRoot fsFSRL, ByteProvider provider) {
|
||||
this.fsFSRL = fsFSRL;
|
||||
super(fsFSRL, FileSystemService.getInstance());
|
||||
this.provider = provider;
|
||||
this.fsih = new FileSystemIndexHelper<>(this, fsFSRL);
|
||||
}
|
||||
|
||||
public void mount(TaskMonitor monitor) throws IOException {
|
||||
@ -52,7 +46,7 @@ public class OmfArchiveFileSystem implements GFileSystem {
|
||||
for (OmfLibraryRecord.MemberHeader member : memberHeaders) {
|
||||
String name = member.name;
|
||||
monitor.setMessage(name);
|
||||
fsih.storeFile(name, fsih.getFileCount(), false, member.size, member);
|
||||
fsIndex.storeFile(name, fsIndex.getFileCount(), false, member.size, member);
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,17 +57,7 @@ public class OmfArchiveFileSystem implements GFileSystem {
|
||||
provider.close();
|
||||
provider = null;
|
||||
}
|
||||
fsih.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsFSRL.getContainer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsFSRL;
|
||||
fsIndex.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -81,40 +65,20 @@ public class OmfArchiveFileSystem implements GFileSystem {
|
||||
return provider == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileCount() {
|
||||
return fsih.getFileCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return refManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsih.lookup(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) {
|
||||
OmfLibraryRecord.MemberHeader member = fsih.getMetadata(file);
|
||||
OmfLibraryRecord.MemberHeader member = fsIndex.getMetadata(file);
|
||||
return (member != null)
|
||||
? new ByteProviderWrapper(provider, member.payloadOffset, member.size,
|
||||
file.getFSRL())
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
return fsih.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
FileAttributes result = new FileAttributes();
|
||||
|
||||
OmfLibraryRecord.MemberHeader entry = fsih.getMetadata(file);
|
||||
OmfLibraryRecord.MemberHeader entry = fsIndex.getMetadata(file);
|
||||
if (entry != null) {
|
||||
result.add(NAME_ATTR, entry.name);
|
||||
result.add(SIZE_ATTR, entry.size);
|
||||
|
@ -42,11 +42,7 @@ import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
|
||||
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
|
||||
|
||||
@FileSystemInfo(type = "7zip", description = "7Zip", factory = SevenZipFileSystemFactory.class)
|
||||
public class SevenZipFileSystem implements GFileSystem {
|
||||
private FileSystemService fsService;
|
||||
private FileSystemIndexHelper<ISimpleInArchiveItem> fsIndexHelper;
|
||||
private FSRLRoot fsrl;
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
public class SevenZipFileSystem extends AbstractFileSystem<ISimpleInArchiveItem> {
|
||||
private Map<Integer, String> passwords = new HashMap<>();
|
||||
|
||||
private IInArchive archive;
|
||||
@ -56,9 +52,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
private ArchiveFormat archiveFormat;
|
||||
|
||||
public SevenZipFileSystem(FSRLRoot fsrl, FileSystemService fsService) {
|
||||
this.fsService = fsService;
|
||||
this.fsrl = fsrl;
|
||||
this.fsIndexHelper = new FileSystemIndexHelper<>(this, fsrl);
|
||||
super(fsrl, fsService);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,7 +80,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
ensurePasswords(monitor);
|
||||
}
|
||||
catch (SevenZipException e) {
|
||||
throw new IOException("Failed to open archive: " + fsrl, e);
|
||||
throw new IOException("Failed to open archive: " + fsFSRL, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,20 +95,19 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
FSUtilities.uncheckedClose(szBPStream, null);
|
||||
szBPStream = null;
|
||||
|
||||
fsIndexHelper.clear();
|
||||
fsIndex.clear();
|
||||
items = null;
|
||||
}
|
||||
|
||||
private void indexFiles(TaskMonitor monitor) throws CancelledException, SevenZipException {
|
||||
monitor.initialize(items.length);
|
||||
monitor.setMessage("Indexing files");
|
||||
monitor.initialize(items.length, "Indexing files");
|
||||
for (ISimpleInArchiveItem item : items) {
|
||||
if (monitor.isCancelled()) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
|
||||
long itemSize = Objects.requireNonNullElse(item.getSize(), -1L);
|
||||
fsIndexHelper.storeFile(fixupItemPath(item), item.getItemIndex(), item.isFolder(),
|
||||
fsIndex.storeFile(fixupItemPath(item), item.getItemIndex(), item.isFolder(),
|
||||
itemSize, item);
|
||||
}
|
||||
}
|
||||
@ -124,7 +117,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
if (items.length == 1 && itemPath.isBlank()) {
|
||||
// special case when there is a single unnamed file.
|
||||
// use the name of the 7zip file itself, minus the extension
|
||||
itemPath = FilenameUtils.getBaseName(fsrl.getContainer().getName());
|
||||
itemPath = FilenameUtils.getBaseName(fsFSRL.getContainer().getName());
|
||||
}
|
||||
if (itemPath.isEmpty()) {
|
||||
itemPath = "<blank>";
|
||||
@ -134,14 +127,15 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
|
||||
private String getPasswordForFile(GFile file, ISimpleInArchiveItem encryptedItem,
|
||||
TaskMonitor monitor) {
|
||||
FSRL containerFSRL = fsFSRL.getContainer();
|
||||
int itemIndex = encryptedItem.getItemIndex();
|
||||
if (!passwords.containsKey(itemIndex)) {
|
||||
try (CryptoSession cryptoSession = fsService.newCryptoSession()) {
|
||||
String prompt = passwords.isEmpty()
|
||||
? fsrl.getContainer().getName()
|
||||
: String.format("%s in %s", file.getName(), fsrl.getContainer().getName());
|
||||
? containerFSRL.getName()
|
||||
: "%s in %s".formatted(file.getName(), containerFSRL.getName());
|
||||
for (Iterator<Password> pwIt =
|
||||
cryptoSession.getPasswordsFor(fsrl.getContainer(), prompt); pwIt.hasNext();) {
|
||||
cryptoSession.getPasswordsFor(containerFSRL, prompt); pwIt.hasNext();) {
|
||||
try (Password passwordValue = pwIt.next()) {
|
||||
monitor.setMessage("Testing password for " + file.getName());
|
||||
|
||||
@ -158,7 +152,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
passwords.put(unlockedFileIndex, password);
|
||||
}
|
||||
if (!successFileIndexes.isEmpty()) {
|
||||
cryptoSession.addSuccessfulPassword(fsrl.getContainer(), passwordValue);
|
||||
cryptoSession.addSuccessfulPassword(containerFSRL, passwordValue);
|
||||
}
|
||||
if (passwords.containsKey(itemIndex)) {
|
||||
break;
|
||||
@ -190,7 +184,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
return arrayResult;
|
||||
}
|
||||
|
||||
private void ensurePasswords(TaskMonitor monitor) throws CancelledException, IOException {
|
||||
private void ensurePasswords(TaskMonitor monitor) throws IOException {
|
||||
// Alert! Unusual code!
|
||||
// Background: contrary to normal expectations, zip container files can have a
|
||||
// unique password per-embedded-file.
|
||||
@ -210,7 +204,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
ISimpleInArchiveItem encryptedItem = null;
|
||||
while ((encryptedItem = getFirstItemWithoutPassword(encryptedItems)) != null &&
|
||||
!monitor.isCancelled()) {
|
||||
GFile gFile = fsIndexHelper.getFileByIndex(encryptedItem.getItemIndex());
|
||||
GFile gFile = fsIndex.getFileByIndex(encryptedItem.getItemIndex());
|
||||
if (gFile == null) {
|
||||
throw new IOException("Unable to retrieve file " + encryptedItem.getPath());
|
||||
}
|
||||
@ -226,7 +220,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
if (!noPasswordFoundList.isEmpty()) {
|
||||
Msg.warn(this,
|
||||
"Unable to find password for " + noPasswordFoundList.size() + " file(s) in " +
|
||||
fsrl.getContainer().getName());
|
||||
fsFSRL.getContainer().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,45 +246,20 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsrl.getContainer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return szBPStream == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return refManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsIndexHelper.lookup(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
return fsIndexHelper.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
FileAttributes result = new FileAttributes();
|
||||
if (fsIndexHelper.getRootDir().equals(file)) {
|
||||
if (fsIndex.getRootDir().equals(file)) {
|
||||
result.add(NAME_ATTR, "/");
|
||||
result.add("Archive Format", archiveFormat.toString());
|
||||
}
|
||||
else {
|
||||
ISimpleInArchiveItem item = fsIndexHelper.getMetadata(file);
|
||||
ISimpleInArchiveItem item = fsIndex.getMetadata(file);
|
||||
if (item == null) {
|
||||
return result;
|
||||
}
|
||||
@ -309,7 +278,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
result.add(SIZE_ATTR, uncheckedGet(item::getSize, null));
|
||||
|
||||
Integer crc = uncheckedGet(item::getCRC, null);
|
||||
result.add("CRC", crc != null ? String.format("%08X", crc) : null);
|
||||
result.add("CRC", crc != null ? "%08X".formatted(crc) : null);
|
||||
result.add("Compression Method", uncheckedGet(item::getMethod, null));
|
||||
result.add(CREATE_DATE_ATTR, uncheckedGet(item::getCreationTime, null));
|
||||
result.add(MODIFIED_DATE_ATTR, uncheckedGet(item::getLastWriteTime, null));
|
||||
@ -321,7 +290,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
try {
|
||||
ISimpleInArchiveItem item = fsIndexHelper.getMetadata(file);
|
||||
ISimpleInArchiveItem item = fsIndex.getMetadata(file);
|
||||
|
||||
if (item == null) {
|
||||
return null;
|
||||
@ -410,8 +379,8 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
if (currentItem.isEncrypted() && !passwords.containsKey(currentIndex)) {
|
||||
// if we lack a password for this item, don't try to extract it
|
||||
Msg.debug(SevenZipFileSystem.this,
|
||||
"No password for file[" + currentIndex + "] " + currentName + " of " +
|
||||
fsrl.getContainer().getName() + ", unable to extract.");
|
||||
"No password for file[%d] %s of %s, unable to extract.".formatted(currentIndex,
|
||||
currentName, fsFSRL.getContainer().getName()));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -425,8 +394,7 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
if (!currentItem.isFolder() && extractAskMode == ExtractAskMode.EXTRACT) {
|
||||
try {
|
||||
currentCacheEntryBuilder = fsService.createTempFile(currentItem.getSize());
|
||||
monitor.initialize(currentItem.getSize());
|
||||
monitor.setMessage("Extracting " + currentName);
|
||||
monitor.initialize(currentItem.getSize(), "Extracting " + currentName);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
@ -441,9 +409,8 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
String password = passwords.get(currentIndex);
|
||||
if (password == null) {
|
||||
|
||||
Msg.debug(SevenZipFileSystem.this,
|
||||
"No password for file[" + currentIndex + "] " + currentName + " of " +
|
||||
fsrl.getContainer().getName());
|
||||
Msg.debug(SevenZipFileSystem.this, "No password for file[%d] %s of %s"
|
||||
.formatted(currentIndex, currentName, fsFSRL.getContainer().getName()));
|
||||
// hack, return a non-null bad password. normally shouldn't get here as
|
||||
// encrypted files w/missing password are skipped by getStream()
|
||||
password = "";
|
||||
@ -456,8 +423,8 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
// STEP 3: SevenZip calls this multiple times for all the bytes in the file.
|
||||
// We write them to our temp file.
|
||||
if (currentCacheEntryBuilder == null) {
|
||||
throw new SevenZipException(
|
||||
"Bad Sevenzip Extract Callback state, " + currentIndex + ", " + currentName);
|
||||
throw new SevenZipException("Bad Sevenzip Extract Callback state, %d, %s"
|
||||
.formatted(currentIndex, currentName));
|
||||
}
|
||||
try {
|
||||
currentCacheEntryBuilder.write(data);
|
||||
@ -479,9 +446,9 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
try {
|
||||
FileCacheEntry fce = currentCacheEntryBuilder.finish();
|
||||
if (extractOperationResult == ExtractOperationResult.OK) {
|
||||
GFile gFile = fsIndexHelper.getFileByIndex(currentIndex);
|
||||
GFile gFile = fsIndex.getFileByIndex(currentIndex);
|
||||
if (gFile != null && gFile.getFSRL().getMD5() == null) {
|
||||
fsIndexHelper.updateFSRL(gFile, gFile.getFSRL().withMD5(fce.getMD5()));
|
||||
fsIndex.updateFSRL(gFile, gFile.getFSRL().withMD5(fce.getMD5()));
|
||||
}
|
||||
if (saveResults) {
|
||||
extractResults.put(currentIndex, fce);
|
||||
@ -490,8 +457,8 @@ public class SevenZipFileSystem implements GFileSystem {
|
||||
FSUtilities.formatSize(fce.length()));
|
||||
}
|
||||
else {
|
||||
Msg.warn(SevenZipFileSystem.this, "Failed to push file[" + currentIndex +
|
||||
"] " + currentName + " to cache: " + extractOperationResult);
|
||||
Msg.warn(SevenZipFileSystem.this, "Failed to push file[%d] %s to cache: %s"
|
||||
.formatted(currentIndex, currentName, extractOperationResult));
|
||||
extractOperationResultToException(extractOperationResult);
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ public class SparseImageFileSystem implements GFileSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
public GFile lookup(String path) {
|
||||
return fsIndexHelper.lookup(path);
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ package ghidra.file.formats.tar;
|
||||
import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
@ -38,23 +37,9 @@ import ghidra.util.task.TaskMonitor;
|
||||
* <p>
|
||||
*/
|
||||
@FileSystemInfo(type = "tar", description = "TAR", priority = FileSystemInfo.PRIORITY_HIGH, factory = TarFileSystemFactory.class)
|
||||
public class TarFileSystem implements GFileSystem {
|
||||
public class TarFileSystem extends AbstractFileSystem<TarMetadata> {
|
||||
|
||||
private static class TarMetadata {
|
||||
TarArchiveEntry tarArchiveEntry;
|
||||
int fileNum;
|
||||
|
||||
TarMetadata(TarArchiveEntry tae, int fileNum) {
|
||||
this.tarArchiveEntry = tae;
|
||||
this.fileNum = fileNum;
|
||||
}
|
||||
}
|
||||
|
||||
private FSRLRoot fsrl;
|
||||
private FileSystemService fsService;
|
||||
private ByteProvider provider;
|
||||
private FileSystemIndexHelper<TarMetadata> fsih;
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
private int fileCount;
|
||||
|
||||
/**
|
||||
@ -65,10 +50,9 @@ public class TarFileSystem implements GFileSystem {
|
||||
* @param fsService reference to the {@link FileSystemService}.
|
||||
*/
|
||||
public TarFileSystem(FSRLRoot fsrl, ByteProvider provider, FileSystemService fsService) {
|
||||
this.fsrl = fsrl;
|
||||
this.fsih = new FileSystemIndexHelper<>(this, fsrl);
|
||||
super(fsrl, fsService);
|
||||
|
||||
this.provider = provider;
|
||||
this.fsService = fsService;
|
||||
}
|
||||
|
||||
ByteProvider getProvider() {
|
||||
@ -84,31 +68,27 @@ public class TarFileSystem implements GFileSystem {
|
||||
monitor.checkCancelled();
|
||||
|
||||
int fileNum = fileCount++;
|
||||
GFile newFile = fsih.storeFile(tarEntry.getName(), fileCount,
|
||||
GFile newFile = fsIndex.storeFile(tarEntry.getName(), fileCount,
|
||||
tarEntry.isDirectory(), tarEntry.getSize(), new TarMetadata(tarEntry, fileNum));
|
||||
|
||||
if (tarEntry.getSize() < FileCache.MAX_INMEM_FILESIZE) {
|
||||
// because tar files are sequential access, we cache smaller files if they
|
||||
// will fit in a in-memory ByteProvider
|
||||
try (ByteProvider bp =
|
||||
fsService.getDerivedByteProvider(fsrl.getContainer(), newFile.getFSRL(),
|
||||
fsService.getDerivedByteProvider(fsFSRL.getContainer(), newFile.getFSRL(),
|
||||
newFile.getPath(), tarEntry.getSize(), () -> tarInput, monitor)) {
|
||||
fsih.updateFSRL(newFile, newFile.getFSRL().withMD5(bp.getFSRL().getMD5()));
|
||||
fsIndex.updateFSRL(newFile,
|
||||
newFile.getFSRL().withMD5(bp.getFSRL().getMD5()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsrl.getContainer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
refManager.onClose();
|
||||
fsih.clear();
|
||||
fsIndex.clear();
|
||||
if (provider != null) {
|
||||
provider.close();
|
||||
provider = null;
|
||||
@ -120,19 +100,9 @@ public class TarFileSystem implements GFileSystem {
|
||||
return provider == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileCount() {
|
||||
return fileCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
TarMetadata tmd = fsih.getMetadata(file);
|
||||
TarMetadata tmd = fsIndex.getMetadata(file);
|
||||
if (tmd == null) {
|
||||
return null;
|
||||
}
|
||||
@ -162,16 +132,11 @@ public class TarFileSystem implements GFileSystem {
|
||||
return FileType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsih.lookup(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
TarMetadata tmd = fsih.getMetadata(file);
|
||||
TarMetadata tmd = fsIndex.getMetadata(file);
|
||||
if (tmd == null) {
|
||||
throw new IOException("Unknown file " + file);
|
||||
}
|
||||
@ -197,14 +162,5 @@ public class TarFileSystem implements GFileSystem {
|
||||
|
||||
return fileBP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
return fsih.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return refManager;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
/* ###
|
||||
* 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.file.formats.tar;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
|
||||
class TarMetadata {
|
||||
TarArchiveEntry tarArchiveEntry;
|
||||
int fileNum;
|
||||
|
||||
TarMetadata(TarArchiveEntry tae, int fileNum) {
|
||||
this.tarArchiveEntry = tae;
|
||||
this.fileNum = fileNum;
|
||||
}
|
||||
}
|
@ -15,13 +15,11 @@
|
||||
*/
|
||||
package ghidra.file.formats.zip;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.sql.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
@ -48,24 +46,13 @@ import ghidra.util.task.TaskMonitor;
|
||||
* and {@link GFileSystem#getDescription()} to operate in the default manner.
|
||||
*/
|
||||
@FileSystemInfo(type = "zip", description = "ZIP", factory = ZipFileSystemFactory.class, priority = FileSystemInfo.PRIORITY_HIGH)
|
||||
public class ZipFileSystemBuiltin implements GFileSystem {
|
||||
public class ZipFileSystemBuiltin extends AbstractFileSystem<ZipEntry> {
|
||||
static final String TEMPFILE_PREFIX = "ghidra_tmp_zipfile";
|
||||
|
||||
private FileSystemIndexHelper<ZipEntry> fsIndexHelper;
|
||||
private FSRLRoot fsFSRL;
|
||||
private ZipFile zipFile;
|
||||
private FileSystemRefManager refManager = new FileSystemRefManager(this);
|
||||
private FileSystemService fsService;
|
||||
|
||||
public ZipFileSystemBuiltin(FSRLRoot fsFSRL, FileSystemService fsService) {
|
||||
this.fsFSRL = fsFSRL;
|
||||
this.fsService = fsService;
|
||||
this.fsIndexHelper = new FileSystemIndexHelper<>(this, fsFSRL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return fsFSRL.getContainer().getName();
|
||||
super(fsFSRL, fsService);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -75,7 +62,7 @@ public class ZipFileSystemBuiltin implements GFileSystem {
|
||||
zipFile.close();
|
||||
zipFile = null;
|
||||
}
|
||||
fsIndexHelper.clear();
|
||||
fsIndex.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -83,16 +70,6 @@ public class ZipFileSystemBuiltin implements GFileSystem {
|
||||
return zipFile == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSRLRoot getFSRL() {
|
||||
return fsFSRL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFileCount() {
|
||||
return fsIndexHelper.getFileCount();
|
||||
}
|
||||
|
||||
public void mount(File f, boolean deleteFileWhenDone, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
|
||||
@ -109,14 +86,14 @@ public class ZipFileSystemBuiltin implements GFileSystem {
|
||||
while (entries.hasMoreElements()) {
|
||||
monitor.checkCancelled();
|
||||
ZipEntry currentEntry = entries.nextElement();
|
||||
fsIndexHelper.storeFile(currentEntry.getName(), -1, currentEntry.isDirectory(),
|
||||
fsIndex.storeFile(currentEntry.getName(), -1, currentEntry.isDirectory(),
|
||||
currentEntry.getSize(), currentEntry);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
|
||||
ZipEntry zipEntry = fsIndexHelper.getMetadata(file);
|
||||
ZipEntry zipEntry = fsIndex.getMetadata(file);
|
||||
if (zipEntry == null) {
|
||||
return null;
|
||||
}
|
||||
@ -137,22 +114,17 @@ public class ZipFileSystemBuiltin implements GFileSystem {
|
||||
return "ZipFilesystemBuiltin [ fsrl=" + fsFSRL + ", filename=" + zipFile.getName() + " ]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public GFile lookup(String path) throws IOException {
|
||||
return fsIndexHelper.lookup(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
ZipEntry zipEntry = fsIndexHelper.getMetadata(file);
|
||||
ZipEntry zipEntry = fsIndex.getMetadata(file);
|
||||
return (zipEntry != null) ? zipFile.getInputStream(zipEntry) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
ZipEntry zipEntry = fsIndexHelper.getMetadata(file);
|
||||
ZipEntry zipEntry = fsIndex.getMetadata(file);
|
||||
if (zipEntry == null) {
|
||||
return null;
|
||||
}
|
||||
@ -164,14 +136,4 @@ public class ZipFileSystemBuiltin implements GFileSystem {
|
||||
() -> zipFile.getInputStream(zipEntry),
|
||||
monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GFile> getListing(GFile directory) throws IOException {
|
||||
return fsIndexHelper.getListing(directory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystemRefManager getRefManager() {
|
||||
return refManager;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user