public LfsRecord WriteRecord(int clientIndex, LfsRecordType recordType, ulong clientPreviousLsn, ulong clientUndoNextLsn, uint transactionId, byte[] clientData, bool flushToDisk) { if (m_restartPage == null) { m_restartPage = ReadRestartPage(); } // It's perfectly valid to clear the CleanDismount flag after writing the transfer. // Note that CurrentLsn is used to determine which is restart page recent so we should not update the restart page without incrementing CurrentLsn first. ushort clientSeqNumber = m_restartPage.RestartArea.LogClientArray[clientIndex].SeqNumber; LfsRecord record = new LfsRecord(); record.ClientSeqNumber = clientSeqNumber; record.ClientIndex = (ushort)clientIndex; record.RecordType = recordType; record.ClientPreviousLsn = clientPreviousLsn; record.ClientUndoNextLsn = clientUndoNextLsn; record.TransactionId = transactionId; record.ClientDataLength = (uint)clientData.Length; record.Data = clientData; record.ThisLsn = GetNextLsn(); m_recordsPendingWrite.Add(record); bool endOfTransferRecorded = false; if (flushToDisk) { FlushRecords(m_recordsPendingWrite, out endOfTransferRecorded); m_recordsPendingWrite.Clear(); } // Update CurrentLsn / LastLsnDataLength m_restartPage.RestartArea.CurrentLsn = record.ThisLsn; m_restartPage.RestartArea.LastLsnDataLength = (uint)record.Data.Length; // If the client is writing a ClientRestart, the call to write a restart page (when updating the client record) is imminent, so no need to write a restart page now. // Note that CurrentLsn is used to determine which restart page is more recent so we should not update the restart page without incrementing CurrentLsn. if (recordType != LfsRecordType.ClientRestart && endOfTransferRecorded) { // When the NTFS v5.1 driver restarts a dirty log file, it does not expect to find more than one transfer after the last flushed LSN. // If more than one transfer is encountered, it is treated as a fatal error and the driver will report STATUS_DISK_CORRUPT_ERROR. // Updating the CurrentLsn / LastLsnDataLength in the restart area will make the NTFS driver see the next page we write as the only transfer after the flushed LSN. WriteRestartPage(false); } return(record); }
public LfsRecord(byte[] buffer, int offset) { ThisLsn = LittleEndianConverter.ToUInt64(buffer, offset + 0x00); ClientPreviousLsn = LittleEndianConverter.ToUInt64(buffer, offset + 0x08); ClientUndoNextLsn = LittleEndianConverter.ToUInt64(buffer, offset + 0x10); ClientDataLength = LittleEndianConverter.ToUInt32(buffer, offset + 0x18); ClientSeqNumber = LittleEndianConverter.ToUInt16(buffer, offset + 0x1C); ClientIndex = LittleEndianConverter.ToUInt16(buffer, offset + 0x1E); RecordType = (LfsRecordType)LittleEndianConverter.ToUInt32(buffer, offset + 0x20); TransactionId = LittleEndianConverter.ToUInt32(buffer, offset + 0x24); Flags = (LfsRecordFlags)LittleEndianConverter.ToUInt16(buffer, offset + 0x28); if (IsMultiPageRecord) { Data = ByteReader.ReadBytes(buffer, offset + HeaderLength, buffer.Length - (offset + HeaderLength)); } else { Data = ByteReader.ReadBytes(buffer, offset + HeaderLength, (int)ClientDataLength); } }
public LfsRecord WriteRecord(int clientIndex, LfsRecordType recordType, ulong clientPreviousLsn, ulong clientUndoNextLsn, uint transactionId, byte[] clientData) { if (m_restartPage == null) { m_restartPage = ReadRestartPage(); } if (m_restartPage.LogRestartArea.IsClean) { // We should clear the CleanDismount flag before making changes to the log file WriteRestartPage(false); } ushort clientSeqNumber = m_restartPage.LogRestartArea.LogClientArray[clientIndex].SeqNumber; LfsRecord record = new LfsRecord(); record.ClientSeqNumber = clientSeqNumber; record.ClientIndex = (ushort)clientIndex; record.RecordType = recordType; record.ClientPreviousLsn = clientPreviousLsn; record.ClientUndoNextLsn = clientUndoNextLsn; record.TransactionId = transactionId; record.ClientDataLength = (uint)clientData.Length; record.Data = clientData; record.ThisLsn = GetNextLsn(); WriteRecord(record); // Update CurrentLsn / LastLsnDataLength m_restartPage.LogRestartArea.CurrentLsn = record.ThisLsn; m_restartPage.LogRestartArea.LastLsnDataLength = (uint)record.Data.Length; if (record.IsMultiPageRecord) { // We can optimize by only writing the restart page if we already had one transfer after the last flushed LSN. WriteRestartPage(false); } return(record); }