Skip to content

Commit

Permalink
GP-4640 add support for symlinks in fs browser, reorg actions
Browse files Browse the repository at this point in the history
  • Loading branch information
dev747368 committed Jul 15, 2024
1 parent d81f79e commit 3f86719
Show file tree
Hide file tree
Showing 64 changed files with 3,593 additions and 3,054 deletions.
1 change: 1 addition & 0 deletions Ghidra/Features/Base/data/ExtensionPoint.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ ChecksumAlgorithm
OverviewColorService
DWARFFunctionFixup
ElfInfoProducer
FSBFileHandler
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ icon.fsbrowser.file.extension.zip = images/oxygen/16x16/application-x-bzi
icon.fsbrowser.file.substring.release. = images/famfamfam_silk_icons_v013/bullet_purple.png

icon.fsbrowser.file.overlay.imported = EMPTY_ICON{images/checkmark_green.gif[size(8,8)][move(8,8)]} // lower right quadrant
icon.fsbrowser.file.overlay.filesystem = EMPTY_ICON{images/ledgreen.png[size(8,8)][move(0,8)]} // lower left quadrant
icon.fsbrowser.file.overlay.missing.password = EMPTY_ICON{images/lock.png[size(8,8)][move(8,0)]} // upper right quadrant
icon.fsbrowser.file.overlay.filesystem = EMPTY_ICON{images/ledgreen.png[size(8,8)][move(0,8)]} // lower left quadrant
icon.fsbrowser.file.overlay.link = EMPTY_ICON{icon.content.handler.link[move(0,8)]} // lower-left quadrant
icon.fsbrowser.file.overlay.missing.password = EMPTY_ICON{images/lock.png[size(8,8)][move(8,0)]} // upper right quadrant
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import ghidra.formats.gfilesystem.FSRL;
import ghidra.framework.model.*;
import ghidra.framework.store.LockException;
import ghidra.plugin.importer.ProgramMappingService;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.*;
Expand Down Expand Up @@ -367,8 +366,7 @@ public static void setProgramProperties(Program prog, ByteProvider provider,
if (fsrl.getMD5() == null) {
fsrl = fsrl.withMD5(md5);
}
prog.getOptions(Program.PROGRAM_INFO)
.setString(ProgramMappingService.PROGRAM_SOURCE_FSRL, fsrl.toString());
FSRL.writeToProgramInfo(prog, fsrl);
}
prog.setExecutableMD5(md5);
String sha256 = computeBinarySHA256(provider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package ghidra.formats.gfilesystem;

import java.io.IOException;
import java.util.Comparator;
import java.util.List;

Expand Down Expand Up @@ -65,6 +66,11 @@ public GFile lookup(String path) {
return fsIndex.lookup(null, path, getFilenameComparator());
}

@Override
public GFile getRootDir() {
return fsIndex.getRootDir();
}

@Override
public List<GFile> getListing(GFile directory) {
return fsIndex.getListing(directory);
Expand All @@ -75,4 +81,9 @@ public int getFileCount() {
return fsIndex.getFileCount();
}

@Override
public GFile resolveSymlinks(GFile file) throws IOException {
return fsIndex.resolveSymlinks(file);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.net.MalformedURLException;
import java.util.*;

import ghidra.plugin.importer.ProgramMappingService;
import ghidra.program.model.listing.Program;
import ghidra.util.SystemUtilities;

Expand Down Expand Up @@ -64,6 +63,7 @@
*/
public class FSRL {
public static final String PARAM_MD5 = "MD5";
public static final String FSRL_OPTION_NAME = "FSRL";

/**
* Returns the {@link FSRL} stored in a {@link Program}'s properties, or null if not present
Expand All @@ -73,8 +73,7 @@ public class FSRL {
* @return {@link FSRL} from program's properties, or null if not present or invalid
*/
public static FSRL fromProgram(Program program) {
String fsrlStr = program.getOptions(Program.PROGRAM_INFO)
.getString(ProgramMappingService.PROGRAM_SOURCE_FSRL, null);
String fsrlStr = program.getOptions(Program.PROGRAM_INFO).getString(FSRL_OPTION_NAME, null);
if (fsrlStr != null) {
try {
return FSRL.fromString(fsrlStr);
Expand All @@ -86,6 +85,16 @@ public static FSRL fromProgram(Program program) {
return null;
}

/**
* Writes a FSRL value to a {@link Program}'s properties.
*
* @param program {@link Program}
* @param fsrl {@link FSRL} to write
*/
public static void writeToProgramInfo(Program program, FSRL fsrl) {
program.getOptions(Program.PROGRAM_INFO).setString(FSRL_OPTION_NAME, fsrl.toString());
}

/**
* Creates a {@link FSRL} from a raw string. The parent portions of the FSRL
* are not intern()'d so will not be shared with other FSRL instances.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
Expand All @@ -31,6 +33,7 @@
import docking.widgets.OptionDialog;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.fileinfo.FileType;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
Expand Down Expand Up @@ -400,9 +403,7 @@ public static List<String> getLines(ByteProvider byteProvider) throws IOExceptio
public static String getFileMD5(File f, TaskMonitor monitor)
throws IOException, CancelledException {
try (FileInputStream fis = new FileInputStream(f)) {
monitor.initialize(f.length());
monitor.setMessage("Hashing file: " + f.getName());
return getMD5(fis, monitor);
return getMD5(fis, f.getName(), f.length(), monitor);
}
}

Expand All @@ -418,31 +419,47 @@ public static String getFileMD5(File f, TaskMonitor monitor)
public static String getMD5(ByteProvider provider, TaskMonitor monitor)
throws IOException, CancelledException {
try (InputStream is = provider.getInputStream(0)) {
monitor.initialize(provider.length());
monitor.setMessage("Hashing file: " + provider.getName());
return getMD5(is, monitor);
return getMD5(is, provider.getName(), provider.length(), monitor);
}
}

/**
* Calculate the hash of an {@link InputStream}.
*
* @param is {@link InputStream}
* @param name of the inputstream
* @param expectedLength the length of the inputstream
* @param monitor {@link TaskMonitor} to update
* @return md5 as a hex encoded string, never null
* @throws IOException if error
* @throws CancelledException if cancelled
*/
public static String getMD5(InputStream is, TaskMonitor monitor)
throws IOException, CancelledException {
public static String getMD5(InputStream is, String name, long expectedLength,
TaskMonitor monitor) throws IOException, CancelledException {
try {
long startms = System.currentTimeMillis();
long prevElapsed = startms;

monitor.initialize(expectedLength, "Hashing %s".formatted(name));

MessageDigest messageDigest = MessageDigest.getInstance(HashUtilities.MD5_ALGORITHM);
byte[] buf = new byte[16 * 1024];
int bufSize = (int) Math.max(1024, Math.min(expectedLength, 1024 * 1024));
byte[] buf = new byte[bufSize];
int bytesRead;
long totalBytesRead = 0;
while ((bytesRead = is.read(buf)) >= 0) {
messageDigest.update(buf, 0, bytesRead);
monitor.incrementProgress(bytesRead);
monitor.checkCancelled();
totalBytesRead += bytesRead;
monitor.increment(bytesRead);

long now = System.currentTimeMillis();
if (now - prevElapsed > 5000 /*5 seconds*/ && totalBytesRead > bufSize) {
prevElapsed = now;
long elapsed = now - startms;
long rate = (long) (totalBytesRead / (elapsed / 1000f));
monitor.setMessage(
"Hashing %s %s/s".formatted(name, FileUtilities.formatLength(rate)));
}
}
return NumericUtilities.convertBytesToString(messageDigest.digest());
}
Expand Down Expand Up @@ -587,4 +604,50 @@ public static void uncheckedClose(Closeable c, String msg) {
e);
}
}

public static boolean isSymlink(File f) {
try {
return f != null && Files.isSymbolicLink(f.toPath());
}
catch (IllegalArgumentException e) {
return false;
}
}

/**
* Returns the destination of a symlink, or null if not a symlink or other error
*
* @param f {@link File} that is a symlink
* @return destination path string of the symlink, or null if not symlink
*/
public static String readSymlink(File f) {
try {
Path symlink = Files.readSymbolicLink(f.toPath());
return symlink.toString();
}
catch (Throwable th) {
// ignore and return null
}
return null;
}

public static FileType getFileType(File f) {
try {
Path p = f.toPath();
if (Files.isSymbolicLink(p)) {
return FileType.SYMBOLIC_LINK;
}
if (Files.isDirectory(p)) {
return FileType.DIRECTORY;
}
if (Files.isRegularFile(p)) {
return FileType.FILE;
}
}
catch (IllegalArgumentException e) {
// fall thru
}
return FileType.UNKNOWN;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,61 +31,57 @@ public interface GFile {
* The {@link GFileSystem} that owns this file.
* @return {@link GFileSystem} that owns this file.
*/
public GFileSystem getFilesystem();
GFileSystem getFilesystem();

/**
* The {@link FSRL} of this file.
*
* @return {@link FSRL} of this file.
*/
public FSRL getFSRL();
FSRL getFSRL();

/**
* The parent directory of this file.
*
* @return parent {@link GFile} directory of this file.
*/
public GFile getParentFile();
GFile getParentFile();

/**
* The path and filename of this file, relative to its owning filesystem.
*
* @return path and filename of this file, relative to its owning filesystem.
*/
public String getPath();
String getPath();

/**
* The name of this file.
*
* @return name of this file.
*/
public String getName();
String getName();

/**
* Returns true if this is a directory.
* <p>
* @return boolean true if this file is a directory, false otherwise.
*/
public boolean isDirectory();
boolean isDirectory();

/**
* Returns the length of this file, or -1 if not known.
*
* @return number of bytes in this file.
*/
public long getLength();

default public long getLastModified() {
return -1;
}
long getLength();

/**
* Returns a listing of files in this sub-directory.
* <p>
* @return {@link List} of {@link GFile} instances.
* @throws IOException if not a directory or error when accessing files.
*/
default public List<GFile> getListing() throws IOException {
default List<GFile> getListing() throws IOException {
return getFilesystem().getListing(this);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package ghidra.formats.gfilesystem;

import java.io.File;
import java.util.Objects;

/**
* {@link GFile} implementation that refers to a real java.io.File on the local
Expand Down Expand Up @@ -85,11 +86,6 @@ public long getLength() {
return f.length();
}

@Override
public long getLastModified() {
return f.lastModified();
}

public File getLocalFile() {
return f;
}
Expand All @@ -99,4 +95,23 @@ public String toString() {
return "Local " + f.toString() + " with path " + path;
}

@Override
public int hashCode() {
return Objects.hash(f, fs, fsrl, parent, path);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof GFileLocal)) {
return false;
}
GFileLocal other = (GFileLocal) obj;
return Objects.equals(f, other.f) && Objects.equals(fs, other.fs) &&
Objects.equals(fsrl, other.fsrl) && Objects.equals(parent, other.parent) &&
Objects.equals(path, other.path);
}

}
Loading

0 comments on commit 3f86719

Please sign in to comment.