Winapi LockFileEx可以与卷句柄一起使用吗?

Winapi LockFileEx可以与卷句柄一起使用吗?,winapi,ntfs,ntfs-mft,defragmentation,Winapi,Ntfs,Ntfs Mft,Defragmentation,我正在试验FSCTL\u MOVE\u文件。大部分情况下,一切正常。然而,有时如果我试图重新读取(通过FSCTL\u GET\u NTFS\u FILE\u RECORD)我刚刚移动的Mft记录,我会得到一些错误的数据 具体地说,如果文件记录说$ATTRIBUTE_LIST属性是非常驻的,并且我使用卷句柄从磁盘读取数据,我会发现那里的数据内部不一致(记录长度大于数据的实际长度) 当我看到这种情况发生时,原因就很清楚了:在Ntfs驱动程序完成编写之前,我正在读取记录。调试支持这一理论。但知道这一点

我正在试验
FSCTL\u MOVE\u文件
。大部分情况下,一切正常。然而,有时如果我试图重新读取(通过
FSCTL\u GET\u NTFS\u FILE\u RECORD
)我刚刚移动的Mft记录,我会得到一些错误的数据

具体地说,如果文件记录说$ATTRIBUTE_LIST属性是非常驻的,并且我使用卷句柄从磁盘读取数据,我会发现那里的数据内部不一致(记录长度大于数据的实际长度)

当我看到这种情况发生时,原因就很清楚了:在Ntfs驱动程序完成编写之前,我正在读取记录。调试支持这一理论。但知道这一点并不能帮我解决问题。我对
FSCTL\u MOVE\u FILE
调用使用了同步方法,但显然文件系统仍然可以在后台更新内容。嗯

在普通文件中,我会考虑使用共享锁的
LockFileEx
(因为我只是在阅读)。但我不确定这对卷句柄有什么意义?我甚至不太确定Ntfs在内部使用这种机制来确保一致性

不过,这似乎是一个开始。但是我对卷句柄的
LockFileEx
调用正在返回
ERROR\u INVALID\u参数
。我不知道哪个参数可能出错,除非是卷句柄本身。也许他们只是不支持锁?或者在打开卷句柄时,我应该在
CreateFile
中设置一些特殊标志?我已尝试启用
SE\u BACKUP\u NAME
FILE\u FLAG\u BACKUP\u SEMANTICS
,但错误保持不变

展望未来,我可以在这里看到一些备选方案:

  • 了解如何使用卷句柄锁定节(希望Ntfs驱动程序也这样做)。在这一点上似乎是可疑的
  • 找出如何刷新我刚移动的文件的元数据(注意:FlushFileBuffers for the MOVE\u file\u data.FileHandle没有帮助。可能刷新卷句柄?)
  • 是否有一些“官方”方法可以读取不涉及卷句柄的
    ReadFile
    的非常驻数据?我没找到,但也许我错过了
  • 移动数据后等待“一点”,让驱动程序完成所有更新。恶心
  • FWIW,这里有一些针对卷句柄执行LockFileEx的测试代码。请注意,您必须以管理员身份运行才能锁定卷句柄。我正在使用
    J:
    ,因为那是我的闪存驱动器。50000是随机挑选的,但应该小于闪存驱动器的大小

    void Lock()
    {
        WCHAR path[] = L"\\\\.\\j:";
    
        HANDLE hRootHandle = CreateFile(path,
                                 GENERIC_READ, 
                                 FILE_SHARE_READ | FILE_SHARE_WRITE, 
                                 NULL, 
                                 OPEN_EXISTING, 
                                 0, 
                                 NULL);
    
        OVERLAPPED olap;
        memset(&olap, 0, sizeof(olap));
        olap.Offset = 50000;
    
        // Lock 1k of data at offset 50000
        BOOL b = LockFileEx(hRootHandle, 1, 0, 1024, 0, &olap);
        DWORD j = GetLastError();
    
        CloseHandle(hRootHandle);
    }
    
    查看坏数据的代码是。。。相当复杂。然而,它很容易复制。当它失败时,我最终尝试读取长度为“0”的可变长度$ATTRIBUTE_列表项,这会导致一个无限循环,因为看起来我从未完成整个缓冲区的读取。如果长度为零,我就退出,但我担心缓冲区中的“剩余垃圾”而不是干净的零。检测到这一点是不可能的,所以我希望有一个更好的解决方案

    毫不奇怪,关于这一切的信息并不多。因此,如果有人在这里有一些经验,我可以使用一些见解


    编辑1:

    更多不太有效的事情:

    • 在LockFileEx上仍然没有运气
    • 我试着冲洗音量手柄(正如保罗建议的那样)。虽然这样做有效,但它使我的执行时间增加了一倍多。严格地说,它仍然不能解决问题。仍然不能保证Ntfs不会在FlushFileBuffers和FSCTL\u GET\u Ntfs\u FILE\u RECORD/ReadFile之间再改变一些东西
    • 我想知道$STANDARD_信息属性的“RecordChanged”时间戳。但是,它并没有因为属性列表的这些更改而更改
    • 对文件进行碎片化最终会导致添加属性列表,并且随着碎片化的继续增加,更多的数据记录将添加到该列表中。添加数据记录时,UpdateSequenceEnumber(不是MFT_段_引用的一部分,而是另一个)将被更新。不幸的是,有一系列事件要执行此更新。显然,属性_LIST buffer'length'在'updateSequenceEnumber'之前更新。因此,查看“UpdateSequenceEnumber”是否已更改无助于避免读取(潜在的)错误信息

    我下一个最好的想法是看看Ntfs是否总是在更新记录长度之前(或者在记录长度缩短时)将新字节归零。如果我可以依赖于记录长度为零(而不是任何可能占用这些字节的剩余数据),我可以假装将其称为固定。

    您的问题的解决方案似乎确实是使用卷句柄调用
    FlushFileBuffers()
    。在页面底部附近有这样一句话:

    要刷新卷上所有打开的文件,请使用卷句柄调用FlushFileBuffers。调用方必须具有管理权限

    该页面上的其他信息让我相信,这也会刷新元数据,尽管在这种特定情况下它没有直接这样说。也许你能告诉我这方面的最新情况

    为了从细节上退一步,从更大的角度看一会儿,出于各种原因,必须有一个用于此的API,尽管我认为它可能不是公开的。

    我想我已经有了它

    重申目标:

    在使用
    FSCTL\u GET\u NTFS\u FILE\u RECORD
    从Mft读取记录后,我一直发现
    属性列表
    记录处于“不一致状态”,因此报告的记录长度大于记录中的实际数据量。读取写入内容之外的数据似乎有风险,因为我无法确定所读取的内容是否有效,或者是剩余的垃圾