public LfsRecord ReadRecord(ulong lsn) { if (m_restartPage == null) { m_restartPage = ReadRestartPage(); } ulong pageOffsetInFile = LsnToPageOffsetInFile(lsn); int recordOffsetInPage = LsnToRecordOffsetInPage(lsn); LfsRecordPage page = ReadPage(pageOffsetInFile); if (page == null) { throw new InvalidDataException("LfsRecord LSN points to an uninitialized page"); } int dataOffset = m_restartPage.RestartArea.LogPageDataOffset; LfsRecord record = page.ReadRecord(recordOffsetInPage); if (record.ThisLsn != lsn) { throw new InvalidDataException("LfsRecord LSN does not match expected value"); } if (record.Length < LfsRecord.HeaderLength) { throw new InvalidDataException("LfsRecord length is invalid"); } if (record.IsMultiPageRecord) { int recordLength = (int)(LfsRecord.HeaderLength + record.ClientDataLength); int bytesRemaining = recordLength - (LfsRecord.HeaderLength + record.Data.Length); while (bytesRemaining > 0) { pageOffsetInFile += m_restartPage.LogPageSize; if (pageOffsetInFile == m_restartPage.RestartArea.FileSize) { pageOffsetInFile = m_restartPage.SystemPageSize * 2 + m_restartPage.LogPageSize * 2; } page = ReadPage(pageOffsetInFile); int bytesToRead = Math.Min((int)m_restartPage.LogPageSize - dataOffset, bytesRemaining); record.Data = ByteUtils.Concatenate(record.Data, page.ReadBytes(dataOffset, bytesToRead)); bytesRemaining -= bytesToRead; } } return(record); }
/// <summary> /// This method will repair the log file by copying the tail copies back to their correct location. /// If necessary, the restart area will be updated to reflect CurrentLsn and LastLsnDataLength. /// </summary> /// <remarks>This method will only repair the log file, further actions are needed to bring the volume back to a consistent state.</remarks> private void RepairLogFile() { if (m_restartPage == null) { m_restartPage = ReadRestartPage(); } // Note: this implementation is not as exhaustive as it should be. LfsRecordPage firstTailPage = null; LfsRecordPage secondTailPage = null; try { firstTailPage = ReadPageFromFile(m_restartPage.SystemPageSize * 2); } catch (InvalidDataException) { } try { secondTailPage = ReadPageFromFile(m_restartPage.SystemPageSize * 2 + m_restartPage.LogPageSize); } catch (InvalidDataException) { } // Find the most recent tail copy LfsRecordPage tailPage = null; if (firstTailPage != null) { tailPage = firstTailPage; } if (tailPage == null || (secondTailPage != null && secondTailPage.LastEndLsn > firstTailPage.LastEndLsn)) { tailPage = secondTailPage; } if (tailPage != null) { LfsRecordPage page = null; try { page = ReadPageFromFile(tailPage.LastLsnOrFileOffset); } catch (InvalidDataException) { } if (page == null || tailPage.LastEndLsn > page.LastLsnOrFileOffset) { ulong pageOffsetInFile = tailPage.LastLsnOrFileOffset; tailPage.LastLsnOrFileOffset = tailPage.LastEndLsn; WritePage(pageOffsetInFile, tailPage); if (tailPage.LastEndLsn > m_restartPage.LogRestartArea.CurrentLsn) { m_restartPage.LogRestartArea.CurrentLsn = tailPage.LastEndLsn; int recordOffsetInPage = LsnToRecordOffsetInPage(tailPage.LastEndLsn); LfsRecord record = tailPage.ReadRecord(recordOffsetInPage); m_restartPage.LogRestartArea.LastLsnDataLength = (uint)record.Data.Length; WriteRestartPage(m_restartPage); } } } }