关于winapi:TRUNCATE_EXISTING与OPEN_EXISTING + SetEndOFile

TRUNCATE_EXISTING vs. OPEN_EXISTING+SetEndOFile

用TRUNCATE_EXISTING调用Windows的CreateFile并用OPEN_EXISTING调用它,然后再调用SetEndOfFile有什么区别?

前者被证明需要GENERIC_WRITE访问权限,如果我只要求FILE_WRITE_DATA,则CreateFile会失败,并显示ERROR_ACCESS_DENIED。

对于后者,FILE_WRITE_DATA就足够了,CreateFile和SetEndOfFile都成功。


FILE_WRITE_DATA是单访问位(2)。当GENERIC_WRITE是常规访问时(映射到FILE_GENERIC_WRITE中的案例文件中),您可以查看其定义:

1
2
3
4
5
6
#define FILE_GENERIC_WRITE        (STANDARD_RIGHTS_WRITE    |\\
                                   FILE_WRITE_DATA          |\\
                                   FILE_WRITE_ATTRIBUTES    |\\
                                   FILE_WRITE_EA            |\\
                                   FILE_APPEND_DATA         |\\
                                   SYNCHRONIZE)

因此,除了FILE_WRITE_DATA之外,还包括FILE_WRITE_ATTRIBUTESFILE_WRITE_EAFILE_APPEND_DATAREAD_CONTROL==STANDARD_RIGHTS_WRITE。 (CreateFile总是隐式要求SYNCHRONIZE)

如果您为GENERIC_WRITE获得了ERROR_ACCESS_DENIED,但对于FILE_WRITE_DATA则获得了正常,则意味着您具有对文件的FILE_WRITE_DATA访问权限,但没有对FILE_GENERIC_WRITE的其他访问权限。这种情况很少,但有可能。

但实际上,截断文件所需的全部是FILE_WRITE_DATA访问。这是错误的,并且是Win32 API CreateFile的非常糟糕的设计。

对于将文件截断为零大小,我们可以使用或:

  • NtSetInformationFileFileEndOfFileInformation(
    呼叫者必须已打开设置了FILE_WRITE_DATA标志的文件
    在DesiredAccess参数中)或
    FileAllocationInformation-在所有窗口上均可使用
  • SetFileInformationByHandle-非常薄的win32 shell
    NtSetInformationFile,但仅可从Vista获得。与
    FileEndOfFileInfoFileAllocationInfo。请注意,如果我们设置
    AllocationSize设为0-文件也将被截断为0大小。

    The end-of-file (EOF) position for a file must always be less than or
    equal to the file allocation size. If the allocation size is set to a
    value that is less than EOF, the EOF position is automatically
    adjusted to match the file allocation size.

  • SetEndOfFile首先调用ZwQueryInformationFile
    FilePositionInformation(在打开文件句柄后将其设置为0)
    然后使用它(FILE_POSITION_INFORMATION)两次通话
    FileEndOfFileInformationFileEndOfFileInformation
    FileAllocationInformation。很明显,在这种情况下,我们有2个没有
    需要额外调用内核。所以这效率不高先比较2
    方法。
  • CreateFileTRUNCATE_EXISTING的第一个打开文件,然后
    设置FileAllocationInformation调用NtSetInformationFile
    设为0。但是,此调用方式(您如何查看)需要额外的访问权限-
    FILE_GENERIC_WRITE确实不需要。只需要
    FILE_WRITE_DATA。所以这样不好
  • NtCreateFileFILE_OVERWRITE一起使用
    CreateDisposition-在单个调用打开文件中并将EOF设置为0。通过意义TRUNCATE_EXISTING必须执行此操作(使用FILE_OVERWRITE
    处置)。但是由于未知的原因(我认为这是错误),它使用
    FILE_OPEN访问,并有额外的呼叫
    NtSetInformationFileFileAllocationInformation
  • 如果我们不仅需要截断现有文件,还需要创建新文件
    空文件(如果尚不存在)-最好使用FILE_OVERWRITE_IF
    选项或与之对应的CREATE_ALWAYS-这也打开并
    在对内核的单次调用中截断文件。或创建新文件