Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1184 lines
31 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
oplock.c
Abstract:
This module implements all the routines pertaining to oplock in the
NT redirector.
Author:
Larry Osterman (larryo) 1-Apr-1991
Revision History:
1-Apr-1991 larryo
Created
--*/
#define INCLUDE_SMB_LOCK
#define INCLUDE_SMB_OPEN_CLOSE
#include "precomp.h"
#pragma hdrstop
typedef struct _PACKED_LOCK_DESCRIPTOR {
PSMB_BUFFER SmbBuffer;
PLOCKING_ANDX_RANGE LockRange;
ULONG ByteCount;
ULONG NumLockRanges;
ULONG MaxNumLockRanges;
BOOLEAN UseLargeRanges;
} PACKED_LOCK_DESCRIPTOR, *PPACKED_LOCK_DESCRIPTOR;
//
// Forward declarations.
//
DBGSTATIC
VOID
BreakOplock (
PVOID Context
);
DBGSTATIC
NTSTATUS
SendBreakOplockResponse (
PFCB OplockedFcb
);
DBGSTATIC
NTSTATUS
PackFileLock (
IN PFCB Fcb,
IN PFILE_LOCK_INFO FileLock,
IN OUT PPACKED_LOCK_DESCRIPTOR PackedLocks
);
DBGSTATIC
NTSTATUS
FlushLockRequest (
IN PFCB Fcb,
IN OUT PPACKED_LOCK_DESCRIPTOR PackedLocks
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RdrCheckOplockInRaw)
#pragma alloc_text(PAGE, BreakOplock)
#pragma alloc_text(PAGE, SendBreakOplockResponse)
#pragma alloc_text(PAGE, RdrFlushFileLocks)
#pragma alloc_text(PAGE, PackFileLock)
#pragma alloc_text(PAGE, FlushLockRequest)
#pragma alloc_text(PAGE3FILE, RdrQueueOplockBreak)
#pragma alloc_text(PAGE3FILE, RdrBreakOplockCallback)
#endif
//
//
// Context records used for oplock breaks.
//
//
typedef struct _BREAKOPLOCKCONTEXT {
WORK_QUEUE_ITEM WorkHeader; // Standard work context.
USHORT FileId; // FID of file to break oplock on.
PSERVERLISTENTRY Server; // Server oplock is being broken on.
UCHAR OplockLevel; // New level of oplock on break.
} BREAKOPLOCKCONTEXT, *PBREAKOPLOCKCONTEXT;
STANDARD_CALLBACK_HEADER(
RdrBreakOplockCallback
)
/*++
RdrBreakOplockCallback - Indication callback for break oplock request
Routine Description:
This routine is invoked by either the receive based indication lookahead
routine from the transport, or by the connection invalidating
code.
Arguments:
Irp - Pointer to the I/O request packet from the transport
IncomingSmb - Pointer to incoming SMB buffer
MpxTable - Mpx Table entry for request.
Context - Context information passed into NetTranceiveNoWait
ErrorIndicator - TRUE if the network request was in error.
NetworkErrorCode - Error code if request completed with network error
Return Value:
Return value to be returned from receive indication routine.
--*/
{
USHORT FileId;
UCHAR NewOplockLevel;
PREQ_LOCKING_ANDX BreakOplockRequest = (PREQ_LOCKING_ANDX) (Smb+1);
DISCARDABLE_CODE(RdrFileDiscardableSection);
UNREFERENCED_PARAMETER(MpxEntry);
UNREFERENCED_PARAMETER(Irp);
UNREFERENCED_PARAMETER(Server);
UNREFERENCED_PARAMETER(Ctx);
UNREFERENCED_PARAMETER(NetworkErrorCode);
// DbgBreakPoint();
if (ErrorIndicator) {
return STATUS_SUCCESS;
}
if (Smb->Command != SMB_COM_LOCKING_ANDX) {
KdPrint(("RDR: Received oplock break without oplock break command\n"));
return STATUS_SUCCESS;
}
if ((SmbGetUshort(&BreakOplockRequest->LockType) & LOCKING_ANDX_OPLOCK_RELEASE) == 0) {
KdPrint(("RDR: Received oplock break without oplock break bit set\n"));
return STATUS_SUCCESS;
}
ASSERT (MpxEntry->Signature == STRUCTURE_SIGNATURE_MPX_ENTRY);
ASSERT (MpxEntry->Flags & MPX_ENTRY_OPLOCK);
FileId = SmbGetUshort(&BreakOplockRequest->Fid);
if (Server->Capabilities & DF_NT_SMBS) {
NewOplockLevel = BreakOplockRequest->OplockLevel;
if (NewOplockLevel == OPLOCK_BROKEN_TO_NONE) {
NewOplockLevel = SMB_OPLOCK_LEVEL_NONE;
} else if (NewOplockLevel == OPLOCK_BROKEN_TO_II) {
NewOplockLevel = SMB_OPLOCK_LEVEL_II;
} else {
//
// Treat this as a break to none, we don't recognize this level.
//
NewOplockLevel = SMB_OPLOCK_LEVEL_NONE;
//
// Write an error log entry indicating that we had this problem.
//
RdrStatistics.NetworkErrors += 1;
RdrWriteErrorLogEntry(
Server,
IO_ERR_LAYERED_FAILURE,
EVENT_RDR_INVALID_OPLOCK,
STATUS_SUCCESS,
Smb,
(USHORT)*SmbLength
);
}
} else {
NewOplockLevel = SMB_OPLOCK_LEVEL_NONE;
}
//
// Queue the oplock break request to a generic worker thread.
//
RdrQueueOplockBreak(FileId, Server, NewOplockLevel);
return STATUS_SUCCESS; // We're done, eat response and return
}
VOID
RdrQueueOplockBreak(
IN USHORT FileId,
IN PSERVERLISTENTRY Server,
IN UCHAR NewOplockLevel
)
/*++
Routine Description:
This routine is called to queue a request to break an oplock to a
redirector worker thread.
It will queue up a request to an FSP thread to handle the break request.
Arguments:
IN USHORT FileId - Supplies the smb_fid of the file to disconnect.
Return Value:
None.
Note:
This routine can be called at all times (including at DPC_LEVEL).
--*/
{
PBREAKOPLOCKCONTEXT Context;
DISCARDABLE_CODE(RdrFileDiscardableSection);
//
// We need to queue this request to a redirector thread to allow the error
// code to handle the request.
//
// We allocate memory for the context block out of must-succeed pool
// for the transfer to the FSP.
//
Context = ALLOCATE_POOL(NonPagedPoolMustSucceed, sizeof(BREAKOPLOCKCONTEXT), POOL_BREAKOPLOCKCTX);
#if DBG
if (Context == NULL) {
InternalError(("Allocation of disconnection context failed!"));
}
#endif
RdrReferenceServer(Server);
ExInitializeWorkItem(&Context->WorkHeader, BreakOplock, Context);;
Context->FileId = FileId;
Context->Server = Server;
Context->OplockLevel = NewOplockLevel;
//
// Queue the request to a redir worker thread.
//
RdrQueueWorkItem (&Context->WorkHeader, DelayedWorkQueue);
}
VOID
RdrCheckOplockInRaw(
IN PMDL Mdl,
IN PSERVERLISTENTRY Sle,
IN OUT PULONG ByteCount
)
/*++
Routine Description:
This routine is called to check if raw data received contains a break
oplock request.
It will queue up a request to an FSP thread to handle the break request if
appropriate.
Arguments:
IN PMDL Mdl - Supplies an MDL containing receive data to check.
IN PSERVERLISTENTRY Sle - Supplies the server we will be breaking the
oplock on.
IN OUT PULONG ByteCount - Supplies the number of bytes in the request.
Return Value:
None.
Note:
This routine checks the following criteria to determine if this request
is really an oplock break instead of raw data:
1) The length is the size of a locking&x SMB request
2) The message begins with 0xFFSMB
3) The smb_mid of the message is 0xffff
4) The smb_command of the message is 0x24 (locking_andx)
--*/
{
PAGED_CODE();
if (*ByteCount == sizeof(SMB_HEADER)+FIELD_OFFSET(REQ_LOCKING_ANDX, Buffer[0])) {
PSMB_HEADER Smb;
PREQ_LOCKING_ANDX BreakOplockRequest;
UCHAR NewOplockLevel;
Smb = MmGetSystemAddressForMdl(Mdl);
if ((SmbGetUshort(&Smb->Mid) == 0xffff)
&&
(Smb->Command == SMB_COM_LOCKING_ANDX)
&&
(SmbGetUlong(&Smb->Protocol) == SMB_HEADER_PROTOCOL)) {
dprintf(DPRT_OPLOCK, ("CheckOplockInRaw: Oplock and Raw crossed\n"));
BreakOplockRequest = (PREQ_LOCKING_ANDX) (Smb+1);
//
// Pretend we got no data now.
//
*ByteCount = 0;
if (Sle->Capabilities & DF_NT_SMBS) {
NewOplockLevel = BreakOplockRequest->OplockLevel;
if (NewOplockLevel == OPLOCK_BROKEN_TO_NONE) {
NewOplockLevel = SMB_OPLOCK_LEVEL_NONE;
} else {
ASSERT (NewOplockLevel == OPLOCK_BROKEN_TO_II);
NewOplockLevel = SMB_OPLOCK_LEVEL_II;
}
} else {
NewOplockLevel = SMB_OPLOCK_LEVEL_NONE;
}
//
// Queue up the break oplock request.
//
RdrQueueOplockBreak(SmbGetUshort(&BreakOplockRequest->Fid), Sle, NewOplockLevel);
}
}
}
DBGSTATIC
VOID
BreakOplock (
PVOID Ctx
)
/*++
Routine Description:
This routine is called to break an oplock on an open file.
machine.
Arguments:
IN PVOID Context - Supplies a context structure containing the fileid
of the oplocked file.
Return Value:
None
--*/
{
PBREAKOPLOCKCONTEXT Context = Ctx;
PFCB OplockedFcb;
NTSTATUS Status;
PLIST_ENTRY IcbEntry;
BOOLEAN ExclusiveFile = FALSE;
PSERVERLISTENTRY Server = Context->Server;
CCHAR OplockLevel = Context->OplockLevel;
CCHAR OldOplockLevel = SMB_OPLOCK_LEVEL_NONE;
PAGED_CODE();
dprintf(DPRT_OPLOCK, ("HandleOplockBreak, FileId: %x\n", Context->FileId));
OplockedFcb = RdrFindOplockedFcb(Context->FileId, Server);
//
// Free up the pool used to queue the request to the FSP, it is no longer
// needed.
//
FREE_POOL(Context);
if (OplockedFcb == NULL) {
dprintf(DPRT_OPLOCK, ("Oplocked file not found!\n"));
// Reference was placed in RdrQueueOplockBreak.
RdrDereferenceServer(NULL, Server);
return;
}
try {
//
// Since an oplock break might come in that would later cause the
// redirector to remove the last reference to an FCB after the file
// was closed, we need to reference the file discardable section here.
//
RdrReferenceDiscardableCode(RdrFileDiscardableSection);
OldOplockLevel = OplockedFcb->NonPagedFcb->OplockLevel;
dprintf(DPRT_OPLOCK, ("Oplocked file %wZ found\n", &OplockedFcb->FileName));
//
// We found a file that is oplocked. Claim the FCB
// resource exclusively to allow us to break this puppy.
//
// There is a deadlock situation that can occur if the file
// is in the process of being opened and an oplock break is
// generated. To avoid the problem, we release the FCB in the
// create code before sending the create SMB over the net. This
// is safe because of the CreateComplete event that serializes create
// operations on the file.
//
//
// We only have to keep the FCB locked while we are removing the
// oplock indications on the file. We have to lock the file
// to prevent us from writing data into the cache while we are trying
// to flush it.
//
// We have to release the FCB lock before the file is flushed because
// otherwise we will deadlock potentially (when the user flushes
// the data from the cache if the application has oplocked the file)
//
RdrAcquireFcbLock(OplockedFcb, ExclusiveLock, TRUE);
//
// Once we finally have the fcb lock, it is possible that the FCB
// is no longer oplocked (if, for instance the user closed the file
// before we got the lock, or if the use was deleted before we got
// the lock).
//
// Re-test the FCB to make sure that it is still oplocked, and if so
// break the oplock.
//
if (OplockedFcb->NonPagedFcb->Flags & FCB_OPLOCKED) {
OplockedFcb->NonPagedFcb->Flags |= FCB_OPLOCKBREAKING;
//
// Walk the ICB chain and mark each file as no longer being
// oplocked.
//
for (IcbEntry = OplockedFcb->InstanceChain.Flink ;
IcbEntry != &OplockedFcb->InstanceChain ;
IcbEntry = IcbEntry->Flink) {
PICB Icb = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
//
// if this isn't a break to II, the file is no longer oplocked.
//
if (OplockLevel != SMB_OPLOCK_LEVEL_II) {
Icb->u.f.Flags &= ~ICBF_OPLOCKED;
}
Icb->u.f.OplockLevel = OplockLevel;
if (Icb->u.f.Flags & ICBF_OPENEDEXCLUSIVE) {
ExclusiveFile = TRUE;
}
}
//
// If we either cannot guarantee exclusive access to this file,
// or there are no processes that have the file opened, we
// want to flush the file from the cache.
//
//
// Please note that this test is over aggressive, it will force
// executables to be flushed from the cache on an oplock break.
//
if (!ExclusiveFile ||
OplockedFcb->NumberOfOpens == 0) {
PLIST_ENTRY IcbEntry;
USHORT OplockedFileId = OplockedFcb->NonPagedFcb->OplockedFileId;
ULONG NumberOfOpenFiles = 0;
//
// If there are any outstanding file locks on this file,
// flush the locks to the server. If the current oplock
// level is 2, then we don't have any locks to flush.
//
if (FsRtlAreThereCurrentFileLocks(&OplockedFcb->FileLock) &&
(OldOplockLevel != SMB_OPLOCK_LEVEL_II) ) {
Status = RdrFlushFileLocks(OplockedFcb);
// ASSERT (NT_SUCCESS(Status));
}
//
// If the file is cached, flush the contents of the cache
// and mark the file as being uncached.
//
//RdrLog(( "rdflushd", &OplockedFcb->FileName, 0 ));
Status = RdrFlushCacheFile(OplockedFcb);
// ASSERT (NT_SUCCESS(Status));
//
// We don't need to purge the cache for the file if
// this is a break to II. The only exception to this
// is in the case where there are no user handles open on
// this file. In that case, we need to purge the cache
// to prevent possible sharing violations on the server.
//
if (OplockLevel != SMB_OPLOCK_LEVEL_II ||
OplockedFcb->NumberOfOpens == 0) {
//
// Mark that the file is no longer oplocked (which will
// be true once the purge is completed)
//
OplockedFcb->NonPagedFcb->Flags &= ~(FCB_OPLOCKED|FCB_OPLOCKBREAKING);
OplockedFcb->NonPagedFcb->OplockLevel = SMB_OPLOCK_LEVEL_NONE;
//
// Now that the cache has been flushed, purge the files
// from the cache. This will invalidate any open
// files and close all outstanding file objects for
// this file if there are no handles open to the file.
//
// Note that RdrPurgeCacheFile releases the FCB lock while
// close the section, reacquiring it before returning.
// This means that the state of the HASOPLOCKEDHANDLE
// must be retested on return, in case the ICB that held
// the oplock is closed in the interim.
//
//RdrLog(( "rdpurgee", &OplockedFcb->FileName, 0 ));
Status = RdrPurgeCacheFile(OplockedFcb);
if (!(OplockedFcb->NonPagedFcb->Flags & FCB_HASOPLOCKHANDLE)) {
try_return(NOTHING);
}
OplockLevel = SMB_OPLOCK_LEVEL_NONE;
}
//
// Count the number of ICB's associated with this FCB that have
// the same file id as the one we are trying breaking the oplock
// on. If there are no remaining ICBs with that file id,
// then we are done with the oplock break, so we should return
// immediately.
//
// If there are still ICB's that share this file's ICB,
// then we are not done, so we should complete the oplock
// break.
//
for (IcbEntry = OplockedFcb->InstanceChain.Flink ;
IcbEntry != &OplockedFcb->InstanceChain ;
IcbEntry = IcbEntry->Flink) {
PICB IcbToFlush = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
if ((IcbToFlush->Flags & ICB_HASHANDLE)
&&
(IcbToFlush->FileId == OplockedFileId)) {
NumberOfOpenFiles += 1;
}
}
if (NumberOfOpenFiles == 0) {
try_return(NOTHING);
}
}
//
// We only want to respond to the oplock break if we are breaking from 1 to 2, or from 1 to none,
// but not if we are breaking from 2 to none.
//
OplockedFcb->NonPagedFcb->OplockLevel = OplockLevel;
if (OldOplockLevel != SMB_OPLOCK_LEVEL_II ||
OplockLevel != SMB_OPLOCK_LEVEL_NONE) {
//
// Now respond to the oplock break.
//
Status = SendBreakOplockResponse (OplockedFcb);
}
if (OplockLevel != SMB_OPLOCK_LEVEL_II) {
OplockedFcb->NonPagedFcb->Flags &= ~FCB_OPLOCKED;
}
OplockedFcb->NonPagedFcb->Flags &= ~FCB_OPLOCKBREAKING;
}
try_exit:NOTHING;
} finally {
//
// The FCB was referenced in RdrFindOplockedFcb, so we have
// to dereference the FCB before leaving.
//
RdrDereferenceFcb(NULL, OplockedFcb->NonPagedFcb, TRUE, 0, NULL);
//
// We will no longer be referencing this server list. We can now
// dereference the reference added in RdrQueueOplockBreak.
//
RdrDereferenceServer(NULL, Server);
RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
}
dprintf(DPRT_OPLOCK, ("Break oplock complete.\n"));
}
DBGSTATIC
NTSTATUS
SendBreakOplockResponse (
PFCB OplockedFcb
)
/*++
Routine Description:
This routine is called to send the final break oplock response to the
server that is servicing the oplock.
Arguments:
IN PFCB OplockedFcb - Supplies the FCB of the file that is oplocked.
Return Value:
None
--*/
{
NTSTATUS Status;
PMPX_ENTRY Mte = NULL;
PSMB_BUFFER SmbBuffer = NULL;
PSMB_HEADER SmbHeader;
PREQ_LOCKING_ANDX OplockResponse;
TRANCEIVE_HEADER Context;
PAGED_CODE();
ASSERT (OplockedFcb->NonPagedFcb->Flags & FCB_HASOPLOCKHANDLE);
dprintf(DPRT_OPLOCK, ("SendOplockBreakResponse for fcb %lx\n", OplockedFcb));
if ((SmbBuffer = RdrAllocateSMBBuffer())==NULL) {
RdrInternalError(EVENT_RDR_OPLOCK_SMB);
return STATUS_INSUFFICIENT_RESOURCES;
}
try {
SmbHeader = (PSMB_HEADER )SmbBuffer->Buffer;
SmbHeader->Command = SMB_COM_LOCKING_ANDX;
OplockResponse = (PREQ_LOCKING_ANDX )(SmbHeader+1);
OplockResponse->WordCount = 8;
OplockResponse->AndXReserved = 0;
OplockResponse->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
SmbPutUshort(&OplockResponse->AndXOffset, 0);
OplockResponse->LockType = LOCKING_ANDX_OPLOCK_RELEASE;
if (OplockedFcb->NonPagedFcb->OplockLevel == SMB_OPLOCK_LEVEL_NONE) {
OplockResponse->OplockLevel = OPLOCK_BROKEN_TO_NONE;
} else if (OplockedFcb->NonPagedFcb->OplockLevel == SMB_OPLOCK_LEVEL_II) {
OplockResponse->OplockLevel = OPLOCK_BROKEN_TO_II;
} else {
InternalError(("Unknown oplock level in break oplock\n"));
RdrStatistics.NetworkErrors += 1;
RdrWriteErrorLogEntry(
OplockedFcb->Connection->Server,
IO_ERR_LAYERED_FAILURE,
EVENT_RDR_INVALID_OPLOCK,
STATUS_SUCCESS,
NULL,
0
);
}
SmbPutUshort(&OplockResponse->Fid, OplockedFcb->NonPagedFcb->OplockedFileId);
SmbPutUlong(&OplockResponse->Timeout, 0L);
SmbPutUshort(&OplockResponse->NumberOfUnlocks, 0);
SmbPutUshort(&OplockResponse->NumberOfLocks, 0);
SmbPutUshort(&OplockResponse->ByteCount, 0);
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_LOCKING_ANDX, Buffer[0]);
Context.TransferSize = SmbBuffer->Mdl->ByteCount;
Context.Type = STRUCTURE_SIGNATURE_CONTEXT_BASE;
Status = RdrStartTranceive(NULL,
OplockedFcb->Connection,
NULL,
FALSE,
FALSE,
0,
FALSE,
&Mte,
Context.TransferSize);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
ASSERT (Mte != NULL);
Mte->RequestContext = &Context;
Mte->Callback = NULL;
Mte->SLE = OplockedFcb->Connection->Server;
//
// Initialize the kernel event as a notification event that is in the
// SIGNALLED state!
//
// This means that RdrWaitTranceive will not wait for a response
// on this request!
//
KeInitializeEvent(&Context.KernelEvent, NotificationEvent, TRUE);
Status = RdrSendSMB(
NT_NORMAL,
OplockedFcb->Connection,
OplockedFcb->NonPagedFcb->OplockedSecurityEntry->PagedSecurityEntry,
Mte,
SmbBuffer->Mdl
);
if (!NT_SUCCESS(Status)) {
try_return(Status);
}
//
// Wait for the SMB
Status = RdrWaitTranceive(Mte);
if (Context.ErrorType == NoError) {
try_return(Status = STATUS_SUCCESS);
} else {
try_return(Status = Context.ErrorCode);
}
try_exit:NOTHING;
} finally {
if (Mte != NULL) {
RdrEndTranceive(Mte);
}
//
// Release the SMB buffer now.
//
if (SmbBuffer != NULL) {
RdrFreeSMBBuffer(SmbBuffer);
}
dprintf(DPRT_OPLOCK, ("SendOplockBreakResponse for fcb %lx done\n", OplockedFcb));
}
return Status;
}
NTSTATUS
RdrFlushFileLocks (
IN PFCB Fcb
)
/*++
Routine Description:
This routine is called to dump all outstanding file locks to a remote
server.
Arguments:
IN PFCB Fcb - Supplies the file whose locks to dump.
Return Value:
NTSTATUS - Status of dump operation.
--*/
{
NTSTATUS Status;
PFILE_LOCK_INFO FileLock;
ULONG MaxNumLockRanges;
BOOLEAN UseLargeRanges;
PACKED_LOCK_DESCRIPTOR ExclusiveLocks;
PACKED_LOCK_DESCRIPTOR SharedLocks;
PAGED_CODE();
if (Fcb->NonPagedFcb->OplockedSecurityEntry == NULL) {
return STATUS_VIRTUAL_CIRCUIT_CLOSED;
}
//
// Initialize the lock descriptors.
//
ExclusiveLocks.SmbBuffer = NULL;
SharedLocks.SmbBuffer = NULL;
//
// If the server is an NT server, we need to ship large lock range descriptors.
//
UseLargeRanges = BooleanFlagOn(Fcb->Connection->Server->Capabilities, DF_NT_SMBS);
ExclusiveLocks.UseLargeRanges = UseLargeRanges;
SharedLocks.UseLargeRanges = UseLargeRanges;
//
// Calculate the number of lock ranges that will fit in an SMB buffer.
//
MaxNumLockRanges =
(MIN((ULONG)SMB_BUFFER_SIZE, Fcb->Connection->Server->BufferSize) -
(sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_LOCKING_ANDX, Buffer[0]))) /
(UseLargeRanges ? sizeof(NTLOCKING_ANDX_RANGE) : sizeof(LOCKING_ANDX_RANGE));
ExclusiveLocks.MaxNumLockRanges = MaxNumLockRanges;
SharedLocks.MaxNumLockRanges = MaxNumLockRanges;
dprintf(DPRT_OPLOCK, ("RdrFlushFileLocks, File:%wZ (%lx)\n", &Fcb->FileName, Fcb))
dprintf(DPRT_OPLOCK, ("NumLocksPerSmb: %lx. %lx bytes of data\n", MaxNumLockRanges,
((MaxNumLockRanges*
(UseLargeRanges?sizeof(NTLOCKING_ANDX_RANGE):sizeof(LOCKING_ANDX_RANGE)))+
sizeof(SMB_HEADER)+FIELD_OFFSET(REQ_LOCKING_ANDX, Buffer[0]))));
ASSERT (MaxNumLockRanges > 0);
for (FileLock = FsRtlGetNextFileLock(&Fcb->FileLock, TRUE)
;
FileLock != NULL
;
FileLock = FsRtlGetNextFileLock(&Fcb->FileLock, FALSE)) {
if (FileLock->ExclusiveLock) {
Status = PackFileLock(Fcb, FileLock, &ExclusiveLocks);
} else {
Status = PackFileLock(Fcb, FileLock, &SharedLocks);
}
// ASSERT(NT_SUCCESS(Status));
}
Status = FlushLockRequest(Fcb, &ExclusiveLocks);
// ASSERT(NT_SUCCESS(Status));
Status = FlushLockRequest(Fcb, &SharedLocks);
// ASSERT(NT_SUCCESS(Status));
if (ExclusiveLocks.SmbBuffer != NULL) {
RdrFreeSMBBuffer(ExclusiveLocks.SmbBuffer);
}
if (SharedLocks.SmbBuffer != NULL) {
RdrFreeSMBBuffer(SharedLocks.SmbBuffer);
}
return Status;
}
DBGSTATIC
NTSTATUS
PackFileLock (
IN PFCB Fcb,
IN PFILE_LOCK_INFO FileLock,
IN OUT PPACKED_LOCK_DESCRIPTOR PackedLocks
)
/*++
Routine Description:
This routine will pack another lock range into an SMB buffer.
Arguments:
IN PFCB Fcb - Fcb to flush locks on.
IN PFILE_LOCK FileLock -
IN OUT PPACKED_LOCK_DESCRIPTOR PackedLocks - Supplies/Returns a lock descriptor.
Return Value:
NTSTATUS - Status of operation if flushed to net.
--*/
{
NTSTATUS Status;
PSMB_BUFFER SmbBuffer;
PSMB_HEADER Smb;
PREQ_LOCKING_ANDX LockRequest;
PLOCKING_ANDX_RANGE LockRange;
PNTLOCKING_ANDX_RANGE NtLockRange;
UCHAR LockType;
PAGED_CODE();
SmbBuffer = PackedLocks->SmbBuffer;
if (SmbBuffer == NULL) {
SmbBuffer = RdrAllocateSMBBuffer();
dprintf(DPRT_OPLOCK, ("PackFileLock. New lock buffer @ %lx\n", SmbBuffer));
if (SmbBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
PackedLocks->SmbBuffer = SmbBuffer;
Smb = (PSMB_HEADER)SmbBuffer->Buffer;
LockRequest = (PREQ_LOCKING_ANDX)(Smb + 1);
Smb->Command = SMB_COM_LOCKING_ANDX;
LockRequest->WordCount = 8;
LockRequest->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
LockRequest->AndXReserved = 0;
SmbPutUlong(&LockRequest->Timeout, 0);
SmbPutUshort(&LockRequest->NumberOfUnlocks, 0);
SmbPutUshort(&LockRequest->AndXOffset, 0);
SmbPutUshort(&LockRequest->Fid, Fcb->NonPagedFcb->OplockedFileId);
LockType = 0;
if ( !FileLock->ExclusiveLock ) {
LockType |= LOCKING_ANDX_SHARED_LOCK;
}
if ( PackedLocks->UseLargeRanges ) {
LockType |= LOCKING_ANDX_LARGE_FILES;
}
SmbPutUshort(&LockRequest->LockType, LockType);
PackedLocks->ByteCount = sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_LOCKING_ANDX, Buffer[0]);
PackedLocks->LockRange = (PLOCKING_ANDX_RANGE)LockRequest->Buffer;
PackedLocks->NumLockRanges = 0;
}
dprintf(DPRT_OPLOCK, ("PackFileLock. Pack range into @ %x Off:%x%08x, Len:%x%08x\n",
SmbBuffer, FileLock->StartingByte.HighPart, FileLock->StartingByte.LowPart,
FileLock->Length.HighPart, FileLock->Length.LowPart));
if ( PackedLocks->UseLargeRanges ) {
NtLockRange = (PNTLOCKING_ANDX_RANGE)PackedLocks->LockRange;
SmbPutUshort(&NtLockRange->Pid, RDR_PROCESS_ID);
SmbPutUshort(&NtLockRange->Pad, 0);
SmbPutUlong(&NtLockRange->OffsetHigh, FileLock->StartingByte.HighPart);
SmbPutUlong(&NtLockRange->OffsetLow, FileLock->StartingByte.LowPart);
SmbPutUlong(&NtLockRange->LengthHigh, FileLock->Length.HighPart);
SmbPutUlong(&NtLockRange->LengthLow, FileLock->Length.LowPart);
PackedLocks->LockRange = (PLOCKING_ANDX_RANGE)(NtLockRange + 1);
PackedLocks->ByteCount += sizeof(NTLOCKING_ANDX_RANGE);
} else {
ASSERT( FileLock->StartingByte.HighPart == 0 );
ASSERT( FileLock->Length.HighPart == 0 );
LockRange = PackedLocks->LockRange;
SmbPutUshort(&LockRange->Pid, RDR_PROCESS_ID);
SmbPutUlong(&LockRange->Offset, FileLock->StartingByte.LowPart);
SmbPutUlong(&LockRange->Length, FileLock->Length.LowPart);
PackedLocks->LockRange = LockRange + 1;
PackedLocks->ByteCount += sizeof(LOCKING_ANDX_RANGE);
}
PackedLocks->NumLockRanges++;
if ( PackedLocks->NumLockRanges == PackedLocks->MaxNumLockRanges ) {
Status = FlushLockRequest( Fcb, PackedLocks );
} else {
Status = STATUS_SUCCESS;
}
return Status;
}
DBGSTATIC
NTSTATUS
FlushLockRequest (
IN PFCB Fcb,
IN OUT PPACKED_LOCK_DESCRIPTOR PackedLocks
)
/*++
Routine Description:
This routine will pack another lock range into an SMB buffer.
Arguments:
IN PFCB Fcb - Fcb for the file to flush.
IN OUT PPACKED_LOCK_DESCRIPTOR PackedLocks - Supplies/Returns a lock descriptor.
Return Value:
NTSTATUS - Status of operation if flushed to net.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSMB_BUFFER SmbBuffer;
PREQ_LOCKING_ANDX LockRequest;
PAGED_CODE();
SmbBuffer = PackedLocks->SmbBuffer;
if ( (SmbBuffer != NULL) && (PackedLocks->NumLockRanges != 0) ) {
dprintf(DPRT_OPLOCK, ("FlushLockRequest. Flush lock SMB %lx\n", SmbBuffer));
LockRequest = (PREQ_LOCKING_ANDX)((PSMB_HEADER)SmbBuffer->Buffer + 1);
//
// We are going to transition lock types, so we have
// to flush this SMB to the server.
//
ASSERT(SmbGetUshort(&LockRequest->Fid) == Fcb->NonPagedFcb->OplockedFileId);
SmbPutUshort(&LockRequest->NumberOfLocks, (USHORT)PackedLocks->NumLockRanges);
SmbPutUshort(
&LockRequest->ByteCount,
(USHORT)(PackedLocks->ByteCount - (sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_LOCKING_ANDX, Buffer[0])))
);
SmbBuffer->Mdl->ByteCount = PackedLocks->ByteCount;
//
// Flush the oplock request.
//
Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT, // Flags
NULL, // Irp
Fcb->Connection,
SmbBuffer->Mdl,
NULL,
Fcb->NonPagedFcb->OplockedSecurityEntry->PagedSecurityEntry);
if (Status == STATUS_INVALID_HANDLE) {
RdrInvalidateFileId(Fcb->NonPagedFcb, Fcb->NonPagedFcb->OplockedFileId);
}
PackedLocks->ByteCount = sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_LOCKING_ANDX, Buffer[0]);
PackedLocks->LockRange = (PLOCKING_ANDX_RANGE)LockRequest->Buffer;
PackedLocks->NumLockRanges = 0;
}
return Status;
}