2.1.5.10.5 FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX
The server provides:
Open: An Open of a DataStream.
InputBuffer: An array of bytes containing a single SMB2_DUPLICATE_EXTENTS_DATA_EX structure indicating the source stream, and source and target regions to copy, as specified in [MS-FSCC] section 2.3.9.2.
InputBufferSize: The number of bytes in InputBuffer.
On completion, the object store MUST return:
Status: An NTSTATUS code that specifies the result.
This routine uses the following local variables:
Open: SourceOpen
Stream: Source
64-bit signed integers: ClusterCount, ClusterNum, SourceVcn, TargetVcn, SourceLcn, TargetLcn
EXTENTS: NewPreviousExtent, NewNextExtent
The purpose of this operation is to make it look like a copy of a region from the source stream to the target stream has occurred when in reality no data is actually copied. This operation modifies the target stream's extent list such that, the same clusters are pointed to by both the source and target streams' extent lists for the region being copied.
When the DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC flag in the SMB2_DUPLICATE_EXTENTS_DATA_EX structure isn’t set, the behavior of operation is identical to FSCTL_DUPLICATE_EXTENTS_TO_FILE. When the flag is set, the operation is source stream atomic. The source stream duplication fully succeeds or it fails without any side effects (when only part of source stream file region is duplicated).
Support for FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX is optional. If the object store does not implement this functionality, the operation MUST be failed with STATUS_INVALID_DEVICE_REQUEST.<93>
Pseudocode for the operation is as follows:
If InputBufferSize is less than 0x30, the operation MUST be failed with STATUS_BUFFER_TOO_SMALL
If InputBuffer.StructureSize is not equal to 0x30, the operation MUST be failed with STATUS_NOT_SUPPORTED.
If Open.File.Volume.IsReadOnly is TRUE, the operation MUST be failed with STATUS_MEDIA_WRITE_PROTECTED.
If InputBuffer.SourceFileOffset is not a multiple of Open.File.Volume.ClusterSize, the operation MUST be failed with STATUS_INVALID_PARAMETER.
If InputBuffer.TargetFileOffset is not a multiple of Open.File.Volume.ClusterSize, the operation MUST be failed with STATUS_INVALID_PARAMETER.
If InputBuffer.ByteCount is not a multiple of Open.File.Volume.ClusterSize, the operation MUST be failed with STATUS_INVALID_PARAMETER.
If InputBuffer.ByteCount is equal to 0, the operation SHOULD return immediately with STATUS_SUCCESS.
If Open.Stream.StreamType != DataStream, the operation MUST be failed with STATUS_NOT_SUPPORTED.
Set SourceOpen to the Open object returned from a successful open of the file identified by InputBuffer.SourceFileID. If the open of the InputBuffer.SourceFileID fails, return the status of the operation.
Set Source to SourceOpen.Stream
If SourceOpen does not represent an open Handle to a DataStream with FILE_READ_DATA | FILE_READ_ATTRIBUTES level access, the operation SHOULD<94> fail with STATUS_INVALID_PARAMETER.
If Source.Size is less than InputBuffer.SourceFileOffset + InputBuffer.ByteCount, the operation MUST be failed with STATUS_NOT_SUPPORTED.
If Source.Volume != Open.File.Volume, the operation MUST be failed with STATUS_INVALID_PARAMETER.
If Source.IsSparse != Open.Stream.IsSparse and Source.IsSparse is TRUE, the operation MUST be failed with STATUS_NOT_SUPPORTED.
The object store SHOULD<95> check for byte range lock conflicts on Open.Stream using the algorithm described in section 2.1.4.10 with ByteOffset set to InputBuffer.TargetFileOffset, Length set to InputBuffer.ByteCount, IsExclusive set to TRUE, LockIntent set to FALSE, and Open set to SourceOpen. If a conflict is detected, the operation MUST be failed with STATUS_FILE_LOCK_CONFLICT.
The object store SHOULD<96> check for byte range lock conflicts on Source using the algorithm described in section 2.1.4.10 with ByteOffset set to InputBuffer.SourceFileOffset, Length set to InputBuffer.ByteCount, IsExclusive set to FALSE, LockIntent set to FALSE, and Open set to SourceOpen. If a conflict is detected, the operation MUST be failed with STATUS_FILE_LOCK_CONFLICT.
The object store MUST modify Open.Stream.ExtentList so that all LCNs in the applicable VCN range match the LCNs in Source.ExtentList in the same VCN range, taking care to adjust the Open.File.Volume.ClusterRefcount array accordingly. Pseudo-code for this is as follows:
ClusterCount = InputBuffer.ByteCount / Open.File.Volume.ClusterSize
For each ClusterNum from 0 to (ClusterCount – 1):
SourceVcn = (InputBuffer.SourceFileOffset / Open.File.Volume.ClusterSize) + ClusterNum
TargetVcn = (InputBuffer.TargetFileOffset / Open.File.Volume.ClusterSize) + ClusterNum
Find the index SourceIndex of the element in Source.ExtentList such that (Source.ExtentList[SourceIndex].NextVcn > SourceVcn) and (SourceIndex == 0 or Source. ExtentList[SourceIndex-1].NextVcn <= SourceVcn).
Find the index TargetIndex of the element in Open.Stream.ExtentList such that (Open.Stream.ExtentList[TargetIndex].NextVcn > TargetVcn) and (TargetIndex == 0 or Open.Stream.ExtentList[TargetIndex-1].NextVcn <= TargetVcn).
// The purpose of this next section is to determine the SourceLcn based on Source. ExtentList[SourceIndex] and SourceVcn.
If Source.ExtentList[SourceIndex].Lcn == 0xffffffffffffffff (indicating an unallocated extent as specified in [MS-FSCC] section 2.3.32.1):
SourceLcn = 0xffffffffffffffff
Else if SourceIndex == 0:
SourceLcn = Source.ExtentList[SourceIndex].Lcn + SourceVcn
Else
SourceLcn = Source.ExtentList[SourceIndex].Lcn + (SourceVcn - Source. ExtentList[SourceIndex-1].NextVcn)
EndIf
// The purpose of this next section is to determine the TargetLcn based on Open.Stream.ExtentList[TargetIndex] and TargetVcn.
If Open.Stream.ExtentList[TargetIndex].Lcn == 0xffffffffffffffff:
TargetLcn = 0xffffffffffffffff
Else if TargetIndex == 0:
TargetLcn = Open.Stream.ExtentList[TargetIndex].Lcn + TargetVcn
Else
TargetLcn = Open.Stream.ExtentList[TargetIndex].Lcn + (TargetVcn - Open.Stream.ExtentList[TargetIndex-1].NextVcn)
EndIf
If TargetLcn != SourceLcn:
If SourceLcn != 0xffffffffffffffff, the object store MUST increment Open.File.Volume.ClusterRefcount[SourceLcn].
If TargetLcn != 0xffffffffffffffff, the object store MUST decrement Open.File.Volume.ClusterRefcount[TargetLcn]. If Open.File.Volume.ClusterRefcount[TargetLcn] goes to zero the cluster MUST be freed.
// The purpose of this next section is to determine what new EXTENTS structures need to be added to the streams ExtentList.
If (TargetIndex == 0 and TargetVcn != 0) or (TargetIndex != 0 and TargetVcn != Open.Stream.ExtentList[TargetIndex-1].NextVcn), the object store MUST initialize a new EXTENTS element NewPreviousExtent as follows:
NewPreviousExtent.NextVcn set to TargetVcn
NewPreviousExtent.Lcn set to Open.Stream.ExtentList[TargetIndex].Lcn
Else
Set NewPreviousExtent to NULL
EndIf
If (TargetVcn != Open.Stream.ExtentList[TargetIndex].NextVcn - 1), the object store MUST initialize a new EXTENTS element NewNextExtent as follows:
NewNextExtent.NextVcn set to Open.Stream.ExtentList[TargetIndex].NextVcn
NewNextExtent.Lcn set to TargetLcn + 1 if TargetLcn != 0xffffffffffffffff, otherwise set to 0xffffffffffffffff
Else
Set NewNextExtent to NULL
EndIf
The object store MUST modify Open.Stream.ExtentList[TargetIndex] as follows:
Set Open.Stream.ExtentList[TargetIndex].NextVcn to TargetVcn + 1
Set Open.Stream.ExtentList[TargetIndex].Lcn to SourceLcn
If NewPreviousExtent != NULL, the object store MUST insert NewPreviousExtent into Open.Stream.ExtentList, coalescing with any adjacent EXTENTS elements that are contiguous with respect to LCN.
If NewNextExtent != NULL, the object store MUST insert NewNextExtent into Open.Stream.ExtentList, coalescing with any adjacent EXTENTS elements that are contiguous with respect to LCN.
EndIf
When any operation failed and DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC is set then undo all operations on Target and set ClusterNum to 0.
EndFor
Upon successful completion of the operation, the object store MUST return:
Status set to STATUS_SUCCESS.