mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-21 14:41:03 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,468 @@
|
||||
//
|
||||
// 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.Collections.ObjectModel;
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user