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.