summaryrefslogtreecommitdiff
path: root/core/src/main/java/org/elasticsearch
diff options
context:
space:
mode:
authorJason Tedor <jason@tedor.me>2017-06-22 07:59:58 -0400
committerGitHub <noreply@github.com>2017-06-22 07:59:58 -0400
commit97a2c4523de9801b8a0ddbe8b056b128f968e327 (patch)
treef6d79bcb1c32643b7da6475a99eb705aee2d9e94 /core/src/main/java/org/elasticsearch
parente41eae9f059ea3fda8be50942b83c7cc0a20776c (diff)
Get short path name for native controllers
Due to limitations with CreateProcessW on Windows (ultimately used by ProcessBuilder) with respect to maximum path lengths, we need to get the short path name for any native controllers before trying to start them in case the absolute path exceeds the maximum path length. This commit uses JNA to invoke the necessary Windows API for this to start the native controller using the short path. To be precise about the limitation here, the MSDN docs for CreateProcessW say for the command line parameter: >The command line to be executed. The maximum length of this string is >32,768 characters, including the Unicode terminating null character. If >lpApplicationName is NULL, the module name portionof lpCommandLine is >limited to MAX_PATH characters. This is exactly how the Windows implementation of Process in the JDK invokes CreateProcessW: with the executable name (lpApplicationName) set to NULL. Relates #25344
Diffstat (limited to 'core/src/main/java/org/elasticsearch')
-rw-r--r--core/src/main/java/org/elasticsearch/bootstrap/JNAKernel32Library.java12
-rw-r--r--core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java30
-rw-r--r--core/src/main/java/org/elasticsearch/bootstrap/Natives.java14
-rw-r--r--core/src/main/java/org/elasticsearch/bootstrap/Spawner.java18
4 files changed, 73 insertions, 1 deletions
diff --git a/core/src/main/java/org/elasticsearch/bootstrap/JNAKernel32Library.java b/core/src/main/java/org/elasticsearch/bootstrap/JNAKernel32Library.java
index 71a7fad623..99574c2b39 100644
--- a/core/src/main/java/org/elasticsearch/bootstrap/JNAKernel32Library.java
+++ b/core/src/main/java/org/elasticsearch/bootstrap/JNAKernel32Library.java
@@ -24,6 +24,7 @@ import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
+import com.sun.jna.WString;
import com.sun.jna.win32.StdCallLibrary;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.Constants;
@@ -224,6 +225,17 @@ final class JNAKernel32Library {
native boolean CloseHandle(Pointer handle);
/**
+ * Retrieves the short path form of the specified path. See
+ * <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989.aspx">{@code GetShortPathName}</a>.
+ *
+ * @param lpszLongPath the path string
+ * @param lpszShortPath a buffer to receive the short name
+ * @param cchBuffer the size of the buffer
+ * @return the length of the string copied into {@code lpszShortPath}, otherwise zero for failure
+ */
+ native int GetShortPathNameW(WString lpszLongPath, char[] lpszShortPath, int cchBuffer);
+
+ /**
* Creates or opens a new job object
*
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms682409%28v=vs.85%29.aspx
diff --git a/core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java b/core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java
index d4e11af71a..b28cc39824 100644
--- a/core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java
+++ b/core/src/main/java/org/elasticsearch/bootstrap/JNANatives.java
@@ -21,6 +21,7 @@ package org.elasticsearch.bootstrap;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
+import com.sun.jna.WString;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.Constants;
import org.elasticsearch.common.logging.Loggers;
@@ -194,6 +195,35 @@ class JNANatives {
}
}
+ /**
+ * Retrieves the short path form of the specified path.
+ *
+ * @param path the path
+ * @return the short path name (or the original path if getting the short path name fails for any reason)
+ */
+ static String getShortPathName(String path) {
+ assert Constants.WINDOWS;
+ try {
+ final WString longPath = new WString("\\\\?\\" + path);
+ // first we get the length of the buffer needed
+ final int length = JNAKernel32Library.getInstance().GetShortPathNameW(longPath, null, 0);
+ if (length == 0) {
+ logger.warn("failed to get short path name: {}", Native.getLastError());
+ return path;
+ }
+ final char[] shortPath = new char[length];
+ // knowing the length of the buffer, now we get the short name
+ if (JNAKernel32Library.getInstance().GetShortPathNameW(longPath, shortPath, length) > 0) {
+ return Native.toString(shortPath);
+ } else {
+ logger.warn("failed to get short path name: {}", Native.getLastError());
+ return path;
+ }
+ } catch (final UnsatisfiedLinkError e) {
+ return path;
+ }
+ }
+
static void addConsoleCtrlHandler(ConsoleCtrlHandler handler) {
// The console Ctrl handler is necessary on Windows platforms only.
if (Constants.WINDOWS) {
diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Natives.java b/core/src/main/java/org/elasticsearch/bootstrap/Natives.java
index ad6ec985ca..6dae75e63b 100644
--- a/core/src/main/java/org/elasticsearch/bootstrap/Natives.java
+++ b/core/src/main/java/org/elasticsearch/bootstrap/Natives.java
@@ -76,6 +76,20 @@ final class Natives {
JNANatives.tryVirtualLock();
}
+ /**
+ * Retrieves the short path form of the specified path.
+ *
+ * @param path the path
+ * @return the short path name (or the original path if getting the short path name fails for any reason)
+ */
+ static String getShortPathName(final String path) {
+ if (!JNA_AVAILABLE) {
+ logger.warn("cannot obtain short path for [{}] because JNA is not avilable", path);
+ return path;
+ }
+ return JNANatives.getShortPathName(path);
+ }
+
static void addConsoleCtrlHandler(ConsoleCtrlHandler handler) {
if (!JNA_AVAILABLE) {
logger.warn("cannot register console handler because JNA is not available");
diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Spawner.java b/core/src/main/java/org/elasticsearch/bootstrap/Spawner.java
index 77cadaa904..f1616ba0ee 100644
--- a/core/src/main/java/org/elasticsearch/bootstrap/Spawner.java
+++ b/core/src/main/java/org/elasticsearch/bootstrap/Spawner.java
@@ -19,6 +19,7 @@
package org.elasticsearch.bootstrap;
+import org.apache.lucene.util.Constants;
import org.apache.lucene.util.IOUtils;
import org.elasticsearch.env.Environment;
import org.elasticsearch.plugins.Platforms;
@@ -99,7 +100,22 @@ final class Spawner implements Closeable {
private Process spawnNativePluginController(
final Path spawnPath,
final Path tmpPath) throws IOException {
- final ProcessBuilder pb = new ProcessBuilder(spawnPath.toString());
+ final String command;
+ if (Constants.WINDOWS) {
+ /*
+ * We have to get the short path name or starting the process could fail due to max path limitations. The underlying issue here
+ * is that starting the process on Windows ultimately involves the use of CreateProcessW. CreateProcessW has a limitation that
+ * if its first argument (the application name) is null, then its second argument (the command line for the process to start) is
+ * restricted in length to 260 characters (cf. https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425.aspx). Since
+ * this is exactly how the JDK starts the process on Windows (cf.
+ * http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/windows/native/java/lang/ProcessImpl_md.c#l319), this
+ * limitation is in force. As such, we use the short name to avoid any such problems.
+ */
+ command = Natives.getShortPathName(spawnPath.toString());
+ } else {
+ command = spawnPath.toString();
+ }
+ final ProcessBuilder pb = new ProcessBuilder(command);
// the only environment variable passes on the path to the temporary directory
pb.environment().clear();