fix timestamp sign problem
This commit is contained in:
4 files changed
+37
-2
No files matched your search
@@ -85,7 +85,18 @@ public class BundleReader {
|
||||
var result = reassembler.writeReassembled(RegionFile.CompressType.ZLIB);
|
||||
try (var fc = FileChannel.open(path, StandardOpenOption.CREATE,
|
||||
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||
result.readBytes(fc, 0, result.readableBytes());
|
||||
// readBytes(FileChannel, ...) does a single channel write and
|
||||
// may write fewer bytes than requested; loop or the region file
|
||||
// is silently truncated.
|
||||
long pos = 0;
|
||||
int remaining = result.readableBytes();
|
||||
while (remaining > 0) {
|
||||
int written = result.readBytes(fc, pos, remaining);
|
||||
if (written <= 0)
|
||||
throw new IOException("Short write restoring " + entry.name() + " to " + path);
|
||||
pos += written;
|
||||
remaining -= written;
|
||||
}
|
||||
} finally {
|
||||
result.release();
|
||||
}
|
||||
|
||||
@@ -25,8 +25,12 @@ public class EntryInputStream extends FilterInputStream {
|
||||
public BundleEntry readEntry() throws IOException {
|
||||
var magic = (short) (in.read() << 8 | in.read()); // u16
|
||||
if (magic != BundleEntry.MAGIC) throw new IOException("Invalid magic number: "+Long.toHexString(magic));
|
||||
// every byte must be widened to long before shifting: an int shift such as
|
||||
// `in.read() << 24` goes negative when the byte is >= 0x80 and sign-extends into the
|
||||
// high 32 bits, corrupting the timestamp (which then makes the entry look "too old"
|
||||
// and get silently skipped on restore).
|
||||
var time = (long) in.read() << 56 | (long) in.read() << 48 | (long) in.read() << 40 | (long) in.read() << 32 |
|
||||
(long) in.read() << 24 | in.read() << 16 | in.read() << 8 | in.read();
|
||||
(long) in.read() << 24 | (long) in.read() << 16 | (long) in.read() << 8 | (long) in.read();
|
||||
var attribute = VarInts.readVarInt(in);
|
||||
var segmentLen = VarInts.readVarLong(in);
|
||||
var lenName = VarInts.readVarInt(in);
|
||||
|
||||
@@ -73,6 +73,26 @@ public class TestBundleReader {
|
||||
"the symlink itself must be recreated at the link's path, not at its target");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimestampWithHighBitSurvivesRoundTrip(@TempDir Path tmp) throws IOException {
|
||||
// 1798761600000 has 0xCE at bits 24-31; an int shift on that byte sign-extends and
|
||||
// makes the decoded time negative, which used to make extract() skip the entry as
|
||||
// "too old". ~half of all real-world dates land in such a band.
|
||||
var time = 1798761600000L;
|
||||
assertTrue(((time >> 24) & 0xFF) >= 0x80, "test vector must exercise the sign-extension path");
|
||||
var bundle = tmp.resolve("hibit.swb.zst");
|
||||
var payload = "kept".getBytes();
|
||||
try (var zstd = new ZstdOutputStreamNoFinalizer(Files.newOutputStream(bundle), 3);
|
||||
var out = new EntryOutputStream(zstd)) {
|
||||
out.writeEntryHeader("world.dat", (short) 0, payload.length, time);
|
||||
out.write(payload);
|
||||
}
|
||||
var dest = tmp.resolve("dest");
|
||||
BundleReader.builder().build().extract(bundle, dest);
|
||||
assertEquals("kept", Files.readString(dest.resolve("world.dat")),
|
||||
"entry must not be skipped just because its timestamp byte has the high bit set");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncatedEntryThrowsInsteadOfLooping(@TempDir Path tmp) throws IOException {
|
||||
var bundle = tmp.resolve("truncated.swb.zst");
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user