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.

1057 lines
32 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+-------------------------------------------------------------------------
  3. //
  4. // Microsoft Windows
  5. //
  6. // File: log.cxx
  7. //
  8. // Contents: Implementation of Tracking (Workstation) Service log of moves.
  9. //
  10. // Classes: CLog
  11. //
  12. // Functions:
  13. //
  14. // Notes: The log is composed of a header and a linked-list of move
  15. // notification entries. This structure is provided by the
  16. // CLogFile class.
  17. //
  18. //--------------------------------------------------------------------------
  19. #include "pch.cxx"
  20. #pragma hdrstop
  21. #include "trkwks.hxx"
  22. //+----------------------------------------------------------------------------
  23. //
  24. // Method: Initialize
  25. //
  26. // Synopsis: Initialize a CLog object.
  27. //
  28. // Arguments: [pLogCallback] (in)
  29. // A PLogCallback object, which we'll call when we have new
  30. // data.
  31. // [pcTrkWksConfiguration] (in)
  32. // Configuration parameters for the log.
  33. // [pcLogFile] (in)
  34. // The object representing the log file.
  35. //
  36. // Returns: None
  37. //
  38. //+----------------------------------------------------------------------------
  39. void
  40. CLog::Initialize( PLogCallback *pLogCallback,
  41. const CTrkWksConfiguration *pcTrkWksConfiguration,
  42. CLogFile *pcLogFile )
  43. {
  44. LogInfo loginfo;
  45. // Save the inputs
  46. TrkAssert( NULL != pcLogFile || NULL != _pcLogFile );
  47. if( NULL != pcLogFile )
  48. _pcLogFile = pcLogFile;
  49. TrkAssert( NULL != pcTrkWksConfiguration || NULL != _pcTrkWksConfiguration );
  50. if( NULL != pcTrkWksConfiguration )
  51. _pcTrkWksConfiguration = pcTrkWksConfiguration;
  52. TrkAssert( NULL != pLogCallback || NULL != _pLogCallback );
  53. if( NULL != pLogCallback )
  54. _pLogCallback = pLogCallback;
  55. // Read the log info from the log header.
  56. _pcLogFile->ReadExtendedHeader( CLOG_LOGINFO_START, &loginfo, CLOG_LOGINFO_LENGTH );
  57. // If the log hadn't been shut down properly, it's been fixed by now, but
  58. // we can't trust the loginfo we just read from the header. We also
  59. // can't trust it if it doesn't make sense. So if for some reason we
  60. // can't trust it, we'll recalculate it (this can be slow, though).
  61. if( !_pcLogFile->IsShutdown() || loginfo.ilogStart == loginfo.ilogEnd )
  62. {
  63. _fDirty = TRUE;
  64. loginfo = QueryLogInfo();
  65. }
  66. // Save the now-good information.
  67. _loginfo = loginfo;
  68. } // CLog::Initialize()
  69. //+----------------------------------------------------------------------------
  70. //
  71. // Method: QueryLogInfo
  72. //
  73. // Synopsis: Read the log entries and determine the indices and sequence
  74. // numbers.
  75. //
  76. // Arguments: None
  77. //
  78. // Returns: A LogInfo structure
  79. //
  80. //+----------------------------------------------------------------------------
  81. LogInfo
  82. CLog::QueryLogInfo()
  83. {
  84. SequenceNumber seqMin, seqMax;
  85. ULONG cEntries;
  86. LogIndex ilogMin, ilogMax, ilogEntry;
  87. LogInfo loginfo;
  88. LogMoveNotification lmn;
  89. BOOL fLogEmpty = TRUE;
  90. LogEntryHeader entryheader;
  91. TrkLog(( TRKDBG_LOG, TEXT("Reading log to determine correct indices") ));
  92. // ------------
  93. // Scan the log
  94. // ------------
  95. seqMin = 0;
  96. seqMax = 0;
  97. cEntries = _pcLogFile->NumEntriesInFile();
  98. ilogMin = 0;
  99. ilogMax = cEntries - 1;
  100. // Scan the log and look at the sequence numbers to find
  101. // the start and end indices.
  102. for( ilogEntry = 0; ilogEntry < cEntries; ilogEntry++ )
  103. {
  104. _pcLogFile->ReadMoveNotification( ilogEntry, &lmn );
  105. if( LE_TYPE_MOVE == lmn.type )
  106. {
  107. SequenceNumber seq = lmn.seq;
  108. // If this is the first move notification that we've
  109. // found, then it is currently both the min and the max.
  110. if( fLogEmpty )
  111. {
  112. fLogEmpty = FALSE;
  113. seqMin = seqMax = seq;
  114. ilogMin = ilogMax = seq;
  115. }
  116. // If this isn't the first entry we've found, then see
  117. // if it is a new min or max.
  118. else
  119. {
  120. if( seq <= seqMin )
  121. {
  122. seqMin = seq;
  123. ilogMin = ilogEntry;
  124. }
  125. else if( seq >= seqMax )
  126. {
  127. seqMax = seq;
  128. ilogMax = ilogEntry;
  129. }
  130. }
  131. } // if( LE_TYPE_MOVE == _pcLogFile->ReadMoveNotification( ilogEntry )->type )
  132. } // for( ilogEntry = 0; ilogEntry < cEntries; ilogEntry++ )
  133. // -------------------------------
  134. // Determine the log indices, etc.
  135. // -------------------------------
  136. // Were there any entries in the log?
  137. if( fLogEmpty )
  138. {
  139. // No, the log is empty.
  140. loginfo.ilogStart = loginfo.ilogWrite = 0;
  141. loginfo.ilogLast = loginfo.ilogEnd = cEntries - 1;
  142. }
  143. else
  144. {
  145. // Yes, the log is non-empty.
  146. // Point the start index to the oldest move in the log.
  147. loginfo.ilogStart = ilogMin;
  148. // Point the last index to the oldest move in the log,
  149. // and point the write index to the entry after that
  150. // (which is the first available entry).
  151. loginfo.ilogLast = loginfo.ilogWrite = ilogMax;
  152. _pcLogFile->AdjustLogIndex( &loginfo.ilogWrite, 1 );
  153. // The write & start indices should only be the same
  154. // in an empty log. We know we're not empty at this point,
  155. // so if they're the same, then the start index must have
  156. // actually advanced (otherwise, the write index wouldn't be
  157. // allowed to be here). So we advance the start index.
  158. if( loginfo.ilogWrite == loginfo.ilogStart )
  159. {
  160. _pcLogFile->AdjustLogIndex( &loginfo.ilogStart, 1 );
  161. }
  162. } // if( fLogEmpty ) ... else
  163. // The end if the log is just before the start in the circular list.
  164. loginfo.ilogEnd = loginfo.ilogStart;
  165. _pcLogFile->AdjustLogIndex( &loginfo.ilogEnd, -1 );
  166. // The read index and next available
  167. // sequence number are stored in the last entry header.
  168. entryheader = _pcLogFile->ReadEntryHeader( loginfo.ilogLast );
  169. loginfo.ilogRead = entryheader.ilogRead;
  170. loginfo.seqNext = entryheader.seq;
  171. // The sequence number of the entry last read is one below the sequence number
  172. // of the entry currently at the read pointer. If everything is read
  173. // (the read pointer is beyond the last entry) or if the entry at the
  174. // read pointer is invalid, then we'll assume that the last read seq
  175. // number is seqNext-1.
  176. _pcLogFile->ReadMoveNotification( loginfo.ilogRead, &lmn );
  177. loginfo.seqLastRead = ( loginfo.ilogWrite != loginfo.ilogRead && LE_TYPE_MOVE == lmn.type )
  178. ? lmn.seq - 1 : loginfo.seqNext-1;
  179. TrkAssert( seqMax + 1 == loginfo.seqNext || 0 == loginfo.seqNext );
  180. TrkAssert( loginfo.seqLastRead < loginfo.seqNext );
  181. return( loginfo );
  182. } // CLog::QueryLogInfo()
  183. //+----------------------------------------------------------------------------
  184. //
  185. // Method: GenerateDefaultLogInfo
  186. //
  187. // Synopsis: Calculates the default _loginfo structure, based only on
  188. // the last index. This requires no calls to CLogFile.
  189. //
  190. // Arguments: [ilogEnd] (in)
  191. // The index of the last entry in the logfile.
  192. //
  193. // Returns: None
  194. //
  195. //+----------------------------------------------------------------------------
  196. void
  197. CLog::GenerateDefaultLogInfo( LogIndex ilogEnd )
  198. {
  199. SetDirty( TRUE ); // Must be called before changing _loginfo
  200. _loginfo.ilogStart = _loginfo.ilogWrite = _loginfo.ilogRead = 0;
  201. _loginfo.ilogLast = _loginfo.ilogEnd = ilogEnd;
  202. _loginfo.seqNext = 0;
  203. _loginfo.seqLastRead = _loginfo.seqNext - 1;
  204. }
  205. //+----------------------------------------------------------------------------
  206. //
  207. // Method: Flush
  208. //
  209. // Synopsis: Write the _loginfo structure to the CLogFile.
  210. //
  211. // Arguments: None
  212. //
  213. // Returns: None
  214. //
  215. //+----------------------------------------------------------------------------
  216. void
  217. CLog::Flush( )
  218. {
  219. if( _fDirty )
  220. _pcLogFile->WriteExtendedHeader( CLOG_LOGINFO_START, &_loginfo, CLOG_LOGINFO_LENGTH );
  221. SetDirty( FALSE );
  222. }
  223. //+----------------------------------------------------------------------------
  224. //
  225. // Method: ExpandLog
  226. //
  227. // Synopsis: Grow the log file, initialize the new entries, and update
  228. // our indices. We determine how much to grow based on
  229. // configuration parameters in CTrkWksConfiguration.
  230. //
  231. // Arguments: None.
  232. //
  233. // Returns: None.
  234. //
  235. //+----------------------------------------------------------------------------
  236. void
  237. CLog::ExpandLog()
  238. {
  239. TrkAssert( !_pcLogFile->IsMaxSize() );
  240. TrkAssert( IsFull() );
  241. SetDirty( TRUE ); // Must be called before changing _loginfo
  242. // Grow the file, initialize the new log entries, and link the new
  243. // entries into the existing linked list. We only need to tell
  244. // the CLogFile where the start of the circular linked-list is.
  245. _pcLogFile->Expand( _loginfo.ilogStart );
  246. // Update the end pointer.
  247. _loginfo.ilogEnd = _loginfo.ilogStart;
  248. _pcLogFile->AdjustLogIndex( &_loginfo.ilogEnd, -1 );
  249. } // CLog::Expand
  250. //+----------------------------------------------------------------------------
  251. //
  252. // Method: Read
  253. //
  254. // Synopsis: Read zero or more entries from the log, starting at the
  255. // Read index. Read until we reach the end of the data in
  256. // the log, or until we've read as many as the caller
  257. // requested.
  258. //
  259. // Note that we don't update the read index after this read,
  260. // the caller must call Seek to accomplish this. This was done
  261. // so that if the caller encountered an error after the Read,
  262. // the log would still be unchanged for a retry.
  263. //
  264. // Arguments: [pNotifications] (in/out)
  265. // Receives the move notification records.
  266. // [pseqFirst] (out)
  267. // The sequence number of the first notification returned.
  268. // [pcRead] (in/out)
  269. // (in) the number of notifications desired
  270. // (out) the number of notifications actually read
  271. // If the number read is less than the number requested,
  272. // the caller may assume that there are no more entries
  273. // to read.
  274. //
  275. // Returns: None
  276. //
  277. //+----------------------------------------------------------------------------
  278. void
  279. CLog::Read(CObjId rgobjidCurrent[],
  280. CDomainRelativeObjId rgdroidBirth[],
  281. CDomainRelativeObjId rgdroidNew[],
  282. SequenceNumber *pseqFirst,
  283. IN OUT ULONG *pcRead)
  284. {
  285. // --------------
  286. // Initialization
  287. // --------------
  288. LogIndex ilogEntry;
  289. ULONG cRead = 0;
  290. ULONG iRead = 0;
  291. SequenceNumber seqExpected = 0;
  292. ilogEntry = _loginfo.ilogRead;
  293. // ----------------
  294. // Read the entries
  295. // ----------------
  296. // We can NOOP if the call request no entries, or if there
  297. // are no entries in the log, or if all the entries have
  298. // been read already.
  299. if( *pcRead != 0 && !IsEmpty() && !IsRead() )
  300. {
  301. LogMoveNotification lmn;
  302. // There are entries which we can read.
  303. // Save the sequence number of the first entry that
  304. // we'll return to the caller.
  305. _pcLogFile->ReadMoveNotification( ilogEntry, &lmn );
  306. *pseqFirst = lmn.seq;
  307. seqExpected = lmn.seq;
  308. // Read the entries from the log in order, validating
  309. // the sequence numbers as we go.
  310. do
  311. {
  312. // Copy the move information into the caller's buffer.
  313. // ReadMoveNotification doesn't make the CLogFile dirty.
  314. _pcLogFile->ReadMoveNotification( ilogEntry, &lmn );
  315. TrkAssert( seqExpected == lmn.seq );
  316. if( seqExpected != lmn.seq )
  317. {
  318. TrkLog(( TRKDBG_ERROR, TEXT("Invalid sequence numbers reading log (%d, %d)"),
  319. seqExpected, lmn.seq ));
  320. TrkRaiseException( TRK_E_CORRUPT_LOG );
  321. }
  322. seqExpected++;
  323. rgobjidCurrent[iRead] = lmn.objidCurrent;
  324. rgdroidNew[iRead] = lmn.droidNew;
  325. rgdroidBirth[iRead] = lmn.droidBirth;
  326. cRead++;
  327. iRead++;
  328. // Move on to the next entry.
  329. _pcLogFile->AdjustLogIndex( &ilogEntry, 1 );
  330. // Continue as long as there's still room in the caller's buffer
  331. // and we haven't reached the last entry.
  332. } while ( cRead < *pcRead && ilogEntry != _loginfo.ilogWrite );
  333. }
  334. *pcRead = cRead;
  335. } // CLog:Read()
  336. //+----------------------------------------------------------------------------
  337. //
  338. // CLog::DoSearch
  339. //
  340. // This is a private worker method that searches the log, either for a
  341. // sequence number, or an object ID (which to use is determined by the
  342. // fSearchUsingSeq parameter).
  343. //
  344. // The log entry data and index are returned.
  345. //
  346. //+----------------------------------------------------------------------------
  347. // NOTE! *piFound is not modified if Search returns FALSE
  348. BOOL
  349. CLog::DoSearch( BOOL fSearchUsingSeq,
  350. SequenceNumber seqSearch, // Use this if fSearchUsingSeq
  351. const CObjId &objidCurrent, // Use this if !fSearchUsingSeq
  352. ULONG *piFound,
  353. CDomainRelativeObjId *pdroidNew,
  354. CMachineId *pmcidNew,
  355. CDomainRelativeObjId *pdroidBirth )
  356. {
  357. BOOL fFound = FALSE;
  358. BOOL fFirstPass = TRUE;
  359. SequenceNumber seqPrevious = 0;
  360. #if DBG
  361. LONG l = GetTickCount();
  362. #endif
  363. // Only bother to look if there's entries in the log.
  364. if (!IsEmpty())
  365. {
  366. // Determine the max entries in the log so that we can
  367. // detect if we're in an infinite loop.
  368. ULONG cEntriesMax = _pcLogFile->NumEntriesInFile();
  369. ULONG cEntryCurrent = 0;
  370. LogIndex ilogSearch = _loginfo.ilogWrite;
  371. // Search from the end, until we find what we're looking for,
  372. // or we reach the beginning of the log.
  373. // It's important that we search backwards because of tunneling.
  374. // Here's the scenario ... An object is moved from machine A to
  375. // B, quickly back to A, and then to C. Since it reappeared on A
  376. // quickly after it first disappeared, tunneling will give it the
  377. // same Object ID that it had before. So when it moves to C,
  378. // we end up with two entries in the log for the object. We want
  379. // to search backwards so that we see the move to C, not the move
  380. // to B.
  381. while( !fFound && ilogSearch != _loginfo.ilogStart )
  382. {
  383. // Check to see if we're in an infinite loop.
  384. if( ++cEntryCurrent > cEntriesMax )
  385. {
  386. TrkLog((TRKDBG_ERROR, TEXT("Corrupt log file: cycle found during search")));
  387. TrkRaiseException( TRK_E_CORRUPT_LOG );
  388. }
  389. // Read the previous entry.
  390. _pcLogFile->AdjustLogIndex( &ilogSearch, -1 );
  391. LogMoveNotification lmnSearch;
  392. _pcLogFile->ReadMoveNotification( ilogSearch, &lmnSearch );
  393. // If this isn't a move entry, then there's nothing left to search.
  394. if( LE_TYPE_MOVE != lmnSearch.type )
  395. goto Exit;
  396. // Or, if this isn't the first pass, ensure that the sequence numbers
  397. // are consequtive.
  398. else if( !fFirstPass )
  399. {
  400. fFirstPass = FALSE;
  401. TrkAssert( seqPrevious - 1 == lmnSearch.seq );
  402. if( seqPrevious - 1 != lmnSearch.seq )
  403. {
  404. TrkLog(( TRKDBG_ERROR, TEXT("Corrupt log file: non-consequtive sequence numbers (%d %d)"),
  405. seqPrevious, lmnSearch.seq ));
  406. TrkRaiseException( TRK_E_CORRUPT_LOG );
  407. }
  408. }
  409. // Is this the entry we're looking for?
  410. if( fSearchUsingSeq && seqSearch == lmnSearch.seq
  411. ||
  412. !fSearchUsingSeq && objidCurrent == lmnSearch.objidCurrent )
  413. {
  414. if( NULL != piFound )
  415. *piFound = ilogSearch;
  416. if( NULL != pdroidNew )
  417. {
  418. *pdroidNew = lmnSearch.droidNew;
  419. *pmcidNew = lmnSearch.mcidNew;
  420. *pdroidBirth = lmnSearch.droidBirth;
  421. }
  422. fFound = TRUE;
  423. }
  424. } // while( !fFound && ilogSearch != _loginfo.ilogStart )
  425. } // if (!IsEmpty())
  426. Exit:
  427. return( fFound );
  428. } // CLog::DoSearch
  429. //+----------------------------------------------------------------------------
  430. //
  431. // Method: Search
  432. //
  433. // Synopsis: Search the log for an Object ID. Once found, return that
  434. // entry's LinkData and BirthID.
  435. //
  436. // Arguments: [droidCurrent] (in)
  437. // The ObjectID for which to search.
  438. // [pdroidNew] (out)
  439. // The entry's LinkData
  440. // [pdroidBirth] (out)
  441. // The entry's Birth ID.
  442. //
  443. // Returns: None
  444. //
  445. //+----------------------------------------------------------------------------
  446. BOOL
  447. CLog::Search( const CObjId &objidCurrent,
  448. CDomainRelativeObjId *pdroidNew,
  449. CMachineId *pmcidNew,
  450. CDomainRelativeObjId *pdroidBirth )
  451. {
  452. ULONG iFound;
  453. return DoSearch( FALSE, // => Use objidCurrent
  454. 0, // Therefore, we don't need a seq number
  455. objidCurrent,
  456. &iFound,
  457. pdroidNew,
  458. pmcidNew,
  459. pdroidBirth );
  460. } // CLog::Search( CDomainRelativeObjId& ...
  461. //+----------------------------------------------------------------------------
  462. //
  463. // Method: CLog::Search
  464. //
  465. // Synopsis: Search the log for the entry with a particular sequence
  466. // number.
  467. //
  468. // Arguments: [seqSearch] (in)
  469. // The sequence number for which to search.
  470. // [piFound] (out)
  471. // The index with this sequence number (if found).
  472. //
  473. // Returns: [BOOL]
  474. // TRUE if found, FALSE otherwise.
  475. //
  476. //+----------------------------------------------------------------------------
  477. BOOL
  478. CLog::Search( SequenceNumber seqSearch, ULONG *piFound )
  479. {
  480. CObjId oidNull;
  481. return DoSearch( TRUE, // => Use seqSearch
  482. seqSearch,
  483. oidNull, // We don't need to pass an objid
  484. piFound,
  485. // And we don't need out-droids & mcid.
  486. NULL, NULL, NULL );
  487. } // CLog::Search( SequenceNumber ...
  488. //+----------------------------------------------------------------------------
  489. //
  490. // Method: Append
  491. //
  492. // Synopsis: Add a move notification to the log. If the log is full,
  493. // either overwrite an old entry, or grow the log.
  494. //
  495. // Arguments: [droidCurrent] (in)
  496. // The link information of the file which was moved.
  497. // [droidNew] (in)
  498. // The link information of the new file.
  499. // [droidBirth] (in)
  500. // The Birth ID of the file.
  501. //
  502. // Returns: None
  503. //
  504. //+----------------------------------------------------------------------------
  505. // Perf optimization: Tell the caller if the log is now full. The service can then lazily
  506. // expand it, hopefully before the next move occurs.
  507. void
  508. CLog::Append(const CVolumeId &volidCurrent,
  509. const CObjId &objidCurrent,
  510. const CDomainRelativeObjId &droidNew,
  511. const CMachineId &mcidNew,
  512. const CDomainRelativeObjId &droidBirth)
  513. {
  514. LogMoveNotification lmnWrite;
  515. CFILETIME cftNow; // Defaults to current UTC
  516. BOOL fAdvanceStart = FALSE;
  517. LogEntryHeader entryheader;
  518. LogInfo loginfoZero;
  519. // -----------------
  520. // Handle a Full Log
  521. // -----------------
  522. if( IsFull() )
  523. {
  524. // Is the log already maxed? If so, we wrap.
  525. if( _pcLogFile->IsMaxSize() )
  526. {
  527. fAdvanceStart = TRUE;
  528. TrkLog(( TRKDBG_VOLUME, TEXT("Wrapping log") ));
  529. }
  530. // Otherwise, we'll handle it by growing the log file.
  531. else
  532. ExpandLog();
  533. }
  534. // -------------------------
  535. // Write the data to the log
  536. // -------------------------
  537. // Before anything else, we must mark ourselves dirty. If the logfile is
  538. // currently in the ProperShutdown state, this SetDirty call will take it
  539. // out of that state and do a flush.
  540. SetDirty( TRUE );
  541. // Mark our loginfo cache in the header as invalid,
  542. // in case we get pre-empted.
  543. memset( &loginfoZero, 0, sizeof(loginfoZero) );
  544. _pcLogFile->WriteExtendedHeader( CLOG_LOGINFO_START, &loginfoZero, CLOG_LOGINFO_LENGTH );
  545. // Collect the move-notification information
  546. memset( &lmnWrite, 0, sizeof(lmnWrite) );
  547. lmnWrite.seq = _loginfo.seqNext;
  548. lmnWrite.type = LE_TYPE_MOVE;
  549. lmnWrite.objidCurrent = objidCurrent;
  550. lmnWrite.droidNew = droidNew;
  551. lmnWrite.mcidNew = mcidNew;
  552. lmnWrite.droidBirth = droidBirth;
  553. lmnWrite.DateWritten = TrkTimeUnits( cftNow );
  554. // Collect the entry header information
  555. memset( &entryheader, 0, sizeof(entryheader) );
  556. entryheader.ilogRead = _loginfo.ilogRead;
  557. entryheader.seq = _loginfo.seqNext + 1; // Reflect that we'll increment after the write
  558. // Write everything to the log (this will do a flush). If this fails, it will raise.
  559. _pcLogFile->WriteMoveNotification( _loginfo.ilogWrite, lmnWrite, entryheader );
  560. // Update the sequence number and last & write indices now that we
  561. // know the write was successful (all the way to the disk).
  562. _loginfo.seqNext++;
  563. _loginfo.ilogLast = _loginfo.ilogWrite;
  564. _pcLogFile->AdjustLogIndex( &_loginfo.ilogWrite, 1 );
  565. // Do we need to advance the start pointer?
  566. // We save this for the end, because it may cause us to access
  567. // the disk.
  568. if( fAdvanceStart )
  569. {
  570. // We're about to advance the start index, and thus effectively
  571. // lose an entry. If the read index points to the same place,
  572. // then we should advance it as well.
  573. if( _loginfo.ilogStart == _loginfo.ilogRead )
  574. _pcLogFile->AdjustLogIndex( &_loginfo.ilogRead, 1 );
  575. // Advance the start/end indices.
  576. _pcLogFile->AdjustLogIndex( &_loginfo.ilogEnd, 1 );
  577. _pcLogFile->AdjustLogIndex( &_loginfo.ilogStart, 1 );
  578. }
  579. TrkLog(( TRKDBG_VOLUME, TEXT("Appended %s to log (seq=%d)"),
  580. (const TCHAR*)CDebugString(objidCurrent), lmnWrite.seq ));
  581. // Notify the callback object that there is data available.
  582. // Note: This must be the last operation of this method.
  583. _pLogCallback->OnEntriesAvailable();
  584. }
  585. //+----------------------------------------------------------------------------
  586. //
  587. // Method: Seek( SequenceNumber ...
  588. //
  589. // Synopsis: Moves the Read index to a the log entry with the specified
  590. // sequence number. If the seq number doesn't exist, we back
  591. // up to the start of the log.
  592. //
  593. // If this seek causes us to back up the Read index, we notify
  594. // the PLogCallback object, since we now have data available
  595. // to read.
  596. //
  597. // Arguments: [seqSeek] (in)
  598. // The sequence number to which to seek.
  599. //
  600. // Returns: [BOOL]
  601. // TRUE if the sequence number was found, FALSE otherwise.
  602. //
  603. //+----------------------------------------------------------------------------
  604. BOOL
  605. CLog::Seek( const SequenceNumber &seqSeek )
  606. {
  607. BOOL fFound = FALSE;
  608. SequenceNumber seqReadOriginal, seqReadNew;
  609. LogMoveNotification lmn;
  610. LogIndex ilogSearch = 0;
  611. // Are we seeking to the end of the log?
  612. if( seqSeek == _loginfo.seqNext )
  613. {
  614. // We found what we're looking for.
  615. fFound = TRUE;
  616. // Are we already at seqNext?
  617. if( !IsRead() )
  618. {
  619. // No, update the read index.
  620. SetDirty( TRUE );
  621. _loginfo.ilogRead = _loginfo.ilogWrite;
  622. }
  623. goto Exit;
  624. }
  625. // If the log is empty, then there's nothing we need do.
  626. if( IsEmpty() )
  627. goto Exit;
  628. // Or, if the caller wishes to seek beyond the end of our log, then
  629. // again there's nothing to do. This could be the case, for example,
  630. // if the log has been restored.
  631. if( seqSeek >= _loginfo.seqNext )
  632. goto Exit;
  633. // Keep track of the current seq number at the read pointer, so that
  634. // we can later tell if it's necessary to notify the client that
  635. // there is "new" data.
  636. if( IsRead() )
  637. {
  638. seqReadOriginal = _loginfo.seqNext;
  639. }
  640. else
  641. {
  642. _pcLogFile->ReadMoveNotification( _loginfo.ilogRead, &lmn );
  643. seqReadOriginal = lmn.seq;
  644. }
  645. // If this seq number is in the log, set the Read index
  646. // to it. Otherwise set it to the oldest entry in the
  647. // log (that's the best we can do).
  648. SetDirty( TRUE ); // Must be called before changing _loginfo
  649. if( fFound = Search( seqSeek, &ilogSearch ))
  650. _loginfo.ilogRead = ilogSearch;
  651. else
  652. _loginfo.ilogRead = _loginfo.ilogStart;
  653. // Calculate the sequence number of the entry now at the read pointer, and
  654. // use it to cache the seq number of the last-read entry (recall that
  655. // the read pointer points to the next entry to be read, not the
  656. // last entry read).
  657. if( IsRead() )
  658. {
  659. seqReadNew = _loginfo.seqNext;
  660. }
  661. else
  662. {
  663. _pcLogFile->ReadMoveNotification( _loginfo.ilogRead, &lmn );
  664. seqReadNew = lmn.seq;
  665. }
  666. _loginfo.seqLastRead = seqReadNew - 1; // We already set _fDirty
  667. // Update the entry headers with the new read pointer.
  668. WriteEntryHeader();
  669. // If we've backed up the read pointer, notify the registered callback.
  670. if ( seqReadOriginal > seqReadNew )
  671. {
  672. // Note: This must be the last operation of this method.
  673. _pLogCallback->OnEntriesAvailable();
  674. }
  675. // ----
  676. // Exit
  677. // ----
  678. Exit:
  679. return( fFound );
  680. } // CLog::Seek( SequenceNumber ...
  681. //+----------------------------------------------------------------------------
  682. //
  683. // Method: Seek( origin ...
  684. //
  685. // Synopsis: Move the read pointer relative to an origin (begin, current, end).
  686. //
  687. // There are two differences between a CLog seek and a file seek:
  688. // - If you seek from the beginning (SEEK_SET), and seek beyond the
  689. // end of the log, the pointer is wrapped, rather than growing
  690. // the log.
  691. // - If you seek from the current location (SEEK_CUR), and seek
  692. // beyond the end of the log, the log is not grown, and there
  693. // is no wrap, the index simply stops there (either at _loginfo.ilogWrite
  694. // or _loginfo.ilogStart).
  695. //
  696. // Arguments: [origin]
  697. // Must be either SEEK_SET or SEEK_CUR (there is currently no
  698. // support for SEEK_END).
  699. // [iSeek]
  700. // The amount to move relative to the origin.
  701. //
  702. // Returns: None
  703. //
  704. //+----------------------------------------------------------------------------
  705. void
  706. CLog::Seek( int origin, int iSeek )
  707. {
  708. SequenceNumber seqReadOriginal = 0, seqReadNew = 0;
  709. LogMoveNotification lmn;
  710. // Early exit if there's nothing to do
  711. if( IsEmpty() )
  712. goto Exit;
  713. // Keep track of where we are now, so that we can determine if
  714. // we've gone overall backwards or forwards.
  715. if( IsRead() )
  716. {
  717. seqReadOriginal = _loginfo.seqNext;
  718. }
  719. else
  720. {
  721. _pcLogFile->ReadMoveNotification( _loginfo.ilogRead, &lmn );
  722. seqReadOriginal = lmn.seq;
  723. }
  724. // Seek based on the origin.
  725. switch( origin )
  726. {
  727. case SEEK_SET:
  728. {
  729. // Advance from the start index.
  730. LogIndex ilogRead = _loginfo.ilogStart;
  731. _pcLogFile->AdjustLogIndex( &ilogRead, iSeek );
  732. SetDirty( TRUE ); // Must be called before changing _loginfo
  733. _loginfo.ilogRead = ilogRead;
  734. }
  735. break;
  736. case SEEK_CUR:
  737. {
  738. // Advance or retreat from the current read index.
  739. LogIndex ilogRead = _loginfo.ilogRead;
  740. _pcLogFile->AdjustLogIndex( &ilogRead, iSeek, CLogFile::ADJUST_WITHIN_LIMIT,
  741. iSeek >= 0 ? _loginfo.ilogWrite : _loginfo.ilogStart );
  742. SetDirty( TRUE ); // Must be called before changing _loginfo
  743. _loginfo.ilogRead = ilogRead;
  744. }
  745. break;
  746. default:
  747. TrkAssert( FALSE && TEXT("Unexpected origin in CLog::Seek") );
  748. break;
  749. }
  750. // Calculate the sequence number of the entry at the read pointer, and
  751. // use it to store the seq number of the last-read entry (recall that
  752. // the read pointer points to the next entry to be read, not the
  753. // last entry read).
  754. if( IsRead() )
  755. {
  756. seqReadNew = _loginfo.seqNext;
  757. }
  758. else
  759. {
  760. _pcLogFile->ReadMoveNotification( _loginfo.ilogRead, &lmn );
  761. seqReadNew = lmn.seq;
  762. }
  763. SetDirty( TRUE ); // Must be called before changing _loginfo
  764. _loginfo.seqLastRead = seqReadNew - 1;
  765. // Update the entry headers with the new read pointer.
  766. WriteEntryHeader();
  767. // If we've backed up the read pointer, notify the registered callback.
  768. if ( seqReadOriginal > seqReadNew )
  769. {
  770. // Note: This must be the last operation of this method.
  771. _pLogCallback->OnEntriesAvailable();
  772. }
  773. // ----
  774. // Exit
  775. // ----
  776. Exit:
  777. return;
  778. } // CLog::Seek( origin ...
  779. //+----------------------------------------------------------------------------
  780. //
  781. // Method: CLog::IsRead( LogIndex ) (private)
  782. //
  783. // Synopsis: Determine if the specified entry has been read. See also
  784. // the IsRead(void) overload, which checks to see if the whole
  785. // log has been read.
  786. //
  787. // Inputs: [ilog] (in)
  788. // The index in the log to be checked. It is assumed
  789. // that this index points to a valid move notification
  790. // entry.
  791. //
  792. // Outputs: [BOOL]
  793. // True if and only if the entry has been marked as read.
  794. //
  795. //+----------------------------------------------------------------------------
  796. BOOL
  797. CLog::IsRead( LogIndex ilog )
  798. {
  799. LogMoveNotification lmn;
  800. // Has the whole log been read?
  801. if( IsRead() )
  802. return( TRUE );
  803. // Or, has this entry been read?
  804. _pcLogFile->ReadMoveNotification( ilog, &lmn );
  805. if( _loginfo.seqLastRead >= lmn.seq )
  806. return( TRUE );
  807. // Otherwise, we know the entry hasn't been read.
  808. else
  809. return( FALSE );
  810. } // CLog::IsRead( LogIndex )