1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
From 25f06715b685a629d93544f7a05d99c243b6d56e 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 eac8b22b..d58cda9a 100644
--- a/src/main/java/net/minecraft/server/RegionFile.java
+++ b/src/main/java/net/minecraft/server/RegionFile.java
@@ -28,10 +28,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;
@@ -45,10 +45,11 @@ public class RegionFile {
this.h = file.lastModified();
}
+
this.c = new RandomAccessFile(file, "rw");
- if (this.c.length() < 4096L) {
- this.c.write(RegionFile.a);
- this.c.write(RegionFile.a);
+ if (this.c.length() < 8192L) { // Paper - headers should be 8192
+ this.c.write(a);
+ this.c.write(a);
this.g += 8192;
}
@@ -96,22 +97,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) + (length) <= 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, Boolean.valueOf(false));
}
}
// Spigot start
- else if (length > 0) {
+ else if (k != 0) { // Paper
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.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();
@@ -302,6 +304,55 @@ 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);
+ org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger();
+ String debug = "idx:" + + j1 + " - " + x + "," + z + " - offset: " + offset + " - len: " + len;
+ try {
+ RandomAccessFile file = getDataFile();
+ file.seek(j1 * 4);
+ file.writeInt(0);
+ // clear the timestamp
+ file.seek(4096 + j1 * 4);
+ file.writeInt(0);
+ timestamps[j1] = 0;
+ offsets[j1] = 0;
+ logger.error("Deleted corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e);
+ } catch (IOException e) {
+
+ logger.error("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
|