mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-4085 Added ability to add VTSession to a shared repository
This commit is contained in:
parent
b8f004c792
commit
c3386b72a2
33 changed files with 1063 additions and 565 deletions
|
@ -25,50 +25,81 @@ import ghidra.feature.vt.api.util.VTOptions;
|
|||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
||||
public abstract class GhidraVersionTrackingScript extends GhidraScript {
|
||||
protected VTSession vtSession;
|
||||
protected Program sourceProgram;
|
||||
protected Program destinationProgram;
|
||||
public abstract class AbstractGhidraVersionTrackingScript extends GhidraScript {
|
||||
private VTSession vtSession;
|
||||
private Program sourceProgram;
|
||||
private Program destinationProgram;
|
||||
|
||||
private int transactionID;
|
||||
|
||||
public void createVersionTrackingSession(String sourceProgramPath,
|
||||
String destinationProgramPath) throws Exception {
|
||||
protected VTSession getVTSession() {
|
||||
return vtSession;
|
||||
}
|
||||
|
||||
protected Program getSourceProgram() {
|
||||
return sourceProgram;
|
||||
}
|
||||
|
||||
protected Program getDestinationProgram() {
|
||||
return destinationProgram;
|
||||
}
|
||||
|
||||
public VTSession createVersionTrackingSession(String sourceProgramPath,
|
||||
String destinationProgramPath)
|
||||
throws VersionException, CancelledException, IOException {
|
||||
|
||||
if (vtSession != null) {
|
||||
throw new RuntimeException("Attempted to open a new session with one already open!");
|
||||
}
|
||||
sourceProgram = openProgram(sourceProgramPath);
|
||||
destinationProgram = openProgram(destinationProgramPath);
|
||||
|
||||
createVersionTrackingSession("New Session", sourceProgram, destinationProgram);
|
||||
try {
|
||||
sourceProgram = openProgram(sourceProgramPath);
|
||||
|
||||
destinationProgram = openProgram(destinationProgramPath);
|
||||
|
||||
vtSession = new VTSessionDB("New Session", sourceProgram, destinationProgram, this);
|
||||
transactionID = vtSession.startTransaction("VT Script");
|
||||
}
|
||||
finally {
|
||||
if (vtSession == null) {
|
||||
closeVersionTrackingSession();
|
||||
}
|
||||
}
|
||||
return vtSession;
|
||||
}
|
||||
|
||||
public void createVersionTrackingSession(String name, Program source, Program destination)
|
||||
throws Exception {
|
||||
public VTSession createVersionTrackingSession(String name, Program source, Program destination)
|
||||
throws IOException {
|
||||
|
||||
if (vtSession != null) {
|
||||
throw new RuntimeException("Attempted to create a new session with one already open!");
|
||||
}
|
||||
sourceProgram = source;
|
||||
destinationProgram = destination;
|
||||
|
||||
if (!sourceProgram.isUsedBy(this)) {
|
||||
try {
|
||||
sourceProgram = source;
|
||||
sourceProgram.addConsumer(this);
|
||||
}
|
||||
if (!destinationProgram.isUsedBy(this)) {
|
||||
destinationProgram.addConsumer(this);
|
||||
}
|
||||
|
||||
vtSession = VTSessionDB.createVTSession(name, sourceProgram, destinationProgram, this);
|
||||
transactionID = vtSession.startTransaction("VT Script");
|
||||
destinationProgram = destination;
|
||||
destinationProgram.addConsumer(this);
|
||||
|
||||
vtSession = new VTSessionDB(name, sourceProgram, destinationProgram, this);
|
||||
transactionID = vtSession.startTransaction("VT Script");
|
||||
}
|
||||
finally {
|
||||
if (vtSession == null) {
|
||||
closeVersionTrackingSession();
|
||||
}
|
||||
}
|
||||
return vtSession;
|
||||
}
|
||||
|
||||
public void openVersionTrackingSession(String path) throws Exception {
|
||||
public VTSession openVersionTrackingSession(String path)
|
||||
throws VersionException, CancelledException, IOException {
|
||||
if (vtSession != null) {
|
||||
throw new RuntimeException("Attempted to open a session with one already open!");
|
||||
}
|
||||
|
@ -79,51 +110,72 @@ public abstract class GhidraVersionTrackingScript extends GhidraScript {
|
|||
DomainFile file = state.getProject().getProjectData().getFile(path);
|
||||
vtSession = (VTSessionDB) file.getDomainObject(this, true, true, monitor);
|
||||
sourceProgram = vtSession.getSourceProgram();
|
||||
sourceProgram.addConsumer(this);
|
||||
destinationProgram = vtSession.getDestinationProgram();
|
||||
destinationProgram.addConsumer(this);
|
||||
|
||||
if (!sourceProgram.isUsedBy(this)) {
|
||||
sourceProgram.addConsumer(this);
|
||||
}
|
||||
if (!destinationProgram.isUsedBy(this)) {
|
||||
destinationProgram.addConsumer(this);
|
||||
}
|
||||
transactionID = vtSession.startTransaction("VT Script");
|
||||
return vtSession;
|
||||
}
|
||||
|
||||
public void saveVersionTrackingSession() throws IOException {
|
||||
if (vtSession != null) {
|
||||
throw new RuntimeException("Attempted to save a session when not open!");
|
||||
}
|
||||
vtSession.endTransaction(transactionID, true);
|
||||
vtSession.save();
|
||||
transactionID = vtSession.startTransaction("VT Script");
|
||||
try {
|
||||
vtSession.save();
|
||||
}
|
||||
finally {
|
||||
transactionID = vtSession.startTransaction("VT Script");
|
||||
}
|
||||
}
|
||||
|
||||
public void saveSessionAs(String path, String name) throws Exception {
|
||||
DomainFolder folder = state.getProject().getProjectData().getFolder(path);
|
||||
folder.createFile(name, vtSession, monitor);
|
||||
vtSession.setName(name);
|
||||
public void saveSessionAs(String path, String name)
|
||||
throws InvalidNameException, CancelledException, IOException {
|
||||
if (vtSession != null) {
|
||||
throw new RuntimeException("Attempted to save a session when not open!");
|
||||
}
|
||||
vtSession.endTransaction(transactionID, true);
|
||||
try {
|
||||
DomainFolder folder = state.getProject().getProjectData().getFolder(path);
|
||||
folder.createFile(name, vtSession, monitor);
|
||||
vtSession.setName(name);
|
||||
}
|
||||
finally {
|
||||
transactionID = vtSession.startTransaction("VT Script");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup(boolean success) {
|
||||
closeVersionTrackingSession();
|
||||
if (destinationProgram != null) {
|
||||
closeProgram(destinationProgram);
|
||||
}
|
||||
if (sourceProgram != null) {
|
||||
closeProgram(sourceProgram);
|
||||
}
|
||||
sourceProgram = null;
|
||||
destinationProgram = null;
|
||||
super.cleanup(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will release the current session and both source and destination programs.
|
||||
* If either program needs to be held it is the script's responsibility to first retain
|
||||
* the instance and add itself as a consumer. Any program consumer must release it
|
||||
* when done using it.
|
||||
*/
|
||||
public void closeVersionTrackingSession() {
|
||||
if (vtSession != null) {
|
||||
vtSession.endTransaction(transactionID, true);
|
||||
vtSession.release(this);
|
||||
vtSession = null;
|
||||
}
|
||||
if (destinationProgram != null) {
|
||||
destinationProgram.release(this);
|
||||
destinationProgram = null;
|
||||
}
|
||||
if (sourceProgram != null) {
|
||||
sourceProgram.release(this);
|
||||
sourceProgram = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Program openProgram(String path)
|
||||
private Program openProgram(String path)
|
||||
throws VersionException, CancelledException, IOException {
|
||||
if (state.getProject() == null) {
|
||||
throw new RuntimeException("No project open.");
|
||||
|
@ -132,11 +184,6 @@ public abstract class GhidraVersionTrackingScript extends GhidraScript {
|
|||
return (Program) file.getDomainObject(this, true, true, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeProgram(Program program) {
|
||||
program.release(this);
|
||||
}
|
||||
|
||||
public Set<String> getSourceFunctions() {
|
||||
if (vtSession == null) {
|
||||
throw new RuntimeException("You must have an open vt session");
|
|
@ -13,33 +13,30 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.feature.vt.api.impl;
|
||||
package ghidra.feature.vt.api.db;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import db.DBHandle;
|
||||
import db.OpenMode;
|
||||
import db.buffers.BufferFile;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.feature.vt.api.db.VTSessionDB;
|
||||
import ghidra.framework.data.DBContentHandler;
|
||||
import ghidra.framework.data.DomainObjectMergeManager;
|
||||
import ghidra.framework.model.ChangeSet;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.store.*;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class VTSessionContentHandler extends DBContentHandler<VTSessionDB> {
|
||||
|
||||
private static Icon ICON = new GIcon("icon.version.tracking.session.content.type");
|
||||
public static final String CONTENT_TYPE = "VersionTracking";
|
||||
|
||||
public final static String CONTENT_TYPE = "VersionTracking";
|
||||
private static final Icon ICON = new GIcon("icon.version.tracking.session.content.type");
|
||||
|
||||
@Override
|
||||
public long createFile(FileSystem fs, FileSystem userfs, String path, String name,
|
||||
|
@ -74,22 +71,40 @@ public class VTSessionContentHandler extends DBContentHandler<VTSessionDB> {
|
|||
return "Version Tracking";
|
||||
}
|
||||
|
||||
private void checkContentAndExclusiveCheckout(FolderItem item) throws IOException {
|
||||
String contentType = item.getContentType();
|
||||
if (!contentType.equals(CONTENT_TYPE)) {
|
||||
throw new IOException("Unsupported content type: " + contentType);
|
||||
}
|
||||
|
||||
// NOTE: item.isVersioned indicates that item is located on versioned filesystem
|
||||
// and is not checked-out, otheriwse assume item in local filesystem and must
|
||||
// ensure if any checkout is exclusive.
|
||||
if (item.isVersioned() || (item.isCheckedOut() && !item.isCheckedOutExclusive())) {
|
||||
throw new IOException(
|
||||
"Unsupported VT Session use: session file must be checked-out exclusive");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VTSessionDB getDomainObject(FolderItem item, FileSystem userfs, long checkoutId,
|
||||
boolean okToUpgrade, boolean okToRecover, Object consumer, TaskMonitor monitor)
|
||||
throws IOException, CancelledException, VersionException {
|
||||
|
||||
String contentType = item.getContentType();
|
||||
if (!contentType.equals(CONTENT_TYPE)) {
|
||||
throw new IOException("Unsupported content type: " + contentType);
|
||||
checkContentAndExclusiveCheckout(item);
|
||||
|
||||
if (item.isReadOnly()) {
|
||||
throw new ReadOnlyException("VT Session file is set read-only which prevents its use");
|
||||
}
|
||||
|
||||
try {
|
||||
DatabaseItem dbItem = (DatabaseItem) item;
|
||||
BufferFile bf = dbItem.openForUpdate(checkoutId);
|
||||
DBHandle dbh = new DBHandle(bf, okToRecover, monitor);
|
||||
boolean success = false;
|
||||
try {
|
||||
VTSessionDB db = VTSessionDB.getVTSession(dbh, OpenMode.UPGRADE, consumer, monitor);
|
||||
// NOTE: Always open with DB upgrade enabled
|
||||
VTSessionDB db = new VTSessionDB(dbh, monitor, consumer);
|
||||
success = true;
|
||||
return db;
|
||||
}
|
||||
|
@ -99,13 +114,7 @@ public class VTSessionContentHandler extends DBContentHandler<VTSessionDB> {
|
|||
}
|
||||
}
|
||||
}
|
||||
catch (VersionException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
catch (VersionException | IOException | CancelledException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
|
@ -134,12 +143,7 @@ public class VTSessionContentHandler extends DBContentHandler<VTSessionDB> {
|
|||
int minChangeVersion, TaskMonitor monitor)
|
||||
throws IOException, CancelledException, VersionException {
|
||||
|
||||
String contentType = item.getContentType();
|
||||
if (!contentType.equals(CONTENT_TYPE)) {
|
||||
throw new IOException("Unsupported content type: " + contentType);
|
||||
}
|
||||
return getReadOnlyObject(item, -1, false, consumer, monitor);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -154,43 +158,14 @@ public class VTSessionContentHandler extends DBContentHandler<VTSessionDB> {
|
|||
Object consumer, TaskMonitor monitor)
|
||||
throws IOException, VersionException, CancelledException {
|
||||
|
||||
String contentType = item.getContentType();
|
||||
if (contentType != null && !contentType.equals(CONTENT_TYPE)) {
|
||||
throw new IOException("Unsupported content type: " + contentType);
|
||||
}
|
||||
try {
|
||||
DatabaseItem dbItem = (DatabaseItem) item;
|
||||
BufferFile bf = dbItem.open();
|
||||
DBHandle dbh = new DBHandle(bf);
|
||||
boolean success = false;
|
||||
try {
|
||||
VTSessionDB manager =
|
||||
VTSessionDB.getVTSession(dbh, OpenMode.READ_ONLY, consumer, monitor);
|
||||
success = true;
|
||||
return manager;
|
||||
}
|
||||
finally {
|
||||
if (!success) {
|
||||
dbh.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.error(this, "Get read-only object failed", t);
|
||||
String msg = t.getMessage();
|
||||
if (msg == null) {
|
||||
msg = t.toString();
|
||||
}
|
||||
throw new IOException("Open failed: " + msg, t);
|
||||
}
|
||||
checkContentAndExclusiveCheckout(item);
|
||||
|
||||
throw new ReadOnlyException("VT Session does not support read-only use");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrivateContentType() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,6 +26,7 @@ import ghidra.feature.vt.api.correlator.program.ImpliedMatchProgramCorrelator;
|
|||
import ghidra.feature.vt.api.correlator.program.ManualMatchProgramCorrelator;
|
||||
import ghidra.feature.vt.api.impl.*;
|
||||
import ghidra.feature.vt.api.main.*;
|
||||
import ghidra.feature.vt.api.util.VTSessionFileUtil;
|
||||
import ghidra.framework.data.DomainObjectAdapterDB;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.model.TransactionInfo.Status;
|
||||
|
@ -36,26 +37,30 @@ import ghidra.program.database.map.AddressMap;
|
|||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
||||
|
||||
private final static Field[] COL_FIELDS = new Field[] { StringField.INSTANCE };
|
||||
private final static String[] COL_TYPES = new String[] { "Value" };
|
||||
private final static Schema SCHEMA =
|
||||
new Schema(0, StringField.INSTANCE, "Key", COL_FIELDS, COL_TYPES);
|
||||
|
||||
private static final String PROGRAM_ID_PROPERTYLIST_NAME = "ProgramIDs";
|
||||
private static final String SOURCE_PROGRAM_ID_PROPERTY_KEY = "SourceProgramID";
|
||||
private static final String DESTINATION_PROGRAM_ID_PROPERTY_KEY = "DestinationProgramID";
|
||||
// Source and Destination Program IDs are retained within OptionsDB
|
||||
static final String PROGRAM_ID_PROPERTYLIST_NAME = "ProgramIDs";
|
||||
static final String SOURCE_PROGRAM_ID_PROPERTY_KEY = "SourceProgramID";
|
||||
static final String DESTINATION_PROGRAM_ID_PROPERTY_KEY = "DestinationProgramID";
|
||||
|
||||
private static final String UNUSED_DEFAULT_NAME = "Untitled";
|
||||
private static final int EVENT_NOTIFICATION_DELAY = 500;
|
||||
private static final int EVENT_BUFFER_SIZE = 100;
|
||||
|
||||
private static final long MANUAL_MATCH_SET_ID = 0;
|
||||
private static final long IMPLIED_MATCH_SET_ID = -1;
|
||||
|
||||
// PropertyTable is used solely to retain DB version
|
||||
// NOTE: OptionsDB already has a table named "Property Table"
|
||||
private static final String PROPERTY_TABLE_NAME = "PropertyTable";
|
||||
private static final String DB_VERSION_PROPERTY_NAME = "DB_VERSION";
|
||||
|
||||
|
@ -65,8 +70,11 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||
* 14-Nov-2019 - version 2 - Corrected fixed length indexing implementation causing
|
||||
* change in index table low-level storage for newly
|
||||
* created tables.
|
||||
* 16-Feb-2024 - version 3 - No schema change. Version imposed to prevent older versions
|
||||
* of Ghidra from opening session objects which may have been
|
||||
* added to version controlled repository.
|
||||
*/
|
||||
private static final int DB_VERSION = 2;
|
||||
private static final int DB_VERSION = 3;
|
||||
|
||||
/**
|
||||
* UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION any time the
|
||||
|
@ -75,7 +83,7 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||
* if the data's version is >= UPGRADE_REQUIRED_BEFORE_VERSION and <= DB_VERSION.
|
||||
*/
|
||||
// NOTE: Schema upgrades are not currently supported
|
||||
private static final int UPGRADE_REQUIRED_BEFORE_VERSION = 1;
|
||||
private static final int UPGRADE_REQUIRED_BEFORE_VERSION = 3;
|
||||
|
||||
private VTMatchSetTableDBAdapter matchSetTableAdapter;
|
||||
private AssociationDatabaseManager associationManager;
|
||||
|
@ -89,40 +97,119 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||
private VTMatchSet impliedMatchSet;
|
||||
|
||||
private boolean changeSetsModified = false;
|
||||
private Table propertyTable;
|
||||
private Table propertyTable; // used to retain DB version only
|
||||
|
||||
/**
|
||||
* Factory method which constructs a new VTSessionDB using specified source and desitination
|
||||
* programs.
|
||||
* @param name name to be assigned to the resulting domain object file
|
||||
* @param sourceProgram session source program within active project
|
||||
* @param destinationProgram session destination program open for update within active project
|
||||
* @param consumer object consumer resposible for the proper release of the returned instance.
|
||||
* @return new {@link VTSessionDB} object
|
||||
* @throws IOException if an IO error occurs
|
||||
* @deprecated {@link #VTSessionDB(String, Program, Program, Object)} should be used instead
|
||||
*/
|
||||
@Deprecated(since = "11.1", forRemoval = true)
|
||||
public static VTSessionDB createVTSession(String name, Program sourceProgram,
|
||||
Program destinationProgram, Object consumer) throws IOException {
|
||||
return new VTSessionDB(name, sourceProgram, destinationProgram, consumer);
|
||||
}
|
||||
|
||||
VTSessionDB session = new VTSessionDB(new DBHandle(), consumer);
|
||||
/**
|
||||
* Construct a new VTSessionDB using specified source and desitination programs.
|
||||
* @param name name to be assigned to the resulting domain object file
|
||||
* @param sourceProgram session source program within active project
|
||||
* @param destinationProgram session destination program open for update within active project
|
||||
* @param consumer object consumer resposible for the proper release of the returned instance.
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public VTSessionDB(String name, Program sourceProgram, Program destinationProgram,
|
||||
Object consumer) throws IOException {
|
||||
super(new DBHandle(), UNUSED_DEFAULT_NAME, EVENT_NOTIFICATION_DELAY, consumer);
|
||||
|
||||
int ID = session.startTransaction("Constructing New Version Tracking Match Set");
|
||||
propertyTable = dbh.getTable(PROPERTY_TABLE_NAME);
|
||||
|
||||
int ID = startTransaction("Constructing New Version Tracking Match Set");
|
||||
try {
|
||||
session.propertyTable = session.dbh.createTable(PROPERTY_TABLE_NAME, SCHEMA);
|
||||
session.matchSetTableAdapter = VTMatchSetTableDBAdapter.createAdapter(session.dbh);
|
||||
session.associationManager =
|
||||
AssociationDatabaseManager.createAssociationManager(session.dbh, session);
|
||||
session.matchTagAdapter = VTMatchTagDBAdapter.createAdapter(session.dbh);
|
||||
session.initializePrograms(sourceProgram, destinationProgram);
|
||||
session.createMatchSet(
|
||||
new ManualMatchProgramCorrelator(sourceProgram, destinationProgram),
|
||||
propertyTable = dbh.createTable(PROPERTY_TABLE_NAME, SCHEMA);
|
||||
matchSetTableAdapter = VTMatchSetTableDBAdapter.createAdapter(dbh);
|
||||
associationManager = AssociationDatabaseManager.createAssociationManager(dbh, this);
|
||||
matchTagAdapter = VTMatchTagDBAdapter.createAdapter(dbh);
|
||||
|
||||
initializePrograms(sourceProgram, destinationProgram, true);
|
||||
|
||||
createMatchSet(new ManualMatchProgramCorrelator(sourceProgram, destinationProgram),
|
||||
MANUAL_MATCH_SET_ID);
|
||||
session.createMatchSet(
|
||||
new ImpliedMatchProgramCorrelator(sourceProgram, destinationProgram),
|
||||
createMatchSet(new ImpliedMatchProgramCorrelator(sourceProgram, destinationProgram),
|
||||
IMPLIED_MATCH_SET_ID);
|
||||
session.updateVersion();
|
||||
|
||||
updateVersion();
|
||||
}
|
||||
finally {
|
||||
session.endTransaction(ID, true);
|
||||
endTransaction(ID, true);
|
||||
}
|
||||
|
||||
try {
|
||||
session.addSynchronizedDomainObject(destinationProgram);
|
||||
addSynchronizedDomainObject(destinationProgram);
|
||||
}
|
||||
catch (Exception e) {
|
||||
session.close();
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
close();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an existing VT session object and open with UPGRADE enabled.
|
||||
* The caller (i.e., content handler) must ensure that project has exclusive access to
|
||||
* the domain file before it was open and {@link DBHandle} supplied.
|
||||
* @param dbHandle database handle
|
||||
* @param monitor TaskMonitor that allows the open to be canceled.
|
||||
* @param consumer the object that keeping the session open.
|
||||
* @throws IOException if an error accessing the database occurs.
|
||||
* @throws VersionException if database version does not match implementation, UPGRADE may be possible.
|
||||
* @throws CancelledException if instantiation is canceled by monitor
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
VTSessionDB(DBHandle dbHandle, TaskMonitor monitor, Object consumer)
|
||||
throws VersionException, IOException, CancelledException {
|
||||
super(dbHandle, UNUSED_DEFAULT_NAME, EVENT_NOTIFICATION_DELAY, consumer);
|
||||
|
||||
// openMode forced to UPGRADE since we do not support read-only mode
|
||||
// It is assumed we always have exclusive access to the underlying database
|
||||
OpenMode openMode = OpenMode.UPGRADE;
|
||||
|
||||
propertyTable = dbHandle.getTable(PROPERTY_TABLE_NAME);
|
||||
|
||||
int storedVersion = getVersion();
|
||||
if (storedVersion > DB_VERSION) {
|
||||
throw new VersionException(VersionException.NEWER_VERSION, false);
|
||||
}
|
||||
|
||||
// The following version logic holds true for DB_VERSION <= 3 which assume no additional
|
||||
// DB index tables will be added when open for update/upgrade. This may not hold
|
||||
// true for future revisions associated with table schema changes in which case the
|
||||
// UPGRADE_REQUIRED_BEFORE_VERSION value should equal DB_VERSION. Current logic
|
||||
// assumes no schema changes will be made during upgrade.
|
||||
if (storedVersion < UPGRADE_REQUIRED_BEFORE_VERSION) {
|
||||
if (openMode != OpenMode.UPGRADE) { // should always be open with UPGRADE mode
|
||||
throw new VersionException(
|
||||
"Version Tracking Sessions do not support schema upgrades.",
|
||||
VersionException.OLDER_VERSION, true);
|
||||
}
|
||||
withTransaction("Update DBVersion", () -> updateVersion());
|
||||
clearUndo(false);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// NOTE: code below will not make changes (no transaction is open)
|
||||
// Additional supported required to facilitate schema change during upgrade if needed.
|
||||
|
||||
matchSetTableAdapter = VTMatchSetTableDBAdapter.getAdapter(dbHandle, openMode, monitor);
|
||||
associationManager =
|
||||
AssociationDatabaseManager.getAssociationManager(dbHandle, this, openMode, monitor);
|
||||
matchTagAdapter = VTMatchTagDBAdapter.getAdapter(dbHandle, openMode, monitor);
|
||||
loadMatchSets(openMode, monitor);
|
||||
}
|
||||
|
||||
private void updateVersion() throws IOException {
|
||||
|
@ -131,47 +218,174 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||
propertyTable.putRecord(record);
|
||||
}
|
||||
|
||||
public static VTSessionDB getVTSession(DBHandle dbHandle, OpenMode openMode, Object consumer,
|
||||
TaskMonitor monitor) throws VersionException, IOException {
|
||||
|
||||
VTSessionDB session = new VTSessionDB(dbHandle, consumer);
|
||||
int storedVersion = session.getVersion();
|
||||
|
||||
if (storedVersion > DB_VERSION) {
|
||||
throw new VersionException(VersionException.NEWER_VERSION, false);
|
||||
private int getVersion() throws IOException {
|
||||
// DB Version was added in release (11/6/2012)
|
||||
// if record does not exist return 0;
|
||||
if (propertyTable == null) {
|
||||
return 0;
|
||||
}
|
||||
// The following version logic holds true for DB_VERSION=2 which assumes no additional
|
||||
// DB index tables will be added when open for update/upgrade. This will not hold
|
||||
// true for future revisions associated with table schema changes in which case the
|
||||
// UPGRADE_REQUIRED_BEFORE_VERSION value should equal DB_VERSION.
|
||||
if (storedVersion < UPGRADE_REQUIRED_BEFORE_VERSION) {
|
||||
throw new VersionException("Version Tracking Sessions do not support schema upgrades.");
|
||||
DBRecord record = propertyTable.getRecord(new StringField(DB_VERSION_PROPERTY_NAME));
|
||||
|
||||
if (record != null) {
|
||||
String s = record.getString(0);
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
// just use default
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDomainFile(DomainFile df) throws DomainObjectException {
|
||||
DomainFolder parent = df.getParent();
|
||||
if (parent != null && sourceProgram == null) {
|
||||
try {
|
||||
openSourceAndDestinationPrograms(parent.getProjectData());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new DomainObjectException(e);
|
||||
}
|
||||
}
|
||||
super.setDomainFile(df);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open associated source and destination program files and complete session initialization.
|
||||
* @param projectData active project data
|
||||
* @throws IOException if source or destination program not found within specified project
|
||||
* or an error occured while opening them (e.g., upgrade required).
|
||||
*/
|
||||
private void openSourceAndDestinationPrograms(ProjectData projectData) throws IOException {
|
||||
String sourceProgramID = getSourceProgramID();
|
||||
String destinationProgramID = getDestinationProgramID();
|
||||
DomainFile sourceFile = projectData.getFileByID(sourceProgramID);
|
||||
DomainFile destinationFile = projectData.getFileByID(destinationProgramID);
|
||||
if (sourceFile == null) {
|
||||
throw new IOException("Source program is missing for this Version Tracking Session!");
|
||||
}
|
||||
if (destinationFile == null) {
|
||||
throw new IOException(
|
||||
"Destination program is missing for this Version Tracking Session!");
|
||||
}
|
||||
|
||||
session.matchSetTableAdapter =
|
||||
VTMatchSetTableDBAdapter.getAdapter(session.getDBHandle(), openMode, monitor);
|
||||
session.associationManager =
|
||||
AssociationDatabaseManager.getAssociationManager(dbHandle, session, openMode, monitor);
|
||||
session.matchTagAdapter =
|
||||
VTMatchTagDBAdapter.getAdapter(session.getDBHandle(), openMode, monitor);
|
||||
session.loadMatchSets(openMode, monitor);
|
||||
return session;
|
||||
// Must ensure that destination program file can be updated
|
||||
VTSessionFileUtil.validateDestinationProgramFile(destinationFile, true,
|
||||
SystemUtilities.isInHeadlessMode());
|
||||
|
||||
VTSessionFileUtil.validateSourceProgramFile(sourceFile, true);
|
||||
|
||||
sourceProgram = openProgram(sourceFile, true);
|
||||
|
||||
if (sourceProgram != null) {
|
||||
destinationProgram = openProgram(destinationFile, false);
|
||||
}
|
||||
|
||||
if (sourceProgram == null || destinationProgram == null) {
|
||||
StringBuilder buffer = new StringBuilder(
|
||||
"Session not opened because one or both programs did not open.\n");
|
||||
if (sourceProgram != null) {
|
||||
sourceProgram.release(this);
|
||||
sourceProgram = null;
|
||||
}
|
||||
else {
|
||||
buffer.append("\tUnable to open source program \"" + sourceFile + "\"\n");
|
||||
}
|
||||
|
||||
if (destinationProgram != null) {
|
||||
destinationProgram.release(this);
|
||||
destinationProgram = null;
|
||||
}
|
||||
else {
|
||||
buffer.append("\tUnable to open destination program \"" + destinationFile + "\"\n");
|
||||
}
|
||||
|
||||
throw new IOException(buffer.toString());
|
||||
}
|
||||
|
||||
associationManager.sessionInitialized();
|
||||
|
||||
try {
|
||||
addSynchronizedDomainObject(destinationProgram);
|
||||
}
|
||||
catch (Exception e) {
|
||||
sourceProgram.release(this);
|
||||
sourceProgram = null;
|
||||
destinationProgram.release(this);
|
||||
destinationProgram = null;
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Program openProgram(DomainFile domainFile, boolean isSource) {
|
||||
|
||||
String type = isSource ? "VT Source Program" : "VT Destination Program";
|
||||
|
||||
if (SystemUtilities.isInHeadlessMode()) {
|
||||
try {
|
||||
return (Program) domainFile.getDomainObject(this, false, false, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
throw new AssertionError(e); // unexpected
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(null, domainFile.getName(), type, "open",
|
||||
e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Can't open " + type + ": " + domainFile.getName(),
|
||||
e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Headed GUI Mode
|
||||
|
||||
OpenProgramTask openTask = new OpenProgramTask(domainFile, this);
|
||||
openTask.setOpenPromptText("Open " + type);
|
||||
|
||||
TaskLauncher.launch(openTask);
|
||||
|
||||
OpenProgramRequest openProgram = openTask.getOpenProgram();
|
||||
return openProgram != null ? openProgram.getProgram() : null;
|
||||
}
|
||||
|
||||
public String getSourceProgramID() {
|
||||
Options properties = getOptions(PROGRAM_ID_PROPERTYLIST_NAME);
|
||||
return properties.getString(SOURCE_PROGRAM_ID_PROPERTY_KEY, "");
|
||||
}
|
||||
|
||||
public String getDestinationProgramID() {
|
||||
Options properties = getOptions(PROGRAM_ID_PROPERTYLIST_NAME);
|
||||
return properties.getString(DESTINATION_PROGRAM_ID_PROPERTY_KEY, "");
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
// this is from our constructor
|
||||
private void initializePrograms(Program sourceProgram, Program destinationProgram) {
|
||||
this.sourceProgram = sourceProgram;
|
||||
this.destinationProgram = destinationProgram;
|
||||
sourceProgram.addConsumer(this);
|
||||
destinationProgram.addConsumer(this);
|
||||
Options properties = getOptions(PROGRAM_ID_PROPERTYLIST_NAME);
|
||||
DomainFile sourceDomainFile = sourceProgram.getDomainFile();
|
||||
properties.setString(SOURCE_PROGRAM_ID_PROPERTY_KEY, sourceDomainFile.getFileID());
|
||||
DomainFile destinationDomainFile = destinationProgram.getDomainFile();
|
||||
properties.setString(DESTINATION_PROGRAM_ID_PROPERTY_KEY,
|
||||
destinationDomainFile.getFileID());
|
||||
private void initializePrograms(Program sourceProgram, Program destinationProgram,
|
||||
boolean rememberProgramIds) throws IOException {
|
||||
|
||||
if (!destinationProgram.canSave()) {
|
||||
throw new ReadOnlyException(
|
||||
"VT Session destination program is read-only which prevents its use");
|
||||
}
|
||||
|
||||
this.sourceProgram = sourceProgram;
|
||||
sourceProgram.addConsumer(this);
|
||||
|
||||
this.destinationProgram = destinationProgram;
|
||||
destinationProgram.addConsumer(this);
|
||||
|
||||
if (rememberProgramIds) {
|
||||
Options properties = getOptions(PROGRAM_ID_PROPERTYLIST_NAME);
|
||||
DomainFile sourceDomainFile = sourceProgram.getDomainFile();
|
||||
properties.setString(SOURCE_PROGRAM_ID_PROPERTY_KEY, sourceDomainFile.getFileID());
|
||||
DomainFile destinationDomainFile = destinationProgram.getDomainFile();
|
||||
properties.setString(DESTINATION_PROGRAM_ID_PROPERTY_KEY,
|
||||
destinationDomainFile.getFileID());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -202,119 +416,6 @@ public class VTSessionDB extends DomainObjectAdapterDB implements VTSession {
|
|||
sourceProgram.addConsumer(this);
|
||||
}
|
||||
|
||||
public String getSourceProgramID() {
|
||||
Options properties = getOptions(PROGRAM_ID_PROPERTYLIST_NAME);
|
||||
return properties.getString(SOURCE_PROGRAM_ID_PROPERTY_KEY, "");
|
||||
}
|
||||
|
||||
public String getDestinationProgramID() {
|
||||
Options properties = getOptions(PROGRAM_ID_PROPERTYLIST_NAME);
|
||||
return properties.getString(DESTINATION_PROGRAM_ID_PROPERTY_KEY, "");
|
||||
}
|
||||
|
||||
private VTSessionDB(DBHandle dbHandle, Object consumer) {
|
||||
super(dbHandle, UNUSED_DEFAULT_NAME, EVENT_NOTIFICATION_DELAY, consumer);
|
||||
propertyTable = dbHandle.getTable(PROPERTY_TABLE_NAME);
|
||||
}
|
||||
|
||||
public int getVersion() throws IOException {
|
||||
// DB Version was added in release (11/6/2012)
|
||||
// if record does not exist return 0;
|
||||
if (propertyTable == null) {
|
||||
return 0;
|
||||
}
|
||||
DBRecord record = propertyTable.getRecord(new StringField(DB_VERSION_PROPERTY_NAME));
|
||||
|
||||
if (record != null) {
|
||||
String s = record.getString(0);
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
// just use default
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDomainFile(DomainFile df) {
|
||||
super.setDomainFile(df);
|
||||
DomainFolder parent = df.getParent();
|
||||
if (parent == null) {
|
||||
return;
|
||||
}
|
||||
if (sourceProgram != null) { // source and destination are already open
|
||||
return;
|
||||
}
|
||||
ProjectData projectData = parent.getProjectData();
|
||||
String sourceProgramID = getSourceProgramID();
|
||||
String destinationProgramID = getDestinationProgramID();
|
||||
DomainFile sourceFile = projectData.getFileByID(sourceProgramID);
|
||||
DomainFile destinationFile = projectData.getFileByID(destinationProgramID);
|
||||
if (sourceFile == null) {
|
||||
throw new RuntimeException(
|
||||
"Source program is missing for this Version Tracking Session!");
|
||||
}
|
||||
if (destinationFile == null) {
|
||||
throw new RuntimeException(
|
||||
"Destination program is missing for this Version Tracking Session!");
|
||||
}
|
||||
|
||||
sourceProgram = openProgram(sourceFile, true);
|
||||
|
||||
if (sourceProgram != null) {
|
||||
destinationProgram = openProgram(destinationFile, false);
|
||||
}
|
||||
|
||||
if (sourceProgram == null || destinationProgram == null) {
|
||||
StringBuilder buffer = new StringBuilder(
|
||||
"Session not opened because one or both programs did not open.\n");
|
||||
if (sourceProgram != null) {
|
||||
sourceProgram.release(this);
|
||||
sourceProgram = null;
|
||||
}
|
||||
else {
|
||||
buffer.append("\tUnable to open source program \"" + sourceFile + "\"\n");
|
||||
}
|
||||
|
||||
if (destinationProgram != null) {
|
||||
destinationProgram.release(this);
|
||||
destinationProgram = null;
|
||||
}
|
||||
else {
|
||||
buffer.append("\tUnable to open destination program \"" + destinationFile + "\"\n");
|
||||
}
|
||||
|
||||
throw new RuntimeException(buffer.toString());
|
||||
}
|
||||
|
||||
associationManager.sessionInitialized();
|
||||
|
||||
try {
|
||||
addSynchronizedDomainObject(destinationProgram);
|
||||
}
|
||||
catch (Exception e) {
|
||||
sourceProgram.release(this);
|
||||
destinationProgram.release(this);
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Program openProgram(DomainFile domainFile, boolean isSource) {
|
||||
|
||||
OpenProgramTask openTask = new OpenProgramTask(domainFile, this);
|
||||
String type = isSource ? "(source program)" : "(destination program)";
|
||||
openTask.setOpenPromptText("Open " + type);
|
||||
|
||||
TaskLauncher.launch(openTask);
|
||||
|
||||
OpenProgramRequest openProgram = openTask.getOpenProgram();
|
||||
return openProgram != null ? openProgram.getProgram() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(Object consumer) {
|
||||
super.release(consumer);
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
/* ###
|
||||
* 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.feature.vt.api.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.dialog.CheckoutDialog;
|
||||
import ghidra.app.util.task.ProgramOpener;
|
||||
import ghidra.feature.vt.api.db.VTSessionDB;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.remote.User;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
/**
|
||||
* {@link VTSessionFileUtil} provides methods for checking {@link VTSessionDB} source and
|
||||
* destination program files prior to being opened and used during session instantiation.
|
||||
*/
|
||||
public class VTSessionFileUtil {
|
||||
|
||||
// static utility class
|
||||
private VTSessionFileUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a VT source program to ensure it meets minimum criteria to open with a VTSession.
|
||||
* The following validation checks are performed:
|
||||
* <ul>
|
||||
* <li>file must correspond to a ProgramDB</li>
|
||||
* </ul>
|
||||
* If an error is thrown it is intended to be augmented for proper presentation.
|
||||
*
|
||||
* @param file VT Session source program domain file
|
||||
* @param includeFilePathInError if true file path will be appended to any exception throw
|
||||
* @throws IllegalArgumentException if any VT source program file criteria is not satisfied
|
||||
*/
|
||||
public static void validateSourceProgramFile(DomainFile file, boolean includeFilePathInError)
|
||||
throws IllegalArgumentException {
|
||||
String error = null;
|
||||
if (!ProgramDB.class.isAssignableFrom(file.getDomainObjectClass())) {
|
||||
error = "Source file does not correspond to a Program";
|
||||
}
|
||||
if (error != null) {
|
||||
if (includeFilePathInError) {
|
||||
error += ":\n" + file.getPathname();
|
||||
}
|
||||
throw new IllegalArgumentException(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a VT destination program to ensure it meets minimum criteria to open with a VTSession.
|
||||
* GUI mode only: If file is versioned and not checked-out the user may be prompted to perform
|
||||
* an optional checkout of the file. Prompting for checkout will not occur if this method
|
||||
* is invoked from the Swing thread or operating in a headless mode.
|
||||
* The following validation checks are performed:
|
||||
* <ul>
|
||||
* <li>file must correspond to a ProgramDB</li>
|
||||
* <li>file must be contained within the active project</li>
|
||||
* <li>file must not be marked read-only</li>
|
||||
* <li>if file is versioned it must be checked-out (user may be prompted to do this)</li>
|
||||
* </ul>
|
||||
* If an error is thrown it is intended to be augmented for proper presentation.
|
||||
*
|
||||
* @param file VT Session destination program domain file
|
||||
* @param includeFilePathInError if true file path will be appended to any exception throw
|
||||
* @param silent if user interaction should not be performed. This should be true if
|
||||
* filesystem lock is currently held.
|
||||
* @throws IllegalArgumentException if any VT destination program file criteria is not satisfied
|
||||
*/
|
||||
public static void validateDestinationProgramFile(DomainFile file,
|
||||
boolean includeFilePathInError, boolean silent) throws IllegalArgumentException {
|
||||
String error = null;
|
||||
if (!ProgramDB.class.isAssignableFrom(file.getDomainObjectClass())) {
|
||||
error = "Destination file does not correspond to a Program";
|
||||
}
|
||||
else {
|
||||
DomainFolder folder = file.getParent();
|
||||
if (folder == null || !folder.isInWritableProject()) {
|
||||
error = "Destination file must be from active project";
|
||||
}
|
||||
else if (file.isReadOnly()) {
|
||||
error = "Destination file must not be read-only";
|
||||
}
|
||||
else if (file.isVersioned()) {
|
||||
if (!silent) {
|
||||
doOptionalDestinationProgramCheckout(file);
|
||||
}
|
||||
if (!file.isCheckedOut()) {
|
||||
error = "Versioned destination file must be checked-out for update";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error != null) {
|
||||
if (includeFilePathInError) {
|
||||
error += ":\n" + file.getPathname();
|
||||
}
|
||||
throw new IllegalArgumentException(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the specified {@link DomainFile} will permit update.
|
||||
* @param file domain file
|
||||
* @return true if file permits update else false
|
||||
*/
|
||||
public static boolean canUpdate(DomainFile file) {
|
||||
DomainFolder folder = file.getParent();
|
||||
if (folder == null || !folder.isInWritableProject()) {
|
||||
return false;
|
||||
}
|
||||
if (file.isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
if (file.isVersioned()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void doOptionalDestinationProgramCheckout(DomainFile file) {
|
||||
|
||||
if (SystemUtilities.isInHeadlessMode() || !file.canCheckout()) {
|
||||
return;
|
||||
}
|
||||
|
||||
User user = file.getParent().getProjectData().getUser();
|
||||
CheckoutDialog dialog = new CheckoutDialog(file, user);
|
||||
dialog.setTitle("VT Destination Program not Checked Out");
|
||||
if (dialog.showDialog() == CheckoutDialog.CHECKOUT) { // uses Swing thread
|
||||
CheckoutDestinationProgramTask task =
|
||||
new CheckoutDestinationProgramTask(file, dialog.exclusiveCheckout());
|
||||
TaskLauncher.launch(task);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CheckoutDestinationProgramTask extends Task {
|
||||
|
||||
private DomainFile file;
|
||||
boolean exclusiveCheckout;
|
||||
|
||||
CheckoutDestinationProgramTask(DomainFile file, boolean exclusiveCheckout) {
|
||||
super("Checking Out " + file, true, true, true, true);
|
||||
this.file = file;
|
||||
this.exclusiveCheckout = exclusiveCheckout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
monitor.setMessage("Checking Out " + file);
|
||||
try {
|
||||
if (!file.checkout(exclusiveCheckout, monitor)) {
|
||||
Msg.showError(ProgramOpener.class, null, "Checkout Failed",
|
||||
"Exclusive checkout failed for: " + file +
|
||||
"\nOne or more users have file checked out!");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(ProgramOpener.class, null, "Checkout Failed",
|
||||
"Checkout failed for: " + file + "\n" + e.getMessage());
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -46,7 +46,7 @@ public interface VTController extends VTSessionSupplier {
|
|||
@Override
|
||||
public VTSession getSession();
|
||||
|
||||
public void openVersionTrackingSession(DomainFile domainFile);
|
||||
public boolean openVersionTrackingSession(DomainFile domainFile);
|
||||
|
||||
public void openVersionTrackingSession(VTSession session);
|
||||
|
||||
|
|
|
@ -22,31 +22,36 @@ import java.util.*;
|
|||
import javax.swing.SwingUtilities;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerActionContext;
|
||||
import ghidra.app.plugin.core.colorizer.ColorizingService;
|
||||
import ghidra.feature.vt.api.db.VTAssociationDB;
|
||||
import ghidra.feature.vt.api.db.VTSessionDB;
|
||||
import ghidra.feature.vt.api.main.*;
|
||||
import ghidra.feature.vt.api.util.VTSessionFileUtil;
|
||||
import ghidra.feature.vt.gui.duallisting.VTListingContext;
|
||||
import ghidra.feature.vt.gui.provider.markuptable.VTMarkupItemContext;
|
||||
import ghidra.feature.vt.gui.task.SaveTask;
|
||||
import ghidra.feature.vt.gui.task.VtTask;
|
||||
import ghidra.feature.vt.gui.util.MatchInfo;
|
||||
import ghidra.feature.vt.gui.util.MatchInfoFactory;
|
||||
import ghidra.framework.client.RepositoryAdapter;
|
||||
import ghidra.framework.data.DomainObjectAdapterDB;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.main.SaveDataDialog;
|
||||
import ghidra.framework.main.projectdata.actions.CheckoutsDialog;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.framework.store.ItemCheckoutStatus;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.AddressCorrelation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.WeakValueHashMap;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
|
@ -94,30 +99,193 @@ public class VTControllerImpl
|
|||
return session;
|
||||
}
|
||||
|
||||
private boolean checkSessionFileAccess(DomainFile domainFile) {
|
||||
|
||||
DomainFolder folder = domainFile.getParent();
|
||||
if (folder == null || !folder.isInWritableProject()) {
|
||||
Msg.showError(this, null, "Can't open VT Session: " + domainFile,
|
||||
"VT Session file use limited to active project only.");
|
||||
return false;
|
||||
}
|
||||
if (domainFile.isVersioned()) {
|
||||
if (domainFile.isCheckedOut()) {
|
||||
if (!domainFile.isCheckedOutExclusive()) {
|
||||
Msg.showError(this, null, "Can't open VT Session: " + domainFile,
|
||||
"VT Session file is checked-out but does not have exclusive access.\n" +
|
||||
"You must undo checkout and re-checkout with exclusive access.");
|
||||
return false;
|
||||
}
|
||||
if (domainFile.isReadOnly()) {
|
||||
Msg.showError(this, null, "Can't open VT Session: " + domainFile,
|
||||
"VT Session file is set read-only which prevents its use.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return checkoutSession(domainFile);
|
||||
}
|
||||
else if (domainFile.isReadOnly()) { // non-versioned file
|
||||
Msg.showError(this, null, "Can't open VT Session: " + domainFile,
|
||||
"VT Session file is set read-only which prevents its use.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkoutSession(DomainFile domainFile) {
|
||||
|
||||
Project activeProject = AppInfo.getActiveProject();
|
||||
RepositoryAdapter repository = activeProject.getRepository();
|
||||
|
||||
if (repository != null) {
|
||||
try {
|
||||
ItemCheckoutStatus[] checkouts = domainFile.getCheckouts();
|
||||
if (checkouts.length != 0) {
|
||||
int rc = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null,
|
||||
"Checkout VT Session",
|
||||
"VT Session " + domainFile.getName() + " is NOT CHECKED OUT but " +
|
||||
"is checked-out by another user.\n" +
|
||||
"Opening VT Session requires an exclusive check out of this file.\n" +
|
||||
"Do you want to view the list of active checkouts for this file?",
|
||||
"View Checkout(s)...");
|
||||
if (rc != OptionDialog.OPTION_ONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CheckoutsDialog dialog = new CheckoutsDialog(plugin.getTool(),
|
||||
repository.getUser(), domainFile, checkouts);
|
||||
plugin.getTool().showDialog(dialog);
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Checkout VT Session Failed: " + domainFile.getName(),
|
||||
e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int rc = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null, "Checkout VT Session",
|
||||
"VT Session " + domainFile.getName() + " is NOT CHECKED OUT.\n" +
|
||||
"Opening VT Session requires an exclusive check out of this file.\n" +
|
||||
"Do you want to Check Out this file?",
|
||||
"Checkout...");
|
||||
if (rc != OptionDialog.OPTION_ONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TaskLauncher.launchModal("Checkout VT Session", new MonitoredRunnable() {
|
||||
|
||||
@Override
|
||||
public void monitoredRun(TaskMonitor monitor) {
|
||||
try {
|
||||
domainFile.checkout(true, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// ignore
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Checkout VT Session Failed: " + domainFile.getName(),
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
return domainFile.isCheckedOutExclusive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openVersionTrackingSession(DomainFile domainFile) {
|
||||
public boolean openVersionTrackingSession(DomainFile domainFile) {
|
||||
if (!VTSession.class.isAssignableFrom(domainFile.getDomainObjectClass())) {
|
||||
throw new IllegalArgumentException("File does not correspond to a VTSession");
|
||||
}
|
||||
if (!checkForUnSavedChanges()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
VTSessionDB newSession =
|
||||
(VTSessionDB) domainFile.getDomainObject(this, true, true, TaskMonitor.DUMMY);
|
||||
doOpenSession(newSession);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
Msg.showError(this, null, "Can't open domainFile " + domainFile.getName(),
|
||||
e.getMessage());
|
||||
if (!checkSessionFileAccess(domainFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VTSessionDB vtSessionDB = getVTSessionDB(domainFile, this);
|
||||
if (vtSessionDB != null) {
|
||||
try {
|
||||
openVersionTrackingSession(vtSessionDB);
|
||||
return true;
|
||||
}
|
||||
finally {
|
||||
vtSessionDB.release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
Msg.error(this, "Got unexexped cancelled exception", e);
|
||||
// ignore - return false
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(null, domainFile.getName(), "VT Session",
|
||||
"open", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Can't open " + domainFile.getName(), e.getMessage());
|
||||
Msg.showError(this, null, "Can't open VT Session: " + domainFile.getName(),
|
||||
e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class OpenVTSessionTask extends Task {
|
||||
|
||||
private final Object consumer;
|
||||
private final DomainFile vtSessionFile;
|
||||
|
||||
Exception exception;
|
||||
VTSessionDB vtSessionDB;
|
||||
|
||||
OpenVTSessionTask(DomainFile vtSessionFile, Object consumer) {
|
||||
super("Opening VT Session", true, false, true, true);
|
||||
this.vtSessionFile = vtSessionFile;
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
try {
|
||||
vtSessionDB =
|
||||
(VTSessionDB) vtSessionFile.getDomainObject(consumer, true, true, monitor);
|
||||
}
|
||||
catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VTSessionDB getVTSessionDB(DomainFile vtSessionFile, Object consumer)
|
||||
throws IOException, VersionException, CancelledException {
|
||||
|
||||
OpenVTSessionTask task = new OpenVTSessionTask(vtSessionFile, consumer);
|
||||
|
||||
TaskLauncher.launch(task);
|
||||
|
||||
if (task.exception != null) {
|
||||
if (task.exception instanceof CancelledException ce) {
|
||||
throw ce;
|
||||
}
|
||||
if (task.exception instanceof VersionException ve) {
|
||||
throw ve;
|
||||
}
|
||||
if (task.exception instanceof IOException ioe) {
|
||||
throw ioe;
|
||||
}
|
||||
throw new IOException("VTSessionDB failure", task.exception);
|
||||
}
|
||||
|
||||
return task.vtSessionDB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openVersionTrackingSession(VTSession newSession) {
|
||||
// FIXME: new session wizard should have handled existing session before starting -
|
||||
// should be no need for this check
|
||||
if (!checkForUnSavedChanges()) {
|
||||
return;
|
||||
}
|
||||
|
@ -595,43 +763,79 @@ public class VTControllerImpl
|
|||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private void updateProgram(DomainFile file, boolean isSource) {
|
||||
|
||||
String type = isSource ? "Source" : "Destination";
|
||||
Program newProgram;
|
||||
try {
|
||||
newProgram = (Program) file.getDomainObject(this, false, false, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(this, getParentComponent(),
|
||||
"Error opening VT " + type + " Program: " + file, e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSource) {
|
||||
session.updateSourceProgram(newProgram);
|
||||
}
|
||||
else {
|
||||
session.updateDestinationProgram(newProgram);
|
||||
}
|
||||
|
||||
// List<DomainObjectChangeRecord> events = new ArrayList<DomainObjectChangeRecord>();
|
||||
// events.add(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
// domainObjectChanged(new DomainObjectChangedEvent(newProgram, events));
|
||||
matchInfoFactory.clearCache();
|
||||
fireSessionChanged();
|
||||
}
|
||||
|
||||
private class MyFolderListener extends DomainFolderListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void domainFileObjectReplaced(DomainFile file, DomainObject oldObject) {
|
||||
|
||||
/**
|
||||
* Special handling for when a file is checked-in. The existing program has be moved
|
||||
* to a proxy file (no longer in the project) so that it can be closed and the program
|
||||
* re-opened with the new version after the check-in merge.
|
||||
*/
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
if (session.getSourceProgram() != oldObject &&
|
||||
session.getDestinationProgram() != oldObject) {
|
||||
return;
|
||||
}
|
||||
Program newProgram;
|
||||
try {
|
||||
newProgram = (Program) file.getDomainObject(this, false, false, TaskMonitor.DUMMY);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(this, getParentComponent(), "Error opening program " + file, e);
|
||||
|
||||
if (session.getSourceProgram() == oldObject) {
|
||||
updateProgram(file, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (oldObject == session.getSourceProgram()) {
|
||||
session.updateSourceProgram(newProgram);
|
||||
String type;
|
||||
if (session == oldObject) {
|
||||
type = "VT Session";
|
||||
}
|
||||
else if (oldObject == session.getDestinationProgram()) {
|
||||
session.updateDestinationProgram(newProgram);
|
||||
else if (session.getDestinationProgram() == oldObject) {
|
||||
if (VTSessionFileUtil.canUpdate(file)) {
|
||||
updateProgram(file, false);
|
||||
return;
|
||||
}
|
||||
type = "Destination Program";
|
||||
}
|
||||
// List<DomainObjectChangeRecord> events = new ArrayList<DomainObjectChangeRecord>();
|
||||
// events.add(new DomainObjectChangeRecord(DomainObjectEvent.RESTORED));
|
||||
// domainObjectChanged(new DomainObjectChangedEvent(newProgram, events));
|
||||
matchInfoFactory.clearCache();
|
||||
fireSessionChanged();
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Session or destination program can no longer be saved to project so we
|
||||
// have no choice but to close session.
|
||||
|
||||
// Since we are already in the Swing thread we need to delay closing so we do
|
||||
// not continue to block the Swing thread and the checkin which is in progress.
|
||||
// This allows the DomainFile checkin to complete its processing first.
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
|
||||
Msg.showInfo(this, plugin.getTool().getToolFrame(), "Closing VT Session",
|
||||
type + " checkin has forced session close.\n" +
|
||||
"You will be prompted to save any other changes if needed, after which\n" +
|
||||
"you may reopen the VT Session.");
|
||||
|
||||
closeVersionTrackingSession();
|
||||
|
||||
// NOTE: a future convenience could be added to attempt reopening of session
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -216,10 +216,10 @@ public class VTPlugin extends Plugin {
|
|||
for (DomainFile domainFile : data) {
|
||||
if (domainFile != null &&
|
||||
VTSession.class.isAssignableFrom(domainFile.getDomainObjectClass())) {
|
||||
openVersionTrackingSession(domainFile);
|
||||
return true;
|
||||
return controller.openVersionTrackingSession(domainFile);
|
||||
}
|
||||
}
|
||||
|
||||
DomainFile programFile1 = null;
|
||||
DomainFile programFile2 = null;
|
||||
for (DomainFile domainFile : data) {
|
||||
|
@ -249,10 +249,6 @@ public class VTPlugin extends Plugin {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void openVersionTrackingSession(DomainFile domainFile) {
|
||||
controller.openVersionTrackingSession(domainFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
controller.readConfigState(saveState);
|
||||
|
@ -274,20 +270,18 @@ public class VTPlugin extends Plugin {
|
|||
@Override
|
||||
public void readDataState(SaveState saveState) {
|
||||
String pathname = saveState.getString("PATHNAME", null);
|
||||
String location = saveState.getString("PROJECT_LOCATION", null);
|
||||
String projectName = saveState.getString("PROJECT_NAME", null);
|
||||
if (location == null || projectName == null) {
|
||||
if (pathname == null) {
|
||||
return;
|
||||
}
|
||||
ProjectLocator url = new ProjectLocator(location, projectName);
|
||||
|
||||
ProjectData projectData = tool.getProject().getProjectData(url);
|
||||
if (projectData == null) {
|
||||
Msg.showError(this, tool.getToolFrame(), "File Not Found", "Could not find " + url);
|
||||
Project project = tool.getProject();
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectData projectData = project.getProjectData();
|
||||
DomainFile domainFile = projectData.getFile(pathname);
|
||||
if (domainFile == null) {
|
||||
return;
|
||||
}
|
||||
controller.openVersionTrackingSession(domainFile);
|
||||
}
|
||||
|
||||
|
@ -298,21 +292,7 @@ public class VTPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
DomainFile domainFile = session.getDomainFile();
|
||||
|
||||
String projectLocation = null;
|
||||
String projectName = null;
|
||||
String path = null;
|
||||
ProjectLocator url = domainFile.getProjectLocator();
|
||||
if (url != null) {
|
||||
projectLocation = url.getLocation();
|
||||
projectName = url.getName();
|
||||
path = domainFile.getPathname();
|
||||
}
|
||||
|
||||
saveState.putString("PROJECT_LOCATION", projectLocation);
|
||||
saveState.putString("PROJECT_NAME", projectName);
|
||||
saveState.putString("PATHNAME", path);
|
||||
|
||||
saveState.putString("PATHNAME", domainFile.getPathname());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,10 +15,11 @@
|
|||
*/
|
||||
package ghidra.feature.vt.gui.wizard;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import docking.wizard.WizardState;
|
||||
import ghidra.feature.vt.api.db.VTSessionDB;
|
||||
import ghidra.feature.vt.api.main.VTSession;
|
||||
import ghidra.feature.vt.gui.plugin.VTController;
|
||||
import ghidra.framework.data.DomainObjectAdapterDB;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.InvalidNameException;
|
||||
|
@ -28,11 +28,6 @@ import ghidra.util.exception.CancelledException;
|
|||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.io.IOException;
|
||||
|
||||
import docking.wizard.WizardState;
|
||||
|
||||
public class CreateNewSessionTask extends Task {
|
||||
private final WizardState<VTWizardStateKey> state;
|
||||
private final VTController controller;
|
||||
|
@ -45,57 +40,41 @@ public class CreateNewSessionTask extends Task {
|
|||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
VTSession session = null;
|
||||
VTSessionDB session = null;
|
||||
String name = null;
|
||||
try {
|
||||
Program sourceProgram = (Program) state.get(VTWizardStateKey.SOURCE_PROGRAM);
|
||||
Program destinationProgram = (Program) state.get(VTWizardStateKey.DESTINATION_PROGRAM);
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession("New Session", sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB("New Session", sourceProgram, destinationProgram, this);
|
||||
|
||||
DomainObjectAdapterDB dobj = null;
|
||||
if (session instanceof DomainObjectAdapterDB) {
|
||||
dobj = (DomainObjectAdapterDB) session;
|
||||
}
|
||||
sourceProgram.release(controller.getTool());
|
||||
destinationProgram.release(controller.getTool());
|
||||
if (dobj != null) {
|
||||
name = (String) state.get(VTWizardStateKey.SESSION_NAME);
|
||||
DomainFolder folder = (DomainFolder) state.get(VTWizardStateKey.NEW_SESSION_FOLDER);
|
||||
try {
|
||||
folder.createFile(name, dobj, monitor);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
Msg.showError(this, null, "Invalid Domain Object Name",
|
||||
"Please report this error; the name should have been checked already");
|
||||
}
|
||||
|
||||
name = (String) state.get(VTWizardStateKey.SESSION_NAME);
|
||||
DomainFolder folder = (DomainFolder) state.get(VTWizardStateKey.NEW_SESSION_FOLDER);
|
||||
try {
|
||||
folder.createFile(name, session, monitor);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
Msg.showError(this, null, "Invalid Domain Object Name",
|
||||
"Please report this error; the name should have been checked already");
|
||||
}
|
||||
|
||||
final VTSession finalSession = session;
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
controller.openVersionTrackingSession(finalSession);
|
||||
releaseDomainObject(finalSession);
|
||||
}
|
||||
});
|
||||
controller.openVersionTrackingSession(session);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// the user cancelled; just cleanup
|
||||
releaseDomainObject(session);
|
||||
// ignore
|
||||
}
|
||||
catch (IOException e) {
|
||||
releaseDomainObject(session);
|
||||
Msg.showError(this, null, "Failed to Create Session", "Failed to create db file: " +
|
||||
name, e);
|
||||
Msg.showError(this, null, "Failed to Create Session",
|
||||
"Failed to create db file: " + name, e);
|
||||
}
|
||||
finally {
|
||||
if (session != null) {
|
||||
session.release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseDomainObject(VTSession session) {
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
((VTSessionDB) session).release(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,22 +15,10 @@
|
|||
*/
|
||||
package ghidra.feature.vt.gui.wizard;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
|
@ -38,22 +26,19 @@ import org.apache.commons.lang3.StringUtils;
|
|||
|
||||
import docking.widgets.button.BrowseButton;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.wizard.AbstractMageJPanel;
|
||||
import docking.wizard.WizardPanelDisplayability;
|
||||
import docking.wizard.WizardState;
|
||||
import docking.wizard.*;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.GThemeDefaults.Ids.Fonts;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.app.util.task.OpenProgramRequest;
|
||||
import ghidra.app.util.task.OpenProgramTask;
|
||||
import ghidra.feature.vt.api.util.VTSessionFileUtil;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.StringUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
|
||||
/**
|
||||
|
@ -309,28 +294,31 @@ public class NewSessionPanel extends AbstractMageJPanel<VTWizardStateKey> {
|
|||
private String createVTSessionName(String sourceName, String destinationName) {
|
||||
|
||||
// if together they are within the bounds just return session name with both full names
|
||||
if (sourceName.length() + destinationName.length() <= 2 * VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) {
|
||||
if (sourceName.length() + destinationName.length() <= 2 *
|
||||
VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) {
|
||||
return "VT_" + sourceName + "_" + destinationName;
|
||||
}
|
||||
|
||||
// give destination name all space not used by source name
|
||||
if (sourceName.length() < VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) {
|
||||
int leftover = VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH - sourceName.length();
|
||||
destinationName =
|
||||
StringUtilities.trimMiddle(destinationName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH + leftover);
|
||||
destinationName = StringUtilities.trimMiddle(destinationName,
|
||||
VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH + leftover);
|
||||
return "VT_" + sourceName + "_" + destinationName;
|
||||
}
|
||||
|
||||
// give source name all space not used by destination name
|
||||
if (destinationName.length() < VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH) {
|
||||
int leftover = VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH - destinationName.length();
|
||||
sourceName = StringUtilities.trimMiddle(sourceName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH + leftover);
|
||||
sourceName = StringUtilities.trimMiddle(sourceName,
|
||||
VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH + leftover);
|
||||
return "VT_" + sourceName + "_" + destinationName;
|
||||
}
|
||||
|
||||
// if both too long, shorten both of them
|
||||
sourceName = StringUtilities.trimMiddle(sourceName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH);
|
||||
destinationName = StringUtilities.trimMiddle(destinationName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH);
|
||||
destinationName =
|
||||
StringUtilities.trimMiddle(destinationName, VTSESSION_NAME_PROGRAM_NAME_MAX_LENGTH);
|
||||
|
||||
return "VT_" + sourceName + "_" + destinationName;
|
||||
}
|
||||
|
@ -418,16 +406,17 @@ public class NewSessionPanel extends AbstractMageJPanel<VTWizardStateKey> {
|
|||
state.put(VTWizardStateKey.NEW_SESSION_FOLDER, folder);
|
||||
}
|
||||
|
||||
private void openProgram(ProgramInfo programInfo) {
|
||||
private boolean openProgram(ProgramInfo programInfo) {
|
||||
|
||||
if (programInfo.hasProgram()) {
|
||||
return; // already open
|
||||
return true; // already open
|
||||
}
|
||||
|
||||
OpenProgramTask openProgramTask = new OpenProgramTask(programInfo.getFile(), tool);
|
||||
new TaskLauncher(openProgramTask, tool.getActiveWindow());
|
||||
OpenProgramRequest openProgram = openProgramTask.getOpenProgram();
|
||||
programInfo.setProgram(openProgram != null ? openProgram.getProgram() : null);
|
||||
return programInfo.hasProgram();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -480,19 +469,25 @@ public class NewSessionPanel extends AbstractMageJPanel<VTWizardStateKey> {
|
|||
DomainFile file = folder.getFile(name);
|
||||
if (file != null) {
|
||||
notifyListenersOfStatusMessage(
|
||||
"'" + file.getPathname() + "' is the name of an existing domain file");
|
||||
"'" + file.getPathname() + "' is the name of an existing project file");
|
||||
return false;
|
||||
}
|
||||
|
||||
openProgram(sourceProgramInfo);
|
||||
if (!sourceProgramInfo.hasProgram()) {
|
||||
// Known Issue: Opening programs before comitted to using them (i.e., Next is clicked) seems
|
||||
// premature and will subject user to prompts about possible checkout and/or upgrades
|
||||
// with possible slow re-disassembly (see GP-4151)
|
||||
|
||||
if (!isValidDestinationProgramFile() || !isValidSourceProgramFile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!openProgram(sourceProgramInfo)) {
|
||||
notifyListenersOfStatusMessage(
|
||||
"Can't open source program " + sourceProgramInfo.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
openProgram(destinationProgramInfo);
|
||||
if (!destinationProgramInfo.hasProgram()) {
|
||||
if (!openProgram(destinationProgramInfo)) {
|
||||
notifyListenersOfStatusMessage(
|
||||
"Can't open destination program " + destinationProgramInfo.getName());
|
||||
return false;
|
||||
|
@ -502,6 +497,29 @@ public class NewSessionPanel extends AbstractMageJPanel<VTWizardStateKey> {
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean isValidSourceProgramFile() {
|
||||
try {
|
||||
VTSessionFileUtil.validateSourceProgramFile(sourceProgramInfo.file, false);
|
||||
}
|
||||
catch (Exception e) {
|
||||
notifyListenersOfStatusMessage(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isValidDestinationProgramFile() {
|
||||
try {
|
||||
VTSessionFileUtil.validateDestinationProgramFile(destinationProgramInfo.file, false,
|
||||
false);
|
||||
}
|
||||
catch (Exception e) {
|
||||
notifyListenersOfStatusMessage(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDependencies(WizardState<VTWizardStateKey> state) {
|
||||
// none
|
||||
|
|
|
@ -44,8 +44,7 @@ public class VTNewSessionWizardManager extends AbstractMagePanelManager<VTWizard
|
|||
|
||||
@Override
|
||||
protected List<MagePanel<VTWizardStateKey>> createPanels() {
|
||||
List<MagePanel<VTWizardStateKey>> panels =
|
||||
new ArrayList<>();
|
||||
List<MagePanel<VTWizardStateKey>> panels = new ArrayList<>();
|
||||
panels.add(new NewSessionPanel(controller.getTool()));
|
||||
panels.add(new PreconditionsPanel(this));
|
||||
panels.add(new SummaryPanel());
|
||||
|
|
|
@ -115,9 +115,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testAddToSessionNoSelectionUnlimitedAddresses() throws Exception {
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
@ -170,9 +169,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testAddToSessionNoSelectionLimitAddressesToEntireProgram() throws Exception {
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
@ -231,9 +229,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testAddToSessionNoSelectionLimitAddressesToMyOwn() throws Exception {
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
@ -292,9 +289,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testAddToSessionNoSelectionLimitAddressesToMyOwnChanged() throws Exception {
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
@ -366,9 +362,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testAddToSessionWithSelectionLimitAddressesToEntireProgram() throws Exception {
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
@ -429,9 +424,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testAddToSessionWithSelectionLimitAddressesToSelection() throws Exception {
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
@ -492,9 +486,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testAddToSessionWithSelectionLimitAddressesToMyOwn() throws Exception {
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
@ -568,9 +561,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
@Test
|
||||
public void testAddToSessionWithSelectionLimitAddressesToMyOwnThenBackNext() throws Exception {
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
@ -671,9 +663,8 @@ public class VTAddToSessionTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testAddToSessionResultingInNoMatchesFound() throws Exception {
|
||||
|
||||
setErrorGUIEnabled(true);
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
String sessionName = "Untitled";
|
||||
|
||||
|
|
|
@ -78,9 +78,8 @@ public class VTMatchAcceptTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
plugin = getPlugin(tool, VTPlugin.class);
|
||||
controller = new VTControllerImpl(plugin);
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
runSwing(() -> controller.openVersionTrackingSession(session));
|
||||
|
||||
|
|
|
@ -81,9 +81,8 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
|
|||
VTPlugin plugin = getPlugin(tool, VTPlugin.class);
|
||||
controller = new VTControllerImpl(plugin);
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
runSwing(() -> controller.openVersionTrackingSession(session));
|
||||
|
||||
|
@ -390,8 +389,7 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testApplyMatch_ReplaceSignature_CustomSourceAndDest()
|
||||
throws Exception {
|
||||
public void testApplyMatch_ReplaceSignature_CustomSourceAndDest() throws Exception {
|
||||
|
||||
useMatch("0x00401040", "0x00401040");
|
||||
|
||||
|
@ -442,8 +440,7 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testApplyMatch_ReplaceSignature_NormalSourceCustomDest()
|
||||
throws Exception {
|
||||
public void testApplyMatch_ReplaceSignature_NormalSourceCustomDest() throws Exception {
|
||||
|
||||
useMatch("0x00401040", "0x00401040");
|
||||
|
||||
|
@ -666,9 +663,8 @@ public class VTMatchApplyFunctionSignatureTest extends AbstractGhidraHeadedInteg
|
|||
|
||||
env.release(destinationProgram);
|
||||
destinationProgram = createToyDestinationProgram();// env.getProgram("helloProgram"); // get a program without cdecl
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
runSwing(() -> controller.openVersionTrackingSession(session));
|
||||
|
||||
useMatch("0x00401040", "0x00010938");
|
||||
|
|
|
@ -94,9 +94,8 @@ public class VTMatchApplyTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
plugin = getPlugin(tool, VTPlugin.class);
|
||||
controller = new VTControllerImpl(plugin);
|
||||
|
||||
session =
|
||||
VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
session = new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
|
||||
runSwing(() -> controller.openVersionTrackingSession(session));
|
||||
|
||||
|
|
|
@ -79,8 +79,7 @@ public abstract class AbstractCorrelatorTest extends AbstractGhidraHeadedIntegra
|
|||
protected void exerciseFunctionsForFactory(final VTProgramCorrelatorFactory factory,
|
||||
AddressSetView sourceSetThatShouldBeFound) throws Exception {
|
||||
String name = factory.getName();
|
||||
VTSession session =
|
||||
VTSessionDB.createVTSession(name, sourceProgram, destinationProgram, this);
|
||||
VTSession session = new VTSessionDB(name, sourceProgram, destinationProgram, this);
|
||||
|
||||
try {
|
||||
int sessionTransaction = session.startTransaction(name);
|
||||
|
@ -145,8 +144,7 @@ public abstract class AbstractCorrelatorTest extends AbstractGhidraHeadedIntegra
|
|||
protected void exercisePreciseMatchesForFactory(VTProgramCorrelatorFactory factory,
|
||||
Map<Address, Address> map) throws Exception {
|
||||
String name = factory.getName();
|
||||
VTSession session =
|
||||
VTSessionDB.createVTSession(name, sourceProgram, destinationProgram, this);
|
||||
VTSession session = new VTSessionDB(name, sourceProgram, destinationProgram, this);
|
||||
|
||||
try {
|
||||
int sessionTransaction = session.startTransaction(name);
|
||||
|
|
|
@ -348,7 +348,7 @@ public abstract class AbstractVTMarkupItemTest extends AbstractGhidraHeadedInteg
|
|||
}
|
||||
|
||||
protected VTSessionDB createNewSession() throws Exception {
|
||||
return VTSessionDB.createVTSession(testName.getMethodName() + " - Test Match Set Manager",
|
||||
return new VTSessionDB(testName.getMethodName() + " - Test Match Set Manager",
|
||||
sourceProgram, destinationProgram, this);
|
||||
}
|
||||
|
||||
|
|
|
@ -81,8 +81,7 @@ public class VTBaseTestCase extends AbstractGenericTest {
|
|||
}
|
||||
|
||||
public VTSessionDB createVTSession() throws IOException {
|
||||
return VTSessionDB.createVTSession("Test DB", sourceProgram, destinationProgram,
|
||||
VTTestUtils.class);
|
||||
return new VTSessionDB("Test DB", sourceProgram, destinationProgram, VTTestUtils.class);
|
||||
}
|
||||
|
||||
public static int getRandomInt() {
|
||||
|
|
|
@ -69,7 +69,7 @@ public class VTTestEnv extends TestEnv {
|
|||
sourceProgram = getProgram(sourceProgramName);
|
||||
destinationProgram = getProgram(destinationProgramName);
|
||||
|
||||
session = VTSessionDB.createVTSession("Test", sourceProgram, destinationProgram, getTool());
|
||||
session = new VTSessionDB("Test", sourceProgram, destinationProgram, getTool());
|
||||
|
||||
VTProgramCorrelator correlator = factory.createCorrelator(sourceProgram,
|
||||
sourceProgram.getMemory(), destinationProgram, destinationProgram.getMemory(), null);
|
||||
|
@ -111,7 +111,7 @@ public class VTTestEnv extends TestEnv {
|
|||
}
|
||||
|
||||
private VTSessionDB createAndOpenVTSession() throws IOException {
|
||||
session = VTSessionDB.createVTSession("Test", sourceProgram, destinationProgram, getTool());
|
||||
session = new VTSessionDB("Test", sourceProgram, destinationProgram, getTool());
|
||||
|
||||
runSwing(() -> controller.openVersionTrackingSession(session), false);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ public class StubVTController implements VTController {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void openVersionTrackingSession(DomainFile domainFile) {
|
||||
public boolean openVersionTrackingSession(DomainFile domainFile) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue