The SMB2 LOCK_ELEMENT Structure packet is used by the SMB2 LOCK Request packet to indicate segments of files that should be locked or unlocked.
        public void FileServerFailover_Lock()
        {
            FILEID fileIdBeforeFailover;
            uint treeIdBeforeFailover;
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "BeforeFailover: Connect to general file server {0}.", TestConfig.ClusteredFileServerName);
            ConnectGeneralFileServerBeforeFailover(TestConfig.ClusteredFileServerName, out treeIdBeforeFailover);

            #region CREATE a durable open with flag DHANDLE_FLAG_PERSISTENT
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "BeforeFailover: CREATE a durable open with flag DHANDLE_FLAG_PERSISTENT.");
            Smb2CreateContextResponse[] serverCreateContexts;
            createGuid = Guid.NewGuid();

            status = clientBeforeFailover.Create(
                treeIdBeforeFailover,
                fileName,
                CreateOptions_Values.FILE_NON_DIRECTORY_FILE,
                out fileIdBeforeFailover,
                out serverCreateContexts,
                RequestedOplockLevel_Values.OPLOCK_LEVEL_NONE,
                new Smb2CreateContextRequest[] {
                    new Smb2CreateDurableHandleRequestV2
                    {
                         CreateGuid = createGuid,
                         Flags = CREATE_DURABLE_HANDLE_REQUEST_V2_Flags.DHANDLE_FLAG_PERSISTENT,
                         Timeout = 3600000,
                    },
                    new Smb2CreateQueryOnDiskId
                    {
                    },
                });
            #endregion

            #region WRITE content to file
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "BeforeFailover: WRITE content to file.");
            status = clientBeforeFailover.Write(treeIdBeforeFailover, fileIdBeforeFailover, contentWrite);
            #endregion

            #region Request byte range lock
            LOCK_ELEMENT[] locks = new LOCK_ELEMENT[1];
            uint lockSequence = 0;
            locks[0].Offset = 0;
            locks[0].Length = (ulong)TestConfig.WriteBufferLengthInKb * 1024;
            locks[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_SHARED_LOCK;

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client1 start to lock a byte range for file \"{0}\" with parameters offset:{1}, length:{2}, flags: {3}",
                fileName, locks[0].Offset, locks[0].Length, locks[0].Flags.ToString());
            status = clientBeforeFailover.Lock(treeIdBeforeFailover, lockSequence++, fileIdBeforeFailover, locks);

            #endregion

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Do failover of the file server.");
            FailoverServer(currentAccessIp, TestConfig.ClusteredFileServerName, FileServerType.GeneralFileServer);

            FILEID fileIdAfterFailover = FILEID.Zero;
            uint treeIdAfterFailover;
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "AfterFailover: Reconnect to the same general file server {0}.", TestConfig.ClusteredFileServerName);
            ReconnectServerAfterFailover(TestConfig.ClusteredFileServerName, FileServerType.GeneralFileServer, out treeIdAfterFailover);

            #region CREATE to reconnect previous duarable open with flag DHANDLE_FLAG_PERSISTENT
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "AfterFailover: CREATE to reconnect previous duarable open with flag DHANDLE_FLAG_PERSISTENT.");
            status = DoUntilSucceed(
                () => clientAfterFailover.Create(
                    treeIdAfterFailover,
                    fileName,
                    CreateOptions_Values.FILE_NON_DIRECTORY_FILE,
                    out fileIdAfterFailover,
                    out serverCreateContexts,
                    RequestedOplockLevel_Values.OPLOCK_LEVEL_NONE,
                    new Smb2CreateContextRequest[] {
                        new Smb2CreateDurableHandleReconnectV2
                        {
                            FileId = new FILEID { Persistent = fileIdBeforeFailover.Persistent },
                            CreateGuid = createGuid,
                            Flags = CREATE_DURABLE_HANDLE_RECONNECT_V2_Flags.DHANDLE_FLAG_PERSISTENT
                        },
                    },
                    checker: (header, response) => { }),
                TestConfig.FailoverTimeout,
                "Retry Create until succeed within timeout span");
            #endregion

            #region READ and WRITE
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "AfterFailover: Read the contents written before failover.");
            status = clientAfterFailover.Read(
                treeIdAfterFailover,
                fileIdAfterFailover,
                0,
                (uint)contentWrite.Length,
                out contentRead);

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "AfterFailover: Verify the contents are the same as the one written before failover.");
            BaseTestSite.Assert.IsTrue(
                contentWrite.Equals(contentRead),
                "Content read after failover should be identical to that written before failover.");

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "AfterFailover: Write contents to the locking range, it MUST NOT be allowed.");
            status = clientAfterFailover.Write(
                treeIdAfterFailover,
                fileIdAfterFailover,
                contentWrite,
                checker: (header, response) =>
                {
                    BaseTestSite.Assert.AreNotEqual(
                        Smb2Status.STATUS_SUCCESS,
                        header.Status,
                        "All opens MUST NOT be allowed to write within the range when SMB2_LOCKFLAG_SHARED_LOCK set");
                    BaseTestSite.CaptureRequirementIfAreEqual(
                        Smb2Status.STATUS_FILE_LOCK_CONFLICT,
                        header.Status,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Id,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Description);
                });
            #endregion

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "From client3: Read and write the locking range when the file is locked, it should fail.");
            ValidateByteLockRangeFromAnotherClient(true, TestConfig.ClusteredFileServerName, fileName);

            #region Unlock byte range
            locks[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_UNLOCK;

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "AfterFailover: Client2 attempts to unlock the range");
            status = clientAfterFailover.Lock(treeIdAfterFailover, lockSequence++, fileIdAfterFailover, locks);
            #endregion

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "From client3: Read and write the locking range from a separate client when the file is unlocked, it should succeed.");
            ValidateByteLockRangeFromAnotherClient(false, TestConfig.ClusteredFileServerName, fileName);

            status = clientAfterFailover.Close(treeIdAfterFailover, fileIdAfterFailover);

            status = clientAfterFailover.TreeDisconnect(treeIdAfterFailover);

            status = clientAfterFailover.LogOff();
        }
        /// <summary>
        /// Unlock a range of a file
        /// </summary>
        /// <param name="offset">The start position of bytes to be unlocked</param>
        /// <param name="length">The bytes size to be unlocked</param>
        /// <returns>NTStatus code</returns>
        public MessageStatus UnlockByteRange(UInt64 offset, UInt64 length)
        {
            // SMB2 can lock or unlock multi-ranges within a file.
            // However this interface method requires one locked range, so only need to specify a one element lock_element array.
            LOCK_ELEMENT[] elements = new LOCK_ELEMENT[1];
            elements[0].Offset = offset;
            elements[0].Length = length;
            elements[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_UNLOCK;

            LOCK_Response responsePayload;
            uint status = this.smb2Client.Lock(
                1,
                1,
                this.packetHeaderFlag,
                this.messageId++,
                this.sessionId,
                this.treeId,
                this.lockSequence++,
                this.fileId,
                elements,
                out packetHeader,
                out responsePayload);

            return (MessageStatus)status;
        }
        /// <summary>
        /// Lock range of a file
        /// </summary>
        /// <param name="offset">The start position of bytes to be locked</param>
        /// <param name="length">The bytes size to be locked</param>
        /// <param name="exclusiveLock">
        /// NONE = 0,
        /// LOCKFLAG_SHARED_LOCK = 1,
        /// LOCKFLAG_EXCLUSIVE_LOCK = 2,
        /// LOCKFLAG_UNLOCK = 4,
        /// LOCKFLAG_FAIL_IMMEDIATELY = 16,
        /// </param>
        /// <param name="failImmediately">If the range is locked. Indicate whether the operation failed at once or wait for the range been unlocked.</param>
        /// <returns>NTStatus code</returns>
        public MessageStatus LockByteRange(
            UInt64 offset,
            UInt64 length,
            bool exclusiveLock,
            bool failImmediately)
        {
            // SMB2 can lock multi-ranges within a file. But this interface method only requires one range be locked.
            // So only need specify a one element lock_element array.
            LOCK_ELEMENT[] elements = new LOCK_ELEMENT[1];
            elements[0].Offset = offset;
            elements[0].Length = length;
            elements[0].Flags = LOCK_ELEMENT_Flags_Values.NONE;
            if (failImmediately)
            {
                elements[0].Flags |= LOCK_ELEMENT_Flags_Values.LOCKFLAG_FAIL_IMMEDIATELY;
            }
            if (exclusiveLock)
            {
                elements[0].Flags |= LOCK_ELEMENT_Flags_Values.LOCKFLAG_EXCLUSIVE_LOCK;
            }
            else
            {
                elements[0].Flags |= LOCK_ELEMENT_Flags_Values.LOCKFLAG_SHARED_LOCK;
            }

            LOCK_Response responsePayload;
            uint status = this.smb2Client.Lock(
                1,
                1,
                this.packetHeaderFlag,
                this.messageId++,
                this.sessionId,
                this.treeId,
                this.lockSequence++,
                this.fileId,
                elements,
                out packetHeader,
                out responsePayload);

            return (MessageStatus)status;
        }
Пример #4
0
        public void BVT_SMB2Basic_LockAndUnLock()
        {
            uint status;
            string content = Smb2Utility.CreateRandomString(TestConfig.WriteBufferLengthInKb);

            #region From client1 lock a byte range and try to write content to the file within the range
            BaseTestSite.Log.Add(
                LogEntryKind.Comment,
                "From client1 locks a byte range and try to write content to the file within the range.");

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Start client1 to create a file with sending the following requests: NEGOTIATE; SESSION_SETUP; TREE_CONNECT; CREATE.");
            client1 = new Smb2FunctionalClient(TestConfig.Timeout, TestConfig, BaseTestSite);
            client1.ConnectToServer(TestConfig.UnderlyingTransport, TestConfig.SutComputerName, TestConfig.SutIPAddress);
            status = client1.Negotiate(TestConfig.RequestDialects, TestConfig.IsSMB1NegotiateEnabled);
            status = client1.SessionSetup(
                TestConfig.DefaultSecurityPackage,
                TestConfig.SutComputerName,
                TestConfig.AccountCredential,
                TestConfig.UseServerGssToken);
            uint treeId1;
            status = client1.TreeConnect(uncSharePath, out treeId1);
            Smb2CreateContextResponse[] serverCreateContexts;
            FILEID fileId1;
            string fileName = Guid.NewGuid().ToString() + ".txt";
            status = client1.Create(
                treeId1,
                fileName,
                CreateOptions_Values.FILE_NON_DIRECTORY_FILE,
                out fileId1,
                out serverCreateContexts);

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "Client1 writes content to the file created.");
            status = client1.Write(treeId1, fileId1, content);

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "Tear down client1 by sending CLOSE request.");
            client1.Close(treeId1, fileId1);

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "Client1 sends CREATE request.");
            status = client1.Create(
                treeId1,
                fileName,
                CreateOptions_Values.FILE_NON_DIRECTORY_FILE,
                out fileId1,
                out serverCreateContexts);

            //Construct LOCK_ELEMENT
            LOCK_ELEMENT[] locks = new LOCK_ELEMENT[1];
            uint lockSequence = 0;
            locks[0].Offset = 0;
            locks[0].Length = (ulong)TestConfig.WriteBufferLengthInKb * 1024;
            locks[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_SHARED_LOCK;

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client1 starts to lock a byte range for file \"{0}\" with parameters offset:{1}, length:{2}, flags: {3})",
                fileName, locks[0].Offset, locks[0].Length, locks[0].Flags.ToString());
            status = client1.Lock(treeId1, lockSequence++, fileId1, locks);

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client1 sends WRITE request to write content to the locking range");
            status = client1.Write(
                treeId1,
                fileId1,
                content,
                checker: (header, response) =>
                {
                    BaseTestSite.Assert.AreNotEqual(
                        Smb2Status.STATUS_SUCCESS,
                        header.Status,
                        "All opens MUST NOT be allowed to write within the range when SMB2_LOCKFLAG_SHARED_LOCK set, actually server returns {0}.", Smb2Status.GetStatusCode(header.Status));
                    BaseTestSite.CaptureRequirementIfAreEqual(
                        Smb2Status.STATUS_FILE_LOCK_CONFLICT,
                        header.Status,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Id,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Description);
                });
            #endregion

            #region From client2 to read and write the locking range of the same file after lock
            BaseTestSite.Log.Add(
                LogEntryKind.Comment,
                "From client2 to read and take shared lock on the locking range of the same file after lock");

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Start client2 to create a file with sending the following requests: NEGOTIATE; SESSION_SETUP; TREE_CONNECT; CREATE.");
            client2 = new Smb2FunctionalClient(TestConfig.Timeout, TestConfig, BaseTestSite);
            client2.ConnectToServer(TestConfig.UnderlyingTransport, TestConfig.SutComputerName, TestConfig.SutIPAddress);
            status = client2.Negotiate(TestConfig.RequestDialects, TestConfig.IsSMB1NegotiateEnabled);
            status = client2.SessionSetup(
                TestConfig.DefaultSecurityPackage,
                TestConfig.SutComputerName,
                TestConfig.AccountCredential,
                TestConfig.UseServerGssToken);
            uint treeId2;
            status = client2.TreeConnect(uncSharePath, out treeId2);
            FILEID fileId2;
            status = client2.Create(
                treeId2,
                fileName,
                CreateOptions_Values.FILE_NON_DIRECTORY_FILE,
                out fileId2,
                out serverCreateContexts);

            string data;
            Random random = new Random();
            uint offset = (uint)random.Next(0, TestConfig.WriteBufferLengthInKb * 1024 - 1);
            uint length = (uint)random.Next(0, (int)(TestConfig.WriteBufferLengthInKb * 1024 - offset));
            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client2 sends READ request to read a random area in the locking range of file \"{0}\" with offset: {1}, length: {2}",
                fileName, offset, length);
            status = client2.Read(treeId2, fileId2, offset, length, out data);

            //Construct LOCK_ELEMENT
            LOCK_ELEMENT[] locksFromOtherOpen = new LOCK_ELEMENT[1];
            locksFromOtherOpen[0].Offset = offset;
            locksFromOtherOpen[0].Length = (ulong)length;
            locksFromOtherOpen[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_SHARED_LOCK;

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client2 attempts to take a shared lock on random range of file \"{0}\" with parameters offset:{1}, length:{2}, flags: {3})",
                fileName, locksFromOtherOpen[0].Offset, locksFromOtherOpen[0].Length, locksFromOtherOpen[0].Flags.ToString());
            status = client2.Lock(treeId2, lockSequence++, fileId2, locksFromOtherOpen);

            locksFromOtherOpen[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_UNLOCK;
            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client2 attempts to unlock the range");
            status = client2.Lock(treeId2, lockSequence++, fileId2, locksFromOtherOpen);

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client2 sends WRITE request to write a random area in the locking range of file \"{0}\" after lock", fileName);
            status = client2.Write(
                treeId2,
                fileId2,
                content,
                offset,
                checker: (header, response) =>
                {
                    BaseTestSite.Assert.AreNotEqual(
                        Smb2Status.STATUS_SUCCESS,
                        header.Status,
                        "All opens MUST NOT be allowed to write within the range when SMB2_LOCKFLAG_SHARED_LOCK set, actually server returns {0}.", Smb2Status.GetStatusCode(header.Status));
                    BaseTestSite.CaptureRequirementIfAreEqual(
                        Smb2Status.STATUS_FILE_LOCK_CONFLICT,
                        header.Status,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Id,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Description);
                });
            #endregion

            #region From client1 unlock the range
            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client1 unlocks the range");
            locks[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_UNLOCK;
            BaseTestSite.Log.Add(
                LogEntryKind.Debug,
                "Client1 attempts to unlock the range");
            status = client1.Lock(treeId1, lockSequence++, fileId1, locks);

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "Tear down client1 by sending the following requests: CLOSE; TREE_DISCONNECT; LOG_OFF");
            client1.Close(treeId1, fileId1);
            client1.TreeDisconnect(treeId1);
            client1.LogOff();
            #endregion

            #region From client2 write content to the previous locking range after unlock
            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Client2 sends WRITE request to write content to the previous locking range after unlock");
            BaseTestSite.Log.Add(
                LogEntryKind.Debug,
                "Client2 attempts to write a random area in the locking range of file \"{0}\" after unlock", fileName);
            status = client2.Write(treeId2, fileId2, content, offset);

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "Tear down client2 by sending the following requests: CLOSE; TREE_DISCONNECT; LOG_OFF");
            client2.Close(treeId2, fileId2);
            client2.TreeDisconnect(treeId2);
            client2.LogOff();
            #endregion
        }
        public void AppInstanceId_Lock_ExpectNoLockInReOpen()
        {
            #region Check Applicability
            TestConfig.CheckDialect(DialectRevision.Smb30);
            TestConfig.CheckCapabilities(NEGOTIATE_Response_Capabilities_Values.GLOBAL_CAP_PERSISTENT_HANDLES);
            TestConfig.CheckCreateContext(CreateContextTypeValue.SMB2_CREATE_APP_INSTANCE_ID, CreateContextTypeValue.SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2);
            #endregion

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "InitialOpen: Connect to share via Nic1.");
            FILEID fileIdForInitialOpen;
            uint treeIdForInitialOpen;
            ConnectShare(TestConfig.SutIPAddress, TestConfig.ClientNic1IPAddress, clientForInitialOpen, out treeIdForInitialOpen);

            #region Create an open with AppInstanceId
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "InitialOpen: Create an open with AppInstanceId.");
            Guid appInstanceId = Guid.NewGuid();
            Smb2CreateContextResponse[] serverCreateContexts;
            status = clientForInitialOpen.Create(
                treeIdForInitialOpen,
                fileName,
                CreateOptions_Values.FILE_NON_DIRECTORY_FILE,
                out fileIdForInitialOpen,
                out serverCreateContexts,
                RequestedOplockLevel_Values.OPLOCK_LEVEL_NONE,
                new Smb2CreateContextRequest[] {
                    new Smb2CreateDurableHandleRequestV2
                    {
                         CreateGuid = Guid.NewGuid(),
                         Flags = CREATE_DURABLE_HANDLE_REQUEST_V2_Flags.DHANDLE_FLAG_PERSISTENT,
                    },
                    new Smb2CreateAppInstanceId
                    {
                         AppInstanceId = appInstanceId
                    }
                },
                accessMask: AccessMask.GENERIC_READ | AccessMask.GENERIC_WRITE,
                shareAccess: ShareAccess_Values.NONE);
            #endregion

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "InitialOpen: Write contents to file.");
            status = clientForInitialOpen.Write(treeIdForInitialOpen, fileIdForInitialOpen, contentWrite);

            #region Request ByteRangeLock
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "InitialOpen: Request ByteRangeLock.");
            LOCK_ELEMENT[] locks = new LOCK_ELEMENT[1];
            uint lockSequence = 0;
            locks[0].Offset = 0;
            locks[0].Length = (ulong)TestConfig.WriteBufferLengthInKb * 1024;
            locks[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_SHARED_LOCK;

            BaseTestSite.Log.Add(
                LogEntryKind.Debug,
                "Client starts to lock a byte range for file \"{0}\" with parameters offset:{1}, length:{2}, flags: {3}",
                fileName, locks[0].Offset, locks[0].Length, locks[0].Flags.ToString());
            status = clientForInitialOpen.Lock(treeIdForInitialOpen, lockSequence++, fileIdForInitialOpen, locks);
            #endregion

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "ReOpen: Connect to share via Nic2.");
            FILEID fileIdForReOpen;
            uint treeIdForReOpen;
            ConnectShare(TestConfig.SutIPAddress, TestConfig.ClientNic2IPAddress, clientForReOpen, out treeIdForReOpen);

            #region Create an open with same AppInstanceId
            BaseTestSite.Log.Add(LogEntryKind.TestStep, "ReOpen: Create an open with same AppInstanceId.");
            status = clientForReOpen.Create(
                treeIdForReOpen,
                fileName,
                CreateOptions_Values.FILE_NON_DIRECTORY_FILE,
                out fileIdForReOpen,
                out serverCreateContexts,
                RequestedOplockLevel_Values.OPLOCK_LEVEL_NONE,
                new Smb2CreateContextRequest[] {
                    new Smb2CreateDurableHandleRequestV2
                    {
                         CreateGuid = Guid.NewGuid(),
                         Flags = CREATE_DURABLE_HANDLE_REQUEST_V2_Flags.DHANDLE_FLAG_PERSISTENT,
                    },
                    new Smb2CreateAppInstanceId
                    {
                        // Use the same application instance id to force the server close all files
                        // and will clear previous ByteRangeLock
                        AppInstanceId = appInstanceId
                    }
                },
                accessMask: AccessMask.GENERIC_READ);
            #endregion

            BaseTestSite.Log.Add(LogEntryKind.TestStep, "ReOpen: Read the contents written by InitialOpen.");
            status = clientForReOpen.Read(treeIdForReOpen, fileIdForReOpen, 0, (uint)contentWrite.Length, out contentRead);

            BaseTestSite.Assert.IsTrue(
                contentRead.Equals(contentWrite),
                "The written content should equal to read content.");

            #region AfterFailover: Attempt to access same file with previous byte lock range
            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "ReOpen: Attempt to access same file with previous byte lock range.");
            ValidateByteLockRangeFromAnotherClient(false);
            #endregion

            #region Client tear down
            BaseTestSite.Log.Add(TestTools.LogEntryKind.TestStep, "ReOpen: Close file.");
            status = clientForReOpen.Close(treeIdForReOpen, fileIdForReOpen);

            BaseTestSite.Log.Add(TestTools.LogEntryKind.TestStep, "ReOpen: Disconnect from the share.");
            status = clientForReOpen.TreeDisconnect(treeIdForReOpen);

            BaseTestSite.Log.Add(TestTools.LogEntryKind.TestStep, "ReOpen: Log off.");
            status = clientForReOpen.LogOff();

            #endregion
        }
        /// <summary>
        /// Establish alternative channel and lock/unlock byte range of a file from same or different channel client
        /// </summary>
        /// <param name="lockUnlockOnSameChannel">Set this parameter to true if the unlock operation is taken from the same channel client</param>
        private void MultipleChannelTestWithLock(bool lockUnlockOnSameChannel)
        {
            uint treeId;
            FILEID fileId;

            #region Check Applicability
            TestConfig.CheckDialect(DialectRevision.Smb30);
            TestConfig.CheckCapabilities(NEGOTIATE_Response_Capabilities_Values.GLOBAL_CAP_MULTI_CHANNEL);
            #endregion

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Establish main channel with client {0} and server {1}.", clientIps[0].ToString(), serverIps[0].ToString());
            EstablishMainChannel(
                TestConfig.RequestDialects,
                serverIps[0],
                clientIps[0],
                out treeId);

            #region CREATE to open file for WRITE and LOCK
            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Main channel: CREATE file.");
            Smb2CreateContextResponse[] serverCreateContexts;
            status = mainChannelClient.Create(
                treeId,
                fileName,
                CreateOptions_Values.FILE_NON_DIRECTORY_FILE,
                out fileId,
                out serverCreateContexts);
            #endregion

            #region WRITE content to file
            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Main channel: WRITE content to file.");
            status = mainChannelClient.Write(treeId, fileId, contentWrite);
            #endregion

            #region Request byte lock range
            //Construct LOCK_ELEMENT
            LOCK_ELEMENT[] locks = new LOCK_ELEMENT[1];
            uint lockSequence = 0;
            locks[0].Offset = 0;
            locks[0].Length = (ulong)TestConfig.WriteBufferLengthInKb * 1024;
            locks[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_SHARED_LOCK;

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Main channel client starts to lock a byte range for file \"{0}\" with parameters offset:{1}, length:{2}, flags: {3})",
                fileName, locks[0].Offset, locks[0].Length, locks[0].Flags.ToString());
            status = mainChannelClient.Lock(treeId, lockSequence++, fileId, locks);
            #endregion

            #region WRITE content within the locking range
            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Main channel: attempts to write content within the locking range.");
            status = mainChannelClient.Write(
                treeId,
                fileId,
                contentWrite,
                checker: (header, response) =>
                {
                    BaseTestSite.Assert.AreNotEqual(
                        Smb2Status.STATUS_SUCCESS,
                        header.Status,
                        "Write should not success when shared lock is taken");

                    BaseTestSite.CaptureRequirementIfAreEqual(
                        Smb2Status.STATUS_FILE_LOCK_CONFLICT,
                        header.Status,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Id,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Description);
                });
            #endregion

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Establish alternative channel with client {0} and server {1}.", clientIps[1].ToString(), serverIps[1].ToString());
            EstablishAlternativeChannel(
                TestConfig.RequestDialects,
                serverIps[1],
                clientIps[1],
                treeId);

            #region READ/WRITE from alternative channel within the locking range
            Random random = new Random();
            uint offset = (uint)random.Next(0, TestConfig.WriteBufferLengthInKb * 1024 - 1);
            uint length = (uint)random.Next(0, (int)(TestConfig.WriteBufferLengthInKb * 1024 - offset));
            BaseTestSite.Log.Add(
                LogEntryKind.Debug,
                "Alternative channel: attempts to randomly read the locking range with offset: {0} and length: {1}", offset, length);
            status = alternativeChannelClient.Read(treeId, fileId, offset, length, out contentRead);

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Alternative channel: attempts to write content to the locking range");
            status = alternativeChannelClient.Write(
                treeId,
                fileId,
                contentWrite,
                checker: (header, response) =>
                {
                    BaseTestSite.Assert.AreNotEqual(
                        Smb2Status.STATUS_SUCCESS,
                        header.Status,
                        "All opens MUST NOT be allowed to write within the range when SMB2_LOCKFLAG_SHARED_LOCK set");
                    BaseTestSite.CaptureRequirementIfAreEqual(
                        Smb2Status.STATUS_FILE_LOCK_CONFLICT,
                        header.Status,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Id,
                        RequirementCategory.STATUS_FILE_LOCK_CONFLICT.Description);
                });
            #endregion

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Read and write the locking range from Client3 when the file is locked");
            ValidateByteLockRangeFromAnotherClient(true);

            locks[0].Flags = LOCK_ELEMENT_Flags_Values.LOCKFLAG_UNLOCK;
            if (lockUnlockOnSameChannel)
            {
                BaseTestSite.Log.Add(
                    LogEntryKind.TestStep,
                    "From main channel client unlock the range");
                status = mainChannelClient.Lock(treeId, lockSequence++, fileId, locks);
            }
            else
            {
                BaseTestSite.Log.Add(
                    LogEntryKind.TestStep,
                    "From alternative channel client unlock the range");
                status = alternativeChannelClient.Lock(treeId, lockSequence++, fileId, locks);
            }

            BaseTestSite.Log.Add(
                LogEntryKind.TestStep,
                "Read and write the locking range from Client3 when the file is unlocked");
            ValidateByteLockRangeFromAnotherClient(false);

            ClientTearDown(mainChannelClient, treeId, fileId);
        }