2.1.5.10.22 FSCTL_QUERY_ALLOCATED_RANGES

The server provides:

  • Open: An Open of a DataFile.

  • InputBuffer: An array of bytes containing a single FILE_ALLOCATED_RANGE_BUFFER structure indicating the range to query for allocation, as specified in [MS-FSCC] section 2.3.52.

  • InputBufferSize: The number of bytes in InputBuffer.

  • OutputBufferSize: The maximum number of bytes to return in OutputBuffer.

On completion, the object store MUST return:

  • Status: An NTSTATUS code that specifies the result.

  • OutputBuffer: An array of bytes that will return an array of zero or more FILE_ALLOCATED_RANGE_BUFFER structures as specified in [MS-FSCC] section 2.3.52.

  • BytesReturned: The number of bytes returned in OutputBuffer.

This operation uses the following local variables:

  • 32-bit unsigned integer indicating the index of the next FILE_ALLOCATED_RANGE_BUFFER to fill in OutputBuffer (initialized to 0): OutputBufferIndex.

  • 64-bit unsigned integer QueryStart: Is initialized to ClustersFromBytesTruncate(Open.File.Volume, InputBuffer.FileOffset). This is the cluster containing the first byte of the queried range.

  • 64-bit unsigned integer QueryNext: Is initialized to ClustersFromBytesTruncate(Open.File.Volume, (InputBuffer.FileOffset + InputBuffer.Length - 1) ) + 1. This is the cluster following the last cluster of the range.

  • 64-bit unsigned integers (initialized to 0): ExtentFirstVcn, ExtentNextVcn, RangeFirstVcn, RangeNextVcn

  • Boolean values (initialized to FALSE): FoundRangeStart, FoundRangeEnd

  • Pointer to an EXTENTS element (initialized to NULL): Extent

  • FILE_ALLOCATED_RANGE_BUFFER (initialized to zeros): Range

Support for this operation is optional. If the object store does not implement this functionality, the operation MUST be failed with STATUS_INVALID_DEVICE_REQUEST.<114>

Pseudocode for the operation is as follows:

  • If Open.Stream.StreamType is DirectoryStream, the operation MUST be failed with STATUS_INVALID_PARAMETER.

  • If InputBufferSize is less than sizeof(FILE_ALLOCATED_RANGE_BUFFER), the operation MUST be failed with STATUS_INVALID_PARAMETER.

  • If (InputBuffer.FileOffset < 0) or (InputBuffer.Length < 0) or (InputBuffer.Length > MAXLONGLONG - InputBuffer.FileOffset), the operation MUST be failed with STATUS_INVALID_PARAMETER. If InputBuffer.Length is 0:

    • Set BytesReturned to 0.

    • Return STATUS_SUCCESS.

  • EndIf

  • If OutputBufferSize < sizeof(FILE_ALLOCATED_RANGE_BUFFER), the operation MUST be failed with STATUS_BUFFER_TOO_SMALL.

  • If Open.Stream.IsSparse is FALSE:

    • Set OutputBuffer.FileOffset to InputBuffer.FileOffset.

    • Set OutputBuffer.Length to InputBuffer.Length.

    • Set BytesReturned to sizeof(FILE_ALLOCATED_RANGE_BUFFER).

    • Return STATUS_SUCCESS.

  • Else:

    • For sparse files, return a list of contiguous allocated ranges within the requested range. Contiguous allocated ranges in a sparse file might be fragmented on disk, therefore it is necessary to loop through the EXTENTS on this stream, coalescing the adjacent allocated EXTENTS into a single FILE_ALLOCATED_RANGE_BUFFER entry.

    • Set Status to STATUS_SUCCESS.

    • Set BytesReturned to 0.

    • For each Extent in Open.Stream.ExtentList:

      • Set ExtentFirstVcn to ExtentNextVcn.

      • Set ExtentNextVcn to Extent.NextVcn.

      • If Extent.Lcn != 0xffffffffffffffff, meaning Extent is allocated (not a sparse hole):

        • If FoundRangeStart is FALSE:

          • If QueryStart < ExtentFirstVcn:

            • Set FoundRangeStart to TRUE.

            • Set RangeFirstVcn to ExtentFirstVcn.

          • Else If ExtentFirstVcn <= QueryStart and QueryStart < ExtentNextVcn:

            • Set FoundRangeStart to TRUE.

            • Set RangeFirstVcn to QueryStart.

          • EndIf

        • EndIf

        • If FoundRangeStart is TRUE:

          • If QueryNext <= ExtentFirstVcn:

            • Break out of the For loop.

          • Else If ExtentFirstVcn < QueryNext and QueryNext <= ExtentNextVcn:

            • Set FoundRangeEnd to TRUE.

            • Set RangeNextVcn to QueryNext.

          • Else (ExtentNextVcn < QueryNext):

            • Set FoundRangeEnd to FALSE.

            • Set RangeNextVcn to ExtentNextVcn.

          • EndIf

        • EndIf

      • Else If FoundRangeStart is TRUE:

        • Set FoundRangeEnd to TRUE.

      • EndIf

      • If FoundRangeEnd is TRUE:

        • Set FoundRangeStart to FALSE and FoundRangeEnd to FALSE.

        • Add Range to OutputBuffer as follows:

          • Set Range.FileOffset to RangeFirstVcn * Open.File.Volume.ClusterSize.

          • Set Range.Length to (RangeNextVcn - RangeFirstVcn) * Open.File.Volume.ClusterSize.

          • If OutputBufferSize < ((OutputBufferIndex + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER) ) then:

            • Set RangeFirstVcn to 0 and RangeNextVcn to 0.

            • Set Status to STATUS_BUFFER_OVERFLOW.

            • Break out of the For loop.

          • EndIf

          • Copy Range to OutputBuffer[OutputBufferIndex].

          • Increment OutputBufferIndex by 1.

          • Set RangeFirstVcn to 0 and RangeNextVcn to 0.

      • EndIf

    • EndFor

    • If RangeNextVcn is not 0:

      • If OutputBufferSize < ((OutputBufferIndex + 1) * sizeof(FILE_ALLOCATED_RANGE_BUFFER)) then:

        • Set Status to STATUS_BUFFER_OVERFLOW.

      • Else add Range to OutputBuffer as follows:

        • Set Range.FileOffset to RangeFirstVcn * Open.File.Volume.ClusterSize.

        • Set Range.Length to (RangeNextVcn - RangeFirstVcn) * Open.File.Volume.ClusterSize.

        • Copy Range to OutputBuffer[OutputBufferIndex].

        • Increment OutputBufferIndex by 1.

      • EndIf

    • EndIf

    • Bias the first and the last returned ranges so that they match the offset/length passed in, using the following algorithm:

    • If OutputBufferIndex > 0:

      • If OutputBuffer[0].FileOffset < InputBuffer.FileOffset:

        • Set OutputBuffer[0].Length to OutputBuffer[0].Length - (InputBuffer.FileOffset -OutputBuffer[0].FileOffset).

        • Set OutputBuffer[0].FileOffset to InputBuffer.FileOffset.

      • EndIf

      • If (OutputBuffer[OutputBufferIndex - 1].FileOffset + OutputBuffer[OutputBufferIndex - 1].Length) > (InputBuffer.FileOffset + InputBuffer.Length):

        • Set OutputBuffer[OutputBufferIndex - 1].Length to InputBuffer.FileOffset + InputBuffer.Length - OutputBuffer[OutputBufferIndex - 1].FileOffset.

      • EndIf

    • EndIf

  • Endif

  • Upon successful completion of the operation, the object store MUST return:

    • BytesReturned set to OutputBufferIndex * sizeof(FILE_ALLOCATED_RANGE_BUFFER).

    • Status set to STATUS_SUCCESS.