Everything研究之读取NTFS下的USN日志文件(2)
续>>
/******************************************
?2010.11.10更新了代码,调整了一处地方,lowUsn的设置。
?
******************************************/
?
第四步:获取USN Journal文件的基本信息
MSDN: http://msdn.microsoft.com/en-us/library/aa364583%28v=VS.85%29.aspx
?
[[
他是这么一个结构:
typedef struct {
? DWORDLONG UsnJournalID;
? USN?????? FirstUsn;
? USN?????? NextUsn;
? USN?????? LowestValidUsn;
? USN?????? MaxUsn;
? DWORDLONGMaximumSize;
? DWORDLONGAllocationDelta;
} USN_JOURNAL_DATA, *PUSN_JOURNAL_DATA;
?
其中的UsnJournalID,FirstUsn,NextUsn是我们后续操作需要用到的。
?
获取他很简单,通过DeviceIoControl()配合FSCTL_QUERY_USN_JOURNAL来实现。
]]
?
?
给出一个实现参考:
第五步:列出USN Journal文件的数据前面提到USN日志的数据是以USN_RECORD形式储存的,在MSDN的介绍:
http://msdn.microsoft.com/en-us/library/aa365722%28VS.85%29.aspx
[[
同时分享一个网上找到的带中文注释的USN_RECORD:(原文链接)
typedefstruct {
DWORD RecordLength; // 记录长度
WORD MajorVersion; // 主版本
WORD MinorVersion; // 次版本
DWORDLONG FileReferenceNumber; // 文件引用数
DWORDLONG ParentFileReferenceNumber; // 父目录引用数
USN Usn; // USN
LARGE_INTEGER TimeStamp; // 时间戳
DWORD Reason; // 原因
DWORD SourceInfo; // 源信息
DWORD SecurityId; // 安全
ID DWORD FileAttributes; // 文件属性
WORD FileNameLength; // 文件长度
WORD FileNameOffset; // penultimate of original version2.0 <文件名偏移>
DWORD ExtraInfo1; // Hypothetically added in version2.1
DWORD ExtraInfo2; // Hypothetically added in version2.2
DWORD ExtraInfo3; // Hypothetically added in version2.3
WCHAR FileName[1]; // variable length always at the end<文件名第一位的指针>
}USN_RECORD, *PUSN_RECORD;
?
其中<>是我补充上的,要实现类似Everything的搜索,我们主要关注下面几个参数:
FileReferenceNumber,ParentFileReferenceNumber -> 可以用来找回文件路径
FileNameLength,FileName[1] -> 储存了文件名
]]
?
需要枚举USN的数据,我们需要使用DeviceIoControl()和FSCTL_ENUM_USN_DATA配合。
MSDN:http://msdn.microsoft.com/en-us/library/aa364563%28v=VS.85%29.aspx
?
这里需要提供一个MTF_ENUM_DATA的结构作为参数,正如描述所指:
Enumerates the update sequencenumber (USN) data between two specified boundaries to obtain master file table(MFT) records.
?
这些USN_RECORD储存在一个叫MFT的表里,MTF_ENUM_DATA的作用就是制定一个范围。对这个我也不是很了解,不管他,把参数填上看看结果再说。
?
[[
关键就是如何构建一个MFT_ENUM_DATA的结构来指定获取数据的范围,我们来看一下这个结构:
typedef struct {
? DWORDLONGStartFileReferenceNumber;
? USN?????? LowUsn;
? USN?????? HighUsn;
} MFT_ENUM_DATA, *PMFT_ENUM_DATA;
?
最后我在MSDN中找到关键的一段话:
The firstcall to FSCTL_ENUM_USN_DATA during an enumeration must have the StartFileReferenceNumber memberset to (DWORDLONG)0. Each call to FSCTL_ENUM_USN_DATAretrieves the starting point for the subsequent call as the first entry in theoutput buffer. Subsequent calls must be made with StartFileReferenceNumber set tothis value. For more information, see FSCTL_ENUM_USN_DATA.
大概意思是:
初始时将StartFileReferenceNumber设置为0,然后每次进行枚举后获取起始点进行下一次操作,这个新的起始点包含在返回的buffer的开头。
?
我们不难发现,这里指的buffer就是DeviceIoControl函数中的:
BOOL DeviceIoControl(
? (HANDLE)hDevice,??????????? // handle to volume
?FSCTL_ENUM_USN_DATA,???????? //dwIoControlCode
? (LPVOID)lpInBuffer,???????? // input buffer
? (DWORD)nInBufferSize,?????? // size of inputbuffer
? (LPVOID)lpOutBuffer,??????? // output buffer <这里>
? (DWORD)nOutBufferSize,????? // size of outputbuffer
? (LPDWORD)lpBytesReturned,?? // number of bytesreturned
? (LPOVERLAPPED)lpOverlapped? // OVERLAPPED structure
);
?
在MFT_ENUM_DATA中还有另外两个参数:
LowUsn, HighUsn
分别制定范围的起止。
?
我们这里要获取所有USN的数据,需要知道整个USN的起止位置。可通过DeviceIoControl()配合FSCTL_QUERY_USN_JOURNAL来获取一个USN_JOURNAL_DATA结构体,里面包含我们需要的信息(后续的删除操作也要用到这些信息):
typedef struct {
? DWORDLONGUsnJournalID;
? USN?????? FirstUsn;
? USN?????? NextUsn;
? USN?????? LowestValidUsn;
? USN?????? MaxUsn;
? DWORDLONGMaximumSize;
? DWORDLONGAllocationDelta;
} USN_JOURNAL_DATA, *PUSN_JOURNAL_DATA;
?
其中的FirstUsn和NextUsn就分别对应了LowUsn和HighUsn.
]]
?
首先需要构建一个MFT_ENUM_DATA,我们用上一步获取到的USN_JOURNAL_DATA进行填充。?
这里给出一个实现参考:
?
?有了MFT_ENUM_DATA,就可以获取到USN的数据了。
MSDN上也提供了一个例子可做参考,虽然是FSCTL_READ_USN_JOURNAL,和我们要执行的ENUM是类似的。
?
枚举USN数据,这里给出实现参考:
??
现在只要对列出的数据进行筛选,基本就可以实现Everything的快速搜索了。
还需要结合索引的建立,才能达到更快的速度。
?
下面再介绍下如何删除USNJournal文件,当你不再需要他的时候。
?
第六步:删除USN Journal文件
MSDN参考:http://msdn.microsoft.com/en-us/library/aa363928%28v=VS.85%29.aspx
?
实现参考:
??
拓展实现:通过File Reference Number获取文件路径
这个这里暂时不讲了,感兴趣的可以参考文章最后给出的链接的实现。
C++上可以通过使用NtCreateFile()和NtQueryFileInformation()来实现路径获取。
基本MSDN上有比较详细的说明了。
===================================================================================================================
>>最后
附上一个完整的C++实现的例子:?NftsUsnJournalDemo_2010.11.10_.rar
另外,还有一个在国外BLOG找到的C#实现的例子,比较完成,注释也很清晰。我也参考了不少。
原BLOG上是以文本贴出代码的,我这里整理成可编译的项目提供下载。
原文BLOG:http://www.dreamincode.net/forums/blog/1017-stcroixskippers-blog/
整理的项目:UsnJournalProject_v1.3.rar
===================================================================================================================
以上都是出于个人兴趣的研究,接触C++实在迫不得已……Java某些部分的局限性太大了。
上面很多东西都是GOOGLE+MSDN完成的,若有疏忽请指教。
下一步准备将这些基本操作封装成DLL,再通过JNI供JAVA使用。欢迎交流。?
1 楼 laorer 2010-11-08 嗯,当时也想看下 everything的实现机制,不过太懒了,一直没去找,多谢博主 2 楼 lslin 2010-12-25 博主你好!
一直都在使用Everything,实在是太赞了。以前知道Everything的大概原理,但是自己却不回实现,看了博主的介绍后对受益匪浅。
不过,在初始化USN日记的时候,我遇到了以下问题(我用的是VC++6.0编译器):
error C2065: 'CREATE_USN_JOURNAL_DATA' : undeclared identifier
error C2146: syntax error : missing ';' before identifier 'cujd'
error C2065: 'cujd' : undeclared identifier
error C2228: left of '.MaximumSize' must have class/struct/union type
error C2228: left of '.AllocationDelta' must have class/struct/union type
error C2065: 'FSCTL_CREATE_USN_JOURNAL' : undeclared identifier
我已经在原文件中加入了以下语句:
#include <winioctl.h>
请问博主知道是怎么回事吗?
我看了一下你提供的源代码,发现你只是include了Windows.h,这样也可以使用'CREATE_USN_JOURNAL_DATA' 这个结构体吗?
谢谢! 3 楼 univasity 2010-12-30 lslin 写道博主你好!
一直都在使用Everything,实在是太赞了。以前知道Everything的大概原理,但是自己却不回实现,看了博主的介绍后对受益匪浅。
不过,在初始化USN日记的时候,我遇到了以下问题(我用的是VC++6.0编译器):
error C2065: 'CREATE_USN_JOURNAL_DATA' : undeclared identifier
error C2146: syntax error : missing ';' before identifier 'cujd'
error C2065: 'cujd' : undeclared identifier
error C2228: left of '.MaximumSize' must have class/struct/union type
error C2228: left of '.AllocationDelta' must have class/struct/union type
error C2065: 'FSCTL_CREATE_USN_JOURNAL' : undeclared identifier
我已经在原文件中加入了以下语句:
#include <winioctl.h>
请问博主知道是怎么回事吗?
我看了一下你提供的源代码,发现你只是include了Windows.h,这样也可以使用'CREATE_USN_JOURNAL_DATA' 这个结构体吗?
谢谢!
提示未定义的错误。我用2010的,可能版本问题。
这些结构体如果没有可以自己定义,具体参考MSDN,直接将结构体代码拷贝到使用前的位置就可以。
我引用的这些是被定义在一个叫WinIoCtl.h的头文件。你需要的结构体如下:
#define FSCTL_CREATE_USN_JOURNAL CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 57, METHOD_NEITHER, FILE_ANY_ACCESS) // CREATE_USN_JOURNAL_DATA,
typedef struct {
DWORDLONG MaximumSize;
DWORDLONG AllocationDelta;
} CREATE_USN_JOURNAL_DATA, *PCREATE_USN_JOURNAL_DATA;
VC++6下我暂时无法测试,希望能帮到你。