2.1.5.10.4 FSCTL_DUPLICATE_EXTENTS_TO_FILE
The server provides:
Open: An Open of a DataStream.
InputBuffer: An array of bytes containing a single SMB2_DUPLICATE_EXTENTS_DATA structure indicating the source stream, and source and target regions to copy, as specified in [MS-FSCC] section 2.3.7.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.
Support for FSCTL_DUPLICATE_EXTENTS_TO_FILE is optional. If the object store does not implement this functionality, the operation MUST be failed with STATUS_INVALID_DEVICE_REQUEST.<89>
Pseudocode for the operation is as follows:
If InputBufferSizes is less than sizeof(DUPLICATE_EXTENTS_DATA), the operation MUST be failed with STATUS_BUFFER_TOO_SMALL
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<90> 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<91> 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 Open. If a conflict is detected, the operation MUST be failed with STATUS_FILE_LOCK_CONFLICT.
The object store SHOULD<92> 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
EndFor
Upon successful completion of the operation, the object store MUST return:
Status set to STATUS_SUCCESS.