Windows CopyFileA fails then succeeds

Viewed 651

I have an issue when trying to copy a file to a folder with specific permissions.

Here is my very simple test program:

#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");
}

And here is the result :

Fail
OK

I can copy as many files as I want. For each run, the first always fails and subsequents always succeed. The error is ERROR_ACCESS_DENIED.

Here is the important detail: my destination folder has restricted permissions and this behaviour occurs only if I remove "Write attributes" or "Write extended attributes". enter image description here

I used sysInternals' Process Monitor: no other process is accessing source nor destination file. However, behaviour on destination file are not similar. The first attempt close the file and try to reopen it. Here are the logs:

Action Status Details
FAILED ATTEMP (FIRST) - -
CreateFile SUCCESS Desired Access: Generic Read/Write, Delete, Write DAC, Disposition: Create, Options: Sequential Access, Non-Directory File, Attributes: A, ShareMode: None, AllocationSize: 0, OpenResult: Created
CloseFile SUCCESS
CreateFile ACCESS DENIED Desired Access: Generic Read/Write, Delete, Write DAC, Disposition: OpenIf, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: A, ShareMode: None, AllocationSize: 0
CreateFile ACCESS DENIED Desired Access: Generic Read/Write, Delete, Write DAC, Disposition: OpenIf, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: A, ShareMode: Read, Write, AllocationSize: 0
CreateFile ACCESS DENIED Desired Access: Generic Read/Write, Write DAC, Disposition: OpenIf, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: A, ShareMode: Read, Write, AllocationSize: 0
CreateFile ACCESS DENIED Desired Access: Generic Read/Write, Disposition: OpenIf, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: A, ShareMode: None, AllocationSize: 0
CreateFile ACCESS DENIED Desired Access: Generic Read/Write, Disposition: OpenIf, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: A, ShareMode: Read, Write, AllocationSize: 0
SUCCEDDED ATTEMPT (SECOND AND SUBSEQUENTS) - -
CreateFile SUCCESS Desired Access: Generic Read/Write, Delete, Write DAC, Disposition: Create, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Attributes: A, ShareMode: None, AllocationSize: 0, OpenResult: Created
QueryAttributeInformationVolume SUCCESS FileSystemAttributes: Case Preserved, Case Sensitive, Unicode, ACLs, Compression, Named Streams, EFS, Object IDs, Reparse Points, Sparse Files, Quotas, Transactions, 0x3c00600, MaximumComponentNameLength: 255, FileSystemName: NTFS
QueryBasicInformationFile SUCCESS 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 SUCCESS EndOfFile: 24
WriteFile SUCCESS Offset: 0, Length: 24, Priority: Normal
ReadFile SUCCESS Offset: 0, Length: 24, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal
SetBasicInformationFile SUCCESS 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: n/a
QueryRemoteProtocolInformation INVALID PARAMETER
CloseFile SUCCESS

EDIT1:

Here is get-acl D:\LOCKED | fl response:

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)

You can reproduce full behaviour (folder creation and file copy) with my C# example program at https://github.com/KiwiJaune/CantMove/blob/master/Program.cs.

EDIT2:

Here is fltmc result on one of the several tested PCs:

Filter name Num Instances Altitude Frame
bindflt 1 409800 0
FsDepends 12 407000 0
WdFilter 12 328010 0
storqosflt 0 244000 0
wcifs 4 189900 0
CldFlt 2 180451 0
aksdf 12 145900 0
FileCrypt 0 141100 0
luafv 1 135000 0
npsvctrig 1 46000 0
Wof 8 40700 0
FileInfo 12 40500 0

I discover that it always fails on D:/ or flash drives, but succeeds on C:/, no matter if D: is another physical disk or a partition on the same disk as C: ...

I have the same behaviour on 4 PC tested (3 Win10 & 1 Win7)

Is there a Windows known behaviour ? Thanks in advance for your ideas.

1 Answers

Moving a file from one location to another on the same drive just causes Windows to move its directory location, moving to a location on a different drive causes Windows to copy the file to the destination and then delete the original file.

When copying a file using the API, Windows appears to use two methods:

  1. Windows create an empty destination file and then opens it to write the data.
  2. Windows creates the file at the destination in write mode to add the data.

This is what is happening in your program, the first time you call to Move the file, Windows is using method 1... subsequent calls are using method 2, the problem is that method 1 requires Modify permission on the directory. Effectively, Windows creates the empty file but cannot then open the file to add the data, this is borne out by the empty file that is being created in D:\LOCKING, giving Modify permission to the destination directory resolves this issue, alternatively create the temporary files on the same drive as the intended destination.

Related