fix timestamp sign problem

This commit is contained in:
iceBear67 committed 2026-07-17 20:02:59 +08:00
1 parent cf3049624e
commit f9de570f25
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");
BIN
View File
Binary file not shown.