diff --git a/Configuration/src/main/java/io/deephaven/configuration/DataDir.java b/Configuration/src/main/java/io/deephaven/configuration/DataDir.java index 9dfedce05cd..308501a763d 100644 --- a/Configuration/src/main/java/io/deephaven/configuration/DataDir.java +++ b/Configuration/src/main/java/io/deephaven/configuration/DataDir.java @@ -12,6 +12,7 @@ public class DataDir { @Deprecated private static final String WORKSPACE_PROPERTY = "workspace"; public static final String ENV_VAR = "DEEPHAVEN_DATA_DIR"; + public static final String WINDOWS_SEPARATOR = "\\"; private static final String DEFAULT_DATA_DIR = "."; diff --git a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java old mode 100644 new mode 100755 index ee60bdfd0e6..b2603e5c852 --- a/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java +++ b/server/src/main/java/io/deephaven/server/notebook/FilesystemStorageServiceGrpcImpl.java @@ -10,7 +10,6 @@ import com.google.rpc.Code; import io.deephaven.configuration.Configuration; import io.deephaven.configuration.DataDir; -import io.deephaven.extensions.barrage.util.GrpcUtil; import io.deephaven.internal.log.LoggerFactory; import io.deephaven.io.logger.Logger; import io.deephaven.proto.backplane.grpc.CreateDirectoryRequest; @@ -92,6 +91,11 @@ public class FilesystemStorageServiceGrpcImpl extends StorageServiceGrpc.Storage */ @Deprecated private static final String REQUIRED_PATH_PREFIX = "/"; + /** + * Although REQUIRED_PATH_PREFIX has been deprecated, it is still used; this constant is for the same forward slash, + * to detect situations where the UI path separator is different from the OS path separator (i.e. on Windows). + */ + private static final String LINUX_SEPARATOR = "/"; private final Path root = Paths.get(STORAGE_PATH).normalize(); private final SessionService sessionService; @@ -114,14 +118,19 @@ public FilesystemStorageServiceGrpcImpl( } private Path resolveOrThrow(String incomingPath) { - if (incomingPath.startsWith(File.separator)) { + String resolveDetails = "\nOriginal incoming path is: " + incomingPath; + resolveDetails = resolveDetails + "\nFile separator is: " + File.separator; + incomingPath = maybeReplaceFileSeparators(incomingPath); + while (incomingPath.startsWith(REQUIRED_PATH_PREFIX)) { incomingPath = incomingPath.substring(1); } Path resolved = root.resolve(incomingPath).normalize(); + if (resolved.startsWith(root)) { return resolved; } - throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Invalid path: " + incomingPath); + throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Invalid path: " + incomingPath + + "." + resolveDetails); } private void requireNotRoot(Path path, String message) { @@ -130,6 +139,20 @@ private void requireNotRoot(Path path, String message) { } } + /** + * On Windows systems, we need to replace backslashes with forward slashes for the Web UI + * + * @param path Path String that may need to be modified + * @return Original path on non-Windows systems, or path with backslashes replaced with slashes on Windows systems. + */ + private String maybeReplaceFileSeparators(final String path) { + if (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) { + return path.replace(DataDir.WINDOWS_SEPARATOR, LINUX_SEPARATOR); + } else { + return path; + } + } + @Override public void listItems( @NotNull final ListItemsRequest request, @@ -148,8 +171,9 @@ public void listItems( } BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class); boolean isDirectory = attrs.isDirectory(); + String relativePath = maybeReplaceFileSeparators(root.relativize(p).toString()); ItemInfo.Builder info = ItemInfo.newBuilder() - .setPath(REQUIRED_PATH_PREFIX + root.relativize(p)); + .setPath(REQUIRED_PATH_PREFIX + relativePath); if (isDirectory) { info.setType(ItemType.DIRECTORY); } else { @@ -172,7 +196,7 @@ private static PathMatcher createPathFilter(String filterGlob) { if (filterGlob.contains("**")) { throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Bad glob, only single `*`s are supported"); } - if (filterGlob.contains(File.separator)) { + if (filterGlob.contains(File.separator) || filterGlob.contains(LINUX_SEPARATOR)) { throw Exceptions.statusRuntimeException(Code.INVALID_ARGUMENT, "Bad glob, only the same directory can be checked"); } diff --git a/server/src/main/java/io/deephaven/server/runner/MainHelper.java b/server/src/main/java/io/deephaven/server/runner/MainHelper.java index 9146b3782e2..5bd2eb5cb5d 100644 --- a/server/src/main/java/io/deephaven/server/runner/MainHelper.java +++ b/server/src/main/java/io/deephaven/server/runner/MainHelper.java @@ -25,7 +25,7 @@ import io.deephaven.util.process.ProcessEnvironment; import org.jetbrains.annotations.NotNull; import org.slf4j.bridge.SLF4JBridgeHandler; - +import java.io.File; import java.io.IOException; import java.io.Reader; import java.nio.charset.Charset; @@ -63,6 +63,14 @@ private static void bootstrapFromFile(Path configFile) throws IOException { } } + // Spaces in the paths are tedious on Windows + private static String maybeRemoveSpaces(final String pathToCheck) { + if (pathToCheck == null) { + return null; + } + return (File.separator.equals(DataDir.WINDOWS_SEPARATOR)) ? pathToCheck.replace(" ", "") : pathToCheck; + } + @VisibleForTesting public static void bootstrapProjectDirectories() throws IOException { final String applicationName = @@ -72,9 +80,9 @@ public static void bootstrapProjectDirectories() throws IOException { final dev.dirs.ProjectDirectories defaultDirectories = dev.dirs.ProjectDirectories.from("io", "Deephaven Data Labs", applicationName); - final Path cacheDir = CacheDir.getOrSet(defaultDirectories.cacheDir); - final Path configDir = ConfigDir.getOrSet(defaultDirectories.configDir); - final Path dataDir = DataDir.getOrSet(defaultDirectories.dataDir); + final Path cacheDir = CacheDir.getOrSet(maybeRemoveSpaces(defaultDirectories.cacheDir)); + final Path configDir = ConfigDir.getOrSet(maybeRemoveSpaces(defaultDirectories.configDir)); + final Path dataDir = DataDir.getOrSet(maybeRemoveSpaces(defaultDirectories.dataDir)); Files.createDirectories(cacheDir); Files.createDirectories(dataDir);