我正在试验FSCTL_MOVE_FILE
. 大多数情况下,一切都按预期工作。但是,有时如果我尝试重新读取(通过FSCTL_GET_NTFS_FILE_RECORD
)我刚刚移动的 Mft 记录,我会得到一些错误的数据。
具体来说,如果文件记录说 $ATTRIBUTE_LIST 属性是非常驻的,我使用我的卷句柄从磁盘读取数据,我发现那里的数据内部不一致(记录长度大于数据的实际长度) .
我一看到这种情况,原因就很清楚了:我在 Ntfs 驱动程序完成写入之前读取记录。调试支持这一理论。但是知道这并不能帮助我解决它。我正在使用同步方法进行FSCTL_MOVE_FILE
调用,但显然文件系统仍然可以在后台更新内容。唔。
在普通文件中,我会考虑LockFileEx
使用共享锁(因为我只是在阅读)。但我不确定这对音量句柄有什么意义?而且我更不确定 Ntfs 在内部使用这种机制来确保一致性。
不过,这似乎是一个开始的地方。但是我LockFileEx
对音量句柄的调用正在返回ERROR_INVALID_PARAMETER
。我没有看到哪个参数可能出错,除非它是音量句柄本身。也许他们只是不支持锁?CreateFile
或者也许在打开音量手柄时我应该设置一些特殊的标志?我尝试启用SE_BACKUP_NAME
and FILE_FLAG_BACKUP_SEMANTICS
,但错误保持不变。
展望未来,我可以在这里看到一些替代方案:
- 弄清楚如何使用卷句柄锁定部分(并希望 Ntfs 驱动程序也这样做)。在这一点上似乎很可疑。
- 弄清楚如何刷新我刚刚移动的文件的元数据(nb:MOVE_FILE_DATA.FileHandle 的 FlushFileBuffers 没有帮助。也许刷新卷句柄?)。
- 是否有一些“官方”方法可以读取不涉及
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_LIST 条目,这会导致无限循环,因为看起来我从未完成读取整个缓冲区。如果长度为零,我正在通过退出来解决它,但我担心缓冲区中的“剩余垃圾”而不是干净的零。检测到这是不可能的,所以我希望有更好的解决方案。
毫不奇怪,关于这方面的信息并不多。因此,如果有人在这里有一些经验,我可以使用一些见解。
编辑1:
更多不太有效的事情:
- LockFileEx 仍然没有运气。
- 我尝试冲洗音量手柄(如保罗建议的那样)。虽然这有效,但它使我的执行时间增加了一倍以上。而且,严格来说,它仍然不能解决问题。仍然不能保证 Ntfs 不会在 FlushFileBuffers 和 FSCTL_GET_NTFS_FILE_RECORD / ReadFile 之间做出更多改变。
- 我想知道 $STANDARD_INFORMATION 属性的“RecordChanged”时间戳。但是,由于对 ATTRIBUTE_LIST 的这些更改,它没有被更改。
- 对文件进行分段最终会导致添加一个 ATTRIBUTE_LIST,并且随着分段的不断增加,更多的 DATA 记录将添加到该列表中。添加 DATA 记录后,UpdateSequenceNumber(不是 MFT_SEGMENT_REFERENCE 的一部分,另一个)会更新。不幸的是,有一系列事件可以执行此更新。显然,ATTRIBUTE_LIST 缓冲区“长度”在“UpdateSequenceNumber”之前更新。因此,查看“UpdateSequenceNumber”是否已更改无助于避免读取(可能)错误信息。
我的下一个最佳想法是看看 Ntfs 是否总是在更新记录长度之前将新字节归零(或者可能每当记录长度缩小时?)。如果我可以依赖记录长度为零(而不是任何剩余数据可能占用这些字节),我可以假装称之为固定。