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(); }
private static MftDetails GetMftDetails(RawDisk disk, BootSector boot) { byte[] mftFirst = disk.ReadClusters((long)boot.MFTCluster, 1); FileRecord rec = FileRecord.Parse(mftFirst, 0, (ushort)disk.SectorSize, (uint)(boot.MFTRecordSizeBytes / disk.SectorSize)); // Get DATA attrib // TODO: Handle multiple DATA attributes AttributeGeneric dataAttrib = null; foreach (Attribute attribute in rec.Attributes) { if (attribute.Type == AttributeType.DATA && attribute.AttributeName.Length == 0) { // Got it! dataAttrib = attribute as AttributeGeneric; break; } } Debug.Assert(dataAttrib != null); MftDetails res = new MftDetails(); // Get attribute data // Parse out extents if (dataAttrib.NonResidentFlag == ResidentFlag.Resident) { byte[] dataAttribData = dataAttrib.Data; res.MftSize = dataAttrib.ResidentHeader.ContentLength; res.MftExtents = DataFragment.ParseFragments(dataAttribData, dataAttribData.Length, 0, 0, 0); } else { res.MftSize = dataAttrib.NonResidentHeader.ContentSize; res.MftExtents = dataAttrib.NonResidentHeader.Fragments; } Debug.Assert(res.MftExtents != null); return(res); }