环境
操作系统:Win11 jdk:corretto-11 (亚马逊) xnio: 3.8.0Final
起因
在使用xnio 库创建Channel 时,需要用到对各系统“黑洞”的路径。而undertow 中,引入了xnio 库。 xnio 创建channel的代码: 后出现报错信息:Exception in thread “main” java.io.IOException: Invalid file path。
解决方案
在 jvm 参数中,添加-Djdk.io.File.enableADS=true 即可。
原因分析
首先介绍NUL: 这种形式,是Windows下特有的,被称为:alternative data streams (ADS)。ADS的解释如下:
Since NT 3.1, the NTFS file system has supported multiple data-streams for files. There has never been built-in support for viewing or manipulating these additional streams, but the Windows API functions include support for them with a special file syntax: Filename.ext:StreamName. Even Win9x machines can access the alternative data streams of files on any NTFS volume they have access to, e.g., through a mapped drive. Because the Scripting.FileSystemObject and many other libraries call the CreateFile API behind the scenes, even scripts have been able to access alternative streams quite easily (although enumerating the existing streams has always been tricky).
通过查看jdk源码后,发现在java.io.File 中,有判断文件是否合法的函数:isInvalid() ,当前版本的源 码如下:
if (isInvalid()) {
throw new IOException("Invalid file path");
}
final boolean isInvalid() {
PathStatus s = status;
if (s == null) {
s = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED:PathStatus.INVALID;
}
return s == PathStatus.INVALID;
}
切换JDK至Oracle或DragonWell后,不会报错。因此猜想是该函数在各JDK中的实现不一致。 Oracle jdk11的判定规则,参考以下isInvalid() 。(corretto-11在 commit 8278356: Improve file 之前也是如此实现的)
corretto-11在 commit 8278356: Improve file 之后,isInvalid() 源码如下:
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
final boolean isInvalid() {
PathStatus s = status;
if (s == null) {
s = fs.isInvalid(this) ? PathStatus.INVALID : PathStatus.CHECKED;
status = s;
}
return s == PathStatus.INVALID;
}
@Override
public boolean isInvalid(File f) {
if (f.getPath().indexOf('\u0000') >= 0)
return true;
if (ENABLE_ADS)
return false;
String pathname = f.getPath();
int lastColon = pathname.lastIndexOf(":");
if (lastColon < 0 ||
(lastColon == 1 && isLetter(pathname.charAt(0))))
return false;
Path path = null;
try {
path = sun.nio.fs.DefaultFileSystemProvider.theFileSystem().getPath(pathname);
return false;
} catch (InvalidPathException ignored) {
}
return true;
}
从corretto-11 项目的测试用例 OpenNUL.java 来看,NUL: 此种ADS 的路径格式是否合法,是与WinNTFileSystem#_ENABLE_ADS_ 这个标识有关的,以下为源码中对_ENABLE_ADS_标识的说明:
private static final boolean ENABLE_ADS;
static {
String enableADS = GetPropertyAction.privilegedGetProperty("jdk.io.File.enableADS");
if (enableADS != null) {
ENABLE_ADS = "".equals(enableADS) || Boolean.parseBoolean(enableADS);
} else {
ENABLE_ADS = false;
}
}
综上,在corretto-11 中,需要开启ENABLE_ADS ,才能使NUL: 此种ADS 的路径判定为合法。 即在 jvm 参数中,添加-Djdk.io.File.enableADS=true 即可。
|