Merge remote-tracking branch 'origin/GP-5208_dev747368_use_filename_compare_for_lookups--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-12-27 11:16:01 -05:00
commit 2c9f7fcacf
14 changed files with 155 additions and 71 deletions

View file

@ -1181,27 +1181,20 @@ public abstract class AbstractLibrarySupportLoader extends AbstractProgramLoader
*/
protected FSRL resolveLibraryFile(GFileSystem fs, Path libraryParentPath, String libraryName)
throws IOException {
GFile libraryParentDir = fs.lookup(
libraryParentPath != null ? FilenameUtils.separatorsToUnix(libraryParentPath.toString())
: null);
boolean compareWithoutExtension = isOptionalLibraryFilenameExtensions() &&
FilenameUtils.getExtension(libraryName).equals("");
if (libraryParentDir != null) {
Comparator<String> libNameComparator = getLibraryNameComparator();
for (GFile file : fs.getListing(libraryParentDir)) {
if (file.isDirectory()) {
continue;
}
String compareName = file.getName();
if (compareWithoutExtension) {
compareName = FilenameUtils.getBaseName(compareName);
}
if (libNameComparator.compare(libraryName, compareName) == 0) {
return file.getFSRL();
}
}
}
return null;
String lpp = libraryParentPath != null
? FilenameUtils.separatorsToUnix(libraryParentPath.toString())
: null;
String targetPath = FSUtilities.appendPath(lpp, libraryName);
Comparator<String> baseNameComp = getLibraryNameComparator();
Comparator<String> nameComp = isOptionalLibraryFilenameExtensions() &&
FilenameUtils.getExtension(libraryName).isEmpty()
? (s1, s2) -> baseNameComp.compare(FilenameUtils.getBaseName(s1),
FilenameUtils.getBaseName(s2))
: baseNameComp;
GFile foundFile = fs.lookup(targetPath, nameComp);
return foundFile != null && !foundFile.isDirectory() ? foundFile.getFSRL() : null;
}
/**

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -66,6 +66,11 @@ public abstract class AbstractFileSystem<METADATATYPE> implements GFileSystem {
return fsIndex.lookup(null, path, getFilenameComparator());
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsIndex.lookup(null, path, nameComp);
}
@Override
public GFile getRootDir() {
return fsIndex.getRootDir();

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -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;
@ -23,6 +24,7 @@ import ghidra.app.util.bin.ByteProviderInputStream;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.fileinfo.FileAttribute;
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
import ghidra.util.Msg;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -129,6 +131,26 @@ public interface GFileSystem extends Closeable, ExtensionPoint {
*/
GFile lookup(String path) throws IOException;
/**
* Retrieves a {@link GFile} from this filesystem based on its full path and filename, using
* the specified 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
* @param nameComp string comparator used to compare filenames. Use {@code null} to specify
* the file system's native comparison logic.
* @return {@link GFile} instance of requested file, null if not found.
* @throws IOException if IO error when looking up file.
*/
default GFile lookup(String path, Comparator<String> nameComp) throws IOException {
// If you are seeing this error in your log file, it means your GFileSystem needs to be
// updated to implement this lookup() method.
Msg.error(GFileSystem.class,
"Unimplemented %s.lookup(path, comparator), falling back to non-comparator lookup"
.formatted(this.getClass().getSimpleName()));
return lookup(path);
}
/**
* Returns the file system's root directory.
* <p>

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -165,10 +165,14 @@ public abstract class GFileSystemBase implements GFileSystem {
@Override
public GFile lookup(String path) throws IOException {
return lookup(path, getFilenameComparator());
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
if (path == null || path.equals("/")) {
return root;
}
Comparator<String> nameComp = getFilenameComparator();
GFile current = root;
String[] parts = path.split("/");

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -213,7 +213,12 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
@Override
public GFile lookup(String path) throws IOException {
File f = lookupFile(null, path, null);
return lookup(path, null);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
File f = lookupFile(null, path, nameComp);
return f != null ? GFileImpl.fromPathString(this,
FSUtilities.normalizeNativePath(f.getPath()), null, f.isDirectory(), f.length()) : null;
}
@ -327,24 +332,8 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
// If not using a comparator, or if the requested path is a
// root element (eg "/", or "c:\\"), don't do per-directory-path lookups.
if (OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS) {
// 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.
// We don't want to do this on unix-ish file systems as it will follow symlinks
f = f.getCanonicalFile();
}
return FSUtilities.isSymlink(f) || f.exists() ? f : null;
}
// Test the file's path using the name comparator
if (f.exists() && baseDir == null) {
// 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;
}
f = updateCaseInsensitiveFilePath(f);
return (FSUtilities.isSymlink(f) || f.exists()) ? f : null;
}
// For path "/subdir/file", pathParts will contain, in reverse order:
@ -375,7 +364,28 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
}
}
static File findInDir(File dir, String name, Comparator<String> nameComp) {
static File updateCaseInsensitiveFilePath(File f) throws IOException {
// On Windows, getCanonicalFile() will return a corrected path using the case of
// the actual file element on the file system (eg. "c:/users" -> "c:/Users"), if the
// element exists.
return OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS
? f.getCanonicalFile()
: f;
}
static File findInDir(File dir, String name, Comparator<String> nameComp) throws IOException {
File exact = new File(dir, name);
if (exact.exists()) {
// Skip listing entire dir contents if exact match exists and the match agrees with
// the caller's comparator. The comparator could reject this test, for example
// if it was an exact case compare and the requested filename's case didn't match
// the actual file's case (on windows).
exact = updateCaseInsensitiveFilePath(exact);
if (nameComp.compare(exact.getName(), name) == 0) {
return exact;
}
}
// 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
@ -393,7 +403,7 @@ public class LocalFileSystem implements GFileSystem, GFileHashProvider {
}
}
}
Collections.sort(candidateMatches);
Collections.sort(candidateMatches); // ensures stable results regardless if OS mutates order of listing on different runs
return !candidateMatches.isEmpty() ? candidateMatches.get(0) : null;
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -16,8 +16,7 @@
package ghidra.formats.gfilesystem;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.fileinfo.FileAttributes;
@ -155,8 +154,13 @@ public class LocalFileSystemSub implements GFileSystem, GFileHashProvider {
@Override
public GFile lookup(String path) throws IOException {
File f = LocalFileSystem.lookupFile(localfsRootDir, path, null);
if ( f == null ) {
return lookup(path, null);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
File f = LocalFileSystem.lookupFile(localfsRootDir, path, nameComp);
if (f == null) {
return null;
}
GFile result = getGFile(f);

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -197,6 +197,11 @@ public class CartFileSystem implements GFileSystem {
return fsIndexHelper.lookup(path);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsIndexHelper.lookup(null, path, nameComp);
}
/**
* Helper function to create byte provider for CaRT payload content that is
* decompressed and decrypted.

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,6 +17,7 @@ package ghidra.file.formats.complzss;
import java.io.IOException;
import java.io.InputStream;
import java.util.Comparator;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
@ -99,4 +100,9 @@ public class CompLzssFileSystem implements GFileSystem {
return fsIndex.lookup(path);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsIndex.lookup(null, path, nameComp);
}
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -140,6 +140,11 @@ public class GZipFileSystem implements GFileSystem {
return fsIndex.lookup(path);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsIndex.lookup(null, path, nameComp);
}
@Override
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
throws IOException, CancelledException {

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -16,6 +16,7 @@
package ghidra.file.formats.ios.img2;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
@ -106,4 +107,9 @@ public class Img2FileSystem implements GFileSystem {
return fsIndexHelper.lookup(path);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsIndexHelper.lookup(null, path, nameComp);
}
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -16,6 +16,7 @@
package ghidra.file.formats.java;
import java.io.*;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.io.FilenameUtils;
@ -134,6 +135,11 @@ public class JavaClassDecompilerFileSystem implements GFileSystem {
return fsIndexHelper.lookup(path);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsIndexHelper.lookup(null, path, nameComp);
}
@Override
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
throws IOException, CancelledException {

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,6 +17,7 @@ package ghidra.file.formats.lzfse;
import java.io.File;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
@ -111,4 +112,9 @@ public class LzfseFileSystem implements GFileSystem {
public GFile lookup(String path) throws IOException {
return fsIndex.lookup(path);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsIndex.lookup(null, path, nameComp);
}
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,6 +18,7 @@ package ghidra.file.formats.sparseimage;
import static ghidra.formats.gfilesystem.fileinfo.FileAttributeType.*;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
@ -96,6 +97,11 @@ public class SparseImageFileSystem implements GFileSystem {
return fsIndexHelper.lookup(path);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsIndexHelper.lookup(null, path, nameComp);
}
private ByteProvider getPayload(FSRL payloadFSRL, TaskMonitor monitor)
throws CancelledException, IOException {
return fsService.getDerivedByteProviderPush(byteProvider.getFSRL(), payloadFSRL, "sparse",

View file

@ -16,6 +16,7 @@
package skeleton;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import ghidra.app.util.bin.ByteProvider;
@ -112,6 +113,11 @@ public class SkeletonFileSystem implements GFileSystem {
return fsih.lookup(path);
}
@Override
public GFile lookup(String path, Comparator<String> nameComp) throws IOException {
return fsih.lookup(null, path, nameComp);
}
@Override
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
throws IOException, CancelledException {