changes from review

This commit is contained in:
ghidravore
2020-06-02 12:58:36 -04:00
parent e3aebe3adb
commit ca58264d65
6 changed files with 41 additions and 277 deletions
@@ -1,221 +0,0 @@
/* ###
* 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.server.remote;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import ghidra.util.Msg;
/**
* <code>RMIClassServer</code> provides a server for serializing classes to an
* RMI client as needed. This implementation starts a new listener thread each
* time a connection is accepted.
*/
public class RMIClassServer implements Runnable {
private static RMIClassServer classServer;
private ServerSocket server;
private ArrayList<Thread> threads = new ArrayList<Thread>();
/**
* Construct a new server.
* @param port
* @throws IOException if port is in use or not permitted.
*/
private RMIClassServer(int port) throws IOException {
if (classServer != null) {
throw new RuntimeException("Class server already running");
}
classServer = this;
server = new ServerSocket(port);
newListener();
}
private void dispose() {
synchronized (RMIClassServer.class) {
classServer = null;
for (Thread t : threads) {
t.interrupt();
}
if (server != null) {
try {
server.close();
} catch (IOException e) {
}
server = null;
}
}
}
private void newListener() {
synchronized (RMIClassServer.class) {
if (classServer == this) { // make sure we have not been stopped
Thread t = new Thread(this, "RMI Class Server");
threads.add(t);
t.start();
}
}
}
/*
* @see java.lang.Runnable#run()
*/
public void run() {
// accept connection
Socket socket = null;
try {
socket = server.accept();
} catch (NullPointerException e) {
// Just in case server is null after dispose
} catch (InterruptedIOException e) {
} catch (Throwable t) {
Msg.error(this, "Class server error: " + t.toString(), t);
}
synchronized (RMIClassServer.class) {
// create a new thread to accept the next connection
newListener();
if (socket == null) {
threads.remove(Thread.currentThread());
return;
}
}
try {
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
try {
// get path to class file
DataInputStream in = new DataInputStream(socket.getInputStream());
String path = getClassName(in);
byte[] bytecodes = getBytes(path);
// send bytecodes in response (assumes HTTP/1.0 or later)
try {
out.writeBytes("HTTP/1.0 200 OK\r\n");
out.writeBytes("Content-Length: " + bytecodes.length + "\r\n");
out.writeBytes("Content-Type: application/java\r\n\r\n");
out.write(bytecodes);
out.flush();
} catch (IOException ie) {
return;
}
} catch (Exception e) {
// write out error response
out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n");
out.writeBytes("Content-Type: text/html\r\n\r\n");
out.flush();
}
} catch (InterruptedIOException e) {
} catch (Throwable t) {
// eat exception
Msg.error(this, "error writing response: " + t.getMessage(), t);
}
try {
socket.close();
} catch (IOException e) {
} finally {
synchronized (RMIClassServer.class) {
threads.remove(Thread.currentThread());
}
}
}
/**
* Get the full classname requested or null if not a valid class file request.
* Consumes all input from client.
* @param in HTTP input stream
* @return class name including package prefix
* @throws IOException if request was not for a class file
*/
private String getClassName(DataInputStream in) throws IOException {
String line = in.readLine();
String path = "";
if (line.startsWith("GET /")) {
line = line.substring(5, line.length()-1).trim();
int index = line.indexOf(".class");
if (index != -1) {
path = line.substring(0, index+6);
}
}
// eat the rest of header
do {
line = in.readLine();
} while ((line.length() != 0));
if (path.length() != 0) {
return path;
}
throw new IOException("Malformed Header");
}
/**
* Get byte code data for specified classname
* @param classname full class name including package
* @return byte-code data for class
*/
private byte[] getBytes(String classname) throws IOException {
InputStream istream = ClassLoader.getSystemResourceAsStream(classname);
if (istream == null) {
throw new IOException("Class not found");
}
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataInputStream in = new DataInputStream(istream);
byte[] buf = new byte[4096];
int readLen = 0;
while ((readLen = in.read(buf)) >= 0) {
out.write(buf, 0, readLen);
}
return out.toByteArray();
}
finally {
istream.close();
}
}
/**
* Start a class file server.
* @param port port to use
*/
static synchronized void startServer(int port) throws IOException {
new RMIClassServer(port);
}
/**
* Stop the class server if running
*/
static synchronized void stopServer() {
if (classServer != null) {
Msg.info(RMIClassServer.class, "Stopping class server...");
classServer.dispose();
}
}
}
@@ -310,12 +310,11 @@ class CheckoutManager {
Element root = new Element("CHECKOUT_LIST");
root.setAttribute("NEXT_ID", Long.toString(nextCheckoutId));
for (long id : checkouts.keySet()) {
ItemCheckoutStatus coStatus = checkouts.get(id);
for (ItemCheckoutStatus status : checkouts.values()) {
// TRANSIENT checkout data must not be persisted - the existence
// of such checkouts is retained in-memory only
if (coStatus.getCheckoutType() != CheckoutType.TRANSIENT) {
root.addContent(getCheckoutElement(coStatus));
if (status.getCheckoutType() != CheckoutType.TRANSIENT) {
root.addContent(getCheckoutElement(status));
}
}
@@ -105,37 +105,6 @@ public class ObjectCache {
addToHardCache(obj);
}
/**
* Clear both hard and weak caches.
* The cache should be cleared when all cached objects have become invalid.
*/
public synchronized void clear() {
processQueue();
for (KeyedSoftReference<?> ref : hashTable.values()) {
ref.clear();
}
hashTable.clear();
refQueue = new ReferenceQueue<Object>();
}
/**
* Remove the specified range of keyed objects from both hard and weak caches.
* A cache range should be cleared when the corresponding objects have become invalid.
* @param startKey minimum object key value
* @param endKey maximum object key value
*/
public synchronized void remove(long startKey, long endKey) {
if ((endKey >> 1) - (startKey >> 1) < (hashTable.size() >> 1)) {
for (long i = startKey; i <= endKey; i++) {
remove(i);
}
}
else {
hashTable.keySet().removeIf(key -> (key >= startKey && key <= endKey));
}
}
/**
* Remove the specified keyed object from both hard and weak caches.
* An object should be removed from the cache when it becomes invalid.
@@ -35,7 +35,7 @@ public class StringAttribute<T extends KeyedObject> extends Attribute<T> {
*/
public StringAttribute(String name, KeyIndexableSet<T> set) {
super(name, set);
this.values = new HashMap<>(set.capacity());// String[set.capacity()];
this.values = new HashMap<>();
}
/** Set the value of this attribute for the specified KeyedObject.
@@ -162,11 +162,8 @@ class OverlaySpaceAdapterDB {
}
}
}
if (map.size() != 0) {
for (OverlayAddressSpace space : map.values()) {
factory.removeOverlaySpace(space.getName());
}
map.clear();
for (OverlayAddressSpace space : map.values()) {
factory.removeOverlaySpace(space.getName());
}
}
@@ -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,6 +15,11 @@
*/
package ghidra.program.database.properties;
import java.io.IOException;
import java.util.NoSuchElementException;
import db.*;
import db.util.ErrorHandler;
import ghidra.program.database.map.*;
import ghidra.program.database.util.DatabaseTableUtils;
import ghidra.program.model.address.*;
@@ -28,12 +32,6 @@ import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
import java.io.IOException;
import java.util.NoSuchElementException;
import db.*;
import db.util.ErrorHandler;
/**
* Abstract class which defines a map containing properties over a set of addresses.
* The map is stored within a database table.
@@ -137,8 +135,9 @@ public abstract class PropertyMapDB implements PropertyMap {
monitor.setProgress(++count);
}
if (tempTable == null)
if (tempTable == null) {
return;
}
// Remove old table
dbHandle.deleteTable(getTableName());
@@ -192,6 +191,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getName()
*/
@Override
public String getName() {
return name;
}
@@ -221,7 +221,7 @@ public abstract class PropertyMapDB implements PropertyMap {
lock.acquire();
try {
if (propertyTable != null) {
cache.clear();
cache = null;
dbHandle.deleteTable(getTableName());
propertyTable = null;
}
@@ -234,6 +234,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#intersects(ghidra.program.model.address.Address, ghidra.program.model.address.Address)
*/
@Override
public boolean intersects(Address startAddr, Address endAddr) {
if (propertyTable == null) {
return false;
@@ -252,6 +253,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#intersects(ghidra.program.model.address.AddressSetView)
*/
@Override
public boolean intersects(AddressSetView set) {
if (propertyTable == null) {
return false;
@@ -270,6 +272,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#removeRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address)
*/
@Override
public boolean removeRange(Address startAddr, Address endAddr) {
if (propertyTable == null) {
return false;
@@ -277,7 +280,7 @@ public abstract class PropertyMapDB implements PropertyMap {
lock.acquire();
try {
if (AddressRecordDeleter.deleteRecords(propertyTable, addrMap, startAddr, endAddr)) {
cache.clear();
cache = new ObjectCache(DEFAULT_CACHE_SIZE);
return true;
}
}
@@ -293,6 +296,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#remove(ghidra.program.model.address.Address)
*/
@Override
public boolean remove(Address addr) {
if (propertyTable == null) {
return false;
@@ -317,6 +321,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#hasProperty(ghidra.program.model.address.Address)
*/
@Override
public boolean hasProperty(Address addr) {
if (propertyTable == null) {
return false;
@@ -341,6 +346,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getNextPropertyAddress(ghidra.program.model.address.Address)
*/
@Override
public Address getNextPropertyAddress(Address addr) {
if (propertyTable == null) {
return null;
@@ -360,6 +366,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getPreviousPropertyAddress(ghidra.program.model.address.Address)
*/
@Override
public Address getPreviousPropertyAddress(Address addr) {
if (propertyTable == null) {
return null;
@@ -379,6 +386,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getFirstPropertyAddress()
*/
@Override
public Address getFirstPropertyAddress() {
if (propertyTable == null) {
return null;
@@ -398,6 +406,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getLastPropertyAddress()
*/
@Override
public Address getLastPropertyAddress() {
if (propertyTable == null) {
return null;
@@ -419,6 +428,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getSize()
*/
@Override
public int getSize() {
return propertyTable != null ? propertyTable.getRecordCount() : 0;
}
@@ -434,8 +444,9 @@ public abstract class PropertyMapDB implements PropertyMap {
public AddressKeyIterator getAddressKeyIterator(AddressSetView set, boolean atStart)
throws IOException {
if (propertyTable == null)
if (propertyTable == null) {
return new AddressKeyIterator();
}
if (atStart) {
return new AddressKeyIterator(propertyTable, addrMap, set, set.getMinAddress(), true);
}
@@ -452,8 +463,9 @@ public abstract class PropertyMapDB implements PropertyMap {
public AddressKeyIterator getAddressKeyIterator(Address start, boolean before)
throws IOException {
if (propertyTable == null)
if (propertyTable == null) {
return new AddressKeyIterator();
}
return new AddressKeyIterator(propertyTable, addrMap, start, before);
}
@@ -469,8 +481,9 @@ public abstract class PropertyMapDB implements PropertyMap {
public AddressKeyIterator getAddressKeyIterator(Address start, Address end, boolean atStart)
throws IOException {
if (propertyTable == null)
if (propertyTable == null) {
return new AddressKeyIterator();
}
if (atStart) {
return new AddressKeyIterator(propertyTable, addrMap, start, end, start, true);
}
@@ -480,6 +493,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getPropertyIterator(ghidra.program.model.address.Address, ghidra.program.model.address.Address)
*/
@Override
public AddressIterator getPropertyIterator(Address start, Address end) {
AddressKeyIterator keyIter = null;
try {
@@ -494,6 +508,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getPropertyIterator(ghidra.program.model.address.Address, ghidra.program.model.address.Address, boolean)
*/
@Override
public AddressIterator getPropertyIterator(Address start, Address end, boolean forward) {
AddressKeyIterator keyIter = null;
try {
@@ -508,6 +523,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getPropertyIterator()
*/
@Override
public AddressIterator getPropertyIterator() {
if (propertyTable == null) {
return new EmptyAddressIterator();
@@ -525,6 +541,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getPropertyIterator(ghidra.program.model.address.AddressSetView)
*/
@Override
public AddressIterator getPropertyIterator(AddressSetView asv) {
if (propertyTable == null) {
return new EmptyAddressIterator();
@@ -543,6 +560,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getPropertyIterator(ghidra.program.model.address.AddressSetView, boolean)
*/
@Override
public AddressIterator getPropertyIterator(AddressSetView asv, boolean forward) {
if (propertyTable == null) {
return new EmptyAddressIterator();
@@ -567,6 +585,7 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#getPropertyIterator(ghidra.program.model.address.Address, boolean)
*/
@Override
public AddressIterator getPropertyIterator(Address start, boolean forward) {
if (propertyTable == null) {
return new EmptyAddressIterator();
@@ -588,7 +607,7 @@ public abstract class PropertyMapDB implements PropertyMap {
lock.acquire();
try {
propertyTable = dbHandle.getTable(getTableName());
cache.clear();
cache = new ObjectCache(DEFAULT_CACHE_SIZE);
}
finally {
lock.release();
@@ -599,10 +618,11 @@ public abstract class PropertyMapDB implements PropertyMap {
/**
* @see ghidra.program.model.util.PropertyMap#moveRange(ghidra.program.model.address.Address, ghidra.program.model.address.Address, ghidra.program.model.address.Address)
*/
@Override
public void moveRange(Address start, Address end, Address newStart) {
lock.acquire();
try {
cache.clear();
cache = new ObjectCache(DEFAULT_CACHE_SIZE);
if (propertyTable != null) {
try {
DatabaseTableUtils.updateAddressKey(propertyTable, addrMap, start, end,