// // Copyright (c) 2008-2011, Kenneth Bell // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // namespace DiscUtils.Ntfs { using System; using System.IO; internal abstract class FixupRecordBase { private int _sectorSize; private string _magic; private ushort _updateSequenceOffset; private ushort _updateSequenceCount; private ushort _updateSequenceNumber; private ushort[] _updateSequenceArray; public FixupRecordBase(string magic, int sectorSize) { _magic = magic; _sectorSize = sectorSize; } public FixupRecordBase(string magic, int sectorSize, int recordLength) { Initialize(magic, sectorSize, recordLength); } public string Magic { get { return _magic; } } public ushort UpdateSequenceOffset { get { return _updateSequenceOffset; } } public ushort UpdateSequenceCount { get { return _updateSequenceCount; } } public ushort UpdateSequenceNumber { get { return _updateSequenceNumber; } } public int UpdateSequenceSize { get { return _updateSequenceCount * 2; } } public int Size { get { return CalcSize(); } } public void FromBytes(byte[] buffer, int offset) { FromBytes(buffer, offset, false); } public void FromBytes(byte[] buffer, int offset, bool ignoreMagic) { string diskMagic = Utilities.BytesToString(buffer, offset + 0x00, 4); if (_magic == null) { _magic = diskMagic; } else { if (diskMagic != _magic && ignoreMagic) { return; } if (diskMagic != _magic) { throw new IOException("Corrupt record"); } } _updateSequenceOffset = Utilities.ToUInt16LittleEndian(buffer, offset + 0x04); _updateSequenceCount = Utilities.ToUInt16LittleEndian(buffer, offset + 0x06); _updateSequenceNumber = Utilities.ToUInt16LittleEndian(buffer, offset + _updateSequenceOffset); _updateSequenceArray = new ushort[_updateSequenceCount - 1]; for (int i = 0; i < _updateSequenceArray.Length; ++i) { _updateSequenceArray[i] = Utilities.ToUInt16LittleEndian(buffer, offset + _updateSequenceOffset + (2 * (i + 1))); } UnprotectBuffer(buffer, offset); Read(buffer, offset); } public void ToBytes(byte[] buffer, int offset) { _updateSequenceOffset = Write(buffer, offset); ProtectBuffer(buffer, offset); Utilities.StringToBytes(_magic, buffer, offset + 0x00, 4); Utilities.WriteBytesLittleEndian(_updateSequenceOffset, buffer, offset + 0x04); Utilities.WriteBytesLittleEndian(_updateSequenceCount, buffer, offset + 0x06); Utilities.WriteBytesLittleEndian(_updateSequenceNumber, buffer, offset + _updateSequenceOffset); for (int i = 0; i < _updateSequenceArray.Length; ++i) { Utilities.WriteBytesLittleEndian(_updateSequenceArray[i], buffer, offset + _updateSequenceOffset + (2 * (i + 1))); } } protected void Initialize(string magic, int sectorSize, int recordLength) { _magic = magic; _sectorSize = sectorSize; _updateSequenceCount = (ushort)(1 + Utilities.Ceil(recordLength, Sizes.Sector)); _updateSequenceNumber = 1; _updateSequenceArray = new ushort[_updateSequenceCount - 1]; } protected abstract void Read(byte[] buffer, int offset); protected abstract ushort Write(byte[] buffer, int offset); protected abstract int CalcSize(); private void UnprotectBuffer(byte[] buffer, int offset) { // First do validation check - make sure the USN matches on all sectors) for (int i = 0; i < _updateSequenceArray.Length; ++i) { if (_updateSequenceNumber != Utilities.ToUInt16LittleEndian(buffer, offset + (Sizes.Sector * (i + 1)) - 2)) { throw new IOException("Corrupt file system record found"); } } // Now replace the USNs with the actual data from the sequence array for (int i = 0; i < _updateSequenceArray.Length; ++i) { Utilities.WriteBytesLittleEndian(_updateSequenceArray[i], buffer, offset + (Sizes.Sector * (i + 1)) - 2); } } private void ProtectBuffer(byte[] buffer, int offset) { _updateSequenceNumber++; // Read in the bytes that are replaced by the USN for (int i = 0; i < _updateSequenceArray.Length; ++i) { _updateSequenceArray[i] = Utilities.ToUInt16LittleEndian(buffer, offset + (Sizes.Sector * (i + 1)) - 2); } // Overwrite the bytes that are replaced with the USN for (int i = 0; i < _updateSequenceArray.Length; ++i) { Utilities.WriteBytesLittleEndian(_updateSequenceNumber, buffer, offset + (Sizes.Sector * (i + 1)) - 2); } } } }