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.

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