scan LFH from mark supported input stream

This commit is contained in:
REAndroid 2023-04-25 15:11:32 +02:00
parent 06185e4fff
commit c517505cbf
3 changed files with 130 additions and 42 deletions

View File

@ -17,6 +17,9 @@ package com.reandroid.archive2.block;
import com.reandroid.archive2.ZipSignature; import com.reandroid.archive2.ZipSignature;
import java.io.IOException;
import java.io.InputStream;
public class LocalFileHeader extends CommonHeader { public class LocalFileHeader extends CommonHeader {
private DataDescriptor dataDescriptor; private DataDescriptor dataDescriptor;
public LocalFileHeader(){ public LocalFileHeader(){
@ -64,6 +67,15 @@ public class LocalFileHeader extends CommonHeader {
lfh.setExtra(ceh.getExtra()); lfh.setExtra(ceh.getExtra());
return lfh; return lfh;
} }
public static LocalFileHeader read(InputStream inputStream) throws IOException {
LocalFileHeader localFileHeader = new LocalFileHeader();
localFileHeader.readBytes(inputStream);
if(localFileHeader.isValidSignature()){
return localFileHeader;
}
return null;
}
private static final int OFFSET_signature = 0; private static final int OFFSET_signature = 0;
private static final int OFFSET_versionMadeBy = 4; private static final int OFFSET_versionMadeBy = 4;
private static final int OFFSET_platform = 5; private static final int OFFSET_platform = 5;

View File

@ -17,31 +17,32 @@ package com.reandroid.archive2.io;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
public class FileChannelInputStream extends InputStream { public class FileChannelInputStream extends InputStream {
private final FileChannel fileChannel; private final FileChannel fileChannel;
private final long length; private final long totalLength;
private long total; private long startOffset;
private long position;
private final byte[] buffer; private final byte[] buffer;
private int pos; private int bufferPosition;
private int bufferLength; private int bufferLength;
public FileChannelInputStream(FileChannel fileChannel, long length){ public FileChannelInputStream(FileChannel fileChannel, long length, int bufferSize) throws IOException {
this.fileChannel = fileChannel; this.fileChannel = fileChannel;
this.length = length; this.totalLength = length;
int len = 1024 * 1000 * 100; if(length < bufferSize){
if(length < len){ bufferSize = (int) length;
len = (int) length;
} }
this.buffer = new byte[len]; this.buffer = new byte[bufferSize];
this.bufferLength = len; this.bufferLength = bufferSize;
this.pos = len; this.bufferPosition = bufferSize;
this.startOffset = fileChannel.position();
} }
public FileChannelInputStream(FileChannel fileChannel, long length) throws IOException {
public FileChannel getFileChannel() { this(fileChannel, length, DEFAULT_BUFFER_SIZE);
return fileChannel;
} }
@Override @Override
@ -72,7 +73,7 @@ public class FileChannelInputStream extends InputStream {
return result; return result;
} }
private int readBuffer(byte[] bytes, int offset, int length){ private int readBuffer(byte[] bytes, int offset, int length){
int avail = bufferLength - pos; int avail = bufferLength - bufferPosition;
if(avail == 0){ if(avail == 0){
return 0; return 0;
} }
@ -80,29 +81,105 @@ public class FileChannelInputStream extends InputStream {
if(read > avail){ if(read > avail){
read = avail; read = avail;
} }
System.arraycopy(buffer, pos, bytes, offset, read); System.arraycopy(buffer, bufferPosition, bytes, offset, read);
pos += read; bufferPosition += read;
total += read; position += read;
return read; return read;
} }
private void loadBuffer() throws IOException { private void loadBuffer() throws IOException {
byte[] buffer = this.buffer; byte[] buffer = this.buffer;
if(this.pos < buffer.length){ if(this.bufferPosition < bufferLength){
return; return;
} }
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer); ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
bufferLength = fileChannel.read(byteBuffer); bufferLength = fileChannel.read(byteBuffer);
pos = 0; bufferPosition = 0;
} }
private boolean isFinished(){ private boolean isFinished(){
return total >= length; return position >= totalLength;
} }
@Override @Override
public int read() throws IOException { public int read() throws IOException {
throw new IOException("Why one byte?"); byte[] bytes = new byte[1];
int read = read(bytes);
if(read < 0){
return read;
}
return bytes[0] & 0xff;
}
public long transferTo(OutputStream out) throws IOException{
long transferred = 0;
if(isFinished()){
return transferred;
}
while (!isFinished()){
loadBuffer();
int offset = bufferPosition;
int length = bufferLength - bufferPosition;
if(length <= 0){
break;
}
out.write(buffer, offset, length);
bufferPosition += length;
position += length;
transferred += length;
}
return transferred;
} }
@Override @Override
public void reset(){ public long skip(long amount) throws IOException {
total = 0; if(amount <= 0){
return amount;
}
long remaining = amount;
remaining = remaining - skipBuffer((int) remaining);
if(remaining == 0){
return amount;
}
long availableChannel = totalLength - position;
if(availableChannel > remaining){
availableChannel = remaining;
}
position += availableChannel;
remaining = remaining - availableChannel;
amount = amount - remaining;
fileChannel.position(fileChannel.position() + amount);
return amount;
} }
private int skipBuffer(int amount){
int availableBuffer = bufferLength - bufferPosition;
if(availableBuffer > amount){
availableBuffer = amount;
}
bufferPosition += availableBuffer;
position += availableBuffer;
return availableBuffer;
}
@Override
public void reset() throws IOException {
position = 0;
bufferPosition = bufferLength;
fileChannel.position(startOffset);
}
@Override
public int available(){
return (int) (totalLength - position);
}
@Override
public boolean markSupported() {
return true;
}
@Override
public synchronized void mark(int readLimit){
if(readLimit < 0){
readLimit = 0;
}
startOffset = readLimit;
}
@Override
public String toString(){
return position + " / " + totalLength;
}
private static final int DEFAULT_BUFFER_SIZE = 1024 * 100;
} }

View File

@ -17,12 +17,11 @@ package com.reandroid.archive2.model;
import com.reandroid.archive2.block.*; import com.reandroid.archive2.block.*;
import com.reandroid.archive2.block.ApkSignatureBlock; import com.reandroid.archive2.block.ApkSignatureBlock;
import com.reandroid.archive2.io.FileChannelInputStream;
import com.reandroid.archive2.io.ZipInput; import com.reandroid.archive2.io.ZipInput;
import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.io.BlockReader;
import java.io.IOException; import java.io.IOException;
import java.nio.channels.FileChannel; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -45,36 +44,36 @@ public class LocalFileDirectory {
private void visitLocalFile(ZipInput zipInput) throws IOException { private void visitLocalFile(ZipInput zipInput) throws IOException {
List<LocalFileHeader> headerList = this.getHeaderList(); List<LocalFileHeader> headerList = this.getHeaderList();
long offset; long offset;
int read;
int index = 0; int index = 0;
CentralFileDirectory centralFileDirectory = getCentralFileDirectory(); CentralFileDirectory centralFileDirectory = getCentralFileDirectory();
long length = zipInput.getLength(); long length = zipInput.getLength();
FileChannelInputStream inputStream= (FileChannelInputStream) zipInput.getInputStream(0, length); InputStream inputStream = zipInput.getInputStream(0, length);
FileChannel fileChannel = inputStream.getFileChannel(); for(CentralEntryHeader ceh : centralFileDirectory.getHeaderList()){
for(CentralEntryHeader ceh: centralFileDirectory.getHeaderList()){
offset = ceh.getLocalRelativeOffset(); offset = ceh.getLocalRelativeOffset();
fileChannel.position(offset); inputStream.reset();
LocalFileHeader lfh = new LocalFileHeader(); offset = inputStream.skip(offset);
lfh.readBytes(inputStream); LocalFileHeader lfh = LocalFileHeader.read(inputStream);
lfh.mergeZeroValues(ceh); if(lfh == null){
throw new IOException("Error reading LFH at "
+ offset + ", for CEH = " + ceh.getFileName());
}
offset = offset + lfh.countBytes(); offset = offset + lfh.countBytes();
lfh.setFileOffset(offset); lfh.setFileOffset(offset);
ceh.setFileOffset(offset); ceh.setFileOffset(offset);
offset = inputStream.skip(lfh.getDataSize()); lfh.mergeZeroValues(ceh);
inputStream.skip(lfh.getDataSize());
DataDescriptor dataDescriptor = null; DataDescriptor dataDescriptor = null;
if(lfh.hasDataDescriptor()){ if(lfh.hasDataDescriptor()){
dataDescriptor = new DataDescriptor(); dataDescriptor = new DataDescriptor();
read = dataDescriptor.readBytes(inputStream); int read = dataDescriptor.readBytes(inputStream);
if(read>0){ if(read != dataDescriptor.countBytes()){
offset += read; dataDescriptor = null;
} }
} }
index++;
lfh.setIndex(index);
lfh.setDataDescriptor(dataDescriptor); lfh.setDataDescriptor(dataDescriptor);
lfh.setIndex(index);
headerList.add(lfh); headerList.add(lfh);
length = length - offset; index++;
inputStream.reset();
} }
} }
private void visitApkSigBlock(ZipInput zipInput) throws IOException{ private void visitApkSigBlock(ZipInput zipInput) throws IOException{