What is the purpose of CopyFileEx's COPY_FILE_OPEN_SOURCE_FOR_WRITE?

huangapple go评论52阅读模式
英文:

What is the purpose of CopyFileEx's COPY_FILE_OPEN_SOURCE_FOR_WRITE?

问题

Win32的CopyFileExCopyFile2都具有一个选项标志COPY_FILE_OPEN_SOURCE_FOR_WRITE,文档中如下所述:

COPY_FILE_OPEN_SOURCE_FOR_WRITE 0x00000004

复制文件并打开原始文件以进行写访问。

现在,这听起来很简单,但当我使用这个标志并尝试复制一个已由其他人使用"写锁"打开的文件,即没有FILE_SHARE_WRITE,而是FILE_SHARE_READ时,文件仍然被复制!

以下是调用此函数时ProcMon所显示的内容。请注意,这些CreateFile事件来自对CopyFileEx函数的单个调用,我省略了对目标文件的其他CreateFile调用:

(对于电影格式的数据,我很抱歉)

没有COPY_FILE_OPEN_SOURCE_FOR_WRITE

CreateFile D:\tmp\cpy.txt SUCCESS 所需访问权限:通用读取,处理:打开,选项:顺序访问,非目录文件,打开重解析点,属性:n/a,共享模式:读取,删除,分配大小:n/a,打开结果:已打开
CreateFile D:\tmp\cpy.txt SUCCESS 所需访问权限:通用读取,处理:打开,选项:顺序访问,同步IO非警报,非目录文件,打开重解析点,属性:n/a,共享模式:读取,删除,分配大小:n/a,打开结果:已打开

使用COPY_FILE_OPEN_SOURCE_FOR_WRITE

CreateFile D:\tmp\cpy.txt SUCCESS 所需访问权限:通用读取/写入,处理:打开,选项:顺序访问,非目录文件,打开重解析点,属性:n/a,共享模式:读取,删除,分配大小:n/a,打开结果:已打开
CreateFile D:\tmp\cpy.txt SUCCESS 所需访问权限:通用读取/写入,处理:打开,选项:顺序访问,同步IO非警报,非目录文件,打开重解析点,属性:n/a,共享模式:读取,删除,分配大小:n/a,打开结果:已打开

没有标志,但文件已由他人以GENERIC_READ+WRITE + FILE_SHARE_READ(拒绝写入)打开

CreateFile D:\tmp\cpy.txt SHARING VIOLATION 所需访问权限:通用读取,处理:打开,选项:顺序访问,非目录文件,打开重解析点,属性:n/a,共享模式:读取,删除,分配大小:n/a
CreateFile D:\tmp\cpy.txt SUCCESS 所需访问权限:通用读取,处理:打开,选项:顺序访问,非目录文件,打开重解析点,属性:n/a,共享模式:读取,写入,删除,分配大小:n/a,打开结果:已打开
CreateFile D:\tmp\cpy.txt SUCCESS 所需访问权限:通用读取,处理:打开,选项:顺序访问,同步IO非警报,非目录文件,打开重解析点,属性:n/a,共享模式:读取,写入,删除,分配大小:n/a,打开结果:已打开

标志,但文件已由他人以GENERIC_READ+WRITE + FILE_SHARE_READ(拒绝写入)打开

CreateFile D:\tmp\cpy.txt SHARING VIOLATION 所需访问权限:通用读取/写入,处理:打开,选项:顺序访问,非目录文件,打开重解析点,属性:n/a,共享模式:读取,删除,分配大小:n/a
CreateFile D:\tmp\cpy.txt SHARING VIOLATION 所需访问权限:通用读取,处理:打开,选项:顺序访问,非目录文件,打开重解析点,属性:n/a,共享模式:读取,删除,分配大小:n/a
CreateFile D:\tmp\cpy.txt SHARING VIOLATION 所需访问权限:通用读取/写入,处理:打开,选项:顺序访问,非目录文件,打开重解析点,属性:n/a,共享模式:读取,写入,删除,分配大小:n/a
CreateFile D:\tmp\cpy.txt SUCCESS 所需访问权限:通用读取,处理:打开,选项:顺序访问,非目录文件,打开重解析点,属性:n/a,共享模式:读取,写入,删除,分配大小:n/a,打开结果:已打开
CreateFile D:\tmp\cpy.txt SUCCESS 所需访问权限:通用读取,处理:打开,选项:顺序访问,同步IO非警报,非目录文件,打开重解析点,属性:n/a,共享模式:读取,写入,删除,分配大小:n/a,打开结果:已打开

正如我们所看到的,如果没有人事先访问该文件,它确实以“通用读取/写入”权限打开。

然而,如果看看没有标志的情况,特别是当其他人已经以写入方式打开文件时,我不明白这个标志实际上可以实现什么:

  • 由于在两种情况下文件都以ShareMode: Read, Delete(不共享写入)打开,因此无论如何都无法再以写入方式打开它。
  • 两种“共享”情况下,如果其他人已经以写入方式打开它,该函数会内部回退所需访问权限:通用读取... 共享模式:读取,写入,删除

所以,

  • 该标志不能阻止任何人以读取权限打开文件。
  • 写入权限已经
英文:

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, Delete anyways.

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?

答案1

得分: 1

来自Old New Thing评论功能

> Malcolm Smith
>
> 它是为了支持MoveFileWithProgress,特别是当移动的文件正在跨卷移动并且是快捷方式的目标时。...
> 一旦文件被“移动”到新卷,此信息将从移动的源中删除,并应用于移动的目标,然后删除源。从源中删除是一种修改。标志只是由于代码结构的方式,
> 其中复制打开句柄。

... MoveFileWithProgress 恶作剧将使用此标志,以便 CopyFileEx 尝试 以可写方式打开源文件,以便在其回调处理程序在达到完成状态时可能执行一些修改操作吗?

因此,一般用例可以表述为:如果您的回调处理程序想要修改源文件上的某些内容,请使用此标志。但不能保证(也就是说,CopyFileEx 本身不会失败),源文件将以写模式打开,这只是尽力而为。

> ... 当编写此代码时,它在回调处理程序中执行此更新,因此依赖于复制以打开句柄以进行写入。
> 现在它不再这样做了,但这仍然是添加标志的原因。
>
> 就“尝试”以写入方式打开句柄的方式而言,还请参阅MOVEFILE_FAIL_IF_NOT_TRACKABLE。由于此案例非常狭窄,大多数情况下不需要写入访问权限,因此降级为只读是完全无害的。...
>
> 因此,此标志的剩余公共用途正如您所描述:您可以在回调处理程序中更新源,但必须准备好检测到拒绝访问条件,并在适当的时间执行适当的错误处理

英文:

Courtesy of Old New Thing Comment Power:

> Malcolm Smith
>
> It’s to support MoveFileWithProgress, specifically when the file being
> moved is moving across volumes and is the target of a shortcut. ...
> Once a file is “moved” to a new volume, this information is removed
> from the source of the move and applied to the target of the move,
> then the source is deleted. Removing from the source is a
> modification. The flag is just due to how the code is structured,
> where copy opens the handles.

... the MoveFileWithProgress shenanigans will use this flag to have CopyFileEx try 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 CopyFileEx itself) that the source file will be opened in write mode, it’s just best effort.

> ... When the code was written, it performed this update in
> the callback handler, so depended on copy to open handles for write.
> It doesn’t do that anymore, but that’s still the reason the flag was
> added.
>
> As far as the way it “tries” to open the handles for write, see also
> MOVEFILE_FAIL_IF_NOT_TRACKABLE. Since the case for this is very
> narrow, most of the time write access isn’t required, so downgrading
> to read only is completely benign. ...
>
> So the remaining public use for this is exactly as you describe: you
> can update the source in a callback handler, but must be prepared to
> detect access denied conditions and perform appropriate error handling
> at the time
.

huangapple
  • 本文由 发表于 2023年2月9日 00:25:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75388794.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定