Using VBScript to Check Partitions for Advanced Format (4K / 512e) Sector Alignment

As you may know Dell recently announced that they will start shipping Advanced Format (4k/512e) drives in their enterprise systems in May 2011.  For those not wanting to follow the links, the cliff’s notes version is that Advanced Format drives use a 4096 byte (4k) sector rather than a 512 byte sector.  This allows higher bit densities by reducing the amount of space tied up in ECC and sector gap.

Old and New Sector Formats Credit: Dougolsen / Wikimedia Commons
Old and New Sector Formats Credit: Dougolsen / Wikimedia Commons

So what’s the big deal then?  Well in order to make the transition, 4K/512e advanced format drives still present themselves externally as a 512 byte per sector drive, and internally lump reads and writes into eight logical 512 byte sectors to match up with their physical 4096 byte sectors.  This has the unfortunate side effect that if your partitions are not aligned to a 4K boundary the disk may have to perform a Read-Modify-Write across two physical sectors.  This process can incur extra latency waiting for an additional rotation of the disk platters which drastically impacts performance.  Further compounding the matter Windows XP/2003 operating systems and most disk management tools create the first partition on the disk starting at LBA sector 63 which if you do your math correctly is not 4K aligned.

Recently the question came up as to whether or not it was possible to use a script to detect partitions that were not 4K aligned and therefore may be causing performance issues, or to perhaps use in an OSD task sequence to halt the OS install if the partition is not correctly aligned.  After some browsing through the WMI classes I found what I was looking for.  The Win32_DiskPartion class has a property StartingOffset that, according to MSDN is in fact the “starting offset (in bytes) of the partition”.  A little modulus division later and you have a simple way to determine if your partition is 4K sector aligned.

Option Explicit

Dim strComputer: strComputer = "."
Dim objWMIService: Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Dim colPartitions: Set colPartitions = objWMIService.ExecQuery("Select * from Win32_DiskPartition",,48)

Dim objPartition
For Each objPartition In colPartitions
    Dim iResult: iResult = objPartition.StartingOffset / 4096
    If iResult = Fix(iResult) Then
        WScript.Echo objPartition.Caption & " is 4K/512e sector aligned."
    Else
        WScript.Echo objPartition.Caption & " is NOT 4K/512e sector aligned."
    End If
Next

There is one caveat I should mention if you’re planning to use this in an OSD task sequence from WinPE.  It seems that the Win32_DiskPartition class has been removed from WinPE 3.x.  Luckily others already have a solution for you.

UPDATE: 5/26/11

Per the request by Eric in the comments I’ve updated the script to show the drive letter associated with the partition. 

Option Explicit

Dim strComputer: strComputer = "."
Dim objWMIService: Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Dim colPartitions: Set colPartitions = objWMIService.ExecQuery("Select * from Win32_DiskPartition",,48)

Dim objPartition

For Each objPartition In colPartitions
    Dim strDriveLetter: strDriveLetter = PartitionToDrive(objWMIService, objPartition.DeviceID)
    Dim iResult: iResult = objPartition.StartingOffset / 4096
    If iResult = Fix(iResult) Then
        WScript.Echo "(" & strDriveLetter & ") " & objPartition.Caption & " is 4K/512e sector aligned."
    Else
        WScript.Echo "(" & strDriveLetter & ") " & objPartition.Caption & " is NOT 4K/512e sector aligned."
    End If
Next

Private Function PartitionToDrive(objWMIService, DeviceID)
    Dim objLogcialDisks: Set objLogcialDisks = objWMIService.ExecQuery( _
    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & DeviceID & _
    "'} WHERE AssocClass = Win32_LogicalDiskToPartition")

    Dim objLogicalDisk
    For Each objLogicalDisk In objLogcialDisks
        PartitionToDrive = objLogicalDisk.DeviceID
        Exit Function
    Next

    PartitionToDrive = ""
End Function
Advertisements

14 Responses to Using VBScript to Check Partitions for Advanced Format (4K / 512e) Sector Alignment

  1. Eric says:

    Thanks for this script.
    It does work, but ends with an Overflow error.

    Could you please:
    1. Fix the Overflow error.
    2. Add partition letter (C, D, E,…)
    3. Add volume name (for example – C: System, D: Data)

    I noticed that Win32_DiskPartition does not contain parameters for drive letter and volume name.
    However, Win32_LogicalDisk does contain those parameters (string Name; string VolumeName).

    • Josh says:

      Eric,

      I’m not seeing an overflow when I run it in my environment, can you provide the exact error you’re seeing?

      Also, the reason for not using Win32_LogicalDisk is that the Win32_LogicalDisk class does not provide information on the starting offset of the partition itself. However it is possible to query WMI for the association between a Win32_DiskPartition and a Win32_LogicalDisk. I’ve provided an update to this post as an example.

  2. Eric says:

    On WinServer 2008 R2 Enterprise, I get:

    Error: Overflow: '[string: "5247934720"]'
    Code: 800A0006
    Source: Microsoft VBScript runtime error

    On WinXP SP3 eng x86, I get:

    Error: Overflow: '[string: "55899586560"]'
    Code: 800A0006
    Source: Microsoft VBScript runtime error

    In both cases, it points to the line with the IF statement.

    • Josh says:

      Ok, I see the issue here. Win32_DiskPartition.StartingOffset is an Int64 and the VBScript Mod operator is only good up to 32bits. I’m guessing you have more than one partition to a disk and that at least one of those partitions starts more than 2^32 bytes into the disk. I wasn’t seeing this because I generally have one partition to a disk which means that the StartingOffset for all of my partitions is generally near the beginning of the disk and hence well below the 32bit limit for the Mod operator. The fix is fairly simple in that the script doesn’t really need the result of the Mod operator, it just needs to know that the result of StartingOffset / 4096 was not fractional. This can be accomplished by changing objPartition.StartingOffset Mod 4096 = 0 to Fix(objPartition.StartingOffset / 4096) = (objPartition.StartingOffset / 4096), which I’ve done above.

  3. Eric says:

    Thanks, Josh.

    The fix worked.
    And you were right – I do have some disks with more than 1 partition.
    I even have 2 drives in RAID0, which are then split into 2 partitions (don’t ask why).

    Anyway, the script works great right now.

    Could you please also add “volume name”?
    You can add a separate line for “volume name” by adding & vbCr & _ .

    Eric

    • Josh says:

      One thing you’ll find is I’m very much a “teach a man to fish” rather than “give a man a fish” kind of guy. To that end, I’ll give you some hints and leave it as an exercise for you.

      Basically you’ve got a couple options, but IMHO the easiest and most “code readable” way would be use the PartitionToDrive function as a template and create a new function (let’s arbitrarily call it PartitionToVolumeLabel). Then you’ll need to change what property of the Win32_LogicalDisk instance (contained in objLogicalDisk in the function) is returned from the new function from “DeviceID” to whatever property contains the Logical Volume name. The documentation for the Win32_LogicalDisk class is here http://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx, but I bet you could guess what the property name is for volume name if you had to 😉

      Then you just need to modify the main part of the script to take advantage of your new function. Again hints would be to duplicate (and modify) line 10, and modify lines 13 and 15.

      If you give it a go and get stuck let me know what you’ve come up with and I’ll be happy to help.

  4. Eric says:

    I came up with this, but I’m doing something wrong:

    Option Explicit

    Dim strComputer: strComputer = "."
    Dim objWMIService: Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
    Dim colPartitions: Set colPartitions = objWMIService.ExecQuery("Select * from Win32_DiskPartition",,48)

    Dim objPartition

    For Each objPartition In colPartitions
    Dim strDriveLetter: strDriveLetter = PartitionToDrive(objWMIService, objPartition.DeviceID)
    Dim strVolumeName: strVolumeName = PartitionToVolumeLabel(objWMIService, objPartition.VolumeName)
    Dim iResult: iResult = objPartition.StartingOffset / 4096
    If iResult = Fix(iResult) Then
    WScript.Echo "(" & strDriveLetter & ") " & objPartition.Caption & " is 4K sector aligned." & vbCr & _
    "Volume Name:" & strVolumeName
    Else
    WScript.Echo "(" & strDriveLetter & ") " & objPartition.Caption & " is NOT 4K sector aligned." & vbCr & _
    "Volume Name:" & strVolumeName
    End If
    Next

    Private Function PartitionToDrive(objWMIService, DeviceID)
    Dim objLogcialDisks: Set objLogcialDisks = objWMIService.ExecQuery( _
    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & DeviceID & _
    "'} WHERE AssocClass = Win32_LogicalDiskToPartition")

    Dim objLogicalDisk
    For Each objLogicalDisk In objLogcialDisks
    PartitionToDrive = objLogicalDisk.DeviceID
    Exit Function
    Next

    PartitionToDrive = ""
    End Function

    Private Function PartitionToVolumeLabel(objWMIService, VolumeName)
    Dim objVolumeNames: Set objVolumeNames = objWMIService.ExecQuery( _
    "ASSOCIATORS OF {Win32_LogicalDisk.VolumeName='" & VolumeName & _
    "'} WHERE AssocClass = Win32_LogicalDiskToPartition")

    Dim objVolumeName
    For Each objVolumeName In objVolumeNames
    PartitionToVolumeLabel = objVolumeName.VolumeName
    Exit Function
    Next

    PartitionToVolumeLabel = ""
    End Function

    • Josh says:

      “A” for effort, however I think you got a little overzealous with the modifications to the new function. The first part would still be like this:

      Private Function PartitionToVolumeLabel(objWMIService, DeviceID)
      Dim objLogcialDisks: Set objLogcialDisks = objWMIService.ExecQuery( _
      “ASSOCIATORS OF {Win32_DiskPartition.DeviceID='” & DeviceID & _
      “‘} WHERE AssocClass = Win32_LogicalDiskToPartition”)

      Dim objLogicalDisk
      For Each objLogicalDisk In objLogcialDisks

      because you really still want to map your Win32_DiskPartiton object to it’s associated Win32_LogicalDisk (you can read up on the ASSOCIATORS OF statement here http://msdn.microsoft.com/en-us/library/aa384793(v=vs.85).aspx). Also, notice that we’re still passing the function the DeviceID of objPartition because objPartion references an instance of Win32_DiskPartion and therefore doesn’t contain a property VolumeName (that’s what we’re doing with the WMI query in the function; mapping the partition to the associated logical volume so we can get the volume name). Which makes this part of the main script:

      Dim strVolumeName: strVolumeName = PartitionToVolumeLabel(objWMIService, objPartition.VolumeName)

      into this:

      Dim strVolumeName: strVolumeName = PartitionToVolumeLabel(objWMIService, objPartition.DeviceID)

      The given changes (and a few variable name tweaks to the latter part of the function if you just copy and paste from this comment) should get you there.

  5. Eric says:

    OK, Josh.

    Thanks for your instructions. I got it working now.

  6. Eric says:

    Josh,
    One more thing: Is it possible to add to this script a check whether the HDD(s) are 4K Advanced Format or not?
    This would save us time and effort – taking out the HDD from the PC/laptop and inspecting its label.

    • Josh says:

      Unfortunately there is no easy way to do this in VBScript. In fact there is really no easy way to do this at all, and to my knowledge no way whatsoever unless you’re running under Windows 7 SP1 or Windows Server 2008 R2, and in either environment have KB982018 installed. Assuming you were in one of these environments you’ve got two options. Option one, forego VBScript, move over to C and go digging around in the DeviceIOControl API, issue a IOCTL_STORAGE_QUERY_PROPERTY query and examine the BytesPerPhysicalSector field in the returned STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR structure, all of which is WAY beyond the scope of this blog. Option two would be to have FSUTIL.exe do the dirty work for you by running “fsutil fsinfo ntfsinfo [driveletter]” capture the output and parse it for the line “Bytes Per Physical Sector :”. Even then, as noted in the KB article, you may still not get your answer because both the drive and the disk controller driver have to support the new request in the underlying IOCTL_STORAGE_QUERY_PROPERTY call and if they don’t you will get back “Not Supported”.

      KB982018 is here: http://support.microsoft.com/kb/982018
      and some more general information from Microsoft on 512e can be found here: http://msdn.microsoft.com/en-us/library/hh182553(v=vs.85).aspx

  7. Eric says:

    And here I thought that adding this function might do the trick:


    Option Explicit

    Dim strComputer: strComputer = "."
    Dim objWMIService: Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
    Dim colPartitions: Set colPartitions = objWMIService.ExecQuery("Select * from Win32_DiskPartition",,48)

    Dim objPartition

    For Each objPartition In colPartitions
    Dim strDriveLetter: strDriveLetter = PartitionToDrive(objWMIService, objPartition.DeviceID)
    Dim strVolumeName: strVolumeName = PartitionToVolumeLabel(objWMIService, objPartition.DeviceID)
    Dim strSectorBytes: strSectorBytes = PartitionToSectorBytes(objWMIService, objPartition.DeviceID)
    Dim iResult: iResult = objPartition.StartingOffset / 4096
    If iResult = Fix(iResult) Then
    WScript.Echo objPartition.Caption & " is 4K sector aligned." & vbCr & _
    "Partition Name: " & strVolumeName & " " & strDriveLetter & vbCr & _
    "Partition Size: " & Int(objPartition.Size /1073741824) & " GB" & vbCr & _
    "Bytes / Sector: " & strSectorBytes & " bytes"
    Else
    WScript.Echo objPartition.Caption & " is NOT 4K sector aligned." & vbCr & _
    "Partition Name: " & strVolumeName & " " & strDriveLetter & vbCr & _
    "Partition Size: " & Int(objPartition.Size /1073741824) & " GB" & vbCr & _
    "Bytes / Sector: " & strSectorBytes & " bytes"
    End If
    Next

    Private Function PartitionToDrive(objWMIService, DeviceID)
    Dim objLogcialDisks: Set objLogcialDisks = objWMIService.ExecQuery( _
    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & DeviceID & _
    "'} WHERE AssocClass = Win32_LogicalDiskToPartition")

    Dim objLogicalDisk
    For Each objLogicalDisk In objLogcialDisks
    PartitionToDrive = objLogicalDisk.DeviceID
    Exit Function
    Next

    PartitionToDrive = ""
    End Function

    Private Function PartitionToVolumeLabel(objWMIService, DeviceID)
    Dim objVolumeNames: Set objVolumeNames = objWMIService.ExecQuery( _
    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & DeviceID & _
    "'} WHERE AssocClass = Win32_LogicalDiskToPartition")

    Dim objVolumeName
    For Each objVolumeName In objVolumeNames
    PartitionToVolumeLabel = objVolumeName.VolumeName
    Exit Function
    Next

    PartitionToVolumeLabel = ""
    End Function


    Private Function PartitionToSectorBytes(objWMIService, DeviceID)
    Dim objSectorBytes: Set objSectorBytes = objWMIService.ExecQuery( _
    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & DeviceID & _
    "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition")

    Dim objSectorByte
    For Each objSectorByte In objSectorBytes
    PartitionToSectorBytes = objSectorByte.BytesPerSector
    Exit Function
    Next

    PartitionToSectorBytes = ""
    End Function

    • Josh says:

      While I commend you on creating the new function unfortunately I beleive it will only give you the logical bytes per sector not the physical bytes per sector. I can’t find any specific documentation on the matter but my testing shows this to be the case. Further, if you examine cimwin32.mof where the Win32_DiskDrive class is defined you can see it is utilizing the DISK_GEOMETRY structure and not the STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR structure that contains the BytesPerPhysicalSector member.

  8. Eric says:

    Yeah, I read the KB on Microsoft’s website.
    I guess this won’t matter soon. All HDDs are going to be Advanced Format, and even then – their days are numbered due to the arrival of Solid State Disks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s