Win32 CopyFileEx and CopyFile2 both have an option flag
COPY_FILE_OPEN_SOURCE_FOR_WRITE where the docs state:
COPY_FILE_OPEN_SOURCE_FOR_WRITE 0x00000004
The file is copied and the original file is opened for write access.
Now, this sounds straightforward, but when I use this flag and try to copy a file that is already opened with "write lock" by someone else, that is no FILE_SHARE_WRITE, but FILE_SHARE_READ, the file is still being copied!
Here is what ProcMon shows when calling this function. Note that these CreateFile events are from a single call to the CopyFileEx function and I omitted the other CreateFile call to the target file:
(sorry for the cinemascope data)
# Without COPY_FILE_OPEN_SOURCE_FOR_WRITE
CreateFile D:\tmp\cpy.txt SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened
CreateFile D:\tmp\cpy.txt SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened
# With COPY_FILE_OPEN_SOURCE_FOR_WRITE
CreateFile D:\tmp\cpy.txt SUCCESS Desired Access: Generic Read/Write, Disposition: Open, Options: Sequential Access, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened
CreateFile D:\tmp\cpy.txt SUCCESS Desired Access: Generic Read/Write, Disposition: Open, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened
# Without, but file already opened prior by someone else as GENERIC_READ+WRITE + FILE_SHARE_READ (deny write)
CreateFile D:\tmp\cpy.txt SHARING VIOLATION Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a
CreateFile D:\tmp\cpy.txt SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
CreateFile D:\tmp\cpy.txt SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
# *With*, but file already opened prior by someone else as GENERIC_READ+WRITE + FILE_SHARE_READ (deny write)
CreateFile D:\tmp\cpy.txt SHARING VIOLATION Desired Access: Generic Read/Write, Disposition: Open, Options: Sequential Access, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a
CreateFile D:\tmp\cpy.txt SHARING VIOLATION Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a
CreateFile D:\tmp\cpy.txt SHARING VIOLATION Desired Access: Generic Read/Write, Disposition: Open, Options: Sequential Access, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a
CreateFile D:\tmp\cpy.txt SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
CreateFile D:\tmp\cpy.txt SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Sequential Access, Synchronous IO Non-Alert, Non-Directory File, Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
As we can see, if noone has access to the file before hand, it is indeed opened with Generic Read/Write access.
However, if you look at the non-flag case and especially at the case where someone else already opened the file for writing, I fail to see what this flag actually gains:
- Since in both "exclusive" cases, the file is opened with
ShareMode: Read, Delete(no share write), noone else can open it for writing anyways in both cases. - In both "shared" cases, if someone else already opened it for writing, the function internally falls back to
Desired Access: Generic Read ... ShareMode: Read, Write, Deleteanyways.
So,
- the flag doesn't prevent anyone from opening the file with read access.
- write access is already locked out by ShareMode Read only in the non-flag case
- in case it doesn't work, it silently falls back to the non flag case.
So what'the'heck is this flag supposed to achieve? What is the Use Case?
Courtesy of Old New Thing Comment Power:
... the MoveFileWithProgress shenanigans will use this flag to have
CopyFileExtry to open the source file in a writable way so that its callback handler can potentially do some modifying operations on the source handle once it gets to the finished state?So the general use case could be phrased: Use this flag if your callback handler wants to modify something on the source file. But there’s no guarantee (that is, no failure on the side
CopyFileExitself) that the source file will be opened in write mode, it’s just best effort.