Paper/Spigot-Server-Patches/0324-Detect-and-repair-corrupt-Region-Files.patch
Aikar 13d1abf01e
Updated Upstream (Bukkit/CraftBukkit/Spigot)
Upstream has released updates that appears to apply and compile correctly.
This update has only been PARTIALLY tested by PaperMC and as with ANY update, please do your own testing

I've tested basic region file saving as well as our oversized chunks approach.

Bukkit Changes:
e167e549 Clarify MerchantInventory#getSelectedRecipe.
3a1d5b8f Apply default permissions by registration order.
c64cc93f Make tags Keyed
ec037ed7 Added a method to get a list of tags
bfb6ef86 Introduce rotation methods to the Vector class
fc727372 Remove draft API from FluidLevelChangeEvent

CraftBukkit Changes:
6430d9c0 SPIGOT-4632: BlockState location is not fixed
14cd1688 Fix CraftInventoryMerchant#getSelectedRecipe if there is no active merchant recipe.
c24abab7 Load custom permissions after default permissions.
bc99dfe8 Make tags Keyed
6fce004f Added a method to get a list of tags

Spigot Changes:
e5e5c7c6 Allow Saving Large Chunks
e8d3881c Rebuild patches
2019-02-21 22:41:40 -05:00

140 lines
6.8 KiB
Diff

From 49d9979c5c63c62f676834076ab49026eecd4b68 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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 e2d4450e90..c20511588d 100644
--- a/src/main/java/net/minecraft/server/RegionFile.java
+++ b/src/main/java/net/minecraft/server/RegionFile.java
@@ -25,10 +25,10 @@ public class RegionFile {
private static final boolean ENABLE_EXTENDED_SAVE = Boolean.parseBoolean(System.getProperty("net.minecraft.server.RegionFile.enableExtendedSave", "true"));
// Spigot end
private static final byte[] a = new byte[4096];
- private final File b;
- private RandomAccessFile c;
- private final int[] d = new int[1024];
- private final int[] e = new int[1024];
+ private final File b;private File getFile() { return b; } // Paper - OBFHELPER
+ private RandomAccessFile c;private RandomAccessFile getDataFile() { return c; } // Paper - OBFHELPER
+ private final int[] d = new int[1024];private int[] offsets = d; // Paper - OBFHELPER
+ private final int[] e = new int[1024];private int[] timestamps = e; // Paper - OBFHELPER
private List<Boolean> f;
private int g;
private long h;
@@ -43,7 +43,7 @@ public class RegionFile {
}
this.c = new RandomAccessFile(file, "rw");
- if (this.c.length() < 4096L) {
+ if (this.c.length() < 8192L) { // Paper - headers should be 8192
this.c.write(RegionFile.a);
this.c.write(RegionFile.a);
this.g += 8192;
@@ -93,22 +93,23 @@ public class RegionFile {
this.c.seek(j * 4 + 4); // Go back to where we were
}
}
- if (k != 0 && (k >> 8) + (length) <= this.f.size()) {
+ if (k > 0 && (k >> 8) > 1 && (k >> 8) + (k & 255) <= this.f.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid
for (int l = 0; l < (length); ++l) {
// Spigot end
this.f.set((k >> 8) + l, false);
}
}
// 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});
+ else if (k != 0) { // Paper
+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Invalid chunk: ({0}, {1}) Offset: {2} Length: {3} runs off end file. {4}", new Object[]{j % 32, (int) (j / 32), k >> 8, length, file}); // Paper
+ deleteChunk(j); // Paper
}
// Spigot end
}
for (j = 0; j < 1024; ++j) {
k = headerAsInts.get(); // Paper
- this.e[j] = k;
+ if (offsets[j] != 0) this.timestamps[j] = k; // Paper - don't set timestamp if it got 0'd above due to corruption
}
} catch (IOException ioexception) {
ioexception.printStackTrace();
@@ -144,10 +145,10 @@ public class RegionFile {
int j1 = this.c.readInt();
if (j1 > 4096 * i1) {
- org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3}>{4} {5}", new Object[]{i, j, l, j1, i1 * 4096, this.b}); // Spigot
+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3}>{4} {5}", new Object[]{i, j, l, j1, i1 * 4096, this.b}); // Spigot
return null;
} else if (j1 <= 0) {
- org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3} {4}", new Object[]{i, j, l, j1, this.b}); // Spigot
+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3} {4}", new Object[]{i, j, l, j1, this.b}); // Spigot
return null;
} else {
byte b0 = this.c.readByte();
@@ -327,6 +328,54 @@ public class RegionFile {
}
+ // 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 + ") " + getFile().getAbsolutePath(), e);
+ } catch (IOException e) {
+
+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Error deleting corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e);
+ }
+ }
+ private boolean backedUp = false;
+ private synchronized void backup() {
+ if (backedUp) {
+ return;
+ }
+ backedUp = true;
+ File file = this.getFile();
+ 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 int b;
--
2.20.1