예제 #1
0
 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");
     }
 }
예제 #2
0
        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));
            }
        }
예제 #4
0
        public AttributeRecord CreateAttributeRecord(AttributeType type, string name)
        {
            AttributeRecord attribute = AttributeRecord.Create(type, name, NextAttributeInstance);

            NextAttributeInstance++;
            FileRecordHelper.InsertSorted(m_immediateAttributes, attribute);
            return(attribute);
        }
예제 #5
0
        /// <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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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;
        }
예제 #8
0
        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);
                }
            }
        }
예제 #9
0
        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);
        }
예제 #10
0
        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);
            }
        }
예제 #11
0
        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);
        }
예제 #12
0
        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));
        }
예제 #13
0
        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);
        }
예제 #14
0
        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);
            }
        }
예제 #15
0
        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);
        }
예제 #16
0
        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;
        }
예제 #17
0
        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);
                }
            }
        }
예제 #18
0
        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);
        }
예제 #21
0
        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);
            }
        }
예제 #22
0
        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);
        }
예제 #23
0
 public AttributeData(NTFSVolume volume, FileRecord fileRecord, AttributeRecord attributeRecord)
 {
     m_volume          = volume;
     m_fileRecord      = fileRecord;
     m_attributeRecord = attributeRecord;
 }
예제 #24
0
        /// <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);
        }
예제 #25
0
        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));
                        }
                    }
                }
            }
        }
예제 #26
0
 public DataRecord(AttributeRecord record)
 {
     m_record = record;
 }
예제 #27
0
 public BitmapData(NTFSVolume volume, FileRecord fileRecord, AttributeRecord attributeRecord, long numberOfUsableBits) : base(volume, fileRecord, attributeRecord)
 {
     m_numberOfUsableBits = numberOfUsableBits;
 }
예제 #28
0
 public AttributeList(NTFSVolume volume, AttributeRecord attributeRecord) : base(volume, null, attributeRecord)
 {
 }
예제 #29
0
 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));
 }
예제 #30
0
        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);
            }
        }