Everything原理(部分)

我在第一次使用 Everything 时,对其速度确实感到惊讶,后来了解到是通过操作 USN 实现的,并且有一定的局限性(只有 NTFS 下才能使用)。

 

近来清闲无事(失业了),搞些自己的小项目玩玩。其中也要处理到本地搜索这块,首先我想到的就是Everything 。

 

我仔细地将官网和他论坛的帖子都看了遍,基本没找到什么讲到原理的。倒是官网上提供了一个 Everything 的SDK 下载,是一个 IPC 的实现(我不是很懂,大概是程序关联之类的),需要 Everything 在后台跑才能调用操作。我也试了下效果,可以实现的,就是这样太麻烦了。

 

经过多日的搜搜,我发现了一个帖子,讨论的正是对 USN 的操作,同时我搜到了回帖者的 BLOG ,这一切让我有了开始的基础 …

 

 

言归正传:

 

初步认识 USN :

USN Journal 相当于 NTFS 的秘书,为他记录下改动的一切,并储存为 USN_RECORD 的格式。

更多的介绍请看以下链接:

Keeping an Eye on Your NTFS Drives: the Windows 2000 Change Journal Explained

fsutil_usn

NTFS文件系统 USN日志

 

下面来分享下近日研究的成果,一步步来探索 Everything 神奇的速度 USN的使用(Everything的快不单是用了USN,还需要建立索引,原来表达有误,改过来)

整个实现分为 6 步:

1.       判断驱动盘是否为 NTFS 格式

2.       获取驱动盘句柄

3.       初始化 USN 日志文件

4.       获取 USN 基本信息

5.       列出 USN 日志的所有数据

6.       删除 USN 日志文件

 

第一步:判断驱动盘是否 NTFS 格式

我们可以通过 GetVolumeInformation() 函数获取相关的信息进行判断。

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

 

[[

这里我还找到了一个中文的说明:

GetVolumeInformation(

  lpRootPathName: PChar;               { 磁盘驱动器代码字符串}

  lpVolumeNameBuffer: PChar;           { 磁盘驱动器卷标名称}

  nVolumeNameSize: DWORD;              { 磁盘驱动器卷标名称长度}

  lpVolumeSerialNumber: PDWORD;        { 磁盘驱动器卷标序列号}

  var lpMaximumComponentLength: DWORD; { 系统允许的最大文件名长度}

  var lpFileSystemFlags: DWORD;        { 文件系统标识}

  lpFileSystemNameBuffer: PChar;       { 文件操作系统名称}

  nFileSystemNameSize: DWORD           { 文件操作系统名称长度}

): BOOL;

上图可以看到,最后一个就是格式类型了,对应 lpFileSystemNameBuffer 。

]]

 

下面给出C++ 的实现作为参考:

Cpp代码   收藏代码
  1. /** 
  2.  * step 01. 判断驱动盘是否 NTFS 格式 
  3.  */  
  4. char sysNameBuf[MAX_PATH] = {0};  
  5. int status = GetVolumeInformationA(volName,  
  6.                                    NULL, // 驱动盘名缓冲,这里我们不需要  
  7.                                    0,  
  8.                                    NULL,  
  9.                                    NULL,  
  10.                                    NULL,  
  11.                                    sysNameBuf, // 驱动盘的系统名( FAT/NTFS)  
  12.                                    MAX_PATH);  
  13.   
  14. if (0!=status){  
  15.   
  16.     printf(" 文件系统名 : %s\n" , sysNameBuf);  
  17.   
  18.     // 比较字符串  
  19.     if (0==strcmp(sysNameBuf, "NTFS" )){  
  20.         isNTFS = true ;  
  21.     }else {  
  22.         printf(" 该驱动盘非 NTFS 格式 \n" );  
  23.     }  
  24.   
  25. }   
 

 

USN Journal 并非一开始就存在的,需要手动打开。我们可以使用函数 DeviceIoControl() 并通过参数FSCTL_CREATE_USN_JOURNAL 来操作。但仔细看 MSDN 会发现,需要先通过 CreateFile() 获取一个驱动盘的句柄。很多的后续操作都要用到这个句柄。

 

第二步:获取驱动盘句柄

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

 

[[

对于我们目前的操作,注意看最后 Remarks ,

Physical Disks and Volumes ” 中的一段:

The following requirements must be met for such a call to succeed:

  • The caller must have administrative privileges. For more information, see Running with Special Privileges .
  • The dwCreationDisposition parameter must have the OPEN_EXISTINGflag.
  • When opening a volume or floppy disk, the dwShareMode parameter must have the FILE_SHARE_WRITEflag.

大概意思是,要成功执行需要满足一下条件:

1.  使用者需要获取管理员权限

2.  dwCreationDisposition 参数 ( 倒数第三个 ) 必须带有 OPEN_EXISTIN 标识

3.  当打开一个驱动盘或软盘 , dwShareMode 参数 ( 第三个 ) 必须带有 FILE_SHARE_WRITE 标识

 

再有就是 ”Files ” 中的一段 :

Windows Server 2003 and Windows XP/2000: 

If CREATE_ALWAYS and FILE_ATTRIBUTE_NORMAL are specified, CreateFile fails and sets the last error to ERROR_ACCESS_DENIED if the file exists and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_SYSTEM attribute. To avoid the error, specify the same attributes as the existing file.

 

大概意思是:

在 windows2003,xp 和 2000 中如果设定了 CREATE_ALWAYS 和 FILE_ATTRIBUTE_NORMAL 两个属性,如果文件存在,并带有属性 FILE_ATTRIBUTE_HIDDEN 或 FILE_ATTRIBUTE_SYSTEM 的话, CreateFile 会失败,并且返回的错误信息为 ERROR_ACCESS_DENIED 。要避免这个错误,需要制定与文件本身相同的属性。

 

>> 所以我们这里尽量不使用 CREATE_ALWAYS 和 FILE_ATTRIBUTE_NORMAL 。我这里使用FILE_ATTRIBUTE_HIDDEN 。

]]

 

 

照样贴上例子(我很少用 C++ ,写得不好,仅参考) :

Cpp代码   收藏代码
  1. /** 
  2.  * step 02. 获取驱动盘句柄 
  3.  */  
  4. char fileName[MAX_PATH];  
  5. fileName[0] = '\0';  
  6.   
  7. // 传入的文件名必须为\\.\C:的形式  
  8. strcpy(fileName, "\\\\.\\");  
  9. strcat(fileName, volName);  
  10. // 为了方便操作,这里转为string进行去尾  
  11. string fileNameStr = (string)fileName;  
  12. fileNameStr.erase(fileNameStr.find_last_of(":")+1);  
  13.   
  14. printf("驱动盘地址: %s\n", fileNameStr.data());  
  15.   
  16. // 调用该函数需要管理员权限  
  17. hVol = CreateFileA(fileNameStr.data(),  
  18.                    GENERIC_READ | GENERIC_WRITE, // 可以为0  
  19.                    FILE_SHARE_READ | FILE_SHARE_WRITE, // 必须包含有FILE_SHARE_WRITE  
  20.                    NULL, // 这里不需要  
  21.                    OPEN_EXISTING, // 必须包含OPEN_EXISTING, CREATE_ALWAYS可能会导致错误  
  22.                    FILE_ATTRIBUTE_READONLY, // FILE_ATTRIBUTE_NORMAL可能会导致错误  
  23.                    NULL); // 这里不需要  
  24.   
  25. if(INVALID_HANDLE_VALUE!=hVol){  
  26.     getHandleSuccess = true;  
  27. }else{  
  28.     printf("获取驱动盘句柄失败 —— handle:%x error:%d\n", hVol, GetLastError());  
  29. }  
 

 

第三步:打开 USN Journal 文件

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

 

 

代码参考:

Cpp代码   收藏代码
  1. /** 
  2.  * step 03. 初始化USN日志文件 
  3.  */  
  4. DWORD br;  
  5. CREATE_USN_JOURNAL_DATA cujd;  
  6. cujd.MaximumSize = 0; // 0表示使用默认值  
  7. cujd.AllocationDelta = 0; // 0表示使用默认值  
  8. status = DeviceIoControl(hVol,  
  9.                          FSCTL_CREATE_USN_JOURNAL,  
  10.                          &cujd,  
  11.                          sizeof(cujd),  
  12.                          NULL,  
  13.                          0,  
  14.                          &br,  
  15.                          NULL);  
  16.   
  17. if(0!=status){  
  18.     initUsnJournalSuccess = true;  
  19. }else{  
  20.     printf("初始化USN日志文件失败 —— status:%x error:%d\n", status, GetLastError());  
  21. }  
 

 

这时如果你能成功创建 USN 日志,那么对 USN 的探索即将开始 …

 

>> 目前你手上有的资源是 :

1.  某个 NTFS 驱动盘的 HANDLE;

2.  该驱动盘的 USN 日记已成功创建 .

 

--------------------------------------------- 未完待续 ------------------------------------------


转载自 http://univasity.iteye.com/blog/805234

已标记关键词 清除标记
相关推荐
Everything:速度最快的文件搜索工具 转载善用佳软,本人用的时候都是用alt+F快捷键的,这个需要到选项设置一下,检索速度太快,占用内存平时7MB左右,不能检索文件内容,仅文件名。可以开启http或ftp服务,用来共享文件还是不错的,毕竟校网传输速度不错,方便 Everything是速度最快的文件搜索软件。其速度之快令人震惊,百G硬盘几十万个文件,可以在几秒钟之内完成索引;文件名搜索瞬间呈现结果。它小巧免费,支持中文,支持正则表达式,可以通过HTTP或FTP分享搜索结果。如果不满意Windows自带的搜索工具、Total Commander的搜索、Google 桌面搜索或百度硬盘搜索,如果正在使用或放弃了Locate32,都值得推荐这款体积小巧、免安装、免费、速度极快(比Locate32更快)的文件搜索工具Everything! 最新beta版。 中文语言包、语言包txt文本 本文目录:简介、初级教程、视频演示、技巧、补充、总结、附录 1. Everything简介 voidtools(中文主页)开发的一款文件搜索工具,官网描述为“基于名称实时定位文件和目录(Locate files and folders by name instantly)”。它体积小巧,界面简洁易用,快速建立索引,快速搜索,同时占用极低的系统资源,实时跟踪文件变化,并且还可以通过http或ftp形式分享搜索。 注意: - 只搜索文件名,不能搜索文件内容; - 只适用NTFS文件系统,不适合FAT32; - 完美支持中文,但必须使用V1.2.及更新版本。 2. 初级教程:Everything的基本用法 2.1 下载与安装 官方下载页。 安装或解压Everything,即可开始使用。 2.2 首次运行 Everything在第一次运行时,会建立索引数据库。但你丝毫不用担心,其速度极快,生成的索引文件极小!看看本文下面的回复,你会充满信心。亲身体验之后,你或者也会为其速度震惊。或者,你根本没感觉到它需要建立索引。 索引之后,简洁的程序界面呈现在你的面前,程序状态栏中还会显示索引的文件数量。看到这个数字,你是否惊奇Everything的神速呢? 2.3 基本搜索 尽管Everything还处在英文界面,但这并不影响你的基本使用。只须在搜索框中,输入几个字母或汉字,搜索结果就会实时呈现在你的眼前。然后,可以直接双击某条结果,打开文件。也可以直接在结果中进行复制、删除等常见操作。 2.4 切换为中文界面 中文语言包,并将语言包内的Everything.lng解压到程序目录,就可以通过菜单切换为中文了:Tools→ Options→ General→ Language→ 简体中文。 需要说明的是,切换语言后,需要重启Everything才能看到效果。另外,点击Everything窗口的关闭按钮后,它只是缩小为托盘图标,并没有真正关闭。 3. Everything搜索技巧 3.1 高效搜索之“与”“或” 技巧:在Everything的搜索框中可以输入多个关键词,以空格分开,表示搜索结果要包括全部关键词。大家肯定对这种做法不会陌生,因为它正是搜索引擎的惯例。 举例:键入(不包括引号,下同)“李白 北京 08 jpg”,可以快速找出某些照片。 技巧:对应“与”的还有“或”(OR)运算,用半角竖线表示:|。当你不确信关键词的准确描述时,这种方式非常有用。 举例:“jpg 李白|libai 北京 08”、“免费|freeware”…… 引伸:既然空格表示“与”,那么如何表示真正的空格呢?很简单,加英文半角引号,比如”program files”。 3.2 正则表达式 Everything支持正则表达式,或者说,支持一些简单的正则表达式。但对大多数用户而言,这已经足够了!Everything支持的正则表达式有: | () ? * + . [] [^] ^ $ {m,n} rex网友的翻译。如果你不了解什么是正则表达式,请自行搜索。 3.3 指定搜索范围 默认情况下,Everything索引、搜索所有本地NTFS磁盘的所有目录。但是你可以通过如下方式,限定搜索范围,以得到更易用的结果列表。 - 希望Everything永不索引某个磁盘,请在“选项”-“NTFS磁盘”中,选定相应盘符,取消“搜索本卷”或“包含在数据库中”。(注:可以取消前一项,这样后一项就自动变灰——但没发现这种做法与只取消后一项的差别。大家可以研究一下!) - 希望永远排除某些目录,可以在“选项”-“排除列表”中设定。和上条方法一样,确认之后Everything会重新生成索引。 - 希望Everything只搜索某个目录,可以在资源管理器或Total Command
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:C马雯娟 返回首页