mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-07-30 07:18:37 -09:00
Merge branch 'GP-6862_CompressNativeMemoryLeak' into patch
This commit is contained in:
@@ -18,12 +18,13 @@ public final class GFileUtilityMethods {
|
||||
public final static File writeTemporaryFile( InputStream inputStream, int maxBytesToWrite ) throws IOException {
|
||||
File tempOutputFile = File.createTempFile( GHIDRA_FILE_SYSTEM_PREFIX, GHIDRA_FILE_SYSTEM_SUFFIX );
|
||||
tempOutputFile.deleteOnExit();
|
||||
OutputStream outputStream = new FileOutputStream( tempOutputFile );
|
||||
try {
|
||||
|
||||
try (OutputStream outputStream = new FileOutputStream(tempOutputFile)) {
|
||||
int nWritten = 0;
|
||||
byte [] buffer = new byte[ 8192 ];
|
||||
byte[] buffer = new byte[8192];
|
||||
while ( true ) {
|
||||
int nRead = inputStream.read( buffer );
|
||||
int limit = Math.min(maxBytesToWrite - nWritten, buffer.length);
|
||||
int nRead = inputStream.read(buffer, 0, limit);
|
||||
if ( nRead == -1 ) {
|
||||
break;
|
||||
}
|
||||
@@ -34,13 +35,10 @@ public final class GFileUtilityMethods {
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
outputStream.close();
|
||||
}
|
||||
return tempOutputFile;
|
||||
}
|
||||
|
||||
public final static File writeTemporaryFile( byte [] bytes, String prefix ) throws IOException {
|
||||
public final static File writeTemporaryFile(byte[] bytes, String prefix) throws IOException {
|
||||
if ( prefix == null ) {
|
||||
prefix = GHIDRA_FILE_SYSTEM_PREFIX;
|
||||
}
|
||||
@@ -51,13 +49,11 @@ public final class GFileUtilityMethods {
|
||||
}
|
||||
File tempFile = File.createTempFile( prefix , GHIDRA_FILE_SYSTEM_SUFFIX );
|
||||
tempFile.deleteOnExit();
|
||||
OutputStream tempFileOut = new FileOutputStream( tempFile );
|
||||
try {
|
||||
|
||||
try (OutputStream tempFileOut = new FileOutputStream(tempFile)) {
|
||||
tempFileOut.write( bytes );
|
||||
}
|
||||
finally {
|
||||
tempFileOut.close();
|
||||
}
|
||||
|
||||
return tempFile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package mobiledevices.dmg.reader;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import org.catacombae.dmgextractor.encodings.encrypted.ReadableCEncryptedEncodingStream;
|
||||
import org.catacombae.hfsexplorer.FileSystemRecognizer;
|
||||
@@ -27,7 +28,6 @@ import mobiledevices.dmg.decmpfs.DecmpfsCompressionTypes;
|
||||
import mobiledevices.dmg.decmpfs.DecmpfsHeader;
|
||||
import mobiledevices.dmg.ghidra.*;
|
||||
import mobiledevices.dmg.hfsplus.AttributesFileParser;
|
||||
import mobiledevices.dmg.zlib.ZLIB;
|
||||
|
||||
public class DmgFileReader implements Closeable {
|
||||
private final static GDataConverter ledc = new GDataConverterLE();
|
||||
@@ -191,24 +191,16 @@ public class DmgFileReader implements Closeable {
|
||||
if ( decmpfsHeader.getCompressionType() == DecmpfsCompressionTypes.CMP_Type3 ) {
|
||||
|
||||
if ( decmpfsHeader.getAttrBytes()[ 0 ] == -1 ) {
|
||||
return new ByteArrayInputStream( decmpfsHeader.getAttrBytes(), 1, decmpfsHeader.getAttrBytes().length - 1 );
|
||||
return new ByteArrayInputStream(decmpfsHeader.getAttrBytes(), 1,
|
||||
decmpfsHeader.getAttrBytes().length - 1);
|
||||
}
|
||||
|
||||
ZLIB zlib = new ZLIB();
|
||||
|
||||
InputStream inputStream =
|
||||
new ByteArrayInputStream(decmpfsHeader.getAttrBytes());
|
||||
|
||||
ByteArrayOutputStream uncompressedBytes =
|
||||
zlib.decompress(inputStream, (int) decmpfsHeader.getUncompressedSize());
|
||||
|
||||
File tempDecompressedFile = GFileUtilityMethods.writeTemporaryFile(
|
||||
uncompressedBytes.toByteArray(), entry.getName());
|
||||
return new FileInputStream(tempDecompressedFile);
|
||||
return new RestrictedInflaterInputStream(decmpfsHeader.getAttrBytes(),
|
||||
(int) decmpfsHeader.getUncompressedSize());
|
||||
}
|
||||
else if ( decmpfsHeader.getCompressionType() == DecmpfsCompressionTypes.CMP_Type4 ) {
|
||||
|
||||
return decompressResourceFork( entry, resourceForkStream, (int)decmpfsHeader.getUncompressedSize() );
|
||||
return decompressResourceFork(entry, resourceForkStream,
|
||||
(int) decmpfsHeader.getUncompressedSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,34 +215,102 @@ public class DmgFileReader implements Closeable {
|
||||
System.err.println(
|
||||
"dmg resource fork for " + entry.getName() + ": " + tempFile.getAbsolutePath());
|
||||
|
||||
InputStream input = new FileInputStream( tempFile );
|
||||
// Copy compressed portion of tempFile to tempCompressedFile
|
||||
File tempCompressedFile;
|
||||
try (InputStream input = new FileInputStream(tempFile)) {
|
||||
|
||||
for ( int i = 0 ; i < 0x100 ; ++i ) {
|
||||
input.read();
|
||||
for (int i = 0; i < 0x100; ++i) {
|
||||
input.read();
|
||||
}
|
||||
|
||||
byte[] sizeBytes = new byte[4];
|
||||
input.read(sizeBytes);
|
||||
int size = sizeBytes[0] == 0 ? bedc.getInt(sizeBytes) : ledc.getInt(sizeBytes);
|
||||
|
||||
byte[] flagsBytes = new byte[4];
|
||||
input.read(flagsBytes);
|
||||
|
||||
byte[] startDistanceBytes = new byte[4];
|
||||
input.read(startDistanceBytes);
|
||||
int startDistance = ledc.getInt(startDistanceBytes);
|
||||
|
||||
input.skip(startDistance - 8);//skip to the start of the zlib compressed file
|
||||
|
||||
tempCompressedFile =
|
||||
GFileUtilityMethods.writeTemporaryFile(input, size - startDistance);
|
||||
}
|
||||
finally {
|
||||
tempFile.delete();
|
||||
}
|
||||
|
||||
byte [] sizeBytes = new byte[ 4 ];
|
||||
input.read( sizeBytes );
|
||||
int size = sizeBytes[ 0 ] == 0 ?
|
||||
bedc.getInt( sizeBytes ) :
|
||||
ledc.getInt( sizeBytes );
|
||||
return new RestrictedInflaterInputStream(tempCompressedFile, expectedLength);
|
||||
}
|
||||
|
||||
byte [] flagsBytes = new byte[ 4 ];
|
||||
input.read( flagsBytes );
|
||||
private class RestrictedInflaterInputStream extends InflaterInputStream {
|
||||
|
||||
byte [] startDistanceBytes = new byte[ 4 ];
|
||||
input.read( startDistanceBytes );
|
||||
int startDistance = ledc.getInt( startDistanceBytes );
|
||||
private File tempCompressedFile;
|
||||
private int readLimit;
|
||||
private int readCount = 0;
|
||||
|
||||
input.skip( startDistance - 8 );//skip to the start of the zlib compressed file
|
||||
/**
|
||||
* Creates a new Inflater input stream with a default decompressor and buffer size.
|
||||
* NOTE: The default Inflater instance will be ended when this stream closes.
|
||||
* @param tempCompressedFile the temporary file containing compressed data. File will be
|
||||
* removed when this stream is closed.
|
||||
* @param readLimit maximum data read count. Exceeding this limit will result in an
|
||||
* IOException.
|
||||
* @throws FileNotFoundException if tempCompressedFile does not exist
|
||||
*/
|
||||
RestrictedInflaterInputStream(File tempCompressedFile, int readLimit)
|
||||
throws FileNotFoundException {
|
||||
super(new FileInputStream(tempCompressedFile));
|
||||
this.tempCompressedFile = tempCompressedFile;
|
||||
this.readLimit = readLimit;
|
||||
}
|
||||
|
||||
File tempCompressedFile = GFileUtilityMethods.writeTemporaryFile( input, size - startDistance );
|
||||
InputStream inputStream = new FileInputStream( tempCompressedFile );
|
||||
/**
|
||||
* Creates a new Inflater input stream with a default decompressor and buffer size.
|
||||
* NOTE: The default Inflater instance will be ended when this stream closes.
|
||||
* @param compressedData the byte array containing compressed data.
|
||||
* @param readLimit maximum data read count. Exceeding this limit will result in an
|
||||
* IOException.
|
||||
*/
|
||||
RestrictedInflaterInputStream(byte[] compressedData, int readLimit) {
|
||||
super(new ByteArrayInputStream(compressedData));
|
||||
this.tempCompressedFile = null;
|
||||
this.readLimit = readLimit;
|
||||
}
|
||||
|
||||
ZLIB zlib = new ZLIB( );
|
||||
ByteArrayOutputStream uncompressedByteStream = zlib.decompress( inputStream, expectedLength );
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
}
|
||||
finally {
|
||||
// Cleanup temporary file input stream and remove file
|
||||
in.close();
|
||||
if (tempCompressedFile != null) {
|
||||
tempCompressedFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ByteArrayInputStream( uncompressedByteStream.toByteArray() );
|
||||
@Override
|
||||
public int read(byte[] b, int off, int length) throws IOException {
|
||||
if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (readCount >= readLimit) {
|
||||
throw new IOException("Decompression limit exceeded: " + readLimit);
|
||||
}
|
||||
// Limit read length to avoid exceeding readLimit
|
||||
int limit = Math.min(readLimit - readCount, length);
|
||||
int count = super.read(b, off, limit);
|
||||
if (count > 0) {
|
||||
readCount += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getInfo( String path ) {
|
||||
|
||||
@@ -128,6 +128,8 @@ public class DmgServer {
|
||||
|
||||
sendResponse(temporaryFile.getAbsolutePath());
|
||||
|
||||
// TODO: When can we remove temporaryFile
|
||||
|
||||
if (expectedFileLength != temporaryFile.length()) {
|
||||
log("file sizes do not match!");
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.zlib;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO make this more memory efficient!!
|
||||
*
|
||||
*/
|
||||
public class ZLIB {
|
||||
|
||||
public ZLIB() {
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream decompress( InputStream compressedIn, int expectedDecompressedLength ) throws IOException {
|
||||
return decompress( compressedIn, expectedDecompressedLength, false );
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream decompress( InputStream compressedIn, int expectedDecompressedLength, boolean noWrap ) throws IOException {
|
||||
|
||||
byte [] compressedBytes = convertInputStreamToByteArray( compressedIn );
|
||||
|
||||
ByteArrayOutputStream decompressedBOS = new ByteArrayOutputStream();
|
||||
|
||||
byte [] tempDecompressedBytes = new byte[ 0x10000 ];
|
||||
|
||||
int totalDecompressed = 0;
|
||||
int offset = 0;
|
||||
|
||||
try {
|
||||
while ( offset < compressedBytes.length && totalDecompressed < expectedDecompressedLength ) {
|
||||
|
||||
if ( !noWrap && compressedBytes [ offset ] != 0x78 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
Inflater inflater = new Inflater( noWrap );
|
||||
|
||||
inflater.setInput( compressedBytes, offset, compressedBytes.length - offset );
|
||||
|
||||
int nDecompressed = inflater.inflate( tempDecompressedBytes );
|
||||
|
||||
if ( nDecompressed == 0 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
totalDecompressed += nDecompressed;
|
||||
|
||||
decompressedBOS.write( tempDecompressedBytes, 0, nDecompressed );
|
||||
|
||||
offset += inflater.getTotalIn();//increment total compressed bytes consumed
|
||||
}
|
||||
}
|
||||
catch ( DataFormatException e ) {
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
|
||||
return decompressedBOS;
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream compress(byte[] decompressedBytes) {
|
||||
return compress( false, decompressedBytes );
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream compress(boolean noWrap, byte[] decompressedBytes) {
|
||||
ByteArrayOutputStream compressedBOS = new ByteArrayOutputStream();
|
||||
|
||||
byte [] tempBuffer = new byte[ 0x10000 ];
|
||||
|
||||
int offset = 0;
|
||||
|
||||
while ( offset < decompressedBytes.length ) {
|
||||
|
||||
Deflater deflater = new Deflater( 0, noWrap );
|
||||
|
||||
deflater.setInput( decompressedBytes, offset, decompressedBytes.length - offset );
|
||||
|
||||
deflater.finish();
|
||||
|
||||
if ( deflater.needsInput() ) {
|
||||
System.err.println( "needs input??" );
|
||||
}
|
||||
|
||||
int nDeflated = deflater.deflate( tempBuffer );
|
||||
|
||||
if ( nDeflated == 0 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
compressedBOS.write( tempBuffer, 0, nDeflated );
|
||||
|
||||
offset += deflater.getTotalIn();
|
||||
}
|
||||
|
||||
return compressedBOS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the contents of an input stream to a byte array
|
||||
* @param compressedIn
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private byte [] convertInputStreamToByteArray( InputStream compressedIn ) throws IOException {
|
||||
byte [] bytes = new byte[ 8096 ];
|
||||
ByteArrayOutputStream compressedBOS = new ByteArrayOutputStream();
|
||||
while ( true ) {
|
||||
int nRead = compressedIn.read( bytes );
|
||||
if ( nRead == -1 ) {
|
||||
break;
|
||||
}
|
||||
compressedBOS.write( bytes, 0, nRead );
|
||||
}
|
||||
return compressedBOS.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -15,12 +15,13 @@
|
||||
*/
|
||||
package ghidra.file.formats.zlib;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.*;
|
||||
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO make this more memory efficient!!
|
||||
@@ -57,12 +58,14 @@ public class ZLIB {
|
||||
* Note: When using the 'noWrap' option it is also necessary to provide an extra "dummy" byte as input.
|
||||
* This is required by the ZLIB native library in order to support certain optimizations.
|
||||
* @param compressedIn an input stream containing the compressed data
|
||||
* @param expectedDecompressedLength the expected length of the decompressed data
|
||||
* @param decompressedSizeLimit the maximum length of the decompressed data. Actual decompressed
|
||||
* data within returned ByteArrayOutputStream may still exceed this limit.
|
||||
* @param noWrap if true then support GZIP compatible compression
|
||||
* @return an output stream containing the decompressed data
|
||||
* @throws IOException
|
||||
* @throws IOException if IO error occurs while reading compressedIn stream
|
||||
*/
|
||||
public ByteArrayOutputStream decompress( InputStream compressedIn, int expectedDecompressedLength, boolean noWrap ) throws IOException {
|
||||
public ByteArrayOutputStream decompress(InputStream compressedIn, int decompressedSizeLimit,
|
||||
boolean noWrap) throws IOException {
|
||||
|
||||
byte [] compressedBytes = convertInputStreamToByteArray( compressedIn );
|
||||
|
||||
@@ -70,37 +73,25 @@ public class ZLIB {
|
||||
|
||||
byte [] tempDecompressedBytes = new byte[ 0x10000 ];
|
||||
|
||||
int totalDecompressed = 0;
|
||||
int offset = 0;
|
||||
|
||||
try {
|
||||
while ( offset < compressedBytes.length && totalDecompressed < expectedDecompressedLength ) {
|
||||
|
||||
if ( !noWrap && compressedBytes [ offset ] != 0x78 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
Inflater inflater = new Inflater( noWrap );
|
||||
|
||||
inflater.setInput( compressedBytes, offset, compressedBytes.length - offset );
|
||||
|
||||
int nDecompressed = inflater.inflate( tempDecompressedBytes );
|
||||
|
||||
if ( nDecompressed == 0 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
totalDecompressed += nDecompressed;
|
||||
|
||||
decompressedBOS.write( tempDecompressedBytes, 0, nDecompressed );
|
||||
|
||||
offset += inflater.getTotalIn();//increment total compressed bytes consumed
|
||||
}
|
||||
}
|
||||
catch ( DataFormatException e ) {
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
|
||||
Inflater inflater = new Inflater(noWrap);
|
||||
try {
|
||||
inflater.setInput(compressedBytes);
|
||||
while (!inflater.finished()) {
|
||||
int nDecompressed = inflater.inflate(tempDecompressedBytes);
|
||||
decompressedBOS.write(tempDecompressedBytes, 0, nDecompressed);
|
||||
if (decompressedBOS.size() > decompressedSizeLimit) {
|
||||
Msg.warn(this, "ZLIB decompress exceeded specified limit (" +
|
||||
decompressedBOS.size() + " > " + decompressedSizeLimit + ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (DataFormatException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
finally {
|
||||
inflater.end();
|
||||
}
|
||||
return decompressedBOS;
|
||||
}
|
||||
|
||||
@@ -129,9 +120,8 @@ public class ZLIB {
|
||||
* to support the compression format used in both GZIP and PKZIP.
|
||||
* @param decompressedBytes the decompressed bytes
|
||||
* @return an output stream containing the compressed data
|
||||
* @throws IOException
|
||||
*/
|
||||
public ByteArrayOutputStream compress( byte [] decompressedBytes ) throws IOException {
|
||||
public ByteArrayOutputStream compress(byte[] decompressedBytes) {
|
||||
return compress( false, decompressedBytes );
|
||||
}
|
||||
|
||||
@@ -142,38 +132,24 @@ public class ZLIB {
|
||||
* @param noWrap if true then use GZIP compatible compression
|
||||
* @param decompressedBytes the decompressed bytes
|
||||
* @return an output stream containing the compressed data
|
||||
* @throws IOException
|
||||
*/
|
||||
public ByteArrayOutputStream compress( boolean noWrap, byte [] decompressedBytes ) throws IOException {
|
||||
public ByteArrayOutputStream compress(boolean noWrap, byte[] decompressedBytes) {
|
||||
ByteArrayOutputStream compressedBOS = new ByteArrayOutputStream();
|
||||
|
||||
byte [] tempBuffer = new byte[ 0x10000 ];
|
||||
|
||||
int offset = 0;
|
||||
|
||||
while ( offset < decompressedBytes.length ) {
|
||||
|
||||
Deflater deflater = new Deflater( 0, noWrap );
|
||||
|
||||
deflater.setInput( decompressedBytes, offset, decompressedBytes.length - offset );
|
||||
|
||||
deflater.finish();
|
||||
|
||||
if ( deflater.needsInput() ) {
|
||||
System.out.println( "needs input??" );
|
||||
}
|
||||
|
||||
int nDeflated = deflater.deflate( tempBuffer );
|
||||
|
||||
if ( nDeflated == 0 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
compressedBOS.write( tempBuffer, 0, nDeflated );
|
||||
|
||||
offset += deflater.getTotalIn();
|
||||
}
|
||||
|
||||
Deflater deflater = new Deflater(0, noWrap);
|
||||
try {
|
||||
deflater.setInput(decompressedBytes);
|
||||
deflater.finish();
|
||||
while (!deflater.finished()) {
|
||||
int nDeflated = deflater.deflate(tempBuffer);
|
||||
compressedBOS.write(tempBuffer, 0, nDeflated);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
deflater.end();
|
||||
}
|
||||
return compressedBOS;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/* ###
|
||||
* 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.file.formats.zlib;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGTest;
|
||||
|
||||
public class ZLIBTest extends AbstractGTest {
|
||||
|
||||
@Test
|
||||
public void testCompressDecompress() throws Exception {
|
||||
|
||||
ZLIB zlib = new ZLIB();
|
||||
|
||||
byte[] data = new byte[100000];
|
||||
SecureRandom random = new SecureRandom();
|
||||
random.nextBytes(data);
|
||||
|
||||
// TODO: ZLIB API should be revised to avoid senseless exposure of ByteArrayOutputStream
|
||||
|
||||
try (ByteArrayOutputStream compress = zlib.compress(data)) {
|
||||
byte[] compressedBytes = compress.toByteArray();
|
||||
new ByteArrayInputStream(compressedBytes);
|
||||
|
||||
ByteArrayOutputStream decompressOut =
|
||||
zlib.decompress(new ByteArrayInputStream(compressedBytes), data.length);
|
||||
byte[] decompressedBytes = decompressOut.toByteArray();
|
||||
|
||||
assertTrue("Zlib round-trip failed", Arrays.equals(data, decompressedBytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* ###
|
||||
* 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.stream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
|
||||
/**
|
||||
* {@link RemoteDeflaterOutputStream} extends {@link DeflaterOutputStream} and
|
||||
* allows a non-default {@link Deflater} to "end" properly when this output stream
|
||||
* is closed.
|
||||
*/
|
||||
class RemoteDeflaterOutputStream extends DeflaterOutputStream {
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
/**
|
||||
* Construct a deflater output stream using a specified compression level.
|
||||
* @param out output stream providing the uncompressed data
|
||||
* @param deflaterLevel {@link Deflater} compression level
|
||||
*/
|
||||
RemoteDeflaterOutputStream(OutputStream out, int deflaterLevel) {
|
||||
super(out, new Deflater(deflaterLevel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (!closed) {
|
||||
try {
|
||||
super.close();
|
||||
}
|
||||
finally {
|
||||
closed = true;
|
||||
def.end(); // Must end on close since we did not use default Deflater
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -18,7 +18,8 @@ package ghidra.server.stream;
|
||||
import java.io.*;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.zip.*;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import db.buffers.*;
|
||||
|
||||
@@ -56,13 +57,17 @@ public class RemoteInputBlockStreamHandle extends RemoteBlockStreamHandle<InputB
|
||||
|
||||
private final Socket socket;
|
||||
private final InputStream in;
|
||||
|
||||
|
||||
private int blocksRemaining = getBlockCount();
|
||||
|
||||
ClientInputBlockStream(Socket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
in = compressed ? new InflaterInputStream(socket.getInputStream())
|
||||
: socket.getInputStream();
|
||||
if (compressed) {
|
||||
// Uses default Inflater with nowrap=false
|
||||
in = new InflaterInputStream(socket.getInputStream());
|
||||
} else {
|
||||
in = socket.getInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,7 +138,7 @@ public class RemoteInputBlockStreamHandle extends RemoteBlockStreamHandle<InputB
|
||||
socket.setSendBufferSize(getPreferredBufferSize());
|
||||
|
||||
InputBlockStream inputBlockStream = (InputBlockStream) blockStream;
|
||||
try (OutputStream out = socket.getOutputStream()) {
|
||||
try (OutputStream out = getBlockInputStream(socket)) {
|
||||
|
||||
copyBlockData(inputBlockStream, out);
|
||||
|
||||
@@ -150,13 +155,17 @@ public class RemoteInputBlockStreamHandle extends RemoteBlockStreamHandle<InputB
|
||||
}
|
||||
}
|
||||
|
||||
private OutputStream getBlockInputStream(Socket socket) throws IOException {
|
||||
OutputStream out = socket.getOutputStream();
|
||||
if (compressed) {
|
||||
out = new RemoteDeflaterOutputStream(out, Deflater.BEST_SPEED);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private void copyBlockData(InputBlockStream inputBlockStream, OutputStream out)
|
||||
throws IOException {
|
||||
|
||||
if (compressed) {
|
||||
out = new DeflaterOutputStream(out, new Deflater(Deflater.BEST_SPEED));
|
||||
}
|
||||
|
||||
int blocksRemaining = getBlockCount();
|
||||
|
||||
BufferFileBlock block;
|
||||
@@ -167,11 +176,6 @@ public class RemoteInputBlockStreamHandle extends RemoteBlockStreamHandle<InputB
|
||||
out.write(block.toBytes());
|
||||
--blocksRemaining;
|
||||
}
|
||||
|
||||
// done with compressed stream, force compressed data to flush
|
||||
if (out instanceof DeflaterOutputStream) {
|
||||
((DeflaterOutputStream) out).finish();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -60,10 +60,11 @@ public class RemoteOutputBlockStreamHandle extends RemoteBlockStreamHandle<Outpu
|
||||
|
||||
ClientOutputBlockStream(Socket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
out = compressed
|
||||
? new DeflaterOutputStream(socket.getOutputStream(),
|
||||
new Deflater(Deflater.BEST_SPEED))
|
||||
: socket.getOutputStream();
|
||||
if (compressed) {
|
||||
out = new RemoteDeflaterOutputStream(socket.getOutputStream(), Deflater.BEST_SPEED);
|
||||
} else {
|
||||
out = socket.getOutputStream();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,7 +124,7 @@ public class RemoteOutputBlockStreamHandle extends RemoteBlockStreamHandle<Outpu
|
||||
socket.setReceiveBufferSize(getPreferredBufferSize());
|
||||
|
||||
OutputBlockStream outputBlockStream = (OutputBlockStream) blockStream;
|
||||
try (InputStream in = socket.getInputStream()) {
|
||||
try (InputStream in = getBlockInputStream(socket)) {
|
||||
|
||||
copyBlockData(outputBlockStream, in);
|
||||
|
||||
@@ -148,12 +149,16 @@ public class RemoteOutputBlockStreamHandle extends RemoteBlockStreamHandle<Outpu
|
||||
|
||||
}
|
||||
|
||||
private void copyBlockData(OutputBlockStream outputBlockStream, InputStream in)
|
||||
throws IOException, EOFException {
|
||||
|
||||
private InputStream getBlockInputStream(Socket socket) throws IOException {
|
||||
InputStream in = socket.getInputStream();
|
||||
if (compressed) {
|
||||
in = new InflaterInputStream(in);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
private void copyBlockData(OutputBlockStream outputBlockStream, InputStream in)
|
||||
throws IOException, EOFException {
|
||||
|
||||
int blocksRemaining = getBlockCount();
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -330,23 +330,29 @@ public class DataBuffer implements Buffer, Externalizable {
|
||||
*/
|
||||
private static int deflateData(byte[] data, byte[] compressedData) {
|
||||
|
||||
Deflater deflate = new Deflater(Deflater.BEST_COMPRESSION, true);
|
||||
deflate.setStrategy(Deflater.HUFFMAN_ONLY);
|
||||
deflate.setInput(data, 0, data.length);
|
||||
deflate.finish();
|
||||
// Deflater must be consistent with inflateData nowrap option and should
|
||||
// not be changed since client/server must match.
|
||||
// NOTE: compression mode may be adjusted to optimize performance
|
||||
Deflater deflate = new Deflater(Deflater.BEST_SPEED, true);
|
||||
try {
|
||||
deflate.setInput(data, 0, data.length);
|
||||
deflate.finish();
|
||||
|
||||
int compressedDataOffset = 0;
|
||||
|
||||
while (!deflate.finished() && compressedDataOffset < compressedData.length) {
|
||||
compressedDataOffset += deflate.deflate(compressedData, compressedDataOffset,
|
||||
compressedData.length - compressedDataOffset, Deflater.SYNC_FLUSH);
|
||||
int compressedDataOffset = 0;
|
||||
|
||||
while (!deflate.finished() && compressedDataOffset < compressedData.length) {
|
||||
compressedDataOffset += deflate.deflate(compressedData, compressedDataOffset,
|
||||
compressedData.length - compressedDataOffset);
|
||||
}
|
||||
|
||||
if (!deflate.finished()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return compressedDataOffset;
|
||||
} finally {
|
||||
deflate.end(); // get rid of any native memory rather than waiting for GC
|
||||
}
|
||||
|
||||
if (!deflate.finished()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return compressedDataOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -412,10 +418,11 @@ public class DataBuffer implements Buffer, Externalizable {
|
||||
*/
|
||||
private static void inflateData(byte[] compressedData, byte[] data) throws IOException {
|
||||
|
||||
// Inflater must be consistent with deflateData nowrap option and should
|
||||
// not be changed since client/server must match.
|
||||
Inflater inflater = new Inflater(true);
|
||||
inflater.setInput(compressedData, 0, compressedData.length);
|
||||
|
||||
try {
|
||||
inflater.setInput(compressedData);
|
||||
int off = 0;
|
||||
while (!inflater.finished() && off < data.length) {
|
||||
off += inflater.inflate(data, off, data.length - off);
|
||||
@@ -430,6 +437,9 @@ public class DataBuffer implements Buffer, Externalizable {
|
||||
catch (DataFormatException e) {
|
||||
throw new IOException("DataBuffer inflation failed", e);
|
||||
}
|
||||
finally {
|
||||
inflater.end(); // get rid of any native memory rather than waiting for GC
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -248,21 +248,19 @@ public class SlaFormat {
|
||||
* @throws IOException if the header is invalid or there are problems reading the file
|
||||
*/
|
||||
public static PackedDecode buildDecoder(ResourceFile sleighFile) throws IOException {
|
||||
InputStream stream = sleighFile.getInputStream();
|
||||
try {
|
||||
try (InputStream stream = sleighFile.getInputStream()) {
|
||||
if (!isSlaFormat(stream)) {
|
||||
throw new IOException("Missing SLA format header");
|
||||
}
|
||||
InflaterInputStream inflaterStream = new InflaterInputStream(stream);
|
||||
PackedDecode decoder = new PackedDecode();
|
||||
decoder.open(MAX_FILE_SIZE, ".sla file loader");
|
||||
decoder.ingestStream(inflaterStream);
|
||||
|
||||
try (InflaterInputStream inflaterStream = new InflaterInputStream(stream)) {
|
||||
decoder.ingestStream(inflaterStream);
|
||||
}
|
||||
|
||||
decoder.endIngest();
|
||||
inflaterStream.close();
|
||||
return decoder;
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user