how to call FSCTL_GET_NTFS_VOLUME_DATA IOCTL from c#

spinlock 1 Reputation point
2024-06-24T17:05:04.55+00:00

In a C# program, I need to get details, like MFT size, etc, on NTFS partitions.

I found a link() to an article that alleges to do this, BUT unfortunately MS redirects it to "Ask a Question".I appreciate any good words, especially sample code, tohat can enlighten me.

thanks!

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,534 questions
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. Michael Taylor 50,426 Reputation points
    2024-06-24T19:42:52.7066667+00:00

    You can call DeviceIoControl via PInvoke. The syntax is shown here. Of course for those types of calls to work you need to map the structure that is being returned which the site can help with as well.

    Of course there are a couple of Nuget packages people have put together over the years to do some of this already so maybe you can just goggle Nuget.org to see if there is already a library that provides that information.

    0 comments No comments

  2. RLWA32 42,181 Reputation points
    2024-06-24T19:46:43.99+00:00

    Following example code must be run with Administrator privileges to successfully open a volume handle. The example opens the volume F: but you should replace that string with your own volume letter.

    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace CSNTFSVolumeTest
    {
        internal class Program
        {
            const uint GENERIC_READ = 0x80000000;
            const uint FILE_SHARE_READ = 0x00000001;
            const uint FILE_SHARE_WRITE = 0x00000002;
            const uint OPEN_EXISTING = 3;
            const uint FSCTL_NTFS_GET_VOLUME_DATA = 589924;
            static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
    
            [StructLayout(LayoutKind.Explicit)]
            public struct LARGE_INTEGER
            {
                [FieldOffset(0)]
                public uint LowPart;
                [FieldOffset(4)]
                public int HighPart;
                [FieldOffset(0)]
                public long QuadPart;
            };
    
            [StructLayout(LayoutKind.Sequential)]
            public struct NTFS_VOLUME_DATA_BUFFER
            {
                public LARGE_INTEGER VolumeSerialNumber;
                public LARGE_INTEGER NumberSectors;
                public LARGE_INTEGER TotalClusters;
                public LARGE_INTEGER FreeClusters;
                public LARGE_INTEGER TotalReserved;
                public uint BytesPerSector;
                public uint BytesPerCluster;
                public uint BytesPerFileRecordSegment;
                public uint ClustersPerFileRecordSegment;
                public LARGE_INTEGER MftValidDataLength;
                public LARGE_INTEGER MftStartLcn;
                public LARGE_INTEGER Mft2StartLcn;
                public LARGE_INTEGER MftZoneStart;
                public LARGE_INTEGER MftZoneEnd;
            };
    
    
            [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
            static extern IntPtr CreateFile(string lpFileName, uint dwDesiredaccess, uint dwShareMode,
                IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
            [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
            static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize,
                ref NTFS_VOLUME_DATA_BUFFER lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
    
            [DllImport("kernel32.dll", ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
            static extern bool CloseHandle(IntPtr handle);
    
            static void Main(string[] args)
            {
                IntPtr hVolume = CreateFile(@"\\.\F:", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
                if (hVolume != INVALID_HANDLE_VALUE)
                {
                    NTFS_VOLUME_DATA_BUFFER nvdb = new NTFS_VOLUME_DATA_BUFFER();
                    uint BytesReturned;
                    if (DeviceIoControl(hVolume, FSCTL_NTFS_GET_VOLUME_DATA, IntPtr.Zero, 0, ref nvdb, (uint)Marshal.SizeOf(nvdb), out BytesReturned, IntPtr.Zero))
                    {
                        Console.WriteLine($"\"MftZoneStart: {nvdb.MftZoneStart.QuadPart}, MftZoneEnd: {nvdb.MftZoneEnd.QuadPart}," +
                            $" MftValidDataLength: {nvdb.MftValidDataLength.QuadPart}, BytesPerCluster: {nvdb.BytesPerCluster}, TotalCluseters: {nvdb.TotalClusters.QuadPart}");
                    }
                    else
                        Console.WriteLine($"DeviceIoControl error was {Marshal.GetLastWin32Error()}");
    
                    CloseHandle(hVolume);
                }
                else
                    Console.WriteLine($"CreateFile error was {Marshal.GetLastWin32Error()}");
            }
        }
    }
    
    0 comments No comments

  3. Jiale Xue - MSFT 41,736 Reputation points Microsoft Vendor
    2024-06-25T09:39:11.7233333+00:00

    Hi @spinlock , Welcome to Microsoft Q&A,

    To call the FSCTL_GET_NTFS_VOLUME_DATA IOCTL from C# and retrieve NTFS volume data such as the Master File Table (MFT) size, you need to use P/Invoke to call native Windows API functions.

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using Microsoft.Win32.SafeHandles;
    
    class Program
    {
        // Define the control code
        private const uint FSCTL_GET_NTFS_VOLUME_DATA = 0x00090064;
    
        // Define the NTFS_VOLUME_DATA_BUFFER structure
        [StructLayout(LayoutKind.Sequential)]
        private struct NTFS_VOLUME_DATA_BUFFER
        {
            public ulong VolumeSerialNumber;
            public ulong NumberSectors;
            public ulong TotalClusters;
            public ulong FreeClusters;
            public ulong TotalReserved;
            public uint BytesPerSector;
            public uint BytesPerCluster;
            public uint BytesPerFileRecordSegment;
            public uint ClustersPerFileRecordSegment;
            public ulong MftValidDataLength;
            public ulong MftStartLcn;
            public ulong Mft2StartLcn;
            public ulong MftZoneStart;
            public ulong MftZoneEnd;
        }
    
        // Define the DeviceIoControl function
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool DeviceIoControl(
            SafeFileHandle hDevice,
            uint dwIoControlCode,
            IntPtr lpInBuffer,
            uint nInBufferSize,
            IntPtr lpOutBuffer,
            uint nOutBufferSize,
            out uint lpBytesReturned,
            IntPtr lpOverlapped);
    
        // Define the CreateFile function
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern SafeFileHandle CreateFile(
            string lpFileName,
            uint dwDesiredAccess,
            uint dwShareMode,
            IntPtr lpSecurityAttributes,
            uint dwCreationDisposition,
            uint dwFlagsAndAttributes,
            IntPtr hTemplateFile);
    
        private const uint GENERIC_READ = 0x80000000;
        private const uint OPEN_EXISTING = 3;
        private const uint FILE_SHARE_READ = 0x00000001;
        private const uint FILE_SHARE_WRITE = 0x00000002;
    
        static void Main(string[] args)
        {
            string drivePath = @"\\.\C:"; // Path to the drive
    
            // Open the volume
            SafeFileHandle hVolume = CreateFile(drivePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
                                                IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
    
            if (hVolume.IsInvalid)
            {
                Console.WriteLine("Failed to open volume.");
                return;
            }
    
            try
            {
                uint bytesReturned;
                NTFS_VOLUME_DATA_BUFFER volumeData = new NTFS_VOLUME_DATA_BUFFER();
                int bufferSize = Marshal.SizeOf(volumeData);
                IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
                Marshal.StructureToPtr(volumeData, buffer, false);
    
                // Call DeviceIoControl
                bool result = DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, IntPtr.Zero, 0, buffer, (uint)bufferSize, out bytesReturned, IntPtr.Zero);
    
                if (result)
                {
                    volumeData = (NTFS_VOLUME_DATA_BUFFER)Marshal.PtrToStructure(buffer, typeof(NTFS_VOLUME_DATA_BUFFER));
                    Console.WriteLine("MFT Valid Data Length: " + volumeData.MftValidDataLength);
                    Console.WriteLine("MFT Start LCN: " + volumeData.MftStartLcn);
                    Console.WriteLine("MFT2 Start LCN: " + volumeData.Mft2StartLcn);
                    Console.WriteLine("MFT Zone Start: " + volumeData.MftZoneStart);
                    Console.WriteLine("MFT Zone End: " + volumeData.MftZoneEnd);
                    // Add more fields if needed
                }
                else
                {
                    Console.WriteLine("DeviceIoControl failed.");
                }
    
                Marshal.FreeHGlobal(buffer);
            }
            finally
            {
                hVolume.Close();
            }
        }
    }
    

    Best Regards,

    Jiale


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". 

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments