diff --git a/LaunchServer/source/LaunchServer.java b/LaunchServer/source/LaunchServer.java index 53ed341..b1f718e 100644 --- a/LaunchServer/source/LaunchServer.java +++ b/LaunchServer/source/LaunchServer.java @@ -281,7 +281,7 @@ // Sync and sign update dir LogHelper.subInfo("Syncing '%s' update dir", name); - HashedDir updateHDir = new HashedDir(updateDir, null); + HashedDir updateHDir = new HashedDir(updateDir, null, true); newUpdatesDirMap.put(name, new SignedObjectHolder<>(updateHDir, privateKey)); } } diff --git a/Launcher/source/client/ClientLauncher.java b/Launcher/source/client/ClientLauncher.java index fa68912..42b01fe 100644 --- a/Launcher/source/client/ClientLauncher.java +++ b/Launcher/source/client/ClientLauncher.java @@ -199,7 +199,7 @@ } // Hash directory and compare (ignore update-only matcher entries, it will break offline-mode) - HashedDir currentHDir = new HashedDir(dir, matcher); + HashedDir currentHDir = new HashedDir(dir, matcher, false); if (!hdir.diff(currentHDir, matcher).isSame()) { throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir))); } diff --git a/Launcher/source/hasher/DirWatcher.java b/Launcher/source/hasher/DirWatcher.java index 69ae552..d165135 100644 --- a/Launcher/source/hasher/DirWatcher.java +++ b/Launcher/source/hasher/DirWatcher.java @@ -15,6 +15,7 @@ import java.util.LinkedList; import java.util.Objects; +import com.sun.nio.file.ExtendedWatchEventModifier; import com.sun.nio.file.SensitivityWatchEventModifier; import launcher.LauncherAPI; import launcher.helper.IOHelper; @@ -22,7 +23,13 @@ import launcher.helper.LogHelper; public final class DirWatcher implements Runnable, AutoCloseable { + private static final boolean FILE_TREE_SUPPORTED = JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE; + + // Constants private static final WatchEvent.Modifier[] MODIFIERS = { SensitivityWatchEventModifier.HIGH }; + private static final WatchEvent.Modifier[] FILE_TREE_MODIFIERS = { + ExtendedWatchEventModifier.FILE_TREE, SensitivityWatchEventModifier.HIGH + }; private static final WatchEvent.Kind[] KINDS = { StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE }; @@ -38,9 +45,15 @@ this.dir = Objects.requireNonNull(dir, "dir"); this.hdir = Objects.requireNonNull(hdir, "hdir"); this.matcher = matcher; + service = dir.getFileSystem().newWatchService(); + + // Use FILE_TREE if supported + if (FILE_TREE_SUPPORTED) { + dir.register(service, KINDS, FILE_TREE_MODIFIERS); + return; + } // Register dirs recursively - service = dir.getFileSystem().newWatchService(); IOHelper.walk(dir, new RegisterFileVisitor(), true); } diff --git a/Launcher/source/hasher/HashedDir.java b/Launcher/source/hasher/HashedDir.java index f54bdb4..ec6c3d9 100644 --- a/Launcher/source/hasher/HashedDir.java +++ b/Launcher/source/hasher/HashedDir.java @@ -27,8 +27,8 @@ } @LauncherAPI - public HashedDir(Path dir, FileNameMatcher matcher) throws IOException { - IOHelper.walk(dir, new HashFileVisitor(dir, matcher), true); + public HashedDir(Path dir, FileNameMatcher matcher, boolean allowSymlinks) throws IOException { + IOHelper.walk(dir, new HashFileVisitor(dir, matcher, allowSymlinks), true); } @LauncherAPI @@ -173,15 +173,17 @@ private final class HashFileVisitor extends SimpleFileVisitor { private final Path dir; private final FileNameMatcher matcher; - private final Deque path = new LinkedList<>(); - private final Deque stack = new LinkedList<>(); + private final boolean allowSymlinks; // State private HashedDir current = HashedDir.this; + private final Deque path = new LinkedList<>(); + private final Deque stack = new LinkedList<>(); - private HashFileVisitor(Path dir, FileNameMatcher matcher) { + private HashFileVisitor(Path dir, FileNameMatcher matcher, boolean allowSymlinks) { this.dir = dir; this.matcher = matcher; + this.allowSymlinks = allowSymlinks; } @Override @@ -207,6 +209,12 @@ return result; } + // Verify is not symlink + // Symlinks was disallowed because modification of it's destination are ignored by DirWatcher + if (!allowSymlinks && attrs.isSymbolicLink()) { + throw new SecurityException("Symlinks are not allowed"); + } + // Add child stack.add(current); current = new HashedDir(); @@ -218,9 +226,13 @@ @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - path.add(IOHelper.getFileName(file)); + // Verify is not symlink + if (!allowSymlinks && attrs.isSymbolicLink()) { + throw new SecurityException("Symlinks are not allowed"); + } // Add file (may be unhashed, if exclusion) + path.add(IOHelper.getFileName(file)); boolean hash = matcher == null || matcher.shouldUpdate(path); current.map.put(path.removeLast(), new HashedFile(file, attrs.size(), hash)); return super.visitFile(file, attrs); diff --git a/Launcher/source/request/update/UpdateRequest.java b/Launcher/source/request/update/UpdateRequest.java index 89672c8..d99335b 100644 --- a/Launcher/source/request/update/UpdateRequest.java +++ b/Launcher/source/request/update/UpdateRequest.java @@ -65,7 +65,7 @@ @Override public SignedObjectHolder request() throws Exception { Files.createDirectories(dir); - localDir = new HashedDir(dir, matcher); + localDir = new HashedDir(dir, matcher, false); // Start request return super.request();