diff --git a/build.gradle b/build.gradle
index 72e04cd..5dcdb51 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'fabric-loom' version '1.0-SNAPSHOT'
+ id 'fabric-loom' version '1.2-SNAPSHOT'
id 'maven-publish'
}
@@ -17,17 +17,6 @@ repositories {
mavenCentral()
}
-loom {
- runs {
- testServer {
- server()
- ideConfigGenerated project.rootProject == project
- name = "Testmod Server"
- source sourceSets.test
- }
- }
-}
-
dependencies {
//to change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
@@ -46,8 +35,8 @@ dependencies {
modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}")
//General compression library
- implementation "org.apache.commons:commons-compress:1.21"
- include "org.apache.commons:commons-compress:1.21"
+ implementation "org.apache.commons:commons-compress:1.22"
+ include "org.apache.commons:commons-compress:1.22"
//LZMA support
implementation 'org.tukaani:xz:1.9'
@@ -116,7 +105,7 @@ publishing {
}
static def getMcMinor(ver) {
- String[] arr = ((String)ver).split("\\.")
+ String[] arr = ((String)ver).split("[.-]")
if(arr.length < 2) return ver
diff --git a/gradle.properties b/gradle.properties
index 3ed2600..0bbd976 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,26 +1,25 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
-minecraft_version=1.19.2
-yarn_mappings=1.19.2+build.28
-loader_version=0.14.10
+minecraft_version=1.20-rc1
+yarn_mappings=1.20-rc1+build.2
+loader_version=0.14.21
#Fabric api
-fabric_version=0.64.0+1.19.2
+fabric_version=0.83.0+1.20
#Cloth Config
-cloth_version=8.2.88
+cloth_version=11.0.98
#ModMenu
-modmenu_version=4.1.0
+modmenu_version=7.0.0-beta.2
-#Lazy DFU for faster dev start
-lazydfu_version=v0.1.3
+databreaker_version=0.2.10
#Hash of commit form which parallel gzip will be build
pgzip_commit_hash=af5f5c297e735f3f2df7aa4eb0e19a5810b8aff6
# Mod Properties
-mod_version = 2.5.0
+mod_version = 3.0.0
maven_group = net.szum123321
archives_base_name = textile_backup
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ae04661..fae0804 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/src/main/java/net/szum123321/textile_backup/Globals.java b/src/main/java/net/szum123321/textile_backup/Globals.java
index 8d33251..2567528 100644
--- a/src/main/java/net/szum123321/textile_backup/Globals.java
+++ b/src/main/java/net/szum123321/textile_backup/Globals.java
@@ -1,30 +1,33 @@
/*
- * A simple backup mod for Fabric
- * Copyright (C) 2022 Szum123321
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package net.szum123321.textile_backup;
import net.minecraft.server.MinecraftServer;
+import net.szum123321.textile_backup.core.digest.BalticHash;
+import net.szum123321.textile_backup.core.digest.Hash;
import net.szum123321.textile_backup.core.Utilities;
-import net.szum123321.textile_backup.core.create.MakeBackupRunnable;
+
import net.szum123321.textile_backup.core.restore.AwaitThread;
import org.apache.commons.io.FileUtils;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.format.DateTimeFormatter;
@@ -34,19 +37,48 @@
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+import java.util.zip.CRC32;
public class Globals {
public static final Globals INSTANCE = new Globals();
- private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
- public final static DateTimeFormatter defaultDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss");
+ private static final TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
+ public static final DateTimeFormatter defaultDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss");
+ public static final Supplier CHECKSUM_SUPPLIER = BalticHash::new;/*() -> new Hash() {
+ private final CRC32 crc = new CRC32();
+
+ @Override
+ public void update ( int b){
+ crc.update(b);
+ }
+
+ @Override
+ public void update ( long b) {
+ ByteBuffer v = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
+ v.putLong(b);
+ crc.update(v.array());
+ }
+
+ @Override
+ public void update ( byte[] b, int off, int len){
+ crc.update(b, off, len);
+ }
- private ExecutorService executorService = null;// = Executors.newSingleThreadExecutor();
+ @Override
+ public long getValue () {
+ return crc.getValue();
+ }
+ };*/
+
+ private ExecutorService executorService = null;//TODO: AAAAAAAAAAAAAAA MEMORY LEAK!!!!!!!!!
public final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true);
public boolean disableWatchdog = false;
private boolean disableTMPFiles = false;
private AwaitThread restoreAwaitThread = null;
private Path lockedPath = null;
+ private String combinedVersionString;
+
private Globals() {}
public ExecutorService getQueueExecutor() { return executorService; }
@@ -64,8 +96,8 @@ public void shutdownQueueExecutor(long timeout) {
if(!executorService.awaitTermination(timeout, TimeUnit.MICROSECONDS)) {
log.error("Timeout occurred while waiting for currently running backups to finish!");
executorService.shutdownNow().stream()
- .filter(r -> r instanceof MakeBackupRunnable)
- .map(r -> (MakeBackupRunnable)r)
+ // .filter(r -> r instanceof ExecutableBackup)
+ // .map(r -> (ExecutableBackup)r)
.forEach(r -> log.error("Dropping: {}", r.toString()));
if(!executorService.awaitTermination(1000, TimeUnit.MICROSECONDS))
log.error("Couldn't shut down the executor!");
@@ -84,8 +116,8 @@ public void shutdownQueueExecutor(long timeout) {
public Optional getLockedFile() { return Optional.ofNullable(lockedPath); }
public void setLockedFile(Path p) { lockedPath = p; }
- public boolean disableTMPFS() { return disableTMPFiles; }
- public void updateTMPFSFlag(MinecraftServer server) {
+ public synchronized boolean disableTMPFS() { return disableTMPFiles; }
+ public synchronized void updateTMPFSFlag(MinecraftServer server) {
disableTMPFiles = false;
Path tmp_dir = Path.of(System.getProperty("java.io.tmpdir"));
if(
@@ -103,4 +135,12 @@ public void updateTMPFSFlag(MinecraftServer server) {
if(disableTMPFiles) log.error("Might cause: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems");
}
+
+ public String getCombinedVersionString() {
+ return combinedVersionString;
+ }
+
+ public void setCombinedVersionString(String combinedVersionString) {
+ this.combinedVersionString = combinedVersionString;
+ }
}
diff --git a/src/main/java/net/szum123321/textile_backup/TextileBackup.java b/src/main/java/net/szum123321/textile_backup/TextileBackup.java
index d1e313b..876bc5b 100644
--- a/src/main/java/net/szum123321/textile_backup/TextileBackup.java
+++ b/src/main/java/net/szum123321/textile_backup/TextileBackup.java
@@ -1,20 +1,20 @@
/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package net.szum123321.textile_backup;
@@ -26,6 +26,7 @@
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
+import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.commands.create.CleanupCommand;
import net.szum123321.textile_backup.commands.create.StartBackupCommand;
@@ -38,9 +39,9 @@
import net.szum123321.textile_backup.config.ConfigHelper;
import net.szum123321.textile_backup.config.ConfigPOJO;
import net.szum123321.textile_backup.core.ActionInitiator;
-import net.szum123321.textile_backup.core.create.BackupContext;
import net.szum123321.textile_backup.core.create.BackupScheduler;
-import net.szum123321.textile_backup.core.create.MakeBackupRunnableFactory;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
+import net.szum123321.textile_backup.test.BalticHashTest;
public class TextileBackup implements ModInitializer {
public static final String MOD_NAME = "Textile Backup";
@@ -51,7 +52,13 @@ public class TextileBackup implements ModInitializer {
@Override
public void onInitialize() {
- log.info("Starting Textile Backup by Szum123321");
+ Globals.INSTANCE.setCombinedVersionString(
+ FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow().getMetadata().getVersion().getFriendlyString() +
+ ":" +
+ FabricLoader.getInstance().getModContainer("minecraft").orElseThrow().getMetadata().getVersion().getFriendlyString()
+ );
+
+ log.info("Starting Textile Backup {} by Szum123321", Globals.INSTANCE.getCombinedVersionString());
ConfigHelper.updateInstance(AutoConfig.register(ConfigPOJO.class, JanksonConfigSerializer::new));
@@ -63,19 +70,21 @@ public void onInitialize() {
Globals.INSTANCE.updateTMPFSFlag(server);
});
- //Wait 60s for already submited backups to finish. After that kill the bastards and run the one last if required
+ //Wait 60s for already submitted backups to finish. After that kill the bastards and run the one last if required
ServerLifecycleEvents.SERVER_STOPPED.register(server -> {
Globals.INSTANCE.shutdownQueueExecutor(60000);
if (config.get().shutdownBackup && Globals.INSTANCE.globalShutdownBackupFlag.get()) {
- MakeBackupRunnableFactory.create(
- BackupContext.Builder
- .newBackupContextBuilder()
- .setServer(server)
- .setInitiator(ActionInitiator.Shutdown)
- .setComment("shutdown")
- .build()
- ).run();
+ try {
+ ExecutableBackup.Builder
+ .newBackupContextBuilder()
+ .setServer(server)
+ .setInitiator(ActionInitiator.Shutdown)
+ .setComment("shutdown")
+ .announce()
+ .build()
+ .call();
+ } catch (Exception ignored) {}
}
});
diff --git a/src/main/java/net/szum123321/textile_backup/TextileLogger.java b/src/main/java/net/szum123321/textile_backup/TextileLogger.java
index 6d2815a..08b5dc7 100644
--- a/src/main/java/net/szum123321/textile_backup/TextileLogger.java
+++ b/src/main/java/net/szum123321/textile_backup/TextileLogger.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
import net.minecraft.text.MutableText;
import net.minecraft.util.Formatting;
import net.szum123321.textile_backup.core.Utilities;
-import net.szum123321.textile_backup.core.create.BackupContext;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -94,7 +94,7 @@ boolean sendFeedback(Level level, ServerCommandSource source, String msg, Object
else if(level.intLevel() <= Level.WARN.intLevel()) text.formatted(Formatting.RED);
else text.formatted(Formatting.WHITE);
- source.sendFeedback(prefixText.copy().append(text), false);
+ source.sendFeedback(() -> prefixText.copy().append(text), false);
return true;
} else {
@@ -112,7 +112,7 @@ public void sendInfo(ServerCommandSource source, String msg, Object... args) {
sendFeedback(Level.INFO, source, msg, args);
}
- public void sendInfo(BackupContext context, String msg, Object... args) {
+ public void sendInfo(ExecutableBackup context, String msg, Object... args) {
sendInfo(context.commandSource(), msg, args);
}
@@ -120,7 +120,8 @@ public void sendError(ServerCommandSource source, String msg, Object... args) {
sendFeedback(Level.ERROR, source, msg, args);
}
- public void sendError(BackupContext context, String msg, Object... args) {
+
+ public void sendError(ExecutableBackup context, String msg, Object... args) {
sendError(context.commandSource(), msg, args);
}
@@ -134,7 +135,7 @@ public void sendInfoAL(ServerCommandSource source, String msg, Object... args) {
sendToPlayerAndLog(Level.INFO, source, msg, args);
}
- public void sendInfoAL(BackupContext context, String msg, Object... args) {
+ public void sendInfoAL(ExecutableBackup context, String msg, Object... args) {
sendInfoAL(context.commandSource(), msg, args);
}
@@ -142,7 +143,7 @@ public void sendErrorAL(ServerCommandSource source, String msg, Object... args)
sendToPlayerAndLog(Level.ERROR, source, msg, args);
}
- public void sendErrorAL(BackupContext context, String msg, Object... args) {
+ public void sendErrorAL(ExecutableBackup context, String msg, Object... args) {
sendErrorAL(context.commandSource(), msg, args);
}
}
diff --git a/src/main/java/net/szum123321/textile_backup/client/ModMenuEntry.java b/src/main/java/net/szum123321/textile_backup/client/ModMenuEntry.java
index 75017a2..ddb2c04 100644
--- a/src/main/java/net/szum123321/textile_backup/client/ModMenuEntry.java
+++ b/src/main/java/net/szum123321/textile_backup/client/ModMenuEntry.java
@@ -1,20 +1,19 @@
/*
- * A simple backup mod for Fabric
- * Copyright (C) 2022 Szum123321
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package net.szum123321.textile_backup.client;
diff --git a/src/main/java/net/szum123321/textile_backup/commands/CommandExceptions.java b/src/main/java/net/szum123321/textile_backup/commands/CommandExceptions.java
index 3c31542..e0009d0 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/CommandExceptions.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/CommandExceptions.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/commands/FileSuggestionProvider.java b/src/main/java/net/szum123321/textile_backup/commands/FileSuggestionProvider.java
index 319e341..da2ec52 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/FileSuggestionProvider.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/FileSuggestionProvider.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/commands/create/CleanupCommand.java b/src/main/java/net/szum123321/textile_backup/commands/create/CleanupCommand.java
index c0567ee..765e4ae 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/create/CleanupCommand.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/create/CleanupCommand.java
@@ -1,20 +1,20 @@
/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package net.szum123321.textile_backup.commands.create;
diff --git a/src/main/java/net/szum123321/textile_backup/commands/create/StartBackupCommand.java b/src/main/java/net/szum123321/textile_backup/commands/create/StartBackupCommand.java
index 830a88a..7964c5a 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/create/StartBackupCommand.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/create/StartBackupCommand.java
@@ -1,20 +1,20 @@
/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package net.szum123321.textile_backup.commands.create;
@@ -25,8 +25,7 @@
import net.szum123321.textile_backup.Globals;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.TextileLogger;
-import net.szum123321.textile_backup.core.create.BackupContext;
-import net.szum123321.textile_backup.core.create.MakeBackupRunnableFactory;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
import javax.annotation.Nullable;
@@ -41,22 +40,15 @@ public static LiteralArgumentBuilder register() {
}
private static int execute(ServerCommandSource source, @Nullable String comment) {
- try {
- Globals.INSTANCE.getQueueExecutor().submit(
- MakeBackupRunnableFactory.create(
- BackupContext.Builder
- .newBackupContextBuilder()
- .setCommandSource(source)
- .setComment(comment)
- .guessInitiator()
- .saveServer()
- .build()
- )
- );
- } catch (Exception e) {
- log.error("Something went wrong while executing command!", e);
- throw e;
- }
+ Globals.INSTANCE.getQueueExecutor().submit(
+ ExecutableBackup.Builder
+ .newBackupContextBuilder()
+ .setCommandSource(source)
+ .setComment(comment)
+ .guessInitiator()
+ .saveServer()
+ .build()
+ );
return 1;
}
diff --git a/src/main/java/net/szum123321/textile_backup/commands/manage/BlacklistCommand.java b/src/main/java/net/szum123321/textile_backup/commands/manage/BlacklistCommand.java
index 5baaedd..6e138be 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/manage/BlacklistCommand.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/manage/BlacklistCommand.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/commands/manage/DeleteCommand.java b/src/main/java/net/szum123321/textile_backup/commands/manage/DeleteCommand.java
index 5fc0279..f1c4d0c 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/manage/DeleteCommand.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/manage/DeleteCommand.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/commands/manage/ListBackupsCommand.java b/src/main/java/net/szum123321/textile_backup/commands/manage/ListBackupsCommand.java
index 962709e..c9b4656 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/manage/ListBackupsCommand.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/manage/ListBackupsCommand.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/commands/manage/WhitelistCommand.java b/src/main/java/net/szum123321/textile_backup/commands/manage/WhitelistCommand.java
index c5da2f4..728e62b 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/manage/WhitelistCommand.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/manage/WhitelistCommand.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/commands/restore/KillRestoreCommand.java b/src/main/java/net/szum123321/textile_backup/commands/restore/KillRestoreCommand.java
index f0578a0..f63efc8 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/restore/KillRestoreCommand.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/restore/KillRestoreCommand.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/commands/restore/RestoreBackupCommand.java b/src/main/java/net/szum123321/textile_backup/commands/restore/RestoreBackupCommand.java
index b53f18a..b65e166 100644
--- a/src/main/java/net/szum123321/textile_backup/commands/restore/RestoreBackupCommand.java
+++ b/src/main/java/net/szum123321/textile_backup/commands/restore/RestoreBackupCommand.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/config/ConfigHelper.java b/src/main/java/net/szum123321/textile_backup/config/ConfigHelper.java
index 07332bd..704c082 100644
--- a/src/main/java/net/szum123321/textile_backup/config/ConfigHelper.java
+++ b/src/main/java/net/szum123321/textile_backup/config/ConfigHelper.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/config/ConfigPOJO.java b/src/main/java/net/szum123321/textile_backup/config/ConfigPOJO.java
index d6ad376..7ab41ca 100644
--- a/src/main/java/net/szum123321/textile_backup/config/ConfigPOJO.java
+++ b/src/main/java/net/szum123321/textile_backup/config/ConfigPOJO.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,16 +18,17 @@
package net.szum123321.textile_backup.config;
-import blue.endless.jankson.annotation.SerializedName;
+import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.annotation.SerializedName;
+import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment;
import me.shedaniel.autoconfig.ConfigData;
import me.shedaniel.autoconfig.annotation.Config;
import me.shedaniel.autoconfig.annotation.ConfigEntry;
-import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment;
import net.szum123321.textile_backup.TextileBackup;
import java.time.format.DateTimeFormatter;
import java.util.*;
+//TODO: Remove BZIP2 and LZMA compressors. As for the popular vote
@Config(name = TextileBackup.MOD_ID)
public class ConfigPOJO implements ConfigData {
@Comment("\nShould every world have its own backup folder?\n")
@@ -90,7 +91,7 @@ public class ConfigPOJO implements ConfigData {
public long maxAge = 0;
@Comment("""
- \nMaximum size of backup folder in kilo bytes (1024).
+ \nMaximum size of backup folder in kibi bytes (1024).
If set to 0 then backups will not be deleted
""")
@ConfigEntry.Gui.Tooltip()
@@ -114,8 +115,6 @@ public class ConfigPOJO implements ConfigData {
\nAvailable formats are:
ZIP - normal zip archive using standard deflate compression
GZIP - tar.gz using gzip compression
- BZIP2 - tar.bz2 archive using bzip2 compression
- LZMA - tar.xz using lzma compression
TAR - .tar with no compression
""")
@ConfigEntry.Gui.Tooltip()
@@ -162,6 +161,14 @@ public class ConfigPOJO implements ConfigData {
@ConfigEntry.Gui.Tooltip()
public String dateTimeFormat = "yyyy.MM.dd_HH-mm-ss";
+ @Comment("""
+ \nThe Strict mode (default) aborts backup creation in case of any problem and deletes created files
+ Permissible mode keeps partial/damaged backup but won't allow to restore it
+ Very Permissible mode will skip the verification process. THIS MOST CERTAINLY WILL LEAD TO DATA LOSS OR CORRUPTION
+ """)
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
+ public IntegrityVerificationMode integrityVerificationMode = IntegrityVerificationMode.STRICT;
+
@Override
public void validatePostLoad() throws ValidationException {
if(compressionCoreCountLimit > Runtime.getRuntime().availableProcessors())
@@ -177,6 +184,16 @@ public void validatePostLoad() throws ValidationException {
}
}
+ public enum IntegrityVerificationMode {
+ STRICT,
+ PERMISSIBLE,
+ VERY_PERMISSIBLE;
+
+ public boolean isStrict() { return this == STRICT; }
+
+ public boolean verify() { return this != VERY_PERMISSIBLE; }
+ }
+
public enum ArchiveFormat {
ZIP("zip"),
GZIP("tar", "gz"),
diff --git a/src/main/java/net/szum123321/textile_backup/core/ActionInitiator.java b/src/main/java/net/szum123321/textile_backup/core/ActionInitiator.java
index 07375b9..21e58aa 100644
--- a/src/main/java/net/szum123321/textile_backup/core/ActionInitiator.java
+++ b/src/main/java/net/szum123321/textile_backup/core/ActionInitiator.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,9 +26,7 @@ public enum ActionInitiator {
ServerConsole("Server Console", "from"), //some/ting typed a command and it was not a player (command blocks and server console count)
Timer("Timer", "by"), //a.k.a scheduler
Shutdown("Server Shutdown", "by"),
- Restore("Backup Restoration", "because of"),
- Null("Null (That shouldn't have happened)", "form");
-
+ Restore("Backup Restoration", "because of");
private final String name;
private final String prefix;
diff --git a/src/main/java/net/szum123321/textile_backup/core/Cleanup.java b/src/main/java/net/szum123321/textile_backup/core/Cleanup.java
index 56fd97d..6347b61 100644
--- a/src/main/java/net/szum123321/textile_backup/core/Cleanup.java
+++ b/src/main/java/net/szum123321/textile_backup/core/Cleanup.java
@@ -1,20 +1,19 @@
/*
- * A simple backup mod for Fabric
- * Copyright (C) 2022 Szum123321
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package net.szum123321.textile_backup.core;
diff --git a/src/main/java/net/szum123321/textile_backup/core/CompressionStatus.java b/src/main/java/net/szum123321/textile_backup/core/CompressionStatus.java
new file mode 100644
index 0000000..de5a2b3
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/CompressionStatus.java
@@ -0,0 +1,100 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core;
+
+import net.szum123321.textile_backup.core.restore.RestoreContext;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Map;
+import java.util.Optional;
+
+public record CompressionStatus(long treeHash, Map brokenFiles, LocalDateTime date, long startTimestamp, long finishTimestamp, String version) implements Serializable {
+ public static final String DATA_FILENAME = "textile_status.data";
+
+ public Optional validate(long hash, RestoreContext ctx) throws RuntimeException {
+ if(hash != treeHash)
+ return Optional.of("Tree Hash mismatch!\n Expected: " + hex(treeHash) + ", got: " + hex(hash));
+
+ if(!brokenFiles.isEmpty()) return Optional.of("Damaged files present! ^");
+
+ if(ctx.restoreableFile().getCreationTime().equals(date))
+ return Optional.of(
+ "Creation date mismatch!\n Expected: " +
+ date.format(DateTimeFormatter.ISO_DATE_TIME) + ", got: " +
+ ctx.restoreableFile().getCreationTime().format(DateTimeFormatter.ISO_DATE_TIME)
+ );
+
+ return Optional.empty();
+ }
+
+ public static Path resolveStatusFilename(Path directory) { return directory.resolve(DATA_FILENAME); }
+
+ public static CompressionStatus readFromFile(Path directory) throws IOException, ClassNotFoundException {
+ try(InputStream i = Files.newInputStream(directory.resolve(DATA_FILENAME));
+ ObjectInputStream obj = new ObjectInputStream(i)) {
+ return (CompressionStatus) obj.readObject();
+ }
+ }
+
+ public byte[] serialize() throws IOException {
+ try (ByteArrayOutputStream bo = new ByteArrayOutputStream();
+ ObjectOutputStream o = new ObjectOutputStream(bo)) {
+ o.writeObject(this);
+ return bo.toByteArray();
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("{ ");
+ builder.append("Hash: ")
+ .append(hex(treeHash))
+ .append(", Date: ")
+ .append(date.format(DateTimeFormatter.ISO_DATE_TIME))
+ .append(", Start timestamp: ").append(startTimestamp)
+ .append(", Finish timestamp: ").append(finishTimestamp)
+ .append(", Mod Version: ").append(version);
+
+ builder.append(", Broken files: ");
+ if(brokenFiles.isEmpty()) builder.append("[]");
+ else {
+ builder.append("[\n");
+ for(Path i: brokenFiles.keySet()) {
+ builder.append(i.toString())
+ .append(":");
+
+ ByteArrayOutputStream o = new ByteArrayOutputStream();
+ brokenFiles.get(i).printStackTrace(new PrintStream(o));
+ builder.append(o).append("\n");
+ }
+ builder.append("]");
+ }
+
+ builder.append(" }");
+
+ return builder.toString();
+ }
+
+ private static String hex(long val) { return "0x" + Long.toHexString(val).toUpperCase(); }
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/LivingServer.java b/src/main/java/net/szum123321/textile_backup/core/DataLeftException.java
similarity index 77%
rename from src/main/java/net/szum123321/textile_backup/core/LivingServer.java
rename to src/main/java/net/szum123321/textile_backup/core/DataLeftException.java
index 0011d6b..02ac7f0 100644
--- a/src/main/java/net/szum123321/textile_backup/core/LivingServer.java
+++ b/src/main/java/net/szum123321/textile_backup/core/DataLeftException.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,6 +18,8 @@
package net.szum123321.textile_backup.core;
-public interface LivingServer {
- boolean isAlive();
+import java.io.IOException;
+
+public class DataLeftException extends IOException {
+ public DataLeftException(long n) { super("Input stream closed with " + n + " bytes left!"); }
}
diff --git a/src/main/java/net/szum123321/textile_backup/core/NoSpaceLeftOnDeviceException.java b/src/main/java/net/szum123321/textile_backup/core/NoSpaceLeftOnDeviceException.java
index 3f71e4b..a487f89 100644
--- a/src/main/java/net/szum123321/textile_backup/core/NoSpaceLeftOnDeviceException.java
+++ b/src/main/java/net/szum123321/textile_backup/core/NoSpaceLeftOnDeviceException.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,6 +25,6 @@
*/
public class NoSpaceLeftOnDeviceException extends IOException {
public NoSpaceLeftOnDeviceException(Throwable cause) {
- super(cause);
+ super("The underlying filesystem has ran out of available space.\nSee: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems", cause);
}
}
diff --git a/src/main/java/net/szum123321/textile_backup/core/RestoreableFile.java b/src/main/java/net/szum123321/textile_backup/core/RestoreableFile.java
index b44d9e5..5db2d24 100644
--- a/src/main/java/net/szum123321/textile_backup/core/RestoreableFile.java
+++ b/src/main/java/net/szum123321/textile_backup/core/RestoreableFile.java
@@ -1,20 +1,19 @@
/*
- * A simple backup mod for Fabric
- * Copyright (C) 2022 Szum123321
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package net.szum123321.textile_backup.core;
@@ -57,7 +56,7 @@ private RestoreableFile(Path file, ConfigPOJO.ArchiveFormat archiveFormat, Local
}
//removes repetition of the files stream thingy with awfully large lambdas
- public static T applyOnFiles(Path root, T def, Consumer errorConsumer, Function, T> streamConsumer) {
+ public static T applyOnFiles(Path root, T def, Consumer errorConsumer, Function, T> streamConsumer) {
try (Stream stream = Files.list(root)) {
return streamConsumer.apply(stream.flatMap(f -> RestoreableFile.build(f).stream()));
} catch (IOException e) {
diff --git a/src/main/java/net/szum123321/textile_backup/core/Utilities.java b/src/main/java/net/szum123321/textile_backup/core/Utilities.java
index 65a6f9f..7d82400 100644
--- a/src/main/java/net/szum123321/textile_backup/core/Utilities.java
+++ b/src/main/java/net/szum123321/textile_backup/core/Utilities.java
@@ -1,20 +1,20 @@
/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package net.szum123321.textile_backup.core;
@@ -115,9 +115,9 @@ public static Path getBackupRootPath(String worldName) {
}
public static boolean isBlacklisted(Path path) {
- if(isWindows()) { //hotfix!
- if (path.getFileName().toString().equals("session.lock")) return true;
- }
+ if (path.getFileName().equals("session.lock")) return true;
+
+ if(path.getFileName().endsWith(CompressionStatus.DATA_FILENAME)) return true;
return config.get().fileBlacklist.stream().anyMatch(path::startsWith);
}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/BackupContext.java b/src/main/java/net/szum123321/textile_backup/core/create/BackupContext.java
deleted file mode 100644
index 63a3e3e..0000000
--- a/src/main/java/net/szum123321/textile_backup/core/create/BackupContext.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package net.szum123321.textile_backup.core.create;
-
-import net.minecraft.entity.player.PlayerEntity;
-import net.minecraft.server.MinecraftServer;
-import net.minecraft.server.command.ServerCommandSource;
-import net.szum123321.textile_backup.core.ActionInitiator;
-import org.jetbrains.annotations.NotNull;
-
-public record BackupContext(@NotNull MinecraftServer server,
- ServerCommandSource commandSource,
- ActionInitiator initiator,
- boolean save,
- String comment) {
-
- public boolean startedByPlayer() {
- return initiator == ActionInitiator.Player;
- }
-
- public boolean shouldSave() {
- return save;
- }
-
- public static class Builder {
- private MinecraftServer server;
- private ServerCommandSource commandSource;
- private ActionInitiator initiator;
- private boolean save;
- private String comment;
-
- private boolean guessInitiator;
-
- public Builder() {
- this.server = null;
- this.commandSource = null;
- this.initiator = null;
- this.save = false;
- this.comment = null;
-
- guessInitiator = false;
- }
-
- public static Builder newBackupContextBuilder() {
- return new Builder();
- }
-
- public Builder setCommandSource(ServerCommandSource commandSource) {
- this.commandSource = commandSource;
- return this;
- }
-
- public Builder setServer(MinecraftServer server) {
- this.server = server;
- return this;
- }
-
- public Builder setInitiator(ActionInitiator initiator) {
- this.initiator = initiator;
- return this;
- }
-
- public Builder setComment(String comment) {
- this.comment = comment;
- return this;
- }
-
- public Builder guessInitiator() {
- this.guessInitiator = true;
- return this;
- }
-
- public Builder saveServer() {
- this.save = true;
- return this;
- }
-
- public BackupContext build() {
- if (guessInitiator) {
- initiator = commandSource.getEntity() instanceof PlayerEntity ? ActionInitiator.Player : ActionInitiator.ServerConsole;
- } else if (initiator == null) {
- initiator = ActionInitiator.Null;
- }
-
- if (server == null) {
- if (commandSource != null) setServer(commandSource.getServer());
- else throw new RuntimeException("Neither MinecraftServer or ServerCommandSource were provided!");
- }
-
- return new BackupContext(server, commandSource, initiator, save, comment);
- }
- }
-}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/BackupScheduler.java b/src/main/java/net/szum123321/textile_backup/core/create/BackupScheduler.java
index fb54d37..55a13f3 100644
--- a/src/main/java/net/szum123321/textile_backup/core/create/BackupScheduler.java
+++ b/src/main/java/net/szum123321/textile_backup/core/create/BackupScheduler.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -53,14 +53,13 @@ public static void tick(MinecraftServer server) {
if(nextBackup <= now) {
//It's time to run
Globals.INSTANCE.getQueueExecutor().submit(
- MakeBackupRunnableFactory.create(
- BackupContext.Builder
- .newBackupContextBuilder()
- .setServer(server)
- .setInitiator(ActionInitiator.Timer)
- .saveServer()
- .build()
- )
+ ExecutableBackup.Builder
+ .newBackupContextBuilder()
+ .setServer(server)
+ .setInitiator(ActionInitiator.Timer)
+ .saveServer()
+ .announce()
+ .build()
);
nextBackup = now + config.get().backupInterval;
@@ -76,14 +75,13 @@ public static void tick(MinecraftServer server) {
if(scheduled && nextBackup <= now) {
//Verify we hadn't done the final one, and it's time to do so
Globals.INSTANCE.getQueueExecutor().submit(
- MakeBackupRunnableFactory.create(
- BackupContext.Builder
- .newBackupContextBuilder()
- .setServer(server)
- .setInitiator(ActionInitiator.Timer)
- .saveServer()
- .build()
- )
+ ExecutableBackup.Builder
+ .newBackupContextBuilder()
+ .setServer(server)
+ .setInitiator(ActionInitiator.Timer)
+ .saveServer()
+ .announce()
+ .build()
);
scheduled = false;
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/BrokenFileHandler.java b/src/main/java/net/szum123321/textile_backup/core/create/BrokenFileHandler.java
new file mode 100644
index 0000000..8e06585
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/create/BrokenFileHandler.java
@@ -0,0 +1,34 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core.create;
+
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BrokenFileHandler {
+ private final Map store = new HashMap<>();
+ public void handle(Path file, Exception e) { store.put(file, e); }
+
+ public boolean valid() { return store.isEmpty(); }
+
+ public Map get() {
+ return store;
+ }
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/ExecutableBackup.java b/src/main/java/net/szum123321/textile_backup/core/create/ExecutableBackup.java
new file mode 100644
index 0000000..f51e033
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/create/ExecutableBackup.java
@@ -0,0 +1,231 @@
+package net.szum123321.textile_backup.core.create;
+
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.command.ServerCommandSource;
+import net.szum123321.textile_backup.Globals;
+import net.szum123321.textile_backup.TextileBackup;
+import net.szum123321.textile_backup.TextileLogger;
+import net.szum123321.textile_backup.config.ConfigHelper;
+import net.szum123321.textile_backup.core.ActionInitiator;
+import net.szum123321.textile_backup.core.Cleanup;
+import net.szum123321.textile_backup.core.Utilities;
+import net.szum123321.textile_backup.core.create.compressors.ParallelZipCompressor;
+import net.szum123321.textile_backup.core.create.compressors.ZipCompressor;
+import net.szum123321.textile_backup.core.create.compressors.tar.AbstractTarArchiver;
+import net.szum123321.textile_backup.core.create.compressors.tar.ParallelGzipCompressor;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.util.NoSuchElementException;
+import java.util.concurrent.Callable;
+
+public record ExecutableBackup(@NotNull MinecraftServer server,
+ ServerCommandSource commandSource,
+ ActionInitiator initiator,
+ boolean save,
+ boolean cleanup,
+ String comment,
+ LocalDateTime startDate) implements Callable {
+
+ private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
+ private final static ConfigHelper config = ConfigHelper.INSTANCE;
+
+ public boolean startedByPlayer() {
+ return initiator == ActionInitiator.Player;
+ }
+
+ public void announce() {
+ if(config.get().broadcastBackupStart) {
+ Utilities.notifyPlayers(server,
+ "Warning! Server backup will begin shortly. You may experience some lag."
+ );
+ } else {
+ log.sendInfoAL(this, "Warning! Server backup will begin shortly. You may experience some lag.");
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("Backup started ");
+
+ builder.append(initiator.getPrefix());
+
+ if(startedByPlayer())
+ builder.append(commandSource.getDisplayName().getString());
+ else
+ builder.append(initiator.getName());
+
+ builder.append(" on: ");
+ builder.append(Utilities.getDateTimeFormatter().format(LocalDateTime.now()));
+
+ log.info(builder.toString());
+ }
+ @Override
+ public Void call() throws Exception {
+ if (save) { //save the world
+ log.sendInfoAL(this, "Saving server...");
+ server.saveAll(true, true, false);
+ }
+
+ Path outFile = Utilities.getBackupRootPath(Utilities.getLevelName(server)).resolve(getFileName());
+
+ log.trace("Outfile is: {}", outFile);
+
+ try {
+ //I think I should synchronise these two next calls...
+ Utilities.disableWorldSaving(server);
+ Globals.INSTANCE.disableWatchdog = true;
+
+ Globals.INSTANCE.updateTMPFSFlag(server);
+
+ log.sendInfoAL(this, "Starting backup");
+
+ Path world = Utilities.getWorldFolder(server);
+
+ log.trace("Minecraft world is: {}", world);
+
+ Files.createDirectories(outFile.getParent());
+ Files.createFile(outFile);
+
+ int coreCount;
+
+ if (config.get().compressionCoreCountLimit <= 0) coreCount = Runtime.getRuntime().availableProcessors();
+ else
+ coreCount = Math.min(config.get().compressionCoreCountLimit, Runtime.getRuntime().availableProcessors());
+
+ log.trace("Running compression on {} threads. Available cores: {}", coreCount, Runtime.getRuntime().availableProcessors());
+
+ switch (config.get().format) {
+ case ZIP -> {
+ if (coreCount > 1 && !Globals.INSTANCE.disableTMPFS()) {
+ log.trace("Using PARALLEL Zip Compressor. Threads: {}", coreCount);
+ ParallelZipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
+ } else {
+ log.trace("Using REGULAR Zip Compressor.");
+ ZipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
+ }
+ }
+ case GZIP -> ParallelGzipCompressor.getInstance().createArchive(world, outFile, this, coreCount);
+ case TAR -> new AbstractTarArchiver().createArchive(world, outFile, this, coreCount);
+ }
+
+ if(cleanup) new Cleanup(commandSource, Utilities.getLevelName(server)).call();
+
+ if (config.get().broadcastBackupDone) Utilities.notifyPlayers(server, "Done!");
+ else log.sendInfoAL(this, "Done!");
+
+ } catch (Throwable e) {
+ //ExecutorService swallows exception, so I need to catch everything
+ log.error("An exception occurred when trying to create a new backup file!", e);
+
+ if (ConfigHelper.INSTANCE.get().integrityVerificationMode.isStrict()) {
+ try {
+ Files.delete(outFile);
+ } catch (IOException ex) {
+ log.error("An exception occurred while trying go delete: {}", outFile, ex);
+ }
+ }
+
+ if (initiator == ActionInitiator.Player)
+ log.sendError(this, "An exception occurred when trying to create new backup file!");
+
+ throw e;
+ } finally {
+ Utilities.enableWorldSaving(server);
+ Globals.INSTANCE.disableWatchdog = false;
+ }
+
+ return null;
+ }
+
+ private String getFileName() {
+ return Utilities.getDateTimeFormatter().format(startDate) +
+ (comment != null ? "#" + comment.replaceAll("[\\\\/:*?\"<>|#]", "") : "") +
+ config.get().format.getCompleteString();
+ }
+ public static class Builder {
+ private MinecraftServer server;
+ private ServerCommandSource commandSource;
+ private ActionInitiator initiator;
+ private boolean save;
+ private boolean cleanup;
+ private String comment;
+ private boolean announce;
+
+ private boolean guessInitiator;
+
+ public Builder() {
+ this.server = null;
+ this.commandSource = null;
+ this.initiator = null;
+ this.save = false;
+ cleanup = true; //defaults
+ this.comment = null;
+ this.announce = false;
+
+ guessInitiator = false;
+ }
+
+ public static ExecutableBackup.Builder newBackupContextBuilder() {
+ return new ExecutableBackup.Builder();
+ }
+
+ public ExecutableBackup.Builder setCommandSource(ServerCommandSource commandSource) {
+ this.commandSource = commandSource;
+ return this;
+ }
+
+ public ExecutableBackup.Builder setServer(MinecraftServer server) {
+ this.server = server;
+ return this;
+ }
+
+ public ExecutableBackup.Builder setInitiator(ActionInitiator initiator) {
+ this.initiator = initiator;
+ return this;
+ }
+
+ public ExecutableBackup.Builder setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ public ExecutableBackup.Builder guessInitiator() {
+ this.guessInitiator = true;
+ return this;
+ }
+
+ public ExecutableBackup.Builder saveServer() {
+ this.save = true;
+ return this;
+ }
+
+ public ExecutableBackup.Builder noCleanup() {
+ this.cleanup = false;
+ return this;
+ }
+
+ public ExecutableBackup.Builder announce() {
+ this.announce = true;
+ return this;
+ }
+
+ public ExecutableBackup build() {
+ if (guessInitiator) {
+ initiator = Utilities.wasSentByPlayer(commandSource) ? ActionInitiator.Player : ActionInitiator.ServerConsole;
+ } else if (initiator == null) throw new NoSuchElementException("No initiator provided!");
+
+ if (server == null) {
+ if (commandSource != null) setServer(commandSource.getServer());
+ else throw new RuntimeException("Neither MinecraftServer or ServerCommandSource were provided!");
+ }
+
+ ExecutableBackup v = new ExecutableBackup(server, commandSource, initiator, save, cleanup, comment, LocalDateTime.now());
+
+ if(announce) v.announce();
+ return v;
+ }
+ }
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/FileInputStreamSupplier.java b/src/main/java/net/szum123321/textile_backup/core/create/FileInputStreamSupplier.java
new file mode 100644
index 0000000..2c43a25
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/create/FileInputStreamSupplier.java
@@ -0,0 +1,70 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core.create;
+
+import net.szum123321.textile_backup.TextileBackup;
+import net.szum123321.textile_backup.TextileLogger;
+import net.szum123321.textile_backup.core.digest.FileTreeHashBuilder;
+import net.szum123321.textile_backup.core.digest.HashingInputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Optional;
+
+public record FileInputStreamSupplier(Path path, String name, FileTreeHashBuilder hashTreeBuilder, BrokenFileHandler brokenFileHandler) implements InputSupplier {
+ private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ try {
+ return new HashingInputStream(Files.newInputStream(path), path, hashTreeBuilder, brokenFileHandler);
+ } catch (IOException e) {
+ //Probably good idea to just put it here. In the case an exception is thrown here, it could be possible
+ //The latch would have never been lifted
+ hashTreeBuilder.update(path, 0, 0);
+ brokenFileHandler.handle(path, e);
+ throw e;
+ }
+ }
+
+ @Override
+ public Optional getPath() { return Optional.of(path); }
+
+ @Override
+ public long size() throws IOException { return Files.size(path); }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public InputStream get() {
+ try {
+ return getInputStream();
+ } catch (IOException e) {
+ log.error("An exception occurred while trying to create an input stream from file: {}!", path.toString(), e);
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/InputSupplier.java b/src/main/java/net/szum123321/textile_backup/core/create/InputSupplier.java
new file mode 100644
index 0000000..76a40e7
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/create/InputSupplier.java
@@ -0,0 +1,35 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core.create;
+
+import org.apache.commons.compress.parallel.InputStreamSupplier;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.Optional;
+
+public interface InputSupplier extends InputStreamSupplier {
+ InputStream getInputStream() throws IOException;
+ //If an entry is virtual (a.k.a. there is no actual file to open, only input stream)
+ Optional getPath();
+ String getName();
+
+ long size() throws IOException;
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnable.java b/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnable.java
deleted file mode 100644
index f2184b9..0000000
--- a/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnable.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
-
-package net.szum123321.textile_backup.core.create;
-
-import net.szum123321.textile_backup.Globals;
-import net.szum123321.textile_backup.TextileBackup;
-import net.szum123321.textile_backup.TextileLogger;
-import net.szum123321.textile_backup.config.ConfigHelper;
-import net.szum123321.textile_backup.core.ActionInitiator;
-import net.szum123321.textile_backup.core.Cleanup;
-import net.szum123321.textile_backup.core.Utilities;
-import net.szum123321.textile_backup.core.create.compressors.ParallelZipCompressor;
-import net.szum123321.textile_backup.core.create.compressors.ZipCompressor;
-import net.szum123321.textile_backup.core.create.compressors.tar.AbstractTarArchiver;
-import net.szum123321.textile_backup.core.create.compressors.tar.ParallelBZip2Compressor;
-import net.szum123321.textile_backup.core.create.compressors.tar.ParallelGzipCompressor;
-import org.apache.commons.compress.compressors.lzma.LZMACompressorOutputStream;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.time.LocalDateTime;
-
-/**
- * The actual object responsible for creating the backup
- */
-public class MakeBackupRunnable implements Runnable {
- private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
- private final static ConfigHelper config = ConfigHelper.INSTANCE;
-
- private final BackupContext context;
-
- public MakeBackupRunnable(BackupContext context) {
- this.context = context;
- }
-
- @Override
- public void run() {
- try {
- Utilities.disableWorldSaving(context.server());
- Globals.INSTANCE.disableWatchdog = true;
-
- Globals.INSTANCE.updateTMPFSFlag(context.server());
-
- log.sendInfoAL(context, "Starting backup");
-
- Path world = Utilities.getWorldFolder(context.server());
-
- log.trace("Minecraft world is: {}", world);
-
- Path outFile = Utilities
- .getBackupRootPath(Utilities.getLevelName(context.server()))
- .resolve(getFileName());
-
- log.trace("Outfile is: {}", outFile);
-
- Files.createDirectories(outFile.getParent());
- Files.createFile(outFile);
-
- int coreCount;
-
- if(config.get().compressionCoreCountLimit <= 0) {
- coreCount = Runtime.getRuntime().availableProcessors();
- } else {
- coreCount = Math.min(config.get().compressionCoreCountLimit, Runtime.getRuntime().availableProcessors());
- }
-
- log.trace("Running compression on {} threads. Available cores: {}", coreCount, Runtime.getRuntime().availableProcessors());
-
- switch (config.get().format) {
- case ZIP -> {
- if (coreCount > 1 && !Globals.INSTANCE.disableTMPFS()) {
- log.trace("Using PARALLEL Zip Compressor. Threads: {}", coreCount);
- ParallelZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
- } else {
- log.trace("Using REGULAR Zip Compressor.");
- ZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
- }
- }
- case BZIP2 -> ParallelBZip2Compressor.getInstance().createArchive(world, outFile, context, coreCount);
- case GZIP -> ParallelGzipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
- case LZMA -> new AbstractTarArchiver() {
- protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
- return new LZMACompressorOutputStream(stream);
- }
- }.createArchive(world, outFile, context, coreCount);
- case TAR -> new AbstractTarArchiver().createArchive(world, outFile, context, coreCount);
- }
-
- new Cleanup(context.commandSource(), Utilities.getLevelName(context.server())).call();
-
- if(config.get().broadcastBackupDone) {
- Utilities.notifyPlayers(
- context.server(),
- "Done!"
- );
- } else {
- log.sendInfoAL(context, "Done!");
- }
- } catch (Throwable e) {
- //ExecutorService swallows exception, so I need to catch everything
- log.error("An exception occurred when trying to create new backup file!", e);
-
- if(context.initiator() == ActionInitiator.Player)
- log.sendError(context, "An exception occurred when trying to create new backup file!");
- } finally {
- Utilities.enableWorldSaving(context.server());
- Globals.INSTANCE.disableWatchdog = false;
- }
- }
-
- private String getFileName(){
- LocalDateTime now = LocalDateTime.now();
-
- return Utilities.getDateTimeFormatter().format(now) +
- (context.comment() != null ? "#" + context.comment().replaceAll("[\\\\/:*?\"<>|#]", "") : "") +
- config.get().format.getCompleteString();
- }
-}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnableFactory.java b/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnableFactory.java
deleted file mode 100644
index 36d8eb6..0000000
--- a/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnableFactory.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * A simple backup mod for Fabric
- * Copyright (C) 2022 Szum123321
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-
-package net.szum123321.textile_backup.core.create;
-
-import net.szum123321.textile_backup.TextileBackup;
-import net.szum123321.textile_backup.TextileLogger;
-import net.szum123321.textile_backup.config.ConfigHelper;
-import net.szum123321.textile_backup.core.Utilities;
-
-import java.time.LocalDateTime;
-
-public class MakeBackupRunnableFactory {
- private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
- private final static ConfigHelper config = ConfigHelper.INSTANCE;
-
- public static Runnable create(BackupContext ctx) {
- if(config.get().broadcastBackupStart) {
- Utilities.notifyPlayers(ctx.server(),
- "Warning! Server backup will begin shortly. You may experience some lag."
- );
- } else {
- log.sendInfoAL(ctx, "Warning! Server backup will begin shortly. You may experience some lag.");
- }
-
- StringBuilder builder = new StringBuilder();
-
- builder.append("Backup started ");
-
- builder.append(ctx.initiator().getPrefix());
-
- if(ctx.startedByPlayer())
- builder.append(ctx.commandSource().getDisplayName().getString());
- else
- builder.append(ctx.initiator().getName());
-
- builder.append(" on: ");
- builder.append(Utilities.getDateTimeFormatter().format(LocalDateTime.now()));
-
- log.info(builder.toString());
-
- if (ctx.shouldSave()) {
- log.sendInfoAL(ctx, "Saving server...");
-
- ctx.server().getPlayerManager().saveAllPlayerData();
-
- try {
- ctx.server().save(false, true, true);
- } catch (Exception e) {
- log.sendErrorAL(ctx,"An exception occurred when trying to save the world!");
- }
- }
-
- return new MakeBackupRunnable(ctx);
- }
-}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/AbstractCompressor.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/AbstractCompressor.java
index 45e5f65..01d3de1 100644
--- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/AbstractCompressor.java
+++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/AbstractCompressor.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,77 +18,96 @@
package net.szum123321.textile_backup.core.create.compressors;
+import net.szum123321.textile_backup.Globals;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.TextileLogger;
-import net.szum123321.textile_backup.core.ActionInitiator;
-import net.szum123321.textile_backup.core.NoSpaceLeftOnDeviceException;
-import net.szum123321.textile_backup.core.Utilities;
-import net.szum123321.textile_backup.core.create.BackupContext;
+import net.szum123321.textile_backup.config.ConfigHelper;
+import net.szum123321.textile_backup.core.*;
+import net.szum123321.textile_backup.core.create.BrokenFileHandler;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
+import net.szum123321.textile_backup.core.create.FileInputStreamSupplier;
+import net.szum123321.textile_backup.core.create.InputSupplier;
+import net.szum123321.textile_backup.core.digest.FileTreeHashBuilder;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
+import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.stream.Stream;
/**
- * Basic abstract class representing directory compressor
+ * Basic abstract class representing directory compressor with all the bells and whistles
*/
public abstract class AbstractCompressor {
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
- public void createArchive(Path inputFile, Path outputFile, BackupContext ctx, int coreLimit) {
+ public void createArchive(Path inputFile, Path outputFile, ExecutableBackup ctx, int coreLimit) throws IOException, ExecutionException, InterruptedException {
Instant start = Instant.now();
+ BrokenFileHandler brokenFileHandler = new BrokenFileHandler(); //Basically a hashmap storing files and their respective exceptions
+
try (OutputStream outStream = Files.newOutputStream(outputFile);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outStream);
OutputStream arc = createArchiveOutputStream(bufferedOutputStream, ctx, coreLimit);
Stream fileStream = Files.walk(inputFile)) {
- fileStream
+ var fileList = fileStream
.filter(path -> !Utilities.isBlacklisted(inputFile.relativize(path)))
- .filter(Files::isRegularFile).forEach(file -> {
- try {
- //hopefully one broken file won't spoil the whole archive
- addEntry(file, inputFile.relativize(file).toString(), arc);
- } catch (IOException e) {
- log.error("An exception occurred while trying to compress: {}", inputFile.relativize(file).toString(), e);
-
- if (ctx.initiator() == ActionInitiator.Player)
- log.sendError(ctx, "Something went wrong while compressing files!");
- }
- });
+ .filter(Files::isRegularFile)
+ .toList();
- finish(arc);
- } catch(NoSpaceLeftOnDeviceException e) {
- log.error("""
- CRITICAL ERROR OCCURRED!
- The backup is corrupt!
- Don't panic! This is a known issue!
- For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems
- In case this isn't it here's also the exception itself""", e);
-
- if(ctx.initiator() == ActionInitiator.Player) {
- log.sendError(ctx, "Backup failed. The file is corrupt.");
- log.error("For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems");
+ FileTreeHashBuilder fileHashBuilder = new FileTreeHashBuilder(fileList.size());
+
+ for (Path file : fileList) {
+ try {
+ addEntry(
+ new FileInputStreamSupplier(
+ file,
+ inputFile.relativize(file).toString(),
+ fileHashBuilder,
+ brokenFileHandler),
+ arc
+ );
+ } catch (IOException e) {
+ brokenFileHandler.handle(file, e);
+ fileHashBuilder.update(file, 0, 0);
+ //In Permissive mode we allow partial backups
+ if (ConfigHelper.INSTANCE.get().integrityVerificationMode.isStrict()) throw e;
+ else log.sendErrorAL(ctx, "An exception occurred while trying to compress: {}",
+ inputFile.relativize(file).toString(), e
+ );
+ }
}
- } catch (IOException | InterruptedException | ExecutionException e) {
- log.error("An exception occurred!", e);
- if(ctx.initiator() == ActionInitiator.Player)
- log.sendError(ctx, "Something went wrong while compressing files!");
+
+ arc.flush();
+
+ //wait for all the InputStreams to close/fail with InputSupplier
+
+ Instant now = Instant.now();
+
+ long treeHash = fileHashBuilder.getValue(true);
+ CompressionStatus status = new CompressionStatus (
+ treeHash,
+ brokenFileHandler.get(),
+ ctx.startDate(), start.toEpochMilli(), now.toEpochMilli(),
+ Globals.INSTANCE.getCombinedVersionString()
+ );
+
+ addEntry(new StatusFileInputSupplier(status.serialize()), arc);
+
+ finish(arc);
} finally {
close();
}
- // close();
-
log.sendInfoAL(ctx, "Compression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
}
- protected abstract OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException;
- protected abstract void addEntry(Path file, String entryName, OutputStream arc) throws IOException;
+ protected abstract OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException;
+ protected abstract void addEntry(InputSupplier inputSupplier, OutputStream arc) throws IOException;
protected void finish(OutputStream arc) throws InterruptedException, ExecutionException, IOException {
//This function is only needed for the ParallelZipCompressor to write out ParallelScatterZipCreator
@@ -97,4 +116,16 @@ protected void finish(OutputStream arc) throws InterruptedException, ExecutionEx
protected void close() {
//Same as above, just for ParallelGzipCompressor to shut down ExecutorService
}
-}
+
+ private record StatusFileInputSupplier(byte[] data) implements InputSupplier {
+ public InputStream getInputStream() { return new ByteArrayInputStream(data); }
+
+ public Optional getPath() { return Optional.empty(); }
+
+ public String getName() { return CompressionStatus.DATA_FILENAME; }
+
+ public long size() { return data.length; }
+
+ public InputStream get() { return getInputStream(); }
+ }
+ }
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/ParallelZipCompressor.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/ParallelZipCompressor.java
index 7933067..7ed67a1 100644
--- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/ParallelZipCompressor.java
+++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/ParallelZipCompressor.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,9 +21,9 @@
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.TextileLogger;
import net.szum123321.textile_backup.core.NoSpaceLeftOnDeviceException;
-import net.szum123321.textile_backup.core.create.BackupContext;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
+import net.szum123321.textile_backup.core.create.InputSupplier;
import org.apache.commons.compress.archivers.zip.*;
-import org.apache.commons.compress.parallel.InputStreamSupplier;
import java.io.*;
import java.nio.file.Files;
@@ -61,25 +61,32 @@ public static ParallelZipCompressor getInstance() {
}
@Override
- protected OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) {
+ protected OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) {
scatterZipCreator = new ParallelScatterZipCreator(Executors.newFixedThreadPool(coreLimit));
return super.createArchiveOutputStream(stream, ctx, coreLimit);
}
@Override
- protected void addEntry(Path file, String entryName, OutputStream arc) throws IOException {
- ZipArchiveEntry entry = (ZipArchiveEntry)((ZipArchiveOutputStream)arc).createArchiveEntry(file, entryName);
-
- if(ZipCompressor.isDotDat(file.getFileName().toString())) {
+ protected void addEntry(InputSupplier input, OutputStream arc) throws IOException {
+ ZipArchiveEntry entry;
+ if(input.getPath().isEmpty()) {
+ entry = new ZipArchiveEntry(input.getName());
entry.setMethod(ZipEntry.STORED);
- entry.setSize(Files.size(file));
- entry.setCompressedSize(Files.size(file));
- entry.setCrc(getCRC(file));
- } else entry.setMethod(ZipEntry.DEFLATED);
+ entry.setSize(input.size());
+ } else {
+ Path file = input.getPath().get();
+ entry = (ZipArchiveEntry) ((ZipArchiveOutputStream) arc).createArchiveEntry(file, input.getName());
+ if (ZipCompressor.isDotDat(file.toString())) {
+ entry.setMethod(ZipEntry.STORED);
+ entry.setSize(Files.size(file));
+ entry.setCompressedSize(Files.size(file));
+ entry.setCrc(getCRC(file));
+ } else entry.setMethod(ZipEntry.DEFLATED);
+ }
entry.setTime(System.currentTimeMillis());
- scatterZipCreator.addArchiveEntry(entry, new FileInputStreamSupplier(file));
+ scatterZipCreator.addArchiveEntry(entry, input);
}
@Override
@@ -97,10 +104,9 @@ This line causes the infamous Out of space error (#20 and #80)
boolean match = (cause.getStackTrace().length >= STACKTRACE_NO_SPACE_ON_LEFT_ON_DEVICE.length);
if(match) {
for(int i = 0; i < STACKTRACE_NO_SPACE_ON_LEFT_ON_DEVICE.length && match; i++)
- if(!STACKTRACE_NO_SPACE_ON_LEFT_ON_DEVICE[i].equals(cause.getStackTrace()[i])) match = false;
-
+ if(!STACKTRACE_NO_SPACE_ON_LEFT_ON_DEVICE[i].matches(cause.getStackTrace()[i])) match = false;
- //For clarity' sake let's not throw the ExecutionException itself rather only the cause, as the EE is just the wrapper
+ //For clarity's sake let's not throw the ExecutionException itself rather only the cause, as the EE is just the wrapper
if(match) throw new NoSpaceLeftOnDeviceException(cause);
}
}
@@ -114,29 +120,8 @@ private record SimpleStackTraceElement (
String methodName,
boolean isNative
) {
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null) return false;
- if(o.getClass() == StackTraceElement.class) {
- StackTraceElement that = (StackTraceElement) o;
- return (isNative == that.isNativeMethod()) && Objects.equals(className, that.getClassName()) && Objects.equals(methodName, that.getMethodName());
- }
- if(getClass() != o.getClass()) return false;
- SimpleStackTraceElement that = (SimpleStackTraceElement) o;
- return isNative == that.isNative && Objects.equals(className, that.className) && Objects.equals(methodName, that.methodName);
- }
- }
-
- record FileInputStreamSupplier(Path sourceFile) implements InputStreamSupplier {
- public InputStream get() {
- try {
- return Files.newInputStream(sourceFile);
- } catch (IOException e) {
- log.error("An exception occurred while trying to create an input stream from file: {}!", sourceFile.toString(), e);
- }
-
- return null;
+ public boolean matches(StackTraceElement o) {
+ return (isNative == o.isNativeMethod()) && Objects.equals(className, o.getClassName()) && Objects.equals(methodName, o.getMethodName());
}
}
}
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/ZipCompressor.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/ZipCompressor.java
index 0210708..a1b224b 100644
--- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/ZipCompressor.java
+++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/ZipCompressor.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,7 +20,8 @@
import net.szum123321.textile_backup.config.ConfigHelper;
import net.szum123321.textile_backup.core.Utilities;
-import net.szum123321.textile_backup.core.create.BackupContext;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
+import net.szum123321.textile_backup.core.create.InputSupplier;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
@@ -42,7 +43,7 @@ public static ZipCompressor getInstance() {
}
@Override
- protected OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) {
+ protected OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) {
ZipArchiveOutputStream arc = new ZipArchiveOutputStream(stream);
arc.setMethod(ZipArchiveOutputStream.DEFLATED);
@@ -54,15 +55,23 @@ protected OutputStream createArchiveOutputStream(OutputStream stream, BackupCont
}
@Override
- protected void addEntry(Path file, String entryName, OutputStream arc) throws IOException {
- try (InputStream fileInputStream = Files.newInputStream(file)){
- ZipArchiveEntry entry = (ZipArchiveEntry)((ZipArchiveOutputStream)arc).createArchiveEntry(file, entryName);
+ protected void addEntry(InputSupplier input, OutputStream arc) throws IOException {
+ try (InputStream fileInputStream = input.getInputStream()) {
+ ZipArchiveEntry entry;
- if(isDotDat(file.getFileName().toString())) {
+ if(input.getPath().isEmpty()) {
+ entry = new ZipArchiveEntry(input.getName());
entry.setMethod(ZipEntry.STORED);
- entry.setSize(Files.size(file));
- entry.setCompressedSize(Files.size(file));
- entry.setCrc(getCRC(file));
+ entry.setSize(input.size());
+ } else {
+ Path file = input.getPath().get();
+ entry = (ZipArchiveEntry) ((ZipArchiveOutputStream) arc).createArchiveEntry(file, input.getName());
+ if (isDotDat(file.toString())) {
+ entry.setMethod(ZipEntry.STORED);
+ entry.setSize(Files.size(file));
+ entry.setCompressedSize(Files.size(file));
+ entry.setCrc(getCRC(file));
+ } else entry.setMethod(ZipEntry.DEFLATED);
}
((ZipArchiveOutputStream)arc).putArchiveEntry(entry);
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/AbstractTarArchiver.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/AbstractTarArchiver.java
index 04489b4..52e86e5 100644
--- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/AbstractTarArchiver.java
+++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/AbstractTarArchiver.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,23 +18,22 @@
package net.szum123321.textile_backup.core.create.compressors.tar;
-import net.szum123321.textile_backup.core.create.BackupContext;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
import net.szum123321.textile_backup.core.create.compressors.AbstractCompressor;
+import net.szum123321.textile_backup.core.create.InputSupplier;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.Path;
public class AbstractTarArchiver extends AbstractCompressor {
- protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
+ protected OutputStream getCompressorOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException {
return stream;
}
@Override
- protected OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
+ protected OutputStream createArchiveOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException {
TarArchiveOutputStream tar = new TarArchiveOutputStream(getCompressorOutputStream(stream, ctx, coreLimit));
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
tar.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
@@ -43,9 +42,15 @@ protected OutputStream createArchiveOutputStream(OutputStream stream, BackupCont
}
@Override
- protected void addEntry(Path file, String entryName, OutputStream arc) throws IOException {
- try (InputStream fileInputStream = Files.newInputStream(file)){
- TarArchiveEntry entry = (TarArchiveEntry)((TarArchiveOutputStream) arc).createArchiveEntry(file, entryName);
+ protected void addEntry(InputSupplier input, OutputStream arc) throws IOException {
+ try (InputStream fileInputStream = input.getInputStream()) {
+ TarArchiveEntry entry;
+ if(input.getPath().isEmpty()) { //Virtual entry
+ entry = new TarArchiveEntry(input.getName());
+ entry.setSize(input.size());
+ } else
+ entry = (TarArchiveEntry)((TarArchiveOutputStream) arc).createArchiveEntry(input.getPath().get(), input.getName());
+
((TarArchiveOutputStream)arc).putArchiveEntry(entry);
IOUtils.copy(fileInputStream, arc);
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/ParallelBZip2Compressor.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/ParallelBZip2Compressor.java
index e3e9e05..2bd2840 100644
--- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/ParallelBZip2Compressor.java
+++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/ParallelBZip2Compressor.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
package net.szum123321.textile_backup.core.create.compressors.tar;
-import net.szum123321.textile_backup.core.create.BackupContext;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
import org.at4j.comp.bzip2.BZip2OutputStream;
import org.at4j.comp.bzip2.BZip2OutputStreamSettings;
@@ -30,7 +30,7 @@ public static ParallelBZip2Compressor getInstance() {
}
@Override
- protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
+ protected OutputStream getCompressorOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException {
return new BZip2OutputStream(stream, new BZip2OutputStreamSettings().setNumberOfEncoderThreads(coreLimit));
}
}
\ No newline at end of file
diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/ParallelGzipCompressor.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/ParallelGzipCompressor.java
index dcf9f1c..94b9da6 100644
--- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/ParallelGzipCompressor.java
+++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/tar/ParallelGzipCompressor.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
package net.szum123321.textile_backup.core.create.compressors.tar;
-import net.szum123321.textile_backup.core.create.BackupContext;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
import org.anarres.parallelgzip.ParallelGZIPOutputStream;
import java.io.*;
@@ -33,7 +33,7 @@ public static ParallelGzipCompressor getInstance() {
}
@Override
- protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
+ protected OutputStream getCompressorOutputStream(OutputStream stream, ExecutableBackup ctx, int coreLimit) throws IOException {
executorService = Executors.newFixedThreadPool(coreLimit);
return new ParallelGZIPOutputStream(stream, executorService);
diff --git a/src/main/java/net/szum123321/textile_backup/core/digest/BalticHash.java b/src/main/java/net/szum123321/textile_backup/core/digest/BalticHash.java
new file mode 100644
index 0000000..14a6050
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/digest/BalticHash.java
@@ -0,0 +1,100 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core.digest;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * This algorithm copies the construction of SeaHash including its IV.
+ * What it differs in is that it uses Xoroshift64* instead of PCG as its pseudo-random function. Although it might lower
+ * the output quality, I don't think it matters that much, honestly. One advantage of xoroshift is that it should be
+ * easier to implement with AVX. Java should soon ship its vector api by default.
+ */
+public class BalticHash implements Hash {
+ //SeaHash IV
+ protected final static long[] IV = { 0x16f11fe89b0d677cL, 0xb480a793d8e6c86cL, 0x6fe2e5aaf078ebc9L, 0x14f994a4c5259381L };
+ private final long[] state = Arrays.copyOf(IV, IV.length);
+ protected final int buffer_limit = state.length * Long.BYTES;
+ protected final byte[] _byte_buffer = new byte[(state.length + 1) * Long.BYTES];
+ //Enforce endianness
+ protected final ByteBuffer buffer = ByteBuffer.wrap(_byte_buffer).order(ByteOrder.LITTLE_ENDIAN);
+ protected long hashed_data_length = 0;
+
+ public void update(int b) {
+ buffer.put((byte)b);
+ hashed_data_length += 1;
+ if (buffer.position() >= buffer_limit) round();
+ }
+
+ public void update(long b) {
+ buffer.putLong(b);
+ hashed_data_length += Long.BYTES;
+ if(buffer.position() >= buffer_limit) round();
+ }
+
+ public void update(byte[] data, int off, int len) {
+ int pos = 0;
+ while(pos < len) {
+ int n = Math.min(len - pos, buffer_limit - buffer.position());
+ System.arraycopy(data, off + pos, _byte_buffer, buffer.position(), n);
+ pos += n;
+ buffer.position(buffer.position() + n);
+ if(buffer.position() >= buffer_limit) round();
+ }
+
+ hashed_data_length += len;
+ }
+
+ public long getValue() {
+ if(buffer.position() != 0) {
+ while(buffer.position() < buffer_limit) buffer.put((byte)0);
+ round();
+ }
+
+ long result = state[0];
+ result ^= state[1];
+ result ^= state[2];
+ result ^= state[3];
+ result ^= hashed_data_length;
+
+ return xorshift64star(result);
+ }
+
+ protected void round() {
+ int p = buffer.position();
+ buffer.rewind();
+
+ for(int i = 0; i < 4; i++) state[i] ^= buffer.getLong();
+ for(int i = 0; i < 4; i++) state[i] = xorshift64star(state[i]);
+
+ if(p > buffer_limit) {
+ System.arraycopy(_byte_buffer, buffer_limit, _byte_buffer, 0, buffer.limit() - p);
+ buffer.position(buffer.limit() - p);
+ } else buffer.rewind();
+ }
+
+ long xorshift64star(long s) {
+ s ^= (s >> 12);
+ s ^= (s << 25);
+ s ^= (s >> 27);
+ return s * 0x2545F4914F6CDD1DL;
+ }
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/digest/FileTreeHashBuilder.java b/src/main/java/net/szum123321/textile_backup/core/digest/FileTreeHashBuilder.java
new file mode 100644
index 0000000..64c03b4
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/digest/FileTreeHashBuilder.java
@@ -0,0 +1,73 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core.digest;
+
+import net.szum123321.textile_backup.Globals;
+import net.szum123321.textile_backup.TextileBackup;
+import net.szum123321.textile_backup.TextileLogger;
+import net.szum123321.textile_backup.core.CompressionStatus;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * What this class does is it collects the hashed files and combines them into a single number,
+ * thus we can verify file tree integrity
+ */
+public class FileTreeHashBuilder {
+ private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
+ private final Object lock = new Object();
+ private long hash = 0, filesProcessed = 0, filesTotalSize = 0;
+
+ private final CountDownLatch latch;
+
+ public FileTreeHashBuilder(int filesToProcess) {
+ latch = new CountDownLatch(filesToProcess);
+ }
+
+ public void update(Path path, long newHash, long bytes) throws IOException {
+ if(path.getFileName().toString().equals(CompressionStatus.DATA_FILENAME)) return;
+
+ latch.countDown();
+
+ synchronized (lock) {
+ this.hash ^= newHash;
+ filesTotalSize += bytes;
+ filesProcessed++;
+ }
+ }
+
+ public int getRemaining() { return (int) latch.getCount(); }
+
+ public long getValue(boolean lock) throws InterruptedException {
+ long leftover = latch.getCount();
+ if(lock) latch.await();
+ else if(leftover != 0) log.warn("Finishing with {} files unprocessed!", leftover);
+
+ var hasher = Globals.CHECKSUM_SUPPLIER.get();
+
+ log.debug("Closing: files: {}, bytes {}, raw hash {}", filesProcessed, filesTotalSize, hash);
+ hasher.update(hash);
+ hasher.update(filesProcessed);
+ hasher.update(filesTotalSize);
+
+ return hasher.getValue();
+ }
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/digest/Hash.java b/src/main/java/net/szum123321/textile_backup/core/digest/Hash.java
new file mode 100644
index 0000000..c601cb8
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/digest/Hash.java
@@ -0,0 +1,32 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core.digest;
+
+public interface Hash {
+
+ void update(int b);
+
+ void update(long b);
+
+ default void update(byte[] b) { update(b, 0, b.length); }
+
+ void update(byte[] b, int off, int len);
+
+ long getValue();
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/digest/HashingInputStream.java b/src/main/java/net/szum123321/textile_backup/core/digest/HashingInputStream.java
new file mode 100644
index 0000000..a288b25
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/digest/HashingInputStream.java
@@ -0,0 +1,88 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core.digest;
+
+import net.szum123321.textile_backup.Globals;
+import net.szum123321.textile_backup.core.DataLeftException;
+import net.szum123321.textile_backup.core.create.BrokenFileHandler;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+
+/**
+ * This class calculates a hash of the file on the input stream, submits it to FileTreeHashBuilder.
+ * In case the underlying stream hasn't been read completely in, puts it into BrokeFileHandler
+
+ * Furthermore, ParallelZip works by putting all the file requests into a queue and then compressing them
+ * with multiple threads. Thus, we have to make sure that all the files have been read before requesting the final value
+ * That is what CountDownLatch does
+ */
+public class HashingInputStream extends FilterInputStream {
+ private final Path path;
+ private final Hash hash = Globals.CHECKSUM_SUPPLIER.get();
+ private final FileTreeHashBuilder hashBuilder;
+ private final BrokenFileHandler brokenFileHandler;
+
+ private long bytesWritten = 0;
+
+ public HashingInputStream(InputStream in, Path path, FileTreeHashBuilder hashBuilder, BrokenFileHandler brokenFileHandler) {
+ super(in);
+ this.path = path;
+ this.hashBuilder = hashBuilder;
+ this.brokenFileHandler = brokenFileHandler;
+ }
+
+ @Override
+ public int read(byte @NotNull [] b, int off, int len) throws IOException {
+ int i = in.read(b, off, len);
+ if(i != -1) {
+ hash.update(b, off, i);
+ bytesWritten += i;
+ }
+ return i;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int i = in.read();
+ if(i != -1) {
+ hash.update(i);
+ bytesWritten++;
+ }
+ return i;
+ }
+
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ @Override
+ public void close() throws IOException {
+ hash.update(path.getFileName().toString().getBytes(StandardCharsets.UTF_8));
+
+ hashBuilder.update(path, hash.getValue(), bytesWritten);
+
+ if(in.available() != 0) brokenFileHandler.handle(path, new DataLeftException(in.available()));
+
+ super.close();
+ }
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/digest/HashingOutputStream.java b/src/main/java/net/szum123321/textile_backup/core/digest/HashingOutputStream.java
new file mode 100644
index 0000000..0cb15ca
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/core/digest/HashingOutputStream.java
@@ -0,0 +1,63 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.core.digest;
+
+import net.szum123321.textile_backup.Globals;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+
+public class HashingOutputStream extends FilterOutputStream {
+ private final Path path;
+ private final Hash hash = Globals.CHECKSUM_SUPPLIER.get();
+ private final FileTreeHashBuilder hashBuilder;
+
+ private long bytesWritten = 0;
+
+ public HashingOutputStream(OutputStream out, Path path, FileTreeHashBuilder hashBuilder) {
+ super(out);
+ this.path = path;
+ this.hashBuilder = hashBuilder;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ out.write(b);
+ hash.update(b);
+ bytesWritten++;
+ }
+
+ @Override
+ public void write(byte @NotNull [] b, int off, int len) throws IOException {
+ out.write(b, off, len);
+ hash.update(b, off, len);
+ bytesWritten += len;
+ }
+
+ @Override
+ public void close() throws IOException {
+ hash.update(path.getFileName().toString().getBytes(StandardCharsets.UTF_8));
+ hashBuilder.update(path, hash.getValue(), bytesWritten);
+ super.close();
+ }
+}
diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/AwaitThread.java b/src/main/java/net/szum123321/textile_backup/core/restore/AwaitThread.java
index c022092..c728d5e 100644
--- a/src/main/java/net/szum123321/textile_backup/core/restore/AwaitThread.java
+++ b/src/main/java/net/szum123321/textile_backup/core/restore/AwaitThread.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreBackupRunnable.java b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreBackupRunnable.java
index f2dd22d..b3520fc 100644
--- a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreBackupRunnable.java
+++ b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreBackupRunnable.java
@@ -1,20 +1,20 @@
/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package net.szum123321.textile_backup.core.restore;
@@ -24,18 +24,22 @@
import net.szum123321.textile_backup.config.ConfigHelper;
import net.szum123321.textile_backup.config.ConfigPOJO;
import net.szum123321.textile_backup.core.ActionInitiator;
-import net.szum123321.textile_backup.core.LivingServer;
+import net.szum123321.textile_backup.core.CompressionStatus;
import net.szum123321.textile_backup.core.Utilities;
-import net.szum123321.textile_backup.core.create.BackupContext;
-import net.szum123321.textile_backup.core.create.MakeBackupRunnableFactory;
+import net.szum123321.textile_backup.core.create.ExecutableBackup;
import net.szum123321.textile_backup.core.restore.decompressors.GenericTarDecompressor;
import net.szum123321.textile_backup.core.restore.decompressors.ZipDecompressor;
+import net.szum123321.textile_backup.mixin.MinecraftServerSessionAccessor;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Optional;
+import java.util.concurrent.FutureTask;
-//TODO: Verify backup's validity?
+/**
+ * This class restores a file provided by RestoreContext.
+ */
public class RestoreBackupRunnable implements Runnable {
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
private final static ConfigHelper config = ConfigHelper.INSTANCE;
@@ -53,45 +57,97 @@ public void run() {
log.info("Shutting down server...");
ctx.server().stop(false);
- awaitServerShutdown();
-
- if(config.get().backupOldWorlds) {
- MakeBackupRunnableFactory.create(
- BackupContext.Builder
- .newBackupContextBuilder()
- .setServer(ctx.server())
- .setInitiator(ActionInitiator.Restore)
- .setComment("Old_World" + (ctx.comment() != null ? "_" + ctx.comment() : ""))
- .build()
- ).run();
- }
- Path worldFile = Utilities.getWorldFolder(ctx.server());
+ Path worldFile = Utilities.getWorldFolder(ctx.server()),
+ tmp;
try {
- Path tmp = Files.createTempDirectory(
- worldFile.getParent(),
- ctx.restoreableFile().getFile().getFileName().toString());
+ tmp = Files.createTempDirectory(
+ ctx.server().getRunDirectory().toPath(),
+ ctx.restoreableFile().getFile().getFileName().toString()
+ );
+ } catch (IOException e) {
+ log.error("An exception occurred while unpacking backup", e);
+ return;
+ }
+ //By making a separate thread we can start unpacking an old backup instantly
+ //Let the server shut down gracefully, and wait for the old world backup to complete
+ FutureTask waitForShutdown = new FutureTask<>(() -> {
+ ctx.server().getThread().join(); //wait for server thread to die and save all its state
+
+ if(config.get().backupOldWorlds) {
+ return ExecutableBackup.Builder
+ .newBackupContextBuilder()
+ .setServer(ctx.server())
+ .setInitiator(ActionInitiator.Restore)
+ .noCleanup()
+ .setComment("Old_World" + (ctx.comment() != null ? "_" + ctx.comment() : ""))
+ .announce()
+ .build().call();
+ }
+ return null;
+ });
+
+ //run the thread.
+ new Thread(waitForShutdown, "Server shutdown wait thread").start();
+
+ try {
log.info("Starting decompression...");
+ long hash;
+
if (ctx.restoreableFile().getArchiveFormat() == ConfigPOJO.ArchiveFormat.ZIP)
- ZipDecompressor.decompress(ctx.restoreableFile().getFile(), tmp);
+ hash = ZipDecompressor.decompress(ctx.restoreableFile().getFile(), tmp);
else
- GenericTarDecompressor.decompress(ctx.restoreableFile().getFile(), tmp);
+ hash = GenericTarDecompressor.decompress(ctx.restoreableFile().getFile(), tmp);
+
+ log.info("Waiting for server to fully terminate...");
+
+ //locks until the backup is finished and the server is dead
+ waitForShutdown.get();
- log.info("Deleting old world...");
+ Optional errorMsg;
- Utilities.deleteDirectory(worldFile);
- Files.move(tmp, worldFile);
+ if(Files.notExists(CompressionStatus.resolveStatusFilename(tmp))) {
+ errorMsg = Optional.of("Status file not found!");
+ } else {
+ CompressionStatus status = CompressionStatus.readFromFile(tmp);
- if (config.get().deleteOldBackupAfterRestore) {
- log.info("Deleting old backup");
+ log.info("Status: {}", status);
- Files.delete(ctx.restoreableFile().getFile());
+ Files.delete(tmp.resolve(CompressionStatus.DATA_FILENAME));
+
+ errorMsg = status.validate(hash, ctx);
}
- } catch (IOException e) {
+
+ if(errorMsg.isEmpty() || !config.get().integrityVerificationMode.verify()) {
+ if (errorMsg.isEmpty()) log.info("Backup valid. Restoring");
+ else log.info("Backup is damaged, but verification is disabled [{}]. Restoring", errorMsg.get());
+
+ //Disables write lock to override world file
+ ((MinecraftServerSessionAccessor) ctx.server()).getSession().close();
+
+ Utilities.deleteDirectory(worldFile);
+ Files.move(tmp, worldFile);
+
+ if (config.get().deleteOldBackupAfterRestore) {
+ log.info("Deleting restored backup file");
+ Files.delete(ctx.restoreableFile().getFile());
+ }
+ } else {
+ log.error(errorMsg.get());
+ }
+
+ } catch (Exception e) {
log.error("An exception occurred while trying to restore a backup!", e);
+ } finally {
+ //Regardless of what happened, we should still clean up
+ if(Files.exists(tmp)) {
+ try {
+ Utilities.deleteDirectory(tmp);
+ } catch (IOException ignored) {}
+ }
}
//in case we're playing on client
@@ -99,14 +155,4 @@ public void run() {
log.info("Done!");
}
-
- private void awaitServerShutdown() {
- while(((LivingServer)ctx.server()).isAlive()) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- log.error("Exception occurred!", e);
- }
- }
- }
}
\ No newline at end of file
diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreContext.java b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreContext.java
index f5391a5..e0ac528 100644
--- a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreContext.java
+++ b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreContext.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2020 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreHelper.java b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreHelper.java
index 075e9fd..a0bf092 100644
--- a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreHelper.java
+++ b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreHelper.java
@@ -1,20 +1,20 @@
/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package net.szum123321.textile_backup.core.restore;
diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/GenericTarDecompressor.java b/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/GenericTarDecompressor.java
index de4696c..8d06c34 100644
--- a/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/GenericTarDecompressor.java
+++ b/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/GenericTarDecompressor.java
@@ -1,26 +1,28 @@
/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package net.szum123321.textile_backup.core.restore.decompressors;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.TextileLogger;
+import net.szum123321.textile_backup.core.digest.FileTreeHashBuilder;
import net.szum123321.textile_backup.core.Utilities;
+import net.szum123321.textile_backup.core.digest.HashingOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.CompressorException;
@@ -36,8 +38,9 @@
public class GenericTarDecompressor {
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
- public static void decompress(Path input, Path target) throws IOException {
+ public static long decompress(Path input, Path target) throws IOException {
Instant start = Instant.now();
+ FileTreeHashBuilder treeBuilder = new FileTreeHashBuilder(0);
try (InputStream fileInputStream = Files.newInputStream(input);
InputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
@@ -46,10 +49,8 @@ public static void decompress(Path input, Path target) throws IOException {
TarArchiveEntry entry;
while ((entry = archiveInputStream.getNextTarEntry()) != null) {
- if(!archiveInputStream.canReadEntryData(entry)) {
- log.error("Something when wrong while trying to decompress {}", entry.getName());
- continue;
- }
+ if(!archiveInputStream.canReadEntryData(entry))
+ throw new IOException("Couldn't read archive entry! " + entry.getName());
Path file = target.resolve(entry.getName());
@@ -58,8 +59,8 @@ public static void decompress(Path input, Path target) throws IOException {
} else {
Files.createDirectories(file.getParent());
try (OutputStream outputStream = Files.newOutputStream(file);
- BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
- IOUtils.copy(archiveInputStream, bufferedOutputStream);
+ HashingOutputStream out = new HashingOutputStream(outputStream, file, treeBuilder)) {
+ IOUtils.copy(archiveInputStream, out);
}
}
}
@@ -68,6 +69,12 @@ public static void decompress(Path input, Path target) throws IOException {
}
log.info("Decompression took {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
+
+ try {
+ return treeBuilder.getValue(false);
+ } catch (InterruptedException ignored) {
+ return 0;
+ }
}
private static InputStream getCompressorInputStream(InputStream inputStream) throws CompressorException {
diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/ZipDecompressor.java b/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/ZipDecompressor.java
index 0e12347..996f38b 100644
--- a/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/ZipDecompressor.java
+++ b/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/ZipDecompressor.java
@@ -1,26 +1,28 @@
/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
package net.szum123321.textile_backup.core.restore.decompressors;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.TextileLogger;
+import net.szum123321.textile_backup.core.digest.FileTreeHashBuilder;
import net.szum123321.textile_backup.core.Utilities;
+import net.szum123321.textile_backup.core.digest.HashingOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.utils.IOUtils;
@@ -35,9 +37,11 @@
public class ZipDecompressor {
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
- public static void decompress(Path inputFile, Path target) throws IOException {
+ public static long decompress(Path inputFile, Path target) throws IOException {
Instant start = Instant.now();
+ FileTreeHashBuilder hashBuilder = new FileTreeHashBuilder(0);
+
try(ZipFile zipFile = new ZipFile(inputFile.toFile())) {
for (Iterator it = zipFile.getEntries().asIterator(); it.hasNext(); ) {
ZipArchiveEntry entry = it.next();
@@ -48,13 +52,21 @@ public static void decompress(Path inputFile, Path target) throws IOException {
} else {
Files.createDirectories(file.getParent());
try (OutputStream outputStream = Files.newOutputStream(file);
- BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
- IOUtils.copy(zipFile.getInputStream(entry), bufferedOutputStream);
+ HashingOutputStream out = new HashingOutputStream(outputStream, file, hashBuilder);
+ InputStream in = zipFile.getInputStream(entry)) {
+
+ IOUtils.copy(in, out);
}
}
}
}
log.info("Decompression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
+
+ try {
+ return hashBuilder.getValue(false);
+ } catch (InterruptedException ignored) {
+ return 0;
+ }
}
}
diff --git a/src/main/java/net/szum123321/textile_backup/mixin/DedicatedServerWatchdogMixin.java b/src/main/java/net/szum123321/textile_backup/mixin/DedicatedServerWatchdogMixin.java
index 415939c..5c15842 100644
--- a/src/main/java/net/szum123321/textile_backup/mixin/DedicatedServerWatchdogMixin.java
+++ b/src/main/java/net/szum123321/textile_backup/mixin/DedicatedServerWatchdogMixin.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerMixin.java b/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerMixin.java
deleted file mode 100644
index 758ba84..0000000
--- a/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerMixin.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- A simple backup mod for Fabric
- Copyright (C) 2020 Szum123321
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-*/
-
-package net.szum123321.textile_backup.mixin;
-
-import net.minecraft.server.MinecraftServer;
-import net.szum123321.textile_backup.core.LivingServer;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Unique;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-@Mixin(MinecraftServer.class)
-public class MinecraftServerMixin implements LivingServer {
- @Unique
- private boolean isAlive = true;
-
- @Inject(method = "shutdown", at = @At("TAIL"))
- public void onFinalWorldSave(CallbackInfo ci) {
- isAlive = false;
- }
-
- @Unique
- @Override
- public boolean isAlive() {
- return isAlive;
- }
-}
diff --git a/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerSessionAccessor.java b/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerSessionAccessor.java
index 91a8696..36f099b 100644
--- a/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerSessionAccessor.java
+++ b/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerSessionAccessor.java
@@ -1,6 +1,6 @@
/*
* A simple backup mod for Fabric
- * Copyright (C) 2021 Szum123321
+ * Copyright (C) 2022 Szum123321
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/main/java/net/szum123321/textile_backup/test/BalticHashTest.java b/src/main/java/net/szum123321/textile_backup/test/BalticHashTest.java
new file mode 100644
index 0000000..55300fd
--- /dev/null
+++ b/src/main/java/net/szum123321/textile_backup/test/BalticHashTest.java
@@ -0,0 +1,63 @@
+/*
+ * A simple backup mod for Fabric
+ * Copyright (C) 2022 Szum123321
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package net.szum123321.textile_backup.test;
+
+import net.minecraft.util.math.random.Random;
+import net.szum123321.textile_backup.TextileBackup;
+import net.szum123321.textile_backup.TextileLogger;
+import net.szum123321.textile_backup.core.digest.BalticHash;
+
+public class BalticHashTest {
+ private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
+ final static int TEST_LEN = 21377; //simple prime
+ public static void run() throws RuntimeException {
+ log.info("Running hash test");
+ Random r = Random.create(2137);
+ long x = 0;
+
+ byte[] data = new byte[TEST_LEN];
+
+ for(int i = 0; i < TEST_LEN; i++) data[i] = (byte)r.nextInt();
+
+ //Test block mode
+ for(int i = 0; i < 5*2; i++) x ^= randomHash(data, r);
+ if(x != 0) throw new RuntimeException("Hash mismatch!");
+
+ log.info("Test passed");
+ }
+
+ static long randomHash(byte[] data, Random r) {
+ int n = data.length;
+
+ BalticHash h = new BalticHash();
+
+ int m = r.nextBetween(1, n);
+
+ int nn = n, p = 0;
+
+ for(int i = 0; i < m; i++) {
+ int k = r.nextBetween(1, nn - (m - i - 1));
+ h.update(data, p, k);
+ p += k;
+ nn -= k;
+ }
+
+ return h.getValue();
+ }
+}
diff --git a/src/main/resources/assets/textile_backup/lang/en_us.json b/src/main/resources/assets/textile_backup/lang/en_us.json
index 7f92d1b..39b3aa3 100644
--- a/src/main/resources/assets/textile_backup/lang/en_us.json
+++ b/src/main/resources/assets/textile_backup/lang/en_us.json
@@ -47,6 +47,9 @@
"text.autoconfig.textile_backup.option.format": "Archive and compression format",
"text.autoconfig.textile_backup.option.format.@Tooltip": "See: https://github.com/Szum123321/textile_backup/wiki/Configuration#format",
+ "text.autoconfig.textile_backup.option.integrityVerificationMode": "Verify backup integrity",
+ "text.autoconfig.textile_backup.option.integrityVerificationMode.@Tooltip": "DO NOT ALTER unless fully aware of consequences",
+
"text.autoconfig.textile_backup.option.permissionLevel": "Min permission level",
"text.autoconfig.textile_backup.option.alwaysSingleplayerAllowed": "Always allow on single-player",
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index 2366667..3b349e6 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -38,9 +38,9 @@
],
"depends": {
- "fabricloader": ">=0.14.6",
+ "fabricloader": ">=0.14.0",
"fabric": "*",
- "minecraft": ">=1.19.1",
+ "minecraft": "^1.20-",
"cloth-config2": "*",
"java": ">=16"
},
diff --git a/src/main/resources/textile_backup.mixins.json b/src/main/resources/textile_backup.mixins.json
index 82c2d00..6222ec9 100644
--- a/src/main/resources/textile_backup.mixins.json
+++ b/src/main/resources/textile_backup.mixins.json
@@ -4,7 +4,6 @@
"compatibilityLevel": "JAVA_16",
"mixins": [
"DedicatedServerWatchdogMixin",
- "MinecraftServerMixin",
"MinecraftServerSessionAccessor"
],
"client": [
diff --git a/src/test/java/net/szum123321/test/textile_backup/TextileBackupTest.java b/src/test/java/net/szum123321/test/textile_backup/TextileBackupTest.java
deleted file mode 100644
index bd646f2..0000000
--- a/src/test/java/net/szum123321/test/textile_backup/TextileBackupTest.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package net.szum123321.test.textile_backup;
-
-import net.fabricmc.api.ModInitializer;
-
-public class TextileBackupTest implements ModInitializer {
- @Override
- public void onInitialize() {
-
- }
-}
diff --git a/src/test/resources/fabric.mod.json b/src/test/resources/fabric.mod.json
deleted file mode 100644
index 809dace..0000000
--- a/src/test/resources/fabric.mod.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "schemaVersion": 1,
- "id": "textile_backup",
- "version": "${version}",
-
- "name": "Textile Backup Test",
- "authors": [
- "Szum123321"
- ],
- "contact": {
- "homepage": "https://www.curseforge.com/minecraft/mc-mods/textile-backup",
- "issues": "https://github.com/Szum123321/textile_backup/issues",
- "sources": "https://github.com/Szum123321/textile_backup"
- },
-
- "license": "GPLv3",
-
- "environment": "*",
- "entrypoints": {
- "main": [
- "net.szum123321.test.textile_backup.TextileBackupTest"
- ]
- },
- "mixins": [
- ],
-
- "depends": {
- "fabricloader": ">=0.14.6",
- "fabric": "*",
- "minecraft": ">=1.19.1",
- "cloth-config2": "*",
- "java": ">=16",
- "textile_backup": "*"
- }
-}
\ No newline at end of file
diff --git a/src/test/resources/textile_backup-test.mixins.json b/src/test/resources/textile_backup-test.mixins.json
deleted file mode 100644
index f858f63..0000000
--- a/src/test/resources/textile_backup-test.mixins.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "required": true,
- "package": "net.szum123321.test.textile_backup.mixin",
- "compatibilityLevel": "JAVA_16",
- "mixins": [
- ],
- "client": [
- ],
- "injectors": {
- "defaultRequire": 1
- }
-}