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.

911 lines
25 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. CheckSup.c
  5. Abstract:
  6. This module implements check routines for Ntfs structures.
  7. Author:
  8. Tom Miller [TomM] 14-4-92
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. //
  13. // Array for log records which require a target attribute.
  14. // A TRUE indicates that the corresponding restart operation
  15. // requires a target attribute.
  16. //
  17. BOOLEAN TargetAttributeRequired[] = {FALSE, FALSE, TRUE, TRUE,
  18. TRUE, TRUE, TRUE, TRUE,
  19. TRUE, TRUE, FALSE, TRUE,
  20. TRUE, TRUE, TRUE, TRUE,
  21. TRUE, TRUE, TRUE, TRUE,
  22. TRUE, TRUE, TRUE, TRUE,
  23. FALSE, FALSE, FALSE, FALSE,
  24. TRUE, FALSE, FALSE, FALSE,
  25. FALSE, TRUE, TRUE };
  26. //
  27. // Local procedure prototypes
  28. //
  29. #ifdef ALLOC_PRAGMA
  30. #pragma alloc_text(PAGE, NtfsCheckAttributeRecord)
  31. #pragma alloc_text(PAGE, NtfsCheckFileRecord)
  32. #pragma alloc_text(PAGE, NtfsCheckIndexBuffer)
  33. #pragma alloc_text(PAGE, NtfsCheckIndexHeader)
  34. #pragma alloc_text(PAGE, NtfsCheckIndexRoot)
  35. #pragma alloc_text(PAGE, NtfsCheckLogRecord)
  36. #pragma alloc_text(PAGE, NtfsCheckRestartTable)
  37. #endif
  38. BOOLEAN
  39. NtfsCheckFileRecord (
  40. IN PVCB Vcb,
  41. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  42. IN PFILE_REFERENCE FileReference OPTIONAL,
  43. OUT PULONG CorruptionHint
  44. )
  45. /*++
  46. Routine Description:
  47. Consistency check for file records.
  48. Arguments:
  49. Vcb - the vcb it belongs to
  50. FileRecord - the filerecord to check
  51. FileReference - if specified double check the sequence number and self ref.
  52. fileref against it
  53. CorruptionHint - hint for debugging on where corruption occured;
  54. Return Value:
  55. FALSE - if the file record is not valid
  56. TRUE - if it is
  57. --*/
  58. {
  59. PATTRIBUTE_RECORD_HEADER Attribute;
  60. PFILE_RECORD_SEGMENT_HEADER EndOfFileRecord;
  61. ULONG BytesPerFileRecordSegment = Vcb->BytesPerFileRecordSegment;
  62. BOOLEAN StandardInformationSeen = FALSE;
  63. ULONG BytesInOldHeader;
  64. PAGED_CODE();
  65. *CorruptionHint = 0;
  66. EndOfFileRecord = Add2Ptr( FileRecord, BytesPerFileRecordSegment );
  67. //
  68. // Check the file record header for consistency.
  69. //
  70. if ((*(PULONG)FileRecord->MultiSectorHeader.Signature != *(PULONG)FileSignature)
  71. ||
  72. ((ULONG)FileRecord->MultiSectorHeader.UpdateSequenceArrayOffset >
  73. (SEQUENCE_NUMBER_STRIDE -
  74. (PAGE_SIZE / SEQUENCE_NUMBER_STRIDE + 1) * sizeof(USHORT)))
  75. ||
  76. ((ULONG)((FileRecord->MultiSectorHeader.UpdateSequenceArraySize - 1) * SEQUENCE_NUMBER_STRIDE) !=
  77. BytesPerFileRecordSegment)
  78. ||
  79. !FlagOn(FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE)) {
  80. DebugTrace( 0, 0, ("Invalid file record: %08lx\n", FileRecord) );
  81. *CorruptionHint = 1;
  82. ASSERTMSG( "Invalid resident file record\n", FALSE );
  83. return FALSE;
  84. }
  85. BytesInOldHeader = QuadAlign( sizeof( FILE_RECORD_SEGMENT_HEADER_V0 ) + (UpdateSequenceArraySize( BytesPerFileRecordSegment ) - 1) * sizeof( USHORT ));
  86. //
  87. // Offset bounds checks
  88. //
  89. if ((FileRecord->FirstFreeByte > BytesPerFileRecordSegment) ||
  90. (FileRecord->FirstFreeByte < BytesInOldHeader) ||
  91. (FileRecord->BytesAvailable != BytesPerFileRecordSegment) ||
  92. (((ULONG)FileRecord->FirstAttributeOffset < BytesInOldHeader) ||
  93. ((ULONG)FileRecord->FirstAttributeOffset >
  94. BytesPerFileRecordSegment - SIZEOF_RESIDENT_ATTRIBUTE_HEADER)) ||
  95. (!IsQuadAligned( FileRecord->FirstAttributeOffset ))) {
  96. *CorruptionHint = 2;
  97. ASSERTMSG( "Out of bound offset in frs\n", FALSE );
  98. return FALSE;
  99. }
  100. //
  101. // Optional fileref number check
  102. //
  103. if (ARGUMENT_PRESENT( FileReference )) {
  104. if ((FileReference->SequenceNumber != FileRecord->SequenceNumber) ||
  105. ((FileRecord->FirstAttributeOffset > BytesInOldHeader) &&
  106. ((FileRecord->SegmentNumberHighPart != FileReference->SegmentNumberHighPart) ||
  107. (FileRecord->SegmentNumberLowPart != FileReference->SegmentNumberLowPart)))) {
  108. *CorruptionHint = 3;
  109. ASSERTMSG( "Filerecord fileref doesn't match expected value\n", FALSE );
  110. return FALSE;
  111. }
  112. }
  113. //
  114. // Loop to check all of the attributes.
  115. //
  116. for (Attribute = NtfsFirstAttribute(FileRecord);
  117. Attribute->TypeCode != $END;
  118. Attribute = NtfsGetNextRecord(Attribute)) {
  119. // if (!StandardInformationSeen &&
  120. // (Attribute->TypeCode != $STANDARD_INFORMATION) &&
  121. // XxEqlZero(FileRecord->BaseFileRecordSegment)) {
  122. //
  123. // DebugTrace( 0, 0, ("Standard Information missing: %08lx\n", Attribute) );
  124. //
  125. // ASSERTMSG( "Standard Information missing\n", FALSE );
  126. // return FALSE;
  127. // }
  128. StandardInformationSeen = TRUE;
  129. if (!NtfsCheckAttributeRecord( Vcb,
  130. FileRecord,
  131. Attribute,
  132. FALSE,
  133. CorruptionHint )) {
  134. return FALSE;
  135. }
  136. }
  137. return TRUE;
  138. }
  139. BOOLEAN
  140. NtfsCheckAttributeRecord (
  141. IN PVCB Vcb,
  142. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  143. IN PATTRIBUTE_RECORD_HEADER Attribute,
  144. IN ULONG CheckHeaderOnly,
  145. IN PULONG CorruptionHint
  146. )
  147. {
  148. PVOID NextAttribute;
  149. PVOID EndOfFileRecord;
  150. PVOID FirstFreeByte;
  151. PVOID Data;
  152. ULONG Length;
  153. ULONG BytesPerFileRecordSegment = Vcb->BytesPerFileRecordSegment;
  154. PAGED_CODE();
  155. EndOfFileRecord = Add2Ptr( FileRecord, BytesPerFileRecordSegment );
  156. FirstFreeByte = Add2Ptr( FileRecord, FileRecord->FirstFreeByte );
  157. //
  158. // Do an alignment check before creating a ptr based on this value
  159. //
  160. if (!IsQuadAligned( Attribute->RecordLength )) {
  161. *CorruptionHint = Attribute->TypeCode + 0xc;
  162. ASSERTMSG( "Misaligned attribute length\n", FALSE );
  163. return FALSE;
  164. }
  165. NextAttribute = NtfsGetNextRecord(Attribute);
  166. //
  167. // Check the fixed part of the attribute record header.
  168. //
  169. if ((Attribute->RecordLength >= BytesPerFileRecordSegment)
  170. ||
  171. (NextAttribute >= EndOfFileRecord)
  172. ||
  173. (FlagOn(Attribute->NameOffset, 1) != 0)
  174. ||
  175. ((Attribute->NameLength != 0) &&
  176. (((ULONG)Attribute->NameOffset + (ULONG)Attribute->NameLength) >
  177. Attribute->RecordLength))) {
  178. DebugTrace( 0, 0, ("Invalid attribute record header: %08lx\n", Attribute) );
  179. *CorruptionHint = Attribute->TypeCode + 1;
  180. ASSERTMSG( "Invalid attribute record header\n", FALSE );
  181. return FALSE;
  182. }
  183. if (NextAttribute > FirstFreeByte) {
  184. *CorruptionHint = Attribute->TypeCode + 2;
  185. ASSERTMSG( "Attributes beyond first free byte\n", FALSE );
  186. return FALSE;
  187. }
  188. //
  189. // Check the resident attribute fields.
  190. //
  191. if (Attribute->FormCode == RESIDENT_FORM) {
  192. if ((Attribute->Form.Resident.ValueLength >= Attribute->RecordLength) ||
  193. (((ULONG)Attribute->Form.Resident.ValueOffset +
  194. Attribute->Form.Resident.ValueLength) > Attribute->RecordLength) ||
  195. (!IsQuadAligned( Attribute->Form.Resident.ValueOffset ))) {
  196. DebugTrace( 0, 0, ("Invalid resident attribute record header: %08lx\n", Attribute) );
  197. *CorruptionHint = Attribute->TypeCode + 3;
  198. ASSERTMSG( "Invalid resident attribute record header\n", FALSE );
  199. return FALSE;
  200. }
  201. //
  202. // Check the nonresident attribute fields
  203. //
  204. } else if (Attribute->FormCode == NONRESIDENT_FORM) {
  205. VCN CurrentVcn, NextVcn;
  206. LCN CurrentLcn;
  207. LONGLONG Change;
  208. PCHAR ch;
  209. ULONG VcnBytes;
  210. ULONG LcnBytes;
  211. if ((Attribute->Form.Nonresident.LowestVcn >
  212. (Attribute->Form.Nonresident.HighestVcn + 1))
  213. ||
  214. ((ULONG)Attribute->Form.Nonresident.MappingPairsOffset >=
  215. Attribute->RecordLength)
  216. ||
  217. (Attribute->Form.Nonresident.ValidDataLength < 0) ||
  218. (Attribute->Form.Nonresident.FileSize < 0) ||
  219. (Attribute->Form.Nonresident.AllocatedLength < 0)
  220. ||
  221. (Attribute->Form.Nonresident.ValidDataLength >
  222. Attribute->Form.Nonresident.FileSize)
  223. ||
  224. (Attribute->Form.Nonresident.FileSize >
  225. Attribute->Form.Nonresident.AllocatedLength)) {
  226. DebugTrace( 0, 0, ("Invalid nonresident attribute record header: %08lx\n", Attribute) );
  227. *CorruptionHint = Attribute->TypeCode + 4;
  228. ASSERTMSG( "Invalid nonresident attribute record header\n", FALSE );
  229. return FALSE;
  230. }
  231. if (CheckHeaderOnly) { return TRUE; }
  232. //
  233. // Implement the decompression algorithm, as defined in ntfs.h.
  234. // (This code should look remarkably similar to what goes on in
  235. // NtfsLookupAllocation!)
  236. //
  237. NextVcn = Attribute->Form.Nonresident.LowestVcn;
  238. CurrentLcn = 0;
  239. ch = (PCHAR)Attribute + Attribute->Form.Nonresident.MappingPairsOffset;
  240. //
  241. // Loop to process mapping pairs, insuring we do not run off the end
  242. // of the attribute, and that we do not map to nonexistant Lcns.
  243. //
  244. while (!IsCharZero(*ch)) {
  245. //
  246. // Set Current Vcn from initial value or last pass through loop.
  247. //
  248. CurrentVcn = NextVcn;
  249. //
  250. // Extract the counts from the two nibbles of this byte.
  251. //
  252. VcnBytes = *ch & 0xF;
  253. LcnBytes = *ch++ >> 4;
  254. //
  255. // Neither of these should be larger than a VCN.
  256. //
  257. if ((VcnBytes > sizeof( VCN )) ||
  258. (LcnBytes > sizeof( VCN ))) {
  259. DebugTrace( 0, 0, ("Invalid maping pair byte count: %08lx\n", Attribute) );
  260. *CorruptionHint = Attribute->TypeCode + 5;
  261. ASSERTMSG( "Invalid maping pair byte count\n", FALSE );
  262. return FALSE;
  263. }
  264. //
  265. // Extract the Vcn change (use of RtlCopyMemory works for little-Endian)
  266. // and update NextVcn.
  267. //
  268. Change = 0;
  269. //
  270. // Make sure we are not going beyond the end of the attribute
  271. // record, and that the Vcn change is not negative or zero.
  272. //
  273. if (((ULONG_PTR)(ch + VcnBytes + LcnBytes + 1) > (ULONG_PTR)NextAttribute)
  274. ||
  275. IsCharLtrZero(*(ch + VcnBytes - 1))) {
  276. DebugTrace( 0, 0, ("Invalid maping pairs array: %08lx\n", Attribute) );
  277. *CorruptionHint = Attribute->TypeCode + 6;
  278. ASSERTMSG( "Invalid maping pairs array\n", FALSE );
  279. return FALSE;
  280. }
  281. RtlCopyMemory( &Change, ch, VcnBytes );
  282. ch += VcnBytes;
  283. NextVcn = NextVcn + Change;
  284. //
  285. // Extract the Lcn change and update CurrentLcn.
  286. //
  287. Change = 0;
  288. if (IsCharLtrZero(*(ch + LcnBytes - 1))) {
  289. Change = Change - 1;
  290. }
  291. RtlCopyMemory( &Change, ch, LcnBytes );
  292. ch += LcnBytes;
  293. CurrentLcn = CurrentLcn + Change;
  294. if ((LcnBytes != 0) &&
  295. ((CurrentLcn + (NextVcn - CurrentVcn) - 1) > Vcb->TotalClusters)) {
  296. DebugTrace( 0, 0, ("Invalid Lcn: %08lx\n", Attribute) );
  297. *CorruptionHint = Attribute->TypeCode + 7;
  298. ASSERTMSG( "Invalid Lcn\n", FALSE );
  299. return FALSE;
  300. }
  301. }
  302. //
  303. // Finally, check HighestVcn.
  304. //
  305. if (NextVcn != (Attribute->Form.Nonresident.HighestVcn + 1)) {
  306. DebugTrace( 0, 0, ("Disagreement with mapping pairs: %08lx\n", Attribute) );
  307. *CorruptionHint = Attribute->TypeCode + 8;
  308. ASSERTMSG( "Disagreement with mapping pairs\n", FALSE );
  309. return FALSE;
  310. }
  311. } else {
  312. DebugTrace( 0, 0, ("Invalid attribute form code: %08lx\n", Attribute) );
  313. ASSERTMSG( "Invalid attribute form code\n", FALSE );
  314. return FALSE;
  315. }
  316. //
  317. // Now check the attributes by type code, if they are resident. Not all
  318. // attributes require specific checks (such as $STANDARD_INFORMATION and $DATA).
  319. //
  320. if (CheckHeaderOnly || !NtfsIsAttributeResident( Attribute )) {
  321. return TRUE;
  322. }
  323. Data = NtfsAttributeValue(Attribute);
  324. Length = Attribute->Form.Resident.ValueLength;
  325. switch (Attribute->TypeCode) {
  326. case $FILE_NAME:
  327. {
  328. if ((ULONG)((PFILE_NAME)Data)->FileNameLength * sizeof( WCHAR ) >
  329. (Length - (ULONG)sizeof(FILE_NAME) + sizeof( WCHAR ))) {
  330. DebugTrace( 0, 0, ("Invalid File Name attribute: %08lx\n", Attribute) );
  331. *CorruptionHint = Attribute->TypeCode + 9;
  332. ASSERTMSG( "Invalid File Name attribute\n", FALSE );
  333. return FALSE;
  334. }
  335. break;
  336. }
  337. case $INDEX_ROOT:
  338. {
  339. return NtfsCheckIndexRoot( Vcb, (PINDEX_ROOT)Data, Length );
  340. }
  341. case $STANDARD_INFORMATION:
  342. {
  343. if (Length < sizeof( STANDARD_INFORMATION ) &&
  344. Length != SIZEOF_OLD_STANDARD_INFORMATION)
  345. {
  346. DebugTrace( 0, 0, ("Invalid Standard Information attribute: %08lx\n", Attribute) );
  347. *CorruptionHint = Attribute->TypeCode + 0xa;
  348. ASSERTMSG( "Invalid Standard Information attribute size\n", FALSE );
  349. return FALSE;
  350. }
  351. break;
  352. }
  353. case $ATTRIBUTE_LIST:
  354. case $OBJECT_ID:
  355. case $SECURITY_DESCRIPTOR:
  356. case $VOLUME_NAME:
  357. case $VOLUME_INFORMATION:
  358. case $DATA:
  359. case $INDEX_ALLOCATION:
  360. case $BITMAP:
  361. case $REPARSE_POINT:
  362. case $EA_INFORMATION:
  363. case $EA:
  364. case $LOGGED_UTILITY_STREAM:
  365. break;
  366. default:
  367. {
  368. DebugTrace( 0, 0, ("Bad Attribute type code: %08lx\n", Attribute) );
  369. *CorruptionHint = Attribute->TypeCode + 0xb;
  370. ASSERTMSG( "Bad Attribute type code\n", FALSE );
  371. return FALSE;
  372. }
  373. }
  374. return TRUE;
  375. }
  376. BOOLEAN
  377. NtfsCheckIndexRoot (
  378. IN PVCB Vcb,
  379. IN PINDEX_ROOT IndexRoot,
  380. IN ULONG AttributeSize
  381. )
  382. {
  383. UCHAR ShiftValue;
  384. PAGED_CODE();
  385. //
  386. // Check whether this index root uses clusters or if the cluster size is larger than
  387. // the index block.
  388. //
  389. if (IndexRoot->BytesPerIndexBuffer >= Vcb->BytesPerCluster) {
  390. ShiftValue = (UCHAR) Vcb->ClusterShift;
  391. } else {
  392. ShiftValue = DEFAULT_INDEX_BLOCK_BYTE_SHIFT;
  393. }
  394. if ((AttributeSize < sizeof(INDEX_ROOT))
  395. ||
  396. ((IndexRoot->IndexedAttributeType != $FILE_NAME) && (IndexRoot->IndexedAttributeType != $UNUSED))
  397. ||
  398. ((IndexRoot->IndexedAttributeType == $FILE_NAME) && (IndexRoot->CollationRule != COLLATION_FILE_NAME))
  399. ||
  400. (IndexRoot->BytesPerIndexBuffer !=
  401. BytesFromIndexBlocks( IndexRoot->BlocksPerIndexBuffer, ShiftValue ))
  402. ||
  403. ((IndexRoot->BlocksPerIndexBuffer != 1) &&
  404. (IndexRoot->BlocksPerIndexBuffer != 2) &&
  405. (IndexRoot->BlocksPerIndexBuffer != 4) &&
  406. (IndexRoot->BlocksPerIndexBuffer != 8) &&
  407. (IndexRoot->BlocksPerIndexBuffer != 16) &&
  408. (IndexRoot->BlocksPerIndexBuffer != 32) &&
  409. (IndexRoot->BlocksPerIndexBuffer != 64) &&
  410. (IndexRoot->BlocksPerIndexBuffer != 128))) {
  411. DebugTrace( 0, 0, ("Bad Index Root: %08lx\n", IndexRoot) );
  412. ASSERTMSG( "Bad Index Root\n", FALSE );
  413. return FALSE;
  414. }
  415. return NtfsCheckIndexHeader( &IndexRoot->IndexHeader,
  416. AttributeSize - sizeof(INDEX_ROOT) + sizeof(INDEX_HEADER) );
  417. }
  418. BOOLEAN
  419. NtfsCheckIndexBuffer (
  420. IN PSCB Scb,
  421. IN PINDEX_ALLOCATION_BUFFER IndexBuffer
  422. )
  423. {
  424. ULONG BytesPerIndexBuffer = Scb->ScbType.Index.BytesPerIndexBuffer;
  425. PAGED_CODE();
  426. //
  427. // Check the index buffer for consistency.
  428. //
  429. if ((*(PULONG)IndexBuffer->MultiSectorHeader.Signature != *(PULONG)IndexSignature)
  430. ||
  431. ((ULONG)IndexBuffer->MultiSectorHeader.UpdateSequenceArrayOffset >
  432. (SEQUENCE_NUMBER_STRIDE - (PAGE_SIZE / SEQUENCE_NUMBER_STRIDE + 1) * sizeof(USHORT)))
  433. ||
  434. ((ULONG)((IndexBuffer->MultiSectorHeader.UpdateSequenceArraySize - 1) * SEQUENCE_NUMBER_STRIDE) !=
  435. BytesPerIndexBuffer)) {
  436. DebugTrace( 0, 0, ("Invalid Index Buffer: %08lx\n", IndexBuffer) );
  437. ASSERTMSG( "Invalid resident Index Buffer\n", FALSE );
  438. return FALSE;
  439. }
  440. return NtfsCheckIndexHeader( &IndexBuffer->IndexHeader,
  441. BytesPerIndexBuffer -
  442. FIELD_OFFSET(INDEX_ALLOCATION_BUFFER, IndexHeader) );
  443. }
  444. BOOLEAN
  445. NtfsCheckIndexHeader (
  446. IN PINDEX_HEADER IndexHeader,
  447. IN ULONG BytesAvailable
  448. )
  449. {
  450. PINDEX_ENTRY IndexEntry, NextIndexEntry;
  451. PINDEX_ENTRY EndOfIndex;
  452. ULONG MinIndexEntry = sizeof(INDEX_ENTRY);
  453. PAGED_CODE();
  454. if (FlagOn(IndexHeader->Flags, INDEX_NODE)) {
  455. MinIndexEntry += sizeof(VCN);
  456. }
  457. if ((IndexHeader->FirstIndexEntry > (BytesAvailable - MinIndexEntry))
  458. ||
  459. (IndexHeader->FirstFreeByte > BytesAvailable)
  460. ||
  461. (IndexHeader->BytesAvailable > BytesAvailable)
  462. ||
  463. ((IndexHeader->FirstIndexEntry + MinIndexEntry) > IndexHeader->FirstFreeByte)
  464. ||
  465. (IndexHeader->FirstFreeByte > IndexHeader->BytesAvailable)) {
  466. DebugTrace( 0, 0, ("Bad Index Header: %08lx\n", IndexHeader) );
  467. ASSERTMSG( "Bad Index Header\n", FALSE );
  468. return FALSE;
  469. }
  470. IndexEntry = NtfsFirstIndexEntry(IndexHeader);
  471. EndOfIndex = Add2Ptr(IndexHeader, IndexHeader->FirstFreeByte);
  472. while (TRUE) {
  473. NextIndexEntry = NtfsNextIndexEntry(IndexEntry);
  474. if (((ULONG)IndexEntry->Length < MinIndexEntry)
  475. ||
  476. (NextIndexEntry > EndOfIndex)
  477. ||
  478. // ((ULONG)IndexEntry->AttributeLength >
  479. // ((ULONG)IndexEntry->Length - MinIndexEntry))
  480. //
  481. // ||
  482. (BooleanFlagOn(IndexEntry->Flags, INDEX_ENTRY_NODE) !=
  483. BooleanFlagOn(IndexHeader->Flags, INDEX_NODE))) {
  484. DebugTrace( 0, 0, ("Bad Index Entry: %08lx\n", IndexEntry) );
  485. ASSERTMSG( "Bad Index Entry\n", FALSE );
  486. return FALSE;
  487. }
  488. if (FlagOn(IndexEntry->Flags, INDEX_ENTRY_END)) {
  489. break;
  490. }
  491. IndexEntry = NextIndexEntry;
  492. }
  493. return TRUE;
  494. }
  495. BOOLEAN
  496. NtfsCheckLogRecord (
  497. IN PNTFS_LOG_RECORD_HEADER LogRecord,
  498. IN ULONG LogRecordLength,
  499. IN TRANSACTION_ID TransactionId,
  500. IN ULONG AttributeEntrySize
  501. )
  502. {
  503. BOOLEAN ValidLogRecord = FALSE;
  504. PAGED_CODE();
  505. //
  506. // We make the following checks on the log record.
  507. //
  508. // - Minimum length must contain an NTFS_LOG_RECORD_HEADER
  509. // - Transaction Id must be a valid value (a valid index offset)
  510. //
  511. // The following are values in the log record.
  512. //
  513. // - Redo/Undo offset must be quadaligned
  514. // - Redo/Undo offset + length must be contained in the log record
  515. // - Target attribute must be a valid value (either 0 or valid index offset)
  516. // - Record offset must be quad-aligned and less than the file record size.
  517. // - Log record size must be sufficient for Lcn's to follow.
  518. //
  519. // Use the separate assert messages in order to identify the error (used the same text so
  520. // the compiler can still optimize).
  521. //
  522. if (LogRecordLength < sizeof( NTFS_LOG_RECORD_HEADER )) {
  523. ASSERTMSG( "Invalid log record\n", FALSE );
  524. } else if (TransactionId == 0) {
  525. ASSERTMSG( "Invalid log record\n", FALSE );
  526. } else if ((TransactionId - sizeof( RESTART_TABLE )) % sizeof( TRANSACTION_ENTRY )) {
  527. ASSERTMSG( "Invalid log record\n", FALSE );
  528. } else if (FlagOn( LogRecord->RedoOffset, 7 )) {
  529. ASSERTMSG( "Invalid log record\n", FALSE );
  530. } else if (FlagOn( LogRecord->UndoOffset, 7 )) {
  531. ASSERTMSG( "Invalid log record\n", FALSE );
  532. } else if ((ULONG) LogRecord->RedoOffset + LogRecord->RedoLength > LogRecordLength) {
  533. ASSERTMSG( "Invalid log record\n", FALSE );
  534. } else if ((LogRecord->UndoOperation != CompensationLogRecord) &&
  535. ((ULONG) LogRecord->UndoOffset + LogRecord->UndoLength > LogRecordLength)) {
  536. ASSERTMSG( "Invalid log record\n", FALSE );
  537. } else if (LogRecordLength < (sizeof( NTFS_LOG_RECORD_HEADER ) +
  538. ((LogRecord->LcnsToFollow != 0) ?
  539. (sizeof( LCN ) * (LogRecord->LcnsToFollow - 1)) :
  540. 0))) {
  541. ASSERTMSG( "Invalid log record\n", FALSE );
  542. //
  543. // NOTE: The next two clauses test different cases for the TargetAttribute in
  544. // the log record. Don't add any tests after this point as the ValidLogRecord
  545. // value is set to TRUE internally and no other checks take place.
  546. //
  547. } else if (LogRecord->TargetAttribute == 0) {
  548. if (((LogRecord->RedoOperation <= UpdateRecordDataAllocation) &&
  549. TargetAttributeRequired[LogRecord->RedoOperation]) ||
  550. ((LogRecord->UndoOperation <= UpdateRecordDataAllocation) &&
  551. TargetAttributeRequired[LogRecord->UndoOperation])) {
  552. ASSERTMSG( "Invalid log record\n", FALSE );
  553. } else {
  554. ValidLogRecord = TRUE;
  555. }
  556. //
  557. // Read the note above if changing this.
  558. //
  559. } else if ((LogRecord->RedoOperation != ForgetTransaction) &&
  560. ((LogRecord->TargetAttribute - sizeof( RESTART_TABLE )) % AttributeEntrySize)) {
  561. ASSERTMSG( "Invalid log record\n", FALSE );
  562. //
  563. // Read the note above if changing this.
  564. //
  565. } else {
  566. ValidLogRecord = TRUE;
  567. }
  568. return ValidLogRecord;
  569. }
  570. BOOLEAN
  571. NtfsCheckRestartTable (
  572. IN PRESTART_TABLE RestartTable,
  573. IN ULONG TableSize
  574. )
  575. {
  576. ULONG ActualTableSize;
  577. ULONG Index;
  578. PDIRTY_PAGE_ENTRY_V0 NextEntry;
  579. PAGED_CODE();
  580. //
  581. // We want to make the following checks.
  582. //
  583. // EntrySize - Must be less than table size and non-zero.
  584. //
  585. // NumberEntries - The table size must contain at least this many entries
  586. // plus the table header.
  587. //
  588. // NumberAllocated - Must be less than/equal to NumberEntries
  589. //
  590. // FreeGoal - Must lie in the table.
  591. //
  592. // FirstFree
  593. // LastFree - Must either be 0 or be on a restart entry boundary.
  594. //
  595. if ((RestartTable->EntrySize == 0) ||
  596. (RestartTable->EntrySize > TableSize) ||
  597. ((RestartTable->EntrySize + sizeof( RESTART_TABLE )) > TableSize) ||
  598. (((TableSize - sizeof( RESTART_TABLE )) / RestartTable->EntrySize) < RestartTable->NumberEntries) ||
  599. (RestartTable->NumberAllocated > RestartTable->NumberEntries)) {
  600. ASSERTMSG( "Invalid Restart Table sizes\n", FALSE );
  601. return FALSE;
  602. }
  603. ActualTableSize = (RestartTable->EntrySize * RestartTable->NumberEntries) +
  604. sizeof( RESTART_TABLE );
  605. if ((RestartTable->FirstFree > ActualTableSize) ||
  606. (RestartTable->LastFree > ActualTableSize) ||
  607. ((RestartTable->FirstFree != 0) && (RestartTable->FirstFree < sizeof( RESTART_TABLE ))) ||
  608. ((RestartTable->LastFree != 0) && (RestartTable->LastFree < sizeof( RESTART_TABLE )))) {
  609. ASSERTMSG( "Invalid Restart Table List Head\n", FALSE );
  610. return FALSE;
  611. }
  612. //
  613. // Make a pass through the table verifying that each entry
  614. // is either allocated or points to a valid offset in the
  615. // table.
  616. //
  617. for (Index = 0;Index < RestartTable->NumberEntries; Index++) {
  618. NextEntry = Add2Ptr( RestartTable,
  619. ((Index * RestartTable->EntrySize) +
  620. sizeof( RESTART_TABLE )));
  621. if ((NextEntry->AllocatedOrNextFree != RESTART_ENTRY_ALLOCATED) &&
  622. (NextEntry->AllocatedOrNextFree != 0) &&
  623. ((NextEntry->AllocatedOrNextFree < sizeof( RESTART_TABLE )) ||
  624. (((NextEntry->AllocatedOrNextFree - sizeof( RESTART_TABLE )) % RestartTable->EntrySize) != 0))) {
  625. ASSERTMSG( "Invalid Restart Table Entry\n", FALSE );
  626. return FALSE;
  627. }
  628. }
  629. //
  630. // Walk through the list headed by the first entry to make sure none
  631. // of the entries are currently being used.
  632. //
  633. for (Index = RestartTable->FirstFree; Index != 0; Index = NextEntry->AllocatedOrNextFree) {
  634. if (Index == RESTART_ENTRY_ALLOCATED) {
  635. ASSERTMSG( "Invalid Restart Table Free List\n", FALSE );
  636. return FALSE;
  637. }
  638. NextEntry = Add2Ptr( RestartTable, Index );
  639. }
  640. return TRUE;
  641. }