public IndexData(NTFSVolume volume, FileRecord fileRecord, AttributeType indexedAttributeType) { m_volume = volume; m_fileRecord = fileRecord; m_indexedAttributeType = indexedAttributeType; m_indexName = IndexHelper.GetIndexName(indexedAttributeType); m_rootRecord = (IndexRootRecord)m_fileRecord.GetAttributeRecord(AttributeType.IndexRoot, m_indexName); // I have observed the NTFS v5.1 driver keeping the IndexAllocation and associated Bitmap attributes after deleting files from the directory even though m_rootRecord.IsParentNode is set to false. m_indexAllocationRecord = (IndexAllocationRecord)m_fileRecord.GetAttributeRecord(AttributeType.IndexAllocation, m_indexName); m_bitmapRecord = m_fileRecord.GetAttributeRecord(AttributeType.Bitmap, m_indexName); if (m_indexAllocationRecord != null && m_bitmapRecord != null) { m_indexAllocationData = new NonResidentAttributeData(m_volume, m_fileRecord, m_indexAllocationRecord); long numberOfUsableBits = (long)(m_indexAllocationRecord.DataLength / m_rootRecord.BytesPerIndexRecord); m_bitmapData = new BitmapData(m_volume, m_fileRecord, m_bitmapRecord, numberOfUsableBits); } else if (m_rootRecord.IsParentNode && m_indexAllocationRecord == null) { throw new InvalidDataException("Missing Index Allocation Record"); } else if (m_rootRecord.IsParentNode && m_bitmapRecord == null) { throw new InvalidDataException("Missing Index Bitmap Record"); } }
public override void Delete(string path) { string streamName = GetStreamName(path); path = GetFilePath(path); FileRecord fileRecord = m_volume.GetFileRecord(path); if (streamName == String.Empty) { m_volume.DeleteFile(fileRecord); } else { // We only delete the named stream AttributeRecord dataRecord = fileRecord.GetAttributeRecord(AttributeType.Data, streamName); if (dataRecord == null) { throw new FileNotFoundException(String.Format("The file '{0}' does not contain a stream named '{1}'", path, streamName)); } AttributeData attributeData = new AttributeData(m_volume, fileRecord, dataRecord); attributeData.Truncate(0); fileRecord.RemoveAttributeRecord(AttributeType.Data, streamName); m_volume.UpdateFileRecord(fileRecord); } }
private FileRecord ReadMftRecord(bool useMftMirror, bool readMftMirror) { NTFSBootRecord bootRecord = m_volume.BootRecord; long mftStartLCN = useMftMirror ? (long)bootRecord.MftMirrorStartLCN : (long)bootRecord.MftStartLCN; long mftSegmentNumber = readMftMirror ? MftMirrorSegmentNumber : MasterFileTableSegmentNumber; FileRecordSegment mftRecordSegment; try { mftRecordSegment = ReadMftRecordSegment(mftStartLCN, mftSegmentNumber); } catch (InvalidDataException) { throw new InvalidDataException("Invalid MFT base record segment"); } if (!mftRecordSegment.IsBaseFileRecord) { throw new InvalidDataException("Invalid MFT file record, MFT segment number did not correspond to a base file record segment"); } AttributeRecord attributeListRecord = mftRecordSegment.GetImmediateAttributeRecord(AttributeType.AttributeList, String.Empty); if (attributeListRecord == null) { return(new FileRecord(mftRecordSegment)); } else { AttributeList attributeList = new AttributeList(m_volume, attributeListRecord); List <AttributeListEntry> entries = attributeList.ReadEntries(); List <MftSegmentReference> references = AttributeList.GetSegmentReferenceList(entries); int baseSegmentIndex = MftSegmentReference.IndexOfSegmentNumber(references, MasterFileTableSegmentNumber); if (baseSegmentIndex >= 0) { references.RemoveAt(baseSegmentIndex); } List <FileRecordSegment> recordSegments = new List <FileRecordSegment>(); // we want the base record segment first recordSegments.Add(mftRecordSegment); foreach (MftSegmentReference reference in references) { FileRecordSegment segment; try { segment = ReadMftRecordSegment(mftStartLCN, reference); } catch (InvalidDataException) { throw new InvalidDataException("Invalid MFT file record, referenced segment is invalid"); } recordSegments.Add(segment); } return(new FileRecord(recordSegments)); } }
public AttributeRecord CreateAttributeRecord(AttributeType type, string name) { AttributeRecord attribute = AttributeRecord.Create(type, name, NextAttributeInstance); NextAttributeInstance++; FileRecordHelper.InsertSorted(m_immediateAttributes, attribute); return(attribute); }
/// <param name="attributeName">The name of the data attribute we wish to access</param> public NTFSFile(NTFSVolume volume, FileRecord fileRecord, string attributeName) { m_volume = volume; m_fileRecord = fileRecord; AttributeRecord attributeRecord = fileRecord.GetAttributeRecord(AttributeType.Data, attributeName); m_data = new AttributeData(m_volume, m_fileRecord, attributeRecord); }
public AttributeRecord CreateAttributeRecord(AttributeType type, string name) { AttributeRecord attribute = AttributeRecord.Create(type, name, m_segments[0].NextAttributeInstance); m_segments[0].NextAttributeInstance++; FileRecordHelper.InsertSorted(this.Attributes, attribute); return(attribute); }
public FileRecordSegment(byte[] buffer, int offset, int bytesPerSector, long segmentNumber) { Signature = ByteReader.ReadAnsiString(buffer, offset + 0x00, 4); ushort updateSequenceArrayOffset = LittleEndianConverter.ToUInt16(buffer, offset + 0x04); ushort updateSequenceArraySize = LittleEndianConverter.ToUInt16(buffer, offset + 0x06); LogFileSequenceNumber = LittleEndianConverter.ToUInt64(buffer, offset + 0x08); SequenceNumber = LittleEndianConverter.ToUInt16(buffer, offset + 0x10); HardLinkCount = LittleEndianConverter.ToUInt16(buffer, offset + 0x12); ushort firstAttributeOffset = LittleEndianConverter.ToUInt16(buffer, offset + 0x14); m_flags = (FileRecordFlags)LittleEndianConverter.ToUInt16(buffer, offset + 0x16); uint segmentRealSize = LittleEndianConverter.ToUInt32(buffer, offset + 0x18); uint segmentAllocatedSize = LittleEndianConverter.ToUInt32(buffer, offset + 0x1C); BaseFileRecordSegmentNumber = LittleEndianConverter.ToUInt64(buffer, offset + 0x20); NextAttributeId = LittleEndianConverter.ToUInt16(buffer, offset + 0x28); // 2 zeros - padding MftSegmentNumberXP = LittleEndianConverter.ToUInt32(buffer, offset + 0x2C); // There is an UpdateSequenceNumber for the FileRecordSegment, // and an entry in the UpdateSequenceArray for each sector of the record // The last two bytes of each sector contains this entry for integrity-check purposes int position = offset + updateSequenceArrayOffset; UpdateSequenceNumber = LittleEndianConverter.ToUInt16(buffer, position); position += 2; // This stores the data that was supposed to be placed at the end of each sector, and was replaced with an UpdateSequenceNumber List <byte[]> updateSequenceReplacementData = new List <byte[]>(); for (int index = 0; index < updateSequenceArraySize - 1; index++) { byte[] endOfSectorBytes = new byte[2]; endOfSectorBytes[0] = buffer[position + 0]; endOfSectorBytes[1] = buffer[position + 1]; updateSequenceReplacementData.Add(endOfSectorBytes); position += 2; } MultiSectorHelper.DecodeSegmentBuffer(buffer, offset, UpdateSequenceNumber, updateSequenceReplacementData); // read attributes position = offset + firstAttributeOffset; while (!IsEndMarker(buffer, position)) { AttributeRecord attribute = AttributeRecord.FromBytes(buffer, position); m_immediateAttributes.Add(attribute); position += (int)attribute.StoredRecordLength; if (position > buffer.Length) { throw new InvalidDataException("Improper attribute length"); } } m_mftSegmentNumber = segmentNumber; }
public void Extend(ulong additionalLengthInBytes) { ulong currentSize = this.Length; if (m_attributeRecord is NonResidentAttributeRecord) { NonResidentAttributeData attributeData = new NonResidentAttributeData(m_volume, m_fileRecord, (NonResidentAttributeRecord)m_attributeRecord); attributeData.Extend(additionalLengthInBytes); } else { byte[] data = ((ResidentAttributeRecord)m_attributeRecord).Data; ulong finalDataLength = (uint)data.Length + additionalLengthInBytes; ulong finalRecordLength = (uint)m_attributeRecord.RecordLength + additionalLengthInBytes; if (finalRecordLength >= (ulong)m_volume.AttributeRecordLengthToMakeNonResident && m_attributeRecord.AttributeType != AttributeType.AttributeList) // We will create an attribute list with the right attribute form in advance. { // Convert the attribute to non-resident long clustersToAllocate = (long)Math.Ceiling((double)finalDataLength / m_volume.BytesPerCluster); if (clustersToAllocate > m_volume.NumberOfFreeClusters) { throw new DiskFullException(); } NonResidentAttributeRecord attributeRecord = NonResidentAttributeRecord.Create(m_attributeRecord.AttributeType, m_attributeRecord.Name); NonResidentAttributeData attributeData = new NonResidentAttributeData(m_volume, null, attributeRecord); attributeData.Extend(finalDataLength); if (data.Length % m_volume.BytesPerCluster > 0) { int fillDataLength = m_volume.BytesPerCluster - (data.Length % m_volume.BytesPerCluster); if ((uint)data.Length + (uint)fillDataLength < finalDataLength) { // Only the last cluster can be partially used, if this is not the last cluster, zero-fill it data = ByteUtils.Concatenate(data, new byte[fillDataLength]); } } attributeData.WriteClusters(0, data); // Note that we overwrite the old attribute only after writing the non-resident data if (m_fileRecord != null) { m_fileRecord.RemoveAttributeRecord(m_attributeRecord.AttributeType, m_attributeRecord.Name); FileRecordHelper.InsertSorted(m_fileRecord.Attributes, attributeRecord); } m_attributeRecord = attributeRecord; } else { int currentLength = data.Length; byte[] temp = new byte[currentLength + (int)additionalLengthInBytes]; Array.Copy(data, temp, data.Length); ((ResidentAttributeRecord)m_attributeRecord).Data = temp; } if (m_fileRecord != null) { m_volume.UpdateFileRecord(m_fileRecord); } } }
internal AttributeRecord CreateAttributeRecord(AttributeType type, string name, bool isResident) { AttributeRecord attribute = AttributeRecord.Create(type, name, isResident); attribute.Instance = NextAttributeInstance; NextAttributeInstance++; FileRecordHelper.InsertSorted(m_immediateAttributes, attribute); return(attribute); }
private FileRecord ReadMftRecord(bool useMftMirror, bool readMftMirror) { NTFSBootRecord bootRecord = m_volume.BootRecord; if (bootRecord != null) { long mftStartLCN = useMftMirror ? (long)bootRecord.MftMirrorStartLCN : (long)bootRecord.MftStartLCN; long mftSegmentNumber = readMftMirror ? MftMirrorSegmentNumber : MasterFileTableSegmentNumber; FileRecordSegment mftRecordSegment = ReadFileRecordSegment(mftStartLCN, mftSegmentNumber); if (!mftRecordSegment.IsBaseFileRecord) { throw new InvalidDataException("Invalid MFT record, not a base record"); } AttributeRecord attributeListRecord = mftRecordSegment.GetImmediateAttributeRecord(AttributeType.AttributeList, String.Empty); if (attributeListRecord == null) { return(new FileRecord(mftRecordSegment)); } else { // I have never personally seen an MFT with an attribute list AttributeList attributeList = new AttributeList(m_volume, attributeListRecord); List <AttributeListEntry> entries = attributeList.ReadEntries(); List <MftSegmentReference> references = AttributeList.GetSegmentReferenceList(entries); int baseSegmentIndex = MftSegmentReference.IndexOfSegmentNumber(references, MasterFileTableSegmentNumber); if (baseSegmentIndex >= 0) { references.RemoveAt(baseSegmentIndex); } List <FileRecordSegment> recordSegments = new List <FileRecordSegment>(); // we want the base record segment first recordSegments.Add(mftRecordSegment); foreach (MftSegmentReference reference in references) { FileRecordSegment segment = ReadFileRecordSegment(mftStartLCN, reference); if (segment != null) { recordSegments.Add(segment); } else { throw new InvalidDataException("Invalid MFT record, missing segment"); } } return(new FileRecord(recordSegments)); } } else { return(null); } }
private static int CompareAttributeTypes(AttributeRecord attribute1, AttributeRecord attribute2) { int result = attribute1.AttributeType.CompareTo(attribute2.AttributeType); if (result == 0) { result = String.Compare(attribute1.Name, attribute2.Name, StringComparison.OrdinalIgnoreCase); } return(result); }
public LfsRecord WriteLogRecord(MftSegmentReference fileReference, AttributeRecord attributeRecord, ulong streamOffset, int structureLength, int recordOffset, int attributeOffset, NTFSLogOperation redoOperation, byte[] redoData, NTFSLogOperation undoOperation, byte[] undoData, uint transactionID, bool flushToDisk) { int openAttributeOffset = 0; if (fileReference != null) { int openAttributeIndex = IndexOfOpenAttribute(fileReference, attributeRecord.AttributeType, attributeRecord.Name); if (openAttributeIndex == -1) { openAttributeIndex = AddToOpenAttributeTable(fileReference, attributeRecord.AttributeType, attributeRecord.Name, m_lastClientLsn); m_openAttributes[openAttributeIndex].AssociatedTransactions.Add(transactionID); openAttributeOffset = OpenAttributeIndexToOffset(openAttributeIndex); OpenAttributeEntry entry = new OpenAttributeEntry(m_majorVersion); entry.AllocatedOrNextFree = RestartTableEntry.RestartEntryAllocated; // Note: NTFS v5.1 driver calulates AttributeOffset using entry length of 0x28, the reason is unclear but we're immitating this. entry.AttributeOffset = (uint)(RestartTableHeader.Length + openAttributeIndex * OpenAttributeEntry.LengthV1); entry.FileReference = fileReference; entry.LsnOfOpenRecord = m_lastClientLsn; entry.AttributeTypeCode = attributeRecord.AttributeType; if (attributeRecord.AttributeType == AttributeType.IndexAllocation) { entry.BytesPerIndexBuffer = (uint)Volume.BytesPerIndexRecord; } byte[] openAttributeBytes = entry.GetBytes(); byte[] attributeNameBytes = System.Text.Encoding.Unicode.GetBytes(attributeRecord.Name); LfsRecord openAttributeRecord = WriteLogRecord(openAttributeOffset, 0, 0, 0, new List <long>(), NTFSLogOperation.OpenNonResidentAttribute, openAttributeBytes, NTFSLogOperation.Noop, attributeNameBytes, transactionID, false); } else { openAttributeOffset = OpenAttributeIndexToOffset(openAttributeIndex); if (!m_openAttributes[openAttributeIndex].AssociatedTransactions.Contains(transactionID)) { m_openAttributes[openAttributeIndex].AssociatedTransactions.Add(transactionID); } } } List <long> lcnList = new List <long>(); if (attributeRecord is NonResidentAttributeRecord) { long startVCN = (long)(streamOffset / (uint)Volume.BytesPerCluster); int clusterCount = (int)Math.Ceiling((double)structureLength / Volume.BytesPerCluster); for (long vcn = startVCN; vcn < startVCN + clusterCount; vcn++) { long lcn = ((NonResidentAttributeRecord)attributeRecord).DataRunSequence.GetDataClusterLCN(vcn); lcnList.Add(lcn); } } return(WriteLogRecord(openAttributeOffset, streamOffset, recordOffset, attributeOffset, lcnList, redoOperation, redoData, undoOperation, undoData, transactionID, flushToDisk)); }
public AttributeRecord CreateAttributeRecord(AttributeType type, string name) { if (name.Length > AttributeRecord.MaxAttributeNameLength) { throw new InvalidNameException(); } bool isResident = (type != AttributeType.IndexAllocation); AttributeRecord attribute = AttributeRecord.Create(type, name, isResident); FileRecordHelper.InsertSorted(this.Attributes, attribute); return(attribute); }
public FileRecord GetFileRecord(long baseSegmentNumber) { FileRecordSegment baseSegment = GetFileRecordSegment(baseSegmentNumber); if (baseSegment != null && baseSegment.IsBaseFileRecord) { AttributeRecord attributeListRecord = baseSegment.GetImmediateAttributeRecord(AttributeType.AttributeList, String.Empty); if (attributeListRecord == null) { return(new FileRecord(baseSegment)); } else { // The attribute list contains entries for every attribute the record has (excluding the attribute list), // including attributes that reside within the base record segment. AttributeList attributeList = new AttributeList(m_volume, attributeListRecord); List <AttributeListEntry> entries = attributeList.ReadEntries(); List <MftSegmentReference> references = AttributeList.GetSegmentReferenceList(entries); int baseSegmentIndex = MftSegmentReference.IndexOfSegmentNumber(references, baseSegmentNumber); if (baseSegmentIndex >= 0) { references.RemoveAt(baseSegmentIndex); } List <FileRecordSegment> recordSegments = new List <FileRecordSegment>(); // we want the base record segment first recordSegments.Add(baseSegment); foreach (MftSegmentReference reference in references) { FileRecordSegment segment = GetFileRecordSegment(reference); if (segment != null) { recordSegments.Add(segment); } else { // record is invalid return(null); } } return(new FileRecord(recordSegments)); } } else { return(null); } }
public AttributeRecord CreateAttributeListRecord(bool isResident) { AttributeRecord attribute; if (isResident) { attribute = AttributeRecord.Create(AttributeType.AttributeList, String.Empty, NextAttributeInstance); } else { attribute = NonResidentAttributeRecord.Create(AttributeType.AttributeList, String.Empty, NextAttributeInstance); } NextAttributeInstance++; FileRecordHelper.InsertSorted(m_immediateAttributes, attribute); return(attribute); }
public FileRecordSegment(byte[] buffer, int offset, long segmentNumber) { MultiSectorHeader multiSectorHeader = new MultiSectorHeader(buffer, offset + 0x00); if (multiSectorHeader.Signature != ValidSignature) { throw new InvalidDataException("Invalid FILE record signature"); } LogFileSequenceNumber = LittleEndianConverter.ToUInt64(buffer, offset + 0x08); m_sequenceNumber = LittleEndianConverter.ToUInt16(buffer, offset + 0x10); ReferenceCount = LittleEndianConverter.ToUInt16(buffer, offset + 0x12); ushort firstAttributeOffset = LittleEndianConverter.ToUInt16(buffer, offset + 0x14); m_flags = (FileRecordFlags)LittleEndianConverter.ToUInt16(buffer, offset + 0x16); uint segmentLength = LittleEndianConverter.ToUInt32(buffer, offset + 0x18); uint segmentAllocatedLength = LittleEndianConverter.ToUInt32(buffer, offset + 0x1C); m_baseFileRecordSegment = new MftSegmentReference(buffer, offset + 0x20); NextAttributeInstance = LittleEndianConverter.ToUInt16(buffer, offset + 0x28); // 2 bytes padding m_segmentNumberOnDisk = LittleEndianConverter.ToUInt32(buffer, offset + 0x2C); UpdateSequenceNumber = LittleEndianConverter.ToUInt16(buffer, offset + multiSectorHeader.UpdateSequenceArrayOffset); if (firstAttributeOffset % 8 > 0) { throw new InvalidDataException("Corrupt file record segment, first attribute not aligned to 8-byte boundary"); } // Read attributes int position = offset + firstAttributeOffset; while (!IsEndMarker(buffer, position)) { AttributeRecord attribute = AttributeRecord.FromBytes(buffer, position); m_immediateAttributes.Add(attribute); position += (int)attribute.RecordLengthOnDisk; if (position > buffer.Length) { throw new InvalidDataException("Invalid attribute length"); } } m_segmentNumber = segmentNumber; }
public void AddEntry(MftSegmentReference fileReference, byte[] key) { IndexEntry entry = new IndexEntry(); entry.FileReference = fileReference; entry.Key = key; if (!m_rootRecord.IsParentNode) { int insertIndex = CollationHelper.FindIndexForSortedInsert(m_rootRecord.IndexEntries, key, m_rootRecord.CollationRule); m_rootRecord.IndexEntries.Insert(insertIndex, entry); if (m_rootRecord.RecordLength >= m_volume.AttributeRecordLengthToMakeNonResident) { if (m_indexAllocationRecord == null) { m_indexAllocationRecord = (IndexAllocationRecord)m_fileRecord.CreateAttributeRecord(AttributeType.IndexAllocation, m_indexName); m_indexAllocationData = new NonResidentAttributeData(m_volume, m_fileRecord, m_indexAllocationRecord); m_bitmapRecord = m_fileRecord.CreateAttributeRecord(AttributeType.Bitmap, m_indexName); m_bitmapData = new BitmapData(m_volume, m_fileRecord, m_bitmapRecord, 0); } SplitRootIndexRecord(); } else { m_volume.UpdateFileRecord(m_fileRecord); } } else { KeyValuePairList <int, IndexRecord> path = FindInsertPath(key); IndexRecord leafRecord = path[path.Count - 1].Value; long leafRecordVBN = leafRecord.RecordVBN; int insertIndex = CollationHelper.FindIndexForSortedInsert(leafRecord.IndexEntries, key, m_rootRecord.CollationRule); leafRecord.IndexEntries.Insert(insertIndex, entry); long leafRecordIndex = ConvertToRecordIndex(leafRecordVBN); if (leafRecord.DoesFit((int)m_rootRecord.BytesPerIndexRecord)) { WriteIndexRecord(leafRecordIndex, leafRecord); } else { // Split index record SplitIndexRecord(path); } } }
public AttributeListRecord(NTFSVolume volume, AttributeRecord record) { m_volume = volume; m_record = record; byte[] data = m_record.GetData(volume); int position = 0; while (position < data.Length) { AttributeListEntry entry = new AttributeListEntry(data, position); AttributeList.Add(entry); position += entry.Length; if (entry.Length < AttributeListEntry.HeaderLength) { string message = String.Format("Invalid attribute list entry, data length: {0}, position: {1}", data.Length, position); throw new Exception(message); } } }
/// <param name="useMftMirror">Strap the MFT using the MFT mirror</param> public MasterFileTable(NTFSVolume volume, bool useMftMirror, bool manageMftMirror) { m_volume = volume; m_mftRecord = ReadMftRecord(useMftMirror, manageMftMirror); AttributeRecord dataRecord = m_mftRecord.DataRecord; if (dataRecord == null) { throw new InvalidDataException("Invalid MFT Record, missing Data attribute"); } m_mftData = new AttributeData(m_volume, m_mftRecord, dataRecord); long numberOfUsableBits = (long)(m_mftData.Length / (uint)m_volume.BytesPerFileRecordSegment); if (!manageMftMirror) { AttributeRecord bitmapRecord = m_mftRecord.BitmapRecord; if (bitmapRecord == null) { throw new InvalidDataException("Invalid MFT Record, missing Bitmap attribute"); } m_mftBitmap = new BitmapData(volume, m_mftRecord, bitmapRecord, numberOfUsableBits); } AttributeRecordLengthToMakeNonResident = m_volume.BytesPerFileRecordSegment * 5 / 16; // We immitate the NTFS v5.1 driver if (m_volume.Size >= LargeVolumeThreshold) { ExtendGranularity = ExtendGranularityLargeVolume; } else if (m_volume.Size >= MediumVolumeThreshold) { ExtendGranularity = ExtendGranularityMediumVolume; } else { ExtendGranularity = ExtendGranularitySmallVolume; } NumberOfClustersRequiredToExtend = GetNumberOfClusteredRequiredToExtend(); }
public void UpdateFileRecord(FileRecord fileRecord, uint transactionID) { Dictionary <MftSegmentReference, byte[]> undoDictionary = new Dictionary <MftSegmentReference, byte[]>(); foreach (FileRecordSegment segment in fileRecord.Segments) { byte[] segmentBytes = segment.GetBytes(m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion, false); undoDictionary.Add(segment.SegmentReference, segmentBytes); } AttributeRecord oldAttributeList = fileRecord.BaseSegment.GetImmediateAttributeRecord(AttributeType.AttributeList, String.Empty); fileRecord.UpdateSegments(m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion); FileRecordSegment baseSegment = fileRecord.BaseSegment; for (int segmentIndex = 1; segmentIndex < fileRecord.Segments.Count; segmentIndex++) { FileRecordSegment segment = fileRecord.Segments[segmentIndex]; if (segment.SegmentReference == MftSegmentReference.NullReference) { // New segment, we must allocate space for it MftSegmentReference segmentReference; if (baseSegment.SegmentNumber == MasterFileTable.MasterFileTableSegmentNumber) { segmentReference = AllocateReservedFileRecordSegment(transactionID); } else { segmentReference = AllocateFileRecordSegment(transactionID); } FileRecordSegment newSegment = new FileRecordSegment(segmentReference.SegmentNumber, segmentReference.SequenceNumber, baseSegment.SegmentReference); newSegment.IsInUse = true; newSegment.IsDirectory = fileRecord.IsDirectory; newSegment.NextAttributeInstance = segment.NextAttributeInstance; newSegment.ImmediateAttributes.AddRange(segment.ImmediateAttributes); fileRecord.Segments[segmentIndex] = newSegment; } else if (segment.ImmediateAttributes.Count == 0) { byte[] undoData = undoDictionary[segment.SegmentReference]; ulong streamOffset = (ulong)(segment.SegmentNumber * m_volume.BytesPerFileRecordSegment); m_volume.LogClient.WriteLogRecord(m_mftRecord.BaseSegmentReference, m_mftRecord.DataRecord, streamOffset, m_volume.BytesPerFileRecordSegment, NTFSLogOperation.DeallocateFileRecordSegment, new byte[0], NTFSLogOperation.InitializeFileRecordSegment, undoData, transactionID); DeallocateFileRecordSegment(segment, transactionID); fileRecord.Segments.RemoveAt(segmentIndex); segmentIndex--; } } for (int segmentIndex = 1; segmentIndex < fileRecord.Segments.Count; segmentIndex++) { FileRecordSegment segment = fileRecord.Segments[segmentIndex]; byte[] undoData; byte[] redoData = segment.GetBytes(m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion, false); ulong streamOffset = (ulong)(segment.SegmentNumber * m_volume.BytesPerFileRecordSegment); if (undoDictionary.TryGetValue(segment.SegmentReference, out undoData)) { m_volume.LogClient.WriteLogRecord(m_mftRecord.BaseSegmentReference, m_mftRecord.DataRecord, streamOffset, m_volume.BytesPerFileRecordSegment, NTFSLogOperation.InitializeFileRecordSegment, redoData, NTFSLogOperation.InitializeFileRecordSegment, undoData, transactionID); } else { // New segment m_volume.LogClient.WriteLogRecord(m_mftRecord.BaseSegmentReference, m_mftRecord.DataRecord, streamOffset, m_volume.BytesPerFileRecordSegment, NTFSLogOperation.InitializeFileRecordSegment, redoData, NTFSLogOperation.DeallocateFileRecordSegment, new byte[0], transactionID); } UpdateFileRecordSegment(segment); } if (oldAttributeList is NonResidentAttributeRecord) { new NonResidentAttributeData(m_volume, null, (NonResidentAttributeRecord)oldAttributeList).Truncate(0); } bool needsAttributeList = (fileRecord.Segments.Count > 1); if (needsAttributeList) { List <AttributeListEntry> entries = FileRecordHelper.BuildAttributeList(fileRecord.Segments, m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion); int dataLength = AttributeList.GetLength(entries); int attributeListRecordLength = ResidentAttributeRecord.GetRecordLength(0, dataLength); int numberOfBytesFreeInBaseSegment = baseSegment.GetNumberOfBytesFree(m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion); bool isResident = (attributeListRecordLength <= numberOfBytesFreeInBaseSegment); AttributeRecord attributeListRecord = baseSegment.CreateAttributeListRecord(isResident); AttributeList attributeList = new AttributeList(m_volume, attributeListRecord); attributeList.WriteEntries(entries); } byte[] baseRecordUndoData = undoDictionary[baseSegment.SegmentReference]; byte[] baseRecordRedoData = baseSegment.GetBytes(m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion, false); ulong baseRecordStreamOffset = (ulong)(baseSegment.SegmentNumber * m_volume.BytesPerFileRecordSegment); m_volume.LogClient.WriteLogRecord(m_mftRecord.BaseSegmentReference, m_mftRecord.DataRecord, baseRecordStreamOffset, m_volume.BytesPerFileRecordSegment, NTFSLogOperation.InitializeFileRecordSegment, baseRecordRedoData, NTFSLogOperation.InitializeFileRecordSegment, baseRecordUndoData, transactionID); UpdateFileRecordSegment(baseSegment); }
public void UpdateFileRecord(FileRecord fileRecord) { AttributeRecord oldAttributeList = fileRecord.BaseSegment.GetImmediateAttributeRecord(AttributeType.AttributeList, String.Empty); fileRecord.UpdateSegments(m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion); FileRecordSegment baseSegment = fileRecord.BaseSegment; for (int segmentIndex = 1; segmentIndex < fileRecord.Segments.Count; segmentIndex++) { FileRecordSegment segment = fileRecord.Segments[segmentIndex]; if (segment.SegmentReference == MftSegmentReference.NullReference) { // New segment, we must allocate space for it MftSegmentReference segmentReference; if (baseSegment.SegmentNumber == MasterFileTable.MasterFileTableSegmentNumber) { segmentReference = AllocateReservedFileRecordSegment(); } else { segmentReference = AllocateFileRecordSegment(); } FileRecordSegment newSegment = new FileRecordSegment(segmentReference.SegmentNumber, segmentReference.SequenceNumber, baseSegment.SegmentReference); newSegment.IsInUse = true; newSegment.IsDirectory = fileRecord.IsDirectory; newSegment.NextAttributeInstance = segment.NextAttributeInstance; newSegment.ImmediateAttributes.AddRange(segment.ImmediateAttributes); fileRecord.Segments[segmentIndex] = newSegment; } else if (segment.ImmediateAttributes.Count == 0) { DeallocateFileRecordSegment(segment); fileRecord.Segments.RemoveAt(segmentIndex); segmentIndex--; } } foreach (FileRecordSegment segment in fileRecord.Segments) { UpdateFileRecordSegment(segment); } if (oldAttributeList is NonResidentAttributeRecord) { new NonResidentAttributeData(m_volume, null, (NonResidentAttributeRecord)oldAttributeList).Truncate(0); } bool needsAttributeList = (fileRecord.Segments.Count > 1); if (needsAttributeList) { List <AttributeListEntry> entries = FileRecordHelper.BuildAttributeList(fileRecord.Segments, m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion); int dataLength = AttributeList.GetLength(entries); int attributeListRecordLength = ResidentAttributeRecord.GetRecordLength(0, dataLength); int numberOfBytesFreeInBaseSegment = baseSegment.GetNumberOfBytesFree(m_volume.BytesPerFileRecordSegment, m_volume.MinorVersion); bool isResident = (attributeListRecordLength <= numberOfBytesFreeInBaseSegment); AttributeRecord attributeListRecord = baseSegment.CreateAttributeListRecord(isResident); AttributeList attributeList = new AttributeList(m_volume, attributeListRecord); attributeList.WriteEntries(entries); UpdateFileRecordSegment(baseSegment); } }
public override Stream OpenFile(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { string streamName = GetStreamName(path); path = GetFilePath(path); FileRecord fileRecord = null; AttributeRecord dataRecord = null; if (mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.OpenOrCreate) { bool fileExists = false; try { fileRecord = m_volume.GetFileRecord(path); fileExists = true; } catch (FileNotFoundException) { } catch (DirectoryNotFoundException) { } if (fileExists) { if (mode == FileMode.CreateNew) { if (streamName == String.Empty) { throw new AlreadyExistsException(); } else { dataRecord = fileRecord.GetAttributeRecord(AttributeType.Data, streamName); if (dataRecord != null) { throw new AlreadyExistsException(); } } } if (streamName != String.Empty && dataRecord == null) { // We might need to allocate an additional FileRecordSegment so we have to make sure we can extend the MFT if it is full if (m_volume.NumberOfFreeClusters < m_volume.NumberOfClustersRequiredToExtendMft) { throw new DiskFullException(); } fileRecord.CreateAttributeRecord(AttributeType.Data, streamName); m_volume.UpdateFileRecord(fileRecord); } if (mode == FileMode.Create) { mode = FileMode.Truncate; } } else { string directoryPath = Path.GetDirectoryName(path); string fileName = Path.GetFileName(path); FileRecord directoryRecord = m_volume.GetFileRecord(directoryPath); fileRecord = m_volume.CreateFile(directoryRecord.BaseSegmentReference, fileName, false); if (streamName != String.Empty) { fileRecord.CreateAttributeRecord(AttributeType.Data, streamName); m_volume.UpdateFileRecord(fileRecord); } } } else // Open, Truncate or Append { fileRecord = m_volume.GetFileRecord(path); dataRecord = fileRecord.GetAttributeRecord(AttributeType.Data, streamName); if (streamName != String.Empty && dataRecord == null) { throw new FileNotFoundException(String.Format("The file '{0}' does not contain a stream named '{1}'", path, streamName)); } } if (fileRecord.IsDirectory) { throw new UnauthorizedAccessException(); } List <NTFSFileStream> openStreams; lock (m_openStreams) { if (m_openStreams.TryGetValue(fileRecord.BaseSegmentNumber, out openStreams)) { if ((access & FileAccess.Write) != 0) { // Currently we only support opening a file stream for write access if no other stream is opened for that file throw new SharingViolationException(); } else if ((access & FileAccess.Read) != 0) { foreach (NTFSFileStream openStream in openStreams) { if (openStream.CanWrite && ((share & FileShare.Write) == 0)) { throw new SharingViolationException(); } } } } else { openStreams = new List <NTFSFileStream>(); m_openStreams.Add(fileRecord.BaseSegmentNumber, openStreams); } } NTFSFile file = new NTFSFile(m_volume, fileRecord, streamName); NTFSFileStream stream = new NTFSFileStream(file, access); openStreams.Add(stream); stream.Closed += delegate(object sender, EventArgs e) { openStreams.Remove(stream); if (openStreams.Count == 0) { lock (m_openStreams) { m_openStreams.Remove(fileRecord.BaseSegmentNumber); } } }; if (mode == FileMode.Truncate) { stream.SetLength(0); } else if (mode == FileMode.Append) { stream.Seek((long)file.Length, SeekOrigin.Begin); } return(stream); }
public AttributeData(NTFSVolume volume, FileRecord fileRecord, AttributeRecord attributeRecord) { m_volume = volume; m_fileRecord = fileRecord; m_attributeRecord = attributeRecord; }
/// <remarks> /// FileRecordSegment attributes MUST be sorted by AttributeType with a secondary sort by Name. /// </remarks> public static void InsertSorted(List <AttributeRecord> attributes, AttributeRecord attribute) { int insertIndex = SortedList <AttributeRecord> .FindIndexForSortedInsert(attributes, CompareAttributeTypes, attribute); attributes.Insert(insertIndex, attribute); }
public static void SliceAttributes(List <FileRecordSegment> segments, List <AttributeRecord> attributes, int bytesPerFileRecordSegment, ushort minorNTFSVersion) { int bytesAvailableInSegment = FileRecordSegment.GetNumberOfBytesAvailable(bytesPerFileRecordSegment, minorNTFSVersion); LinkedList <KeyValuePair <AttributeRecord, bool> > remainingAttributes = new LinkedList <KeyValuePair <AttributeRecord, bool> >(); FileRecordSegment baseFileRecordSegment = segments[0]; long segmentNumber = baseFileRecordSegment.SegmentNumber; bool isMftFileRecord = (segmentNumber == MasterFileTable.MasterFileTableSegmentNumber || segmentNumber == MasterFileTable.MftMirrorSegmentNumber); foreach (AttributeRecord attribute in attributes) { if (attribute.AttributeType == AttributeType.StandardInformation || attribute.AttributeType == AttributeType.FileName) { baseFileRecordSegment.ImmediateAttributes.Add(attribute); } else if (isMftFileRecord && attribute.AttributeType == AttributeType.Data) { List <NonResidentAttributeRecord> slices = SliceAttributeRecord((NonResidentAttributeRecord)attribute, bytesPerFileRecordSegment / 2, bytesAvailableInSegment); baseFileRecordSegment.ImmediateAttributes.Add(slices[0]); slices.RemoveAt(0); foreach (NonResidentAttributeRecord slice in slices) { remainingAttributes.AddLast(new KeyValuePair <AttributeRecord, bool>(slice, true)); } } else { remainingAttributes.AddLast(new KeyValuePair <AttributeRecord, bool>(attribute, false)); } } int segmentIndex = 1; int remainingLengthInCurrentSegment = bytesAvailableInSegment; while (remainingAttributes.Count > 0) { AttributeRecord attribute = remainingAttributes.First.Value.Key; bool isSlice = remainingAttributes.First.Value.Value; if (segmentIndex == segments.Count) { MftSegmentReference newSegmentReference = MftSegmentReference.NullReference; FileRecordSegment newFileRecordSegment = new FileRecordSegment(newSegmentReference.SegmentNumber, newSegmentReference.SequenceNumber, baseFileRecordSegment.SegmentReference); newFileRecordSegment.IsInUse = true; segments.Add(newFileRecordSegment); } if (attribute.RecordLength <= remainingLengthInCurrentSegment) { remainingLengthInCurrentSegment -= (int)attribute.RecordLength; segments[segmentIndex].ImmediateAttributes.Add(attribute); remainingAttributes.RemoveFirst(); // Instead of renumbering each attribute slice in the new FileRecordSegment, we use the original Instance number. if (segments[segmentIndex].NextAttributeInstance <= attribute.Instance) { segments[segmentIndex].NextAttributeInstance = (ushort)(attribute.Instance + 1); } } else { if (attribute is ResidentAttributeRecord || isSlice) { segmentIndex++; remainingLengthInCurrentSegment = bytesAvailableInSegment; } else { NonResidentAttributeRecord nonResidentAttribute = ((NonResidentAttributeRecord)attribute); List <NonResidentAttributeRecord> slices = SliceAttributeRecord((NonResidentAttributeRecord)attribute, remainingLengthInCurrentSegment, bytesAvailableInSegment); remainingAttributes.RemoveFirst(); slices.Reverse(); foreach (NonResidentAttributeRecord slice in slices) { remainingAttributes.AddFirst(new KeyValuePair <AttributeRecord, bool>(slice, true)); } } } } }
public DataRecord(AttributeRecord record) { m_record = record; }
public BitmapData(NTFSVolume volume, FileRecord fileRecord, AttributeRecord attributeRecord, long numberOfUsableBits) : base(volume, fileRecord, attributeRecord) { m_numberOfUsableBits = numberOfUsableBits; }
public AttributeList(NTFSVolume volume, AttributeRecord attributeRecord) : base(volume, null, attributeRecord) { }
public LfsRecord WriteLogRecord(MftSegmentReference fileReference, AttributeRecord attributeRecord, ulong streamOffset, int structureLength, NTFSLogOperation redoOperation, byte[] redoData, NTFSLogOperation undoOperation, byte[] undoData, uint transactionID) { return(WriteLogRecord(fileReference, attributeRecord, streamOffset, structureLength, 0, 0, redoOperation, redoData, undoOperation, undoData, transactionID, true)); }
private FileRecord ReadMftRecord() { NTFSBootRecord bootRecord = m_volume.BootRecord; if (bootRecord != null) { long mftStartLCN; if (m_useMftMirror) { mftStartLCN = (long)bootRecord.MftMirrorStartLCN; } else { mftStartLCN = (long)bootRecord.MftStartLCN; } FileRecordSegment mftRecordSegment = GetRecordSegmentOfMasterFileTable(mftStartLCN, MasterFileTableSegmentNumber); if (!mftRecordSegment.IsBaseFileRecord) { return(null); } AttributeRecord attributeListRecord = mftRecordSegment.GetImmediateAttributeRecord(AttributeType.AttributeList); if (attributeListRecord == null) { return(new FileRecord(mftRecordSegment)); } else { // I have never personally seen an MFT with an attribute list AttributeListRecord attributeList = new AttributeListRecord(m_volume, attributeListRecord); List <MftSegmentReference> references = attributeList.GetSegmentReferenceList(); int baseSegmentIndex = MftSegmentReference.IndexOfSegmentNumber(references, MasterFileTableSegmentNumber); if (baseSegmentIndex >= 0) { references.RemoveAt(baseSegmentIndex); } List <FileRecordSegment> recordSegments = new List <FileRecordSegment>(); // we want the base record segment first recordSegments.Add(mftRecordSegment); foreach (MftSegmentReference reference in references) { FileRecordSegment segment = GetRecordSegmentOfMasterFileTable(mftStartLCN, reference); if (segment != null) { recordSegments.Add(segment); } else { // MFT is invalid return(null); } } return(new FileRecord(recordSegments)); } } else { return(null); } }