GP-5726 fix absolute symlink path resolution

Absolute paths were being treated as relative.
This commit is contained in:
dev747368 2025-05-29 19:51:06 +00:00
parent d4120b4b4d
commit 3ecbccaf6f
2 changed files with 51 additions and 55 deletions

View file

@ -177,7 +177,10 @@ public class FileSystemIndexHelper<METADATATYPE> {
*/ */
public synchronized GFile lookup(GFile baseDir, String path, Comparator<String> nameComp) { public synchronized GFile lookup(GFile baseDir, String path, Comparator<String> nameComp) {
try { try {
return lookup(baseDir, path, false, nameComp); FileData<METADATATYPE> baseDirData = getFileData(baseDir);
FileData<METADATATYPE> fileData =
lookup(baseDirData, splitPath(path), -1, false, nameComp);
return (fileData != null) ? fileData.file : null;
} }
catch (IOException e) { catch (IOException e) {
// shouldn't happen, fall thru // shouldn't happen, fall thru
@ -185,43 +188,16 @@ public class FileSystemIndexHelper<METADATATYPE> {
return null; return null;
} }
protected GFile lookup(GFile baseDir, String path, boolean followSymlinks,
Comparator<String> nameComp) throws IOException {
FileData<METADATATYPE> baseDirData = getFileData(baseDir);
FileData<METADATATYPE> fileData =
lookup(baseDirData, splitPath(path), -1, false, followSymlinks, 0, null, nameComp);
return (fileData != null) ? fileData.file : null;
}
protected FileData<METADATATYPE> lookup(FileData<METADATATYPE> baseDir, String[] nameparts, protected FileData<METADATATYPE> lookup(FileData<METADATATYPE> baseDir, String[] nameparts,
int maxpart, boolean createIfMissing, boolean followSymlinks, int depth, int maxpart, boolean createIfMissing, Comparator<String> nameComp) {
StringBuilder symlinkPathDebug, Comparator<String> nameComp) throws IOException {
symlinkPathDebug = Objects.requireNonNullElseGet(symlinkPathDebug, StringBuilder::new);
maxpart = maxpart < 0 ? nameparts.length : maxpart; 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<METADATATYPE> currentFile = Objects.requireNonNullElse(baseDir, rootDir); FileData<METADATATYPE> currentFile = Objects.requireNonNullElse(baseDir, rootDir);
for (int i = 0; i < maxpart && currentFile != null; i++) { for (int i = 0; i < maxpart && currentFile != null; i++) {
String name = nameparts[i]; String name = nameparts[i];
symlinkPathDebug.append(i != 0 ? "," : "").append(name);
if (name.isEmpty()) { if (name.isEmpty()) {
continue; 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<String, FileData<METADATATYPE>> currentDirContents = Map<String, FileData<METADATATYPE>> currentDirContents =
getDirectoryContents(currentFile.file, createIfMissing); getDirectoryContents(currentFile.file, createIfMissing);
@ -229,9 +205,46 @@ public class FileSystemIndexHelper<METADATATYPE> {
if (next == null && createIfMissing) { if (next == null && createIfMissing) {
next = doStoreMissingDir(name, currentFile.file); next = doStoreMissingDir(name, currentFile.file);
} }
if (next != null && next.symlinkPath != null && followSymlinks) { currentFile = next;
next = lookup(currentFile, splitPath(next.symlinkPath), -1, createIfMissing, }
followSymlinks, depth + 1, symlinkPathDebug, nameComp);
return currentFile;
}
protected FileData<METADATATYPE> resolveSymlinkPath(FileData<METADATATYPE> baseDir, String path,
int depth, StringBuilder symlinkPathDebug, Comparator<String> 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<METADATATYPE> 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<String, FileData<METADATATYPE>> currentDirContents =
getDirectoryContents(currentFile.file, false);
FileData<METADATATYPE> next = lookupFileInDir(currentDirContents, name, nameComp);
if (next != null && next.symlinkPath != null) {
next = resolveSymlinkPath(currentFile, next.symlinkPath, depth + 1,
symlinkPathDebug, nameComp);
} }
currentFile = next; currentFile = next;
} }
@ -252,8 +265,7 @@ public class FileSystemIndexHelper<METADATATYPE> {
public synchronized GFile resolveSymlinks(GFile file) throws IOException { public synchronized GFile resolveSymlinks(GFile file) throws IOException {
FileData<METADATATYPE> fd = getFileData(file); FileData<METADATATYPE> fd = getFileData(file);
if (fd.symlinkPath != null) { if (fd.symlinkPath != null) {
fd = lookup(getParentFileData(fd), splitPath(fd.symlinkPath), -1, false, true, 0, null, fd = resolveSymlinkPath(getParentFileData(fd), fd.symlinkPath, 0, null, null);
null);
} }
return fd != null ? fd.file : null; return fd != null ? fd.file : null;
} }
@ -481,17 +493,10 @@ public class FileSystemIndexHelper<METADATATYPE> {
*/ */
protected GFile lookupParent(String[] nameparts, Comparator<String> nameComp) { protected GFile lookupParent(String[] nameparts, Comparator<String> nameComp) {
try {
FileData<METADATATYPE> parent = FileData<METADATATYPE> parent =
lookup(rootDir, nameparts, nameparts.length - 1, true, false, 0, null, nameComp); lookup(rootDir, nameparts, nameparts.length - 1, true, nameComp);
return parent.file; return parent.file;
} }
catch (IOException e) {
// fall thru, return rootdir
}
return rootDir.file;
}
protected String[] splitPath(String path) { protected String[] splitPath(String path) {
return Objects.requireNonNullElse(path, "").replace('\\', '/').split("/"); return Objects.requireNonNullElse(path, "").replace('\\', '/').split("/");

View file

@ -234,15 +234,6 @@ public class Ext4FileSystem extends AbstractFileSystem<Ext4File> {
return inode; 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 @Override
public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) throws IOException { public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) throws IOException {
file = fsIndex.resolveSymlinks(file); file = fsIndex.resolveSymlinks(file);