Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Physical disk size not correct (IoCtlDiskGetDriveGeometry)

I use the code below to get the physical disk size, but the size returned is not correct. I've checked the size with other tools.

The code below reports

Total disk space: 8.249.955.840 bytes

and it should be

Total disk space: 8.254.390.272 bytes

How can I retrieve the actual/correct physical disk size? Tested on USB drives and normal hard drives. The code is long, here separate it in parts to show.

The structure:

[StructLayout(LayoutKind.Sequential)]
internal struct DiskGeometry {
    public long Cylinders;
    public int MediaType;
    public int TracksPerCylinder;
    public int SectorsPerTrack;
    public int BytesPerSector;
}

Native methods:

internal static class NativeMethods {
    [DllImport("Kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern SafeFileHandle CreateFile(
        string fileName,
        uint fileAccess,
        uint fileShare,
        IntPtr securityAttributes,
        uint creationDisposition,
        uint flags,
        IntPtr template
        );

    [DllImport("Kernel32.dll", SetLastError=false, CharSet=CharSet.Auto)]
    public static extern int DeviceIoControl(
        SafeFileHandle device,
        uint controlCode,
        IntPtr inBuffer,
        uint inBufferSize,
        IntPtr outBuffer,
        uint outBufferSize,
        ref uint bytesReturned,
        IntPtr overlapped
        );

    internal const uint FileAccessGenericRead=0x80000000;
    internal const uint FileShareWrite=0x2;
    internal const uint FileShareRead=0x1;
    internal const uint CreationDispositionOpenExisting=0x3;
    internal const uint IoCtlDiskGetDriveGeometry=0x70000;
}

Main entry:

internal const uint IoCtlDiskGetDriveGeometry=0x70000;

public static void Main() {
    SafeFileHandle diskHandle=
        NativeMethods.CreateFile(
            @"\\.\PhysicalDrive0",
            NativeMethods.FileAccessGenericRead,
            NativeMethods.FileShareWrite|NativeMethods.FileShareRead,
            IntPtr.Zero,
            NativeMethods.CreationDispositionOpenExisting,
            0,
            IntPtr.Zero
            );

    if(diskHandle.IsInvalid) {
        Console.WriteLine("CreateFile failed with error: {0}", Marshal.GetLastWin32Error());
        return;
    }

    int geometrySize=Marshal.SizeOf(typeof(DiskGeometry));
    Console.WriteLine("geometry size = {0}", geometrySize);

    IntPtr geometryBlob=Marshal.AllocHGlobal(geometrySize);
    uint numBytesRead=0;

    if(
        0==NativeMethods.DeviceIoControl(
            diskHandle,
            NativeMethods.IoCtlDiskGetDriveGeometry,
            IntPtr.Zero,
            0,
            geometryBlob,
            (uint)geometrySize,
            ref numBytesRead,
            IntPtr.Zero
            )
        ) {
        Console.WriteLine(
            "DeviceIoControl failed with error: {0}",
            Marshal.GetLastWin32Error()
            );

        return;
    }

    Console.WriteLine("Bytes read = {0}", numBytesRead);

    DiskGeometry geometry=(DiskGeometry)Marshal.PtrToStructure(geometryBlob, typeof(DiskGeometry));
    Marshal.FreeHGlobal(geometryBlob);

    long bytesPerCylinder=(long)geometry.TracksPerCylinder*(long)geometry.SectorsPerTrack*(long)geometry.BytesPerSector;
    long totalSize=geometry.Cylinders*bytesPerCylinder;

    Console.WriteLine("Media Type:           {0}", geometry.MediaType);
    Console.WriteLine("Cylinders:            {0}", geometry.Cylinders);
    Console.WriteLine("Tracks per Cylinder:  {0}", geometry.TracksPerCylinder);
    Console.WriteLine("Sectors per Track:    {0}", geometry.SectorsPerTrack);
    Console.WriteLine("Bytes per Sector:     {0}", geometry.BytesPerSector);
    Console.WriteLine("Bytes per Cylinder:   {0}", bytesPerCylinder);
    Console.WriteLine("Total disk space:     {0}", totalSize);
}
like image 623
John Doe Avatar asked Feb 24 '13 12:02

John Doe


1 Answers

Your code calculates it in a wrong way. About the description of physical to logical sector number calculation, take a look of the artical on Wikipedia

  • Logical block addressing

and following is an online bidirectional conversion script

  • CHS/LBA Conversion

According to your post, the physical last sector would be at

chs(1003, 137, 30) = ((1003 * 255) + 137) * 63 + 30 - 1 = lba(16121855)

And the size would be

total sectors = 1+16121855 = 16121856 sectors

16121856 * 512 bytes per sector = 8254390272 bytes

Since you specify that it should be 8,254,390,272, I calculate the last physical sector according to that size.

255*63 is only for alignment, it's called cylinder boundary. Usually, the physical last sector is NOT end at the boundary, but for the reason not to access nonexistence sectors, it should be larger than

[total cylinders] * [tracks per cylinder(also heads)] * [sectors per track]

For example, if your physical last sector was as the calculated value above, then simply ignore the cylinder next to 1002, and use the sectors max to chs(1002, 255, 63) as your logical last sector would be safe.

To get the physical disk size, you can invoke DeviceIoControl with the control code IOCTL_DISK_GET_DRIVE_GEOMETRY_EX. Here is the reference on MSDN

  • IOCTL_DISK_GET_DRIVE_GEOMETRY_EX control code (Windows)
like image 178
Ken Kin Avatar answered Nov 14 '22 08:11

Ken Kin