Create directories with symlinks in mind, fixes #349

This commit is contained in:
Lukas Rieger (Blue) 2022-10-14 10:24:19 +02:00
parent 4534202c75
commit 81f309b0be
No known key found for this signature in database
GPG Key ID: 2D09EC5ED2687FF2
9 changed files with 48 additions and 27 deletions

View File

@ -45,7 +45,7 @@ import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.resources.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.storage.Storage;
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
import de.bluecolored.bluemap.core.util.FileHelper;
import de.bluecolored.bluemap.core.world.World;
import org.apache.commons.io.FileUtils;
import org.spongepowered.configurate.ConfigurateException;
@ -315,7 +315,7 @@ public class BlueMapService implements Closeable {
Path resourcePackFolder = serverInterface.getConfigFolder().resolve("resourcepacks");
try {
Files.createDirectories(resourcePackFolder);
FileHelper.createDirectories(resourcePackFolder);
} catch (IOException ex) {
throw new ConfigurationException(
"BlueMap failed to create this folder:\n" +
@ -330,11 +330,11 @@ public class BlueMapService implements Closeable {
try {
Logger.global.logInfo("Downloading " + minecraftVersion.getResource().getClientUrl() + " to " + defaultResourceFile + " ...");
Files.createDirectories(defaultResourceFile.getParent());
FileHelper.createDirectories(defaultResourceFile.getParent());
Path tempResourceFile = defaultResourceFile.getParent().resolve(defaultResourceFile.getFileName() + ".filepart");
Files.deleteIfExists(tempResourceFile);
FileUtils.copyURLToFile(new URL(minecraftVersion.getResource().getClientUrl()), tempResourceFile.toFile(), 10000, 10000);
AtomicFileHelper.move(tempResourceFile, defaultResourceFile);
FileHelper.move(tempResourceFile, defaultResourceFile);
} catch (IOException ex) {
throw new ConfigurationException("Failed to download resources!", ex);
}
@ -346,7 +346,7 @@ public class BlueMapService implements Closeable {
try {
Files.deleteIfExists(resourceExtensionsFile);
Files.createDirectories(resourceExtensionsFile.getParent());
FileHelper.createDirectories(resourceExtensionsFile.getParent());
URL resourceExtensionsUrl = Objects.requireNonNull(
Plugin.class.getResource(
"/de/bluecolored/bluemap/" + minecraftVersion.getResource().getResourcePrefix() +

View File

@ -29,6 +29,7 @@ import de.bluecolored.bluemap.common.config.WebappConfig;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson;
import de.bluecolored.bluemap.core.util.FileHelper;
import org.apache.commons.io.FileUtils;
import java.io.BufferedReader;
@ -64,7 +65,7 @@ public class WebFilesManager {
}
public void saveSettings() throws IOException {
Files.createDirectories(getSettingsFile().getParent());
FileHelper.createDirectories(getSettingsFile().getParent());
try (BufferedWriter writer = Files.newBufferedWriter(getSettingsFile(),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
ResourcesGson.addAdapter(new GsonBuilder())

View File

@ -2,6 +2,7 @@ package de.bluecolored.bluemap.common.api;
import de.bluecolored.bluemap.api.WebApp;
import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.core.util.FileHelper;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@ -51,7 +52,7 @@ public class WebAppImpl implements WebApp {
Path imageRootFolder = webRoot.resolve(IMAGE_ROOT_PATH);
Path imagePath = imageRootFolder.resolve(Path.of(path.replace("/", separator) + ".png")).toAbsolutePath();
Files.createDirectories(imagePath.getParent());
FileHelper.createDirectories(imagePath.getParent());
Files.deleteIfExists(imagePath);
Files.createFile(imagePath);

View File

@ -6,6 +6,7 @@ import de.bluecolored.bluemap.common.config.storage.StorageConfig;
import de.bluecolored.bluemap.common.serverinterface.ServerInterface;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.util.FileHelper;
import de.bluecolored.bluemap.core.util.Tristate;
import java.io.IOException;
@ -101,7 +102,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider {
presetRenderThreadCount = 3;
try {
Files.createDirectories(configFolder);
FileHelper.createDirectories(configFolder);
Files.writeString(
configFolder.resolve("core.conf"),
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/core.conf")
@ -129,7 +130,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider {
if (!Files.exists(configFile)) {
try {
Files.createDirectories(configFolder);
FileHelper.createDirectories(configFolder);
Files.writeString(
configFolder.resolve("webserver.conf"),
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webserver.conf")
@ -152,7 +153,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider {
if (!Files.exists(configFile)) {
try {
Files.createDirectories(configFolder);
FileHelper.createDirectories(configFolder);
Files.writeString(
configFolder.resolve("webapp.conf"),
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webapp.conf")
@ -175,7 +176,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider {
if (!Files.exists(configFile)) {
try {
Files.createDirectories(configFolder);
FileHelper.createDirectories(configFolder);
Files.writeString(
configFolder.resolve("plugin.conf"),
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/plugin.conf")
@ -198,7 +199,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider {
if (!Files.exists(mapConfigFolder)){
try {
Files.createDirectories(mapConfigFolder);
FileHelper.createDirectories(mapConfigFolder);
var worlds = serverInterface.getLoadedWorlds();
if (worlds.isEmpty()) {
Files.writeString(
@ -281,7 +282,7 @@ public class BlueMapConfigs implements BlueMapConfigProvider {
if (!Files.exists(storageConfigFolder)){
try {
Files.createDirectories(storageConfigFolder);
FileHelper.createDirectories(storageConfigFolder);
Files.writeString(
storageConfigFolder.resolve("file.conf"),
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/storages/file.conf")

View File

@ -47,6 +47,7 @@ import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.metrics.Metrics;
import de.bluecolored.bluemap.core.storage.MetaType;
import de.bluecolored.bluemap.core.storage.Storage;
import de.bluecolored.bluemap.core.util.FileHelper;
import de.bluecolored.bluemap.core.world.World;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.spongepowered.configurate.serialize.SerializationException;
@ -56,7 +57,6 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.TimeUnit;
@ -160,7 +160,7 @@ public class Plugin implements ServerEventListener {
//create and start webserver
if (webserverConfig.isEnabled()) {
Path webroot = webserverConfig.getWebroot();
Files.createDirectories(webroot);
FileHelper.createDirectories(webroot);
RoutingRequestHandler routingRequestHandler = new RoutingRequestHandler();

View File

@ -30,6 +30,7 @@ import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask;
import de.bluecolored.bluemap.api.debug.DebugDump;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.util.FileHelper;
import java.io.IOException;
import java.nio.file.*;
@ -60,7 +61,7 @@ public class RegionFileWatchService extends Thread {
this.scheduledUpdates = new HashMap<>();
Path folder = map.getWorld().getSaveFolder().resolve("region");
Files.createDirectories(folder);
FileHelper.createDirectories(folder);
this.watchService = folder.getFileSystem().newWatchService();

View File

@ -27,7 +27,7 @@ package de.bluecolored.bluemap.core.storage.file;
import com.flowpowered.math.vector.Vector2i;
import de.bluecolored.bluemap.api.debug.DebugDump;
import de.bluecolored.bluemap.core.storage.*;
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
import de.bluecolored.bluemap.core.util.FileHelper;
import de.bluecolored.bluemap.core.util.DeletingPathVisitor;
import java.io.*;
@ -70,7 +70,7 @@ public class FileStorage extends Storage {
Compression compression = lod == 0 ? this.hiresCompression : Compression.NONE;
Path file = getFilePath(mapId, lod, tile);
OutputStream os = AtomicFileHelper.createFilepartOutputStream(file);
OutputStream os = FileHelper.createFilepartOutputStream(file);
os = new BufferedOutputStream(os);
try {
@ -140,7 +140,7 @@ public class FileStorage extends Storage {
public OutputStream writeMeta(String mapId, MetaType metaType) throws IOException {
Path file = getFilePath(mapId).resolve(metaType.getFilePath());
OutputStream os = AtomicFileHelper.createFilepartOutputStream(file);
OutputStream os = FileHelper.createFilepartOutputStream(file);
os = new BufferedOutputStream(os);
return os;

View File

@ -28,24 +28,32 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.*;
import java.nio.file.attribute.FileAttribute;
public class AtomicFileHelper {
public class FileHelper {
/**
* Creates an OutputStream that writes to a ".filepart"-file first and then atomically moves (overwrites) to the final target atomically
* once the stream gets closed.
*/
public static OutputStream createFilepartOutputStream(final Path file) throws IOException {
final Path partFile = getPartFile(file);
Files.createDirectories(partFile.getParent());
FileHelper.createDirectories(partFile.getParent());
OutputStream os = Files.newOutputStream(partFile, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
return new WrappedOutputStream(os, () -> {
if (!Files.exists(partFile)) return;
Files.deleteIfExists(file);
Files.createDirectories(file.getParent());
FileHelper.createDirectories(file.getParent());
AtomicFileHelper.move(partFile, file);
FileHelper.move(partFile, file);
});
}
/**
* Tries to move the file atomically, but fallbacks to a normal move operation if moving atomically fails
*/
public static void move(Path from, Path to) throws IOException {
try {
Files.move(from, to, StandardCopyOption.ATOMIC_MOVE);
@ -57,6 +65,15 @@ public class AtomicFileHelper {
}
}
/**
* Same as {@link Files#createDirectories(Path, FileAttribute[])} but accepts symlinked folders.
* @see Files#createDirectories(Path, FileAttribute[])
*/
public static Path createDirectories(Path dir, FileAttribute<?>... attrs) throws IOException {
if (Files.isDirectory(dir)) return dir;
return Files.createDirectories(dir, attrs);
}
private static Path getPartFile(Path file) {
return file.normalize().getParent().resolve(file.getFileName() + ".filepart");
}

View File

@ -48,12 +48,12 @@ import de.bluecolored.bluemap.core.logger.LoggerLogger;
import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.metrics.Metrics;
import de.bluecolored.bluemap.core.storage.Storage;
import de.bluecolored.bluemap.core.util.FileHelper;
import org.apache.commons.cli.*;
import org.apache.commons.lang3.time.DurationFormatUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.TimeUnit;
@ -181,7 +181,7 @@ public class BlueMapCLI implements ServerInterface {
Logger.global.logInfo("Starting webserver ...");
WebserverConfig config = blueMap.getConfigs().getWebserverConfig();
Files.createDirectories(config.getWebroot());
FileHelper.createDirectories(config.getWebroot());
RoutingRequestHandler routingRequestHandler = new RoutingRequestHandler();
@ -274,7 +274,7 @@ public class BlueMapCLI implements ServerInterface {
cli.configFolder = Path.of("config");
if (cmd.hasOption("c")) {
cli.configFolder = Path.of(cmd.getOptionValue("c"));
Files.createDirectories(cli.configFolder);
FileHelper.createDirectories(cli.configFolder);
}
//minecraft version
@ -323,7 +323,7 @@ public class BlueMapCLI implements ServerInterface {
Logger.global.logInfo("Generated default config files for you, here: " + cli.configFolder.toAbsolutePath().normalize() + "\n");
//create resourcepacks folder
Files.createDirectories(cli.configFolder.resolve( "resourcepacks"));
FileHelper.createDirectories(cli.configFolder.resolve( "resourcepacks"));
//print help
BlueMapCLI.printHelp();