KernelUpdateLogPage.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /* Copyright (C) 2014-2016 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Text;
  10. using Utilities;
  11. namespace DiskAccessLibrary.LogicalDiskManager
  12. {
  13. public class KernelUpdateLogEntry
  14. {
  15. public KernelUpdateLogEntryStatus Status;
  16. public ulong CommittedTransactionID;
  17. public ulong PendingTransactionID;
  18. public uint RecoverySequenceNumber; // DMDiag calls this: recover_seqno
  19. }
  20. /// <summary>
  21. /// Each KLOG page is 512 bytes.
  22. /// Note: a sector can contain more than one KLOG page.
  23. /// </summary>
  24. public class KernelUpdateLogPage
  25. {
  26. public const int Length = 512;
  27. public string Signature = "KLOG"; // KLOG
  28. public ulong UnknownTransactionID; // Updates occasionally, gets the value of the latest PendingTransactionID, all the KLOG blocks have the same value here
  29. // SequenceNumber: Learned from observation, not incremented on every write, but always incremented by 1.
  30. // the sequence is log-wide (not per page), however, when updating multiple pages at once, they can share the same sequence number.
  31. public uint SequenceNumber;
  32. public uint NumberOfPages;
  33. public uint PageIndex; // KLOG page index
  34. private List<KernelUpdateLogEntry> m_logEntries = new List<KernelUpdateLogEntry>(); // PendingTransactionID, CommitTransactionID
  35. public KernelUpdateLogPage(byte[] buffer)
  36. {
  37. Signature = ByteReader.ReadAnsiString(buffer, 0x00, 4);
  38. UnknownTransactionID = BigEndianConverter.ToUInt64(buffer, 0x04);
  39. SequenceNumber = BigEndianConverter.ToUInt32(buffer, 0x0C);
  40. NumberOfPages = BigEndianConverter.ToUInt32(buffer, 0x10);
  41. PageIndex = BigEndianConverter.ToUInt32(buffer, 0x14);
  42. // Note: for the first KLog, the most recent entry is at the top
  43. m_logEntries.Clear();
  44. int offset = 0x18;
  45. while (offset < buffer.Length - 24) // room of one more entry
  46. {
  47. KernelUpdateLogEntryStatus status = (KernelUpdateLogEntryStatus)buffer[offset];
  48. offset += 1;
  49. if (status != KernelUpdateLogEntryStatus.NotExist)
  50. {
  51. KernelUpdateLogEntry entry = new KernelUpdateLogEntry();
  52. entry.Status = status;
  53. entry.CommittedTransactionID = BigEndianConverter.ToUInt64(buffer, offset);
  54. offset += 8;
  55. entry.PendingTransactionID = BigEndianConverter.ToUInt64(buffer, offset);
  56. offset += 8;
  57. entry.RecoverySequenceNumber = BigEndianConverter.ToUInt32(buffer, offset);
  58. offset += 4;
  59. m_logEntries.Add(entry);
  60. offset += 3; // padding to align to 4-byte boundary
  61. }
  62. else
  63. {
  64. break;
  65. }
  66. }
  67. }
  68. public byte[] GetBytes()
  69. {
  70. byte[] buffer = new byte[Length];
  71. ByteWriter.WriteAnsiString(buffer, 0, Signature, 4);
  72. BigEndianWriter.WriteUInt64(buffer, 0x04, UnknownTransactionID);
  73. BigEndianWriter.WriteUInt32(buffer, 0x0C, SequenceNumber);
  74. BigEndianWriter.WriteUInt32(buffer, 0x10, NumberOfPages);
  75. BigEndianWriter.WriteUInt32(buffer, 0x14, PageIndex);
  76. int offset = 0x18;
  77. foreach (KernelUpdateLogEntry entry in m_logEntries)
  78. {
  79. buffer[offset] = (byte)entry.Status;
  80. offset += 1;
  81. BigEndianWriter.WriteUInt64(buffer, offset, entry.CommittedTransactionID);
  82. offset += 8;
  83. BigEndianWriter.WriteUInt64(buffer, offset, entry.PendingTransactionID);
  84. offset += 8;
  85. BigEndianWriter.WriteUInt32(buffer, offset, entry.RecoverySequenceNumber);
  86. offset += 4;
  87. offset += 3; // padding to align to 4-byte boundary
  88. }
  89. return buffer;
  90. }
  91. public List<KernelUpdateLogEntry> LogEntries
  92. {
  93. get
  94. {
  95. return m_logEntries;
  96. }
  97. }
  98. public void SetLastEntry(ulong committedTransactionID, ulong pendingTransactionID)
  99. {
  100. if (m_logEntries.Count > 0)
  101. {
  102. m_logEntries.RemoveAt(m_logEntries.Count - 1);
  103. }
  104. KernelUpdateLogEntry entry = new KernelUpdateLogEntry();
  105. entry.Status = KernelUpdateLogEntryStatus.Commit;
  106. entry.CommittedTransactionID = committedTransactionID;
  107. entry.PendingTransactionID = pendingTransactionID;
  108. m_logEntries.Add(entry);
  109. }
  110. public static KernelUpdateLogPage ReadFromDisk(Disk disk, PrivateHeader privateHeader, TOCBlock tocBlock, int pageIndex)
  111. {
  112. ulong sectorIndex = privateHeader.PrivateRegionStartLBA + tocBlock.LogStart + (uint)(pageIndex * Length / disk.BytesPerSector);
  113. int pageOffset = (pageIndex * Length) % disk.BytesPerSector;
  114. byte[] sector = disk.ReadSector((long)sectorIndex);
  115. if (pageOffset > 0)
  116. {
  117. sector = ByteReader.ReadBytes(sector, pageOffset, Length);
  118. }
  119. KernelUpdateLogPage result = new KernelUpdateLogPage(sector);
  120. return result;
  121. }
  122. public static void WriteToDisk(Disk disk, PrivateHeader privateHeader, TOCBlock tocBlock, KernelUpdateLogPage page)
  123. {
  124. ulong sectorIndex = privateHeader.PrivateRegionStartLBA + tocBlock.LogStart + (uint)(page.PageIndex * Length / disk.BytesPerSector);
  125. int pageOffset = ((int)page.PageIndex * Length) % disk.BytesPerSector;
  126. byte[] pageBytes = page.GetBytes();
  127. if (disk.BytesPerSector > Length)
  128. {
  129. byte[] sectorBytes = disk.ReadSector((long)sectorIndex);
  130. ByteWriter.WriteBytes(sectorBytes, pageOffset, pageBytes);
  131. disk.WriteSectors((long)sectorIndex, sectorBytes);
  132. }
  133. else
  134. {
  135. disk.WriteSectors((long)sectorIndex, pageBytes);
  136. }
  137. }
  138. }
  139. }