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
This commit is contained in:
parent
e41eae9f05
commit
97a2c4523d
|
@ -24,6 +24,7 @@ import com.sun.jna.Native;
|
||||||
import com.sun.jna.NativeLong;
|
import com.sun.jna.NativeLong;
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
import com.sun.jna.Structure;
|
import com.sun.jna.Structure;
|
||||||
|
import com.sun.jna.WString;
|
||||||
import com.sun.jna.win32.StdCallLibrary;
|
import com.sun.jna.win32.StdCallLibrary;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.lucene.util.Constants;
|
import org.apache.lucene.util.Constants;
|
||||||
|
@ -223,6 +224,17 @@ final class JNAKernel32Library {
|
||||||
*/
|
*/
|
||||||
native boolean CloseHandle(Pointer handle);
|
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
|
* Creates or opens a new job object
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.bootstrap;
|
||||||
|
|
||||||
import com.sun.jna.Native;
|
import com.sun.jna.Native;
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
|
import com.sun.jna.WString;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.lucene.util.Constants;
|
import org.apache.lucene.util.Constants;
|
||||||
import org.elasticsearch.common.logging.Loggers;
|
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) {
|
static void addConsoleCtrlHandler(ConsoleCtrlHandler handler) {
|
||||||
// The console Ctrl handler is necessary on Windows platforms only.
|
// The console Ctrl handler is necessary on Windows platforms only.
|
||||||
if (Constants.WINDOWS) {
|
if (Constants.WINDOWS) {
|
||||||
|
|
|
@ -76,6 +76,20 @@ final class Natives {
|
||||||
JNANatives.tryVirtualLock();
|
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) {
|
static void addConsoleCtrlHandler(ConsoleCtrlHandler handler) {
|
||||||
if (!JNA_AVAILABLE) {
|
if (!JNA_AVAILABLE) {
|
||||||
logger.warn("cannot register console handler because JNA is not available");
|
logger.warn("cannot register console handler because JNA is not available");
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.bootstrap;
|
package org.elasticsearch.bootstrap;
|
||||||
|
|
||||||
|
import org.apache.lucene.util.Constants;
|
||||||
import org.apache.lucene.util.IOUtils;
|
import org.apache.lucene.util.IOUtils;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.plugins.Platforms;
|
import org.elasticsearch.plugins.Platforms;
|
||||||
|
@ -99,7 +100,22 @@ final class Spawner implements Closeable {
|
||||||
private Process spawnNativePluginController(
|
private Process spawnNativePluginController(
|
||||||
final Path spawnPath,
|
final Path spawnPath,
|
||||||
final Path tmpPath) throws IOException {
|
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
|
// the only environment variable passes on the path to the temporary directory
|
||||||
pb.environment().clear();
|
pb.environment().clear();
|
||||||
|
|
Loading…
Reference in New Issue