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.

1593 lines
51 KiB

  1. /*++
  2. Copyright (c) 1992-1997 Microsoft Corporation
  3. Module Name:
  4. logger.c
  5. Abstract:
  6. Provides the persistent log used by the cluster registry service
  7. This is a very simple logger that supports writing arbitrarily
  8. sized chunks of data in an atomic fashion.
  9. Author:
  10. John Vert (jvert) 15-Dec-1995
  11. Revision History:
  12. sunitas : added mount, scan, checkpointing, reset functionality.
  13. sunitas : added large record support
  14. --*/
  15. #include "service.h"
  16. #include "lmp.h"
  17. #include "clusudef.h"
  18. /****
  19. @doc EXTERNAL INTERFACES CLUSSVC LM
  20. ****/
  21. /****
  22. @func HLOG | LogCreate| Creates or opens a log file. If the file
  23. does not exist, it will be created. If the file already exists, and is
  24. a valid log file, it will be opened.
  25. @parm IN LPWSTR | lpFileName | Supplies the name of the log file to create or open.
  26. @parm IN DWORD | dwMaxFileSize | Supplies the maximum file size in bytes, must be
  27. greater than 8K and smaller than 4 gigabytes. If the file is exceeds this
  28. size, the reset function will be called. If 0, the maximum log file size limit
  29. is set to the default maximum size.
  30. @parm IN PLOG_GETCHECKPOINT_CALLBACK | CallbackRoutine | The callback routine that
  31. will provide a checkpoint file and the transaction associated with that checkpoint
  32. file when LogCheckPoint() is called for this log file. If this is NULL, then the checkpointing capabilities are
  33. not associated with the log file.
  34. @parm IN PVOID | pGetChkPtContext | Supplies an arbitrary context pointer, which will be
  35. passed to the CallbackRoutine.
  36. @parm IN BOOL | bForceReset | If true, this function creates an empty log file
  37. if the log file doesnt exist or if it is corrupt.
  38. @parm LSN | *LastLsn | If present, Returns the last LSN written to the log file.
  39. (NULL_LSN if the log file was created)
  40. @rdesc Returns a handle suitable for use in subsequent log calls. NUll in the case of an error.
  41. @xref <f LogClose>
  42. ****/
  43. HLOG
  44. LogCreate(
  45. IN LPWSTR lpFileName,
  46. IN DWORD dwMaxFileSize,
  47. IN PLOG_GETCHECKPOINT_CALLBACK CallbackRoutine,
  48. IN PVOID pGetChkPtContext,
  49. IN BOOL bForceReset,
  50. OPTIONAL OUT LSN *pLastLsn
  51. )
  52. {
  53. PLOG pLog;
  54. ClRtlLogPrint(LOG_NOISE,
  55. "[LM] LogCreate : Entry FileName=%1!ls! MaxFileSize=0x%2!08lx!\r\n",
  56. lpFileName,dwMaxFileSize);
  57. //create the log structure
  58. pLog = LogpCreate(lpFileName, dwMaxFileSize, CallbackRoutine,
  59. pGetChkPtContext, bForceReset, pLastLsn);
  60. if (pLog == NULL)
  61. goto FnExit;
  62. //create a timer for this log
  63. //SS:TODO?? - currently we have a single timer perfile
  64. //if that is too much of an overhead, we can manage multiple
  65. //file with a single timer.
  66. //create a synchronization timer to manage this file
  67. pLog->hTimer = CreateWaitableTimer(NULL, FALSE, NULL);
  68. if (!(pLog->hTimer))
  69. {
  70. CL_LOGFAILURE(GetLastError());
  71. return (0);
  72. }
  73. //register the timer for this log with the periodic activity timer thread
  74. AddTimerActivity(pLog->hTimer, LOG_MANAGE_INTERVAL, 1, LogpManage, (HLOG)pLog);
  75. FnExit:
  76. ClRtlLogPrint(LOG_NOISE,
  77. "[LM] LogCreate : Exit *LastLsn=0x%1!08lx! Log=0x%2!08lx!\r\n",
  78. *pLastLsn, pLog);
  79. return((HLOG)pLog);
  80. }
  81. /****
  82. @func DWORD | LogGetInfo | This is the callback registered to perform
  83. periodic management functions like flushing for quorum log files.
  84. @parm IN HLOG | hLog | Supplies the identifier of the log.
  85. @parm OUT LPWSTR | szFileName | Should be a pointer to a buffer MAX_PATH characters wide.
  86. @parm OUT LPDWORD | pdwCurLogSize | The current size of the log file
  87. is returned via this.
  88. @parm OUT LPDWORD | pdwMaxLogSize | The maximum size of the log file
  89. is returned via this.
  90. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened.
  91. @xref <f LogCreate>
  92. ****/
  93. DWORD LogGetInfo(
  94. IN HLOG hLog,
  95. OUT LPWSTR szFileName,
  96. OUT LPDWORD pdwCurLogSize,
  97. OUT LPDWORD pdwMaxLogSize
  98. )
  99. {
  100. PLOG pLog;
  101. DWORD dwError=ERROR_SUCCESS;
  102. GETLOG(pLog, hLog);
  103. EnterCriticalSection(&pLog->Lock);
  104. *pdwMaxLogSize = pLog->MaxFileSize;
  105. *pdwCurLogSize = pLog->FileSize;
  106. lstrcpyW(szFileName, pLog->FileName);
  107. LeaveCriticalSection(&pLog->Lock);
  108. return(dwError);
  109. }
  110. /****
  111. @func DWORD | LogSetInfo | This is the callback registered to perform
  112. periodic management functions like flushing for quorum log files.
  113. @parm IN HLOG | hLog | Supplies the identifier of the log.
  114. @parm OUT LPWSTR | szFileName | Should be a pointer to a buffer MAX_PATH characters wide.
  115. @parm OUT LPDWORD | pdwCurLogSize | The current size of the log file
  116. is returned via this.
  117. @parm OUT LPDWORD | pdwMaxLogSize | The maximum size of the log file
  118. is returned via this.
  119. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened.
  120. @xref <f LogCreate>
  121. ****/
  122. DWORD LogSetInfo(
  123. IN HLOG hLog,
  124. IN DWORD dwMaxLogSize
  125. )
  126. {
  127. PLOG pLog;
  128. DWORD dwError=ERROR_SUCCESS;
  129. GETLOG(pLog, hLog);
  130. EnterCriticalSection(&pLog->Lock);
  131. if (dwMaxLogSize == 0) dwMaxLogSize = CLUSTER_QUORUM_DEFAULT_MAX_LOG_SIZE;
  132. pLog->MaxFileSize = dwMaxLogSize;
  133. LeaveCriticalSection(&pLog->Lock);
  134. return(dwError);
  135. }
  136. /****
  137. @func DWORD | LogClose | Closes an open log file. All pending log writes are
  138. committed, all allocations are freed, and all handles are closed.
  139. @parm HLOG | hLog | Supplies the identifier of the log.
  140. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened.
  141. @xref <f LogCreate>
  142. ****/
  143. DWORD
  144. LogClose(
  145. IN HLOG LogFile
  146. )
  147. {
  148. PLOG pLog;
  149. LSN NextLsn;
  150. BOOL Success;
  151. ClRtlLogPrint(LOG_NOISE,
  152. "[LM] LogClose : Entry LogFile=0x%1!08lx!\r\n",
  153. LogFile);
  154. GETLOG(pLog, LogFile);
  155. //this will close the timer handle
  156. // we do this while not holding the log so that if a
  157. // timer event to flush fires it has an opportunity to finish
  158. if (pLog->hTimer)
  159. {
  160. RemoveTimerActivity(pLog->hTimer);
  161. pLog->hTimer = NULL;
  162. }
  163. EnterCriticalSection(&pLog->Lock);
  164. //if the file is open, LogReset calls LogClose after closing the log handle
  165. if (QfsIsHandleValid(pLog->FileHandle))
  166. {
  167. NextLsn = LogFlush(LogFile, pLog->NextLsn);
  168. //close the file handle
  169. Success = QfsCloseHandle(pLog->FileHandle);
  170. // CL_ASSERT(Success); GORN If connection with MNS resource is broken. Close can fail
  171. }
  172. Success = CloseHandle(pLog->Overlapped.hEvent);
  173. CL_ASSERT(Success);
  174. AlignFree(pLog->ActivePage);
  175. CrFree(pLog->FileName);
  176. LeaveCriticalSection(&pLog->Lock);
  177. DeleteCriticalSection(&pLog->Lock);
  178. ZeroMemory(pLog, sizeof(LOG)); // just in case somebody tries to
  179. // use this log again.
  180. CrFree(pLog);
  181. ClRtlLogPrint(LOG_NOISE,
  182. "[LM] LogClose : Exit returning success\r\n");
  183. return(ERROR_SUCCESS);
  184. }
  185. /****
  186. @func LSN | LogWrite | Writes a log record to the log file. The record is not
  187. necessarily committed until LogFlush is called with an LSN greater or equal to the returned LSN.
  188. @parm HLOG | hLog | Supplies the identifier of the log.
  189. @parm TRID | TransactionId | Supplies a transaction ID of the record.
  190. @parm TRID | TransactionType | Supplies a transaction type, start/commit/complete/unit
  191. transaction.
  192. @parm RMID | ResourceId | Supplies the ID of the resource manager submitting the log record.
  193. @parm RMTYPE | ResourceFlags |Resource manager associated flags to be associated with this log record.
  194. @parm PVOID | LogData | Supplies a pointer to the data to be logged.
  195. @parm DWORD | DataSize | Supplies the number of bytes of data pointed to by LogData
  196. @rdesc The LSN of the log record that was created. NULL_LSN if something terrible happened.
  197. GetLastError() will provide the error code.
  198. @xref <f LogRead> <f LogFlush>
  199. ****/
  200. LSN
  201. LogWrite(
  202. IN HLOG LogFile,
  203. IN TRID TransactionId,
  204. IN TRTYPE XsactionType,
  205. IN RMID ResourceId,
  206. IN RMTYPE ResourceFlags,
  207. IN PVOID LogData,
  208. IN DWORD DataSize
  209. )
  210. {
  211. PLOGPAGE Page;
  212. PLOG Log;
  213. PLOGRECORD LogRecord;
  214. LSN Lsn=NULL_LSN;
  215. BOOL bMaxFileSizeReached;
  216. DWORD TotalSize;
  217. DWORD dwError;
  218. DWORD dwNumPages;
  219. ClRtlLogPrint(LOG_NOISE,
  220. "[LM] LogWrite : Entry TrId=%1!u! RmId=%2!u! RmType = %3!u! Size=%4!u!\r\n",
  221. TransactionId, ResourceId, ResourceFlags, DataSize);
  222. CL_ASSERT(ResourceId > RMAny); // reserved for logger's use
  223. GETLOG(Log, LogFile);
  224. TotalSize = sizeof(LOGRECORD) + (DataSize + 7) & ~7; // round up to qword size
  225. EnterCriticalSection(&Log->Lock);
  226. #if DBG
  227. {
  228. DWORD dwOldProtect;
  229. DWORD Status;
  230. BOOL VPWorked;
  231. VPWorked = VirtualProtect(Log->ActivePage, Log->SectorSize, PAGE_READWRITE, &dwOldProtect);
  232. Status = GetLastError();
  233. CL_ASSERT( VPWorked );
  234. }
  235. #endif
  236. Page = LogpAppendPage(Log, TotalSize, &LogRecord, &bMaxFileSizeReached, &dwNumPages);
  237. //if a new page couldnt be allocated due to file size limits,
  238. //then try and reset the log
  239. if ((Page == NULL) && bMaxFileSizeReached)
  240. {
  241. //after resetting the log, try and allocate record space again
  242. LogpWriteWarningToEvtLog(LM_LOG_EXCEEDS_MAXSIZE, Log->FileName);
  243. dwError = LogReset(LogFile);
  244. //SS:LogReset sets the page to be readonly again
  245. #if DBG
  246. {
  247. DWORD dwOldProtect;
  248. DWORD Status;
  249. BOOL VPWorked;
  250. VPWorked = VirtualProtect(Log->ActivePage, Log->SectorSize, PAGE_READWRITE, &dwOldProtect);
  251. Status = GetLastError();
  252. CL_ASSERT( VPWorked );
  253. }
  254. #endif
  255. if (dwError == ERROR_SUCCESS)
  256. Page = LogpAppendPage(Log, TotalSize, &LogRecord, &bMaxFileSizeReached, &dwNumPages);
  257. else
  258. SetLastError(dwError);
  259. }
  260. if (Page == NULL)
  261. {
  262. ClRtlLogPrint(LOG_UNUSUAL,
  263. "[LM] LogWrite : LogpAppendPage failed.\r\n");
  264. goto FnExit;
  265. }
  266. CL_ASSERT(((ULONG_PTR)LogRecord & 0x7) == 0); // ensure qword alignment
  267. Lsn = MAKELSN(Page, LogRecord);
  268. //
  269. // Fill in log record.
  270. //
  271. LogRecord->Signature = LOGREC_SIG;
  272. LogRecord->ResourceManager = ResourceId;
  273. LogRecord->Transaction = TransactionId;
  274. LogRecord->XsactionType = XsactionType;
  275. LogRecord->Flags = ResourceFlags;
  276. GetSystemTimeAsFileTime(&LogRecord->Timestamp);
  277. LogRecord->NumPages = dwNumPages;
  278. LogRecord->DataSize = DataSize;
  279. if (dwNumPages < 1)
  280. CopyMemory(&LogRecord->Data, LogData, DataSize);
  281. else
  282. {
  283. if (LogpWriteLargeRecordData(Log, LogRecord, LogData, DataSize)
  284. != ERROR_SUCCESS)
  285. {
  286. ClRtlLogPrint(LOG_UNUSUAL,
  287. "[LM] LogWrite : LogpWriteLargeRecordData failed. Lsn=0x%1!08lx!\r\n",
  288. Lsn);
  289. Lsn = NULL_LSN;
  290. }
  291. }
  292. FnExit:
  293. #if DBG
  294. {
  295. DWORD dwOldProtect;
  296. DWORD Status;
  297. BOOL VPWorked;
  298. VPWorked = VirtualProtect(Log->ActivePage, Log->SectorSize, PAGE_READONLY, & dwOldProtect);
  299. Status = GetLastError();
  300. CL_ASSERT( VPWorked );
  301. }
  302. #endif
  303. LeaveCriticalSection(&Log->Lock);
  304. ClRtlLogPrint(LOG_NOISE,
  305. "[LM] LogWrite : Exit returning=0x%1!08lx!\r\n",
  306. Lsn);
  307. return(Lsn);
  308. }
  309. /****
  310. @func LSN | LogCommitSize | Commits the size for a record of this size.
  311. @parm HLOG | hLog | Supplies the identifier of the log.
  312. @parm DWORD | dwSize | Supplies the size of the data that needs
  313. to be logged.
  314. @rdesc The LSN of the log record that was created. NULL_LSN if something terrible happened.
  315. GetLastError() will provide the error code.
  316. @xref <f LogRead> <f LogFlush>
  317. ****/
  318. DWORD
  319. LogCommitSize(
  320. IN HLOG hLog,
  321. IN RMID ResourceId,
  322. IN DWORD dwDataSize
  323. )
  324. {
  325. PLOGPAGE Page;
  326. PLOG pLog;
  327. PLOGRECORD LogRecord;
  328. LSN Lsn=NULL_LSN;
  329. BOOL bMaxFileSizeReached;
  330. DWORD dwTotalSize;
  331. DWORD dwError = ERROR_SUCCESS;
  332. DWORD dwNumPages;
  333. ClRtlLogPrint(LOG_NOISE,
  334. "[LM] LogCommitSize : Entry RmId=%1!u! Size=%2!u!\r\n",
  335. ResourceId, dwDataSize);
  336. #ifdef CLUSTER_TESTPOINT
  337. TESTPT(TpFailLogCommitSize) {
  338. dwError = ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE;
  339. return(dwError);
  340. }
  341. #endif
  342. CL_ASSERT(ResourceId > RMAny); // reserved for logger's use
  343. GETLOG(pLog, hLog);
  344. dwTotalSize = sizeof(LOGRECORD) + (dwDataSize + 7) & ~7; // round up to qword size
  345. EnterCriticalSection(&pLog->Lock);
  346. //dont force the file to grow beyond its max limit
  347. dwError = LogpEnsureSize(pLog, dwTotalSize, FALSE);
  348. if (dwError == ERROR_SUCCESS)
  349. goto FnExit;
  350. if (dwError == ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE)
  351. {
  352. //after resetting the log, try and allocate record space again
  353. LogpWriteWarningToEvtLog(LM_LOG_EXCEEDS_MAXSIZE, pLog->FileName);
  354. dwError = LogReset(hLog);
  355. if (dwError == ERROR_SUCCESS)
  356. {
  357. //this time force the file to grow beyond its max size if required
  358. //this is because we do want to log records greater than the max
  359. //size of the file
  360. dwError = LogpEnsureSize(pLog, dwTotalSize, TRUE);
  361. }
  362. }
  363. if (dwError == ERROR_DISK_FULL)
  364. {
  365. //map error
  366. dwError = ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE;
  367. }
  368. FnExit:
  369. LeaveCriticalSection(&pLog->Lock);
  370. ClRtlLogPrint(LOG_NOISE,
  371. "[LM] LogCommitSize : Exit, returning 0x%1!08lx!\r\n",
  372. dwError);
  373. return(dwError);
  374. }
  375. /****
  376. @func LSN | LogFlush | Ensures that the given LSN is committed to disk. If this
  377. routine returns successfully, the given LSN is safely stored on disk and
  378. is guaranteed to survive a system crash.
  379. @parm HLOG | hLog | Supplies the identifier of the log.
  380. @parm LSN | MinLsn | Supplies the minimum LSN to be committed to disk.
  381. @rdesc The last LSN actually committed to disk. This will always be >= the requested MinLsn.
  382. NULL_LSN on failure
  383. @xref <f LogWrite>
  384. ****/
  385. LSN
  386. LogFlush(
  387. IN HLOG LogFile,
  388. IN LSN MinLsn
  389. )
  390. {
  391. PLOG pLog;
  392. PLOGPAGE pPage;
  393. PLOGRECORD pRecord;
  394. LSN Lsn;
  395. LSN FlushedLsn = NULL_LSN;
  396. DWORD dwBytesWritten;
  397. DWORD dwError;
  398. /*
  399. //SS: avoid clutter in cluster log
  400. ClRtlLogPrint(LOG_NOISE,
  401. "[LM] LogFlush : Entry LogFile=0x%1!08lx!\r\n",
  402. LogFile);
  403. */
  404. GETLOG(pLog, LogFile);
  405. EnterCriticalSection(&pLog->Lock);
  406. //if the MinLSN is greater than a record written to the log file
  407. if (MinLsn > pLog->NextLsn)
  408. {
  409. dwError = ERROR_INVALID_PARAMETER;
  410. goto FnExit;
  411. }
  412. //find the first record on the active page
  413. pPage = pLog->ActivePage;
  414. pRecord = &pPage->FirstRecord;
  415. Lsn = MAKELSN(pPage, pRecord);
  416. //if the lsn till which a flush is requested is on an unflushed page,
  417. //and there are records on the unflushed page and if the flush till
  418. // this lsn hasnt occured before, orchestrate a flush
  419. // SS: this scheme is not perfect though it shouldnt cause unnecessary
  420. // flushing as the flushing interval is 2 minutes..ideally we want to delay the
  421. // flush if the writes are in progress.
  422. if ((MinLsn >= Lsn) && (Lsn < pLog->NextLsn) && (MinLsn > pLog->FlushedLsn))
  423. {
  424. //there are uncommited records
  425. ClRtlLogPrint(LOG_NOISE,
  426. "[LM] LogFlush : pLog=0x%1!08lx! writing the %2!u! bytes for active page at offset 0x%3!08lx!\r\n",
  427. pLog, pPage->Size, pPage->Offset);
  428. (pLog->Overlapped).Offset = pPage->Offset;
  429. (pLog->Overlapped).OffsetHigh = 0;
  430. if ((dwError = LogpWrite(pLog, pPage, pPage->Size, &dwBytesWritten))
  431. != ERROR_SUCCESS)
  432. {
  433. ClRtlLogPrint(LOG_UNUSUAL,
  434. "[LM] LogFlush::LogpWrite failed, error=0x%1!08lx!\r\n",
  435. dwError);
  436. CL_LOGFAILURE(dwError);
  437. goto FnExit;
  438. }
  439. pLog->FlushedLsn = FlushedLsn = pLog->NextLsn;
  440. }
  441. /*
  442. //SS: avoid clutter in cluster log
  443. ClRtlLogPrint(LOG_NOISE,
  444. "[LM] LogFlush : returning 0x%1!08lx!\r\n",
  445. pLog->NextLsn);
  446. */
  447. FnExit:
  448. LeaveCriticalSection(&pLog->Lock);
  449. return(FlushedLsn);
  450. }
  451. /****
  452. @func LSN | LogRead | Reads a log record from the given log.
  453. @parm IN HLOG | hLog | Supplies the identifier of the log.
  454. @parm IN LSN | Lsn | The LSN of the record to be read. If NULL_LSN, the first
  455. record is read.
  456. @parm OUT RMID | *ResourceId | Returns the resource ID of the requested log record.
  457. @parm OUT RMTYPE | *ResourceFlags |Returns the resource flags associated with the
  458. requested log record.
  459. @parm OUT TRID | *Transaction | Returns the TRID of the requested log record
  460. @parm TRID | TrType | Returns the transaction type, start/commit/complete/unit
  461. transaction.
  462. @parm OUT PVOID | LogData | Returns the data associated with the log record.
  463. @parm IN OUT DWORD | *DataSize | Supplies the available size of the LogData buffer.
  464. Returns the number of bytes of data in the log record
  465. @rdesc Returns the next LSN in the log file. On failure, returns NULL_LSN.
  466. @xref <f LogWrite>
  467. ****/
  468. LSN
  469. LogRead(
  470. IN HLOG LogFile,
  471. IN LSN Lsn,
  472. OUT RMID *Resource,
  473. OUT RMTYPE *ResourceFlags,
  474. OUT TRID *Transaction,
  475. OUT TRTYPE *TrType,
  476. OUT PVOID LogData,
  477. IN OUT DWORD *DataSize
  478. )
  479. {
  480. PLOG pLog;
  481. DWORD PageIndex;
  482. PLOGPAGE pPage=NULL;
  483. BOOL Success;
  484. DWORD dwError=ERROR_SUCCESS;
  485. LSN NextLsn=NULL_LSN;
  486. PLOGRECORD pRecord;
  487. DWORD BytesRead;
  488. LOGPAGE Dummy;
  489. PLOGPAGE pCurPage;
  490. GETLOG(pLog, LogFile);
  491. CL_ASSERT(pLog->SectorSize == SECTOR_SIZE);
  492. EnterCriticalSection(&pLog->Lock);
  493. Dummy.Size = SECTOR_SIZE;
  494. if (Lsn == NULL_LSN)
  495. {
  496. Lsn = pLog->SectorSize + RECORDOFFSETINPAGE(&Dummy, &Dummy.FirstRecord);
  497. }
  498. if (Lsn >= pLog->NextLsn)
  499. {
  500. CL_LOGFAILURE(dwError = ERROR_INVALID_PARAMETER);
  501. goto FnExit;
  502. }
  503. //
  504. // Translate LSN to a page number and offset within the page
  505. //
  506. PageIndex = LSNTOPAGE(Lsn);
  507. //if the record exists in the unflushed page, dont need to read
  508. //from the disk
  509. if (pLog->ActivePage->Offset == PageIndex * pLog->SectorSize)
  510. {
  511. //if this data is being read, should we flush the page
  512. pCurPage = pLog->ActivePage;
  513. goto GetRecordData;
  514. }
  515. pPage = AlignAlloc(SECTOR_SIZE);
  516. if (pPage == NULL)
  517. {
  518. CL_LOGFAILURE(dwError = ERROR_NOT_ENOUGH_MEMORY);
  519. goto FnExit;
  520. }
  521. pCurPage = pPage;
  522. //
  523. // Translate LSN to a page number and offset within the page
  524. //
  525. PageIndex = LSNTOPAGE(Lsn);
  526. pLog->Overlapped.Offset = PageIndex * pLog->SectorSize;
  527. pLog->Overlapped.OffsetHigh = 0;
  528. Success = QfsReadFile(pLog->FileHandle,
  529. pCurPage,
  530. SECTOR_SIZE,
  531. &BytesRead,
  532. &pLog->Overlapped);
  533. if (!Success && (GetLastError() == ERROR_IO_PENDING)) {
  534. Success = QfsGetOverlappedResult(pLog->FileHandle,
  535. &pLog->Overlapped,
  536. &BytesRead,
  537. TRUE);
  538. }
  539. if (!Success)
  540. {
  541. CL_UNEXPECTED_ERROR(dwError = GetLastError());
  542. NextLsn = NULL_LSN;
  543. goto FnExit;
  544. }
  545. GetRecordData:
  546. pRecord = LSNTORECORD(pCurPage, Lsn);
  547. if (pRecord->Signature != LOGREC_SIG)
  548. {
  549. dwError = ERROR_CLUSTERLOG_CORRUPT;
  550. NextLsn = NULL_LSN;
  551. goto FnExit;
  552. }
  553. *Resource = pRecord->ResourceManager;
  554. *ResourceFlags = pRecord->Flags;
  555. *Transaction = pRecord->Transaction;
  556. *TrType = pRecord->XsactionType;
  557. if (LogData)
  558. CopyMemory(LogData, pRecord->Data, *DataSize);
  559. *DataSize = pRecord->RecordSize - sizeof(LOGRECORD);
  560. NextLsn = Lsn + pRecord->RecordSize;
  561. pRecord = LSNTORECORD(pCurPage, NextLsn);
  562. if (pRecord->ResourceManager == RMPageEnd)
  563. {
  564. //
  565. // The next log record is the end of page marker
  566. // Adjust the LSN to be the one at the start of the
  567. // next page.
  568. //
  569. NextLsn = pCurPage->Offset + pCurPage->Size +
  570. RECORDOFFSETINPAGE(pCurPage, &pCurPage->FirstRecord);
  571. }
  572. FnExit:
  573. LeaveCriticalSection(&pLog->Lock);
  574. if (pPage) AlignFree(pPage);
  575. if (dwError !=ERROR_SUCCESS)
  576. SetLastError(dwError);
  577. return(NextLsn);
  578. }
  579. /****
  580. @cb BOOL |(WINAPI *PLOG_SCAN_CALLBACK)| The callback called by LogScan.
  581. @parm IN PVOID | Context | Supplies the CallbackContext specified to LogScan
  582. @parm IN LSN | Lsn | Supplies the LSN of the record.
  583. @parm IN RMID | Resource | Supplies the resource identifier of the log record
  584. @parm IN TRID | Transaction | Supplies the transaction identifier of the log record
  585. @parm IN PVOID | LogData | Supplies a pointer to the log data. This is a read-
  586. only pointer and may be referenced only until the callback returns.
  587. @parm IN DWORD | DataLength | Supplies the length of the log record.
  588. @rdesc Returns TRUE to Continue scan or FALSE to Abort scan.
  589. @xref <f LogScan>
  590. ****/
  591. /****
  592. @func LSN | LogScan| Initiates a scan of the log. The scan can be done either forwards (redo) or
  593. backwards (undo).
  594. @parm IN HLOG | hLog | Supplies the identifier of the log.
  595. @parm IN LSN | First | Supplies the first LSN to start with. If NULL_LSN is specified,
  596. the scan begins at the start (for a forward scan) or end (for
  597. a backwards scan) of the log.
  598. @parm IN BOOL | ScanForward | Supplies the direction to scan the log in.
  599. TRUE - specifies a forward (redo) scan
  600. FALSE - specifies a backwards (undo) scan.
  601. @parm IN PLOG_SCAN_CALLBACK | CallbackRoutine |Supplies the routine to be called for each log record.
  602. @parm IN PVOID | CaklbackContext | Supplies an arbitrary context pointer, which will be
  603. passed to the CallbackRoutine
  604. @rdesc ERROR_SUCCESS if successful. Win32 status if something terrible happened.
  605. @xref <f LogRead> <f (WINAPI *PLOG_SCAN_CALLBACK)>
  606. ****/
  607. DWORD
  608. LogScan(
  609. IN HLOG LogFile,
  610. IN LSN FirstLsn,
  611. IN BOOL ScanForward,
  612. IN PLOG_SCAN_CALLBACK CallbackRoutine,
  613. IN PVOID CallbackContext
  614. )
  615. {
  616. PLOG pLog;
  617. PLOGRECORD pRecord;
  618. LOGPAGE Dummy;
  619. DWORD dwError=ERROR_SUCCESS;
  620. PUCHAR Buffer;
  621. PUCHAR pLargeBuffer;
  622. PLOGPAGE pPage;
  623. int PageIndex;
  624. int OldPageIndex;
  625. LSN Lsn;
  626. DWORD dwBytesRead;
  627. ClRtlLogPrint(LOG_NOISE,
  628. "[LM] LogScan::Entry Lsn=0x%1!08lx! ScanForward=%2!u! CallbackRoutine=0x%3!08lx!\r\n",
  629. FirstLsn, ScanForward, CallbackRoutine);
  630. GETLOG(pLog, LogFile);
  631. CL_ASSERT(pLog->SectorSize == SECTOR_SIZE);
  632. Buffer = AlignAlloc(SECTOR_SIZE);
  633. if (Buffer == NULL) {
  634. CL_UNEXPECTED_ERROR( ERROR_NOT_ENOUGH_MEMORY );
  635. }
  636. Lsn = FirstLsn;
  637. if ((!CallbackRoutine) || (Lsn >= pLog->NextLsn))
  638. {
  639. //set to invaid param
  640. dwError = ERROR_INVALID_PARAMETER;
  641. goto FnExit;
  642. }
  643. if (Lsn == NULL_LSN)
  644. {
  645. if (ScanForward)
  646. {
  647. Dummy.Size = SECTOR_SIZE;
  648. //get the Lsn for the first record
  649. if (Lsn == NULL_LSN)
  650. Lsn = pLog->SectorSize + RECORDOFFSETINPAGE(&Dummy, &Dummy.FirstRecord);
  651. }
  652. else
  653. {
  654. //get the Lsn for the last record
  655. pPage =pLog->ActivePage;
  656. pRecord = LSNTORECORD(pPage, pLog->NextLsn);
  657. Lsn = pRecord->PreviousLsn;
  658. }
  659. }
  660. //initialize to -1 so the first page is read
  661. OldPageIndex = -1;
  662. pPage = (PLOGPAGE)Buffer;
  663. //while there are more records that you can read
  664. //read the page
  665. //SS: For now we grab the crtitical section for entire duration
  666. //Might want to change that
  667. EnterCriticalSection(&pLog->Lock);
  668. while ((Lsn != NULL_LSN) & (Lsn < pLog->NextLsn))
  669. {
  670. //
  671. // Translate LSN to a page number and offset within the page
  672. //
  673. PageIndex = LSNTOPAGE(Lsn);
  674. if (PageIndex != OldPageIndex)
  675. {
  676. //if the record exists in the unflushed page, dont need to read
  677. //from the disk
  678. if (pLog->ActivePage->Offset == PageIndex * pLog->SectorSize)
  679. {
  680. //if this data is being read, should we flush the page
  681. pPage = pLog->ActivePage;
  682. }
  683. else
  684. {
  685. //read the page
  686. pLog->Overlapped.Offset = PageIndex * pLog->SectorSize;
  687. pLog->Overlapped.OffsetHigh = 0;
  688. dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
  689. //if it is the last page, then set the new page as the active
  690. //page
  691. if (dwError)
  692. {
  693. if (dwError == ERROR_HANDLE_EOF)
  694. {
  695. //not fatal, set this page as current page
  696. dwError = ERROR_SUCCESS;
  697. //SS: assume that this page has no
  698. //records, set the offset
  699. //this will be the current page
  700. Lsn = NULL_LSN;
  701. continue;
  702. }
  703. else
  704. goto FnExitUnlock;
  705. }
  706. }
  707. //read was successful, no need to read the page unless the
  708. //record falls on a different page
  709. OldPageIndex = PageIndex;
  710. }
  711. pRecord = LSNTORECORD(pPage, Lsn);
  712. //SS: skip checkpoint records
  713. //TBD:: what if the user wants to scan checkpoint records
  714. //as well
  715. if (pRecord->ResourceManager < RMAny)
  716. {
  717. //
  718. // The next log record is the end of page marker
  719. // Adjust the LSN to the next one
  720. //
  721. Lsn = GETNEXTLSN(pRecord, ScanForward);
  722. continue;
  723. }
  724. //check if the transaction types are the valid ones
  725. //for application.
  726. if ((pRecord->XsactionType != TTStartXsaction) &&
  727. (pRecord->XsactionType != TTCompleteXsaction))
  728. {
  729. //Cant assume that the record will fit in a small page
  730. //even a transaction unit may be large, even though transaction
  731. //units records are not returned by this call.
  732. if (pRecord->NumPages < 1)
  733. {
  734. Lsn = GETNEXTLSN(pRecord, ScanForward);
  735. }
  736. else
  737. {
  738. //if it is a large record it should be followed by
  739. //an eop page record
  740. //get the lsn of the eop record
  741. Lsn = GETNEXTLSN(pRecord,TRUE);
  742. //get the page index of the page where the eop record resides
  743. PageIndex = PageIndex + pRecord->NumPages - 1;
  744. CL_ASSERT(LSNTOPAGE(Lsn) == (DWORD)PageIndex);
  745. //read the last page for the large record
  746. (pLog->Overlapped).Offset = PageIndex * pLog->SectorSize;
  747. (pLog->Overlapped).OffsetHigh = 0;
  748. ClRtlLogPrint(LOG_NOISE,
  749. "[LM] LogScan::reading %1!u! bytes at offset 0x%2!08lx!\r\n",
  750. pLog->SectorSize, PageIndex * pLog->SectorSize);
  751. dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
  752. //if there are no errors, then check the last record
  753. if (dwError != ERROR_SUCCESS)
  754. {
  755. goto FnExitUnlock;
  756. }
  757. //read the page, make sure that the eop record follows the
  758. //large record
  759. pRecord = (PLOGRECORD)((ULONG_PTR) pPage +
  760. (Lsn - (pLog->Overlapped).Offset));
  761. CL_ASSERT((pRecord->Signature == LOGREC_SIG) &&
  762. (pRecord->ResourceManager == RMPageEnd))
  763. Lsn = GETNEXTLSN(pRecord, TRUE);
  764. }
  765. continue;
  766. }
  767. ClRtlLogPrint(LOG_NOISE,
  768. "[LM] LogScan::Calling the scancb for Lsn=0x%1!08lx! Trid=%2!u! RecordSize=%3!u!\r\n",
  769. Lsn, pRecord->Transaction, pRecord->DataSize);
  770. if (pRecord->NumPages < 1)
  771. {
  772. //if the callback requests to stop scan
  773. if (!(*CallbackRoutine)(CallbackContext, Lsn, pRecord->ResourceManager,
  774. pRecord->Flags, pRecord->Transaction, pRecord->XsactionType,
  775. pRecord->Data, pRecord->DataSize))
  776. break;
  777. //else go the the next record
  778. Lsn = GETNEXTLSN(pRecord, ScanForward);
  779. }
  780. else
  781. {
  782. //for a large record you need to read in the entire data
  783. pLargeBuffer = AlignAlloc(pRecord->NumPages * SECTOR_SIZE);
  784. if (pLargeBuffer == NULL)
  785. {
  786. //exit and put something in the eventlog
  787. dwError = ERROR_NOT_ENOUGH_MEMORY ;
  788. CL_LOGFAILURE(ERROR_NOT_ENOUGH_MEMORY);
  789. break;
  790. }
  791. //read the pages
  792. pLog->Overlapped.Offset = PageIndex * pLog->SectorSize;
  793. pLog->Overlapped.OffsetHigh = 0;
  794. dwError = LogpRead(pLog, pLargeBuffer, pRecord->NumPages *
  795. pLog->SectorSize, &dwBytesRead);
  796. //if it is the last page, then set the new page as the active
  797. //page
  798. if (dwError != ERROR_SUCCESS)
  799. {
  800. CL_LOGFAILURE(dwError);
  801. AlignFree(pLargeBuffer);
  802. break;
  803. }
  804. pRecord = LSNTORECORD((PLOGPAGE)pLargeBuffer, Lsn);
  805. //if the callback requests to stop scan
  806. if (!(*CallbackRoutine)(CallbackContext, Lsn, pRecord->ResourceManager,
  807. pRecord->Flags, pRecord->Transaction, pRecord->XsactionType,
  808. pRecord->Data, pRecord->DataSize))
  809. {
  810. AlignFree(pLargeBuffer);
  811. break;
  812. }
  813. //the next record should be an eop buffer on the last page
  814. //of the large record, skip that
  815. Lsn = GETNEXTLSN(pRecord, ScanForward);
  816. //since this page doesnt begin with the typical page info,
  817. //you cant validate this
  818. pRecord = (PLOGRECORD)(
  819. (ULONG_PTR)(pLargeBuffer + (Lsn - (pLog->Overlapped).Offset)));
  820. CL_ASSERT(pRecord->ResourceManager == RMPageEnd);
  821. //go the the next record
  822. Lsn = GETNEXTLSN(pRecord, ScanForward);
  823. AlignFree(pLargeBuffer);
  824. }
  825. }
  826. FnExitUnlock:
  827. LeaveCriticalSection(&pLog->Lock);
  828. ClRtlLogPrint(LOG_NOISE,
  829. "[LM] LogScan::Exit - Returning 0x%1!08lx!\r\n",
  830. dwError);
  831. FnExit:
  832. AlignFree(Buffer);
  833. return(dwError);
  834. }
  835. /****
  836. @func LSN | LogCheckPoint| Initiates a chk point process in the log.
  837. @parm IN HLOG | hLog | Supplies the identifier of the log.
  838. @parm IN BOOL | bAllowReset | Allow the reset of log file to occur
  839. while checkpointing. In general, this will be set to TRUE. Since
  840. When LogReset() internally invokes LogCheckPoint(), this will be
  841. set to FALSE.
  842. @parm IN LPCWSTR | lpszChkPtFile | The checkpt file that should be saved.
  843. If NULL, the callback function registered for checkpointing is invoked
  844. to get the checkpoint.
  845. @parm IN DWORD | dwChkPtSeq | If lpszChkPtFile is not NULL, this should point
  846. to a valid sequence number associated with the checkpoint.
  847. @rdesc ERROR_SUCCESS if successful. Win32 status if something terrible happened.
  848. @comm The log manager writes the start check point record. Then it invokes the call back to get the checkpoint data. After writing the
  849. data to a checkpoint file, it records the lsn of the end checkpoint record in the header.
  850. @xref <f LogGetLastChkPoint>
  851. ****/
  852. DWORD
  853. LogCheckPoint(
  854. IN HLOG LogFile,
  855. IN BOOL bAllowReset,
  856. IN LPCWSTR lpszInChkPtFile,
  857. IN DWORD dwChkPtSeq
  858. )
  859. {
  860. PLOG pLog;
  861. PLOGPAGE pPage;
  862. PLOG_HEADER pLogHeader=NULL;
  863. DWORD dwError=ERROR_SUCCESS;
  864. DWORD dwTotalSize;
  865. PLOGRECORD pLogRecord;
  866. TRID ChkPtTransaction,Transaction;
  867. LSN Lsn,ChkPtLsn;
  868. LOG_CHKPTINFO ChkPtInfo;
  869. DWORD dwBytesRead,dwBytesWritten;
  870. RMID Resource;
  871. RMTYPE RmType;
  872. BOOL bMaxFileSizeReached;
  873. WCHAR szNewChkPtFile[LOG_MAX_FILENAME_LENGTH];
  874. DWORD dwNumPages;
  875. WCHAR szPathName[MAX_PATH];
  876. TRTYPE TrType;
  877. DWORD dwCheckSum = 0;
  878. DWORD dwHeaderSum = 0;
  879. DWORD dwLen;
  880. GETLOG(pLog, LogFile);
  881. CL_ASSERT(pLog->SectorSize == SECTOR_SIZE);
  882. ClRtlLogPrint(LOG_NOISE,
  883. "[LM] LogCheckPoint entry\r\n");
  884. ZeroMemory( &ChkPtInfo, sizeof(LOG_CHKPTINFO) );
  885. EnterCriticalSection(&pLog->Lock);
  886. #if DBG
  887. {
  888. DWORD dwOldProtect;
  889. DWORD Status;
  890. BOOL VPWorked;
  891. VPWorked = VirtualProtect(pLog->ActivePage, pLog->SectorSize, PAGE_READWRITE, & dwOldProtect);
  892. Status = GetLastError();
  893. CL_ASSERT( VPWorked );
  894. }
  895. #endif
  896. //write the start chkpoint record
  897. dwTotalSize = sizeof(LOGRECORD) + 7 & ~7; // round up to qword size
  898. pPage = LogpAppendPage(pLog, dwTotalSize, &pLogRecord, &bMaxFileSizeReached, &dwNumPages);
  899. if ((pPage == NULL) && (bMaxFileSizeReached) && (bAllowReset))
  900. {
  901. //try and reset the log file
  902. //the checkpoint will be taken as a part of the reset process
  903. //if no input checkpoint file is specified
  904. //SS:note here LogCheckPoint will be called recursively
  905. LeaveCriticalSection(&pLog->Lock);
  906. return(LogpReset(pLog, lpszInChkPtFile));
  907. }
  908. if (pPage == NULL)
  909. {
  910. CL_LOGFAILURE(dwError = GetLastError());
  911. ClRtlLogPrint(LOG_UNUSUAL,
  912. "[LM] LogCheckPoint: LogpAppendPage failed, error=0x%1!08lx!\r\n",
  913. dwError);
  914. goto FnExit;
  915. }
  916. CL_ASSERT(((ULONG_PTR)pLogRecord & 0x7) == 0); // ensure qword alignment
  917. Lsn = MAKELSN(pPage, pLogRecord);
  918. pLogRecord->Signature = LOGREC_SIG;
  919. pLogRecord->ResourceManager = RMBeginChkPt;
  920. pLogRecord->Transaction = 0;
  921. pLogRecord->XsactionType = TTDontCare;
  922. pLogRecord->Flags = 0;
  923. GetSystemTimeAsFileTime(&pLogRecord->Timestamp);
  924. pLogRecord->NumPages = dwNumPages;
  925. pLogRecord->DataSize = 0;
  926. //if chkpt is not specifed get one from input checkpoint
  927. if (!lpszInChkPtFile)
  928. {
  929. if (!pLog->pfnGetChkPtCb)
  930. {
  931. dwError = ERROR_INVALID_PARAMETER;
  932. CL_LOGFAILURE(dwError);
  933. goto FnExit;
  934. }
  935. // get a checkpoint
  936. dwError = DmGetQuorumLogPath(szPathName, sizeof(szPathName));
  937. if (dwError != ERROR_SUCCESS)
  938. {
  939. dwError = GetLastError();
  940. ClRtlLogPrint(LOG_UNUSUAL,
  941. "[LM] LogCheckPoint: DmGetQuorumLogPath failed, error=%1!u!\r\n",
  942. dwError);
  943. goto FnExit;
  944. }
  945. szNewChkPtFile[0]= TEXT('\0');
  946. dwError = (*(pLog->pfnGetChkPtCb))(szPathName, pLog->pGetChkPtContext, szNewChkPtFile,
  947. & ChkPtTransaction);
  948. //if the chkptfile is created and if it could not be created because it
  949. //already existed, then we are set.
  950. if ((dwError != ERROR_SUCCESS) &&
  951. ((dwError != ERROR_ALREADY_EXISTS) || (!szNewChkPtFile[0])))
  952. {
  953. ClRtlLogPrint(LOG_UNUSUAL,
  954. "[LM] LogCheckPoint: Callback failed to return a checkpoint\r\n");
  955. CL_LOGCLUSERROR1(LM_CHKPOINT_GETFAILED, pLog->FileName);
  956. goto FnExit;
  957. }
  958. }
  959. else
  960. {
  961. //SS:we trust the application to not write a stale checkpoint
  962. lstrcpyW(szNewChkPtFile, lpszInChkPtFile);
  963. ChkPtTransaction = dwChkPtSeq;
  964. }
  965. //
  966. // Chittur Subbaraman (chitturs) - 1/28/99
  967. //
  968. // Compute and save the checksum for the checkpoint file
  969. //
  970. dwError = QfsMapFileAndCheckSum( szNewChkPtFile, &dwHeaderSum, &dwCheckSum );
  971. if ( dwError != CHECKSUM_SUCCESS )
  972. {
  973. ClRtlLogPrint(LOG_UNUSUAL,
  974. "[LM] LogCheckPoint: MapFileAndCheckSumW returned error=%1!u!\r\n",
  975. dwError);
  976. goto FnExit;
  977. }
  978. ChkPtInfo.dwCheckSum = dwCheckSum;
  979. ClRtlLogPrint(LOG_NOISE,
  980. "[LM] LogCheckPoint: ChkPtFile=%1!ls! Chkpt Trid=%2!d! CheckSum=%3!d!\r\n",
  981. szNewChkPtFile, ChkPtTransaction, dwCheckSum);
  982. //prepare the chkpt info structure
  983. ChkPtInfo.ChkPtBeginLsn = Lsn;
  984. lstrcpyW(ChkPtInfo.szFileName, szNewChkPtFile);
  985. //
  986. // Chittur Subbaraman (chitturs) - 1/29/99
  987. //
  988. // Add a signature at the end of the file name to denote that
  989. // a checksum has been taken.
  990. //
  991. dwLen = lstrlenW( ChkPtInfo.szFileName );
  992. if ( ( dwLen + lstrlenW( CHKSUM_SIG ) + 2 ) <= LOG_MAX_FILENAME_LENGTH )
  993. {
  994. lstrcpyW( &ChkPtInfo.szFileName[dwLen+1], CHKSUM_SIG );
  995. }
  996. dwTotalSize = sizeof(LOGRECORD) + (sizeof(LOG_CHKPTINFO) + 7) & ~7; // round up to qword size
  997. //write the endchk point record to the file.
  998. pPage = LogpAppendPage(pLog, dwTotalSize, &pLogRecord, &bMaxFileSizeReached, &dwNumPages);
  999. if ((pPage == NULL) && bMaxFileSizeReached && bAllowReset)
  1000. {
  1001. //try and reset the log file
  1002. ClRtlLogPrint(LOG_NOISE,
  1003. "[LM] LogCheckPoint: Maxfilesize exceeded. Calling LogpReset\r\n");
  1004. //the checkpoint will be taken as a part of the reset process
  1005. //if no input checkpoint file is specified
  1006. //SS:note here LogCheckPoint will be called recursively
  1007. LeaveCriticalSection(&pLog->Lock);
  1008. return(LogpReset(pLog, lpszInChkPtFile));
  1009. }
  1010. if (pPage == NULL) {
  1011. CL_LOGFAILURE(dwError = GetLastError());
  1012. goto FnExit;
  1013. }
  1014. CL_ASSERT(((ULONG_PTR)pLogRecord & 0x7) == 0); // ensure qword alignment
  1015. ChkPtLsn = MAKELSN(pPage, pLogRecord);
  1016. pLogRecord->Signature = LOGREC_SIG;
  1017. pLogRecord->ResourceManager = RMEndChkPt;
  1018. pLogRecord->Transaction = ChkPtTransaction; // transaction id associated with the chkpt
  1019. pLogRecord->XsactionType = TTDontCare;
  1020. pLogRecord->Flags = 0;
  1021. GetSystemTimeAsFileTime(&pLogRecord->Timestamp);
  1022. pLogRecord->NumPages = dwNumPages;
  1023. pLogRecord->DataSize = sizeof(LOG_CHKPTINFO);
  1024. CopyMemory(&pLogRecord->Data, (PBYTE)&ChkPtInfo, sizeof(LOG_CHKPTINFO));
  1025. //flush the log file
  1026. LogFlush(pLog,ChkPtLsn);
  1027. ClRtlLogPrint(LOG_NOISE,
  1028. "[LM] LogCheckPoint: EndChkpt written. EndChkPtLsn =0x%1!08lx! ChkPt Seq=%2!d! ChkPt FileName=%3!ls!\r\n",
  1029. ChkPtLsn, ChkPtTransaction, ChkPtInfo.szFileName);
  1030. //read the log header and get the old chk point sequence
  1031. //if null
  1032. pLogHeader = AlignAlloc(pLog->SectorSize);
  1033. if (pLogHeader == NULL)
  1034. {
  1035. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1036. CL_LOGFAILURE(dwError);
  1037. ClRtlLogPrint(LOG_UNUSUAL,
  1038. "[LM] LogCheckPoint: couldn't allocate memory for the header\r\n");
  1039. goto FnExit;
  1040. }
  1041. (pLog->Overlapped).Offset = 0;
  1042. (pLog->Overlapped).OffsetHigh = 0;
  1043. if ((dwError = LogpRead(pLog, pLogHeader, pLog->SectorSize, &dwBytesRead))
  1044. != ERROR_SUCCESS)
  1045. {
  1046. {
  1047. ClRtlLogPrint(LOG_UNUSUAL,
  1048. "[LM] LogCheckPoint: failed to read the header. Error=0x%1!08lx!\r\n",
  1049. dwError);
  1050. goto FnExit;
  1051. }
  1052. }
  1053. if (dwBytesRead != pLog->SectorSize)
  1054. {
  1055. ClRtlLogPrint(LOG_UNUSUAL,
  1056. "[LM] LogCheckPoint: failed to read the complete header\r\n");
  1057. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1058. //SS: should we do an implicit reset here
  1059. goto FnExit;
  1060. }
  1061. Lsn = pLogHeader->LastChkPtLsn;
  1062. //if there was a previous chkpoint and in most cases
  1063. //there should be one,except when the system just comes up
  1064. ChkPtInfo.szFileName[0]= TEXT('\0');
  1065. if (Lsn != NULL_LSN)
  1066. {
  1067. (pLog->Overlapped).Offset = LSNTOPAGE(Lsn) * pLog->SectorSize;
  1068. (pLog->Overlapped).OffsetHigh = 0;
  1069. //get the old check point file name
  1070. dwBytesRead = sizeof(LOG_CHKPTINFO);
  1071. if ((LogRead(pLog, Lsn, &Resource, &RmType, &Transaction, &TrType,
  1072. &ChkPtInfo, &dwBytesRead)) == NULL_LSN)
  1073. {
  1074. ClRtlLogPrint(LOG_UNUSUAL,
  1075. "[LM] LogCheckPoint: failed to read the chkpt lsn. Error=0x%1!08lx!\r\n",
  1076. dwError);
  1077. goto FnExit;
  1078. }
  1079. if (Resource != RMEndChkPt)
  1080. {
  1081. //SS: this should not happen
  1082. #if DBG
  1083. if (IsDebuggerPresent())
  1084. DebugBreak();
  1085. #endif
  1086. ChkPtInfo.szFileName[0]= TEXT('\0');
  1087. CL_LOGCLUSERROR1(LM_LOG_CORRUPT, pLog->FileName);
  1088. }
  1089. }
  1090. //update the last chkpoint lsn in the header
  1091. pLogHeader->LastChkPtLsn = ChkPtLsn;
  1092. //write the header
  1093. (pLog->Overlapped).Offset = 0;
  1094. (pLog->Overlapped).OffsetHigh = 0;
  1095. ClRtlLogPrint(LOG_NOISE,
  1096. "[LM] LogpCheckpoint : Writing %1!u! bytes to disk at offset 0x%2!08lx!\r\n",
  1097. pLog->SectorSize, pLog->Overlapped.Offset);
  1098. if ((dwError = LogpWrite(pLog, pLogHeader, pLog->SectorSize, &dwBytesWritten))
  1099. != ERROR_SUCCESS)
  1100. {
  1101. ClRtlLogPrint(LOG_UNUSUAL,
  1102. "[LM] LogCheckPoint: failed to update header after checkpointing, Error=0x%1!08lx!\r\n",
  1103. dwError);
  1104. CL_LOGFAILURE(dwError);
  1105. goto FnExit;
  1106. }
  1107. //flush the log file.
  1108. QfsFlushFileBuffers(pLog->FileHandle);
  1109. //delete the old checkpoint file
  1110. //the old checkpoint file may be the same as the current one, dont delete it, if so
  1111. if (Lsn && (ChkPtInfo.szFileName[0]) &&
  1112. (lstrcmpiW(szNewChkPtFile, ChkPtInfo.szFileName)))
  1113. QfsDeleteFile((LPCTSTR)(ChkPtInfo.szFileName));
  1114. ClRtlLogPrint(LOG_NOISE,
  1115. "[LM] LogCheckPoint Exit\r\n");
  1116. FnExit:
  1117. #if DBG
  1118. {
  1119. DWORD dwOldProtect;
  1120. DWORD Status;
  1121. BOOL VPWorked;
  1122. VPWorked = VirtualProtect(pLog->ActivePage, pLog->SectorSize, PAGE_READONLY, & dwOldProtect);
  1123. Status = GetLastError();
  1124. CL_ASSERT( VPWorked );
  1125. }
  1126. #endif
  1127. if (pLogHeader != NULL) {
  1128. AlignFree(pLogHeader);
  1129. }
  1130. LeaveCriticalSection(&pLog->Lock);
  1131. return(dwError);
  1132. }
  1133. /****
  1134. @func LSN | LogReset| Resets the log file and takes a new checkpoint.
  1135. @parm IN HLOG | hLog | Supplies the identifier of the log.
  1136. @rdesc ERROR_SUCCESS if successful. Win32 status if something terrible happened.
  1137. @comm The log manager creates a new log, takes a checkpoint and renames the old log file.
  1138. @xref <f LogCheckPoint>
  1139. ****/
  1140. DWORD
  1141. LogReset(
  1142. IN HLOG LogFile
  1143. )
  1144. {
  1145. PLOG pLog;
  1146. DWORD dwError;
  1147. GETLOG(pLog, LogFile);
  1148. CL_ASSERT(pLog->SectorSize == SECTOR_SIZE);
  1149. ClRtlLogPrint(LOG_NOISE,
  1150. "[LM] LogReset entry...\r\n");
  1151. dwError = LogpReset (pLog, NULL);
  1152. ClRtlLogPrint(LOG_NOISE,
  1153. "[LM] LogReset exit, returning 0x%1!08lx!\r\n",
  1154. dwError);
  1155. return(dwError);
  1156. }
  1157. /****
  1158. @func DWORD | LogGetLastChkPoint| This is a callback that is called on
  1159. change of state on quorum resource.
  1160. @parm HLOG | LogFile | A pointer to a DMLOGRECORD structure.
  1161. @parm PVOID | szChkPtFileName | The name of the checkpoint file.
  1162. @parm TRID | *pTransaction | The transaction id associated with the
  1163. checkpoint.
  1164. @parm LSN | *pChkPtLsn | The LSN of the start checkpoint record.
  1165. @rdesc Returns a result code. ERROR_SUCCESS on success. To find transactions,
  1166. past this checkpoint, the application must scan from the LSN of the start
  1167. checkpoint record.
  1168. @xref
  1169. ****/
  1170. DWORD LogGetLastChkPoint(
  1171. IN HLOG LogFile,
  1172. OUT LPWSTR szChkPtFileName,
  1173. OUT TRID *pTransactionId,
  1174. OUT LSN *pChkPtLsn)
  1175. {
  1176. PLOG_HEADER pLogHeader=NULL;
  1177. PLOG pLog;
  1178. DWORD dwError = ERROR_SUCCESS;
  1179. DWORD dwBytesRead;
  1180. RMID Resource;
  1181. RMTYPE RmType;
  1182. TRTYPE TrType;
  1183. LOG_CHKPTINFO ChkPtInfo;
  1184. DWORD dwCheckSum = 0;
  1185. DWORD dwHeaderSum = 0;
  1186. DWORD dwLen;
  1187. ClRtlLogPrint(LOG_NOISE,
  1188. "[LM] LogGetLastChkPoint:: Entry\r\n");
  1189. GETLOG(pLog, LogFile);
  1190. CL_ASSERT(pLog->SectorSize == SECTOR_SIZE);
  1191. EnterCriticalSection(&pLog->Lock);
  1192. if (!szChkPtFileName || !pTransactionId || !pChkPtLsn)
  1193. {
  1194. dwError = ERROR_INVALID_PARAMETER;
  1195. goto FnExit;
  1196. }
  1197. *pTransactionId = 0;
  1198. *pChkPtLsn = NULL_LSN;
  1199. pLogHeader = AlignAlloc(pLog->SectorSize);
  1200. if (pLogHeader == NULL) {
  1201. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1202. CL_LOGFAILURE(dwError);
  1203. goto FnExit;
  1204. }
  1205. //read the header
  1206. (pLog->Overlapped).Offset = 0;
  1207. (pLog->Overlapped).OffsetHigh = 0;
  1208. if (LogpRead(LogFile, pLogHeader, pLog->SectorSize, &dwBytesRead) != ERROR_SUCCESS)
  1209. {
  1210. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1211. goto FnExit;
  1212. }
  1213. if (dwBytesRead != pLog->SectorSize)
  1214. {
  1215. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1216. CL_LOGCLUSERROR1(LM_LOG_CORRUPT, pLog->FileName);
  1217. goto FnExit;
  1218. }
  1219. //validate the header
  1220. if (!ISVALIDHEADER((*pLogHeader)))
  1221. {
  1222. ClRtlLogPrint(LOG_UNUSUAL,
  1223. "[LM] LogGetLastChkPoint::the file header is corrupt.\r\n");
  1224. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1225. CL_LOGCLUSERROR1(LM_LOG_CORRUPT, pLog->FileName);
  1226. goto FnExit;
  1227. }
  1228. //read the last Chkpoint end record
  1229. if (pLogHeader->LastChkPtLsn != NULL_LSN)
  1230. {
  1231. dwBytesRead = sizeof(LOG_CHKPTINFO);
  1232. if ((LogRead(LogFile , pLogHeader->LastChkPtLsn, &Resource, &RmType,
  1233. pTransactionId, &TrType, &ChkPtInfo, &dwBytesRead)) == NULL_LSN)
  1234. {
  1235. ClRtlLogPrint(LOG_UNUSUAL,
  1236. "[LM] LogGetLastChkPoint::LogRead for chkpt lsn 0x%1!08lx! failed\r\n",
  1237. pLogHeader->LastChkPtLsn);
  1238. dwError = GetLastError();
  1239. goto FnExit;
  1240. }
  1241. if (Resource != RMEndChkPt)
  1242. {
  1243. //SS: This should not happen
  1244. #if DBG
  1245. if (IsDebuggerPresent())
  1246. DebugBreak();
  1247. #endif
  1248. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1249. CL_LOGFAILURE(dwError);
  1250. CL_LOGCLUSERROR1(LM_LOG_CORRUPT, pLog->FileName);
  1251. goto FnExit;
  1252. }
  1253. //
  1254. // Chittur Subbaraman (chitturs) - 1/28/99
  1255. //
  1256. // Check if the checkpoint file itself got corrupted. But first
  1257. // make sure that a checksum was indeed recorded.
  1258. //
  1259. dwLen = lstrlenW( ChkPtInfo.szFileName );
  1260. if ( ( dwLen + lstrlenW( CHKSUM_SIG ) + 2 <= LOG_MAX_FILENAME_LENGTH ) &&
  1261. ( lstrcmpW( &ChkPtInfo.szFileName[dwLen+1], CHKSUM_SIG ) == 0 ) )
  1262. {
  1263. dwError = QfsMapFileAndCheckSum( ChkPtInfo.szFileName, &dwHeaderSum, &dwCheckSum );
  1264. if ( ( dwError != CHECKSUM_SUCCESS ) ||
  1265. ( dwCheckSum != ChkPtInfo.dwCheckSum ) ||
  1266. ( dwCheckSum == 0 ) )
  1267. {
  1268. ClRtlLogPrint(LOG_UNUSUAL,
  1269. "[LM] LogGetLastChkPoint - MapFileAndCheckSumW returned error=%1!u!\r\n",
  1270. dwError);
  1271. ClRtlLogPrint(LOG_UNUSUAL,
  1272. "[LM] LogGetLastChkPoint - Stored CheckSum=%1!u!, Retrieved CheckSum=%2!u!\r\n",
  1273. ChkPtInfo.dwCheckSum,
  1274. dwCheckSum);
  1275. CL_LOGCLUSERROR1( LM_LOG_CHKPOINT_NOT_FOUND, ChkPtInfo.szFileName );
  1276. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1277. goto FnExit;
  1278. }
  1279. }
  1280. lstrcpyW(szChkPtFileName, ChkPtInfo.szFileName);
  1281. *pChkPtLsn = ChkPtInfo.ChkPtBeginLsn;
  1282. ClRtlLogPrint(LOG_NOISE,
  1283. "[LM] LogGetLastChkPoint: ChkPt File %1!ls! ChkPtSeq=%2!d! ChkPtLsn=0x%3!08lx! Checksum=%4!d!\r\n",
  1284. szChkPtFileName, *pTransactionId, *pChkPtLsn, dwCheckSum);
  1285. }
  1286. else
  1287. {
  1288. dwError = ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND;
  1289. CL_LOGCLUSWARNING1(LM_LOG_CHKPOINT_NOT_FOUND, pLog->FileName);
  1290. }
  1291. FnExit:
  1292. if (pLogHeader) AlignFree(pLogHeader);
  1293. LeaveCriticalSection(&pLog->Lock);
  1294. ClRtlLogPrint(LOG_NOISE,
  1295. "[LM] LogGetLastChkPoint exit, returning 0x%1!08lx!\r\n",
  1296. dwError);
  1297. return (dwError);
  1298. }