2.1.5.9.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.<74>

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<75> 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<76> 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<77> 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.