From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Xymb 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 55dcbb48450b24f80e0a04bedeb54f64e94ebb9e..f3f824d0ab1a2a72825c40b67192386479a0b34c 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 RegionFileFormat regionFormatName = RegionFileFormat.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 abf5e2a06af9853b58ac9107cd6e9787c4185c66..389c68c0becd2f69dc1004d0b383f1a8784214c0 100644 --- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java +++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java @@ -87,10 +87,11 @@ public class ThreadedWorldUpgrader { // Kaiiju start dev.kaiijumc.kaiiju.region.RegionFileFormat 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 61c6bc2859235874aefac71e9e55162f0a89f29f..24bd63c596c9f61f1e89ec5b001b7cb7a29a09ff 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -269,7 +269,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 mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier 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 @@ -314,7 +314,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 4774d3f357d62b0818b4713f8085d05be09eaa5c..1bbd8b475cdc57fb15ca05ffe122220a5539da10 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -426,8 +426,8 @@ public class ServerLevel extends Level implements WorldGenLevel { 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(dev.kaiijumc.kaiiju.region.RegionFileFormat 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 { @@ -666,7 +666,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 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 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 ac35e7eb8cb5f19391a18eb9d6b5ba26769ce2f6..a9c6ca7c621bb2431bcf0ae879b192f748bf931b 100644 --- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java +++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java @@ -120,7 +120,8 @@ public class WorldUpgrader { String worldName = this.levelStorage.getLevelId(); dev.kaiijumc.kaiiju.region.RegionFileFormat 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 187ff795192c7eb56dffafa1ff6fa3068ac341c3..b9cf3b9f2cdb554d267c6dc3436e011c7e607228 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 { // Paper end - rewrite chunk system - public PoiManager(dev.kaiijumc.kaiiju.region.RegionFileFormat 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(dev.kaiijumc.kaiiju.region.RegionFileFormat 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 1d880f27dd147da683fc30ed6f1bfa43ecdb7d93..41598adf6d49a44bcaadfff3797221460a6d93ba 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,11 +37,11 @@ public class ChunkStorage implements AutoCloseable { public final RegionFileStorage regionFileCache; // Paper end - async chunk loading - public ChunkStorage(dev.kaiijumc.kaiiju.region.RegionFileFormat format, int linearCompression, Path directory, DataFixer dataFixer, boolean dsync) { // Kaiiju + public ChunkStorage(dev.kaiijumc.kaiiju.region.RegionFileFormat 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 b35a6a7daf768af3bc453fe18deec823f4b63a3a..8409293b3f329715d6dff3d455c6a484d5b133ee 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 regionCache = new Long2ObjectLinkedOpenHashMap(); // Kaiiju @@ -28,6 +29,7 @@ public class RegionFileStorage implements AutoCloseable { // Kaiiju start - Per world chunk format public final dev.kaiijumc.kaiiju.region.RegionFileFormat format; public final int linearCompression; + public final boolean linearCrashOnBrokenSymlink; // Kaiiju end private final boolean isChunkData; // Paper @@ -60,14 +62,15 @@ public class RegionFileStorage implements AutoCloseable { } // Paper end - cache regionfile does not exist state - protected RegionFileStorage(dev.kaiijumc.kaiiju.region.RegionFileFormat format, int linearCompression, Path directory, boolean dsync) { // Paper - protected constructor + protected RegionFileStorage(dev.kaiijumc.kaiiju.region.RegionFileFormat 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(dev.kaiijumc.kaiiju.region.RegionFileFormat format, int linearCompression, Path directory, boolean dsync, boolean isChunkData) { // Kaiiju + RegionFileStorage(dev.kaiijumc.kaiiju.region.RegionFileFormat 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 @@ -111,6 +114,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); } @@ -146,6 +163,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 { @@ -161,6 +179,7 @@ public class RegionFileStorage implements AutoCloseable { }; path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + "." + extension); // Kaiiju end + guardAgainstBrokenSymlinks(path1); // Kaiiju - Crash on broken symlink this.createRegionFile(regionPos); } // Kaiiju start - Polyglot 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 9394d191c56aab78e63fd3f283efedd69384e323..dcfe4a285cc5865be3b0c1b8104b722895135dd0 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 extends RegionFileStorage implements AutoCloseabl public final RegistryAccess registryAccess; // Paper - rewrite chunk system protected final LevelHeightAccessor levelHeightAccessor; - public SectionStorage(dev.kaiijumc.kaiiju.region.RegionFileFormat format, int linearCompression, Path path, Function> codecFactory, Function 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(dev.kaiijumc.kaiiju.region.RegionFileFormat format, int linearCompression, boolean linearCrashOnBrokenSymlink, Path path, Function> codecFactory, Function 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;