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);
|
var result = reassembler.writeReassembled(RegionFile.CompressType.ZLIB);
|
||||||
try (var fc = FileChannel.open(path, StandardOpenOption.CREATE,
|
try (var fc = FileChannel.open(path, StandardOpenOption.CREATE,
|
||||||
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
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 {
|
} finally {
|
||||||
result.release();
|
result.release();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,12 @@ public class EntryInputStream extends FilterInputStream {
|
|||||||
public BundleEntry readEntry() throws IOException {
|
public BundleEntry readEntry() throws IOException {
|
||||||
var magic = (short) (in.read() << 8 | in.read()); // u16
|
var magic = (short) (in.read() << 8 | in.read()); // u16
|
||||||
if (magic != BundleEntry.MAGIC) throw new IOException("Invalid magic number: "+Long.toHexString(magic));
|
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 |
|
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 attribute = VarInts.readVarInt(in);
|
||||||
var segmentLen = VarInts.readVarLong(in);
|
var segmentLen = VarInts.readVarLong(in);
|
||||||
var lenName = VarInts.readVarInt(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");
|
"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
|
@Test
|
||||||
public void testTruncatedEntryThrowsInsteadOfLooping(@TempDir Path tmp) throws IOException {
|
public void testTruncatedEntryThrowsInsteadOfLooping(@TempDir Path tmp) throws IOException {
|
||||||
var bundle = tmp.resolve("truncated.swb.zst");
|
var bundle = tmp.resolve("truncated.swb.zst");
|
||||||
|
|||||||
Binary file not shown.
Reference in new issue
Block a user