Make sure all streams are really closed via try/finally block. Added a "closeAll" function in StreamUtils which helps to close a list of closeables

This commit is contained in:
Maschell 2019-04-19 11:41:12 +02:00
parent fccd8f8bf4
commit 9f6f9aaabe
6 changed files with 299 additions and 266 deletions

View File

@ -28,6 +28,7 @@ import java.util.Arrays;
import de.mas.wiiu.jnus.implementations.wud.WUDImage; import de.mas.wiiu.jnus.implementations.wud.WUDImage;
import de.mas.wiiu.jnus.utils.PipedInputStreamWithException; import de.mas.wiiu.jnus.utils.PipedInputStreamWithException;
import de.mas.wiiu.jnus.utils.StreamUtils;
import de.mas.wiiu.jnus.utils.cryptography.AESDecryption; import de.mas.wiiu.jnus.utils.cryptography.AESDecryption;
import lombok.Getter; import lombok.Getter;
import lombok.extern.java.Log; import lombok.extern.java.Log;
@ -114,41 +115,44 @@ public abstract class WUDDiscReader {
final int BLOCK_SIZE = 0x10000; final int BLOCK_SIZE = 0x10000;
long totalread = 0; long totalread = 0;
do { try {
long blockNumber = (usedFileOffset / BLOCK_SIZE); do {
long blockOffset = (usedFileOffset % BLOCK_SIZE); long blockNumber = (usedFileOffset / BLOCK_SIZE);
long blockOffset = (usedFileOffset % BLOCK_SIZE);
readOffset = clusterOffset + (blockNumber * BLOCK_SIZE); readOffset = clusterOffset + (blockNumber * BLOCK_SIZE);
// (long)WiiUDisc.WIIU_DECRYPTED_AREA_OFFSET + volumeOffset + clusterOffset + (blockStructure.getBlockNumber() * 0x8000); // (long)WiiUDisc.WIIU_DECRYPTED_AREA_OFFSET + volumeOffset + clusterOffset + (blockStructure.getBlockNumber() * 0x8000);
if (!useFixedIV) { if (!useFixedIV) {
ByteBuffer byteBuffer = ByteBuffer.allocate(0x10); ByteBuffer byteBuffer = ByteBuffer.allocate(0x10);
byteBuffer.position(0x08); byteBuffer.position(0x08);
usedIV = byteBuffer.putLong(usedFileOffset >> 16).array(); usedIV = byteBuffer.putLong(usedFileOffset >> 16).array();
}
buffer = readDecryptedChunk(readOffset, key, usedIV);
maxCopySize = BLOCK_SIZE - blockOffset;
copySize = (usedSize > maxCopySize) ? maxCopySize : usedSize;
try {
outputStream.write(Arrays.copyOfRange(buffer, (int) blockOffset, (int) (blockOffset + copySize)));
} catch (IOException e) {
if (e.getMessage().equals("Pipe closed")) {
break;
} else {
throw e;
} }
}
totalread += copySize; buffer = readDecryptedChunk(readOffset, key, usedIV);
maxCopySize = BLOCK_SIZE - blockOffset;
copySize = (usedSize > maxCopySize) ? maxCopySize : usedSize;
// update counters try {
usedSize -= copySize; outputStream.write(Arrays.copyOfRange(buffer, (int) blockOffset, (int) (blockOffset + copySize)));
usedFileOffset += copySize; } catch (IOException e) {
} while (totalread < size); if (e.getMessage().equals("Pipe closed")) {
break;
} else {
throw e;
}
}
totalread += copySize;
// update counters
usedSize -= copySize;
usedFileOffset += copySize;
} while (totalread < size);
} finally {
StreamUtils.closeAll(outputStream);
}
outputStream.close();
return totalread >= size; return totalread >= size;
} }

View File

@ -23,6 +23,7 @@ import java.util.Arrays;
import de.mas.wiiu.jnus.implementations.wud.WUDImage; import de.mas.wiiu.jnus.implementations.wud.WUDImage;
import de.mas.wiiu.jnus.implementations.wud.WUDImageCompressedInfo; import de.mas.wiiu.jnus.implementations.wud.WUDImageCompressedInfo;
import de.mas.wiiu.jnus.utils.StreamUtils;
public class WUDDiscReaderCompressed extends WUDDiscReader { public class WUDDiscReaderCompressed extends WUDDiscReader {
@ -55,40 +56,40 @@ public class WUDDiscReaderCompressed extends WUDDiscReader {
byte[] buffer = new byte[bufferSize]; byte[] buffer = new byte[bufferSize];
RandomAccessFile input = getRandomAccessFileStream(); RandomAccessFile input = getRandomAccessFileStream();
synchronized (input) { try {
while (usedSize > 0) { synchronized (input) {
long sectorOffset = (usedOffset % info.getSectorSize()); while (usedSize > 0) {
long remainingSectorBytes = info.getSectorSize() - sectorOffset; long sectorOffset = (usedOffset % info.getSectorSize());
long sectorIndex = (usedOffset / info.getSectorSize()); long remainingSectorBytes = info.getSectorSize() - sectorOffset;
int bytesToRead = (int) ((remainingSectorBytes < usedSize) ? remainingSectorBytes : usedSize); // read only up to the end of the current sector long sectorIndex = (usedOffset / info.getSectorSize());
// look up real sector index int bytesToRead = (int) ((remainingSectorBytes < usedSize) ? remainingSectorBytes : usedSize); // read only up to the end of the current
long realSectorIndex = info.getSectorIndex((int) sectorIndex); // sector
long offset2 = info.getOffsetSectorArray() + realSectorIndex * info.getSectorSize() + sectorOffset; // look up real sector index
long realSectorIndex = info.getSectorIndex((int) sectorIndex);
long offset2 = info.getOffsetSectorArray() + realSectorIndex * info.getSectorSize() + sectorOffset;
input.seek(offset2); input.seek(offset2);
int read = input.read(buffer); int read = input.read(buffer);
if (read < 0) { if (read < 0) {
break;
}
try {
out.write(Arrays.copyOfRange(buffer, 0, bytesToRead));
} catch (IOException e) {
if (e.getMessage().equals("Pipe closed")) {
break; break;
} else {
input.close();
throw e;
} }
} try {
out.write(Arrays.copyOfRange(buffer, 0, bytesToRead));
} catch (IOException e) {
if (e.getMessage().equals("Pipe closed")) {
break;
} else {
throw e;
}
}
usedSize -= bytesToRead; usedSize -= bytesToRead;
usedOffset += bytesToRead; usedOffset += bytesToRead;
}
} }
input.close(); } finally {
} StreamUtils.closeAll(input, out);
synchronized (out) {
out.close();
} }
return usedSize == 0; return usedSize == 0;
} }

View File

@ -53,8 +53,11 @@ public final class FileUtils {
*/ */
public static boolean saveByteArrayToFile(@NonNull File output, byte[] data) throws IOException { public static boolean saveByteArrayToFile(@NonNull File output, byte[] data) throws IOException {
FileOutputStream out = new FileOutputStream(output); FileOutputStream out = new FileOutputStream(output);
out.write(data); try {
out.close(); out.write(data);
} finally {
out.close();
}
return true; return true;
} }
@ -81,12 +84,14 @@ public final class FileUtils {
tempFile.createNewFile(); tempFile.createNewFile();
RandomAccessFile outStream = new RandomAccessFile(tempFilePath, "rw"); RandomAccessFile outStream = new RandomAccessFile(tempFilePath, "rw");
outStream.setLength(filesize); try {
outStream.seek(0L); outStream.setLength(filesize);
outStream.seek(0L);
action.apply(new RandomFileOutputStream(outStream)); action.apply(new RandomFileOutputStream(outStream));
} finally {
outStream.close(); outStream.close();
}
// Rename temp file. // Rename temp file.
if (outputFile.exists()) { if (outputFile.exists()) {

View File

@ -120,23 +120,25 @@ public final class HashUtil {
int inBlockBufferRead = 0; int inBlockBufferRead = 0;
byte[] blockBuffer = new byte[bufferSize]; byte[] blockBuffer = new byte[bufferSize];
ByteArrayBuffer overflow = new ByteArrayBuffer(bufferSize); ByteArrayBuffer overflow = new ByteArrayBuffer(bufferSize);
do { try {
inBlockBufferRead = StreamUtils.getChunkFromStream(in, blockBuffer, overflow, bufferSize); do {
inBlockBufferRead = StreamUtils.getChunkFromStream(in, blockBuffer, overflow, bufferSize);
if (inBlockBufferRead <= 0) break; if (inBlockBufferRead <= 0) break;
digest.update(blockBuffer, 0, inBlockBufferRead); digest.update(blockBuffer, 0, inBlockBufferRead);
cur_position += inBlockBufferRead; cur_position += inBlockBufferRead;
} while (cur_position < target_size); } while (cur_position < target_size);
long missing_bytes = target_size - cur_position; long missing_bytes = target_size - cur_position;
if (missing_bytes > 0) { if (missing_bytes > 0) {
byte[] missing = new byte[(int) missing_bytes]; byte[] missing = new byte[(int) missing_bytes];
digest.update(missing, 0, (int) missing_bytes); digest.update(missing, 0, (int) missing_bytes);
}
} finally {
in.close();
} }
in.close();
return digest.digest(); return digest.digest();
} }

View File

@ -16,6 +16,7 @@
****************************************************************************/ ****************************************************************************/
package de.mas.wiiu.jnus.utils; package de.mas.wiiu.jnus.utils;
import java.io.Closeable;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -32,29 +33,40 @@ public final class StreamUtils {
// Utility class // Utility class
} }
/**
* Tries to read a given amount of bytes from a stream and return them as
* a byte array. Closes the inputs stream on success AND failure.
* @param in
* @param size
* @return
* @throws IOException
*/
public static byte[] getBytesFromStream(InputStream in, int size) throws IOException { public static byte[] getBytesFromStream(InputStream in, int size) throws IOException {
synchronized (in) { try {
byte[] result = new byte[size]; synchronized (in) {
byte[] buffer = null; byte[] result = new byte[size];
if (size < 0x8000) { byte[] buffer = null;
buffer = new byte[size]; if (size < 0x8000) {
} else { buffer = new byte[size];
buffer = new byte[0x8000]; } else {
} buffer = new byte[0x8000];
int toRead = size;
int curReadChunk = buffer.length;
do {
if (toRead < curReadChunk) {
curReadChunk = toRead;
} }
int read = in.read(buffer, 0, curReadChunk); int toRead = size;
StreamUtils.checkForException(in); int curReadChunk = buffer.length;
if (read < 0) break; do {
System.arraycopy(buffer, 0, result, size - toRead, read); if (toRead < curReadChunk) {
toRead -= read; curReadChunk = toRead;
} while (toRead > 0); }
in.close(); int read = in.read(buffer, 0, curReadChunk);
return result; StreamUtils.checkForException(in);
if (read < 0) break;
System.arraycopy(buffer, 0, result, size - toRead, read);
toRead -= read;
} while (toRead > 0);
return result;
}
} finally {
StreamUtils.closeAll(in);
} }
} }
@ -141,43 +153,44 @@ public final class StreamUtils {
int read = 0; int read = 0;
long totalRead = 0; long totalRead = 0;
long written = 0; long written = 0;
do {
read = inputStream.read(buffer);
StreamUtils.checkForException(inputStream);
if (read < 0) {
break;
}
totalRead += read;
if (totalRead > filesize) { try {
read = (int) (read - (totalRead - filesize)); do {
read = inputStream.read(buffer);
StreamUtils.checkForException(inputStream);
if (read < 0) {
break;
}
totalRead += read;
if (totalRead > filesize) {
read = (int) (read - (totalRead - filesize));
}
outputStream.write(buffer, 0, read);
written += read;
if (sha1 != null) {
sha1.update(buffer, 0, read);
}
} while (written < filesize);
if (sha1 != null && hash != null) {
long missingInHash = expectedSizeForHash - written;
if (missingInHash > 0) {
sha1.update(new byte[(int) missingInHash]);
}
byte[] calculated_hash = sha1.digest();
byte[] expected_hash = hash;
if (!Arrays.equals(calculated_hash, expected_hash)) {
throw new CheckSumWrongException("Hash doesn't match saves output stream.", calculated_hash, expected_hash);
}
} }
outputStream.write(buffer, 0, read); } finally {
written += read; StreamUtils.closeAll(inputStream, outputStream);
if (sha1 != null) {
sha1.update(buffer, 0, read);
}
} while (written < filesize);
if (sha1 != null && hash != null) {
long missingInHash = expectedSizeForHash - written;
if (missingInHash > 0) {
sha1.update(new byte[(int) missingInHash]);
}
byte[] calculated_hash = sha1.digest();
byte[] expected_hash = hash;
if (!Arrays.equals(calculated_hash, expected_hash)) {
outputStream.close();
inputStream.close();
throw new CheckSumWrongException("Hash doesn't match saves output stream.", calculated_hash, expected_hash);
}
} }
outputStream.close();
inputStream.close();
} }
} }
@ -206,4 +219,17 @@ public final class StreamUtils {
} }
} }
public static void closeAll(Closeable... stream) throws IOException {
IOException exception = null;
for (Closeable cur : stream) {
try {
cur.close();
} catch (IOException e) {
exception = e;
}
}
if (exception != null) {
throw exception;
}
}
} }

View File

@ -31,7 +31,6 @@ import de.mas.wiiu.jnus.entities.content.Content;
import de.mas.wiiu.jnus.utils.ByteArrayBuffer; import de.mas.wiiu.jnus.utils.ByteArrayBuffer;
import de.mas.wiiu.jnus.utils.CheckSumWrongException; import de.mas.wiiu.jnus.utils.CheckSumWrongException;
import de.mas.wiiu.jnus.utils.HashUtil; import de.mas.wiiu.jnus.utils.HashUtil;
import de.mas.wiiu.jnus.utils.PipedInputStreamWithException;
import de.mas.wiiu.jnus.utils.StreamUtils; import de.mas.wiiu.jnus.utils.StreamUtils;
import de.mas.wiiu.jnus.utils.Utils; import de.mas.wiiu.jnus.utils.Utils;
import lombok.extern.java.Log; import lombok.extern.java.Log;
@ -91,106 +90,105 @@ public class NUSDecryption extends AESDecryption {
int skipoffset = (int) (fileOffset % 0x8000); int skipoffset = (int) (fileOffset % 0x8000);
// If we are at the beginning of a block, but it's not the first one, try {
// we need to get the IV from the last 16 bytes of the previous block.
// while beeing paranoid to exactly read 16 bytes but not more. Reading more // If we are at the beginning of a block, but it's not the first one,
// would destroy our input stream. // we need to get the IV from the last 16 bytes of the previous block.
// The input stream has been prepared to start 16 bytes earlier on this case. // while beeing paranoid to exactly read 16 bytes but not more. Reading more
if (fileOffset >= 0x8000 && fileOffset % 0x8000 == 0) { // would destroy our input stream.
int toRead = 16; // The input stream has been prepared to start 16 bytes earlier on this case.
byte[] data = new byte[toRead]; if (fileOffset >= 0x8000 && fileOffset % 0x8000 == 0) {
int readTotal = 0; int toRead = 16;
while (readTotal < toRead) { byte[] data = new byte[toRead];
int res = inputStream.read(data, readTotal, toRead - readTotal); int readTotal = 0;
StreamUtils.checkForException(inputStream); while (readTotal < toRead) {
if (res < 0) { int res = inputStream.read(data, readTotal, toRead - readTotal);
// This should NEVER happen. StreamUtils.checkForException(inputStream);
throw new IOException(); if (res < 0) {
// This should NEVER happen.
throw new IOException();
}
readTotal += res;
} }
readTotal += res; IV = Arrays.copyOfRange(data, 0, toRead);
} }
IV = Arrays.copyOfRange(data, 0, toRead);
}
ByteArrayBuffer overflow = new ByteArrayBuffer(BLOCKSIZE); ByteArrayBuffer overflow = new ByteArrayBuffer(BLOCKSIZE);
// We can only decrypt multiples of 16. So we need to align it. // We can only decrypt multiples of 16. So we need to align it.
long toRead = Utils.align(filesize + 15, 16); long toRead = Utils.align(filesize + 15, 16);
do { do {
// In case we start on the middle of a block we need to consume the "garbage" and save the // In case we start on the middle of a block we need to consume the "garbage" and save the
// current IV. // current IV.
if (skipoffset > 0) { if (skipoffset > 0) {
int skippedBytes = StreamUtils.getChunkFromStream(inputStream, blockBuffer, overflow, skipoffset); int skippedBytes = StreamUtils.getChunkFromStream(inputStream, blockBuffer, overflow, skipoffset);
if (skippedBytes >= 16) { if (skippedBytes >= 16) {
IV = Arrays.copyOfRange(blockBuffer, skippedBytes - 16, skippedBytes); IV = Arrays.copyOfRange(blockBuffer, skippedBytes - 16, skippedBytes);
}
skipoffset = 0;
} }
skipoffset = 0;
}
int curReadSize = BLOCKSIZE; int curReadSize = BLOCKSIZE;
if (toRead < BLOCKSIZE) { if (toRead < BLOCKSIZE) {
curReadSize = (int) toRead; curReadSize = (int) toRead;
} }
inBlockBuffer = StreamUtils.getChunkFromStream(inputStream, blockBuffer, overflow, curReadSize); inBlockBuffer = StreamUtils.getChunkFromStream(inputStream, blockBuffer, overflow, curReadSize);
byte[] output = decryptFileChunk(blockBuffer, (int) Utils.align(inBlockBuffer, 16), IV); byte[] output = decryptFileChunk(blockBuffer, (int) Utils.align(inBlockBuffer, 16), IV);
if (inBlockBuffer == BLOCKSIZE) { if (inBlockBuffer == BLOCKSIZE) {
IV = Arrays.copyOfRange(blockBuffer, BLOCKSIZE - 16, BLOCKSIZE); IV = Arrays.copyOfRange(blockBuffer, BLOCKSIZE - 16, BLOCKSIZE);
} }
int toWrite = inBlockBuffer; int toWrite = inBlockBuffer;
if ((written + inBlockBuffer) > filesize) { if ((written + inBlockBuffer) > filesize) {
toWrite = (int) (filesize - written); toWrite = (int) (filesize - written);
} }
written += toWrite; written += toWrite;
toRead -= toWrite; toRead -= toWrite;
outputStream.write(output, 0, toWrite); outputStream.write(output, 0, toWrite);
if (sha1 != null && sha1fallback != null) {
sha1.update(output, 0, toWrite);
// In some cases it's using the hash of the whole .app file instead of the part
// that's been actually used.
long toFallback = inBlockBuffer;
if (writtenFallback + toFallback > expectedSizeForHash) {
toFallback = expectedSizeForHash - writtenFallback;
}
sha1fallback.update(output, 0, (int) toFallback);
writtenFallback += toFallback;
}
if (written >= filesize && h3hash == null) {
break;
}
} while (inBlockBuffer == BLOCKSIZE);
if (sha1 != null && sha1fallback != null) { if (sha1 != null && sha1fallback != null) {
sha1.update(output, 0, toWrite);
// In some cases it's using the hash of the whole .app file instead of the part long missingInHash = expectedSizeForHash - writtenFallback;
// that's been actually used. if (missingInHash > 0) {
long toFallback = inBlockBuffer; sha1fallback.update(new byte[(int) missingInHash]);
if (writtenFallback + toFallback > expectedSizeForHash) {
toFallback = expectedSizeForHash - writtenFallback;
} }
sha1fallback.update(output, 0, (int) toFallback);
writtenFallback += toFallback;
}
if (written >= filesize && h3hash == null) {
break;
}
} while (inBlockBuffer == BLOCKSIZE);
if (sha1 != null && sha1fallback != null) { byte[] calculated_hash1 = sha1.digest();
byte[] calculated_hash2 = sha1fallback.digest();
long missingInHash = expectedSizeForHash - writtenFallback; byte[] expected_hash = h3hash;
if (missingInHash > 0) { if (!Arrays.equals(calculated_hash1, expected_hash) && !Arrays.equals(calculated_hash2, expected_hash)) {
sha1fallback.update(new byte[(int) missingInHash]); throw new CheckSumWrongException("hash checksum failed", calculated_hash1, expected_hash);
} } else {
log.finest("Hash DOES match saves output stream.");
byte[] calculated_hash1 = sha1.digest(); }
byte[] calculated_hash2 = sha1fallback.digest();
byte[] expected_hash = h3hash;
if (!Arrays.equals(calculated_hash1, expected_hash) && !Arrays.equals(calculated_hash2, expected_hash)) {
inputStream.close();
outputStream.close();
throw new CheckSumWrongException("hash checksum failed", calculated_hash1, expected_hash);
} else {
log.finest("Hash DOES match saves output stream.");
} }
} finally {
StreamUtils.closeAll(inputStream, outputStream);
} }
inputStream.close();
outputStream.close();
} }
public void decryptFileStreamHashed(InputStream inputStream, OutputStream outputStream, long filesize, long fileoffset, short contentIndex, byte[] h3Hash) public void decryptFileStreamHashed(InputStream inputStream, OutputStream outputStream, long filesize, long fileoffset, short contentIndex, byte[] h3Hash)
@ -209,45 +207,44 @@ public class NUSDecryption extends AESDecryption {
ByteArrayBuffer overflow = new ByteArrayBuffer(BLOCKSIZE); ByteArrayBuffer overflow = new ByteArrayBuffer(BLOCKSIZE);
long wrote = 0; long wrote = 0;
int inBlockBuffer = 0; int inBlockBuffer = 0;
do { try {
inBlockBuffer = StreamUtils.getChunkFromStream(inputStream, encryptedBlockBuffer, overflow, BLOCKSIZE); do {
inBlockBuffer = StreamUtils.getChunkFromStream(inputStream, encryptedBlockBuffer, overflow, BLOCKSIZE);
if (writeSize > filesize) writeSize = filesize;
if (writeSize > filesize) writeSize = filesize; byte[] output;
try {
byte[] output; output = decryptFileChunkHash(encryptedBlockBuffer, (int) block, contentIndex, h3Hash);
try { } catch (CheckSumWrongException e) {
output = decryptFileChunkHash(encryptedBlockBuffer, (int) block, contentIndex, h3Hash); throw e;
} catch (CheckSumWrongException e) {
outputStream.close();
inputStream.close();
throw e;
}
if ((wrote + writeSize) > filesize) {
writeSize = (int) (filesize - wrote);
}
try {
outputStream.write(output, (int) (0 + soffset), (int) writeSize);
} catch (IOException e) {
if (e.getMessage().equals("Pipe closed")) {
break;
} }
e.printStackTrace();
throw e;
}
wrote += writeSize;
block++; if ((wrote + writeSize) > filesize) {
writeSize = (int) (filesize - wrote);
}
if (soffset > 0) { try {
writeSize = HASHBLOCKSIZE; outputStream.write(output, (int) (0 + soffset), (int) writeSize);
soffset = 0; } catch (IOException e) {
} if (e.getMessage().equals("Pipe closed")) {
} while (wrote < filesize && (inBlockBuffer == BLOCKSIZE)); break;
log.finest("Decryption okay"); }
outputStream.close(); e.printStackTrace();
inputStream.close(); throw e;
}
wrote += writeSize;
block++;
if (soffset > 0) {
writeSize = HASHBLOCKSIZE;
soffset = 0;
}
} while (wrote < filesize && (inBlockBuffer == BLOCKSIZE));
log.finest("Decryption okay");
} finally {
StreamUtils.closeAll(inputStream, outputStream);
}
} }
private byte[] decryptFileChunkHash(byte[] blockBuffer, int block, int contentIndex, byte[] h3_hashes) throws CheckSumWrongException { private byte[] decryptFileChunkHash(byte[] blockBuffer, int block, int contentIndex, byte[] h3_hashes) throws CheckSumWrongException {
@ -277,32 +274,30 @@ public class NUSDecryption extends AESDecryption {
long encryptedFileSize = content.getEncryptedFileSize(); long encryptedFileSize = content.getEncryptedFileSize();
if (content.isEncrypted()) { try {
if (content.isHashed()) { if (content.isEncrypted()) {
byte[] h3 = h3HashHashed.orElseThrow(() -> new FileNotFoundException("h3 hash not found.")); if (content.isHashed()) {
byte[] h3 = h3HashHashed.orElseThrow(() -> new FileNotFoundException("h3 hash not found."));
decryptFileStreamHashed(inputStream, outputStream, size, offset, (short) contentIndex, h3); decryptFileStreamHashed(inputStream, outputStream, size, offset, (short) contentIndex, h3);
} else { } else {
byte[] h3Hash = content.getSHA2Hash(); byte[] h3Hash = content.getSHA2Hash();
// We want to check if we read the whole file or just a part of it. // We want to check if we read the whole file or just a part of it.
// There should be only one actual file inside a non-hashed content. // There should be only one actual file inside a non-hashed content.
// But it could also contain a directory, so we need to filter. // But it could also contain a directory, so we need to filter.
long fstFileSize = content.getEntries().stream().filter(f -> !f.isDir()).findFirst().map(f -> f.getFileSize()).orElse(0L); long fstFileSize = content.getEntries().stream().filter(f -> !f.isDir()).findFirst().map(f -> f.getFileSize()).orElse(0L);
if (size > 0 && size < fstFileSize) { if (size > 0 && size < fstFileSize) {
h3Hash = null; h3Hash = null;
}
decryptFileStream(inputStream, outputStream, size, offset, (short) contentIndex, h3Hash, encryptedFileSize);
} }
decryptFileStream(inputStream, outputStream, size, offset, (short) contentIndex, h3Hash, encryptedFileSize); } else {
StreamUtils.saveInputStreamToOutputStreamWithHash(inputStream, outputStream, size, content.getSHA2Hash(), encryptedFileSize);
} }
} else { } finally {
StreamUtils.saveInputStreamToOutputStreamWithHash(inputStream, outputStream, size, content.getSHA2Hash(), encryptedFileSize); StreamUtils.closeAll(inputStream, outputStream);
} }
synchronized (inputStream) {
inputStream.close();
}
synchronized (outputStream) {
outputStream.close();
}
return true; return true;
} }
} }