diff options
author | Jason Tedor <jason@tedor.me> | 2017-06-22 07:59:58 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-22 07:59:58 -0400 |
commit | 97a2c4523de9801b8a0ddbe8b056b128f968e327 (patch) | |
tree | f6d79bcb1c32643b7da6475a99eb705aee2d9e94 /core/src/main/java/org/elasticsearch | |
parent | e41eae9f059ea3fda8be50942b83c7cc0a20776c (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')
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(); |