LockFileEx可以与Volume Handles一起使用吗?

时间:2018-06-10 00:46:00

标签: winapi ntfs ntfs-mft defragmentation

我正在试验FSCTL_MOVE_FILE。一切都按预期工作。但是,有时如果我尝试重新阅读(通过FSCTL_GET_NTFS_FILE_RECORD)移动的Mft记录,我会收到一些不良数据。

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

当我看到这种情况发生时,原因很明显:我在Ntfs驱动程序完成编写之前读取了记录。调试支持这一理论。但知道这并没有帮助我解决它。我使用同步方法进行FSCTL_MOVE_FILE调用,但显然文件系统仍然可以在后台更新内容。 HMM。

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

不过,它似乎是一个开始的地方。但我对卷句柄的LockFileEx调用正在返回ERROR_INVALID_PARAMETER。我没有看到哪个参数可能出错,除非它是音量句柄本身。也许他们只是不支持锁?或者在打开音量控制柄时,我可能会在CreateFile中设置一些特殊标志?我已尝试启用SE_BACKUP_NAMEFILE_FLAG_BACKUP_SEMANTICS,但错误仍未改变。

继续前进,我可以在这里看到一些替代方案:

  1. 弄清楚如何使用卷句柄锁定节(并希望Ntfs驱动程序也这样做)。在这一点上似乎很可疑。
  2. 弄清楚如何刷新我刚刚移动的文件的元数据(nb:MOVE_FILE_DATA.FileHandle的FlushFileBuffers没有帮助。也许刷新卷句柄?)。
  3. 是否有一些官员'用于读取不会对卷句柄ReadFile涉及的非驻留数据的方法?我没找到,但也许我错过了。
  4. 稍等一下"移动数据后让驱动程序完成所有更新。呸。
  5. 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_LIST条目。 length,导致无限循环,因为它看起来像我从未读完整个缓冲区。如果长度为零,我会通过退出来解决它,但我担心剩下的垃圾"在缓冲区而不是干净的零。检测到这是不可能的,所以我希望有更好的解决方案。

    毫不奇怪,没有任何关于此的信息。所以如果有人在这里有一些经验,我可以使用一些见解。

    编辑1:

    更多不能发挥作用的事情:

    • LockFileEx仍然没有运气。
    • 我试着冲洗音量手柄(正如保罗建议的那样)。虽然这有效,但它的执行时间增加了一倍多。而且,严格地说,它仍然没有解决问题。仍然无法保证Ntfs不会在FlushFileBuffers和FSCTL_GET_NTFS_FILE_RECORD / ReadFile之间进行更改。
    • 我想知道' RecordChanged' $ STANDARD_INFORMATION属性的时间戳。但是,由于对ATTRIBUTE_LIST的这些更改,它没有被更改。
    • 对文件进行分段最终会导致添加ATTRIBUTE_LIST,并且随着碎片的不断增加,更多的DATA记录将被添加到该列表中。当添加DATA记录时,UpdateSequenceNumber(不是MFT_SEGMENT_REFERENCE的一部分,另一部分)得到更新。不幸的是,有一系列事件要执行此更新。显然,ATTRIBUTE_LIST缓冲区长度为'在“更新序列号”之前得到更新。因此,看看' UpdateSequenceNumber'已经改变并没有帮助避免阅读(可能)不良信息。

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

2 个答案:

答案 0 :(得分:2)

问题的解决方案确实似乎是通过卷的句柄调用FlushFileBuffers()。靠近页面底部MSDN可以这样说:

  

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

该页面上的其他信息让我相信这也会刷新元数据,尽管在这种特定情况下它并没有直接说明。也许你可以更新我。

要退出细节并暂时查看大图,作为此处的API,出于各种原因,尽管我认为它可能不公开

答案 1 :(得分:1)

我想我已经明白了。

重申目标:

使用FSCTL_GET_NTFS_FILE_RECORD从Mft读取记录后,我一直发现ATTRIBUTE_LIST记录处于“不一致状态”,​​以致报告的记录长度大于实际记录中的数据量。读取超出已写入数据的数据似乎存在风险,因为我无法确定我所阅读的内容是有效的还是剩余的垃圾。

为此,我提出了4个替代方案,我希望能让我解决这个问题。

  1. 在音量上使用LockFileEx(这在我开始时似乎是最好的答案)结果证明是一个完整的非首发。 RbMm& eryksun(以​​及我自己的实验)提供了一些非常有说服力的证据,这些证据确实无法奏效。作为'文件'在LockFileEx中暗示,此函数仅适用于文件。
  2. 冲洗音量手柄会使症状消失。但是在表现上受到了巨大的(> 100%)惩罚。它还不清楚问题是否真正得到解决,或者只是隐藏在这种原因造成的减速背后。
  3. 其他一些'的想法。 api读取非常驻数据似乎是神话般的。
  4. FSCTL_MOVE_FILE之后等待一些(未指明的)时间不是一个计划,这是一个希望。
  5. 对于brief time,看起来检查NtfsRecord中的UpdateSequenceNumber可能会提供解决方案。但是,更新记录时Ntfs使用的事件顺序意味着ATTRIBUTE_LIST的记录长度在UpdateSequenceNumber之前更新(好)。

    然后我开始考虑这可能是一个什么问题。如果我忽略它,它会在哪里失败?

    目前我遇到了问题,因为ATTRIBUTE_LIST正在增长(因为我故意并大规模地分割文件)。那时,由于零记录长度,它很容易被检测到。我已经多次运行该程序,虽然它只是轶事,但随着记录的增长,额外的空间一直被归零。这是有道理的,因为当您第一次分配它时,您将整个缓冲区归零。标准编程实践和观察都支持这一结论。

    但是当唱片开始缩小时呢?或缩小然后增长?你最终可以得到剩余的数据而不是(容易解释的)零吗?

    然后它击中了我:ATTRIBUTE_LIST 永不缩小 。几个星期前,我just complaining就此问题。即使您完全对文件进行碎片整理并且不再需要所有这些额外的DATA记录,Ntfs也不会压缩它们。现在我第一次看到为什么会这样。这可能会在W10中发生possibility,但这可能只是对未记录的函数过于乐观的解释。

    所以,我不需要担心读取垃圾数据(可能包括无意义的记录长度导致我超出缓冲区)。 ATTRIBUTE_LIST中的记录长度可以信任。最后一条记录可能只有零记录长度。

    我可以忽略零长度记录(基本上返回预增长信息)或重新读取记录,直到UpdateSequenceNumber发生变化(表明更新已完成)。

    多田。