KernelUpdateLogPage.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /* Copyright (C) 2014 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. public class KernelUpdateLogPage // KLOG sector
  21. {
  22. public const int Length = 512;
  23. public string Signature = "KLOG"; // KLOG
  24. public ulong UnknownTransactionID; // Updates occasionally, gets the value of the latest PendingTransactionID, all the KLOG blocks have the same value here
  25. // SequenceNumber: Learned from observation, not incremented on every write, but always incremented by 1.
  26. // the sequence is log-wide (not per page), however, when updating multiple pages at once, they can share the same sequence number.
  27. public uint SequenceNumber;
  28. public uint NumberOfPages;
  29. public uint PageIndex; // KLOG page index
  30. private List<KernelUpdateLogEntry> m_logEntries = new List<KernelUpdateLogEntry>(); // PendingTransactionID, CommitTransactionID
  31. public KernelUpdateLogPage(byte[] buffer)
  32. {
  33. Signature = ByteReader.ReadAnsiString(buffer, 0x00, 4);
  34. UnknownTransactionID = BigEndianConverter.ToUInt64(buffer, 0x04);
  35. SequenceNumber = BigEndianConverter.ToUInt32(buffer, 0x0C);
  36. NumberOfPages = BigEndianConverter.ToUInt32(buffer, 0x10);
  37. PageIndex = BigEndianConverter.ToUInt32(buffer, 0x14);
  38. // Note: for the first KLog, the most recent entry is at the top
  39. m_logEntries.Clear();
  40. int offset = 0x18;
  41. while (offset < buffer.Length - 24) // room of one more entry
  42. {
  43. KernelUpdateLogEntryStatus status = (KernelUpdateLogEntryStatus)buffer[offset];
  44. offset += 1;
  45. if (status != KernelUpdateLogEntryStatus.NotExist)
  46. {
  47. KernelUpdateLogEntry entry = new KernelUpdateLogEntry();
  48. entry.Status = status;
  49. entry.CommittedTransactionID = BigEndianConverter.ToUInt64(buffer, offset);
  50. offset += 8;
  51. entry.PendingTransactionID = BigEndianConverter.ToUInt64(buffer, offset);
  52. offset += 8;
  53. entry.RecoverySequenceNumber = BigEndianConverter.ToUInt32(buffer, offset);
  54. offset += 4;
  55. m_logEntries.Add(entry);
  56. offset += 3; // padding to align to 4-byte boundary
  57. }
  58. else
  59. {
  60. break;
  61. }
  62. }
  63. }
  64. public byte[] GetBytes()
  65. {
  66. byte[] buffer = new byte[Length];
  67. ByteWriter.WriteAnsiString(buffer, 0, Signature, 4);
  68. BigEndianWriter.WriteUInt64(buffer, 0x04, UnknownTransactionID);
  69. BigEndianWriter.WriteUInt32(buffer, 0x0C, SequenceNumber);
  70. BigEndianWriter.WriteUInt32(buffer, 0x10, NumberOfPages);
  71. BigEndianWriter.WriteUInt32(buffer, 0x14, PageIndex);
  72. int offset = 0x18;
  73. foreach (KernelUpdateLogEntry entry in m_logEntries)
  74. {
  75. buffer[offset] = (byte)entry.Status;
  76. offset += 1;
  77. BigEndianWriter.WriteUInt64(buffer, offset, entry.CommittedTransactionID);
  78. offset += 8;
  79. BigEndianWriter.WriteUInt64(buffer, offset, entry.PendingTransactionID);
  80. offset += 8;
  81. BigEndianWriter.WriteUInt32(buffer, offset, entry.RecoverySequenceNumber);
  82. offset += 4;
  83. offset += 3; // padding to align to 4-byte boundary
  84. }
  85. return buffer;
  86. }
  87. public List<KernelUpdateLogEntry> LogEntries
  88. {
  89. get
  90. {
  91. return m_logEntries;
  92. }
  93. }
  94. public void SetLastEntry(ulong committedTransactionID, ulong pendingTransactionID)
  95. {
  96. if (m_logEntries.Count > 0)
  97. {
  98. m_logEntries.RemoveAt(m_logEntries.Count - 1);
  99. }
  100. KernelUpdateLogEntry entry = new KernelUpdateLogEntry();
  101. entry.Status = KernelUpdateLogEntryStatus.Commit;
  102. entry.CommittedTransactionID = committedTransactionID;
  103. entry.PendingTransactionID = pendingTransactionID;
  104. m_logEntries.Add(entry);
  105. }
  106. public static KernelUpdateLogPage ReadFromDisk(Disk disk, PrivateHeader privateHeader, TOCBlock tocBlock, int kLogIndex)
  107. {
  108. ulong sectorIndex = privateHeader.PrivateRegionStartLBA + tocBlock.LogStart + (uint)kLogIndex;
  109. byte[] sector = disk.ReadSector((long)sectorIndex);
  110. KernelUpdateLogPage result = new KernelUpdateLogPage(sector);
  111. return result;
  112. }
  113. public static void WriteToDisk(Disk disk, PrivateHeader privateHeader, TOCBlock tocBlock, KernelUpdateLogPage record)
  114. {
  115. ulong sectorIndex = privateHeader.PrivateRegionStartLBA + tocBlock.LogStart + record.PageIndex;
  116. byte[] sector = record.GetBytes();
  117. disk.WriteSectors((long)sectorIndex, sector);
  118. }
  119. }
  120. }