Windows CopyFileA 失败然后成功

Kiw*_*une 12 c++ windows file

尝试将文件复制到具有特定权限的文件夹时遇到问题。

这是我非常简单的测试程序:

#include <iostream>
#include <windows.h>

int main()
{
    if(CopyFileA("D:/SOURCE/File.txt", "D:/LOCKED/dst1.txt", TRUE))
        std::cout << "OK\n";
    else
        std::cout << "Fail\n";

    if (CopyFileA("D:/SOURCE/File.txt", "D:/LOCKED/dst2.txt", TRUE))
        std::cout << "OK\n";
    else
        std::cout << "Fail\n";

    system("PAUSE");
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

Fail
OK
Run Code Online (Sandbox Code Playgroud)

我可以复制任意数量的文件。对于每次运行,第一次总是失败,后续总是成功。错误是 ERROR_ACCESS_DENIED。

这是重要的细节:我的目标文件夹具有受限的权限,只有当我删除“写入属性”或“写入扩展属性”时才会出现此行为。 在此处输入图片说明

我使用了 sysInternals 的进程监视器:没有其他进程正在访问源文件和目标文件。但是,目标文件上的行为并不相似。第一次尝试关闭文件并尝试重新打开它。以下是日志:

行动 地位 细节
失败的尝试(第一次) —— ——
创建文件 成功 所需访问:通用读/写、删除、写 DAC、处置:创建、选项:顺序访问、非目录文件、属性:A、ShareMode:无、AllocationSize:0、OpenResult:已创建
关闭文件 成功
创建文件 拒绝访问 所需访问:通用读/写、删除、写 DAC、处置:OpenIf、选项:顺序访问、同步 IO 非警报、非目录文件、属性:A、共享模式:无、分配大小:0
创建文件 拒绝访问 所需访问:通用读/写、删除、写 DAC、处置:OpenIf、选项:顺序访问、同步 IO 非警报、非目录文件、属性:A、共享模式:读、写、分配大小:0
创建文件 拒绝访问 所需访问:通用读/写、写 DAC、处置:OpenIf、选项:顺序访问、同步 IO 非警报、非目录文件、属性:A、共享模式:读、写、分配大小:0
创建文件 拒绝访问 所需访问:通用读/写,处置:OpenIf,选项:顺序访问,同步 IO 非警报,非目录文件,属性:A,共享模式:无,分配大小:0
创建文件 拒绝访问 所需访问:通用读/写,处置:OpenIf,选项:顺序访问,同步 IO 非警报,非目录文件,属性:A,共享模式:读、写,分配大小:0
成功的尝试(第二次和后续) —— ——
创建文件 成功 所需访问:通用读/写、删除、写 DAC、处置:创建、选项:顺序访问、同步 IO 非警报、非目录文件、属性:A、ShareMode:无、AllocationSize:0、OpenResult:已创建
QueryAttributeInformationVolume 成功 文件系统属性:保留大小写、区分大小写、Unicode、ACL、压缩、命名流、EFS、对象 ID、重新分析点、稀疏文件、配额、事务、0x3c00600、MaximumComponentNameLength:255、FileSystemName:NTFS
查询基本信息文件 成功 CreationTime:19/08/2021 15:20:32,LastAccessTime:19/08/2021 15:20:32,LastWriteTime:19/08/2021 15:20:32,ChangeTime:19/08/2021 15:20: 32、FileAttributes:A
SetEndOfFileInformationFile 成功 文件结尾:24
写文件 成功 偏移:0,长度:24,优先级:正常
读取文件 成功 偏移量:0,长度:24,I/O 标志:非缓存,分页 I/O,同步分页 I/O,优先级:正常
设置基本信息文件 成功 CreationTime:01/01/1601 02:00:00,LastAccessTime:01/01/1601 02:00:00,LastWriteTime:19/08/2021 14:50:49,ChangeTime:19/08/2021 14:50: 49、FileAttributes:不适用
QueryRemoteProtocolInformation 无效的参数
关闭文件 成功

编辑1:

这是get-acl D:\LOCKED | fl回应:

Path   : Microsoft.PowerShell.Core\FileSystem::D:\LOCKED
Owner  : BUILTIN\Administrators
Group  : KJ-WIN10\None
Access : Everyone Allow  CreateFiles, AppendData, ReadAndExecute, Synchronize
Audit  :
Sddl   : O:BAG:S-1-5-21-1983668899-2794625975-2151939504-513D:PAI(A;OICI;0x1200af;;;WD)
Run Code Online (Sandbox Code Playgroud)

您可以在https://github.com/KiwiJaune/CantMove/blob/master/Program.cs 上使用我的 C# 示例程序重现完整行为(文件夹创建和文件复制)。

编辑2:

以下是fltmc几台经过测试的 PC 之一的结果:

过滤器名称 数量实例 高度 框架
绑定 1 409800 0
依赖 12 407000 0
过滤器 12 328010 0
暴风雪 0 244000 0
西弗斯 4 189900 0
氟利昂 2 180451 0
自卫队 12 145900 0
文件加密 0 141100 0
路飞 1 135000 0
npsvctrig 1 46000 0
沃夫 8 40700 0
文件信息 12 40500 0

我发现它总是在 D:/ 或闪存驱动器上失败,但在 C:/ 上成功,无论 D: 是另一个物理磁盘还是与 C: 相同磁盘上的分区...

我在 4 台经过测试的 PC 上有相同的行为(3 台 Win10 和 1 台 Win7)

是否有 Windows 已知行为?提前感谢您的想法。

小智 2

将文件从同一驱动器上的一个位置移动到另一个位置只会导致 Windows 移动其目录位置;移动到不同驱动器上的位置会导致 Windows 将文件复制到目标,然后删除原始文件。

使用 API 复制文件时,Windows 似乎使用两种方法:

  1. Windows 创建一个空的目标文件,然后打开它来写入数据。
  2. Windows 以写入模式在目标处创建文件以添加数据。

这就是您的程序中发生的情况:第一次调用“移动文件”时,Windows 使用方法 1...后续调用使用方法 2。

问题是方法1需要目录的修改权限。实际上,Windows 创建了空文件,但随后无法打开该文件来添加数据。D:\LOCKING 中创建的空文件证实了这一点:向目标目录授予“修改”权限可以解决此问题;或者,在与预期目标相同的驱动器上创建临时文件。