代码之家  ›  专栏  ›  技术社区  ›  David Wohlferd

CuffFielEX可以与卷柄一起使用吗?

  •  1
  • David Wohlferd  · 技术社区  · 6 年前

    我在试验 FSCTL_MOVE_FILE . 大多数情况下一切都按预期进行。但是,有时如果我试图重读(通过 FSCTL_GET_NTFS_FILE_RECORD )我刚移动的mft记录,我得到了一些坏数据。

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

    我一看到这种情况,原因就很清楚了:在NTFS驱动程序写完之前,我正在阅读记录。调试支持这一理论。但知道这并不能帮我解决问题。我使用同步方法 fsctl_move_文件 调用,但显然文件系统仍然可以在后台更新内容。隐马尔可夫模型。

    在正常的档案里,我会想 LockFileEx 有一个共享锁(因为我只是在看书)。但我不确定这对音量控制有什么意义?我更不确定ntfs在内部使用这种机制来确保一致性。

    不过,这似乎是个开始的地方。但是我的 锁定文件 对卷句柄的调用正在返回 ERROR_INVALID_PARAMETER . 我不知道哪个参数可能出错,除非是卷句柄本身。也许他们就是不支持锁?或者我应该插上一些特别的旗子 CreateFile 打开音量手柄时?我试过启用 SE_BACKUP_NAME FILE_FLAG_BACKUP_SEMANTICS ,但错误保持不变。

    向前看,我可以看到以下几种选择:

    1. 找出如何使用卷句柄锁定分区(并希望ntfs驱动程序也这样做)。在这一点上似乎很可疑。
    2. 找出如何刷新刚刚移动的文件的元数据(注意:flushfilebuffers for the move_file_data.filehandle没有帮助)。可能冲洗音量手柄?).
    3. 是否有一些“官方”方法来读取非本地数据 ReadFile 对着音量手柄?我没有找到,但也许我错过了。
    4. 移动数据后等待“一点”,让驱动程序完成所有更新。讨厌。

    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条目,这会导致无限循环,因为看起来我从未读取完整个缓冲区。如果长度为零,我就退出它,但我担心缓冲区中的“剩余垃圾”而不是干净的零。发现那是不可能的,所以我希望有更好的解决方案。

    不足为奇的是,这方面的信息并不多。所以如果有人有经验的话,我需要一些洞察力。


    编辑1:

    更多不太管用的事情:

    • 洛克菲莱克斯还是不走运。
    • 我试着冲洗音量手柄(正如保罗建议的那样)。虽然这有效,但我的执行时间却增加了一倍多。而且,严格地说,它仍然不能解决问题。仍然不能保证ntfs不会在flushfilebuffers和fsctl-get-ntfs-file-record/readfile之间做更多的改变。
    • 我想知道$standard_information属性的“recordchanged”时间戳。但是,由于属性列表的这些更改,它不会被更改。
    • 对文件进行分段最终会导致添加属性列表,并且随着分段的继续增加,将有更多的数据记录添加到该列表中。当数据记录被添加时,updatesequencenumber(不是mft_段引用的一部分,而是另一部分)将被更新。不幸的是,有一系列事件要执行此更新。显然,属性列表缓冲区'length'在'updatesequencenumber'之前更新。因此,查看“updatesequencenumber”是否已更改,无助于避免读取(潜在的)错误信息。

    我的下一个最佳想法是,在更新记录长度之前(或者在记录长度缩减时),看看ntfs是否总是将新字节归零。如果我可以将记录长度设为零(而不是剩余的数据可能占用这些字节),我可以假装调用这个固定的。

    2 回复  |  直到 6 年前
        1
  •  2
  •   catnip    6 年前

    你的问题的解决办法似乎确实是 FlushFileBuffers() 有音量的手柄。在页面底部附近 MSDN 有话要说:

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

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

    从细节上退一步,看一下大局 在某个地方成为一个api,有各种各样的原因,尽管我认为它可能不是公共的。

        2
  •  1
  •   David Wohlferd    6 年前

    我想我明白了。

    重申目标:

    使用后 FSCTL_GET_NTFS_FILE_RECORD 为了阅读MFT的记录,我发现 ATTRIBUTE_LIST 记录处于“不一致的状态”,使得报告的记录长度大于记录中的实际数据量。除了写的东西之外,阅读数据似乎是有风险的,因为我不能确定我所读的内容是否有效,或者说是垃圾。

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

    1. 在卷上使用lockfileex(在我开始的时候,这似乎是最好的答案)完全不是一个开始。rbmm&eryksun(以及我自己的实验)提供了一些非常有说服力的证据,这只是行不通。正如lockFileEx中的“file”所暗示的,此函数仅对文件起作用。
    2. 冲洗音量手柄可以消除症状。但在表演中却被罚了一大笔(100%)。目前还不清楚这个问题是真的解决了,还是仅仅隐藏在导致经济放缓的背后。
    3. “其他一些”api读取非驻留数据的想法似乎是神话般的。
    4. 在完成一个 FSCTL_MOVE_FILE 不是计划,是希望。

    对于一个 brief time ,检查ntfsrecord中的updatesequencenumber可能会提供一个解决方案。但是,ntfs在更新记录时使用的事件顺序意味着属性列表的记录长度在updatesequencenumber之前得到更新(很好)。

    但后来我开始考虑这到底是什么时候的问题。如果我忽视它,它会在哪里失败?

    目前,随着属性列表的增长,我遇到了这个问题(因为我是故意对一个文件进行大量碎片化)。在那个时候,由于记录长度为零,它很容易被检测到。我已经运行了很多次这个程序,虽然它只是一个传闻,但随着记录的增长,额外的空间总是归零。这是有意义的,因为在第一次分配缓冲区时会将整个缓冲区归零。标准编程实践和观察都支持这一结论。

    但是当唱片开始缩水的时候呢?或者萎缩然后生长?你能用剩下的数据代替(容易解释的)零吗?

    然后我想到:属性列表 永不退缩 . 我是 just complaining 几周前的事。即使当你完全整理文件并且不再需要所有这些额外的数据记录时,ntfs也不会压缩它们。现在我第一次看到了为什么会这样。有一个 possibility 这在w10中可能会发生变化,但这可能只是对未记录的函数过于乐观的解释。

    所以,我不必担心读取垃圾数据(可能包括一个无意义的记录长度,这会导致缓冲区溢出)。属性列表中的记录长度可以被信任。最后一条记录可能只有零记录长度。

    我可以忽略零长度记录(基本上返回预增长信息)或重读记录直到Update StimeNeNoNobe改变(指示更新完成)。

    Tada。

    推荐文章