Leaked source code of windows server 2003
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.

4821 lines
128 KiB

  1. /*++
  2. Copyright (c) 1987-1997 Microsoft Corporation
  3. Module Name:
  4. chutil.c
  5. Abstract:
  6. Change Log utility routines.
  7. Author:
  8. Environment:
  9. User mode only.
  10. Contains NT-specific code.
  11. Requires ANSI C extensions: slash-slash comments, long external names.
  12. Revision History:
  13. 11-Jan-1994 (cliffv)
  14. Split out from changelg.c
  15. --*/
  16. //
  17. // Common include files.
  18. //
  19. #include "logonsrv.h" // Include files common to entire service
  20. #pragma hdrstop
  21. //
  22. // Include files specific to this .c file
  23. //
  24. //
  25. // Tables of related delta types
  26. //
  27. //
  28. // Table of delete delta types.
  29. // Index into the table with a delta type,
  30. // the entry is the delta type that is used to delete the object.
  31. //
  32. // There are some objects that can't be deleted. In that case, this table
  33. // contains a delta type that uniquely identifies the object. That allows
  34. // this table to be used to see if two deltas describe the same object type.
  35. //
  36. const NETLOGON_DELTA_TYPE NlGlobalDeleteDeltaType[MAX_DELETE_DELTA+1]
  37. = {
  38. AddOrChangeDomain, // 0 is an invalid delta type
  39. AddOrChangeDomain, // AddOrChangeDomain,
  40. DeleteGroup, // AddOrChangeGroup,
  41. DeleteGroup, // DeleteGroup,
  42. DeleteGroup, // RenameGroup,
  43. DeleteUser, // AddOrChangeUser,
  44. DeleteUser, // DeleteUser,
  45. DeleteUser, // RenameUser,
  46. DeleteGroup, // ChangeGroupMembership,
  47. DeleteAlias, // AddOrChangeAlias,
  48. DeleteAlias, // DeleteAlias,
  49. DeleteAlias, // RenameAlias,
  50. DeleteAlias, // ChangeAliasMembership,
  51. AddOrChangeLsaPolicy, // AddOrChangeLsaPolicy,
  52. DeleteLsaTDomain, // AddOrChangeLsaTDomain,
  53. DeleteLsaTDomain, // DeleteLsaTDomain,
  54. DeleteLsaAccount, // AddOrChangeLsaAccount,
  55. DeleteLsaAccount, // DeleteLsaAccount,
  56. DeleteLsaSecret, // AddOrChangeLsaSecret,
  57. DeleteLsaSecret, // DeleteLsaSecret,
  58. DeleteGroup, // DeleteGroupByName,
  59. DeleteUser, // DeleteUserByName,
  60. SerialNumberSkip, // SerialNumberSkip,
  61. DummyChangeLogEntry // DummyChangeLogEntry
  62. };
  63. //
  64. // Table of add delta types.
  65. // Index into the table with a delta type,
  66. // the entry is the delta type that is used to add the object.
  67. //
  68. // There are some objects that can't be added. In that case, this table
  69. // contains a delta type that uniquely identifies the object. That allows
  70. // this table to be used to see if two deltas describe the same object type.
  71. //
  72. // In the table, Groups and Aliases are represented as renames. This causes
  73. // NlPackSingleDelta to return both the group attributes and the group
  74. // membership.
  75. //
  76. const NETLOGON_DELTA_TYPE NlGlobalAddDeltaType[MAX_ADD_DELTA+1]
  77. = {
  78. AddOrChangeDomain, // 0 is an invalid delta type
  79. AddOrChangeDomain, // AddOrChangeDomain,
  80. RenameGroup, // AddOrChangeGroup,
  81. RenameGroup, // DeleteGroup,
  82. RenameGroup, // RenameGroup,
  83. AddOrChangeUser, // AddOrChangeUser,
  84. AddOrChangeUser, // DeleteUser,
  85. AddOrChangeUser, // RenameUser,
  86. RenameGroup, // ChangeGroupMembership,
  87. RenameAlias, // AddOrChangeAlias,
  88. RenameAlias, // DeleteAlias,
  89. RenameAlias, // RenameAlias,
  90. RenameAlias, // ChangeAliasMembership,
  91. AddOrChangeLsaPolicy, // AddOrChangeLsaPolicy,
  92. AddOrChangeLsaTDomain, // AddOrChangeLsaTDomain,
  93. AddOrChangeLsaTDomain, // DeleteLsaTDomain,
  94. AddOrChangeLsaAccount, // AddOrChangeLsaAccount,
  95. AddOrChangeLsaAccount, // DeleteLsaAccount,
  96. AddOrChangeLsaSecret, // AddOrChangeLsaSecret,
  97. AddOrChangeLsaSecret, // DeleteLsaSecret,
  98. RenameGroup, // DeleteGroupByName,
  99. AddOrChangeUser, // DeleteUserByName,
  100. SerialNumberSkip, // SerialNumberSkip,
  101. DummyChangeLogEntry // DummyChangeLogEntry
  102. };
  103. //
  104. // Table of Status Codes indicating the object doesn't exist.
  105. // Index into the table with a delta type.
  106. //
  107. // Map to STATUS_SUCCESS for the invalid cases to explicitly avoid other error
  108. // codes.
  109. const NTSTATUS NlGlobalObjectNotFoundStatus[MAX_OBJECT_NOT_FOUND_STATUS+1]
  110. = {
  111. STATUS_SUCCESS, // 0 is an invalid delta type
  112. STATUS_NO_SUCH_DOMAIN, // AddOrChangeDomain,
  113. STATUS_NO_SUCH_GROUP, // AddOrChangeGroup,
  114. STATUS_NO_SUCH_GROUP, // DeleteGroup,
  115. STATUS_NO_SUCH_GROUP, // RenameGroup,
  116. STATUS_NO_SUCH_USER, // AddOrChangeUser,
  117. STATUS_NO_SUCH_USER, // DeleteUser,
  118. STATUS_NO_SUCH_USER, // RenameUser,
  119. STATUS_NO_SUCH_GROUP, // ChangeGroupMembership,
  120. STATUS_NO_SUCH_ALIAS, // AddOrChangeAlias,
  121. STATUS_NO_SUCH_ALIAS, // DeleteAlias,
  122. STATUS_NO_SUCH_ALIAS, // RenameAlias,
  123. STATUS_NO_SUCH_ALIAS, // ChangeAliasMembership,
  124. STATUS_SUCCESS, // AddOrChangeLsaPolicy,
  125. STATUS_OBJECT_NAME_NOT_FOUND, // AddOrChangeLsaTDomain,
  126. STATUS_OBJECT_NAME_NOT_FOUND, // DeleteLsaTDomain,
  127. STATUS_OBJECT_NAME_NOT_FOUND, // AddOrChangeLsaAccount,
  128. STATUS_OBJECT_NAME_NOT_FOUND, // DeleteLsaAccount,
  129. STATUS_OBJECT_NAME_NOT_FOUND, // AddOrChangeLsaSecret,
  130. STATUS_OBJECT_NAME_NOT_FOUND, // DeleteLsaSecret,
  131. STATUS_NO_SUCH_GROUP, // DeleteGroupByName,
  132. STATUS_NO_SUCH_USER, // DeleteUserByName,
  133. STATUS_SUCCESS, // SerialNumberSkip,
  134. STATUS_SUCCESS // DummyChangeLogEntry
  135. };
  136. //
  137. // Context for I_NetLogonReadChangeLog
  138. //
  139. typedef struct _CHANGELOG_CONTEXT {
  140. LARGE_INTEGER SerialNumber;
  141. DWORD DbIndex;
  142. DWORD SequenceNumber;
  143. } CHANGELOG_CONTEXT, *PCHANGELOG_CONTEXT;
  144. //
  145. // Header for buffers returned from I_NetLogonReadChangeLog
  146. //
  147. typedef struct _CHANGELOG_BUFFER_HEADER {
  148. DWORD Size;
  149. DWORD Version;
  150. DWORD SequenceNumber;
  151. DWORD Flags;
  152. } CHANGELOG_BUFFER_HEADER, *PCHANGELOG_BUFFER_HEADER;
  153. #define CHANGELOG_BUFFER_VERSION 1
  154. ULONG NlGlobalChangeLogHandle = 0;
  155. ULONG NlGlobalChangeLogSequenceNumber;
  156. /* NlCreateChangeLogFile and NlWriteChangeLogBytes reference each other */
  157. NTSTATUS
  158. NlWriteChangeLogBytes(
  159. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  160. IN LPBYTE Buffer,
  161. IN DWORD BufferSize,
  162. IN BOOLEAN FlushIt
  163. );
  164. NTSTATUS
  165. NlCreateChangeLogFile(
  166. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
  167. )
  168. /*++
  169. Routine Description:
  170. Try to create a change log file. If it is successful then it sets
  171. the file handle in ChangeLogDesc, otherwise it leaves the handle invalid.
  172. NOTE: This function must be called with the change log locked.
  173. Arguments:
  174. ChangeLogDesc -- Description of the Changelog buffer being used
  175. Return Value:
  176. STATUS_SUCCESS - The Service completed successfully.
  177. --*/
  178. {
  179. NTSTATUS Status;
  180. WCHAR ChangeLogFile[MAX_PATH+CHANGELOG_FILE_POSTFIX_LENGTH+1];
  181. NlAssert( ChangeLogDesc->FileHandle == INVALID_HANDLE_VALUE );
  182. //
  183. // if the change file name is unknown, terminate the operation.
  184. //
  185. if( NlGlobalChangeLogFilePrefix[0] == L'\0' ) {
  186. return STATUS_NO_SUCH_FILE;
  187. }
  188. //
  189. // Create change log file. If it exists already then truncate it.
  190. //
  191. // Note : if a valid change log file exists on the system, then we
  192. // would have opened at initialization time.
  193. //
  194. wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
  195. wcscat( ChangeLogFile,
  196. ChangeLogDesc->TempLog ? TEMP_CHANGELOG_FILE_POSTFIX : CHANGELOG_FILE_POSTFIX );
  197. ChangeLogDesc->FileHandle = CreateFileW(
  198. ChangeLogFile,
  199. GENERIC_READ | GENERIC_WRITE,
  200. FILE_SHARE_READ, // allow backups and debugging
  201. NULL, // Supply better security ??
  202. CREATE_ALWAYS, // Overwrites always
  203. FILE_ATTRIBUTE_NORMAL,
  204. NULL ); // No template
  205. if (ChangeLogDesc->FileHandle == INVALID_HANDLE_VALUE) {
  206. Status = NetpApiStatusToNtStatus( GetLastError());
  207. NlPrint((NL_CRITICAL,"Unable to create changelog file: 0x%lx \n", Status));
  208. return Status;
  209. }
  210. //
  211. // Write cache in backup changelog file if the cache is valid.
  212. //
  213. if( ChangeLogDesc->Buffer != NULL ) {
  214. Status = NlWriteChangeLogBytes(
  215. ChangeLogDesc,
  216. ChangeLogDesc->Buffer,
  217. ChangeLogDesc->BufferSize,
  218. TRUE ); // Flush the bytes to disk
  219. return Status;
  220. }
  221. return STATUS_SUCCESS;
  222. }
  223. VOID
  224. NlWriteChangeLogCorruptEvent(
  225. IN NTSTATUS Status,
  226. IN DWORD DbIndex
  227. )
  228. /*++
  229. Routine Description:
  230. This routine writes the event log message stating that
  231. the change log file is corrupted.
  232. Arguments:
  233. Status -- Status code of the failure.
  234. DBIndex -- Index of the database that is corrupted.
  235. If not void DB, the database name will be logged.
  236. Return Value:
  237. None
  238. --*/
  239. {
  240. LPWSTR Database;
  241. LPWSTR MsgStrings[1];
  242. #ifdef _NETLOGON_SERVER
  243. //
  244. // If caller is calling when the netlogon service isn't running,
  245. // return. Otherwise, we may AV when accessing SAM handle
  246. // in the primary DomainInfo struct that may not be initialized.
  247. //
  248. if ( !NlStartNetlogonCall() ) {
  249. return;
  250. }
  251. //
  252. // Avoid the event if we are not in mixed mode
  253. // when the change log isn't used
  254. //
  255. if ( SamIMixedDomain(NlGlobalDomainInfo->DomSamServerHandle) ) {
  256. if ( DbIndex == LSA_DB ) {
  257. Database = L"LSA";
  258. } else if ( DbIndex == SAM_DB ) {
  259. Database = L"SAM";
  260. } else if ( DbIndex == BUILTIN_DB ) {
  261. Database = L"BUILTIN";
  262. } else {
  263. Database = L"\0"; // void DB
  264. }
  265. MsgStrings[0] = Database;
  266. //
  267. // Be consistent with the old versions of the log.
  268. // If the database is not void, the DB index should be
  269. // written into the raw data. Otherwise, the Status
  270. // should be written into the raw data.
  271. //
  272. NlpWriteEventlog ( NELOG_NetlogonChangeLogCorrupt,
  273. EVENTLOG_WARNING_TYPE,
  274. (DbIndex != VOID_DB) ?
  275. (LPBYTE)&DbIndex :
  276. (LPBYTE)&Status,
  277. (DbIndex != VOID_DB) ?
  278. sizeof(DbIndex) :
  279. sizeof(Status),
  280. MsgStrings,
  281. 1 );
  282. }
  283. //
  284. // Indicate that we are done using the domain info
  285. //
  286. NlEndNetlogonCall();
  287. #else
  288. UNREFERENCED_PARAMETER( Status );
  289. UNREFERENCED_PARAMETER( DbIndex );
  290. #endif // _NETLOGON_SERVER
  291. return;
  292. }
  293. NTSTATUS
  294. NlFlushChangeLog(
  295. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
  296. )
  297. /*++
  298. Routine Description:
  299. Flush any dirty buffers to the change log file itself.
  300. Ensure they are flushed to disk.
  301. NOTE: This function must be called with the change log locked.
  302. Arguments:
  303. ChangeLogDesc -- Description of the Changelog buffer being used
  304. Return Value:
  305. Status of the operation.
  306. --*/
  307. {
  308. NTSTATUS Status = STATUS_SUCCESS;
  309. OVERLAPPED Overlapped;
  310. DWORD BytesWritten;
  311. DWORD BufferSize;
  312. //
  313. // If there's nothing to do,
  314. // just return.
  315. //
  316. if ( ChangeLogDesc->LastDirtyByte == 0 ) {
  317. return STATUS_SUCCESS;
  318. }
  319. //
  320. // Write to the file.
  321. //
  322. if ( ChangeLogDesc->FileHandle == INVALID_HANDLE_VALUE ) {
  323. Status = NlCreateChangeLogFile( ChangeLogDesc );
  324. //
  325. // This must have written entire buffer if it is successful
  326. // creating the change log file.
  327. //
  328. goto Cleanup;
  329. }
  330. //
  331. // if we are unable to create this into the changelog file, work
  332. // with internal cache, but notify admin by sending admin alert.
  333. //
  334. if ( ChangeLogDesc->FileHandle != INVALID_HANDLE_VALUE ) {
  335. #ifdef notdef
  336. NlPrint((NL_CHANGELOG, "NlFlushChangeLog: %ld to %ld\n",
  337. ChangeLogDesc->FirstDirtyByte,
  338. ChangeLogDesc->LastDirtyByte ));
  339. #endif // notdef
  340. //
  341. // Seek to appropriate offset in the file.
  342. //
  343. RtlZeroMemory( &Overlapped, sizeof(Overlapped) );
  344. Overlapped.Offset = ChangeLogDesc->FirstDirtyByte;
  345. //
  346. // Actually write to the file.
  347. //
  348. BufferSize = ChangeLogDesc->LastDirtyByte -
  349. ChangeLogDesc->FirstDirtyByte + 1;
  350. if ( !WriteFile( ChangeLogDesc->FileHandle,
  351. &ChangeLogDesc->Buffer[ChangeLogDesc->FirstDirtyByte],
  352. BufferSize,
  353. &BytesWritten,
  354. &Overlapped ) ) {
  355. Status = NetpApiStatusToNtStatus( GetLastError() );
  356. NlPrint((NL_CRITICAL, "Write to ChangeLog failed 0x%lx\n",
  357. Status ));
  358. //
  359. // Recreate changelog file
  360. //
  361. CloseHandle( ChangeLogDesc->FileHandle );
  362. ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
  363. goto Cleanup;
  364. }
  365. //
  366. // Ensure all the bytes made it.
  367. //
  368. if ( BytesWritten != BufferSize ) {
  369. NlPrint((NL_CRITICAL,
  370. "Write to ChangeLog bad byte count %ld s.b. %ld\n",
  371. BytesWritten,
  372. BufferSize ));
  373. //
  374. // Recreate changelog file
  375. //
  376. CloseHandle( ChangeLogDesc->FileHandle );
  377. ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
  378. Status = STATUS_BUFFER_TOO_SMALL;
  379. goto Cleanup;
  380. }
  381. //
  382. // Force the modifications to disk.
  383. //
  384. if ( !FlushFileBuffers( ChangeLogDesc->FileHandle ) ) {
  385. Status = NetpApiStatusToNtStatus( GetLastError() );
  386. NlPrint((NL_CRITICAL, "Flush to ChangeLog failed 0x%lx\n", Status ));
  387. //
  388. // Recreate changelog file
  389. //
  390. CloseHandle( ChangeLogDesc->FileHandle );
  391. ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
  392. goto Cleanup;
  393. }
  394. //
  395. // Indicate these byte successfully made it out to disk.
  396. //
  397. ChangeLogDesc->FirstDirtyByte = 0;
  398. ChangeLogDesc->LastDirtyByte = 0;
  399. }
  400. Cleanup:
  401. if( !NT_SUCCESS(Status) ) {
  402. //
  403. // Write event log.
  404. //
  405. NlWriteChangeLogCorruptEvent( Status,
  406. VOID_DB ); // no particular DB
  407. }
  408. return Status;
  409. }
  410. NTSTATUS
  411. NlWriteChangeLogBytes(
  412. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  413. IN LPBYTE Buffer,
  414. IN DWORD BufferSize,
  415. IN BOOLEAN FlushIt
  416. )
  417. /*++
  418. Routine Description:
  419. Write bytes from the changelog cache to the change log file.
  420. Arguments:
  421. ChangeLogDesc -- Description of the Changelog buffer being used
  422. Buffer - Address within the changelog cache to write.
  423. BufferSize - Number of bytes to write.
  424. FlushIt - TRUE if the bytes are to be flushed to disk
  425. Return Value:
  426. Status of the operation.
  427. --*/
  428. {
  429. NTSTATUS Status;
  430. ULONG FirstDirtyByte;
  431. ULONG LastDirtyByte;
  432. //
  433. // Compute the new range of dirty bytes.
  434. //
  435. FirstDirtyByte = (ULONG)(((LPBYTE)Buffer) - ((LPBYTE)ChangeLogDesc->Buffer));
  436. LastDirtyByte = FirstDirtyByte + BufferSize - 1;
  437. #ifdef notdef
  438. NlPrint((NL_CHANGELOG, "NlWriteChangeLogBytes: %ld to %ld\n",
  439. FirstDirtyByte,
  440. LastDirtyByte ));
  441. #endif // notdef
  442. if ( ChangeLogDesc->LastDirtyByte == 0 ) {
  443. ChangeLogDesc->FirstDirtyByte = FirstDirtyByte;
  444. ChangeLogDesc->LastDirtyByte = LastDirtyByte;
  445. } else {
  446. if ( ChangeLogDesc->FirstDirtyByte > FirstDirtyByte ) {
  447. ChangeLogDesc->FirstDirtyByte = FirstDirtyByte;
  448. }
  449. if ( ChangeLogDesc->LastDirtyByte < LastDirtyByte ) {
  450. ChangeLogDesc->LastDirtyByte = LastDirtyByte;
  451. }
  452. }
  453. //
  454. // If the bytes are to be flushed,
  455. // do so.
  456. //
  457. if ( FlushIt ) {
  458. Status = NlFlushChangeLog( ChangeLogDesc );
  459. return Status;
  460. }
  461. return STATUS_SUCCESS;
  462. }
  463. PCHANGELOG_BLOCK_HEADER
  464. NlMoveToNextChangeLogBlock(
  465. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  466. IN PCHANGELOG_BLOCK_HEADER BlockPtr
  467. )
  468. /*++
  469. Routine Description:
  470. This function accepts a pointer to a change log
  471. block and returns the pointer to the next change log block in the
  472. buffer. It however wraps around the change log cache.
  473. NOTE: This function must be called with the change log locked.
  474. Arguments:
  475. ChangeLogDesc -- Description of the Changelog buffer being used
  476. BlockPtr - pointer to a change log block.
  477. Return Value:
  478. Returns the pointer to the next change log block in the list.
  479. --*/
  480. {
  481. PCHANGELOG_BLOCK_HEADER ReturnPtr;
  482. ReturnPtr = (PCHANGELOG_BLOCK_HEADER)
  483. ((LPBYTE)BlockPtr + BlockPtr->BlockSize);
  484. NlAssert( (LPBYTE)ReturnPtr <= ChangeLogDesc->BufferEnd );
  485. if( (LPBYTE)ReturnPtr >= ChangeLogDesc->BufferEnd ) {
  486. //
  487. // wrap around
  488. //
  489. ReturnPtr = ChangeLogDesc->FirstBlock;
  490. }
  491. return ReturnPtr;
  492. }
  493. PCHANGELOG_BLOCK_HEADER
  494. NlMoveToPrevChangeLogBlock(
  495. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  496. IN PCHANGELOG_BLOCK_HEADER BlockPtr
  497. )
  498. /*++
  499. Routine Description:
  500. This function accepts a pointer to a change log
  501. block and returns the pointer to the next change log block in the
  502. buffer. It however wraps around the change log cache.
  503. NOTE: This function must be called with the change log locked.
  504. Arguments:
  505. ChangeLogDesc -- Description of the Changelog buffer being used
  506. BlockPtr - pointer to a change log block.
  507. Return Value:
  508. Returns the pointer to the next change log block in the list.
  509. --*/
  510. {
  511. PCHANGELOG_BLOCK_HEADER ReturnPtr;
  512. PCHANGELOG_BLOCK_TRAILER ReturnTrailer;
  513. //
  514. // If this is the first block in the buffer,
  515. // return the last block in the buffer.
  516. //
  517. if ( BlockPtr == ChangeLogDesc->FirstBlock ) {
  518. ReturnTrailer = (PCHANGELOG_BLOCK_TRAILER)
  519. (ChangeLogDesc->BufferEnd - sizeof(CHANGELOG_BLOCK_TRAILER));
  520. //
  521. // Otherwise return the buffer immediately before this one.
  522. //
  523. } else {
  524. ReturnTrailer = (PCHANGELOG_BLOCK_TRAILER)
  525. (((LPBYTE)BlockPtr) - sizeof(CHANGELOG_BLOCK_TRAILER));
  526. }
  527. ReturnPtr = (PCHANGELOG_BLOCK_HEADER)
  528. ((LPBYTE)ReturnTrailer -
  529. ReturnTrailer->BlockSize +
  530. sizeof(CHANGELOG_BLOCK_TRAILER) );
  531. NlAssert( ReturnPtr >= ChangeLogDesc->FirstBlock );
  532. NlAssert( (LPBYTE)ReturnPtr < ChangeLogDesc->BufferEnd );
  533. return ReturnPtr;
  534. }
  535. NTSTATUS
  536. NlAllocChangeLogBlock(
  537. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  538. IN DWORD BlockSize,
  539. OUT PCHANGELOG_BLOCK_HEADER *AllocatedBlock
  540. )
  541. /*++
  542. Routine Description:
  543. This function will allocate a change log block from the free block
  544. at the tail of the change log circular list. If the available free
  545. block size is less than the required size than it will enlarge the
  546. free block by the freeing up change logs from the header. Once the
  547. free block is larger then it will cut the block to the required size
  548. and adjust the free block pointer.
  549. NOTE: This function must be called with the change log locked.
  550. Arguments:
  551. ChangeLogDesc -- Description of the Changelog buffer being used
  552. BlockSize - size of the change log block required.
  553. AllocatedBlock - Returns the pointer to the block that is allocated.
  554. Return Value:
  555. Status of the operation
  556. --*/
  557. {
  558. PCHANGELOG_BLOCK_HEADER FreeBlock;
  559. PCHANGELOG_BLOCK_HEADER NewBlock;
  560. DWORD ReqBlockSize;
  561. DWORD AllocatedBlockSize;
  562. //
  563. // pump up the size to include block header, block trailer,
  564. // and to align to DWORD.
  565. //
  566. // Add in the size of the new free block immediately following the new
  567. // block.
  568. //
  569. AllocatedBlockSize =
  570. ROUND_UP_COUNT( sizeof(CHANGELOG_BLOCK_HEADER), ALIGN_WORST) +
  571. ROUND_UP_COUNT( BlockSize+sizeof(CHANGELOG_BLOCK_TRAILER), ALIGN_WORST);
  572. ReqBlockSize = AllocatedBlockSize +
  573. ROUND_UP_COUNT( sizeof(CHANGELOG_BLOCK_HEADER), ALIGN_WORST) +
  574. ROUND_UP_COUNT( sizeof(CHANGELOG_BLOCK_TRAILER), ALIGN_WORST );
  575. if ( ReqBlockSize >= ChangeLogDesc->BufferSize - 16 ) {
  576. return STATUS_ALLOTTED_SPACE_EXCEEDED;
  577. }
  578. //
  579. // If the current free block isn't big enough,
  580. // make it big enough.
  581. //
  582. FreeBlock = ChangeLogDesc->Tail;
  583. NlAssert( FreeBlock->BlockState == BlockFree );
  584. while ( FreeBlock->BlockSize <= ReqBlockSize ) {
  585. //
  586. // If this is a change log,
  587. // make the free block bigger by wrapping around.
  588. //
  589. {
  590. PCHANGELOG_BLOCK_HEADER NextFreeBlock;
  591. NextFreeBlock = NlMoveToNextChangeLogBlock( ChangeLogDesc, FreeBlock );
  592. //
  593. // If this free block is the end block in the cache,
  594. // so make this as a 'hole' block and wrap around for
  595. // next free block.
  596. //
  597. if( (LPBYTE)NextFreeBlock !=
  598. (LPBYTE)FreeBlock + FreeBlock->BlockSize ) {
  599. NlAssert( ((LPBYTE)FreeBlock + FreeBlock->BlockSize) ==
  600. ChangeLogDesc->BufferEnd );
  601. NlAssert( NextFreeBlock == ChangeLogDesc->FirstBlock );
  602. FreeBlock->BlockState = BlockHole;
  603. //
  604. // Write the 'hole' block status in the file.
  605. // (Write the entire block since the block size in the trailer
  606. // may have changed on previous iterations of this loop.)
  607. //
  608. (VOID) NlWriteChangeLogBytes( ChangeLogDesc,
  609. (LPBYTE) FreeBlock,
  610. FreeBlock->BlockSize,
  611. TRUE ); // Flush the bytes to disk
  612. //
  613. // The free block is now at the front of the cache.
  614. //
  615. FreeBlock = ChangeLogDesc->FirstBlock;
  616. FreeBlock->BlockState = BlockFree;
  617. //
  618. // Otherwise, enlarge the current free block by merging the next
  619. // block into it. The next free block is either a used block or
  620. // the 'hole' block.
  621. //
  622. } else {
  623. //
  624. // If we've just deleted a used block,
  625. // adjust the entry count.
  626. //
  627. // VOID_DB entries are "deleted" entries and have already adjusted
  628. // the entry count.
  629. //
  630. if ( NextFreeBlock->BlockState == BlockUsed ) {
  631. DWORD DBIndex = ((PCHANGELOG_ENTRY)(NextFreeBlock+1))->DBIndex;
  632. if ( DBIndex != VOID_DB ) {
  633. ChangeLogDesc->EntryCount[DBIndex] --;
  634. }
  635. }
  636. FreeBlock->BlockSize += NextFreeBlock->BlockSize;
  637. ChangeLogBlockTrailer(FreeBlock)->BlockSize = FreeBlock->BlockSize;
  638. }
  639. //
  640. // If we've consumed the head of the cache,
  641. // move the head of the cache to the next block.
  642. //
  643. if ( NextFreeBlock == ChangeLogDesc->Head ) {
  644. ChangeLogDesc->Head = NlMoveToNextChangeLogBlock( ChangeLogDesc,
  645. NextFreeBlock );
  646. //
  647. // if we have moved the global header to hole block,
  648. // skip and merge it to free block
  649. //
  650. NextFreeBlock = ChangeLogDesc->Head;
  651. if (NextFreeBlock->BlockState == BlockHole ) {
  652. FreeBlock->BlockSize += NextFreeBlock->BlockSize;
  653. ChangeLogBlockTrailer(FreeBlock)->BlockSize = FreeBlock->BlockSize;
  654. ChangeLogDesc->Head =
  655. NlMoveToNextChangeLogBlock( ChangeLogDesc, NextFreeBlock );
  656. }
  657. }
  658. }
  659. // NlAssert(ChangeLogDesc->Head->BlockState == BlockUsed );
  660. //
  661. // This assertion is overactive in case the whole buffer becomes free
  662. // as it does in the following scenario. Suppose after allocating the
  663. // whole buffer, we allocate a block that is larger than the half of the
  664. // buffer. Then the head (marked used) points to the beginning of the
  665. // buffer and the tail points to the free part at the end of the buffer.
  666. // Then we allocate another block that is of the same size as the
  667. // previously allocated block. In this case the free block at the end of
  668. // the buffer will be marked 'hole', the head will be consumed, the head
  669. // will be moved to the next (hole) block, the head will be moved further
  670. // (since it points to a hole block) and marked free. So we end up with the
  671. // buffer that is completely free; the head points to the beginning of the
  672. // buffer and marked free, not used.
  673. }
  674. NlAssert( (FreeBlock >= ChangeLogDesc->FirstBlock) &&
  675. (FreeBlock->BlockSize <= ChangeLogDesc->BufferSize) &&
  676. ( ((LPBYTE)FreeBlock + FreeBlock->BlockSize) <=
  677. ChangeLogDesc->BufferEnd) );
  678. //
  679. // Cut the free block ...
  680. //
  681. NewBlock = FreeBlock;
  682. FreeBlock = (PCHANGELOG_BLOCK_HEADER)
  683. ((LPBYTE)FreeBlock + AllocatedBlockSize);
  684. FreeBlock->BlockState = BlockFree;
  685. FreeBlock->BlockSize = NewBlock->BlockSize - AllocatedBlockSize;
  686. ChangeLogBlockTrailer(FreeBlock)->BlockSize = FreeBlock->BlockSize;
  687. ChangeLogDesc->Tail = FreeBlock;
  688. RtlZeroMemory( NewBlock, AllocatedBlockSize );
  689. NewBlock->BlockState = BlockUsed;
  690. NewBlock->BlockSize = AllocatedBlockSize;
  691. ChangeLogBlockTrailer(NewBlock)->BlockSize = NewBlock->BlockSize;
  692. NlAssert( (NewBlock >= ChangeLogDesc->FirstBlock) &&
  693. ( ((LPBYTE)NewBlock + BlockSize) <= ChangeLogDesc->BufferEnd) );
  694. *AllocatedBlock = NewBlock;
  695. return STATUS_SUCCESS;
  696. }
  697. PCHANGELOG_ENTRY
  698. NlMoveToNextChangeLogEntry(
  699. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  700. IN PCHANGELOG_ENTRY ChangeLogEntry
  701. )
  702. /*++
  703. Routine Description:
  704. This function is a worker routine to scan the change log list. This
  705. accepts a pointer to a change log structure and returns a pointer to
  706. the next change log structure. It returns NULL pointer if the given
  707. struct is the last change log structure in the list.
  708. NOTE: This function must be called with the change log locked.
  709. Arguments:
  710. ChangeLogDesc -- Description of the Changelog buffer being used
  711. ChangeLogEntry - pointer to a change log strcuture.
  712. Return Value:
  713. Returns the pointer to the next change log structure in the list.
  714. --*/
  715. {
  716. PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
  717. ChangeLogBlock = (PCHANGELOG_BLOCK_HEADER)
  718. ( (LPBYTE) ChangeLogEntry - sizeof(CHANGELOG_BLOCK_HEADER) );
  719. NlAssert( ChangeLogBlock->BlockState == BlockUsed );
  720. ChangeLogBlock = NlMoveToNextChangeLogBlock( ChangeLogDesc, ChangeLogBlock );
  721. //
  722. // If we're at the end of the list,
  723. // return null
  724. //
  725. if ( ChangeLogBlock->BlockState == BlockFree ) {
  726. return NULL;
  727. //
  728. // Skip this block, there will be only one 'Hole' block in the
  729. // list.
  730. //
  731. } else if ( ChangeLogBlock->BlockState == BlockHole ) {
  732. ChangeLogBlock = NlMoveToNextChangeLogBlock( ChangeLogDesc, ChangeLogBlock );
  733. if ( ChangeLogBlock->BlockState == BlockFree ) {
  734. return NULL;
  735. }
  736. }
  737. NlAssert( ChangeLogBlock->BlockState == BlockUsed );
  738. return (PCHANGELOG_ENTRY)
  739. ( (LPBYTE)ChangeLogBlock + sizeof(CHANGELOG_BLOCK_HEADER) );
  740. }
  741. PCHANGELOG_ENTRY
  742. NlMoveToPrevChangeLogEntry(
  743. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  744. IN PCHANGELOG_ENTRY ChangeLogEntry
  745. )
  746. /*++
  747. Routine Description:
  748. This function is a worker routine to scan the change log list. This
  749. accepts a pointer to a change log structure and returns a pointer to
  750. the previous change log structure. It returns NULL pointer if the given
  751. struct is the first change log structure in the list.
  752. NOTE: This function must be called with the change log locked.
  753. Arguments:
  754. ChangeLogDesc -- Description of the Changelog buffer being used
  755. ChangeLogEntry - pointer to a change log strcuture.
  756. Return Value:
  757. Returns the pointer to the next change log structure in the list.
  758. --*/
  759. {
  760. PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
  761. ChangeLogBlock = (PCHANGELOG_BLOCK_HEADER)
  762. ( (LPBYTE) ChangeLogEntry - sizeof(CHANGELOG_BLOCK_HEADER) );
  763. NlAssert( ChangeLogBlock->BlockState == BlockUsed ||
  764. ChangeLogBlock->BlockState == BlockFree );
  765. ChangeLogBlock = NlMoveToPrevChangeLogBlock( ChangeLogDesc, ChangeLogBlock );
  766. //
  767. // If we're at the end of the list,
  768. // return null
  769. //
  770. if ( ChangeLogBlock->BlockState == BlockFree ) {
  771. return NULL;
  772. //
  773. // Skip this block, there will be only one 'Hole' block in the
  774. // list.
  775. //
  776. } else if ( ChangeLogBlock->BlockState == BlockHole ) {
  777. ChangeLogBlock = NlMoveToPrevChangeLogBlock( ChangeLogDesc, ChangeLogBlock );
  778. if ( ChangeLogBlock->BlockState == BlockFree ) {
  779. return NULL;
  780. }
  781. }
  782. NlAssert( ChangeLogBlock->BlockState == BlockUsed );
  783. return (PCHANGELOG_ENTRY)
  784. ( (LPBYTE)ChangeLogBlock + sizeof(CHANGELOG_BLOCK_HEADER) );
  785. }
  786. PCHANGELOG_ENTRY
  787. NlFindFirstChangeLogEntry(
  788. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  789. IN DWORD DBIndex
  790. )
  791. /*++
  792. Routine Description:
  793. Returns a pointer to the first change log entry for the specified
  794. database.
  795. NOTE: This function must be called with the change log locked.
  796. Arguments:
  797. ChangeLogDesc -- Description of the Changelog buffer being used
  798. DBIndex - Describes which database to find the changelog entry for.
  799. Return Value:
  800. Non-NULL - change log entry found
  801. NULL - No such entry exists.
  802. --*/
  803. {
  804. PCHANGELOG_ENTRY ChangeLogEntry = NULL;
  805. //
  806. // If nothing has ever been written to the change log,
  807. // indicate nothing is available.
  808. //
  809. if ( ChangeLogIsEmpty( ChangeLogDesc ) ) {
  810. return NULL;
  811. }
  812. for ( ChangeLogEntry = (PCHANGELOG_ENTRY) (ChangeLogDesc->Head + 1);
  813. ChangeLogEntry != NULL ;
  814. ChangeLogEntry = NlMoveToNextChangeLogEntry( ChangeLogDesc, ChangeLogEntry) ) {
  815. if( ChangeLogEntry->DBIndex == (UCHAR) DBIndex ) {
  816. break;
  817. }
  818. }
  819. return ChangeLogEntry;
  820. }
  821. PCHANGELOG_ENTRY
  822. NlFindChangeLogEntry(
  823. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  824. IN LARGE_INTEGER SerialNumber,
  825. IN BOOL DownLevel,
  826. IN BOOL NeedExactMatch,
  827. IN DWORD DBIndex
  828. )
  829. /*++
  830. Routine Description:
  831. Search the change log entry in change log cache for a given serial
  832. number
  833. NOTE: This function must be called with the change log locked.
  834. Arguments:
  835. ChangeLogDesc -- Description of the Changelog buffer being used
  836. SerialNumber - Serial number of the entry to find.
  837. DownLevel - True if only the least significant portion of the serial
  838. number needs to match.
  839. NeedExactMatch - True if the caller wants us to exactly match the
  840. specified serial number.
  841. DBIndex - Describes which database to find the changelog entry for.
  842. Return Value:
  843. Non-NULL - change log entry found
  844. NULL - No such entry exists.
  845. --*/
  846. {
  847. PCHANGELOG_ENTRY ChangeLogEntry;
  848. PCHANGELOG_ENTRY PriorChangeLogEntry = NULL;
  849. //
  850. // If nothing has ever been written to the change log,
  851. // indicate nothing is available.
  852. //
  853. if ( ChangeLogIsEmpty( ChangeLogDesc ) ) {
  854. return NULL;
  855. }
  856. //
  857. // Search from the tail of the changelog. For huge changelogs, this should
  858. // reduce the working set size since we almost always search for one of
  859. // the last few entries.
  860. //
  861. ChangeLogEntry = (PCHANGELOG_ENTRY) (ChangeLogDesc->Tail + 1);
  862. while ( ( ChangeLogEntry =
  863. NlMoveToPrevChangeLogEntry( ChangeLogDesc, ChangeLogEntry) ) != NULL ) {
  864. if( ChangeLogEntry->DBIndex == (UCHAR) DBIndex ) {
  865. if ( DownLevel ) {
  866. if ( ChangeLogEntry->SerialNumber.LowPart ==
  867. SerialNumber.LowPart ) {
  868. return ChangeLogEntry;
  869. }
  870. } else {
  871. if ( IsSerialNumberEqual( ChangeLogDesc, ChangeLogEntry, &SerialNumber) ){
  872. if ( NeedExactMatch &&
  873. ChangeLogEntry->SerialNumber.QuadPart != SerialNumber.QuadPart ) {
  874. return NULL;
  875. }
  876. return ChangeLogEntry;
  877. }
  878. }
  879. PriorChangeLogEntry = ChangeLogEntry;
  880. }
  881. }
  882. return NULL;
  883. }
  884. PCHANGELOG_ENTRY
  885. NlDuplicateChangeLogEntry(
  886. IN PCHANGELOG_ENTRY ChangeLogEntry,
  887. OUT LPDWORD ChangeLogEntrySize OPTIONAL
  888. )
  889. /*++
  890. Routine Description:
  891. Duplicate the specified changelog entry into an allocated buffer.
  892. NOTE: This function must be called with the change log locked.
  893. Arguments:
  894. ChangeLogEntry -- points to the changelog entry to duplicate
  895. ChangeLogEntrySize - Optionally returns the size (in bytes) of the
  896. returned change log entry.
  897. Return Value:
  898. NULL - Not enough memory to duplicate the change log entry
  899. Non-NULL - returns a pointer to the duplicate change log entry. This buffer
  900. must be freed via NetpMemoryFree.
  901. --*/
  902. {
  903. PCHANGELOG_ENTRY TempChangeLogEntry = NULL;
  904. ULONG Size;
  905. PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
  906. ChangeLogBlock = (PCHANGELOG_BLOCK_HEADER)
  907. ( (LPBYTE) ChangeLogEntry - sizeof(CHANGELOG_BLOCK_HEADER) );
  908. Size = ChangeLogBlock->BlockSize -
  909. sizeof(CHANGELOG_BLOCK_HEADER) -
  910. sizeof(CHANGELOG_BLOCK_TRAILER);
  911. TempChangeLogEntry = (PCHANGELOG_ENTRY) NetpMemoryAllocate( Size );
  912. if( TempChangeLogEntry == NULL ) {
  913. return NULL;
  914. }
  915. RtlCopyMemory( TempChangeLogEntry, ChangeLogEntry, Size );
  916. if ( ChangeLogEntrySize != NULL ) {
  917. *ChangeLogEntrySize = Size;
  918. }
  919. return TempChangeLogEntry;
  920. }
  921. PCHANGELOG_ENTRY
  922. NlFindPromotionChangeLogEntry(
  923. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  924. IN LARGE_INTEGER SerialNumber,
  925. IN DWORD DBIndex
  926. )
  927. /*++
  928. Routine Description:
  929. Find the last change log entry with the same promotion count
  930. as SerialNumber.
  931. NOTE: This function must be called with the change log locked.
  932. Arguments:
  933. ChangeLogDesc -- Description of the Changelog buffer being used
  934. SerialNumber - Serial number containing the promotion count to query.
  935. DBIndex - Describes which database to find the changelog entry for.
  936. Return Value:
  937. Non-NULL - returns a pointer to the duplicate change log entry. This buffer
  938. must be freed via NetpMemoryFree.
  939. NULL - No such entry exists.
  940. --*/
  941. {
  942. PCHANGELOG_ENTRY ChangeLogEntry;
  943. LONG GoalPromotionCount;
  944. LONG PromotionCount;
  945. //
  946. // If nothing has ever been written to the change log,
  947. // indicate nothing is available.
  948. //
  949. if ( ChangeLogIsEmpty( ChangeLogDesc ) ) {
  950. return NULL;
  951. }
  952. //
  953. // Search from the tail of the changelog. For huge changelogs, this should
  954. // reduce the working set size since we almost always search for one of
  955. // the last few entries.
  956. //
  957. ChangeLogEntry = (PCHANGELOG_ENTRY) (ChangeLogDesc->Tail + 1);
  958. GoalPromotionCount = SerialNumber.HighPart & NlGlobalChangeLogPromotionMask;
  959. while ( ( ChangeLogEntry =
  960. NlMoveToPrevChangeLogEntry( ChangeLogDesc, ChangeLogEntry) ) != NULL ) {
  961. if( ChangeLogEntry->DBIndex == (UCHAR) DBIndex ) {
  962. PromotionCount = ChangeLogEntry->SerialNumber.HighPart & NlGlobalChangeLogPromotionMask;
  963. //
  964. // If the Current Change Log entry has a greater promotion count,
  965. // continue searching backward.
  966. //
  967. if ( PromotionCount > GoalPromotionCount ) {
  968. continue;
  969. }
  970. //
  971. // If the current change log entry has a smaller promotion count,
  972. // indicate we couldn't find a change log entry.
  973. //
  974. if ( PromotionCount < GoalPromotionCount ) {
  975. break;
  976. }
  977. //
  978. // Otherwise, success
  979. //
  980. return NlDuplicateChangeLogEntry( ChangeLogEntry, NULL );
  981. }
  982. }
  983. return NULL;
  984. }
  985. PCHANGELOG_ENTRY
  986. NlGetNextDownlevelChangeLogEntry(
  987. ULONG DownlevelSerialNumber
  988. )
  989. /*++
  990. Routine Description:
  991. Find the change log entry for the delta with a serial number greater
  992. than the one specified.
  993. NOTE: This function must be called with the change log locked.
  994. Arguments:
  995. DownlevelSerialNumber - The downlevel serial number
  996. Return Value:
  997. Non-NULL - change log entry found. This changelog entry must be
  998. deallocated using NetpMemoryFree.
  999. NULL - No such entry exists.
  1000. --*/
  1001. {
  1002. PCHANGELOG_ENTRY ChangeLogEntry;
  1003. LARGE_INTEGER SerialNumber;
  1004. SerialNumber.QuadPart = DownlevelSerialNumber + 1;
  1005. ChangeLogEntry = NlFindChangeLogEntry( &NlGlobalChangeLogDesc, SerialNumber, TRUE, TRUE, SAM_DB);
  1006. if ( ChangeLogEntry == NULL ||
  1007. ChangeLogEntry->DeltaType == DummyChangeLogEntry ) {
  1008. return NULL;
  1009. }
  1010. return NlDuplicateChangeLogEntry( ChangeLogEntry, NULL );
  1011. }
  1012. PCHANGELOG_ENTRY
  1013. NlFindNextChangeLogEntry(
  1014. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1015. IN PCHANGELOG_ENTRY LastChangeLogEntry,
  1016. IN DWORD DBIndex
  1017. )
  1018. /*++
  1019. Routine Description:
  1020. Find the next change log entry in change log following a particular
  1021. changelog entry.
  1022. NOTE: This function must be called with the change log locked.
  1023. Arguments:
  1024. ChangeLogDesc -- Description of the Changelog buffer being used
  1025. LastChangeLogEntry - last found changelog entry.
  1026. DBIndex - database index of the next entry to find
  1027. Return Value:
  1028. Non-null - change log entry found
  1029. NULL - No such entry exists.
  1030. --*/
  1031. {
  1032. PCHANGELOG_ENTRY NextChangeLogEntry = LastChangeLogEntry;
  1033. LARGE_INTEGER SerialNumber;
  1034. //
  1035. // Loop through the log finding this entry starting from the last
  1036. // found record.
  1037. //
  1038. SerialNumber.QuadPart = LastChangeLogEntry->SerialNumber.QuadPart + 1;
  1039. while ( ( NextChangeLogEntry =
  1040. NlMoveToNextChangeLogEntry( ChangeLogDesc, NextChangeLogEntry) ) != NULL ) {
  1041. if( NextChangeLogEntry->DBIndex == DBIndex ) {
  1042. //
  1043. // next log entry in the change log for
  1044. // this database. The serial number should match.
  1045. //
  1046. if ( !IsSerialNumberEqual( ChangeLogDesc, NextChangeLogEntry, &SerialNumber) ) {
  1047. NlPrint((NL_CRITICAL,
  1048. "NlFindNextChangeLogEntry: Serial numbers not contigous %lx %lx and %lx %lx\n",
  1049. NextChangeLogEntry->SerialNumber.HighPart,
  1050. NextChangeLogEntry->SerialNumber.LowPart,
  1051. SerialNumber.HighPart,
  1052. SerialNumber.LowPart ));
  1053. //
  1054. // write event log
  1055. //
  1056. NlWriteChangeLogCorruptEvent( STATUS_INTERNAL_DB_CORRUPTION,
  1057. DBIndex );
  1058. return NULL;
  1059. }
  1060. return NextChangeLogEntry;
  1061. }
  1062. }
  1063. return NULL;
  1064. }
  1065. BOOLEAN
  1066. NlCompareChangeLogEntries(
  1067. IN PCHANGELOG_ENTRY ChangeLogEntry1,
  1068. IN PCHANGELOG_ENTRY ChangeLogEntry2
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. The two change log entries are compared to see if the are for the same
  1073. object. If
  1074. Arguments:
  1075. ChangeLogEntry1 - First change log entry to compare.
  1076. ChangeLogEntry2 - Second change log entry to compare.
  1077. Return Value:
  1078. TRUE - iff the change log entries are for the same object.
  1079. --*/
  1080. {
  1081. //
  1082. // Ensure the DbIndex is the same for both entries.
  1083. //
  1084. if ( ChangeLogEntry1->DBIndex != ChangeLogEntry2->DBIndex ) {
  1085. return FALSE;
  1086. }
  1087. //
  1088. // Ensure the entries both describe the same object type.
  1089. //
  1090. if ( ChangeLogEntry1->DeltaType >= MAX_DELETE_DELTA ) {
  1091. NlPrint(( NL_CRITICAL,
  1092. "NlCompateChangeLogEntries: invalid delta type %lx\n",
  1093. ChangeLogEntry1->DeltaType ));
  1094. return FALSE;
  1095. }
  1096. if ( ChangeLogEntry2->DeltaType >= MAX_DELETE_DELTA ) {
  1097. NlPrint(( NL_CRITICAL,
  1098. "NlCompateChangeLogEntries: invalid delta type %lx\n",
  1099. ChangeLogEntry2->DeltaType ));
  1100. return FALSE;
  1101. }
  1102. if ( NlGlobalDeleteDeltaType[ChangeLogEntry1->DeltaType] !=
  1103. NlGlobalDeleteDeltaType[ChangeLogEntry2->DeltaType] ) {
  1104. return FALSE;
  1105. }
  1106. //
  1107. // Depending on the delta type, ensure the entries refer to the same object.
  1108. //
  1109. switch(ChangeLogEntry1->DeltaType) {
  1110. case AddOrChangeGroup:
  1111. case DeleteGroup:
  1112. case RenameGroup:
  1113. case AddOrChangeUser:
  1114. case DeleteUser:
  1115. case RenameUser:
  1116. case ChangeGroupMembership:
  1117. case AddOrChangeAlias:
  1118. case DeleteAlias:
  1119. case RenameAlias:
  1120. case ChangeAliasMembership:
  1121. if (ChangeLogEntry1->ObjectRid == ChangeLogEntry2->ObjectRid ) {
  1122. return TRUE;
  1123. }
  1124. break;
  1125. case AddOrChangeLsaTDomain:
  1126. case DeleteLsaTDomain:
  1127. case AddOrChangeLsaAccount:
  1128. case DeleteLsaAccount:
  1129. NlAssert( ChangeLogEntry1->Flags & CHANGELOG_SID_SPECIFIED );
  1130. NlAssert( ChangeLogEntry2->Flags & CHANGELOG_SID_SPECIFIED );
  1131. if( (ChangeLogEntry1->Flags & CHANGELOG_SID_SPECIFIED) == 0 ||
  1132. (ChangeLogEntry2->Flags & CHANGELOG_SID_SPECIFIED) == 0) {
  1133. break;
  1134. }
  1135. if( RtlEqualSid(
  1136. (PSID)((LPBYTE)ChangeLogEntry1 + sizeof(CHANGELOG_ENTRY)),
  1137. (PSID)((LPBYTE)ChangeLogEntry2 + sizeof(CHANGELOG_ENTRY))) ) {
  1138. return TRUE;
  1139. }
  1140. break;
  1141. case AddOrChangeLsaSecret:
  1142. case DeleteLsaSecret:
  1143. NlAssert( ChangeLogEntry1->Flags & CHANGELOG_NAME_SPECIFIED );
  1144. NlAssert( ChangeLogEntry2->Flags & CHANGELOG_NAME_SPECIFIED );
  1145. if( (ChangeLogEntry1->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ||
  1146. (ChangeLogEntry2->Flags & CHANGELOG_NAME_SPECIFIED) == 0 ) {
  1147. break;
  1148. }
  1149. if( _wcsicmp(
  1150. (LPWSTR)((LPBYTE)ChangeLogEntry1 + sizeof(CHANGELOG_ENTRY)),
  1151. (LPWSTR)((LPBYTE)ChangeLogEntry2 + sizeof(CHANGELOG_ENTRY))
  1152. ) == 0 ) {
  1153. return TRUE;
  1154. }
  1155. break;
  1156. case AddOrChangeLsaPolicy:
  1157. case AddOrChangeDomain:
  1158. return TRUE;
  1159. default:
  1160. NlPrint((NL_CRITICAL,
  1161. "NlCompareChangeLogEntries: invalid delta type %lx\n",
  1162. ChangeLogEntry1->DeltaType ));
  1163. break;
  1164. }
  1165. return FALSE;
  1166. }
  1167. PCHANGELOG_ENTRY
  1168. NlGetNextChangeLogEntry(
  1169. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1170. IN LARGE_INTEGER SerialNumber,
  1171. IN DWORD DBIndex,
  1172. OUT LPDWORD ChangeLogEntrySize OPTIONAL
  1173. )
  1174. /*++
  1175. Routine Description:
  1176. Search the change log entry in change log cache for a given serial
  1177. number.
  1178. Arguments:
  1179. ChangeLogDesc -- Description of the Changelog buffer to use.
  1180. SerialNumber - Serial number preceeding that of the entry to find.
  1181. DBIndex - Describes which database to find the changelog entry for.
  1182. ChangeLogEntrySize - Optionally returns the size (in bytes) of the
  1183. returned change log entry.
  1184. Return Value:
  1185. Non-NULL - returns a pointer to a duplicate of the found change log entry.
  1186. This buffer must be freed via NetpMemoryFree.
  1187. NULL - No such entry exists.
  1188. --*/
  1189. {
  1190. PCHANGELOG_ENTRY ChangeLogEntry;
  1191. //
  1192. // Increment the serial number, get the change log entry, duplicate it
  1193. //
  1194. LOCK_CHANGELOG();
  1195. SerialNumber.QuadPart += 1;
  1196. ChangeLogEntry = NlFindChangeLogEntry(
  1197. ChangeLogDesc,
  1198. SerialNumber,
  1199. FALSE,
  1200. FALSE,
  1201. DBIndex );
  1202. if ( ChangeLogEntry != NULL ) {
  1203. ChangeLogEntry = NlDuplicateChangeLogEntry(ChangeLogEntry, ChangeLogEntrySize );
  1204. }
  1205. UNLOCK_CHANGELOG();
  1206. return ChangeLogEntry;
  1207. }
  1208. PCHANGELOG_ENTRY
  1209. NlGetNextUniqueChangeLogEntry(
  1210. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1211. IN LARGE_INTEGER SerialNumber,
  1212. IN DWORD DBIndex,
  1213. OUT LPDWORD ChangeLogEntrySize OPTIONAL
  1214. )
  1215. /*++
  1216. Routine Description:
  1217. Search the change log entry in change log cache for a given serial
  1218. number. If there are more than one change log entry for the same
  1219. object then this routine will return the last log entry of that
  1220. object.
  1221. NOTE: This function must be called with the change log locked.
  1222. Arguments:
  1223. ChangeLogDesc -- Description of the Changelog buffer to use.
  1224. SerialNumber - Serial number preceeding that of the entry to find.
  1225. DBIndex - Describes which database to find the changelog entry for.
  1226. ChangeLogEntrySize - Optionally returns the size (in bytes) of the
  1227. returned change log entry.
  1228. Return Value:
  1229. Non-NULL - returns a pointer to a duplicate of the found change log entry.
  1230. This buffer must be freed via NetpMemoryFree.
  1231. NULL - No such entry exists.
  1232. --*/
  1233. {
  1234. PCHANGELOG_ENTRY ChangeLogEntry;
  1235. PCHANGELOG_ENTRY NextChangeLogEntry;
  1236. PCHANGELOG_ENTRY FoundChangeLogEntry;
  1237. //
  1238. // Get the first entry we want to deal with.
  1239. //
  1240. SerialNumber.QuadPart += 1;
  1241. ChangeLogEntry = NlFindChangeLogEntry(
  1242. ChangeLogDesc,
  1243. SerialNumber,
  1244. FALSE,
  1245. FALSE,
  1246. DBIndex );
  1247. if ( ChangeLogEntry == NULL ) {
  1248. return NULL;
  1249. }
  1250. //
  1251. // Skip over any leading dummy change log entries
  1252. //
  1253. while ( ChangeLogEntry->DeltaType == DummyChangeLogEntry ) {
  1254. //
  1255. // Get the next change log entry to compare with.
  1256. //
  1257. NextChangeLogEntry = NlFindNextChangeLogEntry( ChangeLogDesc,
  1258. ChangeLogEntry,
  1259. DBIndex );
  1260. if( NextChangeLogEntry == NULL ) {
  1261. return NULL;
  1262. }
  1263. //
  1264. // skip 'ChangeLogEntry' entry
  1265. //
  1266. ChangeLogEntry = NextChangeLogEntry;
  1267. }
  1268. //
  1269. // Check to see if the next entry is a "duplicate" of this entry.
  1270. //
  1271. FoundChangeLogEntry = ChangeLogEntry;
  1272. for (;;) {
  1273. //
  1274. // Don't walk past a change log entry for a promotion.
  1275. // Promotions don't happen very often, but passing the BDC the
  1276. // change log entry will allow it to do a better job of building
  1277. // its own change log.
  1278. //
  1279. if ( FoundChangeLogEntry->Flags & CHANGELOG_PDC_PROMOTION ) {
  1280. break;
  1281. }
  1282. //
  1283. // Get the next change log entry to compare with.
  1284. //
  1285. NextChangeLogEntry = NlFindNextChangeLogEntry( ChangeLogDesc,
  1286. ChangeLogEntry,
  1287. DBIndex );
  1288. if( NextChangeLogEntry == NULL ) {
  1289. break;
  1290. }
  1291. //
  1292. // Just skip any dummy entries.
  1293. //
  1294. if ( NextChangeLogEntry->DeltaType == DummyChangeLogEntry ) {
  1295. ChangeLogEntry = NextChangeLogEntry;
  1296. continue;
  1297. }
  1298. //
  1299. // if 'FoundChangeLogEntry' and 'NextChangeLogEntry' entries are
  1300. // for different objects or are different delta types.
  1301. // then return 'FoundChangeLogEntry' to the caller.
  1302. //
  1303. if ( FoundChangeLogEntry->DeltaType != NextChangeLogEntry->DeltaType ||
  1304. !NlCompareChangeLogEntries( FoundChangeLogEntry, NextChangeLogEntry ) ){
  1305. break;
  1306. }
  1307. //
  1308. // Skip 'FoundChangeLogEntry' entry
  1309. // Mark this entry as the being the best one to return.
  1310. //
  1311. ChangeLogEntry = NextChangeLogEntry;
  1312. FoundChangeLogEntry = ChangeLogEntry;
  1313. }
  1314. return NlDuplicateChangeLogEntry(FoundChangeLogEntry, ChangeLogEntrySize );
  1315. }
  1316. BOOL
  1317. NlRecoverChangeLog(
  1318. PCHANGELOG_ENTRY OrigChangeLogEntry
  1319. )
  1320. /*++
  1321. Routine Description:
  1322. This routine traverses the change log list from current change log entry
  1323. determines whether the current change log can be ignored under
  1324. special conditions.
  1325. Arguments:
  1326. OrigChangeLogEntry - pointer to log structure that is under investigation.
  1327. Return Value:
  1328. TRUE - if the given change log can be ignored.
  1329. FALSE - otherwise.
  1330. --*/
  1331. {
  1332. PCHANGELOG_ENTRY NextChangeLogEntry;
  1333. BOOLEAN ReturnValue;
  1334. //
  1335. // Find the original change log entry.
  1336. //
  1337. LOCK_CHANGELOG();
  1338. NextChangeLogEntry = NlFindChangeLogEntry(
  1339. &NlGlobalChangeLogDesc,
  1340. OrigChangeLogEntry->SerialNumber,
  1341. FALSE, // Not downlevel
  1342. FALSE, // Not exact match
  1343. OrigChangeLogEntry->DBIndex );
  1344. if (NextChangeLogEntry == NULL) {
  1345. ReturnValue = FALSE;
  1346. goto Cleanup;
  1347. }
  1348. if ( OrigChangeLogEntry->DeltaType >= MAX_DELETE_DELTA ) {
  1349. NlPrint(( NL_CRITICAL,
  1350. "NlRecoverChangeLog: invalid delta type %lx\n",
  1351. OrigChangeLogEntry->DeltaType ));
  1352. ReturnValue = FALSE;
  1353. goto Cleanup;
  1354. }
  1355. //
  1356. // Loop for each entry with a greater serial number.
  1357. //
  1358. for (;;) {
  1359. NextChangeLogEntry = NlFindNextChangeLogEntry(
  1360. &NlGlobalChangeLogDesc,
  1361. NextChangeLogEntry,
  1362. OrigChangeLogEntry->DBIndex );
  1363. if (NextChangeLogEntry == NULL) {
  1364. break;
  1365. }
  1366. //
  1367. // If the delta we found is the type that deletes the original delta,
  1368. // and the objects described by the two deltas are the same,
  1369. // tell the caller to not worry about the original delta failing.
  1370. //
  1371. if ( NextChangeLogEntry->DeltaType ==
  1372. NlGlobalDeleteDeltaType[OrigChangeLogEntry->DeltaType] &&
  1373. NlCompareChangeLogEntries( OrigChangeLogEntry,
  1374. NextChangeLogEntry ) ) {
  1375. ReturnValue = TRUE;
  1376. goto Cleanup;
  1377. }
  1378. }
  1379. ReturnValue = FALSE;
  1380. Cleanup:
  1381. UNLOCK_CHANGELOG();
  1382. return ReturnValue;
  1383. }
  1384. VOID
  1385. NlVoidChangeLogEntry(
  1386. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1387. IN PCHANGELOG_ENTRY ChangeLogEntry,
  1388. IN BOOLEAN FlushIt
  1389. )
  1390. /*++
  1391. Routine Description:
  1392. Mark a changelog entry as void. If there are no more change log entries in the file,
  1393. the file is deleted.
  1394. NOTE: This function must be called with the change log locked.
  1395. Arguments:
  1396. ChangeLogDesc -- Description of the Changelog buffer to use.
  1397. ChangeLogEntry -- Change Log Entry to mark as void.
  1398. FlushIt - TRUE if the bytes are to be flushed to disk
  1399. Return Value:
  1400. None.
  1401. --*/
  1402. {
  1403. DWORD DBIndex = ChangeLogEntry->DBIndex;
  1404. //
  1405. // Mark the changelog entry as being deleted.
  1406. // (and force the change to disk).
  1407. //
  1408. NlPrint((NL_CHANGELOG,
  1409. "NlVoidChangeLogEntry: %lx %lx: deleting change log entry.\n",
  1410. ChangeLogEntry->SerialNumber.HighPart,
  1411. ChangeLogEntry->SerialNumber.LowPart ));
  1412. ChangeLogDesc->EntryCount[DBIndex] --;
  1413. ChangeLogEntry->DBIndex = VOID_DB;
  1414. (VOID) NlWriteChangeLogBytes(
  1415. ChangeLogDesc,
  1416. &ChangeLogEntry->DBIndex,
  1417. sizeof(ChangeLogEntry->DBIndex),
  1418. FlushIt );
  1419. return;
  1420. }
  1421. VOID
  1422. NlDeleteChangeLogEntry(
  1423. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1424. IN DWORD DBIndex,
  1425. IN LARGE_INTEGER SerialNumber
  1426. )
  1427. /*++
  1428. Routine Description:
  1429. This routine deletes the change log entry with the particular serial number.
  1430. Arguments:
  1431. ChangeLogDesc -- Description of the Changelog buffer to use.
  1432. DBIndex - Describes which database to find the changelog entry for.
  1433. SerialNumber - Serial number of the entry to find.
  1434. Return Value:
  1435. None.
  1436. --*/
  1437. {
  1438. PCHANGELOG_ENTRY ChangeLogEntry;
  1439. //
  1440. // Find the specified change log entry.
  1441. //
  1442. LOCK_CHANGELOG();
  1443. ChangeLogEntry = NlFindChangeLogEntry(
  1444. ChangeLogDesc,
  1445. SerialNumber,
  1446. FALSE, // Not downlevel
  1447. TRUE, // Exact match
  1448. DBIndex );
  1449. if (ChangeLogEntry != NULL) {
  1450. //
  1451. // Mark the changelog entry as being deleted.
  1452. // (and force the change to disk).
  1453. //
  1454. NlVoidChangeLogEntry( ChangeLogDesc, ChangeLogEntry, TRUE );
  1455. } else {
  1456. NlPrint((NL_CRITICAL,
  1457. "NlDeleteChangeLogEntry: %lx %lx: couldn't find change log entry.\n",
  1458. SerialNumber.HighPart,
  1459. SerialNumber.LowPart ));
  1460. }
  1461. UNLOCK_CHANGELOG();
  1462. return;
  1463. }
  1464. NTSTATUS
  1465. NlCopyChangeLogEntry(
  1466. IN BOOLEAN SourceIsVersion3,
  1467. IN PCHANGELOG_ENTRY SourceChangeLogEntry,
  1468. IN PCHANGELOG_DESCRIPTOR DestChangeLogDesc
  1469. )
  1470. /*++
  1471. Routine Description:
  1472. Copies the specified change log entry for the specified "source" change log to
  1473. the specified "destination" change log. The caller is responsible for flushing the
  1474. entry to disk by calling NlFlushChangeLog.
  1475. NOTE: This function must be called with the change log locked.
  1476. Arguments:
  1477. SourceIsVersion3 - True if the source is a version 3 change log entry
  1478. SourceChangeLogEntry -- The particular entry to copy
  1479. DestChangeLogDesc -- a description of the ChangelogBuffer to copy to
  1480. Return Value:
  1481. NT Status code
  1482. --*/
  1483. {
  1484. NTSTATUS Status;
  1485. CHANGELOG_ENTRY DestChangeLogEntry;
  1486. PSID ObjectSid;
  1487. UNICODE_STRING ObjectNameString;
  1488. PUNICODE_STRING ObjectName;
  1489. //
  1490. // If this entry has been marked void, ignore it.
  1491. //
  1492. if ( SourceChangeLogEntry->DBIndex == VOID_DB ) {
  1493. return STATUS_SUCCESS;
  1494. }
  1495. //
  1496. // Build a version 4 changelog entry from a version 3 one.
  1497. //
  1498. ObjectSid = NULL;
  1499. ObjectName = NULL;
  1500. if ( SourceIsVersion3 ) {
  1501. PCHANGELOG_ENTRY_V3 Version3;
  1502. Version3 = (PCHANGELOG_ENTRY_V3)SourceChangeLogEntry;
  1503. DestChangeLogEntry.SerialNumber = Version3->SerialNumber;
  1504. DestChangeLogEntry.DeltaType = (BYTE) Version3->DeltaType;
  1505. DestChangeLogEntry.DBIndex = Version3->DBIndex;
  1506. DestChangeLogEntry.ObjectRid = Version3->ObjectRid;
  1507. DestChangeLogEntry.Flags = 0;
  1508. if ( Version3->ObjectSidOffset ) {
  1509. ObjectSid = (PSID)(((LPBYTE)Version3) +
  1510. Version3->ObjectSidOffset);
  1511. }
  1512. if ( Version3->ObjectNameOffset ) {
  1513. RtlInitUnicodeString( &ObjectNameString,
  1514. (LPWSTR)(((LPBYTE)Version3) +
  1515. Version3->ObjectNameOffset));
  1516. ObjectName = &ObjectNameString;
  1517. }
  1518. //
  1519. // Build a version 4 changelog entry from a version 4 one.
  1520. //
  1521. } else {
  1522. RtlCopyMemory( &DestChangeLogEntry, SourceChangeLogEntry, sizeof(DestChangeLogEntry) );
  1523. if ( SourceChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
  1524. ObjectSid = (PSID)(((LPBYTE)SourceChangeLogEntry) +
  1525. sizeof(CHANGELOG_ENTRY));
  1526. } else if ( SourceChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
  1527. RtlInitUnicodeString( &ObjectNameString,
  1528. (LPWSTR)(((LPBYTE)SourceChangeLogEntry) +
  1529. sizeof(CHANGELOG_ENTRY)));
  1530. ObjectName = &ObjectNameString;
  1531. }
  1532. }
  1533. Status = NlWriteChangeLogEntry( DestChangeLogDesc,
  1534. &DestChangeLogEntry,
  1535. ObjectSid,
  1536. ObjectName,
  1537. FALSE ); // Don't flush to disk
  1538. return Status;
  1539. }
  1540. BOOLEAN
  1541. NlFixChangeLog(
  1542. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1543. IN DWORD DBIndex,
  1544. IN LARGE_INTEGER SerialNumber
  1545. )
  1546. /*++
  1547. Routine Description:
  1548. This routine scans the change log and 'removes' all change log entries
  1549. with a serial number greater than the one specified.
  1550. NOTE: This function must be called with the change log locked.
  1551. Arguments:
  1552. ChangeLogDesc -- Description of the Changelog buffer to use.
  1553. DBIndex - Describes which database to find the changelog entry for.
  1554. SerialNumber - Serial number of the entry to find.
  1555. Return Value:
  1556. TRUE -- if the entry specied by SerialNumber was found.
  1557. --*/
  1558. {
  1559. PCHANGELOG_ENTRY ChangeLogEntry;
  1560. BOOLEAN SkipFirstEntry = TRUE;
  1561. //
  1562. // In all cases,
  1563. // the new serial number of the change log is the one passed in.
  1564. //
  1565. ChangeLogDesc->SerialNumber[DBIndex] = SerialNumber;
  1566. //
  1567. // Find the specified change log entry.
  1568. //
  1569. ChangeLogEntry = NlFindChangeLogEntry(
  1570. ChangeLogDesc,
  1571. SerialNumber,
  1572. FALSE, // Not downlevel
  1573. TRUE, // exact match
  1574. DBIndex );
  1575. if (ChangeLogEntry == NULL) {
  1576. //
  1577. // If we can't find the entry,
  1578. // simply start from the beginning and delete all entries for this
  1579. // database.
  1580. //
  1581. ChangeLogEntry = NlFindFirstChangeLogEntry( ChangeLogDesc, DBIndex );
  1582. SkipFirstEntry = FALSE;
  1583. if (ChangeLogEntry == NULL) {
  1584. return FALSE;
  1585. }
  1586. }
  1587. //
  1588. // Loop for each entry with a greater serial number.
  1589. //
  1590. for (;;) {
  1591. //
  1592. // Skip past the previous entry.
  1593. //
  1594. // Don't do this the first time if we want to start at the very beginning.
  1595. //
  1596. if ( SkipFirstEntry ) {
  1597. ChangeLogEntry = NlFindNextChangeLogEntry( ChangeLogDesc,
  1598. ChangeLogEntry,
  1599. DBIndex );
  1600. } else {
  1601. SkipFirstEntry = TRUE;
  1602. }
  1603. if (ChangeLogEntry == NULL) {
  1604. break;
  1605. }
  1606. //
  1607. // Mark the changelog entry as being deleted.
  1608. // (but don't flush to disk yet).
  1609. //
  1610. NlVoidChangeLogEntry( ChangeLogDesc, ChangeLogEntry, FALSE );
  1611. //
  1612. // If deleting the change log entry caused the changelog to be deleted,
  1613. // exit now since 'ChangeLogEntry' points to freed memory.
  1614. //
  1615. if ( ChangeLogDesc->EntryCount[DBIndex] == 0 ) {
  1616. break;
  1617. }
  1618. }
  1619. //
  1620. // Flush all the changes to disk.
  1621. //
  1622. (VOID) NlFlushChangeLog( ChangeLogDesc );
  1623. return TRUE;
  1624. }
  1625. BOOL
  1626. NlValidateChangeLogEntry(
  1627. IN PCHANGELOG_ENTRY ChangeLogEntry,
  1628. IN DWORD ChangeLogEntrySize
  1629. )
  1630. /*++
  1631. Routine Description:
  1632. Validate the a ChangeLogEntry is structurally sound.
  1633. Arguments:
  1634. ChangeLogEntry: pointer to a change log entry.
  1635. ChangeLogEntrySize -- Size (in bytes) of the change log entry not including
  1636. header and trailer.
  1637. Return Value:
  1638. TRUE: if the given entry is valid
  1639. FALSE: otherwise.
  1640. --*/
  1641. {
  1642. //
  1643. // Ensure the entry is big enough.
  1644. //
  1645. if ( ChangeLogEntrySize < sizeof(CHANGELOG_ENTRY) ) {
  1646. NlPrint((NL_CRITICAL,
  1647. "NlValidateChangeLogEntry: Entry size is too small: %ld\n",
  1648. ChangeLogEntrySize ));
  1649. return FALSE;
  1650. }
  1651. //
  1652. // Ensure strings are zero terminated.
  1653. //
  1654. if ( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
  1655. LPWSTR ZeroTerminator = (LPWSTR)(ChangeLogEntry+1);
  1656. BOOLEAN ZeroTerminatorFound = FALSE;
  1657. if ( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
  1658. NlPrint((NL_CRITICAL,
  1659. "NlValidateChangeLogEntry: %lx %lx: both Name and Sid specified.\n",
  1660. ChangeLogEntry->SerialNumber.HighPart,
  1661. ChangeLogEntry->SerialNumber.LowPart ));
  1662. return FALSE;
  1663. }
  1664. while ( (DWORD)((LPBYTE)ZeroTerminator - (LPBYTE) ChangeLogEntry) <
  1665. ChangeLogEntrySize - 1 ) {
  1666. if ( *ZeroTerminator == L'\0' ) {
  1667. ZeroTerminatorFound = TRUE;
  1668. break;
  1669. }
  1670. ZeroTerminator ++;
  1671. }
  1672. if ( !ZeroTerminatorFound ) {
  1673. NlPrint((NL_CRITICAL,
  1674. "NlValidateChangeLogEntry: %lx %lx: String not zero terminated. (no string)\n",
  1675. ChangeLogEntry->SerialNumber.HighPart,
  1676. ChangeLogEntry->SerialNumber.LowPart ));
  1677. return FALSE;
  1678. }
  1679. }
  1680. //
  1681. // Ensure the sid is entirely within the block.
  1682. //
  1683. if ( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
  1684. if ( GetSidLengthRequired(0) >
  1685. ChangeLogEntrySize - sizeof(*ChangeLogEntry) ||
  1686. RtlLengthSid( (PSID)(ChangeLogEntry+1) ) >
  1687. ChangeLogEntrySize - sizeof(*ChangeLogEntry) ) {
  1688. NlPrint((NL_CRITICAL,
  1689. "NlValidateChangeLogEntry: %lx %lx: Sid too large.\n",
  1690. ChangeLogEntry->SerialNumber.HighPart,
  1691. ChangeLogEntry->SerialNumber.LowPart ));
  1692. return FALSE;
  1693. }
  1694. }
  1695. //
  1696. // Ensure the database # is valid.
  1697. // ARGH! Allow VOID_DB.
  1698. //
  1699. if ( ChangeLogEntry->DBIndex > NUM_DBS ) {
  1700. NlPrint((NL_CRITICAL,
  1701. "NlValidateChangeLogEntry: %lx %lx: DBIndex is bad %ld.\n",
  1702. ChangeLogEntry->SerialNumber.HighPart,
  1703. ChangeLogEntry->SerialNumber.LowPart,
  1704. ChangeLogEntry->DBIndex ));
  1705. return FALSE;
  1706. }
  1707. return TRUE;
  1708. }
  1709. BOOL
  1710. ValidateThisEntry(
  1711. IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1712. IN PCHANGELOG_ENTRY ChangeLogEntry,
  1713. IN OUT PLARGE_INTEGER NextSerialNumber,
  1714. IN BOOLEAN InitialCall
  1715. )
  1716. /*++
  1717. Routine Description:
  1718. Determine the given log entry is a valid next log in the change log
  1719. list.
  1720. NOTE: This function must be called with the change log locked.
  1721. Arguments:
  1722. ChangeLogDesc -- Description of the Changelog buffer to validate.
  1723. ChangeLogEntry: pointer to a new log entry.
  1724. NextSerialNumber: pointer to an array of serial numbers.
  1725. (NULL if serial numbers aren't to be validated.)
  1726. Initialcall: TRUE iff SerialNumber array should be initialized.
  1727. Return Value:
  1728. TRUE: if the given entry is a valid next entry.
  1729. FALSE: otherwise.
  1730. Assumed: non-empty ChangeLog list.
  1731. --*/
  1732. {
  1733. PCHANGELOG_BLOCK_HEADER Block = ((PCHANGELOG_BLOCK_HEADER)ChangeLogEntry) - 1;
  1734. //
  1735. // Do Version 3 specific things
  1736. //
  1737. if ( ChangeLogDesc->Version3 ) {
  1738. //
  1739. // Ensure the block is big enough.
  1740. //
  1741. if ( Block->BlockSize <
  1742. sizeof(CHANGELOG_ENTRY_V3) + sizeof(CHANGELOG_BLOCK_HEADER) ) {
  1743. NlPrint((NL_CRITICAL,
  1744. "ValidateThisEntry: Block size is too small: %ld\n",
  1745. Block->BlockSize ));
  1746. return FALSE;
  1747. }
  1748. //
  1749. // Ensure the database # is valid.
  1750. //
  1751. if ( ChangeLogEntry->DBIndex > NUM_DBS ) {
  1752. NlPrint((NL_CRITICAL,
  1753. "ValidateThisEntry: %lx %lx: DBIndex is bad %ld.\n",
  1754. ChangeLogEntry->SerialNumber.HighPart,
  1755. ChangeLogEntry->SerialNumber.LowPart,
  1756. ChangeLogEntry->DBIndex ));
  1757. return FALSE;
  1758. }
  1759. //
  1760. // Do version 4 specific validation
  1761. //
  1762. } else {
  1763. //
  1764. // Ensure the block is big enough.
  1765. //
  1766. if ( Block->BlockSize <
  1767. sizeof(CHANGELOG_BLOCK_HEADER) +
  1768. sizeof(CHANGELOG_ENTRY) +
  1769. sizeof(CHANGELOG_BLOCK_TRAILER) ) {
  1770. NlPrint((NL_CRITICAL,
  1771. "ValidateThisEntry: Block size is too small: %ld\n",
  1772. Block->BlockSize ));
  1773. return FALSE;
  1774. }
  1775. //
  1776. // Validate the contents of the block itself.
  1777. //
  1778. if ( !NlValidateChangeLogEntry(
  1779. ChangeLogEntry,
  1780. Block->BlockSize -
  1781. sizeof(CHANGELOG_BLOCK_HEADER) -
  1782. sizeof(CHANGELOG_BLOCK_TRAILER) ) ) {
  1783. return FALSE;
  1784. }
  1785. }
  1786. //
  1787. // Validate the serial number sequence.
  1788. //
  1789. if ( ChangeLogEntry->DBIndex != VOID_DB && NextSerialNumber != NULL ) {
  1790. //
  1791. // If this is the first entry in the database,
  1792. // Save its serial number.
  1793. //
  1794. if ( NextSerialNumber[ChangeLogEntry->DBIndex].QuadPart == 0 ) {
  1795. //
  1796. // first entry for this database
  1797. //
  1798. NextSerialNumber[ChangeLogEntry->DBIndex] = ChangeLogEntry->SerialNumber;
  1799. //
  1800. // Otherwise ensure the serial number is the value expected.
  1801. //
  1802. } else {
  1803. if ( !IsSerialNumberEqual(
  1804. ChangeLogDesc,
  1805. ChangeLogEntry,
  1806. &NextSerialNumber[ChangeLogEntry->DBIndex] )){
  1807. NlPrint((NL_CRITICAL,
  1808. "ValidateThisEntry: %lx %lx: Serial number is bad. s.b. %lx %lx\n",
  1809. ChangeLogEntry->SerialNumber.HighPart,
  1810. ChangeLogEntry->SerialNumber.LowPart,
  1811. NextSerialNumber[ChangeLogEntry->DBIndex].HighPart,
  1812. NextSerialNumber[ChangeLogEntry->DBIndex].LowPart ));
  1813. return FALSE;
  1814. }
  1815. }
  1816. //
  1817. // Increment next expected serial number
  1818. //
  1819. NextSerialNumber[ChangeLogEntry->DBIndex].QuadPart =
  1820. ChangeLogEntry->SerialNumber.QuadPart + 1;
  1821. //
  1822. // The current entry specifies the highest serial number for its
  1823. // database.
  1824. //
  1825. if ( InitialCall ) {
  1826. ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex] =
  1827. ChangeLogEntry->SerialNumber;
  1828. ChangeLogDesc->EntryCount[ChangeLogEntry->DBIndex] ++;
  1829. }
  1830. }
  1831. return TRUE;
  1832. }
  1833. BOOL
  1834. ValidateBlock(
  1835. IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1836. IN PCHANGELOG_BLOCK_HEADER Block,
  1837. IN OUT LARGE_INTEGER *NextSerialNumber,
  1838. IN BOOLEAN InitialCall
  1839. )
  1840. /*++
  1841. Routine Description:
  1842. Validate a changelog block.
  1843. NOTE: This function must be called with the change log locked.
  1844. Arguments:
  1845. ChangeLogDesc -- Description of the Changelog buffer to validate.
  1846. Block: pointer to the change log block to validate
  1847. NextSerialNumber: pointer to an array of serial numbers.
  1848. (NULL if serial numbers aren't to be validated.)
  1849. InitializeCall: TRUE iff SerialNumber array should be initialized.
  1850. Return Value:
  1851. TRUE: if the given entry is a valid next entry.
  1852. FALSE: otherwise.
  1853. --*/
  1854. {
  1855. //
  1856. // Ensure Block size is properly aligned.
  1857. //
  1858. if ( Block->BlockSize != ROUND_UP_COUNT(Block->BlockSize, ALIGN_WORST) ) {
  1859. NlPrint((NL_CRITICAL,
  1860. "ValidateBlock: Block size alignment is bad.\n" ));
  1861. return FALSE;
  1862. }
  1863. //
  1864. // Ensure the block is contained in the cache.
  1865. //
  1866. if ( Block->BlockSize > ChangeLogDesc->BufferSize ||
  1867. ((LPBYTE)Block + Block->BlockSize) > ChangeLogDesc->BufferEnd ) {
  1868. NlPrint((NL_CRITICAL,
  1869. "ValidateBlock: Block extends beyond end of buffer.\n" ));
  1870. return FALSE;
  1871. }
  1872. //
  1873. // Do Version 3 specific things
  1874. //
  1875. if ( ChangeLogDesc->Version3 ) {
  1876. //
  1877. // Ensure the block is big enough.
  1878. //
  1879. if ( Block->BlockSize < sizeof(CHANGELOG_BLOCK_HEADER) ) {
  1880. NlPrint((NL_CRITICAL,
  1881. "ValidateBlock: Block size is too small: %ld\n",
  1882. Block->BlockSize ));
  1883. return FALSE;
  1884. }
  1885. //
  1886. // Do version 4 specific validation
  1887. //
  1888. } else {
  1889. //
  1890. // Ensure the block is big enough.
  1891. //
  1892. if ( Block->BlockSize <
  1893. sizeof(CHANGELOG_BLOCK_HEADER) +
  1894. sizeof(CHANGELOG_BLOCK_TRAILER) ) {
  1895. NlPrint((NL_CRITICAL,
  1896. "ValidateBlock: Block size is too small: %ld\n",
  1897. Block->BlockSize ));
  1898. return FALSE;
  1899. }
  1900. //
  1901. // Ensure trailer and header match
  1902. //
  1903. if ( ChangeLogBlockTrailer(Block)->BlockSize != Block->BlockSize ) {
  1904. NlPrint((NL_CRITICAL,
  1905. "ValidateBlock: Header/Trailer block size mismatch: %ld %ld (Trailer fixed).\n",
  1906. Block->BlockSize,
  1907. ChangeLogBlockTrailer(Block)->BlockSize ));
  1908. ChangeLogBlockTrailer(Block)->BlockSize = Block->BlockSize;
  1909. }
  1910. }
  1911. //
  1912. // Free blocks have no other checking to do
  1913. //
  1914. switch ( Block->BlockState ) {
  1915. case BlockFree:
  1916. break;
  1917. //
  1918. // Used blocks have more checking to do.
  1919. //
  1920. case BlockUsed:
  1921. if ( !ValidateThisEntry( ChangeLogDesc,
  1922. (PCHANGELOG_ENTRY)(Block+1),
  1923. NextSerialNumber,
  1924. InitialCall )) {
  1925. return FALSE;
  1926. }
  1927. break;
  1928. //
  1929. // The hole is allowed only at the end of the buffer.
  1930. //
  1931. case BlockHole:
  1932. if ( (LPBYTE)Block + Block->BlockSize != ChangeLogDesc->BufferEnd ) {
  1933. NlPrint((NL_CRITICAL,
  1934. "ValidateBlock: Hole block in middle of buffer (buffer truncated).\n" ));
  1935. Block->BlockSize = (ULONG)(ChangeLogDesc->BufferEnd - (LPBYTE)Block);
  1936. }
  1937. break;
  1938. default:
  1939. NlPrint((NL_CRITICAL,
  1940. "ValidateBlock: Invalid block type %ld.\n",
  1941. Block->BlockState ));
  1942. return FALSE;
  1943. }
  1944. return TRUE;
  1945. }
  1946. BOOL
  1947. ValidateList(
  1948. IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  1949. IN BOOLEAN InitialCall
  1950. )
  1951. /*++
  1952. Routine Description:
  1953. Determine the given header is a valid header. It is done by
  1954. traversing the circular buffer starting from the given header and
  1955. validate each entry.
  1956. NOTE: This function must be called with the change log locked.
  1957. Arguments:
  1958. ChangeLogDesc -- Description of the Changelog buffer to validate.
  1959. InitialCall: TRUE iff SerialNumber Array and EntryCount should
  1960. be initialized.
  1961. Return Value:
  1962. TRUE: if the given header is valid.
  1963. FALSE: otherwise
  1964. --*/
  1965. {
  1966. LARGE_INTEGER NextSerialNumber[NUM_DBS];
  1967. PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
  1968. DWORD j;
  1969. //
  1970. // setup a NextSerialNumber array first.
  1971. //
  1972. for( j = 0; j < NUM_DBS; j++ ) {
  1973. NextSerialNumber[j].QuadPart = 0;
  1974. if ( InitialCall ) {
  1975. ChangeLogDesc->SerialNumber[j].QuadPart = 0;
  1976. }
  1977. }
  1978. //
  1979. // The cache is valid if it is empty.
  1980. //
  1981. if ( ChangeLogIsEmpty(ChangeLogDesc) ) {
  1982. return TRUE;
  1983. }
  1984. //
  1985. // Validate each block
  1986. //
  1987. for ( ChangeLogBlock = ChangeLogDesc->Head;
  1988. ;
  1989. ChangeLogBlock = NlMoveToNextChangeLogBlock( ChangeLogDesc, ChangeLogBlock) ) {
  1990. //
  1991. // Validate the block.
  1992. //
  1993. if( !ValidateBlock( ChangeLogDesc,
  1994. ChangeLogBlock,
  1995. NextSerialNumber,
  1996. InitialCall) ) {
  1997. return FALSE;
  1998. }
  1999. //
  2000. // Stop when we get to the end.
  2001. //
  2002. if ( ChangeLogBlock->BlockState == BlockFree ) {
  2003. break;
  2004. }
  2005. }
  2006. return TRUE;
  2007. }
  2008. BOOL
  2009. InitChangeLogHeadAndTail(
  2010. IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  2011. IN BOOLEAN NewChangeLog
  2012. )
  2013. /*++
  2014. Routine Description:
  2015. This function initializes the global head and tail pointers of change
  2016. log block list. The change log cache is made up of variable length
  2017. blocks, each block has a header containing the length of the block
  2018. and the block state ( BlockFree, BlockUsed and BlockHole ). The
  2019. last block in the change log block list is always the free block,
  2020. all other blocks in the cache are used blocks except a block at the
  2021. end of the cache may be a unused block known as 'hole' block. So
  2022. the head of the change log block list is the block that is just next
  2023. to the free block and the tail is the free block.
  2024. NOTE: This function must be called with the change log locked.
  2025. Arguments:
  2026. ChangeLogDesc -- Description of the Changelog buffer to analyze.
  2027. On entry, Buffer and BufferSize describe the allocated block containing
  2028. the change log read from disk.
  2029. On TRUE return, all the fields are filled in.
  2030. NewChangeLog -- True if no entries are in the change log
  2031. Return Value:
  2032. TRUE: if valid head and tail are successfully initialized.
  2033. FALSE: if valid head and tail can't be determined. This may be due
  2034. to the corrupted change log file.
  2035. --*/
  2036. {
  2037. PCHANGELOG_BLOCK_HEADER Block;
  2038. PCHANGELOG_BLOCK_HEADER FreeBlock;
  2039. DWORD i;
  2040. ChangeLogDesc->BufferEnd =
  2041. ChangeLogDesc->Buffer + ChangeLogDesc->BufferSize;
  2042. //
  2043. // Compute the address of the first physical cache entry.
  2044. //
  2045. ChangeLogDesc->FirstBlock = (PCHANGELOG_BLOCK_HEADER)
  2046. (ChangeLogDesc->Buffer +
  2047. sizeof(CHANGELOG_SIG));
  2048. ChangeLogDesc->FirstBlock = (PCHANGELOG_BLOCK_HEADER)
  2049. ROUND_UP_POINTER ( ChangeLogDesc->FirstBlock, ALIGN_WORST );
  2050. //
  2051. // Clear the count of entries in the change log and the serial numbers
  2052. // (We'll compute them later when we call ValidateList().)
  2053. for( i = 0; i < NUM_DBS; i++ ) {
  2054. ChangeLogDesc->EntryCount[i] = 0;
  2055. ChangeLogDesc->SerialNumber[i].QuadPart = 0;
  2056. }
  2057. //
  2058. // If this is a new change log,
  2059. // Initialize the Change Log Cache to zero.
  2060. //
  2061. Block = ChangeLogDesc->FirstBlock;
  2062. if ( NewChangeLog ) {
  2063. RtlZeroMemory(ChangeLogDesc->Buffer, ChangeLogDesc->BufferSize);
  2064. (VOID) strcpy( (PCHAR)ChangeLogDesc->Buffer, CHANGELOG_SIG);
  2065. Block->BlockState = BlockFree;
  2066. Block->BlockSize =
  2067. (ULONG)(ChangeLogDesc->BufferEnd - (LPBYTE)ChangeLogDesc->FirstBlock);
  2068. ChangeLogBlockTrailer(Block)->BlockSize = Block->BlockSize;
  2069. ChangeLogDesc->Version3 = FALSE;
  2070. ChangeLogDesc->Head = ChangeLogDesc->Tail = ChangeLogDesc->FirstBlock;
  2071. return TRUE;
  2072. }
  2073. //
  2074. // If no entries have been written to the changelog,
  2075. // simply initialize the head and tail to the block start.
  2076. //
  2077. if ( ChangeLogIsEmpty( ChangeLogDesc ) ) {
  2078. ChangeLogDesc->Head = ChangeLogDesc->Tail = ChangeLogDesc->FirstBlock;
  2079. NlPrint((NL_CHANGELOG,
  2080. "InitChangeLogHeadAndTail: Change log is empty.\n" ));
  2081. return TRUE;
  2082. }
  2083. //
  2084. // Loop through the cache looking for a free block.
  2085. //
  2086. FreeBlock = NULL;
  2087. do {
  2088. //
  2089. // Validate the block's integrity.
  2090. //
  2091. if ( !ValidateBlock( ChangeLogDesc, Block, NULL, FALSE )) {
  2092. return FALSE;
  2093. }
  2094. //
  2095. // Just remember where the free block is.
  2096. //
  2097. if ( Block->BlockState == BlockFree ) {
  2098. if ( FreeBlock != NULL ) {
  2099. NlPrint((NL_CRITICAL,
  2100. "InitChangeLogHeadAndTail: Multiple free blocks found.\n" ));
  2101. return FALSE;
  2102. }
  2103. FreeBlock = Block;
  2104. }
  2105. //
  2106. // Move to next block
  2107. //
  2108. Block = (PCHANGELOG_BLOCK_HEADER) ((LPBYTE)Block + Block->BlockSize);
  2109. } while ( (LPBYTE)Block < ChangeLogDesc->BufferEnd );
  2110. //
  2111. // If we didn't find a free block,
  2112. // the changelog is corrupt.
  2113. //
  2114. if ( FreeBlock == NULL ) {
  2115. NlPrint((NL_CRITICAL,
  2116. "InitChangeLogHeadAndTail: No Free block anywhere in buffer.\n" ));
  2117. return FALSE;
  2118. }
  2119. //
  2120. // We found the free block.
  2121. // (The tail pointer always points to the free block.)
  2122. //
  2123. ChangeLogDesc->Tail = FreeBlock;
  2124. //
  2125. // If free block is the last block in the change log block
  2126. // list, the head of the list is the first block in
  2127. // the list.
  2128. //
  2129. if( ((LPBYTE)FreeBlock + FreeBlock->BlockSize) >=
  2130. ChangeLogDesc->BufferEnd ) {
  2131. ChangeLogDesc->Head = ChangeLogDesc->FirstBlock;
  2132. //
  2133. //
  2134. // Otherwise, the head of the list is immediately after the tail.
  2135. //
  2136. } else {
  2137. ChangeLogDesc->Head = (PCHANGELOG_BLOCK_HEADER)
  2138. ((LPBYTE)FreeBlock + FreeBlock->BlockSize);
  2139. }
  2140. //
  2141. // Validate the list before returning from here.
  2142. //
  2143. if ( !ValidateList( ChangeLogDesc, TRUE) ) {
  2144. return FALSE;
  2145. }
  2146. return TRUE;
  2147. }
  2148. NTSTATUS
  2149. NlResetChangeLog(
  2150. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  2151. IN DWORD NewChangeLogSize
  2152. )
  2153. /*++
  2154. Routine Description:
  2155. This function resets the change log cache and change log file. This
  2156. function is called from InitChangeLog() function to afresh the
  2157. change log. This function may also be called from
  2158. I_NetNotifyDelta() function when the serial number of the new entry
  2159. is out of order.
  2160. NOTE: This function must be called with the change log locked.
  2161. Arguments:
  2162. ChangeLogDesc -- Description of the Changelog buffer being used
  2163. NewChangeLogSize -- Size (in bytes) of the new change log.
  2164. Return Value:
  2165. NT Status code
  2166. --*/
  2167. {
  2168. NTSTATUS Status;
  2169. NlPrint((NL_CHANGELOG, "%s log being reset.\n",
  2170. ChangeLogDesc->TempLog ? "TempChange" : "Change" ));
  2171. //
  2172. // Start with a clean slate.
  2173. //
  2174. NlCloseChangeLogFile( ChangeLogDesc );
  2175. //
  2176. // Allocate a buffer.
  2177. //
  2178. ChangeLogDesc->BufferSize = NewChangeLogSize;
  2179. ChangeLogDesc->Buffer = NetpMemoryAllocate(ChangeLogDesc->BufferSize );
  2180. if ( ChangeLogDesc->Buffer == NULL ) {
  2181. return STATUS_NO_MEMORY;
  2182. }
  2183. //
  2184. // Initialize the Change Log Cache to zero.
  2185. //
  2186. (VOID) InitChangeLogHeadAndTail( ChangeLogDesc, TRUE );
  2187. //
  2188. // Write the cache to the file.
  2189. //
  2190. Status = NlWriteChangeLogBytes( ChangeLogDesc,
  2191. ChangeLogDesc->Buffer,
  2192. ChangeLogDesc->BufferSize,
  2193. TRUE ); // Flush the bytes to disk
  2194. return Status;
  2195. }
  2196. NTSTATUS
  2197. I_NetLogonReadChangeLog(
  2198. IN PVOID InContext,
  2199. IN ULONG InContextSize,
  2200. IN ULONG ChangeBufferSize,
  2201. OUT PVOID *ChangeBuffer,
  2202. OUT PULONG BytesRead,
  2203. OUT PVOID *OutContext,
  2204. OUT PULONG OutContextSize
  2205. )
  2206. /*++
  2207. Routine Description:
  2208. This function returns a portion of the change log to the caller.
  2209. The caller asks for the first portion of the change log by passing zero as
  2210. the InContext/InContextSize. Each call passes out an OutContext that
  2211. indentifies the last change returned to the caller. That context can
  2212. be passed in on a subsequent call to I_NetlogonReadChangeLog.
  2213. Arguments:
  2214. InContext - Opaque context describing the last entry to have been previously
  2215. returned. Specify NULL to request the first entry.
  2216. InContextSize - Size (in bytes) of InContext. Specify 0 to request the
  2217. first entry.
  2218. ChangeBufferSize - Specifies the size (in bytes) of the passed in ChangeBuffer.
  2219. ChangeBuffer - Returns the next several entries from the change log.
  2220. Buffer must be DWORD aligned.
  2221. BytesRead - Returns the size (in bytes) of the entries returned in ChangeBuffer.
  2222. OutContext - Returns an opaque context describing the last entry returned
  2223. in ChangeBuffer. NULL is returned if no entries were returned.
  2224. The buffer must be freed using I_NetLogonFree
  2225. OutContextSize - Returns the size (in bytes) of OutContext.
  2226. Return Value:
  2227. STATUS_MORE_ENTRIES - More entries are available. This function should
  2228. be called again to retrieve the remaining entries.
  2229. STATUS_SUCCESS - No more entries are currently available. Some entries may
  2230. have been returned on this call. This function need not be called again.
  2231. However, the caller can determine if new change log entries were
  2232. added to the log, by calling this function again passing in the returned
  2233. context.
  2234. STATUS_INVALID_PARAMETER - InContext is invalid.
  2235. Either it is too short or the change log entry described no longer
  2236. exists in the change log.
  2237. STATUS_INVALID_DOMAIN_ROLE - Change log not initialized
  2238. STATUS_NO_MEMORY - There is not enough memory to allocate OutContext.
  2239. --*/
  2240. {
  2241. NTSTATUS Status;
  2242. CHANGELOG_CONTEXT Context;
  2243. PCHANGELOG_ENTRY ChangeLogEntry;
  2244. ULONG BytesCopied = 0;
  2245. LPBYTE Where = (LPBYTE)ChangeBuffer;
  2246. ULONG EntriesCopied = 0;
  2247. //
  2248. // Initialization.
  2249. //
  2250. *OutContext = NULL;
  2251. *OutContextSize = 0;
  2252. //
  2253. // Ensure the role is right. Otherwise, all the globals used below
  2254. // aren't initialized.
  2255. //
  2256. if ( NlGlobalChangeLogRole != ChangeLogPrimary ) {
  2257. NlPrint((NL_CHANGELOG,
  2258. "I_NetLogonReadChangeLog: failed 1\n" ));
  2259. return STATUS_INVALID_DOMAIN_ROLE;
  2260. }
  2261. //
  2262. // Also make sure that the change log cache is available.
  2263. //
  2264. if ( NlGlobalChangeLogDesc.Buffer == NULL ) {
  2265. NlPrint((NL_CHANGELOG,
  2266. "I_NetLogonReadChangeLog: failed 2\n" ));
  2267. return STATUS_INVALID_DOMAIN_ROLE;
  2268. }
  2269. //
  2270. // Validate the context.
  2271. //
  2272. LOCK_CHANGELOG();
  2273. if ( InContext == NULL ) {
  2274. NlPrint((NL_CHANGELOG, "I_NetLogonReadChangeLog: called with NULL\n" ));
  2275. //
  2276. // Start the sequence number at one.
  2277. //
  2278. Context.SequenceNumber = 1;
  2279. //
  2280. // If nothing has ever been written to the change log,
  2281. // indicate nothing is available.
  2282. //
  2283. if ( ChangeLogIsEmpty( &NlGlobalChangeLogDesc ) ) {
  2284. ChangeLogEntry = NULL;
  2285. //
  2286. // Otherwise, start at the beginning of the log.
  2287. //
  2288. } else {
  2289. ChangeLogEntry = (PCHANGELOG_ENTRY) (NlGlobalChangeLogDesc.Head + 1);
  2290. }
  2291. } else {
  2292. //
  2293. // Ensure the context wasn't mangled.
  2294. //
  2295. if ( InContextSize < sizeof(CHANGELOG_CONTEXT) ) {
  2296. Status = STATUS_INVALID_PARAMETER;
  2297. goto Cleanup;
  2298. }
  2299. RtlCopyMemory( &Context, InContext, sizeof(CHANGELOG_CONTEXT) );
  2300. NlPrint((NL_CHANGELOG, "I_NetLogonReadChangeLog: called with %lx %lx in %ld (%ld)\n",
  2301. Context.SerialNumber.HighPart,
  2302. Context.SerialNumber.LowPart,
  2303. Context.DbIndex,
  2304. Context.SequenceNumber ));
  2305. //
  2306. // Increment the sequence number.
  2307. //
  2308. Context.SequenceNumber++;
  2309. //
  2310. // Find the change log entry corresponding to the context.
  2311. //
  2312. ChangeLogEntry = NlFindChangeLogEntry( &NlGlobalChangeLogDesc,
  2313. Context.SerialNumber,
  2314. FALSE, // Not downlevel
  2315. TRUE, // Exact match needed
  2316. Context.DbIndex );
  2317. if ( ChangeLogEntry == NULL ) {
  2318. Status = STATUS_INVALID_PARAMETER;
  2319. NlPrint((NL_CHANGELOG, "I_NetLogonReadChangeLog: %lx %lx in %ld: Entry no longer exists in change log.\n",
  2320. Context.SerialNumber.HighPart,
  2321. Context.SerialNumber.LowPart,
  2322. Context.DbIndex ));
  2323. goto Cleanup;
  2324. }
  2325. //
  2326. // The next change log entry is the one to return first.
  2327. //
  2328. ChangeLogEntry = NlMoveToNextChangeLogEntry( &NlGlobalChangeLogDesc,
  2329. ChangeLogEntry );
  2330. }
  2331. //
  2332. // Copy a header into the ChangeBuffer.
  2333. //
  2334. if ( ChangeBufferSize <= sizeof(CHANGELOG_BUFFER_HEADER) ) {
  2335. Status = STATUS_BUFFER_TOO_SMALL;
  2336. goto Cleanup;
  2337. }
  2338. ((PCHANGELOG_BUFFER_HEADER)Where)->Size = sizeof(CHANGELOG_BUFFER_HEADER);
  2339. ((PCHANGELOG_BUFFER_HEADER)Where)->Version = CHANGELOG_BUFFER_VERSION;
  2340. ((PCHANGELOG_BUFFER_HEADER)Where)->SequenceNumber = Context.SequenceNumber;
  2341. ((PCHANGELOG_BUFFER_HEADER)Where)->Flags = 0;
  2342. Where += sizeof(CHANGELOG_BUFFER_HEADER);
  2343. BytesCopied += sizeof(CHANGELOG_BUFFER_HEADER);
  2344. //
  2345. // Loop returning change log entries to the caller.
  2346. //
  2347. // ASSERT: ChangeLogEntry is the next entry to return to the caller.
  2348. // ASSERT: ChangeLogEntry is NULL if there are no more change log entries.
  2349. while ( ChangeLogEntry != NULL ) {
  2350. ULONG SizeToCopy;
  2351. PCHANGELOG_BLOCK_HEADER ChangeLogBlock;
  2352. //
  2353. // Skip over any voided log entries.
  2354. //
  2355. while ( ChangeLogEntry != NULL && ChangeLogEntry->DBIndex == VOID_DB ) {
  2356. //
  2357. // Get the next entry to copy.
  2358. //
  2359. ChangeLogEntry = NlMoveToNextChangeLogEntry( &NlGlobalChangeLogDesc,
  2360. ChangeLogEntry );
  2361. }
  2362. if ( ChangeLogEntry == NULL ) {
  2363. break;
  2364. }
  2365. //
  2366. // Compute the size of the change log entry to copy.
  2367. //
  2368. ChangeLogBlock = (PCHANGELOG_BLOCK_HEADER)
  2369. ( (LPBYTE) ChangeLogEntry - sizeof(CHANGELOG_BLOCK_HEADER) );
  2370. SizeToCopy = ChangeLogBlock->BlockSize -
  2371. sizeof(CHANGELOG_BLOCK_HEADER) -
  2372. sizeof(CHANGELOG_BLOCK_TRAILER);
  2373. NlAssert( SizeToCopy == ROUND_UP_COUNT( SizeToCopy, ALIGN_DWORD ));
  2374. //
  2375. // Ensure the entry fits in the buffer.
  2376. //
  2377. if ( BytesCopied + SizeToCopy + sizeof(DWORD) > ChangeBufferSize ) {
  2378. break;
  2379. }
  2380. //
  2381. // Copy this entry into the buffer.
  2382. //
  2383. *((LPDWORD)Where) = SizeToCopy;
  2384. Where += sizeof(DWORD);
  2385. BytesCopied += sizeof(DWORD);
  2386. RtlCopyMemory( Where, ChangeLogEntry, SizeToCopy );
  2387. Where += SizeToCopy;
  2388. BytesCopied += SizeToCopy;
  2389. EntriesCopied += 1;
  2390. //
  2391. // Remember the context of this Entry.
  2392. //
  2393. Context.SerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart;
  2394. Context.DbIndex = ChangeLogEntry->DBIndex;
  2395. //
  2396. // Get the next entry to copy.
  2397. //
  2398. ChangeLogEntry = NlMoveToNextChangeLogEntry( &NlGlobalChangeLogDesc,
  2399. ChangeLogEntry );
  2400. }
  2401. //
  2402. // Determine the status code to return.
  2403. //
  2404. if ( ChangeLogEntry == NULL ) {
  2405. Status = STATUS_SUCCESS;
  2406. } else {
  2407. Status = STATUS_MORE_ENTRIES;
  2408. //
  2409. // If no data was copied,
  2410. // give a better status.
  2411. //
  2412. if ( EntriesCopied == 0 ) {
  2413. Status = STATUS_BUFFER_TOO_SMALL;
  2414. BytesCopied = 0;
  2415. goto Cleanup;
  2416. }
  2417. }
  2418. //
  2419. // If any data was returned,
  2420. // return context to the caller.
  2421. //
  2422. if ( EntriesCopied ) {
  2423. *OutContext = NetpMemoryAllocate( sizeof(CHANGELOG_CONTEXT) );
  2424. if ( *OutContext == NULL ) {
  2425. Status = STATUS_NO_MEMORY;
  2426. BytesCopied = 0;
  2427. goto Cleanup;
  2428. }
  2429. *((PCHANGELOG_CONTEXT)*OutContext) = Context;
  2430. *OutContextSize = sizeof(CHANGELOG_CONTEXT);
  2431. }
  2432. Cleanup:
  2433. *BytesRead = BytesCopied;
  2434. UNLOCK_CHANGELOG();
  2435. return Status;
  2436. }
  2437. NTSTATUS
  2438. I_NetLogonNewChangeLog(
  2439. OUT HANDLE *ChangeLogHandle
  2440. )
  2441. /*++
  2442. Routine Description:
  2443. This function opens a new changelog file for writing. The new changelog
  2444. is a temporary file. The real change will not be modified until
  2445. I_NetLogonCloseChangeLog is called asking to Comit the changes.
  2446. The caller should follow this call by Zero more calls to
  2447. I_NetLogonAppendChangeLog followed by a call to I_NetLogonCloseChangeLog.
  2448. Only one temporary change log can be active at once.
  2449. Arguments:
  2450. ChangeLogHandle - Returns a handle identifying the temporary change log.
  2451. Return Value:
  2452. STATUS_SUCCESS - The temporary change log has been successfully opened.
  2453. STATUS_INVALID_DOMAIN_ROLE - DC is neither PDC nor BDC.
  2454. STATUS_NO_MEMORY - Not enough memory to create the change log buffer.
  2455. Sundry file creation errors.
  2456. --*/
  2457. {
  2458. NTSTATUS Status;
  2459. NlPrint((NL_CHANGELOG, "I_NetLogonNewChangeLog: called\n" ));
  2460. //
  2461. // Ensure the role is right. Otherwise, all the globals used below
  2462. // aren't initialized.
  2463. //
  2464. if ( NlGlobalChangeLogRole != ChangeLogPrimary &&
  2465. NlGlobalChangeLogRole != ChangeLogBackup ) {
  2466. NlPrint((NL_CHANGELOG,
  2467. "I_NetLogonNewChangeLog: failed 1\n" ));
  2468. return STATUS_INVALID_DOMAIN_ROLE;
  2469. }
  2470. //
  2471. // Initialize the global context.
  2472. //
  2473. LOCK_CHANGELOG();
  2474. InitChangeLogDesc( &NlGlobalTempChangeLogDesc );
  2475. NlGlobalTempChangeLogDesc.TempLog = TRUE;
  2476. //
  2477. // Create a temporary change log the same size as the real changelog
  2478. //
  2479. Status = NlResetChangeLog( &NlGlobalTempChangeLogDesc,
  2480. NlGlobalChangeLogDesc.BufferSize );
  2481. if ( !NT_SUCCESS(Status) ) {
  2482. NlPrint((NL_CHANGELOG,
  2483. "I_NetLogonNewChangeLog: cannot reset temp change log 0x%lx\n",
  2484. Status ));
  2485. goto Cleanup;
  2486. }
  2487. NlGlobalChangeLogSequenceNumber = 0;
  2488. *(PULONG)ChangeLogHandle = ++ NlGlobalChangeLogHandle;
  2489. Cleanup:
  2490. UNLOCK_CHANGELOG();
  2491. return Status;
  2492. }
  2493. NTSTATUS
  2494. I_NetLogonAppendChangeLog(
  2495. IN HANDLE ChangeLogHandle,
  2496. IN PVOID ChangeBuffer,
  2497. IN ULONG ChangeBufferSize
  2498. )
  2499. /*++
  2500. Routine Description:
  2501. This function appends change log information to new changelog file.
  2502. The ChangeBuffer must be a change buffer returned from I_NetLogonReadChangeLog.
  2503. Care should be taken to ensure each call to I_NetLogonReadChangeLog is
  2504. exactly matched by one call to I_NetLogonAppendChangeLog.
  2505. Arguments:
  2506. ChangeLogHandle - A handle identifying the temporary change log.
  2507. ChangeBuffer - A buffer describing a set of changes returned from
  2508. I_NetLogonReadChangeLog.
  2509. ChangeBufferSize - Size (in bytes) of ChangeBuffer.
  2510. Return Value:
  2511. STATUS_SUCCESS - The temporary change log has been successfully opened.
  2512. STATUS_INVALID_DOMAIN_ROLE - DC is neither PDC nor BDC.
  2513. STATUS_INVALID_HANDLE - ChangeLogHandle is not valid.
  2514. STATUS_INVALID_PARAMETER - ChangeBuffer contains invalid data.
  2515. Sundry disk write errors.
  2516. --*/
  2517. {
  2518. NTSTATUS Status;
  2519. LPBYTE Where;
  2520. ULONG BytesLeft;
  2521. LPBYTE AllocatedChangeBuffer = NULL;
  2522. //
  2523. // Ensure the role is right. Otherwise, all the globals used below
  2524. // aren't initialized.
  2525. //
  2526. if ( NlGlobalChangeLogRole != ChangeLogPrimary &&
  2527. NlGlobalChangeLogRole != ChangeLogBackup ) {
  2528. NlPrint((NL_CHANGELOG,
  2529. "I_NetLogonAppendChangeLog: failed 1\n" ));
  2530. return STATUS_INVALID_DOMAIN_ROLE;
  2531. }
  2532. //
  2533. // Check the handle.
  2534. //
  2535. LOCK_CHANGELOG();
  2536. if ( HandleToUlong(ChangeLogHandle) != NlGlobalChangeLogHandle ) {
  2537. Status = STATUS_INVALID_HANDLE;
  2538. goto Cleanup;
  2539. }
  2540. //
  2541. // Make a properly aligned copy of the buffer.
  2542. // Sam gets it as a byte array over RPC. RPC chooses to align it poorly.
  2543. //
  2544. BytesLeft = ChangeBufferSize;
  2545. if ( BytesLeft < sizeof(CHANGELOG_BUFFER_HEADER) ) {
  2546. NlPrint((NL_CRITICAL,
  2547. "I_NetLogonAppendChangeLog: Buffer has no header %ld\n", BytesLeft ));
  2548. Status = STATUS_INVALID_PARAMETER;
  2549. goto Cleanup;
  2550. }
  2551. AllocatedChangeBuffer = LocalAlloc( 0, BytesLeft );
  2552. if ( AllocatedChangeBuffer == NULL ) {
  2553. Status = STATUS_NO_MEMORY;
  2554. goto Cleanup;
  2555. }
  2556. RtlCopyMemory( AllocatedChangeBuffer, ChangeBuffer, BytesLeft );
  2557. //
  2558. // Validate the buffer header.
  2559. //
  2560. Where = (LPBYTE) AllocatedChangeBuffer;
  2561. if ( ((PCHANGELOG_BUFFER_HEADER)Where)->Size < sizeof(CHANGELOG_BUFFER_HEADER) ||
  2562. ((PCHANGELOG_BUFFER_HEADER)Where)->Size > BytesLeft ) {
  2563. NlPrint((NL_CRITICAL,
  2564. "I_NetLogonAppendChangeLog: Header size is bogus %ld %ld\n",
  2565. ((PCHANGELOG_BUFFER_HEADER)Where)->Size,
  2566. BytesLeft ));
  2567. Status = STATUS_INVALID_PARAMETER;
  2568. goto Cleanup;
  2569. }
  2570. if ( ((PCHANGELOG_BUFFER_HEADER)Where)->Version != CHANGELOG_BUFFER_VERSION ) {
  2571. NlPrint((NL_CRITICAL,
  2572. "I_NetLogonAppendChangeLog: Header version is bogus %ld\n",
  2573. ((PCHANGELOG_BUFFER_HEADER)Where)->Version ));
  2574. Status = STATUS_INVALID_PARAMETER;
  2575. goto Cleanup;
  2576. }
  2577. if ( ((PCHANGELOG_BUFFER_HEADER)Where)->SequenceNumber != NlGlobalChangeLogSequenceNumber + 1 ) {
  2578. NlPrint((NL_CRITICAL,
  2579. "I_NetLogonAppendChangeLog: Header out of sequence %ld %ld\n",
  2580. ((PCHANGELOG_BUFFER_HEADER)Where)->SequenceNumber,
  2581. NlGlobalChangeLogSequenceNumber ));
  2582. Status = STATUS_INVALID_PARAMETER;
  2583. goto Cleanup;
  2584. }
  2585. NlPrint((NL_CHANGELOG, "I_NetLogonAppendChangeLog: called (%ld)\n", NlGlobalChangeLogSequenceNumber ));
  2586. NlGlobalChangeLogSequenceNumber += 1;
  2587. BytesLeft -= ((PCHANGELOG_BUFFER_HEADER)Where)->Size;
  2588. Where += ((PCHANGELOG_BUFFER_HEADER)Where)->Size;
  2589. //
  2590. // Loop through the individual changes
  2591. //
  2592. while ( BytesLeft != 0 ) {
  2593. PCHANGELOG_ENTRY ChangeLogEntry;
  2594. ULONG ChangeLogEntrySize;
  2595. //
  2596. // Ensure that at least the size field is present.
  2597. //
  2598. if ( BytesLeft < sizeof(DWORD) ) {
  2599. NlPrint((NL_CRITICAL,
  2600. "I_NetLogonAppendChangeLog: Bytes left is too small %ld\n", BytesLeft ));
  2601. Status = STATUS_INVALID_PARAMETER;
  2602. goto Cleanup;
  2603. }
  2604. //
  2605. // Ensure the entire change log entry is present.
  2606. //
  2607. ChangeLogEntrySize = *((PULONG)Where);
  2608. Where += sizeof(ULONG);
  2609. BytesLeft -= sizeof(ULONG);
  2610. if ( BytesLeft < ChangeLogEntrySize ) {
  2611. NlPrint((NL_CRITICAL,
  2612. "I_NetLogonAppendChangeLog: Bytes left is smaller than entry size %ld %ld\n",
  2613. BytesLeft, ChangeLogEntrySize ));
  2614. Status = STATUS_INVALID_PARAMETER;
  2615. goto Cleanup;
  2616. }
  2617. ChangeLogEntry = (PCHANGELOG_ENTRY)Where;
  2618. Where += ChangeLogEntrySize;
  2619. BytesLeft -= ChangeLogEntrySize;
  2620. //
  2621. // Check the structural integrity of the entry.
  2622. //
  2623. if ( !NlValidateChangeLogEntry( ChangeLogEntry, ChangeLogEntrySize)) {
  2624. NlPrint((NL_CRITICAL,
  2625. "I_NetLogonAppendChangeLog: ChangeLogEntry is bogus\n" ));
  2626. Status = STATUS_INVALID_PARAMETER;
  2627. goto Cleanup;
  2628. }
  2629. //
  2630. // If this is not an entry for the LSA database,
  2631. // copy the change log entry into the temporary change log.
  2632. //
  2633. // The rational here is that this routine is called on this
  2634. // DC when this DC is in the process of becoming a PDC. A
  2635. // new change log is created that is coppied from the one on
  2636. // what is currently the PDC. Parallel to this, the SAM database
  2637. // is replicated using DS from the current PDC to this DC.
  2638. // However, the LSA database isn't replicated in this process.
  2639. // This is OK since after this machine becomes the PDC, it will
  2640. // change the timestamp of the master database creation forcing
  2641. // BDCs to do a full LSA sync from this PDC next time they send a
  2642. // ssync request. However, if we were to write LSA entries into
  2643. // the change log, we could potentially have different serial
  2644. // numbers for what we currently have in the LSA database locally
  2645. // on this machine and in the change log we got from what is
  2646. // currently the PDC. This would potentially produce event log
  2647. // errors next time LSA tries to write into the log file locally.
  2648. // So we choose not to write the LSA entries in the change log at
  2649. // all.
  2650. //
  2651. if ( ChangeLogEntry->DBIndex != LSA_DB ) {
  2652. Status = NlCopyChangeLogEntry(
  2653. FALSE, // Entry is NOT version 3
  2654. ChangeLogEntry,
  2655. &NlGlobalTempChangeLogDesc );
  2656. if ( !NT_SUCCESS(Status) ) {
  2657. NlPrint((NL_CRITICAL,
  2658. "I_NetLogonAppendChangeLog: Cannot copy ChangeLogEntry 0x%lx\n",
  2659. Status ));
  2660. goto Cleanup;
  2661. }
  2662. }
  2663. }
  2664. //
  2665. // Flush the changes to disk.
  2666. //
  2667. Status = NlFlushChangeLog( &NlGlobalTempChangeLogDesc );
  2668. if ( !NT_SUCCESS(Status) ) {
  2669. NlPrint((NL_CRITICAL,
  2670. "I_NetLogonAppendChangeLog: Cannot flush changes 0x%lx\n",
  2671. Status ));
  2672. goto Cleanup;
  2673. }
  2674. Status = STATUS_SUCCESS;
  2675. Cleanup:
  2676. UNLOCK_CHANGELOG();
  2677. if ( AllocatedChangeBuffer != NULL ) {
  2678. LocalFree( AllocatedChangeBuffer );
  2679. }
  2680. return Status;
  2681. }
  2682. NTSTATUS
  2683. I_NetLogonCloseChangeLog(
  2684. IN HANDLE ChangeLogHandle,
  2685. IN BOOLEAN Commit
  2686. )
  2687. /*++
  2688. Routine Description:
  2689. This function closes a new changelog file.
  2690. Arguments:
  2691. ChangeLogHandle - A handle identifying the temporary change log.
  2692. Commit - If true, the specified changes are written to the primary change log.
  2693. If false, the specified change are deleted.
  2694. Return Value:
  2695. STATUS_SUCCESS - The temporary change log has been successfully opened.
  2696. STATUS_INVALID_DOMAIN_ROLE - DC is neither PDC nor BDC.
  2697. STATUS_INVALID_HANDLE - ChangeLogHandle is not valid.
  2698. --*/
  2699. {
  2700. NTSTATUS Status;
  2701. LPBYTE Where;
  2702. ULONG BytesLeft;
  2703. NlPrint((NL_CHANGELOG, "I_NetLogonAppendCloseLog: called (%ld)\n", Commit ));
  2704. //
  2705. // Ensure the role is right. Otherwise, all the globals used below
  2706. // aren't initialized.
  2707. //
  2708. if ( NlGlobalChangeLogRole != ChangeLogPrimary &&
  2709. NlGlobalChangeLogRole != ChangeLogBackup ) {
  2710. NlPrint((NL_CHANGELOG,
  2711. "I_NetLogonCloseChangeLog: failed 1\n" ));
  2712. return STATUS_INVALID_DOMAIN_ROLE;
  2713. }
  2714. //
  2715. // Check the handle.
  2716. //
  2717. LOCK_CHANGELOG();
  2718. if ( HandleToUlong(ChangeLogHandle) != NlGlobalChangeLogHandle ) {
  2719. Status = STATUS_INVALID_HANDLE;
  2720. goto Cleanup;
  2721. }
  2722. NlGlobalChangeLogHandle ++; // Invalidate this handle
  2723. //
  2724. // If the changes are to be committed,
  2725. // copy them now.
  2726. //
  2727. if ( Commit ) {
  2728. //
  2729. // Close the existing change log
  2730. //
  2731. NlCloseChangeLogFile( &NlGlobalChangeLogDesc );
  2732. //
  2733. // Clone the temporary change log.
  2734. //
  2735. NlGlobalChangeLogDesc = NlGlobalTempChangeLogDesc;
  2736. NlGlobalChangeLogDesc.FileHandle = INVALID_HANDLE_VALUE; // Don't use the temporary file
  2737. NlGlobalTempChangeLogDesc.Buffer = NULL; // Don't have two refs to the same buffer
  2738. NlGlobalChangeLogDesc.TempLog = FALSE; // Log is no longer the temporary log
  2739. //
  2740. // Write the cache to the file.
  2741. //
  2742. // Ignore errors since the log is successfully in memory
  2743. //
  2744. (VOID) NlWriteChangeLogBytes( &NlGlobalChangeLogDesc,
  2745. NlGlobalChangeLogDesc.Buffer,
  2746. NlGlobalChangeLogDesc.BufferSize,
  2747. TRUE ); // Flush the bytes to disk
  2748. }
  2749. //
  2750. // Delete the temporary log file.
  2751. //
  2752. NlCloseChangeLogFile( &NlGlobalTempChangeLogDesc );
  2753. #ifdef notdef // Leave it around for debugging purposes.
  2754. {
  2755. WCHAR ChangeLogFile[MAX_PATH+CHANGELOG_FILE_POSTFIX_LENGTH+1];
  2756. wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
  2757. wcscat( ChangeLogFile, TEMP_CHANGELOG_FILE_POSTFIX );
  2758. if ( !DeleteFile( ChangeLogFile ) ) {
  2759. NlPrint(( NL_CRITICAL,
  2760. "NlVoidChangeLogEntry: cannot delete temp change log %ld.\n",
  2761. GetLastError() ));
  2762. }
  2763. }
  2764. #endif // notdef
  2765. Status = STATUS_SUCCESS;
  2766. Cleanup:
  2767. UNLOCK_CHANGELOG();
  2768. return Status;
  2769. }
  2770. #if NETLOGONDBG
  2771. VOID
  2772. PrintChangeLogEntry(
  2773. PCHANGELOG_ENTRY ChangeLogEntry
  2774. )
  2775. /*++
  2776. Routine Description:
  2777. This routine print the content of the given changelog entry.
  2778. Arguments:
  2779. ChangeLogEntry -- pointer to the change log entry to print
  2780. Return Value:
  2781. none.
  2782. --*/
  2783. {
  2784. LPSTR DeltaName;
  2785. switch ( ChangeLogEntry->DeltaType ) {
  2786. case AddOrChangeDomain:
  2787. DeltaName = "AddOrChangeDomain";
  2788. break;
  2789. case AddOrChangeGroup:
  2790. DeltaName = "AddOrChangeGroup";
  2791. break;
  2792. case DeleteGroupByName:
  2793. case DeleteGroup:
  2794. DeltaName = "DeleteGroup";
  2795. break;
  2796. case RenameGroup:
  2797. DeltaName = "RenameGroup";
  2798. break;
  2799. case AddOrChangeUser:
  2800. DeltaName = "AddOrChangeUser";
  2801. break;
  2802. case DeleteUserByName:
  2803. case DeleteUser:
  2804. DeltaName = "DeleteUser";
  2805. break;
  2806. case RenameUser:
  2807. DeltaName = "RenameUser";
  2808. break;
  2809. case ChangeGroupMembership:
  2810. DeltaName = "ChangeGroupMembership";
  2811. break;
  2812. case AddOrChangeAlias:
  2813. DeltaName = "AddOrChangeAlias";
  2814. break;
  2815. case DeleteAlias:
  2816. DeltaName = "DeleteAlias";
  2817. break;
  2818. case RenameAlias:
  2819. DeltaName = "RenameAlias";
  2820. break;
  2821. case ChangeAliasMembership:
  2822. DeltaName = "ChangeAliasMembership";
  2823. break;
  2824. case AddOrChangeLsaPolicy:
  2825. DeltaName = "AddOrChangeLsaPolicy";
  2826. break;
  2827. case AddOrChangeLsaTDomain:
  2828. DeltaName = "AddOrChangeLsaTDomain";
  2829. break;
  2830. case DeleteLsaTDomain:
  2831. DeltaName = "DeleteLsaTDomain";
  2832. break;
  2833. case AddOrChangeLsaAccount:
  2834. DeltaName = "AddOrChangeLsaAccount";
  2835. break;
  2836. case DeleteLsaAccount:
  2837. DeltaName = "DeleteLsaAccount";
  2838. break;
  2839. case AddOrChangeLsaSecret:
  2840. DeltaName = "AddOrChangeLsaSecret";
  2841. break;
  2842. case DeleteLsaSecret:
  2843. DeltaName = "DeleteLsaSecret";
  2844. break;
  2845. case SerialNumberSkip:
  2846. DeltaName = "SerialNumberSkip";
  2847. break;
  2848. case DummyChangeLogEntry:
  2849. DeltaName = "DummyChangeLogEntry";
  2850. break;
  2851. default:
  2852. DeltaName ="(Unknown)";
  2853. break;
  2854. }
  2855. NlPrint((NL_CHANGELOG,
  2856. "DeltaType %s (%ld) SerialNumber: %lx %lx",
  2857. DeltaName,
  2858. ChangeLogEntry->DeltaType,
  2859. ChangeLogEntry->SerialNumber.HighPart,
  2860. ChangeLogEntry->SerialNumber.LowPart ));
  2861. if ( ChangeLogEntry->ObjectRid != 0 ) {
  2862. NlPrint((NL_CHANGELOG," Rid: 0x%lx", ChangeLogEntry->ObjectRid ));
  2863. }
  2864. if ( ChangeLogEntry->Flags & CHANGELOG_PDC_PROMOTION ) {
  2865. NlPrint((NL_CHANGELOG," Promotion" ));
  2866. }
  2867. if( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
  2868. NlPrint(( NL_CHANGELOG, " Name: '" FORMAT_LPWSTR "'",
  2869. (LPWSTR)((PBYTE)(ChangeLogEntry)+ sizeof(CHANGELOG_ENTRY))));
  2870. }
  2871. if( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
  2872. NlPrint((NL_CHANGELOG," Sid: "));
  2873. NlpDumpSid( NL_CHANGELOG,
  2874. (PSID)((PBYTE)(ChangeLogEntry)+ sizeof(CHANGELOG_ENTRY)) );
  2875. } else {
  2876. NlPrint((NL_CHANGELOG,"\n" ));
  2877. }
  2878. }
  2879. #endif // NETLOGONDBG
  2880. NTSTATUS
  2881. NlWriteChangeLogEntry(
  2882. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  2883. IN PCHANGELOG_ENTRY ChangeLogEntry,
  2884. IN PSID ObjectSid,
  2885. IN PUNICODE_STRING ObjectName,
  2886. IN BOOLEAN FlushIt
  2887. )
  2888. /*++
  2889. Routine Description:
  2890. This is the actual worker for the I_NetNotifyDelta(). This function
  2891. acquires the sufficient size memory block from the change log
  2892. buffer, writes the fixed and variable portions of the change log
  2893. delta in change log buffer and also writes the delta into change log
  2894. file.
  2895. Arguments:
  2896. ChangeLogDesc -- Description of the Changelog buffer being used
  2897. ChangeLogEntry - pointer to the fixed portion of the change log.
  2898. ObjectSid - pointer to the variable field SID.
  2899. ObjectName - pointer to the variable field Name.
  2900. FlushIt - True if the written bytes are to be flushed to disk
  2901. Return Value:
  2902. STATUS_SUCCESS - The Service completed successfully.
  2903. --*/
  2904. {
  2905. NTSTATUS Status;
  2906. DWORD LogSize;
  2907. PCHANGELOG_BLOCK_HEADER LogBlock;
  2908. PCHANGELOG_BLOCK_HEADER FreeBlock;
  2909. LPBYTE AllocatedChangeLogEntry;
  2910. //
  2911. // Make sure that the change log cache is available.
  2912. //
  2913. if ( ChangeLogDesc->Buffer == NULL ) {
  2914. return STATUS_INTERNAL_ERROR;
  2915. }
  2916. //
  2917. // Determine the size of this change log entry.
  2918. //
  2919. LogSize = sizeof(CHANGELOG_ENTRY);
  2920. //
  2921. // Ensure we've got the right data for those deltas we care about
  2922. //
  2923. switch (ChangeLogEntry->DeltaType) {
  2924. case AddOrChangeLsaTDomain:
  2925. case DeleteLsaTDomain:
  2926. case AddOrChangeLsaAccount:
  2927. case DeleteLsaAccount:
  2928. NlAssert( ObjectSid != NULL );
  2929. if( ObjectSid != NULL ) {
  2930. ChangeLogEntry->Flags |= CHANGELOG_SID_SPECIFIED;
  2931. LogSize += RtlLengthSid( ObjectSid );
  2932. }
  2933. break;
  2934. case AddOrChangeLsaSecret:
  2935. case DeleteLsaSecret:
  2936. case DeleteGroup:
  2937. case DeleteUser:
  2938. // NlAssert( ObjectName != NULL && ObjectName->Buffer != NULL && ObjectName->Length != 0 );
  2939. if( ObjectName != NULL && ObjectName->Buffer != NULL && ObjectName->Length != 0 ) {
  2940. ChangeLogEntry->Flags |= CHANGELOG_NAME_SPECIFIED;
  2941. LogSize += ObjectName->Length + sizeof(WCHAR);
  2942. }
  2943. break;
  2944. //
  2945. // For all other delta types, save the data if it's there.
  2946. //
  2947. default:
  2948. if( ObjectName != NULL && ObjectName->Buffer != NULL && ObjectName->Length != 0 ) {
  2949. ChangeLogEntry->Flags |= CHANGELOG_NAME_SPECIFIED;
  2950. LogSize += ObjectName->Length + sizeof(WCHAR);
  2951. } else if( ObjectSid != NULL ) {
  2952. ChangeLogEntry->Flags |= CHANGELOG_SID_SPECIFIED;
  2953. LogSize += RtlLengthSid( ObjectSid );
  2954. }
  2955. break;
  2956. }
  2957. //
  2958. // Serialize access to the change log
  2959. //
  2960. LOCK_CHANGELOG();
  2961. //
  2962. // Validate the serial number order of this new entry
  2963. //
  2964. // If we're out of sync with the caller,
  2965. // clear the change log and start all over again.
  2966. //
  2967. // The global serial number array entry for this database must either
  2968. // be zero (indicating no entries for this database) or one less than
  2969. // the new serial number being added.
  2970. //
  2971. if ( ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex].QuadPart != 0 ) {
  2972. LARGE_INTEGER ExpectedSerialNumber;
  2973. LARGE_INTEGER OldSerialNumber;
  2974. ExpectedSerialNumber.QuadPart =
  2975. ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex].QuadPart + 1;
  2976. //
  2977. // If the serial number jumped by the promotion increment,
  2978. // set the flag in the change log entry indicating this is
  2979. // a promotion to PDC.
  2980. //
  2981. if ( ChangeLogEntry->SerialNumber.QuadPart ==
  2982. ExpectedSerialNumber.QuadPart +
  2983. NlGlobalChangeLogPromotionIncrement.QuadPart ) {
  2984. ChangeLogEntry->Flags |= CHANGELOG_PDC_PROMOTION;
  2985. }
  2986. if ( !IsSerialNumberEqual( ChangeLogDesc,
  2987. ChangeLogEntry,
  2988. &ExpectedSerialNumber )) {
  2989. NlPrint((NL_CRITICAL,
  2990. "NlWriteChangeLogEntry: Serial numbers not contigous %lx %lx and %lx %lx\n",
  2991. ChangeLogEntry->SerialNumber.HighPart,
  2992. ChangeLogEntry->SerialNumber.LowPart,
  2993. ExpectedSerialNumber.HighPart,
  2994. ExpectedSerialNumber.LowPart ));
  2995. //
  2996. // write event log.
  2997. //
  2998. NlWriteChangeLogCorruptEvent( STATUS_INTERNAL_DB_CORRUPTION,
  2999. ChangeLogEntry->DBIndex );
  3000. //
  3001. // If the change log is merely newer than the SAM database,
  3002. // we truncate entries newer than what exists in SAM.
  3003. //
  3004. OldSerialNumber.QuadPart = ChangeLogEntry->SerialNumber.QuadPart - 1;
  3005. (VOID) NlFixChangeLog( ChangeLogDesc, ChangeLogEntry->DBIndex, OldSerialNumber );
  3006. }
  3007. //
  3008. // If this is the first entry written to the change log for this database,
  3009. // mark it as a promotion.
  3010. //
  3011. } else {
  3012. //
  3013. // Only mark entries that might possibly be a promotion.
  3014. //
  3015. switch (ChangeLogEntry->DeltaType) {
  3016. case AddOrChangeDomain:
  3017. case AddOrChangeLsaPolicy:
  3018. ChangeLogEntry->Flags |= CHANGELOG_PDC_PROMOTION;
  3019. break;
  3020. }
  3021. }
  3022. //
  3023. // Validate the list before changing anything
  3024. //
  3025. #if DBG
  3026. NlAssert( ValidateList( ChangeLogDesc, FALSE) );
  3027. #endif // DBG
  3028. //
  3029. // copy fixed portion
  3030. //
  3031. Status = NlAllocChangeLogBlock( ChangeLogDesc, LogSize, &LogBlock );
  3032. if ( !NT_SUCCESS(Status) ) {
  3033. goto Cleanup;
  3034. }
  3035. AllocatedChangeLogEntry = ((LPBYTE)LogBlock) + sizeof(CHANGELOG_BLOCK_HEADER);
  3036. RtlCopyMemory( AllocatedChangeLogEntry, ChangeLogEntry, sizeof(CHANGELOG_ENTRY) );
  3037. //
  3038. // copy variable fields
  3039. //
  3040. if( ChangeLogEntry->Flags & CHANGELOG_SID_SPECIFIED ) {
  3041. RtlCopyMemory( AllocatedChangeLogEntry + sizeof(CHANGELOG_ENTRY),
  3042. ObjectSid,
  3043. RtlLengthSid( ObjectSid ) );
  3044. } else if( ChangeLogEntry->Flags & CHANGELOG_NAME_SPECIFIED ) {
  3045. if ( ObjectName != NULL ) {
  3046. RtlCopyMemory( AllocatedChangeLogEntry + sizeof(CHANGELOG_ENTRY),
  3047. ObjectName->Buffer,
  3048. ObjectName->Length );
  3049. //
  3050. // terminate unicode string
  3051. //
  3052. *(WCHAR *)(AllocatedChangeLogEntry + sizeof(CHANGELOG_ENTRY) +
  3053. ObjectName->Length) = 0;
  3054. }
  3055. }
  3056. //
  3057. // Be verbose
  3058. //
  3059. #if NETLOGONDBG
  3060. PrintChangeLogEntry( (PCHANGELOG_ENTRY)AllocatedChangeLogEntry );
  3061. #endif // NETLOGONDBG
  3062. //
  3063. // Write the cache entry to the file.
  3064. //
  3065. // Actually, write this entry plus the header and trailer of the free
  3066. // block that follows. If the free block is huge, write the free
  3067. // block trailer separately.
  3068. //
  3069. FreeBlock =
  3070. (PCHANGELOG_BLOCK_HEADER)((LPBYTE)LogBlock + LogBlock->BlockSize);
  3071. if ( FreeBlock->BlockSize >= 4096 ) {
  3072. Status = NlWriteChangeLogBytes(
  3073. ChangeLogDesc,
  3074. (LPBYTE)LogBlock,
  3075. LogBlock->BlockSize + sizeof(CHANGELOG_BLOCK_HEADER),
  3076. FlushIt );
  3077. if ( NT_SUCCESS(Status) ) {
  3078. Status = NlWriteChangeLogBytes(
  3079. ChangeLogDesc,
  3080. (LPBYTE)ChangeLogBlockTrailer(FreeBlock),
  3081. sizeof(CHANGELOG_BLOCK_TRAILER),
  3082. FlushIt );
  3083. }
  3084. } else {
  3085. Status = NlWriteChangeLogBytes(
  3086. ChangeLogDesc,
  3087. (LPBYTE)LogBlock,
  3088. LogBlock->BlockSize + FreeBlock->BlockSize,
  3089. FlushIt );
  3090. }
  3091. //
  3092. // Done.
  3093. //
  3094. ChangeLogDesc->SerialNumber[ChangeLogEntry->DBIndex] = ChangeLogEntry->SerialNumber;
  3095. ChangeLogDesc->EntryCount[ChangeLogEntry->DBIndex] ++;
  3096. //
  3097. // Validate the list before returning from here.
  3098. //
  3099. Cleanup:
  3100. #if DBG
  3101. NlAssert( ValidateList( ChangeLogDesc, FALSE) );
  3102. #endif // DBG
  3103. UNLOCK_CHANGELOG();
  3104. return Status;
  3105. }
  3106. NTSTATUS
  3107. NlOpenChangeLogFile(
  3108. IN LPWSTR ChangeLogFileName,
  3109. OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  3110. IN BOOLEAN ReadOnly
  3111. )
  3112. /*++
  3113. Routine Description:
  3114. Open the change log file (netlogon.chg) for reading or writing one or
  3115. more records. Create this file if it does not exist or is out of
  3116. sync with the SAM database (see note below).
  3117. This file must be opened for R/W (deny-none share mode) at the time
  3118. the cache is initialized. If the file already exists when NETLOGON
  3119. service started, its contents will be cached in its entirety
  3120. provided the last change log record bears the same serial number as
  3121. the serial number field in SAM database else this file will be
  3122. removed and a new one created. If the change log file did not exist
  3123. then it will be created.
  3124. NOTE: This function must be called with the change log locked.
  3125. Arguments:
  3126. ChangeLogFileName - Name of the changelog file to open.
  3127. ChangeLogDesc -- On success, returns a description of the Changelog buffer
  3128. being used
  3129. ReadOnly -- True if the file should be openned read only.
  3130. Return Value:
  3131. NT Status code
  3132. --*/
  3133. {
  3134. DWORD WinError;
  3135. DWORD BytesRead;
  3136. DWORD MinChangeLogSize;
  3137. //
  3138. // Open change log file if exists
  3139. //
  3140. ChangeLogDesc->FileHandle = CreateFileW(
  3141. ChangeLogFileName,
  3142. ReadOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE),
  3143. ReadOnly ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : FILE_SHARE_READ, // allow backups and debugging
  3144. NULL, // Supply better security ??
  3145. OPEN_EXISTING, // Only open it if it exists
  3146. FILE_ATTRIBUTE_NORMAL,
  3147. NULL ); // No template
  3148. if ( ChangeLogDesc->FileHandle == INVALID_HANDLE_VALUE) {
  3149. WinError = GetLastError();
  3150. NlPrint(( NL_CRITICAL,
  3151. FORMAT_LPWSTR ": Unable to open. %ld\n",
  3152. ChangeLogFileName,
  3153. WinError ));
  3154. goto Cleanup;
  3155. }
  3156. //
  3157. // Get the size of the file.
  3158. //
  3159. ChangeLogDesc->BufferSize = GetFileSize( ChangeLogDesc->FileHandle, NULL );
  3160. if ( ChangeLogDesc->BufferSize == 0xFFFFFFFF ) {
  3161. WinError = GetLastError();
  3162. NlPrint((NL_CRITICAL,
  3163. "%ws: Unable to GetFileSize: %ld \n",
  3164. ChangeLogFileName,
  3165. WinError));
  3166. goto Cleanup;
  3167. }
  3168. // ?? consider aligning to ALIGN_WORST
  3169. MinChangeLogSize = MIN_CHANGELOGSIZE;
  3170. if ( ChangeLogDesc->BufferSize < MinChangeLogSize ||
  3171. ChangeLogDesc->BufferSize > MAX_CHANGELOGSIZE ) {
  3172. WinError = ERROR_INTERNAL_DB_CORRUPTION;
  3173. NlPrint((NL_CRITICAL, FORMAT_LPWSTR ": Changelog size is invalid. %ld.\n",
  3174. ChangeLogFileName,
  3175. ChangeLogDesc->BufferSize ));
  3176. goto Cleanup;
  3177. }
  3178. //
  3179. // Allocate and initialize the change log cache.
  3180. //
  3181. ChangeLogDesc->Buffer = NetpMemoryAllocate( ChangeLogDesc->BufferSize );
  3182. if (ChangeLogDesc->Buffer == NULL) {
  3183. NlPrint((NL_CRITICAL, FORMAT_LPWSTR ": Cannot allocate Changelog buffer. %ld.\n",
  3184. ChangeLogFileName,
  3185. ChangeLogDesc->BufferSize ));
  3186. WinError = ERROR_NOT_ENOUGH_MEMORY;
  3187. goto Cleanup;
  3188. }
  3189. RtlZeroMemory(ChangeLogDesc->Buffer, ChangeLogDesc->BufferSize);
  3190. //
  3191. // Check the signature at the front of the change log.
  3192. //
  3193. // It won't be there if we just created the file.
  3194. //
  3195. if ( !ReadFile( ChangeLogDesc->FileHandle,
  3196. ChangeLogDesc->Buffer,
  3197. ChangeLogDesc->BufferSize,
  3198. &BytesRead,
  3199. NULL ) ) { // Not Overlapped
  3200. WinError = GetLastError();
  3201. NlPrint(( NL_CRITICAL,
  3202. FORMAT_LPWSTR ": Unable to read from changelog file. %ld\n",
  3203. ChangeLogFileName,
  3204. WinError ));
  3205. goto Cleanup;
  3206. }
  3207. if ( BytesRead != ChangeLogDesc->BufferSize ) {
  3208. WinError = ERROR_INTERNAL_DB_CORRUPTION;
  3209. NlPrint(( NL_CRITICAL,
  3210. FORMAT_LPWSTR ": Couldn't read entire file. %ld\n",
  3211. ChangeLogFileName,
  3212. WinError ));
  3213. goto Cleanup;
  3214. }
  3215. if ( strncmp((PCHAR)ChangeLogDesc->Buffer,
  3216. CHANGELOG_SIG, sizeof(CHANGELOG_SIG)) == 0) {
  3217. ChangeLogDesc->Version3 = FALSE;
  3218. } else if ( strncmp((PCHAR)ChangeLogDesc->Buffer,
  3219. CHANGELOG_SIG_V3, sizeof(CHANGELOG_SIG_V3)) == 0) {
  3220. ChangeLogDesc->Version3 = TRUE;
  3221. } else {
  3222. WinError = ERROR_INTERNAL_ERROR;
  3223. NlPrint(( NL_CRITICAL,
  3224. FORMAT_LPWSTR ": Invalid signature. %ld\n",
  3225. ChangeLogFileName,
  3226. WinError ));
  3227. goto Cleanup;
  3228. }
  3229. //
  3230. // Find the Head and Tail pointers of the circular log.
  3231. //
  3232. if( !InitChangeLogHeadAndTail( ChangeLogDesc, FALSE ) ) {
  3233. WinError = ERROR_INTERNAL_DB_CORRUPTION;
  3234. NlPrint(( NL_CRITICAL,
  3235. FORMAT_LPWSTR ": couldn't find head/tail. %ld\n",
  3236. ChangeLogFileName,
  3237. WinError ));
  3238. goto Cleanup;
  3239. }
  3240. WinError = NO_ERROR;
  3241. //
  3242. // Free any resources on error.
  3243. //
  3244. Cleanup:
  3245. if ( WinError != NO_ERROR ) {
  3246. NlCloseChangeLogFile( ChangeLogDesc );
  3247. } else {
  3248. NlPrint((NL_CHANGELOG, "%ws: Changelog successfully opened.\n",
  3249. ChangeLogFileName ));
  3250. }
  3251. return NetpApiStatusToNtStatus(WinError);
  3252. }
  3253. VOID
  3254. NlCloseChangeLogFile(
  3255. IN PCHANGELOG_DESCRIPTOR ChangeLogDesc
  3256. )
  3257. /*++
  3258. Routine Description:
  3259. This function closes the change log file and frees up the resources
  3260. consumed by the change log desriptor.
  3261. Arguments:
  3262. ChangeLogDesc -- Description of the Changelog buffer being used
  3263. Return Value:
  3264. NT Status code
  3265. --*/
  3266. {
  3267. LOCK_CHANGELOG();
  3268. NlPrint((NL_CHANGELOG, "%s log closed.\n",
  3269. ChangeLogDesc->TempLog ? "TempChange" : "Change" ));
  3270. //
  3271. // free up the change log cache.
  3272. //
  3273. if ( ChangeLogDesc->Buffer != NULL ) {
  3274. NetpMemoryFree( ChangeLogDesc->Buffer );
  3275. ChangeLogDesc->Buffer = NULL;
  3276. }
  3277. ChangeLogDesc->Head = NULL;
  3278. ChangeLogDesc->Tail = NULL;
  3279. ChangeLogDesc->FirstBlock = NULL;
  3280. ChangeLogDesc->BufferEnd = NULL;
  3281. ChangeLogDesc->LastDirtyByte = 0;
  3282. ChangeLogDesc->FirstDirtyByte = 0;
  3283. //
  3284. // Close the change log file
  3285. //
  3286. if ( ChangeLogDesc->FileHandle != INVALID_HANDLE_VALUE ) {
  3287. CloseHandle( ChangeLogDesc->FileHandle );
  3288. ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
  3289. }
  3290. UNLOCK_CHANGELOG();
  3291. return;
  3292. }
  3293. NTSTATUS
  3294. NlResizeChangeLogFile(
  3295. IN OUT PCHANGELOG_DESCRIPTOR ChangeLogDesc,
  3296. IN DWORD NewChangeLogSize
  3297. )
  3298. /*++
  3299. Routine Description:
  3300. The buffer described by ChageLogDesc is converted to
  3301. the size requested by NewChangeLogSize and is converted from any
  3302. old format change log to the latest format.
  3303. NOTE: This function must be called with the change log locked.
  3304. Arguments:
  3305. ChangeLogDesc -- a description of the Changelog buffer.
  3306. NewChangeLogSize -- Size (in bytes) of the new change log.
  3307. Return Value:
  3308. NT Status code
  3309. On error, the ChangeLogDesc will still be intact. Merely the size
  3310. changes will not have happened
  3311. --*/
  3312. {
  3313. CHANGELOG_DESCRIPTOR OutChangeLogDesc;
  3314. NTSTATUS Status;
  3315. //
  3316. // If the current buffer is perfect,
  3317. // just use it.
  3318. //
  3319. if ( !ChangeLogDesc->Version3 &&
  3320. ChangeLogDesc->BufferSize == NewChangeLogSize ) {
  3321. return STATUS_SUCCESS;
  3322. }
  3323. //
  3324. // Initialize the template change log descriptor
  3325. //
  3326. InitChangeLogDesc( &OutChangeLogDesc );
  3327. //
  3328. // Close the file so we can resize it.
  3329. //
  3330. if ( ChangeLogDesc->FileHandle != INVALID_HANDLE_VALUE ) {
  3331. CloseHandle( ChangeLogDesc->FileHandle );
  3332. ChangeLogDesc->FileHandle = INVALID_HANDLE_VALUE;
  3333. }
  3334. //
  3335. // Start with a newly initialized change log,
  3336. //
  3337. Status = NlResetChangeLog( &OutChangeLogDesc, NewChangeLogSize );
  3338. if ( !NT_SUCCESS(Status)) {
  3339. return Status;
  3340. }
  3341. //
  3342. // We're done if the old change log is empty.
  3343. //
  3344. if ( !ChangeLogIsEmpty(ChangeLogDesc) ) {
  3345. //
  3346. // Loop through the old change log copying it to the new changelog,
  3347. //
  3348. PCHANGELOG_ENTRY SourceChangeLogEntry = (PCHANGELOG_ENTRY)
  3349. (ChangeLogDesc->Head + 1);
  3350. do {
  3351. Status = NlCopyChangeLogEntry( ChangeLogDesc->Version3,
  3352. SourceChangeLogEntry,
  3353. &OutChangeLogDesc );
  3354. if ( !NT_SUCCESS(Status) ) {
  3355. NlCloseChangeLogFile( &OutChangeLogDesc );
  3356. return Status;
  3357. }
  3358. } while ( (SourceChangeLogEntry =
  3359. NlMoveToNextChangeLogEntry( ChangeLogDesc, SourceChangeLogEntry )) != NULL );
  3360. //
  3361. // Flsuh all the changes to the change log file now.
  3362. //
  3363. Status = NlFlushChangeLog( &OutChangeLogDesc );
  3364. if ( !NT_SUCCESS(Status) ) {
  3365. NlCloseChangeLogFile( &OutChangeLogDesc );
  3366. return Status;
  3367. }
  3368. }
  3369. //
  3370. // Free the old change log buffer.
  3371. //
  3372. NlCloseChangeLogFile( ChangeLogDesc );
  3373. //
  3374. // Copy the new descriptor over the old descriptor
  3375. //
  3376. *ChangeLogDesc = OutChangeLogDesc;
  3377. return STATUS_SUCCESS;
  3378. }
  3379. #if NETLOGONDBG
  3380. DWORD
  3381. NlBackupChangeLogFile(
  3382. )
  3383. /*++
  3384. Routine Description:
  3385. Backup change log content. Since the cache and the change log file
  3386. content are identical, write cache content to the backup file.
  3387. Arguments:
  3388. None.
  3389. Return Value:
  3390. STATUS_SUCCESS - The Service completed successfully.
  3391. --*/
  3392. {
  3393. HANDLE BackupChangeLogHandle;
  3394. WCHAR BackupChangelogFile[MAX_PATH+CHANGELOG_FILE_POSTFIX_LENGTH+1];
  3395. DWORD WinError;
  3396. if( NlGlobalChangeLogFilePrefix[0] == L'\0' ) {
  3397. return ERROR_FILE_NOT_FOUND;
  3398. }
  3399. //
  3400. // make backup file name.
  3401. //
  3402. wcscpy( BackupChangelogFile, NlGlobalChangeLogFilePrefix );
  3403. wcscat( BackupChangelogFile, BACKUP_CHANGELOG_FILE_POSTFIX );
  3404. //
  3405. // Create change log file. If it exists already then truncate it.
  3406. //
  3407. // Note : if a valid change log file exists on the system, then we
  3408. // would have opened at initialization time.
  3409. //
  3410. BackupChangeLogHandle = CreateFileW(
  3411. BackupChangelogFile,
  3412. GENERIC_READ | GENERIC_WRITE,
  3413. FILE_SHARE_READ, // allow backups and debugging
  3414. NULL, // Supply better security ??
  3415. CREATE_ALWAYS, // Overwrites always
  3416. FILE_ATTRIBUTE_NORMAL,
  3417. NULL ); // No template
  3418. if (BackupChangeLogHandle == INVALID_HANDLE_VALUE) {
  3419. NlPrint((NL_CRITICAL,"Unable to create backup changelog file "
  3420. "WinError = %ld \n", WinError = GetLastError() ));
  3421. return WinError;
  3422. }
  3423. //
  3424. // Write cache in changelog file if the cache is valid.
  3425. //
  3426. if( NlGlobalChangeLogDesc.Buffer != NULL ) {
  3427. OVERLAPPED Overlapped;
  3428. DWORD BytesWritten;
  3429. //
  3430. // Seek to appropriate offset in the file.
  3431. //
  3432. RtlZeroMemory( &Overlapped, sizeof(Overlapped) );
  3433. LOCK_CHANGELOG();
  3434. if ( !WriteFile( BackupChangeLogHandle,
  3435. NlGlobalChangeLogDesc.Buffer,
  3436. NlGlobalChangeLogDesc.BufferSize,
  3437. &BytesWritten,
  3438. &Overlapped ) ) {
  3439. UNLOCK_CHANGELOG();
  3440. NlPrint((NL_CRITICAL, "Write to Backup ChangeLog failed %ld\n",
  3441. WinError = GetLastError() ));
  3442. goto Cleanup;
  3443. }
  3444. UNLOCK_CHANGELOG();
  3445. //
  3446. // Ensure all the bytes made it.
  3447. //
  3448. if ( BytesWritten != NlGlobalChangeLogDesc.BufferSize ) {
  3449. NlPrint((NL_CRITICAL,
  3450. "Write to Backup ChangeLog bad byte count %ld s.b. %ld\n",
  3451. BytesWritten,
  3452. NlGlobalChangeLogDesc.BufferSize ));
  3453. goto Cleanup;
  3454. }
  3455. }
  3456. Cleanup:
  3457. CloseHandle( BackupChangeLogHandle );
  3458. return ERROR_SUCCESS;
  3459. }
  3460. #endif // NETLOGONDBG