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 3354e38dc7..13572a96e9 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 @@ -177,7 +177,10 @@ public class FileSystemIndexHelper { */ public synchronized GFile lookup(GFile baseDir, String path, Comparator nameComp) { try { - return lookup(baseDir, path, false, nameComp); + FileData baseDirData = getFileData(baseDir); + FileData fileData = + lookup(baseDirData, splitPath(path), -1, false, nameComp); + return (fileData != null) ? fileData.file : null; } catch (IOException e) { // shouldn't happen, fall thru @@ -185,43 +188,16 @@ public class FileSystemIndexHelper { return null; } - protected GFile lookup(GFile baseDir, String path, boolean followSymlinks, - Comparator nameComp) throws IOException { - FileData baseDirData = getFileData(baseDir); - FileData fileData = - lookup(baseDirData, splitPath(path), -1, false, followSymlinks, 0, null, nameComp); - return (fileData != null) ? fileData.file : null; - } - protected FileData lookup(FileData baseDir, String[] nameparts, - int maxpart, boolean createIfMissing, boolean followSymlinks, int depth, - StringBuilder symlinkPathDebug, Comparator nameComp) throws IOException { - symlinkPathDebug = Objects.requireNonNullElseGet(symlinkPathDebug, StringBuilder::new); + int maxpart, boolean createIfMissing, Comparator nameComp) { maxpart = maxpart < 0 ? nameparts.length : maxpart; - if (depth > MAX_SYMLINK_RECURSE_DEPTH) { - throw new IOException( - "Too many symlinks: %s, %s".formatted(symlinkPathDebug, Arrays.asList(nameparts))); - } - - symlinkPathDebug.append("["); FileData currentFile = Objects.requireNonNullElse(baseDir, rootDir); for (int i = 0; i < maxpart && currentFile != null; i++) { String name = nameparts[i]; - symlinkPathDebug.append(i != 0 ? "," : "").append(name); if (name.isEmpty()) { continue; } - if (followSymlinks) { - // otherwise "." and ".." are valid path elements that need to be matched exactly - if (".".equals(name)) { - continue; - } - if ("..".equals(name)) { - currentFile = getParentFileData(currentFile); - continue; - } - } Map> currentDirContents = getDirectoryContents(currentFile.file, createIfMissing); @@ -229,9 +205,46 @@ public class FileSystemIndexHelper { if (next == null && createIfMissing) { next = doStoreMissingDir(name, currentFile.file); } - if (next != null && next.symlinkPath != null && followSymlinks) { - next = lookup(currentFile, splitPath(next.symlinkPath), -1, createIfMissing, - followSymlinks, depth + 1, symlinkPathDebug, nameComp); + currentFile = next; + } + + return currentFile; + } + + protected FileData resolveSymlinkPath(FileData baseDir, String path, + int depth, StringBuilder symlinkPathDebug, Comparator nameComp) + throws IOException { + symlinkPathDebug = Objects.requireNonNullElseGet(symlinkPathDebug, StringBuilder::new); + + if (depth > MAX_SYMLINK_RECURSE_DEPTH) { + throw new IOException("Too many symlinks: %s, %s".formatted(symlinkPathDebug, path)); + } + + symlinkPathDebug.append("["); + FileData currentFile = Objects.requireNonNullElse(baseDir, rootDir); + String[] pathparts = splitPath(path); + for (int i = 0; i < pathparts.length && currentFile != null; i++) { + String name = pathparts[i]; + symlinkPathDebug.append(i != 0 ? "," : "").append(name); + if (i == 0 && name.isEmpty()) { + // leading '/' was present in the path, it overrides the current location + currentFile = rootDir; + continue; + } + if (name.isEmpty() || ".".equals(name)) { + continue; + } + if ("..".equals(name)) { + currentFile = getParentFileData(currentFile); + continue; + } + + Map> currentDirContents = + getDirectoryContents(currentFile.file, false); + FileData next = lookupFileInDir(currentDirContents, name, nameComp); + if (next != null && next.symlinkPath != null) { + next = resolveSymlinkPath(currentFile, next.symlinkPath, depth + 1, + symlinkPathDebug, nameComp); } currentFile = next; } @@ -252,8 +265,7 @@ public class FileSystemIndexHelper { public synchronized GFile resolveSymlinks(GFile file) throws IOException { FileData fd = getFileData(file); if (fd.symlinkPath != null) { - fd = lookup(getParentFileData(fd), splitPath(fd.symlinkPath), -1, false, true, 0, null, - null); + fd = resolveSymlinkPath(getParentFileData(fd), fd.symlinkPath, 0, null, null); } return fd != null ? fd.file : null; } @@ -481,16 +493,9 @@ public class FileSystemIndexHelper { */ protected GFile lookupParent(String[] nameparts, Comparator nameComp) { - try { - FileData parent = - lookup(rootDir, nameparts, nameparts.length - 1, true, false, 0, null, nameComp); - return parent.file; - } - catch (IOException e) { - // fall thru, return rootdir - } - - return rootDir.file; + FileData parent = + lookup(rootDir, nameparts, nameparts.length - 1, true, nameComp); + return parent.file; } protected String[] splitPath(String path) { 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 7701ae6005..f650d3b654 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 @@ -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. @@ -234,15 +234,6 @@ public class Ext4FileSystem extends AbstractFileSystem { return inode; } - /** - * Returns a {@link ByteProvider} that supplies the bytes of the requested file. - * - * @param file {@link GFile} to get - * @param monitor {@link TaskMonitor} to cancel - * @return {@link ByteProvider} containing the bytes of the requested file, caller is - * responsible for closing the ByteProvider - * @throws IOException if error - */ @Override public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) throws IOException { file = fsIndex.resolveSymlinks(file);