当从应用程序访问共享文件夹时,会返回意外错误。

本文提供了从应用程序访问共享文件夹时发生的意外错误的解决方案。

原始 KB 数: 2990989

症状

请考虑以下方案:

  • 在 Windows Server 2008 R2 中创建共享文件夹,然后向用户授予该文件夹的读取权限。
  • 在共享文件夹上创建子文件夹,禁用子文件夹的父文件夹中的可继承权限,并且不向用户授予子文件夹的“读取”权限。
  • 以用户 A 身份登录到基于 Windows Server 2012 的服务器,然后运行调用 FindFirstFile 作为共享文件夹的应用程序。

在此方案中,应用程序失败。 此外,预期应该返回的错误ERROR_ACCESS_DENIED没有被返回。 而是返回以下错误代码之一:

发生意外的网络错误。 (ERROR_UNEXP_NET_ERR)

参数不正确。 (错误_无效参数)

如果正在运行的应用程序假定在这种情况下返回ERROR_ACCESS_DENIED,以表明它没有适当的访问权限,则此假设可能不正确。

原因

出现此问题是因为内核模式 SMB 组件在 I/O 应该失败并返回STATUS_ERROR_ACCESS_DENIED的情况下,返回了 STATUS_UNEXPECTED_NETWORK_ERROR。 同时,SMB 组件在另一个线程中请求 I/O。 此线程可以返回STATUS_INVALID_PARAMETER。

将 NT 状态传递给文件或文件夹 API 时,SMB 组件返回的这些 NT 状态将转换为以下 Win32 错误之一:

  • 意外的网络错误 (ERROR_UNEXP_NET_ERR)
  • 参数无效错误

决议

如果应用程序假定在这种情况下返回ERROR_ACCESS_DENIED,以确定它是否具有适当的访问权限,并在访问共享文件夹的子文件夹时收到ERROR_UNEXP_NET_ERR或ERROR_INVALID_PARAMETER,而没有任何访问权限,则可以通过执行与接收ERROR_ACCESS_DENIED时相同的处理来避免此问题。

详细信息

打开子文件夹时,Windows Server 2012 中的客户端重定向程序会发送两个 SMB 数据包。 一个数据包是“创建”数据包,另一个数据包是“查询目录”数据包。 创建数据包将打开目录,查询目录数据包使重定向程序能够确定有关子文件夹的信息,以便它可以正确处理子文件夹。 创建数据包失败,错误码为STATUS_ACCESS_DENIED。 但之后,查询目录数据包因STATUS_UNEXPECTED_NETWORK_ERROR错误而失败。 因此,有两个错误代码。 由于查询目录操作发生在创建之后,因此将返回查询目录错误代码。

即使 Windows 的设计已更改,以便可以返回ERROR_ACCESS_DENIED,访问网络共享的应用程序仍会收到ERROR_UNEXP_NET_ERR,因为可能发生实际的网络错误,例如服务器崩溃、网络电源丢失等。

另请注意,本地驱动器的 I/O 与网络文件共享的 I/O 之间存在差异。 尽管应用程序使用的 API(CreateFile、ReadFile、FindFirstFile 等)完全相同,但执行工作的存储系统不同。 当应用程序访问本地文件时,API 会创建 IRP 并将其发送到文件系统驱动程序(NTFS.SYS),I/O 请求最终会发送到磁盘驱动程序。 当应用程序访问网络共享时,API 将调用客户端/服务器网络协议。 API 将请求发送到客户端重定向程序,然后客户端重定向程序发出 SMB 数据包并将 SMB 数据包发送到服务器。 服务器的服务接收数据包,然后执行I/O操作。 I/O作完成后,服务器服务会将响应 SMB 数据包发送回客户端重定向程序,客户端重定向程序返回到应用程序。

由于这些差异,API 可以返回不同类型的错误。 对于本地文件,API 不会返回网络错误。 对于网络文件,API 可以返回文件访问错误,例如ERROR_ACCESS_DENIED和网络错误(如ERROR_UNEXP_NET_ERR),因为网络上可能发生任何一种错误。 此行为是有意为之,并记录在 Microsoft 开发者网络(MSDN)库中的以下两个位置:

本地和网络 I/O 的差异

网络I/O 操作的说明