2.1.5.4 Server Requests a Write
The server provides:
Open: The Open of the DataFile to write to.
InputBuffer: An array of bytes to write.
ByteOffset: The absolute byte offset in the stream where data is written. ByteOffset could be negative, which means the write occurs at the end of the stream.
ByteCount: The number of bytes in InputBuffer to write.
Unbuffered: A Boolean value. TRUE indicates that the write is unbuffered (written directly to disk after writing and removing any cached data for this range); otherwise, the value of Open.Mode.FILE_NO_INTERMEDIATE_BUFFERING determines whether the write is unbuffered.
On completion, the object store MUST return:
Status: An NTSTATUS code that specifies the result.
BytesWritten: The number of bytes written.
This operation uses the following local variables:
Boolean values (initialized to FALSE): DoingIoAtEof, IsUnbuffered
Pseudocode for the operation is as follows:
If UnBuffered is TRUE or Open.Mode.FILE_NO_INTERMEDIATE_BUFFERING is TRUE, then set IsUnbuffered to TRUE.
If IsUnbuffered is TRUE and (ByteOffset >= 0), the operation MUST be failed with STATUS_INVALID_PARAMETER under any of the following conditions:
If (ByteOffset % Open.File.Volume.LogicalBytesPerSector) is not zero.
If (ByteCount % Open.File.Volume.LogicalBytesPerSector) is not zero.
If ByteOffset equals -2, then set ByteOffset to Open.CurrentByteOffset.
If Open.File.Volume.IsReadOnly, the operation MUST be failed with STATUS_MEDIA_WRITE_PROTECTED.
If ((ByteOffset + ByteCount) > MAXLONGLONG (0x7fffffffffffffff) and (ByteOffset >= 0), the operation MUST be failed with STATUS_INVALID_PARAMETER.
If ByteCount is zero, the object store MUST return:
BytesWritten set to 0.
Status set to STATUS_SUCCESS.
If ((ByteOffset < 0) and (Open.Stream.Size + ByteCount)) > MAXLONGLONG (0x7fffffffffffffff), the operation MUST fail with STATUS_INVALID_PARAMETER.
If (ByteOffset < 0), set ByteOffset to Open.Stream.Size.
If (ByteOffset + ByteCount) > MAXFILESIZE (0xfffffff0000), the operation MUST be failed with STATUS_INVALID_PARAMETER.
Initialize UsnReason to zero.
If (ByteOffset + ByteCount) > Open.Stream.Size, set UsnReason.USN_REASON_DATA_EXTEND to TRUE.
If ByteOffset < Open.Stream.Size, set UsnReason.USN_REASON_DATA_OVERWRITE to TRUE.
If Open.Stream.Oplock is not empty, the object store MUST check for an oplock break according to the algorithm in section 2.1.4.12, with input values as follows:
Open equal to this operation's Open
Oplock equal to Open.Stream.Oplock
Operation equal to "WRITE"
OpParams empty
Determine if the write is in conflict with an existing byte range lock on Open.Stream using the algorithm described in section 2.1.4.10 (with ByteOffset set to ByteOffset, Length set to ByteCount, IsExclusive set to TRUE, LockIntent set to FALSE and Open set to Open). If the algorithm returns TRUE, the operation MUST be failed with STATUS_FILE_LOCK_CONFLICT.
The object store MUST post a USN change as specified in section 2.1.4.11 with File equal to File, Reason equal to UsnReason, and FileName equal to Open.Link.Name.
If ((ByteOffset + ByteCount) > Open.Stream.ValidDataLength), then set DoingIoAtEof to TRUE.
If ((ByteOffset + ByteCount) > Open.Stream.AllocationSize), the object store MUST increase Open.Stream.AllocationSize to BlockAlign(ByteOffset + ByteCount, Open.File.Volume.ClusterSize). If there is not enough disk space, the operation MUST be failed with STATUS_DISK_FULL.
If IsUnbuffered is TRUE:
The object store MUST write any unwritten cached data for this range of the stream to disk.
The object store MUST remove from the cache any cached data for this range of the stream.
If the object store supports Open.Volume.ClusterRefcount, it MUST check the reference count of each cluster that is being updated by this operation. If any cluster being updated has a reference count other than 1, the object store MUST do the following:
The object store MUST remove the EXTENTS containing the cluster and decrement the reference count of the cluster in Open.Volume.ClusterRefcount.
The Object store MUST allocate free clusters on the volume and insert new EXTENTS in the Open.Stream.ExtentList pointing to the newly allocated cluster.
The object store MUST increment the reference count of the newly allocated cluster in Open.Volume.ClusterRefcount.
If DoingIoAtEof is TRUE, and (Open.Stream.ValidDataLength < ByteOffset) , write zeroes to the location on disk corresponding to the range between Open.Stream.ValidDataLength and ByteOffset in the stream, and then write the first ByteCount bytes of InputBuffer to the location on disk corresponding to the range starting at offset ByteOffset in the stream. If either write to the disk failed, the operation MUST be failed with the corresponding error status.
EndIf
If IsUnbuffered is FALSE, DoingIoAtEof is TRUE, and (Open.Stream.ValidDataLength < ByteOffset), zero out the range between Open.Stream.ValidDataLength and ByteOffset in the cache for this stream and then write the first ByteCount bytes of InputBuffer into the cache for this stream at offset ByteOffset. If there would not be enough disk space to flush the cache, the operation MUST be failed with STATUS_DISK_FULL. If Open.Mode.FILE_WRITE_THROUGH is TRUE, the cache write will also trigger a flush of the cache for that range to the disk.
If Open.Mode.FILE_SYNCHRONOUS_IO_ALERT is TRUE or Open.Mode.FILE_SYNCHRONOUS_IO_NONALERT is TRUE, the object store MUST set Open.CurrentByteOffset to (ByteOffset + ByteCount).
The object store MUST note that the file has been modified as specified in section 2.1.4.17 with Open equal to Open.
Upon successful completion of the operation, the object store MUST set:
Open.Stream.Size to the maximum of Open.Stream.Size or (ByteOffset + ByteCount).
Open.Stream.ValidDataLength to the maximum of Open.Stream.ValidDataLength or (ByteOffset + ByteCount).
BytesWritten to ByteCount.
Status to STATUS_SUCCESS.