`
univasity
  • 浏览: 808841 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

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;

  DWORDLONG MaximumSize;

  DWORDLONG AllocationDelta;

} USN_JOURNAL_DATA, *PUSN_JOURNAL_DATA;

 

其中的 UsnJournalID FirstUsnNextUsn 是我们后续操作需要用到的。

 

获取他很简单,通过DeviceIoControl() 配合 FSCTL_QUERY_USN_JOURNAL 来实现。

]]

 

 

给出一个实现参考 :

bool getBasicInfoSuccess = false;

/**
 * step 04. 获取USN日志基本信息(用于后续操作)
 * msdn:http://msdn.microsoft.com/en-us/library/aa364583%28v=VS.85%29.aspx
 */
DWORD br;
status = DeviceIoControl(hVol,
                         FSCTL_QUERY_USN_JOURNAL,
                         NULL,
                         0,
			 &UsnInfo,
			 sizeof(UsnInfo),
			 &br,
			 NULL);

if(0!=status){
    getBasicInfoSuccess = true;
}else{
    printf("获取USN日志基本信息失败 —— status:%x error:%d\n", status, GetLastError());
}

 

第五步:列出 USN Journal 文件的数据

前面提到 USN 日志的数据是以 USN_RECORD 形式储存的,在 MSDN 的介绍:

http://msdn.microsoft.com/en-us/library/aa365722%28VS.85%29.aspx

[[

同时分享一个网上找到的带中文注释的 USN_RECORD ( 原文链接 )

typedef struct {

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 version 2.0 < 文件名偏移 >

DWORD ExtraInfo1; // Hypothetically added in version 2.1

DWORD ExtraInfo2; // Hypothetically added in version 2.2

DWORD ExtraInfo3; // Hypothetically added in version 2.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 sequence number (USN) data between two specified boundaries to obtain master file table (MFT) records.

 

这些 USN_RECORD 储存在一个叫 MFT 的表里, MTF_ENUM_DATA 的作用就是制定一个范围 。对这个我也不是很了解,不管他,把参数填上看看结果再说。

 

[[

关键就是如何构建一个 MFT_ENUM_DATA 的结构来指定获取数据的范围,我们来看一下这个结构:

typedef struct {

  DWORDLONG StartFileReferenceNumber;

  USN       LowUsn;

  USN       HighUsn;

} MFT_ENUM_DATA, *PMFT_ENUM_DATA;

 

最后我在 MSDN 中找到关键的一段话:

The first call to FSCTL_ENUM_USN_DATA during an enumeration must have the StartFileReferenceNumber member set to (DWORDLONG)0. Each call to FSCTL_ENUM_USN_DATA retrieves the starting point for the subsequent call as the first entry in the output buffer. Subsequent calls must be made with StartFileReferenceNumber set to this 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 input buffer

  (LPVOID) lpOutBuffer,        // output buffer < 这里>

  (DWORD) nOutBufferSize,      // size of output buffer

  (LPDWORD) lpBytesReturned,   // number of bytes returned

  (LPOVERLAPPED) lpOverlapped  // OVERLAPPED structure

);

 

MFT_ENUM_DATA 还有另外两个参数:

LowUsn, HighUsn

分别制定范围的起止。

 

我们这里要获取所有USN 的数据,需要知道整个USN 的起止位置。可通过 DeviceIoControl() 配合 FSCTL_QUERY_USN_JOURNAL 来获取一个 USN_JOURNAL_DATA 结构体,里面包含我们需要的信息 ( 后续的删除操作也要用到这些信息 )

typedef struct {

  DWORDLONG UsnJournalID;

  USN       FirstUsn;

  USN       NextUsn;

  USN       LowestValidUsn;

  USN       MaxUsn;

  DWORDLONG MaximumSize;

  DWORDLONG AllocationDelta;

} USN_JOURNAL_DATA, *PUSN_JOURNAL_DATA;

 

其中的FirstUsnNextUsn 就分别对应了LowUsnHighUsn.

]]

 

首先需要构建一个MFT_ENUM_DATA ,我们用上一步获取到的USN_JOURNAL_DATA 进行填充。  

这里给出一个实现参考:

 

MFT_ENUM_DATA med;
med.StartFileReferenceNumber = 0;
med.LowUsn = 0;//UsnInfo.FirstUsn; 这里经测试发现,如果用FirstUsn有时候不正确,导致获取到不完整的数据,还是直接写0好.
med.HighUsn = UsnInfo.NextUsn; 
 

 

有了MFT_ENUM_DATA ,就可以获取到USN 的数据了。

MSDN上也提供了一个例子可做参考 ,虽然是FSCTL_READ_USN_JOURNAL,和我们要执行的ENUM是类似的。

 

枚举USN数据,这里给出实现参考:

#define BUF_LEN 4096

CHAR buffer[BUF_LEN]; // 用于储存记录的缓冲 , 尽量足够地大

DWORD usnDataSize;

PUSN_RECORD UsnRecord;

while (0!=DeviceIoControl(hVol,
                          FSCTL_ENUM_USN_DATA,
                          &med,
                          sizeof (med),
                          buffer,
                          BUF_LEN,
                          &usnDataSize,
                          NULL))
{

    DWORD dwRetBytes = usnDataSize - sizeof (USN);

    // 找到第一个 USN 记录
    // from MSDN(http://msdn.microsoft.com/en-us/library/aa365736%28v=VS.85%29.aspx ):
    // return a USN followed by zero or more change journal records, each in a USN_RECORD structure.
    UsnRecord = (PUSN_RECORD)(((PCHAR)buffer)+sizeof (USN));

    printf(" ********************************** \n" );

    while (dwRetBytes>0){

        // 打印获取到的信息
        const int strLen = UsnRecord->FileNameLength;
        char fileName[MAX_PATH] = {0};
        WideCharToMultiByte(CP_OEMCP,NULL,UsnRecord->FileName,strLen/2,fileName,strLen,NULL,FALSE);

       printf("FileName: %s\n" , fileName);
       // 下面两个 file reference number 可以用来获取文件的路径信息
       printf("FileReferenceNumber: %xI64\n" , UsnRecord->FileReferenceNumber);
       printf("ParentFileReferenceNumber: %xI64\n" , UsnRecord->ParentFileReferenceNumber);
       printf("\n" );

       // 获取下一个记录
       DWORD recordLen = UsnRecord->RecordLength;
       dwRetBytes -= recordLen;
       UsnRecord = (PUSN_RECORD)(((PCHAR)UsnRecord)+recordLen);
    
    }

    // 获取下一页数据, MTF 大概是分多页来储存的吧?
    // from MSDN(http://msdn.microsoft.com/en-us/library/aa365736%28v=VS.85%29.aspx ):
    // The USN returned as the first item in the output buffer is the USN of the next record number to be retrieved.
    // Use this value to continue reading records from the end boundary forward.
    med.StartFileReferenceNumber = *(USN *)&buffer;

} 
 

 

现在只要对列出的数据进行筛选,基本就可以实现 Everything 的快速搜索了。

还需要结合索引的建立,才能达到更快的速度。

 

下面再介绍下如何删除 USN Journal 文件,当你不再需要他的时候。

 

第六步:删除 USN Journal 文件

MSDN 参考: http://msdn.microsoft.com/en-us/library/aa363928%28v=VS.85%29.aspx

 

实现参考:

/**
 * step 06. 删除 USN 日志文件 ( 当然也可以不删除 )
 */
DELETE_USN_JOURNAL_DATA dujd;
dujd.UsnJournalID = UsnInfo.UsnJournalID;
dujd.DeleteFlags = USN_DELETE_FLAG_DELETE;

int status = DeviceIoControl(hVol,
                             FSCTL_DELETE_USN_JOURNAL,
                             &dujd,
                             sizeof (dujd),
                             NULL,
                             0,
                             &br,
                             NULL);

if (0!=status){
    printf(" 成功删除 USN 日志文件 !\n" );
}else {
    printf(" 删除 USN 日志文件失败 —— status:%x error:%d\n" , status, GetLastError());
} 
 

 

拓展实现:通过 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使用。欢迎交流。

 

9
0
分享到:
评论
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下我暂时无法测试,希望能帮到你。
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' 这个结构体吗?
谢谢!
1 楼 laorer 2010-11-08  
嗯,当时也想看下 everything的实现机制,不过太懒了,一直没去找,多谢博主

相关推荐

    C#制作的windows系统文件快速搜索工具,读取USN,易用性与速度都已优化的很好。程序为免安装的exe文件。

    原理是读取ntfs的USN文件日志,然后内建索引加速文件搜索过程。 1、列表文件支持批量处理(删除、复制、复制文件名路径、打开、重命名),或者引用系统菜单。 2、支持拼音首字母缩写搜索,指定文件夹内搜索,多...

    Everything1.3.4.67中文版(支持Win7).rar

    Everything在首次使用时会建立一个索引数据库,通过读取NTFS USN日志来建立文件索引树,生成的索引文件非常小。在搜索栏中输入要搜索的文件名,瞬间就能够帮助你找到要找的文件。支持使用搜索语法及正则表达式语法...

    everything源码模拟

    NTFS(New Technology File System)是Windows操作系统中广泛使用的文件系统,它支持多种高级功能,其中之一就是USN(Update Sequence Number)日志。USN日志是一个记录文件系统变化的系统,每次文件或目录发生更改...

    很小但极高速的本地文件搜索工具Everything

    原理是:Everything是靠读取NTFS文件系统中的USN日志来完成的,所以如果你的硬盘不是NTFS的,那就没有什么用了。 这是免安装版,解压使用时,默认是日文界面,要把那个.lng文件放到Everything的执行文件所在文件夹...

    Everything山寨版源码

    这个"Everything山寨版源码"显然是一款基于"Everything"的仿制品或修改版,其核心也是通过读取NTFS文件系统的USN(Update Sequence Number)日志来实现即时的文件索引和搜索功能。 NTFS USN日志是Windows操作系统为...

    Everything-1.2.1.371

    这种快速,是因为Everything的索引无需逐一扫描硬盘文件,而是直接读取NTFS磁盘USN日志。这当然是既省力,又合理的做法。 第二个快速体现在搜索速度。在搜索框中键入字符后,搜索结果——或许称为过滤结果更准确...

    Everything快速搜索

    "Everything"的核心优势在于其利用了NTFS文件系统的USN(Update Sequence Number)日志。USN日志是NTFS文件系统的一个关键特性,它记录了文件系统中所有更改的历史。每当文件被创建、修改、删除或移动,USN日志都会...

    filesearch_everything.rar

    在IT行业中,尤其是在系统开发和数据检索领域,"filesearch_everything.rar"这个压缩包可能包含了一个关于快速文件搜索工具的实现,特别是涉及到USN(Update Sequence Number)日志的利用。USN日志是Windows操作系统...

    NTFS-Search

    NTFS-Search通过读取和解析MFT,结合USN Journal的变化记录,能够在短时间内获取到最新的文件信息,为用户提供即时的文件搜索服务。 项目包含的文件中,UpgradeLog.htm可能是软件升级或更新的日志文件,LICENSE文件...

    Everything软件及源码学习资料

    这是因为Everything通过读取NTFS文件系统的USN日志(Update Sequence Number Journal)和MFT(Master File Table)来构建索引,这两种数据结构记录了文件系统的所有变化,使得搜索结果始终保持最新。 《探索...

    everything

    这种快速,是因为Everything的索引无需逐一扫描硬盘文件,而是直接读取NTFS文件系统的USN日志。这当然是既省力,又合理的做法。 第二个快速体现在搜索速度。在搜索框中键入字符后,搜索结果——或许称为过滤结果更...

    Everything 全球速度最快的电脑文件搜索工具 快到你吐血喷饭

    这种快速,是因为Everything的索引无需逐一扫描硬盘文件,而是直接读取NTFS文件系统的USN日志。这当然是既省力,又合理的做法。 第二个快速体现在搜索速度。在搜索框中键入字符后,搜索结果——或许称为过滤结果更...

    探索Everything背后的技术1

    这款工具之所以能够实现如此高效的搜索,主要得益于对NTFS文件系统的深入理解和巧妙利用。 首先,我们来看NTFS文件系统的基础——Master File Table(MFT)。MFT是NTFS的核心组件,存储了文件和目录的所有元数据。...

Global site tag (gtag.js) - Google Analytics