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.