From a1683b33a2ec42ca595cd7182ffdf5b376c844be Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 11 Aug 2018 00:49:20 -0400 Subject: [PATCH] Detect and repair corrupt Region Files If the file has partial data written but not the full 8192 bytes, then the server will be unable to load that region file... I don't know why mojang only checks for 4096, when anything less than 8192 is a crash. But to be safe, it will attempt to back up the file. diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java index 3aeac69c26..17648c1c04 100644 --- a/src/main/java/net/minecraft/server/RegionFile.java +++ b/src/main/java/net/minecraft/server/RegionFile.java @@ -27,13 +27,13 @@ public class RegionFile implements AutoCloseable { // Spigot end private static final byte[] a = new byte[4096]; private final RandomAccessFile b; private RandomAccessFile getDataFile() { return this.b; } // Paper - OBFHELPER // PAIL dataFile - private final int[] c = new int[1024]; - private final int[] d = new int[1024]; + private final int[] c = new int[1024]; private final int[] offsets = c; // Paper - OBFHELPER + private final int[] d = new int[1024]; private final int[] timestamps = d; // Paper - OBFHELPER private final List e; // PAIL freeSectors public RegionFile(File file) throws IOException { this.b = new RandomAccessFile(file, "rw"); - if (this.b.length() < 4096L) { + if (this.b.length() < 8192L) { // Paper - headers should be 8192 this.b.write(RegionFile.a); this.b.write(RegionFile.a); } @@ -83,7 +83,7 @@ public class RegionFile implements AutoCloseable { this.b.seek(j * 4 + 4); // Go back to where we were } } - if (k != 0 && (k >> 8) + (length) <= this.e.size()) { + if (k > 0 && (k >> 8) > 1 && (k >> 8) + (k & 255) <= this.e.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid for (int l = 0; l < (length); ++l) { // Spigot end this.e.set((k >> 8) + l, false); @@ -92,13 +92,14 @@ public class RegionFile implements AutoCloseable { // Spigot start else if (length > 0) { org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Length: {3} runs off end file. {4}", new Object[]{j % 32, (int) (j / 32), k >> 8, length, file}); + deleteChunk(j); // Paper } // Spigot end } for (j = 0; j < 1024; ++j) { k = headerAsInts.get(); // Paper - this.d[j] = k; + if (this.offsets[j] != 0) this.timestamps[j] = k; // Paper - don't set timestamp if it got 0'd above due to corruption } this.file = file; // Spigot @@ -349,6 +350,53 @@ public class RegionFile implements AutoCloseable { } // Spigot end + // Paper start + public synchronized void deleteChunk(int j1) { + backup(); + int k = offsets[j1]; + int x = j1 & 1024; + int z = j1 >> 2; + int offset = (k >> 8); + int len = (k & 255); + String debug = "idx:" + + j1 + " - " + x + "," + z + " - offset: " + offset + " - len: " + len; + try { + timestamps[j1] = 0; + offsets[j1] = 0; + RandomAccessFile file = getDataFile(); + file.seek(j1 * 4); + file.writeInt(0); + // clear the timestamp + file.seek(4096 + j1 * 4); + file.writeInt(0); + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Deleted corrupt chunk (" + debug + ") " + this.file.getAbsolutePath(), e); + } catch (IOException e) { + + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Error deleting corrupt chunk (" + debug + ") " + this.file.getAbsolutePath(), e); + } + } + private boolean backedUp = false; + private synchronized void backup() { + if (backedUp) { + return; + } + backedUp = true; + java.text.DateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd"); + java.util.Date today = new java.util.Date(); + File corrupt = new File(file.getParentFile(), file.getName() + "." + formatter.format(today) + ".corrupt"); + if (corrupt.exists()) { + return; + } + org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(); + logger.error("Region file " + file.getAbsolutePath() + " was corrupt. Backing up to " + corrupt.getAbsolutePath() + " and repairing"); + try { + java.nio.file.Files.copy(file.toPath(), corrupt.toPath()); + + } catch (IOException e) { + logger.error("Error backing up corrupt file" + file.getAbsolutePath(), e); + } + } + // Paper end + class ChunkBuffer extends ByteArrayOutputStream { private final ChunkCoordIntPair b; -- 2.22.0