private static void RecordExternalDiskExtents(FileRecord rec, MftDiskExtents res) { foreach (Attribute attribute in rec.Attributes) { // Skip RESIDENT attributes if (attribute.NonResidentFlag == ResidentFlag.Resident) { continue; } // Skip DATA attributes, if the MFT id is larger than 26 (first special reserved ID's) Source: https://en.wikipedia.org/wiki/NTFS#Master_File_Table // Skip ID#8 DATA attribs, as they represent either the entire disk or unreadable clusters if (rec.FileReference.FileId == 8 || (rec.FileReference.FileId > 26 && attribute.Type == AttributeType.DATA)) { continue; } // Record external extents res.Extents.AddRange(attribute.NonResidentHeader.Fragments); //if (attribute.Type == AttributeType.INDEX_ALLOCATION) // continue; //Console.WriteLine(" " + rec.FileReference + " : " + attribute.Type + "_" + attribute.AttributeName + " " + string.Join(",", attribute.NonResidentHeader.Fragments.Select(x => x.LCN + "->" + x.Clusters))); } }
static void Main(string[] args) { char driveLetter = 'C'; if (args.Length == 1) { driveLetter = args[0][0]; } using (RawDisk disk = new RawDisk(driveLetter)) { MftDiskExtents res = new MftDiskExtents(); byte[] ntfsBoot = disk.ReadSectors(0, 1); BootSector boot = BootSector.ParseData(ntfsBoot, ntfsBoot.Length, 0); Console.WriteLine("MFT is at LCN " + boot.MFTCluster); MftDetails mft = GetMftDetails(disk, boot); Console.WriteLine("MFT is in " + mft.MftExtents.Length + " extents"); res.Extents.AddRange(mft.MftExtents); using (RawDiskStream diskStream = disk.CreateDiskStream()) using (NtfsDiskStream mftStream = new NtfsDiskStream(diskStream, false, mft.MftExtents, (uint)disk.ClusterSize, 0, (long)mft.MftSize)) { uint sectorsPrRecord = (uint)(boot.MFTRecordSizeBytes / disk.SectorSize); ushort bytesPrSector = (ushort)disk.SectorSize; int records = (int)(mftStream.Length / boot.MFTRecordSizeBytes); byte[] tmp = new byte[boot.MFTRecordSizeBytes]; while (true) { int read = mftStream.Read(tmp, 0, tmp.Length); if (read < boot.MFTRecordSizeBytes) { Console.WriteLine("Stopped reading as we got " + read + " bytes instead of " + tmp.Length + " bytes"); break; } FileRecord rec = FileRecord.Parse(tmp, 0, bytesPrSector, sectorsPrRecord); // Keep track of all external extents to the MFT RecordExternalDiskExtents(rec, res); //// Special case for LIST attributes, since they can further reference more extents outside the MFT //ProcessNonResidentListAttributes(disk, rec); } } long clustersTotal = res.Extents.Sum(x => x.Clusters); Console.WriteLine("To copy: {0:N0} extents", res.Extents.Count); Console.WriteLine("{0:N0} clusters, {1:N0} bytes", clustersTotal, clustersTotal * disk.ClusterSize); } Console.ReadLine(); }