Java 规范化 Windows 上的路径时是否需要支持 ERROR_NO_MORE_FILES?
Does Java need to support ERROR_NO_MORE_FILES when canonicalizing paths on Windows?
问题。
Java、运行 在 Windows 7 中实现的一些守护进程,将文件从一个目录复制到另一个目录,而源目录和目标目录都是由 [= 托管的网络共享76=] Server 2016。复制是使用 Apache Commons IO 完成的,偶尔会发生此过程失败并出现以下堆栈跟踪和一条消息,内容类似于 "no more files":
java.io.IOException: Es sind keine weiteren Dateien vorhanden
at java.io.WinNTFileSystem.canonicalize0(Native Method)
at java.io.WinNTFileSystem.canonicalize(Unknown Source)
at java.io.File.getCanonicalPath(Unknown Source)
at org.apache.commons.io.FileUtils.copyFile(FileUtils.java:642)
at org.apache.commons.io.FileUtils.copyFileToDirectory(FileUtils.java:587)
at org.apache.commons.io.FileUtils.copyFileToDirectory(FileUtils.java:558)
at de.am_soft.osgi.dokliste.eingaenge.impl.internal.Eingang.copyFilesToDbxmlFolders(Eingang.java:283)
Apache Commons IO 在第 642 行使用以下代码,该行实际上只有以下 if
,没有例外:
if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
}
所以问题不在于复制本身,而在于已经生成了规范路径。在运行守护进程的客户端使用 Process Monitor1 也证明了这一点。以下是守护进程清楚地记录上述异常之前的最后一个事件,尝试使用 Logback 和东西发送错误邮件。该事件的结果 (NO MORE FILES
) 完全符合堆栈跟踪的错误消息:
10:12:06,6244515 integration.exe 6928 QueryDirectory \HOST\SHARE$\DocBeam3\[...].zip NO MORE FILES Filter: 20191106-081920-[...].zip
此外,查看 ProcMon 的前几行,可以确定异常仅发生在 destFile
中。在我的本地计算机上执行守护程序反而会导致以下记录事件 (NO SUCH FILE
) 总是:
19:08:03,7485947 java.exe 6232 QueryDirectory C:\Users\[...].zip NO SUCH FILE Filter: 20191022-143101-[...].zip
我已经调试了本机方法并遇到了 lastErrorReportable
,它明确检查了一些特殊的错误代码并且不包含第一个事件的 ERROR_NO_MORE_FILES
,但确实包含 ERROR_FILE_NOT_FOUND
来自第二个:
if ((errval == ERROR_FILE_NOT_FOUND)
|| (errval == ERROR_DIRECTORY)
|| (errval == ERROR_PATH_NOT_FOUND)
|| (errval == ERROR_BAD_NETPATH)
|| (errval == ERROR_BAD_NET_NAME)
|| (errval == ERROR_ACCESS_DENIED)
|| (errval == ERROR_NETWORK_UNREACHABLE)
|| (errval == ERROR_NETWORK_ACCESS_DENIED)) {
return 0;
}
所以似乎每当 ERROR_NO_MORE_FILES
发生时,规范化路径就会因错误而中止,而不是像其他错误那样忽略它:
if (!lastErrorReportable()) {
if (!(dst = wcp(dst, dend, L'[=18=]', src, src + wcslen(src)))){
goto err;
}
break;
} else {
goto err;
}
抛出的异常与我得到的非常吻合,给定的消息只是我的案例中未使用的后备消息:
if (rv == NULL && !(*env)->ExceptionCheck(env)) {
JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
}
补充观察。
现在有趣的是,守护进程并不总是在每个文件副本上失败,而是有时,很少见。但如果它失败了,它似乎与目标目录中已经可用的其他目录和文件有关。虽然它们与守护进程完全无关,并且根据 ProcMon 的说法,它们不会被迭代或类似的东西,但它们的纯粹存在似乎已经产生了影响。如果我简单地删除所有这些文件和目录并以这种方式清空目标目录,复制立即再次成功。这很有趣,因为在我的本地设置的目标目录中有文件和目录似乎没有任何影响:复制永远不会失败,尤其是 ProcMon 记录的事件也永远不会是 ERROR_NO_MORE_FILES
。在清空发生问题的安装目录后,ProcMon 也会再次记录 ERROR_FILE_NOT_FOUND
。
问题。
因此,似乎出于某种原因,在某些当前未知的情况下,Windows 决定使用 ERROR_NO_MORE_FILES
作为 wcanonicalize
调用 FindFirstFileW
的最后一个错误.因为 Java 的例外列表中没有它,所以在这种情况下复制会失败,即使它看起来是完全有效的情况。否则我没有看到任何真正的错误。
所以ERROR_NO_MORE_FILES
应该加在lastErrorReportable
上吗?如果是这样,我实际上需要问谁? :-)
此行为是由于 Windows Server 2019 服务器(文件服务器)与 Windows 以前版本(客户端)之间的 SMB 不兼容引起的。目录元数据缓存的处理方式不同,这会导致在读取包含许多文件和文件夹的共享时出现此问题。
很遗憾,Microsoft 尚未发布此错误的修复程序。
解决方法是使用此注册表设置在客户端禁用 SMB 元数据缓存:
HKLM\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\DirectoryCacheLifetime=0 (DWORD)
问题。
Java、运行 在 Windows 7 中实现的一些守护进程,将文件从一个目录复制到另一个目录,而源目录和目标目录都是由 [= 托管的网络共享76=] Server 2016。复制是使用 Apache Commons IO 完成的,偶尔会发生此过程失败并出现以下堆栈跟踪和一条消息,内容类似于 "no more files":
java.io.IOException: Es sind keine weiteren Dateien vorhanden
at java.io.WinNTFileSystem.canonicalize0(Native Method)
at java.io.WinNTFileSystem.canonicalize(Unknown Source)
at java.io.File.getCanonicalPath(Unknown Source)
at org.apache.commons.io.FileUtils.copyFile(FileUtils.java:642)
at org.apache.commons.io.FileUtils.copyFileToDirectory(FileUtils.java:587)
at org.apache.commons.io.FileUtils.copyFileToDirectory(FileUtils.java:558)
at de.am_soft.osgi.dokliste.eingaenge.impl.internal.Eingang.copyFilesToDbxmlFolders(Eingang.java:283)
Apache Commons IO 在第 642 行使用以下代码,该行实际上只有以下 if
,没有例外:
if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
}
所以问题不在于复制本身,而在于已经生成了规范路径。在运行守护进程的客户端使用 Process Monitor1 也证明了这一点。以下是守护进程清楚地记录上述异常之前的最后一个事件,尝试使用 Logback 和东西发送错误邮件。该事件的结果 (NO MORE FILES
) 完全符合堆栈跟踪的错误消息:
10:12:06,6244515 integration.exe 6928 QueryDirectory \HOST\SHARE$\DocBeam3\[...].zip NO MORE FILES Filter: 20191106-081920-[...].zip
此外,查看 ProcMon 的前几行,可以确定异常仅发生在 destFile
中。在我的本地计算机上执行守护程序反而会导致以下记录事件 (NO SUCH FILE
) 总是:
19:08:03,7485947 java.exe 6232 QueryDirectory C:\Users\[...].zip NO SUCH FILE Filter: 20191022-143101-[...].zip
我已经调试了本机方法并遇到了 lastErrorReportable
,它明确检查了一些特殊的错误代码并且不包含第一个事件的 ERROR_NO_MORE_FILES
,但确实包含 ERROR_FILE_NOT_FOUND
来自第二个:
if ((errval == ERROR_FILE_NOT_FOUND)
|| (errval == ERROR_DIRECTORY)
|| (errval == ERROR_PATH_NOT_FOUND)
|| (errval == ERROR_BAD_NETPATH)
|| (errval == ERROR_BAD_NET_NAME)
|| (errval == ERROR_ACCESS_DENIED)
|| (errval == ERROR_NETWORK_UNREACHABLE)
|| (errval == ERROR_NETWORK_ACCESS_DENIED)) {
return 0;
}
所以似乎每当 ERROR_NO_MORE_FILES
发生时,规范化路径就会因错误而中止,而不是像其他错误那样忽略它:
if (!lastErrorReportable()) {
if (!(dst = wcp(dst, dend, L'[=18=]', src, src + wcslen(src)))){
goto err;
}
break;
} else {
goto err;
}
抛出的异常与我得到的非常吻合,给定的消息只是我的案例中未使用的后备消息:
if (rv == NULL && !(*env)->ExceptionCheck(env)) {
JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
}
补充观察。
现在有趣的是,守护进程并不总是在每个文件副本上失败,而是有时,很少见。但如果它失败了,它似乎与目标目录中已经可用的其他目录和文件有关。虽然它们与守护进程完全无关,并且根据 ProcMon 的说法,它们不会被迭代或类似的东西,但它们的纯粹存在似乎已经产生了影响。如果我简单地删除所有这些文件和目录并以这种方式清空目标目录,复制立即再次成功。这很有趣,因为在我的本地设置的目标目录中有文件和目录似乎没有任何影响:复制永远不会失败,尤其是 ProcMon 记录的事件也永远不会是 ERROR_NO_MORE_FILES
。在清空发生问题的安装目录后,ProcMon 也会再次记录 ERROR_FILE_NOT_FOUND
。
问题。
因此,似乎出于某种原因,在某些当前未知的情况下,Windows 决定使用 ERROR_NO_MORE_FILES
作为 wcanonicalize
调用 FindFirstFileW
的最后一个错误.因为 Java 的例外列表中没有它,所以在这种情况下复制会失败,即使它看起来是完全有效的情况。否则我没有看到任何真正的错误。
所以ERROR_NO_MORE_FILES
应该加在lastErrorReportable
上吗?如果是这样,我实际上需要问谁? :-)
此行为是由于 Windows Server 2019 服务器(文件服务器)与 Windows 以前版本(客户端)之间的 SMB 不兼容引起的。目录元数据缓存的处理方式不同,这会导致在读取包含许多文件和文件夹的共享时出现此问题。
很遗憾,Microsoft 尚未发布此错误的修复程序。
解决方法是使用此注册表设置在客户端禁用 SMB 元数据缓存:
HKLM\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\DirectoryCacheLifetime=0 (DWORD)