Files
KaiijuMC/patches/server/0011-Crash-on-broken-symlink.patch
2023-05-04 17:37:35 +03:00

229 lines
18 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Xymb <xymb@endcrystal.me>
Date: Fri, 14 Apr 2023 02:12:58 +0200
Subject: [PATCH] Crash on broken symlink
diff --git a/src/main/java/dev/kaiijumc/kaiiju/KaiijuWorldConfig.java b/src/main/java/dev/kaiijumc/kaiiju/KaiijuWorldConfig.java
index bac11b0e30a86d0689693ff129b53bcad7c2034c..0f0cfd958f7d2c6d2ca4192be1b986900a2bfa53 100644
--- a/src/main/java/dev/kaiijumc/kaiiju/KaiijuWorldConfig.java
+++ b/src/main/java/dev/kaiijumc/kaiiju/KaiijuWorldConfig.java
@@ -127,6 +127,7 @@ public class KaiijuWorldConfig {
public List<String> regionFormatList = Arrays.asList("ANVIL", "LINEAR");
public String regionFormatName = "ANVIL";
+ public boolean linearCrashOnBrokenSymlink = true;
public int regionFormatLinearCompressionLevel = 1;
private void regionFormatSettings() {
@@ -142,6 +143,7 @@ public class KaiijuWorldConfig {
log(Level.SEVERE, "Falling back to compression level 1.");
regionFormatLinearCompressionLevel = 1;
}
+ linearCrashOnBrokenSymlink = getBoolean("region-format.linear.crash-on-broken-symlink", linearCrashOnBrokenSymlink);
}
public boolean shulkerBoxDropContentsWhenDestroyed = true;
diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
index a62a1b281bd0b6ad7d59b45b9470d84f496f6539..7f87dfa131ab7d40a94cf6355765478f2961342e 100644
--- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
+++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java
@@ -86,10 +86,11 @@ public class ThreadedWorldUpgrader {
// Kaiiju start
String formatName = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatName;
int linearCompression = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatLinearCompressionLevel;
+ boolean linearCrashOnBrokenSymlink = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.linearCrashOnBrokenSymlink;
LOGGER.info("Using format " + formatName + " (" + linearCompression + ")");
// Kaiiju end
final WorldInfo info = new WorldInfo(() -> worldPersistentData,
- new ChunkStorage(formatName, linearCompression, regionFolder.toPath(), this.dataFixer, false), this.removeCaches, this.dimensionType, this.generatorKey); // Kaiiju
+ new ChunkStorage(formatName, linearCompression, linearCrashOnBrokenSymlink, regionFolder.toPath(), this.dataFixer, false), this.removeCaches, this.dimensionType, this.generatorKey); // Kaiiju
long expectedChunks = (long)regionFiles.length * (32L * 32L);
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 5ad209fed6aef1992af0472ac0af0911d178a260..0ef89e0bc214364fd904c3e5022c660b32b2caaa 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -272,7 +272,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
- super(world.getLevel().kaiijuConfig.regionFormatName, world.getLevel().kaiijuConfig.regionFormatLinearCompressionLevel, session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); // Kaiiju
+ super(world.getLevel().kaiijuConfig.regionFormatName, world.getLevel().kaiijuConfig.regionFormatLinearCompressionLevel, world.getLevel().kaiijuConfig.linearCrashOnBrokenSymlink, session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); // Kaiiju
// Paper - rewrite chunk system
this.tickingGenerated = new AtomicInteger();
//this.playerMap = new PlayerMap(); // Folia - region threading
@@ -317,7 +317,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), null, null); // Paper - rewrite chunk system
this.distanceManager = new ChunkMap.ChunkDistanceManager(executor, mainThreadExecutor);
this.overworldDataStorage = persistentStateManagerFactory;
- this.poiManager = new PoiManager(this.level.kaiijuConfig.regionFormatName, this.level.kaiijuConfig.regionFormatLinearCompressionLevel, path.resolve("poi"), dataFixer, dsync, iregistrycustom, world); // Kaiiju
+ this.poiManager = new PoiManager(this.level.kaiijuConfig.regionFormatName, this.level.kaiijuConfig.regionFormatLinearCompressionLevel, this.level.kaiijuConfig.linearCrashOnBrokenSymlink, path.resolve("poi"), dataFixer, dsync, iregistrycustom, world); // Kaiiju
this.setViewDistance(viewDistance);
// Paper start
this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new);
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 20359c63b19c7e0c703ef1562fb774803d631c41..35ae624be25b66d5615dc19cbe7eb8785c7029b5 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -425,8 +425,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
private final EntityRegionFileStorage entityStorage;
private static final class EntityRegionFileStorage extends net.minecraft.world.level.chunk.storage.RegionFileStorage {
- public EntityRegionFileStorage(String format, int linearCompression, Path directory, boolean dsync) { // Kaiiju
- super(format, linearCompression, directory, dsync); // Kaiiju
+ public EntityRegionFileStorage(String format, int linearCompression, boolean linearCrashOnBrokenSymlink, Path directory, boolean dsync) { // Kaiiju
+ super(format, linearCompression, linearCrashOnBrokenSymlink, directory, dsync); // Kaiiju
}
protected void write(ChunkPos pos, net.minecraft.nbt.CompoundTag nbt) throws IOException {
@@ -632,7 +632,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
// CraftBukkit end
boolean flag2 = minecraftserver.forceSynchronousWrites();
DataFixer datafixer = minecraftserver.getFixerUpper();
- this.entityStorage = new EntityRegionFileStorage(this.getLevel().kaiijuConfig.regionFormatName, this.getLevel().kaiijuConfig.regionFormatLinearCompressionLevel, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver); // Kaiiju
+ this.entityStorage = new EntityRegionFileStorage(this.getLevel().kaiijuConfig.regionFormatName, this.getLevel().kaiijuConfig.regionFormatLinearCompressionLevel, this.getLevel().kaiijuConfig.linearCrashOnBrokenSymlink, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver); // Kaiiju
// this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage, this.entitySliceManager); // Paper // Paper - rewrite chunk system
StructureTemplateManager structuretemplatemanager = minecraftserver.getStructureManager();
diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
index 469802b9c454c5e98c0a55ba89559d9d8ba0c4c0..1652398c211c4366dde1d973bdb8e9d2c5c67ff4 100644
--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
+++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
@@ -119,7 +119,8 @@ public class WorldUpgrader {
String worldName = this.levelStorage.getLevelId();
String formatName = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatName;
int linearCompression = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.regionFormatLinearCompressionLevel;
- builder1.put(resourcekey1, new ChunkStorage(formatName, linearCompression, path.resolve("region"), this.dataFixer, true));
+ boolean linearCrashOnBrokenSymlink = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(worldName)).getHandle().kaiijuConfig.linearCrashOnBrokenSymlink;
+ builder1.put(resourcekey1, new ChunkStorage(formatName, linearCompression, linearCrashOnBrokenSymlink, path.resolve("region"), this.dataFixer, true));
// Kaiiju end
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
index f560aa13c6c8ffecb456f478687dc6a9eb5e8017..f0354203f974a8d7440407a5561ffa0fcb63182d 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
@@ -59,8 +59,8 @@ public class PoiManager extends SectionStorage<PoiSection> {
// Paper end - rewrite chunk system
- public PoiManager(String formatName, int linearCompression, Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) { // Kaiiju
- super(formatName, linearCompression, path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world); // Kaiiju
+ public PoiManager(String formatName, int linearCompression, boolean linearCrashOnBrokenSymlink, Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) { // Kaiiju
+ super(formatName, linearCompression, linearCrashOnBrokenSymlink, path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world); // Kaiiju
this.world = (net.minecraft.server.level.ServerLevel)world; // Paper - rewrite chunk system
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
index 52c2e0fe73a5af18535a2b0b9a506919e3f93003..ea54af3f68c6534ff5a71ca0b7a99d126557868e 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -37,12 +37,12 @@ public class ChunkStorage implements AutoCloseable {
public final RegionFileStorage regionFileCache;
// Paper end - async chunk loading
- public ChunkStorage(String format, int linearCompression, Path directory, DataFixer dataFixer, boolean dsync) { // Kaiiju
+ public ChunkStorage(String format, int linearCompression, boolean linearCrashOnBrokenSymlink, Path directory, DataFixer dataFixer, boolean dsync) { // Kaiiju
this.fixerUpper = dataFixer;
// Paper start - async chunk io
// remove IO worker
- this.regionFileCache = new RegionFileStorage(format, linearCompression, directory, dsync, true); // Paper - nuke IOWorker // Paper
+ this.regionFileCache = new RegionFileStorage(format, linearCompression, linearCrashOnBrokenSymlink, directory, dsync, true); // Paper - nuke IOWorker // Paper
// Paper end - async chunk io
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index c9fbba6f709ed0540dcc22b41e50ce9d01a6381c..70e3ee306283263cdcdb7a66c80fc23484385aac 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -20,6 +20,7 @@ import net.minecraft.world.level.ChunkPos;
public class RegionFileStorage implements AutoCloseable {
+ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Kaiiju
public static final String ANVIL_EXTENSION = ".mca";
private static final int MAX_CACHE_SIZE = 256;
public final Long2ObjectLinkedOpenHashMap<dev.kaiijumc.kaiiju.region.AbstractRegionFile> regionCache = new Long2ObjectLinkedOpenHashMap(); // Kaiiju
@@ -28,6 +29,7 @@ public class RegionFileStorage implements AutoCloseable {
// Kaiiju start - Per world chunk format
public final String format;
public final int linearCompression;
+ public final boolean linearCrashOnBrokenSymlink;
// Kaiiju end
private final boolean isChunkData; // Paper
@@ -61,14 +63,15 @@ public class RegionFileStorage implements AutoCloseable {
}
// Paper end - cache regionfile does not exist state
- protected RegionFileStorage(String format, int linearCompression, Path directory, boolean dsync) { // Paper - protected constructor
+ protected RegionFileStorage(String format, int linearCompression, boolean linearCrashOnBrokenSymlink, Path directory, boolean dsync) { // Paper - protected constructor
// Paper start - add isChunkData param
- this(format, linearCompression, directory, dsync, false);
+ this(format, linearCompression, linearCrashOnBrokenSymlink, directory, dsync, false);
}
- RegionFileStorage(String format, int linearCompression, Path directory, boolean dsync, boolean isChunkData) { // Kaiiju
+ RegionFileStorage(String format, int linearCompression, boolean linearCrashOnBrokenSymlink, Path directory, boolean dsync, boolean isChunkData) { // Kaiiju
// Kaiiju start
this.format = format;
this.linearCompression = linearCompression;
+ this.linearCrashOnBrokenSymlink = linearCrashOnBrokenSymlink;
// Kaiiju end
this.isChunkData = isChunkData;
// Paper end - add isChunkData param
@@ -109,6 +112,20 @@ public class RegionFileStorage implements AutoCloseable {
return regionfile != null ? regionfile.hasChunk(pos) : false;
}
+ // Kaiiju start
+ private void guardAgainstBrokenSymlinks(Path path) throws IOException {
+ if (!linearCrashOnBrokenSymlink) return;
+ if (!this.format.equals("LINEAR")) return;
+ if (!java.nio.file.Files.isSymbolicLink(path)) return;
+ Path link = java.nio.file.Files.readSymbolicLink(path);
+ if (!java.nio.file.Files.exists(link) || !java.nio.file.Files.isReadable(link)) {
+ LOGGER.error("Linear region file {} is a broken symbolic link, crashing to prevent data loss", path);
+ net.minecraft.server.MinecraftServer.getServer().halt(false);
+ throw new IOException("Linear region file " + path + " is a broken symbolic link, crashing to prevent data loss");
+ }
+ }
+ // Kaiiju end
+
public synchronized dev.kaiijumc.kaiiju.region.AbstractRegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Kaiiju
return this.getRegionFile(chunkcoordintpair, existingOnly, false);
}
@@ -144,6 +161,7 @@ public class RegionFileStorage implements AutoCloseable {
if (existingOnly) {
Path anvil = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca");
Path linear = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".linear");
+ guardAgainstBrokenSymlinks(linear);
if (java.nio.file.Files.exists(anvil)) path1 = anvil;
else if (java.nio.file.Files.exists(linear)) path1 = linear;
else {
@@ -156,6 +174,7 @@ public class RegionFileStorage implements AutoCloseable {
default -> "mca";
};
path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + "." + extension);
+ guardAgainstBrokenSymlinks(path1);
this.createRegionFile(regionPos);
}
if (dev.kaiijumc.kaiiju.KaiijuConfig.regionFormatDebug)
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
index 414aa3ae00c2d28e754235e3e93b6b623d4fd180..15e376fac063430e4355c6ffcd25c35a8ce20c5b 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
@@ -47,8 +47,8 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
public final RegistryAccess registryAccess; // Paper - rewrite chunk system
protected final LevelHeightAccessor levelHeightAccessor;
- public SectionStorage(String format, int linearCompression, Path path, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, RegistryAccess dynamicRegistryManager, LevelHeightAccessor world) { // Kaiiju
- super(format, linearCompression, path, dsync); // Paper - remove mojang I/O thread // Kaiiju
+ public SectionStorage(String format, int linearCompression, boolean linearCrashOnBrokenSymlink, Path path, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, RegistryAccess dynamicRegistryManager, LevelHeightAccessor world) { // Kaiiju
+ super(format, linearCompression, linearCrashOnBrokenSymlink, path, dsync); // Paper - remove mojang I/O thread // Kaiiju
this.codec = codecFactory;
this.factory = factory;
this.fixerUpper = dataFixer;