2

在 UNIX 中,如果我以附加模式打开文件,例如

fd = open("filename", O_APPEND);

然后给定这样一个文件描述符,我们可以很容易地找出它是用什么标志打开的fcntl

fcntl(fd, F_GETFL) & O_APPEND

我知道这fcntl在 Windows 上不可用,但我想知道是否有某种方法可以确定这一点。Windows 确实支持附加模式,例如在创建带有标志的文件CreateFile并传入FILE_APPEND_DATA标志时。

但是,如果我所拥有的只是一个已打开文件的句柄,那么我终生无法找到一种方法来确定首次打开文件时请求的访问权限。 这个问题提供了检查对特定文件的访问权限的基本方法,但这似乎没有帮助。我试过了,即使我以只读模式打开一个文件,它仍然告诉我如果我要请求它,我可以FILE_APPEND_DATA 访问该文件。换句话说,这个方法只告诉我进程对特定文件的访问权限(继承自启动进程的用户)。它没有说明打开文件时请求的确切访问权限。

这与 Windows 如何跟踪文件是否应仅附加到无关。正是后一个问题,我在任何地方都找不到答案。我发现的最接近的东西是GetFileInformationByHandleEx但是在梳理文档之后,没有一个文件属性可以通过该 API 返回,表明“附加模式”。

更新:为了更好地澄清我的问题,这个问题实际上只适用于 MS VC 运行时库——使用类似 POSIX 的函数打开_open并写入 withfwrite等的文件。似乎本机 win32 文件句柄没有“附加模式”的概念。

4

4 回答 4

4

对于Windows认为处于“附加”模式的文件,这将起作用:

使用半文档化的NtQueryInformationFile系统调用(从 导出ntdll.dll)来查询FILE_ACCESS_INFORMATION. 这应该告诉您打开文件的访问掩码,其中应该包含FILE_APPEND_DATA.

但是,对于C 运行时以“追加”模式打开的文件,这不起作用,因为 Windows 实际上并没有以追加模式打开它。这样做的方法是以某种方式将文件描述符转换为文件对象,并检查那里的标志——但没有记录的方法可以做到这一点。

您可以查看 Visual C++ CRT 源代码以了解如何操作……可能有某种方法可以访问描述符索引的数组,但我不确定如何。
CRT 中的代码似乎_pioinfo(fd)->osfile |= FAPPEND很有帮助。

于 2013-08-30T21:31:19.997 回答
4

FileMode.Append 是 .NET Framework 团队的想象。它不是 Windows 支持的模式。他们添加它是为了使本机 winapi CreateFile() 函数 (FileStream) 的包装器更易于使用。FileMode 是其 dwCreationDisposition 参数的枚举包装器。没有 CREATE_APPEND 选项。

FileStream.Init() 方法中最相关的代码是:

   bool seekToEnd = (mode==FileMode.Append);
   // Must use a valid Win32 constant here...
   if (mode == FileMode.Append)
       mode = FileMode.OpenOrCreate;

换句话说,FileMode.Append 与有效的 CreateFile() 选项不匹配,必须进行映射。该文件将在存在时打开,如果不存在则创建。它负责一个额外的操作,打开文件后,它会寻找文件的末尾。当您附加到现有文件时,您当然会期望它。存在一些额外的错误检查,它还确保您打开文件进行写入。

因此,这反而会在您检测到这一点的过程中留下一个洞。GetFileInformationByHandleEx() winapi 函数可用于恢复文件状态。Mehrdad 的未记录本机 api 调用的记录函数。您可以从 FileDispositionInfo 中获取 dwCreationDisposition 参数,以检查它是否为 OpenOrCreate。然而,这并不是唯一的,它也可以从客户端代码中使用 FileMode.OpenOrCreate 打开的文件开始。罕见,但可能。

你失去的是它寻找文件末尾的记忆。您可以使用 FileStream.Position 属性,但同时该属性当然会受到任何写入的影响。如果这真的很重要,那么您必须保留使用的 FileMode 值。

于 2013-08-30T23:26:09.390 回答
2

自我回答,感谢@mehrdad 和@HansPassant 为我指明了正确的方向。实际上,MSVCRT 导出一个结构数组的数组,该结构称为ioinfo它存储有关进程中每个打开文件句柄的信息。

该结构的确切内容取决于 VC 版本和一些定义,但通常定义了它的前两个成员:

typedef struct {
        intptr_t osfhnd;    /* underlying OS file HANDLE */
        char osfile;        /* attributes of file (e.g., open in text mode?) */
        ....
} ioinfo;

osfile成员是一个有趣的成员——如果打开文件时设置了名为defined as_O_APPEND的标志。FAPPEND0x20

我基于 CPython 的 posixmodule 中的类似代码在 Python 中编写了一个小实用函数,可以执行此检查:https ://gist.github.com/embray/6444262

于 2013-09-04T23:40:44.933 回答
2

(我知道这是 2013 年以来的一个非常古老的问题,但我想与任何想要直接从 Windows 文件句柄获取文件模式[READ、WRITE、APPEND]的人分享我的解决方案)

NtQueryInformationFile api 实际上可以为您提供足够的信息来确定文件处于什么模式。

让我们使用以下代码进行演示:

IO_STATUS_BLOCK statusBlock;
FILE_ACCESS_INFORMATION accessInfo;

// You have to import this API by yourself using LoadLibary and GetProcAddress
auto status = NtQueryInformationFile(
   yourFileHandle, &statusBlock, &accessInfo,
   sizeof(FILE_ACCESS_INFORMATION), (FILE_INFORMATION_CLASS)8);

auto flags = accessInfo.AccessFlags;

// true if in read mode, otherwise false
auto isRead = (FILE_READ_DATA & flags) != 0;
// true if in write mode, otherwise false
auto isWrite = (FILE_WRITE_DATA & flags) != 0;
// true if in write mode or append mode 
// (I think these 2 modes are mutual exclusive), otherwise false
auto isAppend = (FILE_APPEND_DATA & flags) != 0;

其实很简单:

  • 如果文件是使用 GENERIC_READ(或 FILE_READ_DATA)打开的,则设置 FILE_READ_DATA 标志,否则不设置;
  • 如果文件是使用 GENERIC_WRITE(或 FILE_WRITE_DATA)打开的,则设置 FILE_WRITE_DATA 标志,否则不设置;
  • 如果文件是使用 GENERIC_WRITEFILE_APPEND_DATA 打开的,则设置 FILE_APPEND_DATA 标志,否则不设置;

读取模式本身不会打开 FILE_APPEND_DATA 标志,附加模式本身不会打开 FILE_WRITE_DATA 标志。

我希望这可以帮助别人。

于 2020-08-21T06:11:11.593 回答