Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

717 lines
18 KiB

  1. // fastfind.c
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <nt.h>
  5. #include <ntrtl.h>
  6. #include <nturtl.h>
  7. #include <windows.h>
  8. #include <ntioapi.h>
  9. #include <myntfs.h>
  10. #define VOLUME_PATH L"\\\\.\\H:"
  11. #define VOLUME_DRIVE_LETTER_INDEX 4
  12. #define FULL_PATH L"\\??\\H:\\1234567890123456"
  13. #define FULL_DRIVE_LETTER_INDEX 4
  14. #define DEVICE_PREFIX_LEN 14
  15. typedef struct _EXTENT {
  16. LONGLONG Vcn;
  17. LONGLONG Lcn;
  18. LONGLONG Length;
  19. } EXTENT, *PEXTENT;
  20. #define MAX_EXTENTS 64
  21. //
  22. // Some globals.
  23. //
  24. LARGE_INTEGER MftStart;
  25. ULONG ClusterSize;
  26. ULONG FrsSize;
  27. EXTENT Extents[MAX_EXTENTS];
  28. ULONG DebugLevel;
  29. UCHAR CacheBuffer[0x10000]; // max cluster size
  30. LONGLONG CachedOffset = -1;
  31. char mybuffer[32768];
  32. LONGLONG
  33. ComputeFileRecordLbo (
  34. IN ULONG MftIndex
  35. )
  36. {
  37. LONGLONG vcn;
  38. LONGLONG lcn = 0;
  39. ULONG extentIndex;
  40. ULONG offsetWithinCluster;
  41. vcn = (MftIndex * FrsSize) / ClusterSize;
  42. for (extentIndex = 0; extentIndex < MAX_EXTENTS; extentIndex += 1) {
  43. if ((vcn >= Extents[extentIndex].Vcn) &&
  44. (vcn < Extents[extentIndex].Vcn + Extents[extentIndex].Length)) {
  45. lcn = Extents[extentIndex].Lcn + (vcn - Extents[extentIndex].Vcn);
  46. }
  47. }
  48. if (ClusterSize >= FrsSize ) {
  49. offsetWithinCluster = (MftIndex % (ClusterSize / FrsSize)) * FrsSize;
  50. return (lcn * ClusterSize + offsetWithinCluster);
  51. } else {
  52. //
  53. // BUGBUG keithka 4/28/00 Handle old fashioned big frs and/or big
  54. // clusters someday.
  55. //
  56. ASSERT( FALSE );
  57. return 0;
  58. }
  59. }
  60. VOID
  61. FindAttributeInFileRecord (
  62. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  63. IN ATTRIBUTE_TYPE_CODE TypeCode,
  64. IN PATTRIBUTE_RECORD_HEADER PreviousAttribute OPTIONAL,
  65. OUT PATTRIBUTE_RECORD_HEADER *Attribute
  66. )
  67. // Attribute set to NULL if not found.
  68. {
  69. PATTRIBUTE_RECORD_HEADER attr;
  70. *Attribute = NULL;
  71. if (FileRecord->Pad0[0] != 'F' ||
  72. FileRecord->Pad0[1] != 'I' ||
  73. FileRecord->Pad0[2] != 'L' ||
  74. FileRecord->Pad0[3] != 'E') {
  75. if (DebugLevel >= 1) {
  76. printf( "\nBad MFT record %c%c%c%c",
  77. FileRecord->Pad0[0],
  78. FileRecord->Pad0[1],
  79. FileRecord->Pad0[2],
  80. FileRecord->Pad0[3] );
  81. }
  82. //
  83. // This isn't a good file record, but that doesn't make this a corrupt volume.
  84. // It's possible that this file record has never been used. Since we don't look
  85. // at the MFT bitmap, we don't know if this was expected to be a valid filerecord.
  86. // The output Attribute is set to NULL already, so we can exit now.
  87. //
  88. return;
  89. }
  90. if (0 == (FileRecord->Flags & FILE_RECORD_SEGMENT_IN_USE)) {
  91. //
  92. // File record not in use, skip it.
  93. //
  94. return;
  95. }
  96. if (NULL == PreviousAttribute) {
  97. attr = (PATTRIBUTE_RECORD_HEADER) ((PUCHAR)FileRecord + FileRecord->FirstAttributeOffset);
  98. } else {
  99. attr = (PATTRIBUTE_RECORD_HEADER) ((PUCHAR) PreviousAttribute + PreviousAttribute->RecordLength);
  100. if (((PUCHAR)attr - (PUCHAR)FileRecord) > (LONG) FrsSize) {
  101. ASSERT (FALSE);
  102. return;
  103. }
  104. }
  105. while (attr->TypeCode < TypeCode &&
  106. attr->TypeCode != $END) {
  107. ASSERT( attr->RecordLength < FrsSize );
  108. attr = (PATTRIBUTE_RECORD_HEADER) ((PUCHAR) attr + attr->RecordLength);
  109. //
  110. // BUGBUG keitha 4/20/00 need to handle attribute list case someday...
  111. // It's relativley rare that an MFT gets so fragmented it needs an
  112. // attribute list. Certainly rare enough to skip it for now in a
  113. // piece of test code.
  114. //
  115. }
  116. if (attr->TypeCode == TypeCode) {
  117. *Attribute = attr;
  118. }
  119. return;
  120. }
  121. BOOLEAN
  122. FindNameInFileRecord (
  123. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  124. IN PWCHAR FileName,
  125. IN ULONG FileNameLength
  126. )
  127. {
  128. PATTRIBUTE_RECORD_HEADER attr;
  129. PFILE_NAME fileNameAttr;
  130. ULONG cmpResult;
  131. FindAttributeInFileRecord( FileRecord,
  132. $FILE_NAME,
  133. NULL,
  134. &attr );
  135. while (NULL != attr) {
  136. if (((PUCHAR)attr - (PUCHAR)FileRecord) > (LONG) FrsSize) {
  137. ASSERT( FALSE );
  138. return FALSE;
  139. }
  140. //
  141. // Names shouldn't go nonresident.
  142. //
  143. if (attr->FormCode != RESIDENT_FORM) {
  144. ASSERT( FALSE );
  145. return FALSE;
  146. }
  147. fileNameAttr = (PFILE_NAME) ((PUCHAR)attr + attr->Form.Resident.ValueOffset);
  148. if (fileNameAttr->FileNameLength == FileNameLength) {
  149. cmpResult = wcsncmp( FileName,
  150. (PWCHAR) fileNameAttr->FileName,
  151. fileNameAttr->FileNameLength );
  152. if (0 == cmpResult) {
  153. return TRUE;
  154. }
  155. } else if (DebugLevel >= 3) {
  156. printf( "\nNot a match %S,%S", FileName, fileNameAttr->FileName );
  157. }
  158. //
  159. // Find the next filename, if any.
  160. //
  161. FindAttributeInFileRecord( FileRecord,
  162. $FILE_NAME,
  163. attr,
  164. &attr );
  165. }
  166. return FALSE;
  167. }
  168. int
  169. FsTestOpenById (
  170. IN UCHAR *ObjectId,
  171. IN HANDLE VolumeHandle
  172. )
  173. {
  174. HANDLE File;
  175. IO_STATUS_BLOCK IoStatusBlock;
  176. NTSTATUS Status;
  177. NTSTATUS GetNameStatus;
  178. NTSTATUS CloseStatus;
  179. OBJECT_ATTRIBUTES ObjectAttributes;
  180. UNICODE_STRING str;
  181. WCHAR nameBuffer[MAX_PATH];
  182. PFILE_NAME_INFORMATION FileName;
  183. WCHAR Full[] = FULL_PATH; // Arrays of WCHAR's aren't constants
  184. RtlInitUnicodeString( &str, Full );
  185. str.Length = 8;
  186. RtlCopyMemory( &str.Buffer[0], // no device prefix for relative open.
  187. ObjectId,
  188. 8 );
  189. InitializeObjectAttributes( &ObjectAttributes,
  190. &str,
  191. OBJ_CASE_INSENSITIVE,
  192. VolumeHandle,
  193. NULL );
  194. Status = NtCreateFile( &File,
  195. GENERIC_READ,
  196. &ObjectAttributes,
  197. &IoStatusBlock,
  198. NULL,
  199. FILE_ATTRIBUTE_NORMAL,
  200. FILE_SHARE_READ | FILE_SHARE_WRITE,
  201. FILE_OPEN,
  202. FILE_OPEN_BY_FILE_ID,
  203. NULL,
  204. 0 );
  205. if (NT_SUCCESS( Status )) {
  206. RtlZeroMemory( nameBuffer, sizeof(nameBuffer) );
  207. FileName = (PFILE_NAME_INFORMATION) &nameBuffer[0];
  208. FileName->FileNameLength = sizeof(nameBuffer) - sizeof(ULONG);
  209. GetNameStatus = NtQueryInformationFile( File,
  210. &IoStatusBlock,
  211. FileName,
  212. sizeof(nameBuffer),
  213. FileNameInformation );
  214. printf( "%S\n", FileName->FileName );
  215. CloseStatus = NtClose( File );
  216. if (!NT_SUCCESS( CloseStatus )) {
  217. printf( "\nCloseStatus %x", CloseStatus );
  218. }
  219. }
  220. return Status;
  221. }
  222. NTSTATUS
  223. ReadFileRecord (
  224. IN HANDLE VolumeHandle,
  225. IN ULONG RecordIndex,
  226. IN OUT PVOID Buffer
  227. )
  228. {
  229. NTSTATUS status;
  230. LARGE_INTEGER byteOffset;
  231. IO_STATUS_BLOCK ioStatusBlock;
  232. ULONG offsetWithinBuffer;
  233. byteOffset.QuadPart = ComputeFileRecordLbo( RecordIndex );
  234. if (FrsSize >= ClusterSize) {
  235. status = NtReadFile( VolumeHandle,
  236. NULL, // Event
  237. NULL, // ApcRoutine
  238. NULL, // ApcContext
  239. &ioStatusBlock,
  240. Buffer,
  241. FrsSize,
  242. &byteOffset, // ByteOffset
  243. NULL ); // Key
  244. } else {
  245. //
  246. // Clusters bigger than filerecords, do cluster
  247. // size reads and dice up the returns.
  248. //
  249. if ((-1 == CachedOffset) ||
  250. (byteOffset.QuadPart < CachedOffset) ||
  251. ((byteOffset.QuadPart + FrsSize) > (CachedOffset + ClusterSize))) {
  252. if (DebugLevel >= 1) {
  253. printf( "\nCache miss at %I64x", byteOffset.QuadPart );
  254. }
  255. status = NtReadFile( VolumeHandle,
  256. NULL, // Event
  257. NULL, // ApcRoutine
  258. NULL, // ApcContext
  259. &ioStatusBlock,
  260. CacheBuffer,
  261. ClusterSize,
  262. &byteOffset, // ByteOffset
  263. NULL ); // Key
  264. if (STATUS_SUCCESS != status) {
  265. //
  266. // The cache buffer may be junk now, reread it next time.
  267. //
  268. CachedOffset = -1;
  269. return status;
  270. }
  271. CachedOffset = byteOffset.QuadPart;
  272. offsetWithinBuffer = 0;
  273. } else {
  274. if (DebugLevel >= 1) {
  275. printf( "\nCache hit at %I64x", byteOffset.QuadPart );
  276. }
  277. offsetWithinBuffer = (ULONG) (byteOffset.QuadPart % CachedOffset);
  278. status = STATUS_SUCCESS;
  279. }
  280. RtlCopyMemory( Buffer, CacheBuffer + offsetWithinBuffer, FrsSize );
  281. }
  282. return status;
  283. }
  284. int
  285. FastFind (
  286. IN PWCHAR FileName,
  287. IN PWCHAR DriveLetter
  288. )
  289. {
  290. IO_STATUS_BLOCK IoStatusBlock;
  291. UNICODE_STRING str;
  292. NTSTATUS Status;
  293. NTSTATUS ReadStatus;
  294. NTSTATUS CloseStatus;
  295. LARGE_INTEGER byteOffset;
  296. LONGLONG mftBytesRead;
  297. HANDLE volumeHandle;
  298. DWORD WStatus;
  299. WCHAR Full[] = FULL_PATH; // Arrays of WCHAR's aren't constants
  300. WCHAR Volume[] = VOLUME_PATH;
  301. BIOS_PARAMETER_BLOCK bpb;
  302. PPACKED_BOOT_SECTOR bootSector;
  303. PFILE_RECORD_SEGMENT_HEADER fileRecord;
  304. PATTRIBUTE_RECORD_HEADER attr;
  305. VCN nextVcn;
  306. VCN currentVcn;
  307. VCN vcnDelta;
  308. LCN currentLcn;
  309. LCN lcnDelta;
  310. PUCHAR bsPtr;
  311. UCHAR v;
  312. UCHAR l;
  313. UCHAR i;
  314. ULONG extentCount;
  315. ULONG recordIndex;
  316. ULONG mftRecords;
  317. ULONG fileNameLength;
  318. MFT_SEGMENT_REFERENCE segRef;
  319. RtlInitUnicodeString( &str, Full );
  320. RtlCopyMemory( &str.Buffer[FULL_DRIVE_LETTER_INDEX], DriveLetter, sizeof(WCHAR) );
  321. str.Length = 0x1E;
  322. //
  323. // Open the volume for relative opens.
  324. //
  325. RtlCopyMemory( &Volume[VOLUME_DRIVE_LETTER_INDEX], DriveLetter, sizeof(WCHAR) );
  326. printf( "\nOpening volume handle, this may take a while..." );
  327. volumeHandle = CreateFileW( (PUSHORT) &Volume,
  328. GENERIC_READ | GENERIC_WRITE,
  329. FILE_SHARE_READ | FILE_SHARE_WRITE,
  330. NULL,
  331. OPEN_EXISTING,
  332. 0,
  333. NULL );
  334. if (volumeHandle == INVALID_HANDLE_VALUE) {
  335. WStatus = GetLastError();
  336. printf( "Unable to open %ws volume\n", &Volume );
  337. printf( "Error from CreateFile", WStatus );
  338. return WStatus;
  339. }
  340. printf( "\nVolume handle opened, starting MFT scan" );
  341. byteOffset.QuadPart = 0;
  342. ReadStatus = NtReadFile( volumeHandle,
  343. NULL, // Event
  344. NULL, // ApcRoutine
  345. NULL, // ApcContext
  346. &IoStatusBlock,
  347. mybuffer,
  348. 0x200,
  349. &byteOffset, // ByteOffset
  350. NULL ); // Key
  351. if (STATUS_SUCCESS != ReadStatus) {
  352. printf( "\nBoot sector read failed with status %x", ReadStatus );
  353. goto exit;
  354. }
  355. bootSector = (PPACKED_BOOT_SECTOR) mybuffer;
  356. if (bootSector->Oem[0] != 'N' ||
  357. bootSector->Oem[1] != 'T' ||
  358. bootSector->Oem[2] != 'F' ||
  359. bootSector->Oem[3] != 'S') {
  360. printf( "\nNot an NTFS volume" );
  361. goto exit;
  362. }
  363. NtfsUnpackBios( &bpb, &bootSector->PackedBpb );
  364. ClusterSize = bpb.BytesPerSector * bpb.SectorsPerCluster;
  365. if (bootSector->ClustersPerFileRecordSegment < 0) {
  366. FrsSize = 1 << (-1 * bootSector->ClustersPerFileRecordSegment);
  367. } else {
  368. FrsSize = bootSector->ClustersPerFileRecordSegment * ClusterSize;
  369. }
  370. MftStart.QuadPart = ClusterSize * bootSector->MftStartLcn;
  371. mftBytesRead = 0;
  372. ReadStatus = NtReadFile( volumeHandle,
  373. NULL, // Event
  374. NULL, // ApcRoutine
  375. NULL, // ApcContext
  376. &IoStatusBlock,
  377. mybuffer,
  378. FrsSize,
  379. &MftStart, // ByteOffset
  380. NULL ); // Key
  381. if (STATUS_SUCCESS != ReadStatus) {
  382. printf( "\nMFT record 0 read failed with status %x", ReadStatus );
  383. goto exit;
  384. }
  385. mftBytesRead += IoStatusBlock.Information;
  386. FindAttributeInFileRecord( (PFILE_RECORD_SEGMENT_HEADER) mybuffer,
  387. $DATA,
  388. NULL,
  389. &attr );
  390. if (NULL == attr) {
  391. printf( "\nMFT record 0 has no $DATA attribute" );
  392. goto exit;
  393. }
  394. if (attr->FormCode == RESIDENT_FORM) {
  395. printf( "\nVolume has very few files, use dir /s" );
  396. goto exit;
  397. }
  398. //
  399. // BUGBUG keithka 4/28/00 Handle MFT with more than 4billion entries.
  400. //
  401. ASSERT (attr->Form.Nonresident.FileSize <= MAXULONG);
  402. mftRecords = (ULONG) (attr->Form.Nonresident.FileSize / FrsSize);
  403. //
  404. // Crack mapping pairs, read those clusters in a few big trnasfers,
  405. // seek out given filename in those buffers.
  406. //
  407. nextVcn = attr->Form.Nonresident.LowestVcn;
  408. currentLcn = 0;
  409. extentCount = 0;
  410. RtlZeroMemory( Extents, sizeof(Extents) );
  411. bsPtr = ((PUCHAR) attr) + attr->Form.Nonresident.MappingPairsOffset;
  412. while (*bsPtr != 0) {
  413. currentVcn = nextVcn;
  414. //
  415. // Variable names v and l used for consistency with comments in
  416. // ATTRIBUTE_RECORD_HEADER struct explaining how to decompress
  417. // mapping pair information.
  418. //
  419. v = (*bsPtr) & 0xf;
  420. l = ((*bsPtr) & 0xf0) >> 4;
  421. bsPtr += 1;
  422. for (vcnDelta = 0, i = 0; i < v; i++) {
  423. vcnDelta += *(bsPtr++) << (8 * i);
  424. }
  425. for (lcnDelta = 0, i = 0; i < l; i++) {
  426. lcnDelta += *(bsPtr++) << (8 * i);
  427. }
  428. //
  429. // Sign extend.
  430. //
  431. if (0x80 & (*(bsPtr - 1))) {
  432. for(; i < sizeof(lcnDelta); i++) {
  433. lcnDelta += 0xff << (8 * i);
  434. }
  435. }
  436. currentLcn += lcnDelta;
  437. // printf( "\nVcn %I64x, Lcn %I64x, Length %I64x", currentVcn, currentLcn, vcnDelta );
  438. if (extentCount < MAX_EXTENTS) {
  439. Extents[extentCount].Vcn = currentVcn;
  440. Extents[extentCount].Lcn = currentLcn;
  441. Extents[extentCount].Length = vcnDelta;
  442. extentCount += 1;
  443. } else {
  444. printf( "\nExcessive MFT fragmentation, redefine MAX_EXTENTS and recompile" );
  445. }
  446. currentVcn += vcnDelta;
  447. }
  448. //
  449. // Now we know where the MFT is, let's go read it.
  450. //
  451. fileNameLength = wcslen( FileName );
  452. for (recordIndex = 0; recordIndex <= mftRecords; recordIndex++) {
  453. ReadStatus = ReadFileRecord( volumeHandle,
  454. recordIndex,
  455. mybuffer );
  456. if (STATUS_SUCCESS != ReadStatus) {
  457. printf( "\nMFT record read failed with status %x", ReadStatus );
  458. goto exit;
  459. }
  460. if (FindNameInFileRecord( (PFILE_RECORD_SEGMENT_HEADER) mybuffer,
  461. FileName,
  462. fileNameLength )) {
  463. //
  464. // Found a match, open by id and retrieve name.
  465. //
  466. if (DebugLevel >= 1) {
  467. printf( "\nFound match in file %08x %08x\n",
  468. ((PFILE_RECORD_SEGMENT_HEADER) mybuffer)->SequenceNumber,
  469. recordIndex );
  470. } else {
  471. printf( "\n" );
  472. }
  473. segRef.SegmentNumberLowPart = recordIndex;
  474. segRef.SegmentNumberHighPart = 0;
  475. segRef.SequenceNumber = ((PFILE_RECORD_SEGMENT_HEADER) mybuffer)->SequenceNumber;
  476. FsTestOpenById( (PUCHAR) &segRef, volumeHandle );
  477. }
  478. //
  479. // The number 0x400 is completely arbitrary. It's a reasonable interval
  480. // of work to do before printing another period to tell the user we're
  481. // making progress still.
  482. //
  483. if (0 == (recordIndex % 0x400)) {
  484. printf( "." );
  485. }
  486. }
  487. exit:
  488. if (volumeHandle != NULL) {
  489. CloseHandle( volumeHandle );
  490. }
  491. return 0;
  492. }
  493. VOID
  494. FastFindHelp (
  495. char *ExeName
  496. )
  497. {
  498. printf( "This program finds a file by scanning the MFT (ntfs only).\n\n" );
  499. printf( "usage: %s x: filename\n", ExeName );
  500. printf( "Where x: is the drive letter\n" );
  501. printf( "example:\n" );
  502. printf( "%s d: windows.h", ExeName );
  503. }
  504. VOID
  505. _cdecl
  506. main (
  507. int argc,
  508. char *argv[]
  509. )
  510. {
  511. WCHAR drive;
  512. ANSI_STRING fileName;
  513. WCHAR uniBuff[MAX_PATH];
  514. UNICODE_STRING uniFileName;
  515. //
  516. // Get parameters.
  517. //
  518. if (argc < 3) {
  519. FastFindHelp( argv[0] );
  520. return;
  521. }
  522. if (argc >= 4) {
  523. sscanf( argv[3], "%x", &DebugLevel );
  524. } else {
  525. DebugLevel = 0;
  526. }
  527. drive = *argv[1];
  528. RtlInitAnsiString( &fileName, argv[2] );
  529. uniFileName.Buffer = uniBuff;
  530. RtlAnsiStringToUnicodeString( &uniFileName, &fileName, FALSE );
  531. FastFind( uniFileName.Buffer, &drive );
  532. return;
  533. }