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

2368 lines
71 KiB

  1. /*++
  2. Copyright (c) 1992-1997 Microsoft Corporation
  3. Module Name:
  4. lmutils.c
  5. Abstract:
  6. Provides the utility functions used by the logger.
  7. Author:
  8. Sunita Shrivastava (jvert) 30-Mar-1997
  9. Revision History:
  10. --*/
  11. #include "service.h"
  12. #include "lmp.h"
  13. BOOL bLogExceedsMaxSzWarning = FALSE;
  14. /****
  15. @doc EXTERNAL INTERFACES CLUSSVC LM
  16. ****/
  17. //
  18. // DWORD
  19. // LSNOFFSETINPAGE(
  20. // IN PLOGPAGE Page,
  21. // IN LSN Lsn
  22. // );
  23. //
  24. // Given a pointer to a page and an LSN within that page, computes the offset into the
  25. // page that the log record starts at.
  26. //
  27. _inline
  28. DWORD
  29. LSNOFFSETINPAGE(
  30. IN PLOGPAGE Page,
  31. IN LSN Lsn
  32. )
  33. {
  34. DWORD Offset;
  35. Offset = (DWORD)(Lsn - Page->Offset);
  36. CL_ASSERT(Offset < Page->Size);
  37. return(Offset);
  38. }
  39. //
  40. // PLOGRECORD
  41. // LSNTORECORD(
  42. // IN PLOGPAGE Page,
  43. // IN LSN Lsn
  44. // );
  45. //
  46. // Given a pointer to a page and an LSN within that page, generates a pointer to the log record
  47. //
  48. //_inline
  49. PLOGRECORD
  50. LSNTORECORD(
  51. IN PLOGPAGE Page,
  52. IN LSN Lsn
  53. )
  54. {
  55. CL_ASSERT(Lsn != NULL_LSN);
  56. return((PLOGRECORD)((ULONG_PTR)Page + LSNOFFSETINPAGE(Page,Lsn)));
  57. }
  58. //
  59. // DWORD
  60. // RECORDOFFSETINPAGE(
  61. // IN PLOGPAGE Page,
  62. // IN PLOGRECORD LogRecord
  63. // );
  64. //
  65. // Given a pointer to a page and a log record within that page, computes the offset into the
  66. // page that the log record starts at.
  67. //
  68. //_inline
  69. DWORD
  70. RECORDOFFSETINPAGE(
  71. IN PLOGPAGE Page,
  72. IN PLOGRECORD LogRecord
  73. )
  74. {
  75. DWORD Offset;
  76. Offset = (DWORD)((ULONG_PTR)(LogRecord) - (ULONG_PTR)Page);
  77. CL_ASSERT(Offset < Page->Size);
  78. return(Offset);
  79. }
  80. /****
  81. @func PLOG | LogpCreate| Internal entry point for LogCreate.Creates or opens a log file. If the file
  82. does not exist, it will be created. If the file already exists, and is
  83. a valid log file, it will be opened.
  84. @parm IN LPWSTR | lpFileName | Supplies the name of the log file to create or open.
  85. @parm IN DWORD | dwMaxFileSize | Supplies the maximum file size in bytes, must be
  86. greater than 8K and smaller than 4 gigabytes. If the file is exceeds this
  87. size, the reset function will be called. If 0, the maximum log file size limit
  88. is set to the default maximum size.
  89. @parm IN PLOG_GETCHECKPOINT_CALLBACK | CallbackRoutine | The callback routine that
  90. will provide a checkpoint file and the transaction associated with that checkpoint
  91. file when LogCheckPoint() is called for this log file. If this is NULL, then the checkpointing capabilities are
  92. not associated with the log file.
  93. @parm IN PVOID | pGetChkPtContext | Supplies an arbitrary context pointer, which will be
  94. passed to the CallbackRoutine.
  95. @parm IN BOOL | bForceReset | If true, this function creates an empty log file
  96. if the log file doesnt exist or if it is corrupt.
  97. @parm LSN | *LastLsn | If present, Returns the last LSN written to the log file.
  98. (NULL_LSN if the log file was created)
  99. @rdesc Returns a pointer to a PLOG structure. NUll in the case of an error.
  100. @xref <f LogCreate>
  101. ****/
  102. PLOG
  103. LogpCreate(
  104. IN LPWSTR lpFileName,
  105. IN DWORD dwMaxFileSize,
  106. IN PLOG_GETCHECKPOINT_CALLBACK CallbackRoutine,
  107. IN PVOID pGetChkPtContext,
  108. IN BOOL bForceReset,
  109. OPTIONAL OUT LSN *LastLsn
  110. )
  111. {
  112. //create a timer activity for this
  113. PLOG pLog = NULL;
  114. LPWSTR FileName = NULL;
  115. DWORD Status;
  116. BOOL Success;
  117. BOOL FileExists;
  118. //
  119. // Capture the filename string
  120. //
  121. ClRtlLogPrint(LOG_NOISE,
  122. "[LM] LogpCreate : Entry \r\n");
  123. if (dwMaxFileSize == 0) dwMaxFileSize = CLUSTER_QUORUM_DEFAULT_MAX_LOG_SIZE;
  124. //SS: we dont put a upper limit on the MaxFileSize that a user may choose.
  125. FileName = CrAlloc((lstrlenW(lpFileName) + 1) * sizeof(WCHAR));
  126. if (FileName == NULL) {
  127. Status = ERROR_NOT_ENOUGH_MEMORY;
  128. CL_LOGFAILURE(Status);
  129. goto ErrorExit;
  130. }
  131. lstrcpyW(FileName, lpFileName);
  132. //
  133. // Allocate the log file data structure
  134. //
  135. pLog = CrAlloc(sizeof(LOG));
  136. if (pLog == NULL) {
  137. Status = ERROR_NOT_ENOUGH_MEMORY;
  138. CL_LOGFAILURE(Status);
  139. goto ErrorExit;
  140. }
  141. pLog->FileHandle = INVALID_HANDLE_VALUE;
  142. pLog->Overlapped.hEvent = NULL;
  143. pLog->ActivePage = NULL;
  144. pLog->hTimer = NULL;
  145. pLog->FileName = FileName;
  146. pLog->LogSig = LOG_SIG;
  147. pLog->MaxFileSize = dwMaxFileSize;
  148. pLog->pfnGetChkPtCb = CallbackRoutine;
  149. pLog->pGetChkPtContext = pGetChkPtContext;
  150. InitializeCriticalSection(&pLog->Lock);
  151. ZeroMemory(&(pLog->Overlapped), sizeof(OVERLAPPED));
  152. //
  153. // Create the event used to synchronize our overlapped I/O.
  154. //
  155. pLog->Overlapped.hEvent = CreateEvent(NULL,
  156. TRUE,
  157. TRUE,
  158. NULL);
  159. if (pLog->Overlapped.hEvent == NULL) {
  160. Status = GetLastError();
  161. CL_LOGFAILURE(Status);
  162. goto ErrorExit;
  163. }
  164. //
  165. // Create the file
  166. //
  167. //SS: we want to create this file with write through since
  168. //we control the flushing of log pages to the log file
  169. pLog->FileHandle = CreateFileW(pLog->FileName,
  170. GENERIC_READ | GENERIC_WRITE,
  171. FILE_SHARE_READ,
  172. // 0,
  173. NULL,
  174. OPEN_ALWAYS,
  175. FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
  176. // FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
  177. // 0,
  178. NULL);
  179. if (pLog->FileHandle == INVALID_HANDLE_VALUE) {
  180. Status = GetLastError();
  181. CL_LOGFAILURE(Status);
  182. goto ErrorExit;
  183. }
  184. FileExists = (GetLastError() == ERROR_ALREADY_EXISTS);
  185. pLog->SectorSize = SECTOR_SIZE;
  186. if (FileExists)
  187. {
  188. //
  189. // Log already exists, open it up, validate it,
  190. // and set everything up so that we can read and
  191. // write the log records.
  192. //
  193. Status = LogpMountLog(pLog);
  194. if (Status != ERROR_SUCCESS)
  195. {
  196. ClRtlLogPrint(LOG_CRITICAL,
  197. "[LM] LogCreate : LogpMountLog failed, Error=%1!u!\r\n",
  198. Status);
  199. //
  200. // Chittur Subbaraman (chitturs) - 12/4/1999
  201. //
  202. // Try and blow away the corrupt log and create a new one
  203. // if the bForceReset flag is TRUE, else exit with error
  204. // status.
  205. //
  206. if (Status == ERROR_CLUSTERLOG_CORRUPT)
  207. {
  208. if (!bForceReset)
  209. {
  210. CL_LOGFAILURE(Status);
  211. CL_LOGCLUSERROR1(LM_QUORUM_LOG_CORRUPT, pLog->FileName);
  212. goto ErrorExit;
  213. }
  214. //truncate the file
  215. Status = SetFilePointer(pLog->FileHandle,
  216. 0,
  217. NULL,
  218. FILE_BEGIN);
  219. if (Status == 0xFFFFFFFF) {
  220. Status = GetLastError();
  221. CL_LOGFAILURE(Status);
  222. goto ErrorExit;
  223. }
  224. if (!SetEndOfFile(pLog->FileHandle)) {
  225. Status = GetLastError();
  226. CL_LOGFAILURE(Status);
  227. goto ErrorExit;
  228. }
  229. //create a new one
  230. Status = LogpInitLog(pLog);
  231. *LastLsn = NULL_LSN;
  232. }
  233. }
  234. else
  235. {
  236. *LastLsn = pLog->NextLsn;
  237. }
  238. }
  239. else
  240. {
  241. //
  242. // Log has been created, write out header
  243. // page and set everything up for writing.
  244. //
  245. if (bForceReset)
  246. {
  247. Status = LogpInitLog(pLog);
  248. *LastLsn = NULL_LSN;
  249. }
  250. else
  251. {
  252. //
  253. // The user has not allowed a reset. So, log a
  254. // message to the event log and exit with error status.
  255. //
  256. Status = ERROR_CLUSTER_QUORUMLOG_NOT_FOUND;
  257. *LastLsn = NULL_LSN;
  258. CloseHandle(pLog->FileHandle);
  259. pLog->FileHandle = INVALID_HANDLE_VALUE;
  260. DeleteFileW(pLog->FileName);
  261. CL_LOGCLUSERROR1(LM_QUORUM_LOG_NOT_FOUND, pLog->FileName);
  262. }
  263. }
  264. ErrorExit:
  265. if (Status != ERROR_SUCCESS)
  266. {
  267. ClRtlLogPrint(LOG_NOISE,
  268. "[LM] LogpCreate : Exit Error=0x%1!08lx!\r\n",
  269. Status);
  270. if (FileName != NULL) {
  271. CrFree(FileName);
  272. }
  273. if (pLog != NULL) {
  274. DeleteCriticalSection(&pLog->Lock);
  275. if (pLog->FileHandle != INVALID_HANDLE_VALUE) {
  276. CloseHandle(pLog->FileHandle);
  277. }
  278. if (pLog->Overlapped.hEvent != NULL) {
  279. Success = CloseHandle(pLog->Overlapped.hEvent);
  280. CL_ASSERT(Success);
  281. }
  282. if (pLog->ActivePage !=NULL)
  283. AlignFree(pLog->ActivePage);
  284. CrFree(pLog);
  285. }
  286. SetLastError(Status);
  287. return(NULL);
  288. }
  289. else {
  290. ClRtlLogPrint(LOG_NOISE,
  291. "[LM] LogpCreate : Exit with success\r\n");
  292. return(pLog);
  293. }
  294. }
  295. /****
  296. @func DWORD | LogpManage | This is the callback registered to perform
  297. periodic management functions like flushing for quorum log files.
  298. @parm HLOG | hLog | Supplies the identifier of the log.
  299. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened.
  300. @xref <f LogCreate>
  301. ****/
  302. void WINAPI LogpManage(
  303. IN HANDLE hTimer,
  304. IN PVOID pContext)
  305. {
  306. HLOG hLog;
  307. PLOG pLog;
  308. /*
  309. //avoid clutter in cluster log as this is called periodically
  310. ClRtlLogPrint(LOG_NOISE,
  311. "[LM] LogpManage : Entry pContext=0x%1!08lx!\r\n",
  312. pContext);
  313. */
  314. //
  315. //LogpRaiseAlert();
  316. hLog = (HLOG)pContext;
  317. GETLOG(pLog, hLog);
  318. LogFlush(hLog, pLog->NextLsn);
  319. /*
  320. ClRtlLogPrint(LOG_NOISE,
  321. "[LM] LogpManage : Exit\r\n");
  322. */
  323. }
  324. /****
  325. @func DWORD | LogpEnsureSize | This ensures that there is space on
  326. the disk to commit a record of the given size.
  327. @parm IN HLOG | hLog | Supplies the identifier of the log.
  328. @parm IN DWORD |dwSize | The size of the record.
  329. @parm IN BOOL |bForce | If FALSE, the size is not committed if it
  330. exceeds the file size. If TRUE, commit the size irrespective
  331. of the file size.
  332. @comm This function checks if the disk space for the given record is
  333. already committed. If not, it tries to grow the file.
  334. @rdesc ERROR_SUCCESS if successful in commiting disk space or Win32
  335. error code if something horrible happened.
  336. @xref <f LogCommitSize>
  337. ****/
  338. DWORD
  339. LogpEnsureSize(
  340. IN PLOG pLog,
  341. IN DWORD dwSize,
  342. IN BOOL bForce
  343. )
  344. {
  345. PLOGPAGE pPage;
  346. PLOGRECORD pRecord;
  347. DWORD Status=ERROR_SUCCESS;
  348. DWORD dwNumPages;
  349. DWORD dwNewSize;
  350. DWORD dwError;
  351. //
  352. // Nobody should ever write less than one log record
  353. //
  354. CL_ASSERT(dwSize >= sizeof(LOGRECORD));
  355. dwNumPages = 0; //typically zero for small records
  356. pPage = pLog->ActivePage;
  357. //
  358. // Nobody should ever write more than the page size until we
  359. // support dynamically sized pages.
  360. //
  361. if (dwSize > pPage->Size - (sizeof(LOGRECORD) + sizeof(LOGPAGE)))
  362. {
  363. //this is a large record
  364. //calculate the total number of pages required
  365. //sizeof(LOGPAGE) includes space for one record header
  366. //that will account for the eop written after a large record
  367. dwNumPages = (sizeof(LOGPAGE) + sizeof(LOGRECORD) + dwSize)/pPage->Size;
  368. if ((sizeof(LOGPAGE) + sizeof(LOGRECORD) + dwSize) % pPage->Size)
  369. dwNumPages += 1;
  370. ClRtlLogPrint(LOG_NOISE,
  371. "[LM] LogpEnsureSize : Large record Size=%1!u! dwNumPages=%2!u!\r\n",
  372. dwSize, dwNumPages);
  373. /*
  374. //SS: dont restrict record size here- if the registry takes it
  375. //make the best effort to log it
  376. if (dwNumPages > MAXNUMPAGES_PER_RECORD)
  377. {
  378. Status = ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE;
  379. goto FnExit;
  380. }
  381. */
  382. }
  383. pRecord = LSNTORECORD(pPage, pLog->NextLsn);
  384. //
  385. // There must always be enough space remaining in the page to write
  386. // an end-of-page log record.
  387. //
  388. CL_ASSERT((RECORDOFFSETINPAGE(pPage, pRecord) + sizeof(LOGRECORD)) <= pPage->Size);
  389. //
  390. // If there is not enough space in this page for the requested data and
  391. // the end-of-page log record, commit size for the new page.
  392. //
  393. if ((RECORDOFFSETINPAGE(pPage, pRecord) + dwSize + sizeof(LOGRECORD)) > pPage->Size)
  394. {
  395. //make sure there is enough room in the disk for the new page
  396. //if there isnt grow the file.
  397. //if the file has reached its max ceiling, return error
  398. if (pLog->FileAlloc + ((dwNumPages+1) * pLog->SectorSize) >
  399. pLog->FileSize)
  400. {
  401. dwNewSize = pLog->FileSize + GROWTH_CHUNK;
  402. CL_ASSERT(dwNewSize > pLog->FileSize); // bummer, log file is >4GB
  403. //check if the file can be grown, if not, may be a reset
  404. //is required
  405. // if the force flag is set, then allow the file
  406. // to grow the file beyond its max size
  407. if (dwNewSize > pLog->MaxFileSize && !bForce)
  408. {
  409. LogpWriteWarningToEvtLog(LM_LOG_EXCEEDS_MAXSIZE, pLog->FileName);
  410. Status = ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE;
  411. goto FnExit;
  412. }
  413. //
  414. // Grow the file.
  415. //
  416. dwError = SetFilePointer(pLog->FileHandle,
  417. dwNewSize,
  418. NULL,
  419. FILE_BEGIN);
  420. if (dwError == 0xFFFFFFFF)
  421. {
  422. Status = GetLastError();
  423. CL_LOGFAILURE(Status);
  424. goto FnExit;
  425. }
  426. if (!SetEndOfFile(pLog->FileHandle))
  427. {
  428. Status = GetLastError();
  429. CL_LOGFAILURE(Status);
  430. goto FnExit;
  431. }
  432. pLog->FileSize += GROWTH_CHUNK;
  433. }
  434. }
  435. FnExit:
  436. return(Status);
  437. }
  438. PLOGPAGE
  439. LogpAppendPage(
  440. IN PLOG Log,
  441. IN DWORD Size,
  442. OUT PLOGRECORD *Record,
  443. OUT BOOL *pbMaxFileSizeReached,
  444. OUT DWORD *pdwNumPages
  445. )
  446. /*++
  447. Routine Description:
  448. Finds the next available log record. If this is in the current
  449. log page, it is returned directly. If the requested size is too
  450. large for the remaining space in the current log page, the current
  451. log page is written to disk and a new log page allocated.
  452. Arguments:
  453. Log - Supplies the log to be appended to
  454. Size - Supplies the total size in bytes of the log record to append
  455. Record - Returns a pointer to the log record.
  456. pbMaxFileSizeReached - if the maximum file size is reached, this is set to
  457. TRUE.
  458. pdwNumPages - number of partial or complete pages consumed by this record, if this
  459. is a large record. Else it is set to zero.
  460. Return Value:
  461. Returns a pointer to the current log page.
  462. NULL if something horrible happened.
  463. --*/
  464. {
  465. PLOGPAGE pPage;
  466. PLOGRECORD Last;
  467. PLOGRECORD Current;
  468. DWORD Status=ERROR_SUCCESS;
  469. BOOL Success;
  470. DWORD BytesWritten;
  471. LSN LastLsn;
  472. PLOGPAGE pRetPage=NULL;
  473. //
  474. // Nobody should ever write less than one log record
  475. //
  476. CL_ASSERT(Size >= sizeof(LOGRECORD));
  477. *pdwNumPages = 0; //typically zero for small records
  478. *pbMaxFileSizeReached = FALSE;
  479. pPage = Log->ActivePage;
  480. //
  481. // Nobody should ever write more than the page size until we
  482. // support dynamically sized pages.
  483. //
  484. if (Size > pPage->Size - (sizeof(LOGRECORD) + sizeof(LOGPAGE)))
  485. {
  486. //this is a large record
  487. //calculate the total number of pages required
  488. //sizeof(LOGPAGE) includes space for one record header
  489. //that will account for the eop written after a large record
  490. *pdwNumPages = (sizeof(LOGPAGE) + sizeof(LOGRECORD) + Size)/pPage->Size;
  491. if ((sizeof(LOGPAGE) + sizeof(LOGRECORD) + Size) % pPage->Size)
  492. *pdwNumPages += 1;
  493. ClRtlLogPrint(LOG_NOISE,
  494. "[LM] LogpAppendPage : Large record Size=%1!u! dwNumPages=%2!u!\r\n",
  495. Size, *pdwNumPages);
  496. /*
  497. //SS: dont restrict record size here- if the registry takes it
  498. //make the best effort to log it
  499. if (*pdwNumPages > MAXNUMPAGES_PER_RECORD)
  500. {
  501. Status = ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE;
  502. goto FnExit;
  503. }
  504. */
  505. }
  506. Current = LSNTORECORD(pPage, Log->NextLsn);
  507. //
  508. // There must always be enough space remaining in the page to write
  509. // an end-of-page log record.
  510. //
  511. CL_ASSERT((RECORDOFFSETINPAGE(pPage, Current) + sizeof(LOGRECORD)) <= pPage->Size);
  512. //
  513. // If there is not enough space in this page for the requested data and
  514. // the end-of-page log record, write the end-of-page record, send the
  515. // page off to disk, and allocate a new page.
  516. //
  517. if ((RECORDOFFSETINPAGE(pPage, Current) + Size + sizeof(LOGRECORD)) > pPage->Size) {
  518. //
  519. // Create an end-of-page record
  520. //
  521. Current->Signature = LOGREC_SIG;
  522. Current->RecordSize = pPage->Size - RECORDOFFSETINPAGE(pPage, Current) + (sizeof(LOGPAGE)-sizeof(LOGRECORD));
  523. Current->ResourceManager = RMPageEnd;
  524. Current->Transaction = 0;
  525. Current->Flags = 0;
  526. GetSystemTimeAsFileTime(&Current->Timestamp);
  527. Current->NumPages = 0;
  528. //
  529. // PERFNOTE John Vert (jvert) 18-Dec-1995
  530. // No reason this has to be synchronous, there is no commit
  531. // necessary here. If we were smart, we would just post these
  532. // writes and have them complete to a queue which would free
  533. // up or recycle the memory.
  534. //
  535. ClRtlLogPrint(LOG_NOISE,
  536. "[LM] LogpAppendPage : Writing %1!u! bytes to disk at offset 0x%2!08lx!\r\n",
  537. pPage->Size, pPage->Offset);
  538. //
  539. // Write the current page to disk.
  540. //
  541. Log->Overlapped.Offset = pPage->Offset;
  542. Log->Overlapped.OffsetHigh = 0;
  543. Status = LogpWrite(Log, pPage, pPage->Size, &BytesWritten);
  544. if (Status != ERROR_SUCCESS)
  545. {
  546. CL_LOGFAILURE(Status);
  547. goto FnExit;
  548. }
  549. LastLsn = Current->CurrentLsn;
  550. //set the flushed LSN as the LSN of the last record that was committed
  551. Log->FlushedLsn = Log->NextLsn;
  552. Log->NextLsn = LastLsn + Current->RecordSize;
  553. //
  554. // Create new page
  555. //
  556. pPage->Offset += pPage->Size; // voila, new page!
  557. Current = &pPage->FirstRecord; // log record immediately following page header
  558. Current->PreviousLsn = LastLsn;
  559. Current->CurrentLsn = Log->NextLsn;
  560. //make sure there is enough room in the disk for the new page
  561. //if there isnt grow the file.
  562. //if the file has reached its max ceiling, pbMaxFileSizeReached is set to true
  563. //At this point, we try and reset the log file
  564. //SS:Note that if a log file max size is smaller than the number of pages
  565. //required to contain the record, then we will not be able to grow it
  566. //even after resetting it. This means that that right will fail
  567. if ((Status = LogpGrowLog(Log, (*pdwNumPages+1) * Log->SectorSize)) != ERROR_SUCCESS)
  568. {
  569. if (Status == ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE)
  570. *pbMaxFileSizeReached = TRUE;
  571. goto FnExit;
  572. }
  573. }
  574. *Record = Current;
  575. //if the record is a large record but does not use the second last page
  576. //completely, extend it to fill the second last page completely and add the
  577. //size of the logpage so that offset+currentsize points to the eop record.
  578. if ((*pdwNumPages) &&
  579. ((Size + sizeof(LOGPAGE) - sizeof(LOGRECORD)) <=
  580. ((*pdwNumPages - 1) * pPage->Size)))
  581. {
  582. CL_ASSERT(*pdwNumPages > 1);
  583. //large records always start on the beginning of the first page
  584. //the next lsn now points to the first record on the next page
  585. Size = pPage->Size * (*pdwNumPages - 1);
  586. ClRtlLogPrint(LOG_NOISE,
  587. "[LM] LogpAppendPage : the record fits in one page but not with an eop\r\n");
  588. }
  589. Current->RecordSize = Size;
  590. // Advance to next LSN
  591. LastLsn = Current->CurrentLsn;
  592. Log->NextLsn = LastLsn + Current->RecordSize;
  593. //fill in its LSN header
  594. if (*pdwNumPages == 0)
  595. {
  596. //for a large record, logpWriteLargeRecord, will set the next
  597. //lsn
  598. Current = LSNTORECORD(pPage, Log->NextLsn);
  599. Current->PreviousLsn = LastLsn;
  600. Current->CurrentLsn = Log->NextLsn;
  601. }
  602. pRetPage = pPage;
  603. FnExit:
  604. if (Status != ERROR_SUCCESS)
  605. SetLastError(Status);
  606. return(pRetPage);
  607. }
  608. DWORD
  609. LogpInitLog(
  610. IN PLOG pLog
  611. )
  612. /*++
  613. Routine Description:
  614. Initializes a newly created log file.
  615. Arguments:
  616. Log - Supplies the log to be created.
  617. Return Value:
  618. ERROR_SUCCESS if successful
  619. Win32 error code if unsuccessful.
  620. --*/
  621. {
  622. PLOG_HEADER Header=NULL;
  623. PLOGPAGE pPage=NULL;
  624. PLOGRECORD Record;
  625. LPWSTR FileName;
  626. DWORD NameLen;
  627. DWORD MaxLen;
  628. DWORD Status;
  629. DWORD dwBytesWritten;
  630. ClRtlLogPrint(LOG_NOISE,
  631. "[LM] LogpInitLog : Entry pLog=0x%1!08lx!\r\n",
  632. pLog);
  633. //
  634. // Grow the file to accomodate header and the first log page.
  635. //
  636. pLog->FileSize = pLog->FileAlloc = 0;
  637. Status = LogpGrowLog(pLog, 2 * pLog->SectorSize);
  638. if (Status != ERROR_SUCCESS)
  639. {
  640. goto FnExit;
  641. }
  642. //
  643. // Allocate and initialize log header.
  644. //
  645. Header = AlignAlloc(pLog->SectorSize);
  646. if (Header == NULL) {
  647. Status = ERROR_NOT_ENOUGH_MEMORY;
  648. goto FnExit;
  649. }
  650. Header->Signature = LOG_HEADER_SIG;
  651. Header->HeaderSize = pLog->SectorSize;
  652. Header->LastChkPtLsn = NULL_LSN;
  653. GetSystemTimeAsFileTime(&(Header->CreateTime));
  654. FileName = pLog->FileName;
  655. NameLen = lstrlenW(FileName);
  656. MaxLen = sizeof(Header->FileName) / sizeof(WCHAR) - 1;
  657. if (NameLen > MaxLen) {
  658. FileName += (NameLen - MaxLen);
  659. }
  660. lstrcpyW(Header->FileName,FileName);
  661. //
  662. // Write header to disk
  663. //
  664. pLog->Overlapped.Offset = 0;
  665. pLog->Overlapped.OffsetHigh = 0;
  666. ClRtlLogPrint(LOG_NOISE,
  667. "[LM] LogpAppendPage : Writing %1!u! bytes to disk at offset 0x%2!08lx!\r\n",
  668. Header->HeaderSize, pLog->Overlapped.Offset);
  669. if ((Status = LogpWrite(pLog, Header, Header->HeaderSize, &dwBytesWritten))
  670. != ERROR_SUCCESS)
  671. {
  672. ClRtlLogPrint(LOG_UNUSUAL,
  673. "[LM] LogpInitLog: failed to write the file header, Error=0x%1!08lx!\r\n",
  674. Status);
  675. CL_LOGFAILURE(Status);
  676. goto FnExit;
  677. }
  678. //
  679. // Allocate and initialize next log page.
  680. //
  681. pPage = AlignAlloc(pLog->SectorSize);
  682. if (pPage == NULL) {
  683. Status = ERROR_NOT_ENOUGH_MEMORY;
  684. goto FnExit;
  685. }
  686. pLog->ActivePage = pPage;
  687. pPage->Offset = Header->HeaderSize;
  688. pPage->Size = pLog->SectorSize;
  689. Record = &pPage->FirstRecord;
  690. Record->PreviousLsn = NULL_LSN;
  691. Record->CurrentLsn = pLog->NextLsn = MAKELSN(pPage, Record);
  692. pLog->FlushedLsn = pLog->NextLsn;
  693. #if DBG
  694. {
  695. DWORD dwOldProtect;
  696. DWORD Status;
  697. BOOL VPWorked;
  698. VPWorked = VirtualProtect(pPage, pLog->SectorSize, PAGE_READONLY, & dwOldProtect);
  699. Status = GetLastError();
  700. CL_ASSERT( VPWorked );
  701. }
  702. #endif
  703. ClRtlLogPrint(LOG_NOISE,
  704. "[LM] LogpInitLog : NextLsn=0x%1!08lx! FileAlloc=0x%2!08lx! ActivePageOffset=0x%3!08lx!\r\n",
  705. pLog->NextLsn, pLog->FileAlloc, pPage->Offset);
  706. FnExit:
  707. if (Header) {
  708. AlignFree(Header);
  709. }
  710. return(Status);
  711. }
  712. /****
  713. @func DWORD | LogpMountLog| Mounts an existing log file. Reads the log
  714. header, verifies the log integrity, and sets up
  715. the LOG structure to support further operations.
  716. @parm IN PLOG | pLog | Supplies a pointer to the log structure.
  717. @rdesc Returns ERROR_SUCCESS if successful, else returns the error code. If
  718. the log file doesnt look correct, it returns ERROR_LOG_CORRUPT.
  719. @comm This is called by LogCreate() to mount an existing log file.
  720. LogCreate() calls LogpInitLog(), if this function returns
  721. ERROR_CLUSTERLOG_CORRUPT.
  722. @xref <f LogCreate>
  723. ****/
  724. DWORD
  725. LogpMountLog(
  726. IN PLOG pLog
  727. )
  728. {
  729. DWORD dwError = ERROR_SUCCESS;
  730. DWORD dwFileSizeHigh;
  731. PLOGRECORD pRecord;
  732. PLOGPAGE pPage;
  733. DWORD Status;
  734. LSN Lsn,PrevLsn;
  735. int PageIndex, OldPageIndex;
  736. BOOL bLastRecord;
  737. DWORD dwBytesRead;
  738. TRID OldTransaction;
  739. FILETIME LastTimestamp;
  740. LSN ChkPtLsn = NULL_LSN; //the checkptlsn read from the header
  741. LSN LastChkPtLsn = NULL_LSN; // the last checkptlsn record seen while validating
  742. ClRtlLogPrint(LOG_NOISE,
  743. "[LM] LogpMountLog : Entry pLog=0x%1!08lx!\r\n",
  744. pLog);
  745. //check the size
  746. pLog->FileSize = GetFileSize(pLog->FileHandle, &dwFileSizeHigh);
  747. if ((pLog->FileSize == 0xFFFFFFFF) &&
  748. ((dwError = GetLastError()) != NO_ERROR))
  749. {
  750. CL_UNEXPECTED_ERROR(dwError);
  751. ClRtlLogPrint(LOG_UNUSUAL,
  752. "[LM] LogpMountLog GetFileSize returned error=0x%1!08lx!\r\n",
  753. dwError);
  754. goto FnExit;
  755. }
  756. ClRtlLogPrint(LOG_NOISE,
  757. "[LM] LogpMountLog::Quorumlog File size=0x%1!08lx!\r\n",
  758. pLog->FileSize);
  759. //dont let the file grow more than 4 gigabytes or the max limit
  760. if ((dwFileSizeHigh != 0 ) || (pLog->FileSize > pLog->MaxFileSize))
  761. {
  762. //set in the eventlog
  763. dwError = ERROR_CLUSTERLOG_CORRUPT;
  764. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT, pLog->FileName);
  765. goto FnExit;
  766. }
  767. //if filesize is zero, the file exists but essentially needs to
  768. //be created, this is needed for reset functionality
  769. if (!pLog->FileSize)
  770. {
  771. dwError = LogpInitLog(pLog);
  772. goto FnExit;
  773. }
  774. //check if the file is atleast as big as one page.
  775. //assume a fixed sector size
  776. if (pLog->FileSize < pLog->SectorSize)
  777. {
  778. ClRtlLogPrint(LOG_UNUSUAL,
  779. "[LM] LogpMountLog::file is smaller than log header, error=0x%1!08lx!\r\n",
  780. dwError);
  781. //set in the eventlog
  782. dwError = ERROR_CLUSTERLOG_CORRUPT;
  783. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT, pLog->FileName);
  784. goto FnExit;
  785. }
  786. //allocate memore for the active page
  787. pPage = AlignAlloc(pLog->SectorSize);
  788. if (pPage == NULL)
  789. {
  790. dwError = ERROR_NOT_ENOUGH_MEMORY;
  791. CL_LOGFAILURE(dwError);
  792. goto FnExit;
  793. }
  794. //validate the file header, returns the time stamp of the header
  795. dwError = LogpCheckFileHeader(pLog, &(pPage->Offset),&LastTimestamp,
  796. &ChkPtLsn);
  797. if (dwError != ERROR_SUCCESS)
  798. {
  799. ClRtlLogPrint(LOG_UNUSUAL,
  800. "[LM] LogpMountLog::LogpCheckFileHeader failed, error=0x%1!08lx!\r\n",
  801. dwError);
  802. goto FnExit;
  803. }
  804. //traverse the chain of records, to find the active page
  805. //find the next lsn while validating the records.
  806. //pPageOffset is set by LogpCheckFileHeader
  807. pPage->Size = pLog->SectorSize;
  808. pRecord = &pPage->FirstRecord;
  809. OldPageIndex = -1;
  810. OldTransaction = 0;
  811. bLastRecord = FALSE;
  812. Lsn = MAKELSN(pPage, pRecord);
  813. PrevLsn = NULL_LSN;
  814. while (!bLastRecord)
  815. {
  816. //
  817. // Translate LSN to a page number and offset within the page
  818. //
  819. PageIndex = LSNTOPAGE(Lsn);
  820. if (PageIndex != OldPageIndex)
  821. {
  822. //read the page
  823. (pLog->Overlapped).Offset = PageIndex * pLog->SectorSize;
  824. (pLog->Overlapped).OffsetHigh = 0;
  825. ClRtlLogPrint(LOG_NOISE,
  826. "[LM] LogpMountLog::reading %1!u! bytes at offset 0x%2!08lx!\r\n",
  827. pLog->SectorSize, PageIndex * pLog->SectorSize);
  828. dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
  829. //if it is the last page, then set the new page as the active
  830. //page
  831. if (dwError)
  832. {
  833. if (dwError == ERROR_HANDLE_EOF)
  834. {
  835. ClRtlLogPrint(LOG_NOISE,
  836. "[LM] LogpMountLog::eof detected, extend this file,setting this page active\r\n");
  837. //find the current allocated size,
  838. //file alloc is currently at the end of the previous page
  839. pLog->FileAlloc = PageIndex * pLog->SectorSize;
  840. //extend the file to accomodate this page
  841. Status = LogpGrowLog(pLog, pLog->SectorSize);
  842. if (Status != ERROR_SUCCESS)
  843. {
  844. //set in the eventlog
  845. dwError = ERROR_CLUSTERLOG_CORRUPT;
  846. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  847. goto FnExit;
  848. }
  849. //file alloc should now point to the end of the current page
  850. //not fatal, set this page as current page
  851. dwError = ERROR_SUCCESS;
  852. pPage->Offset = (pLog->Overlapped).Offset;
  853. pPage->Size = pLog->SectorSize;
  854. //set the LSN to be the first LSN on this page.
  855. pRecord = &pPage->FirstRecord;
  856. pRecord->PreviousLsn = PrevLsn;
  857. Lsn = pRecord->CurrentLsn = MAKELSN(pPage, pRecord);
  858. bLastRecord = TRUE;
  859. continue;
  860. }
  861. else
  862. goto FnExit;
  863. }
  864. //the read may succeed and the page may have invalid data
  865. //since the last log writes may not be flushed
  866. if ((pPage->Offset != (pLog->Overlapped).Offset) ||
  867. (pPage->Size != pLog->SectorSize))
  868. {
  869. ClRtlLogPrint(LOG_NOISE,
  870. "[LM] LogpMountLog::unflushed page detected, set as active\r\n");
  871. pPage->Offset = (pLog->Overlapped).Offset;
  872. pPage->Size = pLog->SectorSize;
  873. pRecord = &pPage->FirstRecord;
  874. pRecord->PreviousLsn = PrevLsn;
  875. Lsn = pRecord->CurrentLsn = MAKELSN(pPage, pRecord);
  876. bLastRecord = TRUE;
  877. continue;
  878. }
  879. //set the new page index to the old one
  880. OldPageIndex = PageIndex;
  881. }
  882. ClRtlLogPrint(LOG_NOISE,
  883. "[LM] LogpMountLog::checking LSN 0x%1!08lx!\r\n",
  884. Lsn);
  885. pRecord = LSNTORECORD(pPage, Lsn);
  886. //if the record is doesnt look valid then set the active
  887. //record and page as the current one
  888. if ((pRecord->Signature != LOGREC_SIG) || (pRecord->CurrentLsn != Lsn))
  889. {
  890. ClRtlLogPrint(LOG_NOISE,
  891. "[LM] LogpMountLog: Reached last record, RecordLSN=0x%1!08lx!...\n",
  892. pRecord->CurrentLsn);
  893. bLastRecord = TRUE;
  894. continue;
  895. }
  896. //if the new time stamp is smaller, then log a message
  897. if (CompareFileTime(&LastTimestamp, &(pRecord->Timestamp)) > 0)
  898. {
  899. //
  900. // Chittur Subbaraman (chitturs) - 3/7/2001
  901. //
  902. // Do not compare the timestamps for monotonic increase. Due to clocks between nodes
  903. // not being as close in sync as they should be, we run into situation in which
  904. // we stop mounting the log after a certain LSN. This leads the subsequent LogpValidateCheckpoint
  905. // to believe that the log is corrupted when in fact it is just time-screwed.
  906. //
  907. ClRtlLogPrint(LOG_UNUSUAL,
  908. "[LM] LogpMountLog: Timestamp in log is not monotonically increasing, LastTS=0x%1!08lx!, NewTS=0x%2!08lx!\n",
  909. LastTimestamp,
  910. pRecord->Timestamp);
  911. #if 0
  912. bLastRecord = TRUE;
  913. continue;
  914. #endif
  915. }
  916. //if it is a log management record
  917. if (pRecord->ResourceManager < RMAny)
  918. {
  919. // This record is a logmanagement record
  920. // if it is an end checkpoint record, remember that just in case
  921. // the header doesnt indicate that
  922. if (pRecord->ResourceManager == RMEndChkPt)
  923. LastChkPtLsn = Lsn;
  924. // Adjust the LSN to the next one
  925. PrevLsn = Lsn;
  926. Lsn = GETNEXTLSN(pRecord, TRUE);
  927. LastTimestamp = pRecord->Timestamp;
  928. continue;
  929. }
  930. //SS : should we also validate transaction ids on write
  931. //check that the transaction id is greater
  932. if (pRecord->Transaction < OldTransaction)
  933. {
  934. ClRtlLogPrint(LOG_UNUSUAL,
  935. "[LM] LogpMountLog: Current Xid less than last Xid, CurXid=0x%1!08lx!, LastXid=0x%2!08lx!...\n",
  936. pRecord->Transaction,
  937. OldTransaction);
  938. bLastRecord = TRUE;
  939. continue;
  940. }
  941. //save the current LSN,go the the next record if this is valid
  942. PrevLsn = Lsn;
  943. //if this is a large record, skip the eop on the last page
  944. //but look for an eop to ensure that the large record is valid
  945. //SS: Have checksums for phase 2
  946. if (pRecord->NumPages)
  947. {
  948. //if the record is not valid, then set this as the current
  949. //record
  950. if (LogpValidateLargeRecord(pLog, pRecord, &Lsn) != ERROR_SUCCESS)
  951. {
  952. ClRtlLogPrint(LOG_NOISE,
  953. "[LM] LogpMountLog::Invalid large record at LSN 0x%1!08lx!\r\n",
  954. Lsn);
  955. bLastRecord = TRUE;
  956. continue;
  957. }
  958. }
  959. else
  960. {
  961. Lsn = GETNEXTLSN(pRecord, TRUE);
  962. }
  963. //this is a valid record, if the transaction id is the same as the last id
  964. //invalidate the previous LSN
  965. //SS: local xsactions have the same id,
  966. if ((pRecord->Transaction == OldTransaction) &&
  967. ((pRecord->XsactionType == TTCommitXsaction) ||
  968. (pRecord->XsactionType == TTCompleteXsaction)))
  969. LogpInvalidatePrevRecord(pLog, pRecord);
  970. //save the the old transaction id for completed or committed records
  971. //save the time stamp and the transaction id of the current record
  972. LastTimestamp = pRecord->Timestamp;
  973. if ((pRecord->XsactionType == TTCompleteXsaction) ||
  974. (pRecord->XsactionType == TTCommitXsaction))
  975. OldTransaction = pRecord->Transaction;
  976. }
  977. // set the active page and the next record
  978. pLog->NextLsn = Lsn;
  979. pLog->ActivePage = pPage;
  980. //set the file alloc size, to the end of the current page
  981. pLog->FileAlloc = pPage->Offset + pPage->Size;
  982. CL_ASSERT(pLog->FileAlloc <= pLog->FileSize);
  983. //make sure that the next lsn is prepared
  984. pRecord = LSNTORECORD(pPage, Lsn);
  985. pRecord->PreviousLsn = PrevLsn;
  986. pRecord->CurrentLsn = Lsn;
  987. pLog->FlushedLsn = Lsn;
  988. //validate the chkpoint record
  989. //either it should be null or there should be a valid checkpoint record in there
  990. dwError = LogpValidateChkPoint(pLog, ChkPtLsn, LastChkPtLsn);
  991. ClRtlLogPrint(LOG_NOISE,
  992. "[LM] LogpMountLog : NextLsn=0x%1!08lx! FileAlloc=0x%2!08lx! ActivePageOffset=0x%3!08lx!\r\n",
  993. pLog->NextLsn, pLog->FileAlloc, pPage->Offset);
  994. #if DBG
  995. {
  996. DWORD dwOldProtect;
  997. DWORD Status;
  998. BOOL VPWorked;
  999. VPWorked = VirtualProtect(pPage, pLog->SectorSize, PAGE_READONLY, & dwOldProtect);
  1000. Status = GetLastError();
  1001. CL_ASSERT( VPWorked );
  1002. }
  1003. #endif
  1004. FnExit:
  1005. return(dwError);
  1006. }
  1007. /****
  1008. @func DWORD | LogpMountLog| Mounts an existing log file. Reads the log
  1009. header, verifies the log integrity, and sets up
  1010. the LOG structure to support further operations.
  1011. @parm IN PLOG | pLog | Supplies a pointer to the log structure.
  1012. @parm OUT LPDWORD | pdwLogHeaderSize | Returns the size of the log header structure.
  1013. @parm OUT FILETIME | *pHeaderTimestamp | Returns the time when the log header
  1014. was created.
  1015. @rdesc Returns ERROR_SUCCESS if successful, else returns the error code. If
  1016. the log file doesnt look correct, it returns ERROR_CLUSTERLOG_CORRUPT.
  1017. @comm This is called by LogpMountLog() to validate the header of a log file.
  1018. @xref <f LogpMountLog>
  1019. ****/
  1020. DWORD LogpCheckFileHeader(
  1021. IN PLOG pLog,
  1022. OUT LPDWORD pdwLogHeaderSize,
  1023. OUT FILETIME *pHeaderTimestamp,
  1024. OUT LSN *pChkPtLsn
  1025. )
  1026. {
  1027. PLOG_HEADER pLogHeader;
  1028. DWORD dwError = ERROR_SUCCESS;
  1029. DWORD dwBytesRead;
  1030. pLogHeader = AlignAlloc(pLog->SectorSize);
  1031. if (pLogHeader == NULL) {
  1032. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1033. goto FnExit;
  1034. }
  1035. //read the header
  1036. (pLog->Overlapped).Offset = 0;
  1037. (pLog->Overlapped).OffsetHigh = 0;
  1038. if ((dwError = LogpRead(pLog, pLogHeader, pLog->SectorSize, &dwBytesRead))
  1039. != ERROR_SUCCESS)
  1040. {
  1041. ClRtlLogPrint(LOG_UNUSUAL,
  1042. "[LM] LogpCheckFileHeader::Read of the log header failed, error=0x%1!08lx!\r\n",
  1043. dwError);
  1044. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1045. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  1046. goto FnExit;
  1047. }
  1048. if (dwBytesRead != pLog->SectorSize)
  1049. {
  1050. ClRtlLogPrint(LOG_UNUSUAL,
  1051. "[LM] LogpCheckFileHeader::Failed to read the complete header,bytes read 0x%1!u!\r\n",
  1052. dwBytesRead);
  1053. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1054. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  1055. goto FnExit;
  1056. }
  1057. //validate the header
  1058. if (!ISVALIDHEADER((*pLogHeader)))
  1059. {
  1060. ClRtlLogPrint(LOG_UNUSUAL,
  1061. "[LM] LogpCheckFileHeader::the file header is corrupt.\r\n");
  1062. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1063. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  1064. goto FnExit;
  1065. }
  1066. *pdwLogHeaderSize = pLogHeader->HeaderSize;
  1067. *pHeaderTimestamp = pLogHeader->CreateTime;
  1068. *pChkPtLsn = pLogHeader->LastChkPtLsn;
  1069. FnExit:
  1070. if (pLogHeader)
  1071. {
  1072. AlignFree(pLogHeader);
  1073. }
  1074. return(dwError);
  1075. }
  1076. /****
  1077. @func DWORD | LogpValidateChkPt| This checks that the header points to the
  1078. last checkpoint. If not, it scans the log file from the end
  1079. and if it finds a checkpoint, updates the header with that information.
  1080. If no valid checkpoint exists, it sets the header Checkpt LSN to
  1081. NULL_LSN.
  1082. @parm IN PLOG | pLog | Supplies a pointer to the log structure.
  1083. @parm IN LSN | ChkPtLsn | Supplies the ChkPtLsn read from the log header
  1084. @parm IN LSN | LastChkPtLsn | Supplies the last valid chkpoint record found
  1085. during the mount process.
  1086. @rdesc Returns ERROR_SUCCESS if successful, else returns the error code. If
  1087. the log file doesnt look correct, it returns ERROR_CLUSTERLOG_CORRUPT.
  1088. @comm This is called by LogpMountLog() to validate the header of a log file.
  1089. @xref <f LogpMountLog>
  1090. ****/
  1091. DWORD LogpValidateChkPoint(
  1092. IN PLOG pLog,
  1093. IN LSN ChkPtLsn,
  1094. IN LSN LastChkPtLsn)
  1095. {
  1096. PLOG_HEADER pLogHeader = NULL;
  1097. DWORD dwError = ERROR_SUCCESS;
  1098. DWORD dwNumBytes;
  1099. DWORD Status;
  1100. RMID Resource;
  1101. RMTYPE RmType;
  1102. TRTYPE TrType;
  1103. LOG_CHKPTINFO ChkPtInfo;
  1104. TRID TrId;
  1105. HANDLE hChkPtFile = INVALID_HANDLE_VALUE;
  1106. ClRtlLogPrint(LOG_NOISE,
  1107. "[LM] LogpValidateChkPoint: Entry\r\n");
  1108. CL_ASSERT(LastChkPtLsn < pLog->NextLsn);
  1109. //if the header indicates that there is a checkpoint
  1110. //and the most recent checkpoint record is the same as the one in the header
  1111. //there is nothing to do, return success.
  1112. if ((ChkPtLsn == LastChkPtLsn) && (ChkPtLsn < pLog->NextLsn))
  1113. {
  1114. goto ValidateChkPtFile;
  1115. }
  1116. //if the header indicates there is a check point but it wasnt mounted,
  1117. //log corruption in the event log
  1118. if (ChkPtLsn >= pLog->NextLsn)
  1119. {
  1120. ClRtlLogPrint(LOG_NOISE,
  1121. "[LM] LogpValidateChkPoint: ChkptLsn in header wasnt validated by mount\r\n");
  1122. //but the mount procedure failed to validate that record
  1123. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT, pLog->FileName);
  1124. #if DBG
  1125. if (IsDebuggerPresent())
  1126. DebugBreak();
  1127. #endif
  1128. }
  1129. ClRtlLogPrint(LOG_NOISE,
  1130. "[LM] LogpValidateChkPoint: Updating header with the LastChkPtLsn=0x%1!08lx!\r\n",
  1131. LastChkPtLsn);
  1132. //if not it could be that a checkpoint was taken but the header couldnt
  1133. //be flushed with the last chkpt
  1134. pLogHeader = (PLOG_HEADER)AlignAlloc(pLog->SectorSize);
  1135. if (pLogHeader == NULL) {
  1136. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1137. goto FnExit;
  1138. }
  1139. //read the header
  1140. (pLog->Overlapped).Offset = 0;
  1141. (pLog->Overlapped).OffsetHigh = 0;
  1142. if ((dwError = LogpRead(pLog, pLogHeader, pLog->SectorSize, &dwNumBytes))
  1143. != ERROR_SUCCESS)
  1144. {
  1145. ClRtlLogPrint(LOG_UNUSUAL,
  1146. "[LM] LogpValidateChkPoint::Read of the log header failed, error=0x%1!08lx!\r\n",
  1147. dwError);
  1148. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1149. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  1150. goto FnExit;
  1151. }
  1152. //recheck the header signature
  1153. if (!ISVALIDHEADER((*pLogHeader)))
  1154. {
  1155. ClRtlLogPrint(LOG_UNUSUAL,
  1156. "[LM] LogpCheckFileHeader::the file header is corrupt.\r\n");
  1157. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1158. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  1159. goto FnExit;
  1160. }
  1161. //set the last lsn
  1162. pLogHeader->LastChkPtLsn = LastChkPtLsn;
  1163. //write the header back
  1164. pLog->Overlapped.Offset = 0;
  1165. pLog->Overlapped.OffsetHigh = 0;
  1166. ClRtlLogPrint(LOG_NOISE,
  1167. "[LM] LogpValidateChkPoint : Writing %1!u! bytes to disk at offset 0x%2!08lx!\r\n",
  1168. pLogHeader->HeaderSize, pLog->Overlapped.Offset);
  1169. if ((dwError = LogpWrite(pLog, pLogHeader, pLogHeader->HeaderSize, &dwNumBytes))
  1170. != ERROR_SUCCESS)
  1171. {
  1172. ClRtlLogPrint(LOG_UNUSUAL,
  1173. "[LM] LogpInitLog: failed to write the file header, Error=0x%1!08lx!\r\n",
  1174. dwError);
  1175. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1176. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  1177. goto FnExit;
  1178. }
  1179. ValidateChkPtFile:
  1180. //no need to verify that the checkpoint file exists
  1181. if (LastChkPtLsn == NULL_LSN)
  1182. goto FnExit;
  1183. dwNumBytes = sizeof(LOG_CHKPTINFO);
  1184. if ((LogRead((HLOG)pLog , LastChkPtLsn, &Resource, &RmType,
  1185. &TrId, &TrType, &ChkPtInfo, &dwNumBytes)) == NULL_LSN)
  1186. {
  1187. ClRtlLogPrint(LOG_UNUSUAL,
  1188. "[LM] LogpValidateChkPt::LogRead for chkpt lsn 0x%1!08lx! failed\r\n",
  1189. pLogHeader->LastChkPtLsn);
  1190. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1191. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  1192. goto FnExit;
  1193. }
  1194. if (Resource != RMEndChkPt)
  1195. {
  1196. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1197. CL_LOGFAILURE(dwError);
  1198. CL_LOGCLUSERROR1(LM_LOG_CORRUPT, pLog->FileName);
  1199. goto FnExit;
  1200. }
  1201. //get the file name, try and open it
  1202. hChkPtFile = CreateFileW(ChkPtInfo.szFileName,
  1203. GENERIC_READ ,
  1204. FILE_SHARE_READ|FILE_SHARE_WRITE,
  1205. NULL,
  1206. OPEN_EXISTING,
  1207. 0,
  1208. NULL);
  1209. if (hChkPtFile == INVALID_HANDLE_VALUE)
  1210. {
  1211. ClRtlLogPrint(LOG_UNUSUAL,
  1212. "[LM] LogpValidateChkPoint: The checkpt file %1!ws! could not be opened. Error=%2!u!\r\n",
  1213. ChkPtInfo.szFileName, GetLastError());
  1214. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1215. CL_LOGCLUSWARNING1(LM_LOG_CORRUPT,pLog->FileName);
  1216. goto FnExit;
  1217. }
  1218. FnExit:
  1219. ClRtlLogPrint(LOG_NOISE,
  1220. "[LM] LogpValidateChkPoint: Exit, returning 0x%1!08lx!\r\n",
  1221. dwError);
  1222. if (hChkPtFile != INVALID_HANDLE_VALUE)
  1223. CloseHandle(hChkPtFile);
  1224. if (pLogHeader) AlignFree(pLogHeader);
  1225. return(dwError);
  1226. }
  1227. /****
  1228. @func DWORD | LogpValidateLargeRecord| Validates a large record and advances
  1229. the LSN to the record following the eop record which marks the end of
  1230. a large record.
  1231. @parm IN PLOG | pLog | Supplies a pointer to the log structure.
  1232. @parm IN PLOGRECORD | pRecord| Supplies a pointer to the large record.
  1233. @parm IN PLOGRECORD | pNextLsn| The LSN of the record following the
  1234. EOP record after the large record is returned.
  1235. @rdesc If a valid EOP record exists after the large record, the large
  1236. record is considered valid and this function returns ERROR_SUCCESS,
  1237. else it returns an error code.
  1238. @comm This is called by LogpMountLog() to validate large records.
  1239. @xref <f LogpMountLog>
  1240. ****/
  1241. DWORD LogpValidateLargeRecord(
  1242. IN PLOG pLog,
  1243. IN PLOGRECORD pRecord,
  1244. OUT LSN *pNextLsn)
  1245. {
  1246. DWORD dwError = ERROR_SUCCESS;
  1247. LSN EopLsn;
  1248. PLOGRECORD pEopRecord;
  1249. PLOGPAGE pPage = NULL;
  1250. DWORD dwBytesRead;
  1251. DWORD dwPageIndex;
  1252. //traverse the chain of records, to find the active page
  1253. //find the next lsn
  1254. pPage = AlignAlloc(pLog->SectorSize);
  1255. if (pPage == NULL)
  1256. {
  1257. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1258. CL_LOGFAILURE(dwError);
  1259. goto FnExit;
  1260. }
  1261. dwPageIndex = LSNTOPAGE(pRecord->CurrentLsn);
  1262. dwPageIndex = (dwPageIndex + pRecord->NumPages - 1);
  1263. //read the last page for the large record
  1264. (pLog->Overlapped).Offset = dwPageIndex * pLog->SectorSize;
  1265. (pLog->Overlapped).OffsetHigh = 0;
  1266. ClRtlLogPrint(LOG_NOISE,
  1267. "[LM] LogpValidateLargeRecord::reading %1!u! bytes at offset 0x%2!08lx!\r\n",
  1268. pLog->SectorSize, dwPageIndex * pLog->SectorSize);
  1269. dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
  1270. //if there are no errors, then check the last record
  1271. if (dwError == ERROR_SUCCESS)
  1272. {
  1273. //read the page, make sure that the eop record follows the
  1274. //large record
  1275. EopLsn = GETNEXTLSN(pRecord,TRUE);
  1276. CL_ASSERT(LSNTOPAGE(EopLsn) == dwPageIndex);
  1277. pEopRecord = (PLOGRECORD)((ULONG_PTR) pPage +
  1278. (EopLsn - (pLog->Overlapped).Offset));
  1279. if ((pEopRecord->Signature == LOGREC_SIG) &&
  1280. (pEopRecord->ResourceManager == RMPageEnd) &&
  1281. (CompareFileTime(&(pRecord->Timestamp),&(pEopRecord->Timestamp)) <= 0)
  1282. )
  1283. {
  1284. //move to the next page
  1285. *pNextLsn = GETNEXTLSN(pEopRecord, TRUE);
  1286. }
  1287. else
  1288. dwError = ERROR_CLUSTERLOG_CORRUPT;
  1289. }
  1290. FnExit:
  1291. if (pPage)
  1292. AlignFree(pPage);
  1293. return(dwError);
  1294. }
  1295. /****
  1296. @func DWORD | LogpInvalidatePrevRecord| This function is called at mount time to
  1297. invalidate a previous record with the same transaction id.
  1298. @parm IN PLOG | pLog | Supplies a pointer to the log structure.
  1299. @parm IN PLOGRECORD | pRecord| Supplies a pointer to the record.
  1300. @rdesc Returns ERROR_SUCCESS on success, else returns error code.
  1301. @comm This is called by LogpMountLog() to invalidate a record with the same transaction
  1302. id. This is because the locker node may write a transaction record to the
  1303. log and die before it can be propagated to other nodes. This transaction record
  1304. is then invalid.
  1305. @xref <f LogpMountLog>
  1306. ****/
  1307. DWORD LogpInvalidatePrevRecord(
  1308. IN PLOG pLog,
  1309. IN PLOGRECORD pRecord
  1310. )
  1311. {
  1312. DWORD dwError = ERROR_SUCCESS;
  1313. PLOGRECORD pPrevRecord;
  1314. LSN PrevLsn;
  1315. PLOGPAGE pPage = NULL;
  1316. DWORD dwBytesRead;
  1317. DWORD dwPageIndex;
  1318. TRID TrId;
  1319. BOOL bPrevRecordFound = FALSE;
  1320. ClRtlLogPrint(LOG_NOISE,
  1321. "[LM] LogpInvalidatePrevRecord : Entry, TrId=%1!08lx!\r\n",
  1322. pRecord->Transaction);
  1323. //allocate a page to read the record headers
  1324. pPage = AlignAlloc(SECTOR_SIZE);
  1325. if (pPage == NULL)
  1326. {
  1327. CL_LOGFAILURE(dwError = ERROR_NOT_ENOUGH_MEMORY);
  1328. goto FnExit;
  1329. }
  1330. TrId = pRecord->Transaction;
  1331. //try and find the last valid transaction with the same id, there should be one
  1332. pPrevRecord = pRecord;
  1333. while (!bPrevRecordFound)
  1334. {
  1335. PrevLsn = pPrevRecord->PreviousLsn;
  1336. if (PrevLsn == NULL_LSN)
  1337. break;
  1338. dwPageIndex = LSNTOPAGE(PrevLsn);
  1339. pLog->Overlapped.Offset = dwPageIndex * pLog->SectorSize;
  1340. pLog->Overlapped.OffsetHigh = 0;
  1341. dwError = LogpRead(pLog, pPage, pLog->SectorSize, &dwBytesRead);
  1342. if (dwError != ERROR_SUCCESS)
  1343. goto FnExit;
  1344. pPrevRecord = LSNTORECORD(pPage, PrevLsn);
  1345. if (pPrevRecord->ResourceManager < RMAny)
  1346. continue;
  1347. if ((pPrevRecord->ResourceManager == pRecord->ResourceManager) &&
  1348. (pPrevRecord->Transaction == TrId) &&
  1349. ((pPrevRecord->XsactionType == TTCompleteXsaction) ||
  1350. (pPrevRecord->XsactionType == TTCommitXsaction)))
  1351. {
  1352. bPrevRecordFound = TRUE;
  1353. pPrevRecord->ResourceManager = RMInvalidated;
  1354. //write the new page out
  1355. dwError = LogpWrite(pLog, pPage, pLog->SectorSize, &dwBytesRead);
  1356. if (dwError != ERROR_SUCCESS)
  1357. {
  1358. goto FnExit;
  1359. }
  1360. ClRtlLogPrint(LOG_NOISE,
  1361. "[LM] LogpInvalidatePrevRecord : record at LSN=%1!08lx! invalidated\r\n",
  1362. PrevLsn);
  1363. }
  1364. }
  1365. FnExit:
  1366. if (pPage) AlignFree(pPage);
  1367. return(dwError);
  1368. }
  1369. DWORD
  1370. LogpRead(
  1371. IN PLOG pLog,
  1372. OUT PVOID pBuf,
  1373. IN DWORD dwBytesToRead,
  1374. OUT PDWORD pdwBytesRead
  1375. )
  1376. /*++
  1377. Routine Description:
  1378. Reads a page(pLog->SectorSize) from the log file from the offsets set in pLog->Overlapped
  1379. structure.
  1380. Arguments:
  1381. Log - Supplies the log to be grown.
  1382. pBuf - Supplies the buffer to read into
  1383. dwBytesToRead - bytes to read
  1384. pdwBytesRead - pointer where the bytes read are returned
  1385. Return Value:
  1386. ERROR_SUCCESS if successful
  1387. Win32 error code if unsuccessful. ERROR_HANDLE_EOF if the end of file is
  1388. reached.
  1389. --*/
  1390. {
  1391. DWORD dwError=ERROR_SUCCESS;
  1392. BOOL Success;
  1393. *pdwBytesRead = 0;
  1394. //
  1395. // Make sure input buffer is aligned
  1396. //
  1397. CL_ASSERT(((ULONG_PTR)pBuf % 512) == 0);
  1398. Success = ReadFile(pLog->FileHandle,
  1399. pBuf,
  1400. dwBytesToRead,
  1401. pdwBytesRead,
  1402. &(pLog->Overlapped));
  1403. // NULL);
  1404. if (!Success)
  1405. {
  1406. // deal with the error code
  1407. switch (dwError = GetLastError())
  1408. {
  1409. case ERROR_IO_PENDING:
  1410. {
  1411. // asynchronous i/o is still in progress
  1412. // check on the results of the asynchronous read
  1413. Success = GetOverlappedResult(pLog->FileHandle,
  1414. &(pLog->Overlapped),
  1415. pdwBytesRead,
  1416. TRUE);
  1417. // if there was a problem ...
  1418. if (!Success)
  1419. {
  1420. // deal with the error code
  1421. switch (dwError = GetLastError())
  1422. {
  1423. //ss:for end of file dont log error
  1424. case ERROR_HANDLE_EOF:
  1425. break;
  1426. default:
  1427. // deal with other error cases
  1428. CL_LOGFAILURE(dwError);
  1429. break;
  1430. }
  1431. }
  1432. else
  1433. dwError = ERROR_SUCCESS;
  1434. break;
  1435. }
  1436. case ERROR_HANDLE_EOF:
  1437. break;
  1438. default:
  1439. CL_UNEXPECTED_ERROR(dwError);
  1440. break;
  1441. }
  1442. }
  1443. return(dwError);
  1444. }
  1445. DWORD
  1446. LogpWrite(
  1447. IN PLOG pLog,
  1448. IN PVOID pData,
  1449. IN DWORD dwBytesToWrite,
  1450. IN DWORD *pdwBytesWritten)
  1451. {
  1452. DWORD dwError=ERROR_SUCCESS;
  1453. BOOL Success;
  1454. *pdwBytesWritten = 0;
  1455. #if DBG
  1456. if (pLog->Overlapped.Offset == 0)
  1457. {
  1458. ClRtlLogPrint(LOG_NOISE,
  1459. "[LM] LogpWrite : Writing the file header, CheckPtLsn=0x%1!08lx!\r\n",
  1460. ((PLOG_HEADER)pData)->LastChkPtLsn);
  1461. }
  1462. #endif
  1463. Success = WriteFile(pLog->FileHandle,
  1464. pData,
  1465. dwBytesToWrite,
  1466. pdwBytesWritten,
  1467. &(pLog->Overlapped));
  1468. if (!Success)
  1469. {
  1470. // deal with the error code
  1471. switch (dwError = GetLastError())
  1472. {
  1473. case ERROR_IO_PENDING:
  1474. {
  1475. // asynchronous i/o is still in progress
  1476. // check on the results of the asynchronous read
  1477. Success = GetOverlappedResult(pLog->FileHandle,
  1478. &(pLog->Overlapped),
  1479. pdwBytesWritten,
  1480. TRUE);
  1481. // if there was a problem ...
  1482. if (!Success)
  1483. CL_LOGFAILURE((dwError = GetLastError()));
  1484. else
  1485. dwError = ERROR_SUCCESS;
  1486. break;
  1487. }
  1488. default:
  1489. CL_LOGFAILURE(dwError);
  1490. break;
  1491. }
  1492. }
  1493. return(dwError);
  1494. }
  1495. /****
  1496. @func DWORD | LogpWriteLargeRecordData | Writes thru the data for a
  1497. large record.
  1498. @parm PLOG | pLog | The pointer to the log.
  1499. @parm PLOGRECORD | pLogRecord | Supplies the logrecord where this record starts. The
  1500. record header is already written.
  1501. @parm PVOID | pLogData | A pointer to the large record data.
  1502. @parm DWORD | dwDataSize | The size of the large record data.
  1503. @comm Called by LogWrite() to write a large record. The maximum size is
  1504. restricted by the growth chunk size.
  1505. @xref <f LogCreate>
  1506. ****/
  1507. DWORD
  1508. LogpWriteLargeRecordData(
  1509. IN PLOG pLog,
  1510. IN PLOGRECORD pLogRecord,
  1511. IN PBYTE pLogData,
  1512. IN DWORD dwDataSize)
  1513. {
  1514. DWORD dwBytesWritten;
  1515. DWORD dwDataBytesWritten;
  1516. DWORD dwDataBytesLeft;
  1517. DWORD dwNumPagesLeft; //pages written
  1518. DWORD dwError=ERROR_SUCCESS;
  1519. PLOGRECORD Current;
  1520. DWORD Status;
  1521. LSN LastLsn;
  1522. DWORD dwOldOffset;
  1523. PLOGPAGE pPage;
  1524. PBYTE pLargeBuffer=NULL;
  1525. ClRtlLogPrint(LOG_UNUSUAL,
  1526. "[LM] LogpWriteLargeRecordData::dwDataSize=%1!u!\r\n",
  1527. dwDataSize);
  1528. pPage = pLog->ActivePage;
  1529. //write as much data into the current page as you possibly can
  1530. dwDataBytesWritten = pPage->Size - sizeof(LOGPAGE);
  1531. if (dwDataBytesWritten > dwDataSize)
  1532. dwDataBytesWritten = dwDataSize;
  1533. dwDataBytesLeft = dwDataSize - dwDataBytesWritten;
  1534. CopyMemory(&(pLogRecord->Data), pLogData, dwDataBytesWritten);
  1535. //flush this page
  1536. (pLog->Overlapped).Offset = pPage->Offset;
  1537. (pLog->Overlapped).OffsetHigh = 0;
  1538. ClRtlLogPrint(LOG_NOISE,
  1539. "[LM] LogpWriteLargeRecord : Writing(firstpageoflargerecord) %1!u! bytes to disk at offset 0x%2!08lx!\r\n",
  1540. pPage->Size, pPage->Offset);
  1541. if ((dwError = LogpWrite(pLog, pPage, pPage->Size, &dwBytesWritten))
  1542. != ERROR_SUCCESS)
  1543. {
  1544. ClRtlLogPrint(LOG_UNUSUAL,
  1545. "[LM] LogpWriteLargeRecordData::LogpWrite returned error=0x%1!08lx!\r\n",
  1546. dwError);
  1547. CL_LOGFAILURE(dwError);
  1548. goto FnExit;
  1549. }
  1550. //update the data pointer
  1551. pLogData += dwDataBytesWritten;
  1552. dwNumPagesLeft = pLogRecord->NumPages - 1;
  1553. //if the number of bytes left is greater than a page
  1554. //write everything but the last page
  1555. if (dwNumPagesLeft > 1)
  1556. {
  1557. dwDataBytesWritten = (dwNumPagesLeft - 1) * pPage->Size;
  1558. pLargeBuffer = AlignAlloc(dwDataBytesWritten);
  1559. if (pLargeBuffer == NULL)
  1560. {
  1561. dwError = ERROR_NOT_ENOUGH_MEMORY ;
  1562. CL_LOGFAILURE(ERROR_NOT_ENOUGH_MEMORY);
  1563. goto FnExit;
  1564. }
  1565. if (dwDataBytesWritten > dwDataBytesLeft)
  1566. dwDataBytesWritten = dwDataBytesLeft;
  1567. dwDataBytesLeft -= dwDataBytesWritten;
  1568. //continue writing from the next page
  1569. (pLog->Overlapped).Offset = pPage->Size + pPage->Offset;
  1570. (pLog->Overlapped).OffsetHigh = 0;
  1571. CopyMemory(pLargeBuffer, pLogData, dwDataBytesWritten);
  1572. ClRtlLogPrint(LOG_NOISE,
  1573. "[LM] LogpWriteLargeRecord : Writing(restoflargerecord) %1!u! bytes to disk at offset 0x%2!08lx!\r\n",
  1574. dwDataBytesWritten, (pLog->Overlapped).Offset);
  1575. if ((dwError = LogpWrite(pLog, pLargeBuffer,
  1576. (dwNumPagesLeft - 1) * pPage->Size, &dwBytesWritten))
  1577. != ERROR_SUCCESS)
  1578. {
  1579. ClRtlLogPrint(LOG_UNUSUAL,
  1580. "[LM] LogpWriteLargeRecordData::LogpWrite returned error=0x%1!08lx!\r\n",
  1581. dwError);
  1582. CL_LOGFAILURE(dwError);
  1583. goto FnExit;
  1584. }
  1585. //update the data pointer
  1586. pLogData += dwDataBytesWritten;
  1587. //now only the last page is left
  1588. dwNumPagesLeft = 1;
  1589. }
  1590. //set the offset to the last page
  1591. pPage->Offset += pPage->Size * (pLogRecord->NumPages - 1);
  1592. Current = LSNTORECORD(pPage, pLog->NextLsn);
  1593. Current->PreviousLsn = pLogRecord->CurrentLsn;
  1594. Current->CurrentLsn = pLog->NextLsn;
  1595. //write the last page, first write the eop data and then copy the
  1596. //remaining user data into the page and then write to disk
  1597. ClRtlLogPrint(LOG_NOISE,
  1598. "[LM] LogpWriteLargeRecord : Writing eoprecord of %1!u! bytes to disk at offset 0x%2!08lx!\r\n",
  1599. pPage->Size, pPage->Offset);
  1600. pLog->Overlapped.Offset = pPage->Offset;
  1601. pLog->Overlapped.OffsetHigh = 0;
  1602. //current points to the next record in the last page
  1603. //this will be the eop record
  1604. // Create an end-of-page record
  1605. //
  1606. Current->Signature = LOGREC_SIG;
  1607. Current->RecordSize = pPage->Size - RECORDOFFSETINPAGE(pPage, Current) + (sizeof(LOGPAGE)-sizeof(LOGRECORD));
  1608. Current->ResourceManager = RMPageEnd;
  1609. Current->Transaction = 0;
  1610. Current->Flags = 0;
  1611. Current->NumPages = 1;
  1612. GetSystemTimeAsFileTime(&Current->Timestamp);
  1613. dwDataBytesWritten = dwDataBytesLeft;
  1614. if (dwDataBytesWritten)
  1615. dwDataBytesLeft -= dwDataBytesWritten;
  1616. CL_ASSERT(dwDataBytesLeft == 0);
  1617. //use dwDataBytesLeft to remember the page size
  1618. //since we are now going to copy user data over it
  1619. dwDataBytesLeft = pPage->Size;
  1620. dwOldOffset = pPage->Offset;
  1621. if (dwDataBytesWritten)
  1622. CopyMemory(pPage, pLogData, dwDataBytesWritten);
  1623. ClRtlLogPrint(LOG_NOISE,
  1624. "[LM] LogpWriteLargeRecord : Writing(lastpageoflargerecord) %1!u! bytes to disk at offset 0x%2!08lx!\r\n",
  1625. dwDataBytesLeft, (pLog->Overlapped).Offset);
  1626. //write the last page
  1627. dwError = LogpWrite(pLog, pPage, dwDataBytesLeft, &dwBytesWritten);
  1628. if (dwError != ERROR_SUCCESS)
  1629. {
  1630. CL_LOGFAILURE(dwError);
  1631. goto FnExit;
  1632. }
  1633. //restore page size and offset
  1634. pPage->Size = dwDataBytesLeft;
  1635. pPage->Offset = dwOldOffset;
  1636. //set the next lsn to the first record on the next page
  1637. LastLsn = Current->CurrentLsn;
  1638. pLog->NextLsn = LastLsn + Current->RecordSize;
  1639. pLog->FlushedLsn = pLog->NextLsn;
  1640. // Create new page and keep the new record ready
  1641. // note disk space for this record has already been commited
  1642. pPage->Offset += pPage->Size; // voila, new page!
  1643. Current = &pPage->FirstRecord; // log record immediately following page header
  1644. Current->PreviousLsn = LastLsn;
  1645. Current->CurrentLsn = pLog->NextLsn;
  1646. ClRtlLogPrint(LOG_NOISE,
  1647. "[LM] LogpWriteLargeRecord : success pLog->NextLsn=0x%1!08lx!\r\n",
  1648. pLog->NextLsn);
  1649. FnExit:
  1650. if (pLargeBuffer) AlignFree(pLargeBuffer);
  1651. return (dwError);
  1652. }
  1653. DWORD
  1654. LogpGrowLog(
  1655. IN PLOG Log,
  1656. IN DWORD GrowthSize
  1657. )
  1658. /*++
  1659. Routine Description:
  1660. Ensures that there is sufficient disk space to handle subsequent
  1661. writes by preallocating the log file. Two variables, FileSize and
  1662. FileAlloc are tracked in the LOG structure. This routine increases
  1663. FileAlloc by the specified GrowthSize. Once FileAlloc exceeds
  1664. FileSize, the file is grown to accomodate the new data.
  1665. If this routine returns successfully, it guarantees that subsequent
  1666. will not fail due to lack of disk space.
  1667. Arguments:
  1668. Log - Supplies the log to be grown.
  1669. GrowthSize - Supplies the number of bytes required.
  1670. Return Value:
  1671. ERROR_SUCCESS if successful
  1672. Win32 error code if unsuccessful.
  1673. --*/
  1674. {
  1675. DWORD NewSize;
  1676. DWORD Status;
  1677. if(Log->FileAlloc > Log->FileSize)
  1678. {
  1679. return(ERROR_CLUSTERLOG_CORRUPT);
  1680. }
  1681. if (Log->FileAlloc + GrowthSize <= Log->FileSize)
  1682. {
  1683. Log->FileAlloc += GrowthSize;
  1684. return(ERROR_SUCCESS);
  1685. }
  1686. NewSize = Log->FileSize + GROWTH_CHUNK;
  1687. CL_ASSERT(NewSize > Log->FileSize); // bummer, log file is >4GB
  1688. //check if the file can be grown, if not, may be a reset
  1689. //is required
  1690. if (NewSize > Log->MaxFileSize)
  1691. {
  1692. LogpWriteWarningToEvtLog(LM_LOG_EXCEEDS_MAXSIZE, Log->FileName);
  1693. return(ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE);
  1694. }
  1695. //
  1696. // Grow the file.
  1697. //
  1698. Status = SetFilePointer(Log->FileHandle,
  1699. NewSize,
  1700. NULL,
  1701. FILE_BEGIN);
  1702. if (Status == 0xFFFFFFFF) {
  1703. Status = GetLastError();
  1704. CL_LOGFAILURE(Status);
  1705. return(Status);
  1706. }
  1707. if (!SetEndOfFile(Log->FileHandle)) {
  1708. Status = GetLastError();
  1709. CL_LOGFAILURE(Status);
  1710. return(Status);
  1711. }
  1712. Log->FileAlloc += GrowthSize;
  1713. Log->FileSize += GROWTH_CHUNK;
  1714. return(ERROR_SUCCESS);
  1715. }
  1716. DWORD
  1717. LogpReset(
  1718. IN PLOG Log,
  1719. IN LPCWSTR lpszInChkPtFile
  1720. )
  1721. /*++
  1722. Routine Description:
  1723. Resets the log file and takes a new checkpoint if a NULL checkpoint
  1724. file is specified as the second parameter.
  1725. Arguments:
  1726. Log - Supplies the log to be reset.
  1727. lpszInChkPtFile - Supplies the checkpoint file.
  1728. Return Value:
  1729. ERROR_SUCCESS if successful
  1730. Win32 error code if unsuccessful.
  1731. --*/
  1732. {
  1733. PLOG pLog;
  1734. PLOG pNewLog;
  1735. DWORD dwError=ERROR_SUCCESS;
  1736. WCHAR szPathName[MAX_PATH];
  1737. WCHAR szFilePrefix[MAX_PATH]=L"tquolog";
  1738. WCHAR szTmpFileName[MAX_PATH];
  1739. WCHAR szOldChkPtFileName[MAX_PATH];
  1740. LSN Lsn;
  1741. TRID Transaction;
  1742. ClRtlLogPrint(LOG_NOISE,
  1743. "[LM] LogpReset entry...\r\n");
  1744. pLog = Log;
  1745. //
  1746. // SS: the path name must be specified by the api as well,
  1747. // here we assume it is hardcoded for the use for the quorum
  1748. // log
  1749. //
  1750. dwError = DmGetQuorumLogPath(szPathName, sizeof(szPathName));
  1751. if (dwError != ERROR_SUCCESS)
  1752. {
  1753. dwError = GetLastError();
  1754. ClRtlLogPrint(LOG_UNUSUAL,
  1755. "[LM] LogpReset : DmGetQuorumLogPath failed, error=%1!u!\r\n",
  1756. dwError);
  1757. goto FnExit;
  1758. }
  1759. //
  1760. // Generate a tmp file name
  1761. //
  1762. if (!GetTempFileNameW(szPathName, szFilePrefix, 0, szTmpFileName))
  1763. {
  1764. dwError = GetLastError();
  1765. ClRtlLogPrint(LOG_UNUSUAL,
  1766. "[LM] LogpReset failed to generate a tmp file name,PathName=%1!ls!, FilePrefix=%2!ls!, error=%3!u!\r\n",
  1767. szPathName, szFilePrefix, dwError);
  1768. goto FnExit;
  1769. }
  1770. //
  1771. // Initialize the new log file, no timer is created
  1772. //
  1773. if (!(pNewLog = LogpCreate(szTmpFileName, pLog->MaxFileSize,
  1774. pLog->pfnGetChkPtCb, pLog->pGetChkPtContext, TRUE, &Lsn)))
  1775. {
  1776. dwError = GetLastError();
  1777. ClRtlLogPrint(LOG_UNUSUAL,
  1778. "[LM] LogpReset failed to create the new log file, error=0x%1!08lx\n",
  1779. dwError);
  1780. //
  1781. // Chittur Subbaraman (chitturs) - 2/18/99
  1782. //
  1783. // Make sure you get rid of the temp file. Otherwise, repeated
  1784. // log resets can clog the disk.
  1785. //
  1786. if ( !DeleteFileW( szTmpFileName ) )
  1787. {
  1788. ClRtlLogPrint(LOG_UNUSUAL,
  1789. "[LM] LogpReset:: Unable to delete tmp file %1!ws! after failed log create, Error=%2!d!\r\n",
  1790. szTmpFileName,
  1791. GetLastError());
  1792. }
  1793. goto FnExit;
  1794. }
  1795. //
  1796. // Reset the log file
  1797. //
  1798. EnterCriticalSection(&pLog->Lock);
  1799. //
  1800. // Get the name of the previous checkpoint file in the old log file
  1801. //
  1802. szOldChkPtFileName[0] = TEXT('\0');
  1803. if (LogGetLastChkPoint((HLOG)pLog, szOldChkPtFileName, &Transaction, &Lsn)
  1804. != ERROR_SUCCESS)
  1805. {
  1806. //
  1807. // Continue, this only means there is no old file to delete
  1808. //
  1809. ClRtlLogPrint(LOG_UNUSUAL,
  1810. "[LM] LogReset:: no check point found in the old log file\r\n");
  1811. }
  1812. //
  1813. // write a check point to it, if there is a checkpoint function
  1814. //
  1815. if ((dwError = LogCheckPoint((HLOG)pNewLog, FALSE, lpszInChkPtFile, 0))
  1816. != ERROR_SUCCESS)
  1817. {
  1818. ClRtlLogPrint(LOG_UNUSUAL,
  1819. "[LM] LogpReset:: Callback failed to return a checkpoint, error=%1!u!\r\n",
  1820. dwError);
  1821. CL_LOGFAILURE(dwError);
  1822. LogClose((HLOG)pNewLog);
  1823. LeaveCriticalSection(&pLog->Lock);
  1824. //
  1825. // Chittur Subbaraman (chitturs) - 2/18/99
  1826. //
  1827. // Make sure you get rid of the temp file. Otherwise, repeated
  1828. // log resets can clog the disk.
  1829. //
  1830. if ( !DeleteFileW( szTmpFileName ) )
  1831. {
  1832. ClRtlLogPrint(LOG_UNUSUAL,
  1833. "[LM] LogpReset:: Unable to delete tmp file %1!ws! after failed checkpoint attempt, Error=%2!d!\r\n",
  1834. szTmpFileName,
  1835. GetLastError());
  1836. }
  1837. goto FnExit;
  1838. }
  1839. //
  1840. // Get the name of the most recent checkpoint file in the new log file
  1841. //
  1842. szFilePrefix[0] = TEXT('\0');
  1843. if (LogGetLastChkPoint((HLOG)pNewLog, szFilePrefix, &Transaction, &Lsn)
  1844. != ERROR_SUCCESS)
  1845. {
  1846. //
  1847. // Continue, this only means there is no old file to delete
  1848. //
  1849. ClRtlLogPrint(LOG_UNUSUAL,
  1850. "[LM] LogpReset:: no check point found in the old log file\r\n");
  1851. }
  1852. //
  1853. // Close the old file handle so that we can move this temp file over
  1854. //
  1855. CloseHandle(pLog->FileHandle);
  1856. CloseHandle(pNewLog->FileHandle);
  1857. pNewLog->FileHandle = NULL;
  1858. pLog->FileHandle = NULL;
  1859. //
  1860. // Rename the new file to the log file
  1861. //
  1862. if (!MoveFileExW(szTmpFileName, pLog->FileName, MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH))
  1863. {
  1864. dwError = GetLastError();
  1865. ClRtlLogPrint(LOG_UNUSUAL,
  1866. "[LM] LogpReset:: MoveFileExW failed. Error = 0x%1!08lx!\r\n",
  1867. dwError);
  1868. //
  1869. // Move failed, close the new log file
  1870. //
  1871. LogClose((HLOG)pNewLog);
  1872. LeaveCriticalSection(&pLog->Lock);
  1873. //
  1874. // Chittur Subbaraman (chitturs) - 2/18/99
  1875. //
  1876. // Attempt to delete the temp file. You may not necessarily
  1877. // succeed here.
  1878. //
  1879. DeleteFileW( szTmpFileName );
  1880. goto FnExit;
  1881. }
  1882. //
  1883. // Open the new file again
  1884. //
  1885. pNewLog->FileHandle = CreateFileW(pLog->FileName,
  1886. GENERIC_READ | GENERIC_WRITE,
  1887. FILE_SHARE_READ,
  1888. // 0,
  1889. NULL,
  1890. OPEN_ALWAYS,
  1891. FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
  1892. // FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
  1893. // 0,
  1894. NULL);
  1895. if (pNewLog->FileHandle == INVALID_HANDLE_VALUE) {
  1896. dwError = GetLastError();
  1897. CL_LOGFAILURE(dwError);
  1898. LeaveCriticalSection(&pLog->Lock);
  1899. goto FnExit;
  1900. }
  1901. //
  1902. // Delete the last checkpoint in the old log file
  1903. //
  1904. if (szOldChkPtFileName[0] != TEXT('\0') && lstrcmpiW(szOldChkPtFileName, szFilePrefix))
  1905. {
  1906. ClRtlLogPrint(LOG_UNUSUAL,
  1907. "[LM] LogpReset:: deleting previous checkpoint file %1!ls!\r\n",
  1908. szOldChkPtFileName);
  1909. DeleteFileW(szOldChkPtFileName);
  1910. }
  1911. //
  1912. // Free the old resources
  1913. //
  1914. CloseHandle(pLog->Overlapped.hEvent);
  1915. AlignFree(pLog->ActivePage);
  1916. //
  1917. // Update the old log structure with the new info
  1918. // retain the name, callback info and the critical section
  1919. // continue to manage this file with the old timer as well
  1920. //
  1921. pLog->FileHandle = pNewLog->FileHandle;
  1922. pLog->SectorSize = pNewLog->SectorSize;
  1923. pLog->ActivePage = pNewLog->ActivePage;
  1924. pLog->NextLsn = pNewLog->NextLsn;
  1925. pLog->FlushedLsn = pNewLog->FlushedLsn;
  1926. pLog->FileSize = pNewLog->FileSize;
  1927. pLog->FileAlloc = pNewLog->FileAlloc;
  1928. pLog->MaxFileSize = pNewLog->MaxFileSize;
  1929. pLog->Overlapped = pNewLog->Overlapped;
  1930. //
  1931. // Delete the new pLog structure and associated memory for name
  1932. //
  1933. DeleteCriticalSection(&pNewLog->Lock);
  1934. CrFree(pNewLog->FileName);
  1935. CrFree(pNewLog);
  1936. LeaveCriticalSection(&pLog->Lock);
  1937. FnExit:
  1938. ClRtlLogPrint(LOG_NOISE,
  1939. "[LM] LogpReset exit, returning 0x%1!08lx!\r\n",
  1940. dwError);
  1941. return(dwError);
  1942. }
  1943. /****
  1944. @func DWORD | LogpWriteWarningToEvtLog | Conditionally write a warning
  1945. to the event log
  1946. @parm DWORD | dwWarningType | Type of warning.
  1947. @parm LPCWSTR | lpszLogFileName | The log file name.
  1948. @comm This function is added in order to prevent the event log from
  1949. being filled with the same type of warning message.
  1950. @xref
  1951. ****/
  1952. VOID
  1953. LogpWriteWarningToEvtLog(
  1954. IN DWORD dwWarningType,
  1955. IN LPCWSTR lpszLogFileName
  1956. )
  1957. {
  1958. //
  1959. // Chittur Subbaraman (chitturs) - 1/4/99
  1960. //
  1961. // (Use switch-case for easy future expansion purposes)
  1962. //
  1963. switch( dwWarningType )
  1964. {
  1965. case LM_LOG_EXCEEDS_MAXSIZE:
  1966. if( bLogExceedsMaxSzWarning == FALSE )
  1967. {
  1968. CL_LOGCLUSWARNING1( dwWarningType, lpszLogFileName );
  1969. bLogExceedsMaxSzWarning = TRUE;
  1970. }
  1971. break;
  1972. default:
  1973. break;
  1974. }
  1975. }