papermc/Spigot-Server-Patches/0284-Detect-and-repair-corrupt-Region-Files.patch
Zach Brown 70ce6ce831
Move version command update checking to the implementation
This makes it easier for downstream projects (forks) to replace the
version fetching system with their own. It is as simple as implementing
an interface and overriding the default implementation of
org.bukkit.UnsafeValues#getVersionFetcher()

It also makes it easier for us to organize things like the version
history feature.

Lastly I have updated the paper implementation to check against the site
API rather than against jenkins.
2019-05-27 04:13:41 -05:00

115 lines
5 KiB
Diff

From 3691197a487e286eaecab0599fed5036d95629b5 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 4e6288e8b..e68f90194 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
- private final int[] c = new int[1024];
- private final int[] d = new int[1024];
+ private final int[] c = new int[1024]; private int[] offsets = c; // Paper - OBFHELPER
+ private final int[] d = new int[1024];private int[] timestamps = d; // Paper - OBFHELPER
private final List<Boolean> e;
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 (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
@@ -306,6 +307,53 @@ public class RegionFile implements AutoCloseable {
this.b.close();
}
+ // 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.21.0