【问题标题】:Does Java need to support ERROR_NO_MORE_FILES when canonicalizing paths on Windows?在 Windows 上规范化路径时,Java 是否需要支持 ERROR_NO_MORE_FILES?
【发布时间】:2019-11-12 19:39:37
【问题描述】:

问题。

一些用 Java 实现的守护进程,在 Windows 7 上运行,将文件从一个目录复制到另一个目录,而源目录和目标目录都是由 Windows Server 2016 托管的网络共享。复制是使用 Apache Commons IO 完成的,偶尔会发生这种情况此过程失败,并显示以下堆栈跟踪和一条类似于“没有更多文件”的消息:

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;
    }

https://github.com/openjdk/jdk/blob/master/src/java.base/windows/native/libjava/canonicalize_md.c#L131

因此,似乎每当ERROR_NO_MORE_FILES 发生时,规范化路径只会因错误而中止,而不是像其他错误那样忽略它:

if (!lastErrorReportable()) {
   if (!(dst = wcp(dst, dend, L'\0', src, src + wcslen(src)))){
       goto err;
   }
    break;
} else {
    goto err;
}

https://github.com/openjdk/jdk/blob/master/src/java.base/windows/native/libjava/canonicalize_md.c#L246

抛出的异常非常适合我得到的,给定的消息只是我的情况下没有使用的后备:

if (rv == NULL && !(*env)->ExceptionCheck(env)) {
    JNU_ThrowIOExceptionWithLastError(env, "Bad pathname");
}

https://github.com/openjdk/jdk/blob/master/src/java.base/windows/native/libjava/WinNTFileSystem_md.c#L258

补充意见。

现在有趣的是,守护进程并不总是在每个文件副本上都失败,但只是有时,很少见。但是如果它失败了,它似乎与目标目录中已经存在的其他目录和文件有关。虽然这些与守护进程完全无关,并且根据 ProcMon 的说法,它们不会被迭代或填充,但它们的纯粹存在似乎已经产生了影响。如果我只是删除所有这些文件和目录并以这种方式清空目标目录,则复制会立即再次成功。这很有趣,因为在我的本地设置中的目标目录中包含文件和目录似乎没有任何影响:复制永远不会失败,尤其是 ProcMon 记录的事件 NEVER 也是ERROR_NO_MORE_FILES。清空发生问题的设置目录后,ProcMon 也会再次记录 ERROR_FILE_NOT_FOUND。

问题。

因此,在某些当前未知的情况下,Windows 似乎出于某种原因决定使用ERROR_NO_MORE_FILES 作为wcanonicalize 使用的FindFirstFileW 调用中的最后一个错误。因为 Java 的例外列表中没有它,所以在这些情况下复制会失败,即使这似乎是一个完全有效的情况。否则我看不到任何真正的错误。

那么ERROR_NO_MORE_FILES 应该添加到lastErrorReportable 吗?如果是这样,我实际上需要问谁? :-)

【问题讨论】:

  • 好奇你是否从那时起发现了更多信息以及你是如何解决这个问题的。在将文件集群从 WS 2K8R2 升级到 WS 2019 之后,我们现在在工作中面临这个问题。
  • @dSebastien 不,我实施了一些变通方法,以确保我使用的目标目录是空的。这使问题几乎(?)消失了。在使用FindFirstFileW 时,我在用 C++ 实现的本机 Win32 应用程序中也遇到了类似的问题。在极少数情况下,调用这会导致 ERROR_NO_MORE_FILES 而不是 ERROR_FILE_NOT_FOUND,这在过去几年中在旧版本的 Windows Server 中都不会发生。在我看来,Windows 中发生了一些变化,现在需要处理某些 API 中的额外错误。所以我希望Java将来能兼容。
  • @dSebastien 甚至有可能 Windows 根本不是问题,而是像病毒扫描程序这样的低级问题,它会干扰对文件系统的请求等。 stackoverflow.com/questions/58825963/…
  • @ThorstenSchöning 我们尝试了客户端服务器上的注册表设置,到目前为止,“No More File”错误似乎已经停止。

标签: java windows filesystems


【解决方案1】:

此行为是由 Windows Server 2019 服务器(文件服务器)和早期版本的 Windows(客户端)之间的 SMB 不兼容引起的。目录元数据的缓存处理方式不同,这在读取包含许多文件和文件夹的共享时会导致此问题。

很遗憾,Microsoft 尚未发布此错误的修复程序。

解决方法是使用以下注册表设置在客户端禁用 SMB 元数据缓存: HKLM\System\CurrentControlSet\Services\LanmanWorkstation\Parameters\DirectoryCacheLifetime=0 (DWORD)

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 2023-01-20
  • 2018-08-09
  • 1970-01-01
  • 2015-12-18
  • 1970-01-01
相关资源
最近更新 更多