LockFileEx 可以与卷句柄一起使用吗?
Can LockFileEx be used with Volume Handles?
我正在试验 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
和 FILE_FLAG_BACKUP_SEMANTICS
,但错误仍然没有改变。
展望未来,我可以在这里看到一些替代方案:
- 弄清楚如何使用卷句柄锁定部分(并希望 Ntfs 驱动程序也这样做)。在这一点上似乎很可疑。
- 弄清楚如何刷新我刚刚移动的文件的元数据(注意:MOVE_FILE_DATA.FileHandle 的 FlushFileBuffers 没有帮助。也许刷新卷句柄?)。
- 是否有一些 'official' 方法可以读取不涉及
ReadFile
卷句柄的非常驻数据?我没有找到,但也许我错过了。
- 移动数据后等待 "a bit" 让驱动程序完成所有更新。呸
FWIW,这是针对卷句柄执行 LockFileEx 的一些测试代码。请注意,您必须 运行 作为管理员才能锁定卷句柄。我正在使用 J:
,因为那是我的闪存驱动器。 50000是随机抽取的,应该小于U盘的大小。
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 条目,这导致无限循环,因为看起来我从未读完整个缓冲区。如果长度为零,我正在通过退出来解决它,但我担心缓冲区中的 "leftover garbage" 而不是干净的零。检测到那是不可能的,所以我希望有更好的解决方案。
毫不奇怪,关于这方面的信息并不多。所以如果有人在这里有一些经验,我可以使用一些见解。
编辑 1:
更多不太有效的东西:
- LockFileEx 仍然不走运。
- 我尝试刷新音量句柄(正如 Paul 所建议的)。虽然这有效,但它使我的执行时间增加了一倍以上。而且,严格来说,还是没有解决问题。仍然不能保证 Ntfs 不会在 FlushFileBuffers 和 FSCTL_GET_NTFS_FILE_RECORD / ReadFile 之间做出更多改变。
- 我想知道 $STANDARD_INFORMATION 属性的 'RecordChanged' 时间戳。但是,由于对 ATTRIBUTE_LIST.
的这些更改,它没有被更改
- 对文件进行分段最终会导致添加 ATTRIBUTE_LIST,并且随着碎片的不断增加,更多的 DATA 记录将被添加到该列表中。添加 DATA 记录时,UpdateSequenceNumber(不是 MFT_SEGMENT_REFERENCE 的一部分,另一个)会更新。不幸的是,执行此更新需要一系列事件。显然 ATTRIBUTE_LIST 缓冲区 'length' 在 'UpdateSequenceNumber' 之前更新。因此,查看 'UpdateSequenceNumber' 是否已更改无助于避免阅读(可能)不良信息。
我的下一个最佳想法是看看 Ntfs 是否总是在更新记录长度之前将新字节清零(或者每当记录长度缩小时?)。如果我可以依赖记录长度为零(而不是任何剩余数据可能占用这些字节),我可以假装称之为固定的。
您的问题的解决方案似乎确实是使用卷句柄调用 FlushFileBuffers()
。在页面底部附近 MSDN 有这样的话:
To flush all open files on a volume, call FlushFileBuffers with a handle to the volume. The caller must have administrative privileges...
该页面上的其他信息使我相信这也会刷新元数据,尽管在这种特定情况下并没有直接说明。也许你可以告诉我这个。
从细节上退一步,看一下大局,出于各种原因, 必须在某处成为 API , 虽然我想它可能不是 public.
我想我明白了。
重申目标:
在使用FSCTL_GET_NTFS_FILE_RECORD
从Mft读取记录后,我一直发现ATTRIBUTE_LIST
记录处于“不一致状态”,以至于报告的记录长度大于实际数量记录中的数据。读取超出已写入内容的数据似乎有风险,因为我无法确定我读取的内容是否有效,或者是剩余的垃圾。
为此,我提出了 4 个备选方案,希望它们能让我解决这个问题。
- 在卷上使用 LockFileEx(这在我开始时似乎是最好的答案)结果完全是行不通的。 RbMm 和 eryksun(以及我自己的实验)提供了一些非常有说服力的证据,证明这是行不通的。正如 LockFileEx 中的 'File' 所暗示的,此函数仅适用于文件。
- 冲洗音量手柄会使症状消失。但是在性能上有巨大的(> 100%)损失。也不清楚这个问题是否真的得到了解决,或者只是隐藏在由此导致的减速背后。
- 'some other' api 读取非驻留数据的想法似乎是神话。
- 在完成某件事后等待一些(未指定的)时间
FSCTL_MOVE_FILE
不是计划,而是希望。
对于 ,看起来检查 NtfsRecord 中的 UpdateSequenceNumber 可能会提供解决方案。然而,Ntfs 在更新记录时使用的事件顺序意味着 ATTRIBUTE_LIST 的记录长度在 UpdateSequenceNumber.
之前得到更新(很好)
但后来我开始思考究竟什么时候这可能是个问题。如果我忽略它,它会在哪里失败?
目前我遇到的问题是 ATTRIBUTE_LIST 越来越大(因为我故意大量地对文件进行碎片化)。那时,由于记录长度为零,它很容易被检测到。我已经 运行 这个程序好几次了,虽然这只是轶事,但随着记录的增长,额外的 space 总是归零。这是有道理的,因为当你第一次分配它时,你会将整个缓冲区清零。标准的编程实践和观察都支持这一结论。
但是当记录开始缩小时呢?还是先缩小再长大?你能在那里得到剩余数据而不是(容易解释的)零吗?
然后它击中了我:ATTRIBUTE_LIST从不缩小。我是 just complaining about this a few weeks ago. Even when you completely defragment the file and all these extra DATA records are no longer required, Ntfs doesn't compact them. And now for the first time I have a glimpse of why that might be. There's a possibility 这可能会在 W10 中改变,但这可能只是对未记录函数的过于乐观的解释。
因此,我不必担心读取垃圾数据(可能包括无意义的记录长度导致我超过 运行 缓冲区)。 ATTRIBUTE_LIST中的记录长度是可以信任的。最后一条记录的记录长度可能为零。
我可以忽略零长度记录(本质上是返回预增长信息)或者重新读取记录直到UpdateSequenceNumber改变(表示更新完成)
多田.
我正在试验 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
和 FILE_FLAG_BACKUP_SEMANTICS
,但错误仍然没有改变。
展望未来,我可以在这里看到一些替代方案:
- 弄清楚如何使用卷句柄锁定部分(并希望 Ntfs 驱动程序也这样做)。在这一点上似乎很可疑。
- 弄清楚如何刷新我刚刚移动的文件的元数据(注意:MOVE_FILE_DATA.FileHandle 的 FlushFileBuffers 没有帮助。也许刷新卷句柄?)。
- 是否有一些 'official' 方法可以读取不涉及
ReadFile
卷句柄的非常驻数据?我没有找到,但也许我错过了。 - 移动数据后等待 "a bit" 让驱动程序完成所有更新。呸
FWIW,这是针对卷句柄执行 LockFileEx 的一些测试代码。请注意,您必须 运行 作为管理员才能锁定卷句柄。我正在使用 J:
,因为那是我的闪存驱动器。 50000是随机抽取的,应该小于U盘的大小。
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 条目,这导致无限循环,因为看起来我从未读完整个缓冲区。如果长度为零,我正在通过退出来解决它,但我担心缓冲区中的 "leftover garbage" 而不是干净的零。检测到那是不可能的,所以我希望有更好的解决方案。
毫不奇怪,关于这方面的信息并不多。所以如果有人在这里有一些经验,我可以使用一些见解。
编辑 1:
更多不太有效的东西:
- LockFileEx 仍然不走运。
- 我尝试刷新音量句柄(正如 Paul 所建议的)。虽然这有效,但它使我的执行时间增加了一倍以上。而且,严格来说,还是没有解决问题。仍然不能保证 Ntfs 不会在 FlushFileBuffers 和 FSCTL_GET_NTFS_FILE_RECORD / ReadFile 之间做出更多改变。
- 我想知道 $STANDARD_INFORMATION 属性的 'RecordChanged' 时间戳。但是,由于对 ATTRIBUTE_LIST. 的这些更改,它没有被更改
- 对文件进行分段最终会导致添加 ATTRIBUTE_LIST,并且随着碎片的不断增加,更多的 DATA 记录将被添加到该列表中。添加 DATA 记录时,UpdateSequenceNumber(不是 MFT_SEGMENT_REFERENCE 的一部分,另一个)会更新。不幸的是,执行此更新需要一系列事件。显然 ATTRIBUTE_LIST 缓冲区 'length' 在 'UpdateSequenceNumber' 之前更新。因此,查看 'UpdateSequenceNumber' 是否已更改无助于避免阅读(可能)不良信息。
我的下一个最佳想法是看看 Ntfs 是否总是在更新记录长度之前将新字节清零(或者每当记录长度缩小时?)。如果我可以依赖记录长度为零(而不是任何剩余数据可能占用这些字节),我可以假装称之为固定的。
您的问题的解决方案似乎确实是使用卷句柄调用 FlushFileBuffers()
。在页面底部附近 MSDN 有这样的话:
To flush all open files on a volume, call FlushFileBuffers with a handle to the volume. The caller must have administrative privileges...
该页面上的其他信息使我相信这也会刷新元数据,尽管在这种特定情况下并没有直接说明。也许你可以告诉我这个。
从细节上退一步,看一下大局,出于各种原因, 必须在某处成为 API , 虽然我想它可能不是 public.
我想我明白了。
重申目标:
在使用FSCTL_GET_NTFS_FILE_RECORD
从Mft读取记录后,我一直发现ATTRIBUTE_LIST
记录处于“不一致状态”,以至于报告的记录长度大于实际数量记录中的数据。读取超出已写入内容的数据似乎有风险,因为我无法确定我读取的内容是否有效,或者是剩余的垃圾。
为此,我提出了 4 个备选方案,希望它们能让我解决这个问题。
- 在卷上使用 LockFileEx(这在我开始时似乎是最好的答案)结果完全是行不通的。 RbMm 和 eryksun(以及我自己的实验)提供了一些非常有说服力的证据,证明这是行不通的。正如 LockFileEx 中的 'File' 所暗示的,此函数仅适用于文件。
- 冲洗音量手柄会使症状消失。但是在性能上有巨大的(> 100%)损失。也不清楚这个问题是否真的得到了解决,或者只是隐藏在由此导致的减速背后。
- 'some other' api 读取非驻留数据的想法似乎是神话。
- 在完成某件事后等待一些(未指定的)时间
FSCTL_MOVE_FILE
不是计划,而是希望。
对于
但后来我开始思考究竟什么时候这可能是个问题。如果我忽略它,它会在哪里失败?
目前我遇到的问题是 ATTRIBUTE_LIST 越来越大(因为我故意大量地对文件进行碎片化)。那时,由于记录长度为零,它很容易被检测到。我已经 运行 这个程序好几次了,虽然这只是轶事,但随着记录的增长,额外的 space 总是归零。这是有道理的,因为当你第一次分配它时,你会将整个缓冲区清零。标准的编程实践和观察都支持这一结论。
但是当记录开始缩小时呢?还是先缩小再长大?你能在那里得到剩余数据而不是(容易解释的)零吗?
然后它击中了我:ATTRIBUTE_LIST从不缩小。我是 just complaining about this a few weeks ago. Even when you completely defragment the file and all these extra DATA records are no longer required, Ntfs doesn't compact them. And now for the first time I have a glimpse of why that might be. There's a possibility 这可能会在 W10 中改变,但这可能只是对未记录函数的过于乐观的解释。
因此,我不必担心读取垃圾数据(可能包括无意义的记录长度导致我超过 运行 缓冲区)。 ATTRIBUTE_LIST中的记录长度是可以信任的。最后一条记录的记录长度可能为零。
我可以忽略零长度记录(本质上是返回预增长信息)或者重新读取记录直到UpdateSequenceNumber改变(表示更新完成)
多田.