Everything 搜索文件
http://univasity.iteye.com/blog/805234
/** * 对Ntfs下USN操作的示例程序 * * author: univasity * qq: 595920004 * * --------------- 2010.11.10 * 1.修改lowUsn的问题,将原来的通过USN_JOURNAL获取,改为直接设置为0. *
USN是Update Service Number Journal or Change Journal的英文缩写,直译为“更新序列号”,是对NTFS卷里所修改过的信息进行相关记录的功能。当年微软发布Windows 2000时,建立NTFS 5.0的同时,加入了一些新功能和改进了旧版本的文件系统,为它请来了一位可靠的秘书,它可以在分区中设置监视更改的文件和目录的数量,记录下监视对象修改时间和修改内容。没错,它就是USN日志。当这个功能启用时,对于每一个NTFS卷,当发生有关添加、删除和修改文件的信息时,NTFS都使用USN日志记录下来。
NTFS秘书——USN日志的工作方式,相对来说很简单,所以非常的高效。它开始的时候是一个空文件,包括NTFS每个卷的信息。每当NTFS卷有改变的时候,所改变的信息会马上被添加到这个文件里。这其中,每条修改的记录都使用特定符号来标识为日志形式,也就是USN日志。每条日志,记录了包括文件名、文件信息做出的改变。怎样在系统中让秘书开始干活儿呢?如图2所示,在NTFS分区的图标上右击选择“属性”,勾选圈中部分即可。
USN秘书不仅工作高效,而且非常的忠诚,虽然这种忠诚看起来有点迫不得已。日志里包括发生了什么变化(添加、删除或其他操作),但并不会记录数据或其他变化的细节,所以它只能工作在NTFS文件系统中。
看到上面的描述,你也许还是比较难以理解,那么就举个例子说明一下。USN日志为什么不能在FAT32文件系统下运用呢?就像钢笔不能在宣纸上记录,只能在普通纸上记录一样。USN日志相当于一本书的索引,当然书里面内容发生添加、修改或删除的时候,USN日志会记录下来何时做了修改,并使用特定序列号来标识,但它并不会记录里面具体修改了什么东西,所以索引文件很小。而当你想查找某一篇文章时,你就不用一页一页去翻书,可以直接通过查找USN日志(也就是建立的索引)就知道这篇文章是否存在。
*/ #include <iostream> #include <Windows.h> #include <fstream> using namespace std; char* volName = "e:\\"; // 驱动盘名称 HANDLE hVol; // 用于储存驱动盘句柄 USN_JOURNAL_DATA UsnInfo; // 用于储存USN日志的基本信息 #define BUF_LEN 4096 ofstream fout("H:\\log.txt"); // 用来将数据记录到文本,方便查看 long counter = 0; int main(){ bool status; bool isNTFS = false; bool getHandleSuccess = false; bool initUsnJournalSuccess = false; /** * step 01. 判断驱动盘是否NTFS格式 * msdn:http://msdn.microsoft.com/en-us/library/aa364993%28VS.85%29.aspx */ char sysNameBuf[MAX_PATH] = {0}; status = GetVolumeInformationA(volName, NULL, // 驱动盘名缓冲,这里我们不需要 0, NULL, NULL, NULL, sysNameBuf, // 驱动盘的系统名(FAT/NTFS) MAX_PATH); if(0!=status){ printf("文件系统名: %s\n", sysNameBuf); // 比较字符串 if(0==strcmp(sysNameBuf, "NTFS")){ isNTFS = true; }else{ printf("该驱动盘非NTFS格式\n"); } } // 只有NTFS才有USN,才能进行操作 if(isNTFS){ /** * step 02. 获取驱动盘句柄 * msdn:http://msdn.microsoft.com/en-us/library/aa363858%28VS.85%29.aspx */ char fileName[MAX_PATH]; fileName[0] = '\0'; // 传入的文件名必须为\\.\C:的形式 strcpy(fileName, "\\\\.\\"); strcat(fileName, volName); // 为了方便操作,这里转为string进行去尾 string fileNameStr = (string)fileName; fileNameStr.erase(fileNameStr.find_last_of(":")+1); printf("驱动盘地址: %s\n", fileNameStr.data()); // 调用该函数需要管理员权限 hVol = CreateFileA(fileNameStr.data(), GENERIC_READ | GENERIC_WRITE, // 可以为0 FILE_SHARE_READ | FILE_SHARE_WRITE, // 必须包含有FILE_SHARE_WRITE NULL, // 这里不需要 OPEN_EXISTING, // 必须包含OPEN_EXISTING, CREATE_ALWAYS可能会导致错误 FILE_ATTRIBUTE_READONLY, // FILE_ATTRIBUTE_NORMAL可能会导致错误 NULL); // 这里不需要 if(INVALID_HANDLE_VALUE!=hVol){ getHandleSuccess = true; }else{ printf("获取驱动盘句柄失败 —— handle:%x error:%d\n", hVol, GetLastError()); } } if(getHandleSuccess){ /** * step 03. 初始化USN日志文件 * msdn:http://msdn.microsoft.com/en-us/library/aa364558%28v=VS.85%29.aspx */ DWORD br; CREATE_USN_JOURNAL_DATA cujd; cujd.MaximumSize = 0; // 0表示使用默认值 cujd.AllocationDelta = 0; // 0表示使用默认值 status = DeviceIoControl(hVol, FSCTL_CREATE_USN_JOURNAL, &cujd, sizeof(cujd), NULL, 0, &br, NULL); if(0!=status){ initUsnJournalSuccess = true; }else{ printf("初始化USN日志文件失败 —— status:%x error:%d\n", status, GetLastError()); } } if(initUsnJournalSuccess){ 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(USN_JOURNAL_DATA), &br, NULL); if(0!=status){ getBasicInfoSuccess = true; }else{ printf("获取USN日志基本信息失败 —— status:%x error:%d\n", status, GetLastError()); } if(getBasicInfoSuccess){ printf("UsnJournalID: %xI64\n", UsnInfo.UsnJournalID); printf("lowUsn: %xI64\n", UsnInfo.FirstUsn); printf("highUsn: %xI64\n", UsnInfo.NextUsn); /** * step 05. 枚举USN日志文件中的所有记录 * msdn:http://msdn.microsoft.com/en-us/library/aa364563%28v=VS.85%29.aspx */ // from MSDN // On the first call, set the starting point, the StartFileReferenceNumber member of the MFT_ENUM_DATA structure, 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. MFT_ENUM_DATA med; med.StartFileReferenceNumber = 0; med.LowUsn = 0;//UsnInfo.FirstUsn; 这里经测试发现,如果用FirstUsn有时候不正确,导致获取到不完整的数据,还是直接写0好. med.HighUsn = UsnInfo.NextUsn; 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"); fout << "FileName:" << fileName << endl; fout << "frn:" << UsnRecord->FileReferenceNumber << endl; fout << "pfrn:" << UsnRecord->ParentFileReferenceNumber << endl; fout << endl; counter++; // 获取下一个记录 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; } printf("共%d个文件\n", counter); fout << "共" << counter << "个文件" << endl; fout << flush; fout.close(); } /** * step 06. 删除USN日志文件(当然也可以不删除) * msdn:http://msdn.microsoft.com/en-us/library/aa364561%28v=VS.85%29.aspx */ DELETE_USN_JOURNAL_DATA dujd; dujd.UsnJournalID = UsnInfo.UsnJournalID; dujd.DeleteFlags = USN_DELETE_FLAG_DELETE; 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()); } } // 最后释放一些资源 if(getHandleSuccess){ CloseHandle(hVol); } // 避免后台程序一闪而过 MessageBox(0, "按确定退出", "结束", MB_OK); return 0; }