/// <summary><see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/dd405526(v=vs.85).aspx"/></summary> /// <remarks> /// Does not correcly handle all cases of this method!. Especially regarding compressed/encrypted and/or sparse files in NTFS. /// Consider yourself warned. /// </remarks> public FileExtentInfo[] FileSystemGetRetrievalPointers() { STARTING_VCN_INPUT_BUFFER input = new STARTING_VCN_INPUT_BUFFER(); input.StartingVcn = 0; List <FileExtentInfo> extents = new List <FileExtentInfo>(); uint chunkSize = 1024; uint singleExtentSize = MarshalHelper.SizeOf <RETRIEVAL_POINTERS_EXTENT>(); int lastError; do { byte[] data = DeviceIoControlHelper.InvokeIoControl(Handle, IOControlCode.FsctlGetRetrievalPointers, chunkSize, input, out lastError); RETRIEVAL_POINTERS_BUFFER output = new RETRIEVAL_POINTERS_BUFFER(); output.ExtentCount = BitConverter.ToUInt32(data, 0); output.StartingVcn = BitConverter.ToUInt64(data, sizeof(ulong)); output.Extents = new RETRIEVAL_POINTERS_EXTENT[output.ExtentCount]; // Parse extents int extentsSize = (int)(output.ExtentCount * singleExtentSize); using (var dataPtr = new UnmanagedMemory(extentsSize)) { Marshal.Copy(data, sizeof(ulong) + sizeof(ulong), dataPtr, extentsSize); for (ulong i = 0; i < output.ExtentCount; i++) { IntPtr currentPtr = new IntPtr(dataPtr.Handle.ToInt64() + (int)(singleExtentSize * i)); output.Extents[i] = currentPtr.ToStructure <RETRIEVAL_POINTERS_EXTENT>(); } } // Make extents more readable for (ulong i = 0; i < output.ExtentCount; i++) { ulong startVcn = i == 0 ? output.StartingVcn : output.Extents[i - 1].NextVcn; ulong size = output.Extents[i].NextVcn - startVcn; FileExtentInfo extent = new FileExtentInfo(); extent.Size = size; extent.Vcn = startVcn; extent.Lcn = output.Extents[i].Lcn; extents.Add(extent); } if (lastError == 38) { // End of file reached break; } // Prep the start point for the next call input.StartingVcn = output.Extents[output.ExtentCount - 1].NextVcn; } while (lastError == 234); return(extents.ToArray()); }
private void button1_Click(object sender, EventArgs e) { textBox1.Text = ""; totalClusters = 0; int extentNumber = 1; label4.Text = "File fragmentation factor = "; if (path.Length == 0 || !File.Exists(path)) { MessageBox.Show("Select a file!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } textBox1.Text += "Analysis..."; using (var file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { var vcnIn = new STARTING_VCN_INPUT_BUFFER(); var rpb = new RETRIEVAL_POINTERS_BUFFER(); int bytesReturned = 0; int err = 0; vcnIn.StartingVcn = 0L; do { DeviceIoControl( file.SafeFileHandle, FSCTL_GET_RETRIEVAL_POINTERS, &vcnIn, STARTING_VCN_INPUT_BUFFER.Size, &rpb, RETRIEVAL_POINTERS_BUFFER.Size, &bytesReturned, null ); err = Marshal.GetLastWin32Error(); switch (err) { case 38: // ERROR_HANDLE_EOF if (extentNumber == 1) { textBox1.Text += "MFT Table..."; } else { textBox1.Text += $"\r\nTotal clusters: {totalClusters} \r\nFragments: {(extentNumber - 1)}"; } break; case 0: // NO_ERROR textBox1.Text += $"\r\nFragment #{extentNumber}"; textBox1.Text += $"\r\n\tStart cluster: {rpb.Lcn}\r\n\tLength: {rpb.NextVcn - rpb.StartingVcn} clusters"; totalClusters += rpb.NextVcn - rpb.StartingVcn; textBox1.Text += $"\r\nTotal clusters: {totalClusters}\r\nFragments:{extentNumber}"; break; case 234: // ERROR_MORE_DATA textBox1.Text += $"\r\nFragment #{extentNumber++}"; textBox1.Text += $"\r\n\tStart cluster: {rpb.Lcn}\r\n\tLength: {rpb.NextVcn - rpb.StartingVcn} clusters"; totalClusters += rpb.NextVcn - rpb.StartingVcn; vcnIn.StartingVcn = rpb.NextVcn; break; } } while (err == 234); } if (extentNumber - 1 == 0) { textBox1.Text += $"\r\nFile fragmentation factor = 0%"; label4.Text += $"0%"; } else { double coeffragment = (double)(extentNumber - 1) / totalClusters * 100; textBox1.Text += $"\r\nFile fragmentation factor = {coeffragment:0.####} %"; label4.Text += $"{coeffragment:0.######}%"; } }