diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/AbstractFileSystem.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/AbstractFileSystem.java new file mode 100644 index 0000000000..b11a7c90b7 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/AbstractFileSystem.java @@ -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 the type of objects that will be stored in the FileSystemIndexHelper + */ +public abstract class AbstractFileSystem implements GFileSystem { + protected final FileSystemService fsService; + protected final FSRLRoot fsFSRL; + protected FileSystemIndexHelper 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 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 getListing(GFile directory) { + return fsIndex.getListing(directory); + } + + @Override + public int getFileCount() { + return fsIndex.getFileCount(); + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemIndexHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemIndexHelper.java index 848eecf0fe..1d5fb61b43 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemIndexHelper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemIndexHelper.java @@ -160,15 +160,32 @@ public class FileSystemIndexHelper { * @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 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> dirListing = getDirectoryContents(parent, false); - FileData fileData = (dirListing != null) ? dirListing.get(name) : null; + FileData fileData = + lookupFileInDir(getDirectoryContents(parent, false), name, nameComp); return (fileData != null) ? fileData.file : null; } @@ -197,7 +214,7 @@ public class FileSystemIndexHelper { 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 fileData = @@ -301,19 +318,27 @@ public class FileSystemIndexHelper { * 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. *

- * 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. *

* 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. *

- * @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 nameComp) { - GFile currentDir = rootDir; + GFile currentDir = Objects.requireNonNullElse(baseDir, rootDir); for (int i = 0; i < nameparts.length - 1; i++) { Map> currentDirContents = getDirectoryContents(currentDir, true); @@ -321,8 +346,11 @@ public class FileSystemIndexHelper { if (name.isEmpty()) { continue; } - FileData fileData = currentDirContents.get(name); + FileData 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 { return currentDir; } + protected FileData lookupFileInDir( + Map> dirContents, String filename, + Comparator nameComp) { + if (dirContents == null) { + return null; + } + if (nameComp == null) { + // exact match + return dirContents.get(filename); + } + List> candidateFiles = new ArrayList<>(); + for (FileData 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. *

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileImpl.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileImpl.java index acfd707276..0fbcc2ae22 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileImpl.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileImpl.java @@ -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); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileSystem.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileSystem.java index 975367dab3..062ce153d2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileSystem.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileSystem.java @@ -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). *

* @param path string path and filename of a file located in this filesystem. Use * {@code null} or "/" to retrieve the root directory diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileSystemBase.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileSystemBase.java index d860246646..8d874c7e4a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileSystemBase.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/GFileSystemBase.java @@ -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 getFilenameComparator() { + return String::compareTo; + } + @Override public GFile lookup(String path) throws IOException { if (path == null || path.equals("/")) { return root; } - GFile current = null; + Comparator 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 listing = getListing(current); for (GFile gf : listing) { - if (part.equals(gf.getName())) { + if (nameComp.compare(part, gf.getName()) == 0) { current = gf; continue partloop; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystem.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystem.java index 81878c5471..91c03b0819 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystem.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystem.java @@ -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. + *

+ * If any element of the path, or the filename are not found, returns a null. + *

+ * 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 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 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 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 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 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 results = new ArrayList(); + while (f != null) { + results.add(f); + f = f.getParentFile(); + } + return results; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystemSub.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystemSub.java index 41a5505984..864654cab0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystemSub.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/LocalFileSystemSub.java @@ -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 parts = LocalFileSystem.getFilePathParts(f); // [/subdir/subroot/file, /subdir/subroot, /subdir, /] + int rootDirIndex = findRootDirIndex(parts); + if (rootDirIndex < 0) { + throw new IOException("Invalid directory " + f); + } + GFile current = rootGFile; + for (int i = rootDirIndex - 1; i >= 0; i--) { + File part = parts.get(i); + FSRL childFSRL = current.getFSRL().appendPath(part.getName()); + String childPath = FSUtilities.appendPath(current.getPath(), part.getName()); + current = new GFileLocal(part, childPath, childFSRL, this, current); + } + return current; + } + + private int findRootDirIndex(List dirList) { + for (int i = 0; i < dirList.size(); i++) { + if (localfsRootDir.equals(dirList.get(i))) { + return i; + } + } + return -1; + } + @Override public InputStream getInputStream(GFile file, TaskMonitor monitor) throws IOException, CancelledException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/SingleFileSystemIndexHelper.java b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/SingleFileSystemIndexHelper.java index 2195a2bab7..5a9b2e1474 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/SingleFileSystemIndexHelper.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/SingleFileSystemIndexHelper.java @@ -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 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 diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/formats/gfilesystem/LocalGFileSystemTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/formats/gfilesystem/LocalGFileSystemTest.java new file mode 100644 index 0000000000..8341eeda7d --- /dev/null +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/formats/gfilesystem/LocalGFileSystemTest.java @@ -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); + } + } + +} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/coff/CoffArchiveFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/coff/CoffArchiveFileSystem.java index 45723d8a12..4be79dbc56 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/coff/CoffArchiveFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/coff/CoffArchiveFileSystem.java @@ -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 fsih; - private FileSystemRefManager refManager = new FileSystemRefManager(this); +public class CoffArchiveFileSystem extends AbstractFileSystem { 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 getListing(GFile directory) throws IOException { - return fsih.getListing(directory); - } - - @Override - public FileSystemRefManager getRefManager() { - return refManager; - } } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/cpio/CpioFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/cpio/CpioFileSystem.java index 5d8a998110..aaa0ffd6d6 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/cpio/CpioFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/cpio/CpioFileSystem.java @@ -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 fsIndex; - private FileSystemRefManager fsRefManager = new FileSystemRefManager(this); +public class CpioFileSystem extends AbstractFileSystem { 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 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(); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystem.java index abab0c14aa..993bbf8e9f 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ext4/Ext4FileSystem.java @@ -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 { public static final Charset EXT4_DEFAULT_CHARSET = StandardCharsets.UTF_8; - private FileSystemIndexHelper 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 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); - } - } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/gzip/GZipFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/gzip/GZipFileSystem.java index d707e49d08..33c72b0ea8 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/gzip/GZipFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/gzip/GZipFileSystem.java @@ -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); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dmg/DmgClientFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dmg/DmgClientFileSystem.java index 7bfd717707..5e238ad2e4 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dmg/DmgClientFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dmg/DmgClientFileSystem.java @@ -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 { - private final FSRLRoot fsrl; - private FileSystemRefManager refManager = new FileSystemRefManager(this); - private FileSystemIndexHelper 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 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); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img2/Img2FileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img2/Img2FileSystem.java index 9c1a936675..fa4da92201 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img2/Img2FileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img2/Img2FileSystem.java @@ -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); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img3/Img3FileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img3/Img3FileSystem.java index a44b33a91a..3aadc1ca40 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img3/Img3FileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/img3/Img3FileSystem.java @@ -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 { - private FSRLRoot fsFSRL; - private FileSystemRefManager fsRefManager = new FileSystemRefManager(this); - private FileSystemIndexHelper 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 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); - } - } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/java/JavaClassDecompilerFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/java/JavaClassDecompilerFileSystem.java index f86de1d061..4f90c5b153 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/java/JavaClassDecompilerFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/java/JavaClassDecompilerFileSystem.java @@ -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); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf/OmfArchiveFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf/OmfArchiveFileSystem.java index 7cf6e2bc9b..7864d15716 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf/OmfArchiveFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/omf/OmfArchiveFileSystem.java @@ -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 fsih; - private FileSystemRefManager refManager = new FileSystemRefManager(this); +public class OmfArchiveFileSystem extends AbstractFileSystem { 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 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); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sevenzip/SevenZipFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sevenzip/SevenZipFileSystem.java index 259afd7064..891ba38c6c 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sevenzip/SevenZipFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sevenzip/SevenZipFileSystem.java @@ -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 fsIndexHelper; - private FSRLRoot fsrl; - private FileSystemRefManager refManager = new FileSystemRefManager(this); +public class SevenZipFileSystem extends AbstractFileSystem { private Map 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 = ""; @@ -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 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 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); } } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sparseimage/SparseImageFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sparseimage/SparseImageFileSystem.java index 0a3a77fd56..6191d1cf4a 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sparseimage/SparseImageFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/sparseimage/SparseImageFileSystem.java @@ -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); } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/tar/TarFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/tar/TarFileSystem.java index 88ac1eb0f9..e1cbea71cc 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/tar/TarFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/tar/TarFileSystem.java @@ -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; *

*/ @FileSystemInfo(type = "tar", description = "TAR", priority = FileSystemInfo.PRIORITY_HIGH, factory = TarFileSystemFactory.class) -public class TarFileSystem implements GFileSystem { +public class TarFileSystem extends AbstractFileSystem { - 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 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 getListing(GFile directory) throws IOException { - return fsih.getListing(directory); - } - - @Override - public FileSystemRefManager getRefManager() { - return refManager; - } } + diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/tar/TarMetadata.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/tar/TarMetadata.java new file mode 100644 index 0000000000..45ecfb1daa --- /dev/null +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/tar/TarMetadata.java @@ -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; + } +} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/zip/ZipFileSystemBuiltin.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/zip/ZipFileSystemBuiltin.java index f28bed2c20..aea0ea2737 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/zip/ZipFileSystemBuiltin.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/zip/ZipFileSystemBuiltin.java @@ -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 { static final String TEMPFILE_PREFIX = "ghidra_tmp_zipfile"; - private FileSystemIndexHelper 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 getListing(GFile directory) throws IOException { - return fsIndexHelper.getListing(directory); - } - - @Override - public FileSystemRefManager getRefManager() { - return refManager; - } }