Created
September 4, 2018 21:53
-
-
Save mjs3339/414872bf3a61c7fdbe823a13066b7fdd to your computer and use it in GitHub Desktop.
C# FileFragments Class Get a Files Physical Fragment Information
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class FileFragments | |
{ | |
private const int FSCTL_GET_RETRIEVAL_POINTERS = 0x00090073; | |
private const uint FileAccess_GenericRead = 0x80000000; | |
private const uint FileAccess_GenericWrite = 0x40000000; | |
private const uint FileShare_Read = 0x00000001; | |
private const uint FileShare_Write = 0x00000002; | |
private const uint FileShare_ReadWrite = FileShare_Read | FileShare_Write; | |
private const uint FileMode_OpenExisting = 0x00000003; | |
private const uint FileAttributes_Normal = 0x00000080; | |
private const int ERROR_HANDLE_EOF = 0x00000026; | |
private const int ERROR_MORE_DATA = 0x000000EA; | |
private const int NO_ERROR = 0x00000000; | |
private static readonly int BytesReturned = 0x00000000; | |
private static long SectorsPerCluster; | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] | |
private static extern SafeFileHandle CreateFile( | |
string lpFileName, | |
[MarshalAs(UnmanagedType.U4)] uint dwDesiredAccess, | |
[MarshalAs(UnmanagedType.U4)] uint dwShareMode, | |
IntPtr lpSecurityAttributes, | |
[MarshalAs(UnmanagedType.U4)] uint dwCreationDisposition, | |
[MarshalAs(UnmanagedType.U4)] uint dwFlagsAndAttributes, | |
IntPtr hTemplateFile); | |
private static SafeFileHandle GetSafeHandle(string path) | |
{ | |
return CreateFile(path, FileAccess_GenericRead, FileShare_ReadWrite, IntPtr.Zero, | |
FileMode_OpenExisting, FileAttributes_Normal, IntPtr.Zero); | |
} | |
[DllImport("kernel32.dll", SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool DeviceIoControl( | |
SafeFileHandle hFile, | |
uint ioctl, | |
ref StartingVcnInputBuffer invalue, | |
int InSize, | |
out RetrievalPointersBuffer outvalue, | |
int OutSize, | |
int BytesReturned, | |
IntPtr zerovalue | |
); | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] | |
private static extern bool GetDiskFreeSpace( | |
string lpRootPathName, | |
out long lpSectorsPerCluster, | |
out long lpBytesPerSector, | |
out long lpNumberOfFreeClusters, | |
out long lpTotalNumberOfClusters); | |
[SecurityCritical] | |
public static long GetSectorsPerCluster(string FilePath) | |
{ | |
var info = new FileInfo(FilePath); | |
long dummy, sectorsPerCluster, bytesPerSector; | |
var result = GetDiskFreeSpace(info.Directory.Root.FullName, out sectorsPerCluster, out bytesPerSector, out dummy, out dummy); | |
return sectorsPerCluster; | |
} | |
[SecurityCritical] | |
public static long GetBytesPerCluster(string FilePath) | |
{ | |
var info = new FileInfo(FilePath); | |
long dummy, sectorsPerCluster, bytesPerSector; | |
var result = GetDiskFreeSpace(info.Directory.Root.FullName, out sectorsPerCluster, out bytesPerSector, out dummy, out dummy); | |
return sectorsPerCluster * bytesPerSector; | |
} | |
[SecurityCritical] | |
public static long GetBytesPerSector(string FilePath) | |
{ | |
var info = new FileInfo(FilePath); | |
long dummy, sectorsPerCluster, bytesPerSector; | |
var result = GetDiskFreeSpace(info.Directory.Root.FullName, out sectorsPerCluster, out bytesPerSector, out dummy, out dummy); | |
return bytesPerSector; | |
} | |
public static FileFragment GetFileAllocation(string path) | |
{ | |
var VcnBuffer = new StartingVcnInputBuffer(); | |
var rpBuffer = new RetrievalPointersBuffer(); | |
var ThisFilePointer = new FileFragment(); | |
var FileFragment = new PhysicalFileFragment(); | |
var BytePerSector = GetBytesPerSector(path); | |
var MoreDataAvailable = false; | |
var extentNumber = 1; | |
SectorsPerCluster = GetSectorsPerCluster(path); | |
VcnBuffer.StartingVcn = 0L; | |
ThisFilePointer.TotalClusters = 0; | |
ThisFilePointer.TotalSectors = 0; | |
ThisFilePointer.FileAllocation = new List<PhysicalFileFragment>(); | |
var errorLevel =0; | |
using(var handle = GetSafeHandle(path)) | |
{ | |
do | |
{ | |
DeviceIoControl( | |
handle, FSCTL_GET_RETRIEVAL_POINTERS, | |
ref VcnBuffer, StartingVcnInputBuffer.Size, | |
out rpBuffer, RetrievalPointersBuffer.Size, | |
BytesReturned, IntPtr.Zero | |
); | |
errorLevel = Marshal.GetLastWin32Error(); | |
switch(errorLevel) | |
{ | |
case ERROR_HANDLE_EOF: | |
break; | |
case NO_ERROR: | |
case ERROR_MORE_DATA: | |
MoreDataAvailable = true; | |
VcnBuffer.StartingVcn = rpBuffer.NextVcn; | |
ThisFilePointer.TotalClusters += rpBuffer.NextVcn - rpBuffer.StartingVcn; | |
ThisFilePointer.TotalSectors = ThisFilePointer.TotalClusters * (uint) SectorsPerCluster; | |
ThisFilePointer.Length = ThisFilePointer.TotalSectors * BytePerSector; | |
break; | |
default: | |
throw new Win32Exception(errorLevel); | |
} | |
if(MoreDataAvailable && rpBuffer.Lcn >= 0) | |
{ | |
FileFragment.FragmentNumber = extentNumber; | |
FileFragment.StartCluster = rpBuffer.Lcn; | |
FileFragment.StartingSector = FileFragment.StartCluster * SectorsPerCluster; | |
FileFragment.NumberOfClusters = rpBuffer.NextVcn - rpBuffer.StartingVcn; | |
FileFragment.NumberOfSectors = FileFragment.NumberOfClusters * SectorsPerCluster; | |
ThisFilePointer.FileAllocation.Add(FileFragment); | |
extentNumber++; | |
MoreDataAvailable = false; | |
} | |
} while(errorLevel != ERROR_HANDLE_EOF); | |
ThisFilePointer.TotalFragments = (uint) ThisFilePointer.FileAllocation.Count; | |
} | |
return ThisFilePointer; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
private struct StartingVcnInputBuffer | |
{ | |
public static readonly int Size; | |
static StartingVcnInputBuffer() | |
{ | |
Size = Marshal.SizeOf(typeof(StartingVcnInputBuffer)); | |
} | |
public long StartingVcn; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
private struct RetrievalPointersBuffer | |
{ | |
public static readonly int Size; | |
static RetrievalPointersBuffer() | |
{ | |
Size = Marshal.SizeOf(typeof(RetrievalPointersBuffer)); | |
} | |
public readonly int ExtentCount; | |
public readonly long StartingVcn; | |
public readonly long NextVcn; | |
public readonly long Lcn; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct PhysicalFileFragment | |
{ | |
public long FragmentNumber; | |
public long StartCluster; | |
public long StartingSector; | |
public long NumberOfClusters; | |
public long NumberOfSectors; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct FileFragment | |
{ | |
public long TotalClusters; | |
public long TotalSectors; | |
public long TotalFragments; | |
public List<PhysicalFileFragment> FileAllocation; | |
public long Length; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment