From 5595347ab1f5f6ddb79dee8e9798696360bdcc6a Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 26 Apr 2024 22:13:50 -0400 Subject: [PATCH] Support tracking prefix data in front of archives --- pom.xml | 2 +- .../coley/lljzip/format/model/ZipArchive.java | 25 +++++++++++++++++++ .../format/read/ForwardScanZipReader.java | 11 +++++--- .../lljzip/format/read/JvmZipReader.java | 10 ++++++-- .../format/read/NaiveLocalFileZipReader.java | 12 ++++++--- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index a4c258a..ffb7bd6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.coley lljzip - 2.4.1 + 2.5.0 LL Java ZIP Lower level ZIP support for Java diff --git a/src/main/java/software/coley/lljzip/format/model/ZipArchive.java b/src/main/java/software/coley/lljzip/format/model/ZipArchive.java index 4ea84e5..5ec7daf 100644 --- a/src/main/java/software/coley/lljzip/format/model/ZipArchive.java +++ b/src/main/java/software/coley/lljzip/format/model/ZipArchive.java @@ -1,5 +1,6 @@ package software.coley.lljzip.format.model; +import software.coley.lljzip.format.read.ZipReader; import software.coley.lljzip.format.transform.ZipPartMapper; import software.coley.lljzip.util.OffsetComparator; @@ -7,6 +8,7 @@ import javax.annotation.Nullable; import java.io.Closeable; import java.io.IOException; +import java.lang.foreign.MemorySegment; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -19,6 +21,7 @@ public class ZipArchive implements AutoCloseable, Iterable { private final List parts = new ArrayList<>(); private final Closeable closableBackingResource; + private MemorySegment prefixData; /** * New zip archive without any backing resource. @@ -37,6 +40,28 @@ public ZipArchive(@Nonnull Closeable closableBackingResource) { this.closableBackingResource = closableBackingResource; } + /** + * If the {@link ZipReader} used to read this archive supports tracking such information this will return + * any data not contained in the archive that was found at the front of the input content. + *

+ * Some applications like Jar2Exe will prepend a loader executable in front of the jar, in which case this content + * would be that executable. It can be any kind of arbitrary data though. + * + * @return Data at the front of the archive. + */ + @Nullable + public MemorySegment getPrefixData() { + return prefixData; + } + + /** + * @param prefixData + * Data at the front of the archive. + */ + public void setPrefixData(@Nullable MemorySegment prefixData) { + this.prefixData = prefixData; + } + /** * @param mapper * Part mapper to manipulate contained zip parts. diff --git a/src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java b/src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java index 43d59dc..8ef34fe 100644 --- a/src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java +++ b/src/main/java/software/coley/lljzip/format/read/ForwardScanZipReader.java @@ -13,8 +13,7 @@ import javax.annotation.Nonnull; import java.io.IOException; import java.lang.foreign.MemorySegment; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * The standard read strategy that should work with standard zip archives. @@ -67,7 +66,7 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO // Read local files // - Set to prevent duplicate file header entries for the same offset - Set offsets = new HashSet<>(); + NavigableSet offsets = new TreeSet<>(); for (CentralDirectoryFileHeader directory : zip.getCentralDirectories()) { long offset = zipStart + directory.getRelativeOffsetOfLocalHeader(); if (!offsets.contains(offset) && MemorySegmentUtil.readQuad(data, offset) == ZipPatterns.LOCAL_FILE_HEADER_QUAD) { @@ -83,6 +82,12 @@ public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IO } } + // Record any data appearing at the front of the file not associated with the ZIP file contents. + if (!offsets.isEmpty()) { + long firstOffset = offsets.first(); + zip.setPrefixData(data.asSlice(0, firstOffset)); + } + // Sort based on order zip.sortParts(new OffsetComparator()); } diff --git a/src/main/java/software/coley/lljzip/format/read/JvmZipReader.java b/src/main/java/software/coley/lljzip/format/read/JvmZipReader.java index dcfad74..59d1fb5 100644 --- a/src/main/java/software/coley/lljzip/format/read/JvmZipReader.java +++ b/src/main/java/software/coley/lljzip/format/read/JvmZipReader.java @@ -40,7 +40,7 @@ public JvmZipReader() { * * @param skipRevisitedCenToLocalLinks * Flag to skip creating duplicate {@link LocalFileHeader} entries if multiple - * {@link CentralDirectoryFileHeader} point to the same location. + * {@link CentralDirectoryFileHeader} point to the same location. */ public JvmZipReader(boolean skipRevisitedCenToLocalLinks) { this(new JvmZipPartAllocator(), skipRevisitedCenToLocalLinks); @@ -53,7 +53,7 @@ public JvmZipReader(boolean skipRevisitedCenToLocalLinks) { * Allocator to use. * @param skipRevisitedCenToLocalLinks * Flag to skip creating duplicate {@link LocalFileHeader} entries if multiple - * {@link CentralDirectoryFileHeader} point to the same location. + * {@link CentralDirectoryFileHeader} point to the same location. */ public JvmZipReader(@Nonnull ZipPartAllocator allocator, boolean skipRevisitedCenToLocalLinks) { super(allocator); @@ -203,6 +203,12 @@ else if (MemorySegmentUtil.readQuad(data, jvmBaseFileOffset) == ZipPatterns.CENT } } + // Record any data appearing at the front of the file not associated with the ZIP file contents. + if (!entryOffsets.isEmpty()) { + long firstOffset = entryOffsets.first(); + zip.setPrefixData(data.asSlice(0, firstOffset)); + } + // Sort based on order zip.sortParts(new OffsetComparator()); } diff --git a/src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java b/src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java index 966dc11..c422618 100644 --- a/src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java +++ b/src/main/java/software/coley/lljzip/format/read/NaiveLocalFileZipReader.java @@ -34,12 +34,18 @@ public NaiveLocalFileZipReader(@Nonnull ZipPartAllocator allocator) { @Override public void read(@Nonnull ZipArchive zip, @Nonnull MemorySegment data) throws IOException { - long localFileOffset = -1; - while ((localFileOffset = MemorySegmentUtil.indexOfQuad(data, localFileOffset + 1, ZipPatterns.LOCAL_FILE_HEADER_QUAD)) >= 0) { + long localFileOffset = MemorySegmentUtil.indexOfQuad(data, 0, ZipPatterns.LOCAL_FILE_HEADER_QUAD); + if (localFileOffset < 0) return; + if (localFileOffset > 0) { + // The first offset containing archive data is not at the first byte. + // Record whatever content is at the front. + zip.setPrefixData(data.asSlice(0, localFileOffset)); + } + do { LocalFileHeader file = newLocalFileHeader(); file.read(data, localFileOffset); zip.addPart(file); postProcessLocalFileHeader(file); - } + } while ((localFileOffset = MemorySegmentUtil.indexOfQuad(data, localFileOffset + 1, ZipPatterns.LOCAL_FILE_HEADER_QUAD)) >= 0); } }