public void FsCtl_Offload_Read_File_IsOffloadSupported() { BaseTestSite.Log.Add(LogEntryKind.TestStep, "Test case steps:"); MessageStatus status; //Step 1: Create file BaseTestSite.Log.Add(LogEntryKind.TestStep, "1. Create Data File."); status = this.fsaAdapter.CreateFile(FileType.DataFile); //Step 2: Write file long bytesToWrite = 1024; long bytesWritten = 0; BaseTestSite.Log.Add(LogEntryKind.TestStep, "2. Create file with " + bytesToWrite + " bytes."); status = this.fsaAdapter.WriteFile(0, bytesToWrite, out bytesWritten); //Step 3: FSCTL request with FSCTL_OFFLOAD_READ long bytesReturned; byte[] outputBuffer = new byte[0]; FSCTL_OFFLOAD_READ_INPUT offloadReadInput = new FSCTL_OFFLOAD_READ_INPUT(); offloadReadInput.Size = (uint)TypeMarshal.ToBytes <FSCTL_OFFLOAD_READ_INPUT>(offloadReadInput).Length; FSCTL_OFFLOAD_READ_OUTPUT offloadReadOutput = new FSCTL_OFFLOAD_READ_OUTPUT(); //TokenId is static size of 504, set it here to make TypeMarshal works correctly. offloadReadOutput.Token = new STORAGE_OFFLOAD_TOKEN() { TokenId = new byte[504] }; uint outputBufferSize = (uint)TypeMarshal.ToBytes <FSCTL_OFFLOAD_READ_OUTPUT>(offloadReadOutput).Length; BaseTestSite.Log.Add(LogEntryKind.TestStep, "3. FSCTL request with FSCTL_OFFLOAD_READ."); status = this.fsaAdapter.FsCtlOffloadRead(offloadReadInput, outputBufferSize, out bytesReturned, out outputBuffer); if (!IsCurrentTransportSupportCopyOffload(status)) { return; } //Step 4: Verify test result BaseTestSite.Log.Add(LogEntryKind.TestStep, "4. Verify returned NTStatus code."); if (this.fsaAdapter.IsOffloadImplemented == false) { this.fsaAdapter.AssertAreEqual(this.Manager, MessageStatus.INVALID_DEVICE_REQUEST, status, "If the object store does not implement this functionality, the operation MUST be failed with STATUS_INVALID_DEVICE_REQUEST."); } else if (status == MessageStatus.NOT_SUPPORTED) { this.fsaAdapter.AssertAreEqual(this.Manager, MessageStatus.NOT_SUPPORTED, status, "If Open.File.Volume.IsOffloadReadSupported is FALSE, the operation MUST be failed with STATUS_NOT_SUPPORTED."); } else if (status == MessageStatus.STATUS_OFFLOAD_READ_FLT_NOT_SUPPORTED) { this.fsaAdapter.AssertAreEqual(this.Manager, MessageStatus.STATUS_OFFLOAD_READ_FLT_NOT_SUPPORTED, status, "A file system filter on the server has not opted in for Offload Read support."); } else if (status == MessageStatus.STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED) { this.fsaAdapter.AssertAreEqual(this.Manager, MessageStatus.STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED, status, @"Offload read operations cannot be performed on: Compressed files, Sparse files, Encrypted files, File system metadata files"); } else { this.fsaAdapter.AssertAreEqual(this.Manager, MessageStatus.SUCCESS, status, "Status set to STATUS_SUCCESS."); } }
public DetectResult[] CheckIOCTL_CopyOffload(string sharename, ref DetectionInfo info) { logWriter.AddLog(LogLevel.Information, "===== Detecting IOCTL CopyOffload ====="); #region Initialization int contentLength = 32; string content = Smb2Utility.CreateRandomString(contentLength); using (Smb2Client client = new Smb2Client(new TimeSpan(0, 0, defaultTimeoutInSeconds))) { ulong messageId; ulong sessionId; uint treeId; ConnectToShare(sharename, info, client, out messageId, out sessionId, out treeId); #endregion #region Create logWriter.AddLog(LogLevel.Information, "Client creates a file with specified length as for offload copy."); Smb2CreateContextResponse[] serverCreateContexts; FILEID fileIdSrc; CREATE_Response createResponse; Packet_Header header; client.Create( 1, 1, info.smb2Info.IsRequireMessageSigning ? Packet_Header_Flags_Values.FLAGS_SIGNED : Packet_Header_Flags_Values.NONE, messageId++, sessionId, treeId, Guid.NewGuid().ToString(), AccessMask.GENERIC_READ | AccessMask.GENERIC_WRITE | AccessMask.DELETE, ShareAccess_Values.FILE_SHARE_READ | ShareAccess_Values.FILE_SHARE_WRITE | ShareAccess_Values.FILE_SHARE_DELETE, CreateOptions_Values.FILE_NON_DIRECTORY_FILE, CreateDisposition_Values.FILE_OPEN_IF, File_Attributes.NONE, ImpersonationLevel_Values.Impersonation, SecurityFlags_Values.NONE, RequestedOplockLevel_Values.OPLOCK_LEVEL_NONE, null, out fileIdSrc, out serverCreateContexts, out header, out createResponse); if (header.Status != Smb2Status.STATUS_SUCCESS) { LogFailedStatus("CREATE", header.Status); throw new Exception("CREATE failed with " + Smb2Status.GetStatusCode(header.Status)); } #endregion #region Write WRITE_Response writeResponse; client.Write( 1, 1, info.smb2Info.IsRequireMessageSigning ? Packet_Header_Flags_Values.FLAGS_SIGNED : Packet_Header_Flags_Values.NONE, messageId++, sessionId, treeId, 0, fileIdSrc, Channel_Values.CHANNEL_NONE, WRITE_Request_Flags_Values.None, new byte[0], Encoding.ASCII.GetBytes(content), out header, out writeResponse, 0); if (header.Status != Smb2Status.STATUS_SUCCESS) { LogFailedStatus("WRITE", header.Status); throw new Exception("WRITE failed with " + Smb2Status.GetStatusCode(header.Status)); } #endregion #region Flush FLUSH_Response flushResponse; client.Flush( 1, 1, info.smb2Info.IsRequireMessageSigning ? Packet_Header_Flags_Values.FLAGS_SIGNED : Packet_Header_Flags_Values.NONE, messageId++, sessionId, treeId, fileIdSrc, out header, out flushResponse); if (header.Status != Smb2Status.STATUS_SUCCESS) { LogFailedStatus("FLUSH", header.Status); throw new Exception("FLUSH failed with " + Smb2Status.GetStatusCode(header.Status)); } #endregion #region IOCTL OFFLOAD_READ logWriter.AddLog(LogLevel.Information, "Client sends IOCTL request with FSCTL_OFFLOAD_READ to ask server to generate the token of the content for offload copy."); STORAGE_OFFLOAD_TOKEN token; ulong fileOffsetToRead = 0; //FileOffset should be aligned to logical sector boundary on the volume, e.g. 512 bytes ulong copyLengthToRead = (ulong)contentLength / 2 * 1024; //CopyLength should be aligned to logical sector boundary on the volume, e.g. 512 bytes ulong transferLength; // Request hardware to generate a token that represents a range of file to be copied FSCTL_OFFLOAD_READ_INPUT offloadReadInput = new FSCTL_OFFLOAD_READ_INPUT(); offloadReadInput.Size = 32; offloadReadInput.FileOffset = fileOffsetToRead; offloadReadInput.CopyLength = copyLengthToRead; byte[] requestInputOffloadRead = TypeMarshal.ToBytes(offloadReadInput); byte[] responseInput; byte[] responseOutput; IOCTL_Response ioCtlResponse; client.IoCtl( 1, 1, info.smb2Info.IsRequireMessageSigning ? Packet_Header_Flags_Values.FLAGS_SIGNED : Packet_Header_Flags_Values.NONE, messageId++, sessionId, treeId, CtlCode_Values.FSCTL_OFFLOAD_READ, fileIdSrc, 0, requestInputOffloadRead, 32000, IOCTL_Request_Flags_Values.SMB2_0_IOCTL_IS_FSCTL, out responseInput, out responseOutput, out header, out ioCtlResponse); #endregion DetectResult[] result = { DetectResult.UnSupported, DetectResult.UnSupported }; if (Smb2Status.STATUS_SUCCESS != header.Status) { LogFailedStatus("FSCTL_OFFLOAD_READ", header.Status); } else { result[0] = DetectResult.Supported; logWriter.AddLog(LogLevel.Information, "FSCTL_OFFLOAD_READ is supported"); #region IOCTL OFFLOAD_WRITE logWriter.AddLog(LogLevel.Information, "Client creates another file as the destination of offload copy."); // Create another file as the destination of offload copy. FILEID fileIdDes; client.Create( 1, 1, info.smb2Info.IsRequireMessageSigning ? Packet_Header_Flags_Values.FLAGS_SIGNED : Packet_Header_Flags_Values.NONE, messageId++, sessionId, treeId, Guid.NewGuid().ToString(), AccessMask.GENERIC_READ | AccessMask.GENERIC_WRITE | AccessMask.DELETE, ShareAccess_Values.FILE_SHARE_READ | ShareAccess_Values.FILE_SHARE_WRITE | ShareAccess_Values.FILE_SHARE_DELETE, CreateOptions_Values.FILE_NON_DIRECTORY_FILE, CreateDisposition_Values.FILE_OPEN_IF, File_Attributes.NONE, ImpersonationLevel_Values.Impersonation, SecurityFlags_Values.NONE, RequestedOplockLevel_Values.OPLOCK_LEVEL_NONE, null, out fileIdDes, out serverCreateContexts, out header, out createResponse); if (header.Status != Smb2Status.STATUS_SUCCESS) { LogFailedStatus("CREATE", header.Status); throw new Exception("CREATE failed with " + Smb2Status.GetStatusCode(header.Status)); } if (responseOutput != null) { var offloadReadOutput = TypeMarshal.ToStruct <FSCTL_OFFLOAD_READ_OUTPUT>(responseOutput); transferLength = offloadReadOutput.TransferLength; token = offloadReadOutput.Token; } else { transferLength = 0; token = new STORAGE_OFFLOAD_TOKEN(); } logWriter.AddLog(LogLevel.Information, "Client sends IOCTL request with FSCTL_OFFLOAD_WRITE to ask server to copy the content from source to destination."); ulong fileOffsetToWrite = (ulong)contentLength / 2 * 1024; //FileOffset should be aligned to logical sector boundary on the volume, e.g. 512 bytes ulong copyLengthToWrite = transferLength; //CopyLength should be aligned to logical sector boundary on the volume, e.g. 512 bytes ulong transferOffset = 0; //TransferOffset should be aligned to logical sector boundary on the volume, e.g. 512 bytes FSCTL_OFFLOAD_WRITE_INPUT offloadWriteInput = new FSCTL_OFFLOAD_WRITE_INPUT(); offloadWriteInput.Size = 544; offloadWriteInput.FileOffset = fileOffsetToWrite; offloadWriteInput.CopyLength = copyLengthToWrite; offloadWriteInput.TransferOffset = transferOffset; offloadWriteInput.Token = token; byte[] requestInputOffloadWrite = TypeMarshal.ToBytes(offloadWriteInput); // Client sends IOCTL request with FSCTL_OFFLOAD_WRITE to ask server to copy the content from source to destination. client.IoCtl( 1, 1, info.smb2Info.IsRequireMessageSigning ? Packet_Header_Flags_Values.FLAGS_SIGNED : Packet_Header_Flags_Values.NONE, messageId++, sessionId, treeId, CtlCode_Values.FSCTL_OFFLOAD_WRITE, fileIdDes, 0, requestInputOffloadWrite, 32000, IOCTL_Request_Flags_Values.SMB2_0_IOCTL_IS_FSCTL, out responseInput, out responseOutput, out header, out ioCtlResponse); if (Smb2Status.STATUS_SUCCESS == header.Status) { result[1] = DetectResult.Supported; logWriter.AddLog(LogLevel.Information, "FSCTL_OFFLOAD_WRITE is supported"); } else { LogFailedStatus("FSCTL_OFFLOAD_WRITE", header.Status); } #endregion } #region Close CLOSE_Response closeResponse; client.Close( 1, 1, info.smb2Info.IsRequireMessageSigning ? Packet_Header_Flags_Values.FLAGS_SIGNED : Packet_Header_Flags_Values.NONE, messageId++, sessionId, treeId, fileIdSrc, Flags_Values.NONE, out header, out closeResponse); if (header.Status != Smb2Status.STATUS_SUCCESS) { LogFailedStatus("CLOSE", header.Status); } #endregion #region Tree Disconnect TREE_DISCONNECT_Response treeDisconnectResponse; client.TreeDisconnect( 1, 1, info.smb2Info.IsRequireMessageSigning ? Packet_Header_Flags_Values.FLAGS_SIGNED : Packet_Header_Flags_Values.NONE, messageId++, sessionId, treeId, out header, out treeDisconnectResponse); if (header.Status != Smb2Status.STATUS_SUCCESS) { LogFailedStatus("TREEDISCONNECT", header.Status); } #endregion return(result); } }