Files
WPinternals/DiscUtils/Ntfs/FileRecord.cs
T
Gustave Monce a2a1c2302b Code cleanup
2019-12-22 12:25:48 +01:00

468 lines
17 KiB
C#

//
// 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.Collections.Generic;
using System.IO;
[Flags]
internal enum FileRecordFlags : ushort
{
None = 0x0000,
InUse = 0x0001,
IsDirectory = 0x0002,
IsMetaFile = 0x0004,
HasViewIndex = 0x0008
}
internal class FileRecord : FixupRecordBase
{
private ulong _logFileSequenceNumber;
private ushort _sequenceNumber;
private ushort _hardLinkCount;
private ushort _firstAttributeOffset;
private FileRecordFlags _flags;
private uint _recordRealSize;
private uint _recordAllocatedSize;
private FileRecordReference _baseFile;
private ushort _nextAttributeId;
private uint _index; // Self-reference (on XP+)
private List<AttributeRecord> _attributes;
private bool _haveIndex;
private uint _loadedIndex;
public FileRecord(int sectorSize)
: base("FILE", sectorSize)
{
}
public FileRecord(int sectorSize, int recordLength, uint index)
: base("FILE", sectorSize, recordLength)
{
ReInitialize(sectorSize, recordLength, index);
}
public uint MasterFileTableIndex
{
get { return _haveIndex ? _index : _loadedIndex; }
}
public uint LoadedIndex
{
get { return _loadedIndex; }
set { _loadedIndex = value; }
}
public ulong LogFileSequenceNumber
{
get { return _logFileSequenceNumber; }
}
public ushort SequenceNumber
{
get { return _sequenceNumber; }
set { _sequenceNumber = value; }
}
public ushort HardLinkCount
{
get { return _hardLinkCount; }
set { _hardLinkCount = value; }
}
public uint AllocatedSize
{
get { return _recordAllocatedSize; }
}
public uint RealSize
{
get { return _recordRealSize; }
}
public FileRecordReference BaseFile
{
get { return _baseFile; }
set { _baseFile = value; }
}
public FileRecordFlags Flags
{
get { return _flags; }
set { _flags = value; }
}
public List<AttributeRecord> Attributes
{
get { return _attributes; }
}
public AttributeRecord FirstAttribute
{
get { return _attributes.Count > 0 ? _attributes[0] : null; }
}
public FileRecordReference Reference
{
get { return new FileRecordReference(MasterFileTableIndex, SequenceNumber); }
}
public ushort NextAttributeId
{
get { return _nextAttributeId; }
}
public bool IsMftRecord
{
get { return MasterFileTableIndex == MasterFileTable.MftIndex || (_baseFile.MftIndex == MasterFileTable.MftIndex && _baseFile.SequenceNumber != 0); }
}
public static FileAttributeFlags ConvertFlags(FileRecordFlags source)
{
FileAttributeFlags result = FileAttributeFlags.None;
if ((source & FileRecordFlags.IsDirectory) != 0)
{
result |= FileAttributeFlags.Directory;
}
if ((source & FileRecordFlags.HasViewIndex) != 0)
{
result |= FileAttributeFlags.IndexView;
}
if ((source & FileRecordFlags.IsMetaFile) != 0)
{
result |= FileAttributeFlags.Hidden | FileAttributeFlags.System;
}
return result;
}
public void ReInitialize(int sectorSize, int recordLength, uint index)
{
Initialize("FILE", sectorSize, recordLength);
_sequenceNumber++;
_flags = FileRecordFlags.None;
_recordAllocatedSize = (uint)recordLength;
_nextAttributeId = 0;
_index = index;
_hardLinkCount = 0;
_baseFile = new FileRecordReference(0);
_attributes = new List<AttributeRecord>();
_haveIndex = true;
}
/// <summary>
/// Gets an attribute by it's id.
/// </summary>
/// <param name="id">The attribute's id.</param>
/// <returns>The attribute, or <c>null</c>.</returns>
public AttributeRecord GetAttribute(ushort id)
{
foreach (AttributeRecord attrRec in _attributes)
{
if (attrRec.AttributeId == id)
{
return attrRec;
}
}
return null;
}
/// <summary>
/// Gets an unnamed attribute.
/// </summary>
/// <param name="type">The attribute type.</param>
/// <returns>The attribute, or <c>null</c>.</returns>
public AttributeRecord GetAttribute(AttributeType type)
{
return GetAttribute(type, null);
}
/// <summary>
/// Gets an named attribute.
/// </summary>
/// <param name="type">The attribute type.</param>
/// <param name="name">The name of the attribute.</param>
/// <returns>The attribute, or <c>null</c>.</returns>
public AttributeRecord GetAttribute(AttributeType type, string name)
{
foreach (AttributeRecord attrRec in _attributes)
{
if (attrRec.AttributeType == type && attrRec.Name == name)
{
return attrRec;
}
}
return null;
}
public override string ToString()
{
foreach (AttributeRecord attr in _attributes)
{
if (attr.AttributeType == AttributeType.FileName)
{
StructuredNtfsAttribute<FileNameRecord> fnAttr = (StructuredNtfsAttribute<FileNameRecord>)NtfsAttribute.FromRecord(null, new FileRecordReference(0), attr);
return fnAttr.Content.FileName;
}
}
return "No Name";
}
/// <summary>
/// Creates a new attribute.
/// </summary>
/// <param name="type">The type of the new attribute.</param>
/// <param name="name">The name of the new attribute.</param>
/// <param name="indexed">Whether the attribute is marked as indexed.</param>
/// <param name="flags">Flags for the new attribute.</param>
/// <returns>The id of the new attribute.</returns>
public ushort CreateAttribute(AttributeType type, string name, bool indexed, AttributeFlags flags)
{
ushort id = _nextAttributeId++;
_attributes.Add(
new ResidentAttributeRecord(
type,
name,
id,
indexed,
flags));
_attributes.Sort();
return id;
}
/// <summary>
/// Creates a new non-resident attribute.
/// </summary>
/// <param name="type">The type of the new attribute.</param>
/// <param name="name">The name of the new attribute.</param>
/// <param name="flags">Flags for the new attribute.</param>
/// <returns>The id of the new attribute.</returns>
public ushort CreateNonResidentAttribute(AttributeType type, string name, AttributeFlags flags)
{
ushort id = _nextAttributeId++;
_attributes.Add(
new NonResidentAttributeRecord(
type,
name,
id,
flags,
0,
new List<DataRun>()));
_attributes.Sort();
return id;
}
/// <summary>
/// Creates a new attribute.
/// </summary>
/// <param name="type">The type of the new attribute.</param>
/// <param name="name">The name of the new attribute.</param>
/// <param name="flags">Flags for the new attribute.</param>
/// <param name="firstCluster">The first cluster to assign to the attribute.</param>
/// <param name="numClusters">The number of sequential clusters to assign to the attribute.</param>
/// <param name="bytesPerCluster">The number of bytes in each cluster.</param>
/// <returns>The id of the new attribute.</returns>
public ushort CreateNonResidentAttribute(AttributeType type, string name, AttributeFlags flags, long firstCluster, ulong numClusters, uint bytesPerCluster)
{
ushort id = _nextAttributeId++;
_attributes.Add(
new NonResidentAttributeRecord(
type,
name,
id,
flags,
firstCluster,
numClusters,
bytesPerCluster));
_attributes.Sort();
return id;
}
/// <summary>
/// Adds an existing attribute.
/// </summary>
/// <param name="attrRec">The attribute to add.</param>
/// <returns>The new Id of the attribute.</returns>
/// <remarks>This method is used to move an attribute between different MFT records.</remarks>
public ushort AddAttribute(AttributeRecord attrRec)
{
attrRec.AttributeId = _nextAttributeId++;
_attributes.Add(attrRec);
_attributes.Sort();
return attrRec.AttributeId;
}
/// <summary>
/// Removes an attribute by it's id.
/// </summary>
/// <param name="id">The attribute's id.</param>
public void RemoveAttribute(ushort id)
{
for (int i = 0; i < _attributes.Count; ++i)
{
if (_attributes[i].AttributeId == id)
{
_attributes.RemoveAt(i);
break;
}
}
}
public void Reset()
{
_attributes.Clear();
_flags = FileRecordFlags.None;
_hardLinkCount = 0;
_nextAttributeId = 0;
_recordRealSize = 0;
}
internal long GetAttributeOffset(ushort id)
{
int firstAttrPos = (ushort)Utilities.RoundUp((_haveIndex ? 0x30 : 0x2A) + UpdateSequenceSize, 8);
int offset = firstAttrPos;
foreach (var attr in _attributes)
{
if (attr.AttributeId == id)
{
return offset;
}
offset += attr.Size;
}
return -1;
}
internal void Dump(TextWriter writer, string indent)
{
writer.WriteLine(indent + "FILE RECORD (" + ToString() + ")");
writer.WriteLine(indent + " Magic: " + Magic);
writer.WriteLine(indent + " Update Seq Offset: " + UpdateSequenceOffset);
writer.WriteLine(indent + " Update Seq Count: " + UpdateSequenceCount);
writer.WriteLine(indent + " Update Seq Number: " + UpdateSequenceNumber);
writer.WriteLine(indent + " Log File Seq Num: " + _logFileSequenceNumber);
writer.WriteLine(indent + " Sequence Number: " + _sequenceNumber);
writer.WriteLine(indent + " Hard Link Count: " + _hardLinkCount);
writer.WriteLine(indent + " Flags: " + _flags);
writer.WriteLine(indent + " Record Real Size: " + _recordRealSize);
writer.WriteLine(indent + " Record Alloc Size: " + _recordAllocatedSize);
writer.WriteLine(indent + " Base File: " + _baseFile);
writer.WriteLine(indent + " Next Attribute Id: " + _nextAttributeId);
writer.WriteLine(indent + " Attribute Count: " + _attributes.Count);
writer.WriteLine(indent + " Index (Self Ref): " + _index);
}
protected override void Read(byte[] buffer, int offset)
{
_logFileSequenceNumber = Utilities.ToUInt64LittleEndian(buffer, offset + 0x08);
_sequenceNumber = Utilities.ToUInt16LittleEndian(buffer, offset + 0x10);
_hardLinkCount = Utilities.ToUInt16LittleEndian(buffer, offset + 0x12);
_firstAttributeOffset = Utilities.ToUInt16LittleEndian(buffer, offset + 0x14);
_flags = (FileRecordFlags)Utilities.ToUInt16LittleEndian(buffer, offset + 0x16);
_recordRealSize = Utilities.ToUInt32LittleEndian(buffer, offset + 0x18);
_recordAllocatedSize = Utilities.ToUInt32LittleEndian(buffer, offset + 0x1C);
_baseFile = new FileRecordReference(Utilities.ToUInt64LittleEndian(buffer, offset + 0x20));
_nextAttributeId = Utilities.ToUInt16LittleEndian(buffer, offset + 0x28);
if (UpdateSequenceOffset >= 0x30)
{
_index = Utilities.ToUInt32LittleEndian(buffer, offset + 0x2C);
_haveIndex = true;
}
_attributes = new List<AttributeRecord>();
int focus = _firstAttributeOffset;
while (true)
{
int length;
AttributeRecord attr = AttributeRecord.FromBytes(buffer, focus, out length);
if (attr == null)
{
break;
}
_attributes.Add(attr);
focus += (int)length;
}
}
protected override ushort Write(byte[] buffer, int offset)
{
ushort headerEnd = (ushort)(_haveIndex ? 0x30 : 0x2A);
_firstAttributeOffset = (ushort)Utilities.RoundUp(headerEnd + UpdateSequenceSize, 0x08);
_recordRealSize = (uint)CalcSize();
Utilities.WriteBytesLittleEndian(_logFileSequenceNumber, buffer, offset + 0x08);
Utilities.WriteBytesLittleEndian(_sequenceNumber, buffer, offset + 0x10);
Utilities.WriteBytesLittleEndian(_hardLinkCount, buffer, offset + 0x12);
Utilities.WriteBytesLittleEndian(_firstAttributeOffset, buffer, offset + 0x14);
Utilities.WriteBytesLittleEndian((ushort)_flags, buffer, offset + 0x16);
Utilities.WriteBytesLittleEndian(_recordRealSize, buffer, offset + 0x18);
Utilities.WriteBytesLittleEndian(_recordAllocatedSize, buffer, offset + 0x1C);
Utilities.WriteBytesLittleEndian(_baseFile.Value, buffer, offset + 0x20);
Utilities.WriteBytesLittleEndian(_nextAttributeId, buffer, offset + 0x28);
if (_haveIndex)
{
Utilities.WriteBytesLittleEndian((ushort)0, buffer, offset + 0x2A); // Alignment field
Utilities.WriteBytesLittleEndian(_index, buffer, offset + 0x2C);
}
int pos = _firstAttributeOffset;
foreach (var attr in _attributes)
{
pos += attr.Write(buffer, offset + pos);
}
Utilities.WriteBytesLittleEndian(uint.MaxValue, buffer, offset + pos);
return headerEnd;
}
protected override int CalcSize()
{
int firstAttrPos = (ushort)Utilities.RoundUp((_haveIndex ? 0x30 : 0x2A) + UpdateSequenceSize, 8);
int size = firstAttrPos;
foreach (var attr in _attributes)
{
size += attr.Size;
}
return Utilities.RoundUp(size + 4, 8); // 0xFFFFFFFF terminator on attributes
}
}
}