* reactos/ntoskrnl/fs/filelock.c
*
*/
-#include <ntos.h>
+#include <ddk/ntddk.h>
+#include <internal/ifs.h>
#include <ddk/ntifs.h>
+#include <ntos.h>
+
+#define NDEBUG
+#include <internal/debug.h>
+
+/*
+NOTE:
+I'm not using resource syncronization here, since FsRtlFastCheckLockForRead/Write
+are allowed to be called at DISPATCH_LEVEL. Must therefore use nonpaged memory for
+the lists.
+*/
+#define LOCK_START_OFF(Lock) ((Lock).StartingByte.QuadPart)
+#define LOCK_END_OFF(Lock) (((Lock).StartingByte.QuadPart) + ((Lock).Length.QuadPart) - 1)
+#define REQUEST_START_OFF (FileOffset->QuadPart)
+#define REQUEST_END_OFF ((FileOffset->QuadPart) + (Length->QuadPart) - 1)
+
+FAST_MUTEX LockTocMutex;
+NPAGED_LOOKASIDE_LIST GrantedLookaside;
+NPAGED_LOOKASIDE_LIST LockTocLookaside;
+PAGED_LOOKASIDE_LIST LockLookaside;
/**********************************************************************
- * NAME EXPORTED
- * FsRtlCheckLockForReadAccess@8
+ * NAME PRIVATE
+ * FsRtlpInitFileLockingImplementation
*
- * DESCRIPTION
- *
- * ARGUMENTS
+ */
+VOID
+STDCALL
+FsRtlpInitFileLockingImplementation(VOID)
+{
+ ExInitializeNPagedLookasideList( &LockTocLookaside,
+ NULL,
+ NULL,
+ 0,
+ sizeof(FILE_LOCK_TOC),
+ IFS_POOL_TAG,
+ 0
+ );
+
+ ExInitializeNPagedLookasideList( &GrantedLookaside,
+ NULL,
+ NULL,
+ 0,
+ sizeof(FILE_LOCK_GRANTED),
+ IFS_POOL_TAG,
+ 0
+ );
+
+ ExInitializePagedLookasideList( &LockLookaside,
+ NULL,
+ NULL,
+ 0,
+ sizeof(FILE_LOCK),
+ IFS_POOL_TAG,
+ 0
+ );
+
+ ExInitializeFastMutex(&LockTocMutex);
+}
+
+/**********************************************************************
+ * NAME PRIVATE
+ * FsRtlpFileLockCancelRoutine
*
- * RETURN VALUE
+ */
+VOID
+STDCALL
+FsRtlpFileLockCancelRoutine(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ KIRQL oldIrql;
+ PKSPIN_LOCK SpinLock;
+ PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine;
+
+ //don't need this since we have our own sync. protecting irp cancellation
+ IoReleaseCancelSpinLock(Irp->CancelIrql);
+
+ SpinLock = &((PFILE_LOCK_TOC)Irp->Tail.Overlay.DriverContext[1])->SpinLock;
+
+ KeAcquireSpinLock(SpinLock, &oldIrql);
+ RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
+ KeReleaseSpinLock(SpinLock, oldIrql);
+
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+
+ CompleteLockIrpRoutine = ((PFILE_LOCK)Irp->Tail.Overlay.DriverContext[0])->CompleteLockIrpRoutine;
+ if (CompleteLockIrpRoutine)
+ {
+ CompleteLockIrpRoutine(Irp->Tail.Overlay.DriverContext[2], Irp);
+ }
+ else
+ {
+ IofCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+
+}
+
+/**********************************************************************
+ * NAME PRIVATE
+ * FsRtlpCheckLockForReadOrWriteAccess
*
- * NOTE (Bo Branten)
- * All this really does is pick out the lock parameters from
- * the irp (io stack location?), get IoGetRequestorProcess,
- * and pass values on to FsRtlFastCheckLockForRead.
+ */
+BOOLEAN
+FASTCALL
+FsRtlpCheckLockForReadOrWriteAccess(
+ IN PFILE_LOCK FileLock,
+ IN PLARGE_INTEGER FileOffset,
+ IN PLARGE_INTEGER Length,
+ IN ULONG Key,
+ IN PFILE_OBJECT FileObject,
+ IN PEPROCESS Process,
+ IN BOOLEAN Read
+ )
+{
+ KIRQL oldirql;
+ PFILE_LOCK_TOC LockToc;
+ PFILE_LOCK_GRANTED Granted;
+ PLIST_ENTRY EnumEntry;
+
+ assert(FileLock);
+ LockToc = FileLock->LockInformation;
+
+ if (LockToc == NULL || Length->QuadPart == 0)
+ {
+ return TRUE;
+ }
+
+ KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
+
+ EnumEntry = LockToc->GrantedListHead.Flink;
+ while ( EnumEntry != &LockToc->GrantedListHead)
+ {
+ Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED , ListEntry );
+ //if overlapping
+ if(!(REQUEST_START_OFF > LOCK_END_OFF(Granted->Lock) ||
+ REQUEST_END_OFF < LOCK_START_OFF(Granted->Lock)))
+ {
+ //No read conflict if (shared lock) OR (exclusive + our lock)
+ //No write conflict if exclusive lock AND our lock
+ if ((Read && !Granted->Lock.ExclusiveLock) ||
+ (Granted->Lock.ExclusiveLock &&
+ Granted->Lock.Process == Process &&
+ Granted->Lock.FileObject == FileObject &&
+ Granted->Lock.Key == Key ) )
+ {
+ //AND if lock surround request region, stop searching and grant
+ if (REQUEST_START_OFF >= LOCK_START_OFF(Granted->Lock) &&
+ REQUEST_END_OFF <= LOCK_END_OFF(Granted->Lock))
+ {
+ EnumEntry = &LockToc->GrantedListHead;//indicate no conflict
+ break;
+ }
+ //else continue searching for conflicts
+ }
+ else //conflict
+ {
+ break;
+ }
+ }
+ EnumEntry = EnumEntry->Flink;
+ }
+
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+
+ if (EnumEntry == &LockToc->GrantedListHead)
+ { //no conflict
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**********************************************************************
+ * NAME EXPORTED
+ * FsRtlCheckLockForReadAccess
*
+ * @implemented
*/
BOOLEAN
STDCALL
FsRtlCheckLockForReadAccess (
- IN PFILE_LOCK FileLock,
- IN PIRP Irp
- )
+ IN PFILE_LOCK FileLock,
+ IN PIRP Irp
+ )
{
- return FALSE;
+ PIO_STACK_LOCATION Stack;
+ LARGE_INTEGER LocalLength;
+
+ Stack = IoGetCurrentIrpStackLocation(Irp);
+
+ LocalLength.u.LowPart = Stack->Parameters.Read.Length;
+ LocalLength.u.HighPart = 0;
+
+ return FsRtlpCheckLockForReadOrWriteAccess( FileLock,
+ &Stack->Parameters.Read.ByteOffset,
+ &LocalLength,
+ Stack->Parameters.Read.Key,
+ Stack->FileObject,
+ IoGetRequestorProcess(Irp),
+ TRUE//Read?
+ );
}
/**********************************************************************
* NAME EXPORTED
- * FsRtlCheckLockForWriteAccess@8
+ * FsRtlCheckLockForWriteAccess
*
- * DESCRIPTION
- *
- * ARGUMENTS
- *
- * RETURN VALUE
- *
- * NOTE (Bo Branten)
- * All this really does is pick out the lock parameters from
- * the irp (io stack location?), get IoGetRequestorProcess,
- * and pass values on to FsRtlFastCheckLockForWrite.
+ * @implemented
*/
BOOLEAN
STDCALL
FsRtlCheckLockForWriteAccess (
- IN PFILE_LOCK FileLock,
- IN PIRP Irp
- )
+ IN PFILE_LOCK FileLock,
+ IN PIRP Irp
+ )
{
- return FALSE;
+ PIO_STACK_LOCATION Stack;
+ LARGE_INTEGER LocalLength;
+
+ Stack = IoGetCurrentIrpStackLocation(Irp);
+
+ LocalLength.u.LowPart = Stack->Parameters.Read.Length;
+ LocalLength.u.HighPart = 0;
+
+ return FsRtlpCheckLockForReadOrWriteAccess( FileLock,
+ &Stack->Parameters.Write.ByteOffset,
+ &LocalLength,
+ Stack->Parameters.Write.Key,
+ Stack->FileObject,
+ IoGetRequestorProcess(Irp),
+ FALSE//Read?
+ );
+
}
+
+
/**********************************************************************
* NAME EXPORTED
- * FsRtlFastCheckLockForRead@24
- *
- * DESCRIPTION
- *
- * ARGUMENTS
- *
- * RETURN VALUE
+ * FsRtlFastCheckLockForRead
*
+ * @implemented
*/
BOOLEAN
STDCALL
FsRtlFastCheckLockForRead (
- IN PFILE_LOCK FileLock,
- IN PLARGE_INTEGER FileOffset,
- IN PLARGE_INTEGER Length,
- IN ULONG Key,
- IN PFILE_OBJECT FileObject,
- IN PEPROCESS Process
- )
+ IN PFILE_LOCK FileLock,
+ IN PLARGE_INTEGER FileOffset,
+ IN PLARGE_INTEGER Length,
+ IN ULONG Key,
+ IN PFILE_OBJECT FileObject,
+ IN PEPROCESS Process
+ )
{
- return FALSE;
+ return FsRtlpCheckLockForReadOrWriteAccess( FileLock,
+ FileOffset,
+ Length,
+ Key,
+ FileObject,
+ Process,
+ TRUE//Read?
+ );
}
/**********************************************************************
* NAME EXPORTED
- * FsRtlFastCheckLockForWrite@24
- *
- * DESCRIPTION
- *
- * ARGUMENTS
- *
- * RETURN VALUE
+ * FsRtlFastCheckLockForWrite
*
+ * @implemented
*/
BOOLEAN
STDCALL
FsRtlFastCheckLockForWrite (
- IN PFILE_LOCK FileLock,
- IN PLARGE_INTEGER FileOffset,
- IN PLARGE_INTEGER Length,
- IN ULONG Key,
- IN PFILE_OBJECT FileObject,
- IN PEPROCESS Process
- )
+ IN PFILE_LOCK FileLock,
+ IN PLARGE_INTEGER FileOffset,
+ IN PLARGE_INTEGER Length,
+ IN ULONG Key,
+ IN PFILE_OBJECT FileObject,
+ IN PEPROCESS Process
+ )
{
- return FALSE;
+ return FsRtlpCheckLockForReadOrWriteAccess( FileLock,
+ FileOffset,
+ Length,
+ Key,
+ FileObject,
+ Process,
+ FALSE//Read?
+ );
}
+
/**********************************************************************
- * NAME EXPORTED
- * FsRtlFastUnlockAll@16
- * FsRtlFastUnlockAllByKey@20
+ * NAME PRIVATE
+ * FsRtlpFastUnlockAllByKey
*
- * DESCRIPTION
- *
- * ARGUMENTS
- *
- * RETURN VALUE
+ */
+NTSTATUS
+FASTCALL
+FsRtlpFastUnlockAllByKey(
+ IN PFILE_LOCK FileLock,
+ IN PFILE_OBJECT FileObject,
+ IN PEPROCESS Process,
+ IN DWORD Key, /* FIXME: guess */
+ IN BOOLEAN UseKey, /* FIXME: guess */
+ IN PVOID Context OPTIONAL
+ )
+{
+ KIRQL oldirql;
+ PFILE_LOCK_TOC LockToc;
+ PLIST_ENTRY EnumEntry;
+ PFILE_LOCK_GRANTED Granted;
+ BOOLEAN Unlock = FALSE;
+ //must make local copy since FILE_LOCK struct is allowed to be paged
+ PUNLOCK_ROUTINE GotUnlockRoutine;
+
+ assert(FileLock);
+ LockToc = FileLock->LockInformation;
+
+ if (LockToc == NULL)
+ {
+ return STATUS_RANGE_NOT_LOCKED;
+ }
+
+ GotUnlockRoutine = FileLock->UnlockRoutine;
+ KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
+
+ EnumEntry = LockToc->GrantedListHead.Flink;
+ while (EnumEntry != &LockToc->GrantedListHead )
+ {
+ Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED, ListEntry);
+ EnumEntry = EnumEntry->Flink;
+
+ if (Granted->Lock.Process == Process &&
+ Granted->Lock.FileObject == FileObject &&
+ (!UseKey || (UseKey && Granted->Lock.Key == Key)) )
+ {
+ RemoveEntryList(&Granted->ListEntry);
+ Unlock = TRUE;
+
+ if (GotUnlockRoutine)
+ {
+ /*
+ Put on unlocked list and call unlock routine for them afterwards.
+ This way we don't have to restart enum after each call
+ */
+ InsertHeadList(&LockToc->UnlockedListHead,&Granted->ListEntry);
+ }
+ else
+ {
+ ExFreeToNPagedLookasideList(&GrantedLookaside,Granted);
+ }
+ }
+ }
+
+ if (Unlock)
+ {
+ //call unlock routine for each unlocked lock (if any)
+ while (!IsListEmpty(&LockToc->UnlockedListHead))
+ {
+ EnumEntry = RemoveTailList(&LockToc->UnlockedListHead);
+ Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED, ListEntry);
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+ FileLock->UnlockRoutine(Context,&Granted->Lock);
+ ExFreeToNPagedLookasideList(&GrantedLookaside,Granted);
+ KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
+ }
+
+ //NOTE: holding spinlock while calling this
+ FsRtlpCompletePendingLocks(FileLock, LockToc, &oldirql);
+
+ if (IsListEmpty(&LockToc->GrantedListHead))
+ {
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+ FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
+ }
+ else
+ {
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+ }
+ return STATUS_SUCCESS;
+ }
+
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+ return STATUS_RANGE_NOT_LOCKED;
+}
+
+/**********************************************************************
+ * NAME EXPORTED
+ * FsRtlFastUnlockAll
*
+ * @implemented
*/
-static
NTSTATUS
STDCALL
-FsRtlpFastUnlockAllByKey (
- IN PFILE_LOCK FileLock,
- IN PFILE_OBJECT FileObject,
- IN PEPROCESS Process,
- IN DWORD Key, /* FIXME: guess */
- IN BOOLEAN UseKey, /* FIXME: guess */
- IN PVOID Context OPTIONAL
- )
+FsRtlFastUnlockAll /*ByProcess*/ (
+ IN PFILE_LOCK FileLock,
+ IN PFILE_OBJECT FileObject,
+ IN PEPROCESS Process,
+ IN PVOID Context OPTIONAL
+ )
{
- /* FIXME: */
- return (STATUS_RANGE_NOT_LOCKED);
+ return FsRtlpFastUnlockAllByKey( FileLock,
+ FileObject,
+ Process,
+ 0, /* Key */
+ FALSE, /* Do NOT use Key */
+ Context
+ );
}
-
+/**********************************************************************
+ * NAME EXPORTED
+ * FsRtlFastUnlockAllByKey
+ *
+ * @implemented
+ */
NTSTATUS
STDCALL
-FsRtlFastUnlockAll (
- IN PFILE_LOCK FileLock,
- IN PFILE_OBJECT FileObject,
- IN PEPROCESS Process,
- IN PVOID Context OPTIONAL
- )
+FsRtlFastUnlockAllByKey (
+ IN PFILE_LOCK FileLock,
+ IN PFILE_OBJECT FileObject,
+ IN PEPROCESS Process,
+ IN ULONG Key,
+ IN PVOID Context OPTIONAL
+ )
{
- return FsRtlpFastUnlockAllByKey (
- FileLock,
- FileObject,
- Process,
- 0, /* Key */
- FALSE, /* Do NOT use Key */
- Context
- );
+ return FsRtlpFastUnlockAllByKey( FileLock,
+ FileObject,
+ Process,
+ Key,
+ TRUE, /* Use Key */
+ Context
+ );
}
+/**********************************************************************
+ * NAME PRIVATE
+ * FsRtlpAddLock
+ *
+ * NOTE
+ * Spinlock held at entry !!
+ */
NTSTATUS
-STDCALL
-FsRtlFastUnlockAllByKey (
- IN PFILE_LOCK FileLock,
- IN PFILE_OBJECT FileObject,
- IN PEPROCESS Process,
- IN ULONG Key,
- IN PVOID Context OPTIONAL
- )
+FASTCALL
+FsRtlpAddLock(
+ IN PFILE_LOCK_TOC LockToc,
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN PLARGE_INTEGER Length,
+ IN PEPROCESS Process,
+ IN ULONG Key,
+ IN BOOLEAN ExclusiveLock
+ )
{
- return FsRtlpFastUnlockAllByKey (
- FileLock,
- FileObject,
- Process,
- Key,
- TRUE, /* Use Key */
- Context
- );
+ PLIST_ENTRY EnumEntry;
+ PFILE_LOCK_GRANTED Granted;
+
+ EnumEntry = LockToc->GrantedListHead.Flink;
+ while (EnumEntry != &LockToc->GrantedListHead)
+ {
+ Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED, ListEntry);
+ //if overlapping
+ if(!(REQUEST_START_OFF > LOCK_END_OFF(Granted->Lock) ||
+ REQUEST_END_OFF < LOCK_START_OFF(Granted->Lock)))
+ {
+ //never conflict if shared lock and we want to add a shared lock
+ if (!Granted->Lock.ExclusiveLock && !ExclusiveLock)
+ {
+ //AND if lock surround region, stop searching and insert lock
+ if (REQUEST_START_OFF >= LOCK_START_OFF(Granted->Lock) &&
+ REQUEST_END_OFF <= LOCK_END_OFF(Granted->Lock))
+ {
+ EnumEntry = &LockToc->GrantedListHead;
+ break;
+ }
+ //else keep locking for conflicts
+ }
+ else
+ {//conflict if we want share access to excl. lock OR exlc. access to shared lock
+ break;//FAIL
+ }
+ }
+ EnumEntry = EnumEntry->Flink;
+ }
+
+ if (EnumEntry == &LockToc->GrantedListHead)
+ {//no conflict
+ Granted = ExAllocateFromNPagedLookasideList(&GrantedLookaside);
+
+ Granted->Lock.StartingByte = *FileOffset;
+ Granted->Lock.Length = *Length;
+ Granted->Lock.ExclusiveLock = ExclusiveLock;
+ Granted->Lock.Key = Key;
+ Granted->Lock.FileObject = FileObject;
+ Granted->Lock.Process = Process;
+ Granted->Lock.EndingByte.QuadPart = REQUEST_END_OFF;
+
+ InsertHeadList(&LockToc->GrantedListHead,&Granted->ListEntry);
+ return TRUE;
+ }
+
+ return FALSE;
+
}
+
/**********************************************************************
- * NAME EXPORTED
- * FsRtlFastUnlockSingle@32
+ * NAME PRIVATE
+ * FsRtlpCompletePendingLocks
*
- * DESCRIPTION
- *
- * ARGUMENTS
+ * NOTE
+ * Spinlock held at entry !!
+ */
+VOID
+FASTCALL
+FsRtlpCompletePendingLocks(
+ IN PFILE_LOCK FileLock,
+ IN PFILE_LOCK_TOC LockToc,
+ IN OUT PKIRQL oldirql
+ )
+{
+ //walk pending list, FIFO order, try 2 complete locks
+ PLIST_ENTRY EnumEntry;
+ PIRP Irp;
+ PEXTENDED_IO_STACK_LOCATION Stack;
+
+ EnumEntry = LockToc->PendingListHead.Blink;
+ while (EnumEntry != &LockToc->PendingListHead)
+ {
+ Irp = CONTAINING_RECORD(EnumEntry,IRP, Tail.Overlay.ListEntry);
+
+ Stack = (PEXTENDED_IO_STACK_LOCATION) IoGetCurrentIrpStackLocation(Irp);
+ if (FsRtlpAddLock(LockToc,
+ Stack->FileObject,
+ &Stack->Parameters.LockControl.ByteOffset,
+ Stack->Parameters.LockControl.Length,
+ IoGetRequestorProcess(Irp),
+ Stack->Parameters.LockControl.Key,
+ Stack->Flags & SL_EXCLUSIVE_LOCK
+ ) )
+ {
+ RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
+
+ if (!IoSetCancelRoutine(Irp, NULL))
+ {
+ /*
+ Cancel routine WILL be called after we release the spinlock. It will try to remove
+ the irp from the list and cancel/complete this irp. Since we allready removed it,
+ make its ListEntry point to itself.
+ */
+ InitializeListHead(&Irp->Tail.Overlay.ListEntry);
+ }
+ else
+ {
+ /*
+ Cancel routine will NOT be called, canceled or not.
+
+ Put on completed list and complete them all afterwards.
+ This way we don't have to restart enum after each completion.
+ */
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+ InsertHeadList(&LockToc->CompletedListHead,&Irp->Tail.Overlay.ListEntry);
+ }
+ }
+ EnumEntry = EnumEntry->Blink;
+ }
+
+ //complete irp's (if any)
+ while (!IsListEmpty(&LockToc->CompletedListHead))
+ {
+ EnumEntry = RemoveTailList(&LockToc->CompletedListHead);
+ KeReleaseSpinLock(&LockToc->SpinLock, *oldirql);//fires cancel routine
+ Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry);
+
+ if (FileLock->CompleteLockIrpRoutine)
+ {
+ FileLock->CompleteLockIrpRoutine(Irp->Tail.Overlay.DriverContext[2], Irp);
+ }
+ else
+ {
+ IofCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+
+ KeAcquireSpinLock(&LockToc->SpinLock, oldirql);
+ }
+
+}
+
+
+
+/**********************************************************************
+ * NAME PRIVATE
+ * FsRtlpUnlockSingle
*
- * RETURN VALUE
+ */
+NTSTATUS
+FASTCALL
+FsRtlpUnlockSingle(
+ IN PFILE_LOCK FileLock,
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN PLARGE_INTEGER Length,
+ IN PEPROCESS Process,
+ IN ULONG Key,
+ IN PVOID Context OPTIONAL,
+ IN BOOLEAN AlreadySynchronized,
+ IN BOOLEAN CallUnlockRoutine
+ )
+{
+ KIRQL oldirql;
+ PFILE_LOCK_TOC LockToc;
+ PFILE_LOCK_GRANTED Granted;
+ PLIST_ENTRY EnumEntry;
+
+ assert(FileLock);
+ LockToc = FileLock->LockInformation;
+
+ if (LockToc == NULL || Length->QuadPart == 0)
+ {
+ return STATUS_RANGE_NOT_LOCKED;
+ }
+
+ KeAcquireSpinLock(&LockToc->SpinLock, &oldirql );
+
+ EnumEntry = LockToc->GrantedListHead.Flink;
+ while (EnumEntry != &LockToc->GrantedListHead)
+ {
+ Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
+
+ //must be exact match
+ if (FileOffset->QuadPart == Granted->Lock.StartingByte.QuadPart &&
+ Length->QuadPart == Granted->Lock.Length.QuadPart &&
+ Granted->Lock.Process == Process &&
+ Granted->Lock.FileObject == FileObject &&
+ Granted->Lock.Key == Key)
+ {
+ RemoveEntryList(&Granted->ListEntry);
+ FsRtlpCompletePendingLocks(FileLock, LockToc, &oldirql);
+
+ if (IsListEmpty(&LockToc->GrantedListHead))
+ {
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+ FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
+ }
+ else
+ {
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+ }
+
+ if (FileLock->UnlockRoutine && CallUnlockRoutine)
+ {
+ FileLock->UnlockRoutine(Context,&Granted->Lock);
+ }
+
+ ExFreeToNPagedLookasideList(&GrantedLookaside,Granted);
+
+ return STATUS_SUCCESS;
+ }
+ EnumEntry = EnumEntry->Flink;
+ }
+
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+
+ return STATUS_RANGE_NOT_LOCKED;
+
+}
+
+
+
+/**********************************************************************
+ * NAME EXPORTED
+ * FsRtlFastUnlockSingle
*
+ * @implemented
*/
NTSTATUS
STDCALL
FsRtlFastUnlockSingle (
- IN PFILE_LOCK FileLock,
- IN PFILE_OBJECT FileObject,
- IN PLARGE_INTEGER FileOffset,
- IN PLARGE_INTEGER Length,
- IN PEPROCESS Process,
- IN ULONG Key,
- IN PVOID Context OPTIONAL,
- IN BOOLEAN AlreadySynchronized
- )
+ IN PFILE_LOCK FileLock,
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN PLARGE_INTEGER Length,
+ IN PEPROCESS Process,
+ IN ULONG Key,
+ IN PVOID Context OPTIONAL,
+ IN BOOLEAN AlreadySynchronized
+ )
{
- return (STATUS_RANGE_NOT_LOCKED);
+ return FsRtlpUnlockSingle( FileLock,
+ FileObject,
+ FileOffset,
+ Length,
+ Process,
+ Key,
+ Context,
+ AlreadySynchronized,
+ TRUE//CallUnlockRoutine
+ );
}
-
/**********************************************************************
* NAME EXPORTED
- * FsRtlGetNextFileLock@8
+ * FsRtlpDumpFileLocks
*
- * DESCRIPTION
- *
- * ARGUMENTS
+ * NOTE: used for testing and debugging
+ */
+VOID
+FASTCALL
+FsRtlpDumpFileLocks(
+ IN PFILE_LOCK FileLock
+ )
+{
+ KIRQL oldirql;
+ PFILE_LOCK_TOC LockToc;
+ PFILE_LOCK_GRANTED Granted;
+ PIRP Irp;
+ PLIST_ENTRY EnumEntry;
+ PEXTENDED_IO_STACK_LOCATION Stack;
+
+ assert(FileLock);
+ LockToc = FileLock->LockInformation;
+
+ if (LockToc == NULL)
+ {
+ DPRINT1("No file locks\n");
+ return;
+ }
+
+ DPRINT1("Dumping granted file locks, FIFO order\n");
+
+ KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
+
+ EnumEntry = LockToc->GrantedListHead.Blink;
+ while ( EnumEntry != &LockToc->GrantedListHead)
+ {
+ Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED , ListEntry );
+
+ DPRINT1("%s, start: %i, len: %i, end: %i, key: %i, proc: 0x%X, fob: 0x%X\n",
+ Granted->Lock.ExclusiveLock ? "EXCL" : "SHRD",
+ Granted->Lock.StartingByte.QuadPart,
+ Granted->Lock.Length.QuadPart,
+ Granted->Lock.EndingByte.QuadPart,
+ Granted->Lock.Key,
+ Granted->Lock.Process,
+ Granted->Lock.FileObject
+ );
+
+ EnumEntry = EnumEntry->Blink;
+ }
+
+ DPRINT1("Dumping pending file locks, FIFO order\n");
+
+ EnumEntry = LockToc->PendingListHead.Blink;
+ while ( EnumEntry != &LockToc->PendingListHead)
+ {
+ Irp = CONTAINING_RECORD(EnumEntry, IRP , Tail.Overlay.ListEntry );
+
+ Stack = (PEXTENDED_IO_STACK_LOCATION) IoGetCurrentIrpStackLocation(Irp);
+
+ DPRINT1("%s, start: %i, len: %i, end: %i, key: %i, proc: 0x%X, fob: 0x%X\n",
+ (Stack->Flags & SL_EXCLUSIVE_LOCK) ? "EXCL" : "SHRD",
+ Stack->Parameters.LockControl.ByteOffset.QuadPart,
+ Stack->Parameters.LockControl.Length->QuadPart,
+ Stack->Parameters.LockControl.ByteOffset.QuadPart + Stack->Parameters.LockControl.Length->QuadPart - 1,
+ Stack->Parameters.LockControl.Key,
+ IoGetRequestorProcess(Irp),
+ Stack->FileObject
+ );
+
+ EnumEntry = EnumEntry->Blink;
+ }
+
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+}
+
+
+
+/**********************************************************************
+ * NAME EXPORTED
+ * FsRtlGetNextFileLock
*
* RETURN VALUE
* NULL if no more locks.
*
- * NOTE (Bo Branten)
- * Internals: FsRtlGetNextFileLock uses
- * FileLock->LastReturnedLockInfo and FileLock->LastReturnedLock
- * as storage. LastReturnedLock is a pointer to the 'raw' lock
- * inkl. double linked list, and FsRtlGetNextFileLock needs this
- * to get next lock on subsequent calls with Restart = FALSE.
+ * @implemented
*/
PFILE_LOCK_INFO
STDCALL
FsRtlGetNextFileLock (
- IN PFILE_LOCK FileLock,
- IN BOOLEAN Restart
- )
+ IN PFILE_LOCK FileLock,
+ IN BOOLEAN Restart
+ )
{
- return (NULL);
+ /*
+ Messy enumeration of granted locks.
+ What our last ptr. in LastReturnedLock points at, might have been freed between
+ calls, so we have to scan thru the list every time, searching for our last lock.
+ If it's not there anymore, restart the enumeration...
+ */
+ KIRQL oldirql;
+ PLIST_ENTRY EnumEntry;
+ PFILE_LOCK_GRANTED Granted;
+ PFILE_LOCK_TOC LockToc;
+ BOOLEAN FoundPrevious = FALSE;
+ //must make local copy since FILE_LOCK struct is allowed to be in paged mem
+ FILE_LOCK_INFO LocalLastReturnedLockInfo;
+ PVOID LocalLastReturnedLock;
+
+ assert(FileLock);
+ LockToc = FileLock->LockInformation;
+ if (LockToc == NULL)
+ {
+ return NULL;
+ }
+
+ LocalLastReturnedLock = FileLock->LastReturnedLock;
+
+ KeAcquireSpinLock(&LockToc->SpinLock,&oldirql);
+
+restart:;
+
+ EnumEntry = LockToc->GrantedListHead.Flink;
+
+ if (Restart)
+ {
+ if (EnumEntry != &LockToc->GrantedListHead)
+ {
+ Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
+ LocalLastReturnedLockInfo = Granted->Lock;
+ KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
+
+ FileLock->LastReturnedLockInfo = LocalLastReturnedLockInfo;
+ FileLock->LastReturnedLock = EnumEntry;
+ return &FileLock->LastReturnedLockInfo;
+ }
+ else
+ {
+ KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
+ return NULL;
+ }
+ }
+
+ //else: continue enum
+ while (EnumEntry != &LockToc->GrantedListHead)
+ {
+ //found previous lock?
+ if (EnumEntry == LocalLastReturnedLock)
+ {
+ FoundPrevious = TRUE;
+ //get next
+ EnumEntry = EnumEntry->Flink;
+ if (EnumEntry != &LockToc->GrantedListHead)
+ {
+ Granted = CONTAINING_RECORD(EnumEntry,FILE_LOCK_GRANTED,ListEntry);
+ LocalLastReturnedLockInfo = Granted->Lock;
+ KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
+
+ FileLock->LastReturnedLockInfo = LocalLastReturnedLockInfo;
+ FileLock->LastReturnedLock = EnumEntry;
+ return &FileLock->LastReturnedLockInfo;
+ }
+ break;
+ }
+ EnumEntry = EnumEntry->Flink;
+ }
+
+ if (!FoundPrevious)
+ {
+ //got here? uh no, didn't find our last lock..must have been freed...restart
+ Restart = TRUE;
+ goto restart;
+ }
+
+ KeReleaseSpinLock(&LockToc->SpinLock,oldirql);
+
+ return NULL;//no (more) locks
}
/**********************************************************************
* NAME EXPORTED
- * FsRtlInitializeFileLock@12
+ * FsRtlInitializeFileLock
*
- * DESCRIPTION
- *
- * ARGUMENTS
- *
- * RETURN VALUE
+ * NOTE
+ * Called when creating/allocating/initializing FCB
*
+ * @implemented
*/
VOID
STDCALL
FsRtlInitializeFileLock (
- IN PFILE_LOCK FileLock,
- IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
- IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
- )
+ IN PFILE_LOCK FileLock,
+ IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
+ IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
+ )
{
+
+ FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
+ FileLock->CompleteLockIrpRoutine = CompleteLockIrpRoutine;
+ FileLock->UnlockRoutine = UnlockRoutine;
+ FileLock->LockInformation = NULL;
+
}
/**********************************************************************
* NAME EXPORTED
- * FsRtlPrivateLock@48
+ * FsRtlPrivateLock
*
- * DESCRIPTION
- *
- * ARGUMENTS
- *
- * RETURN VALUE
- * IoStatus->Status: STATUS_PENDING, STATUS_LOCK_NOT_GRANTED
- *
- * NOTE (Bo Branten)
- * -Calls IoCompleteRequest if Irp
- * -Uses exception handling / ExRaiseStatus with
- * STATUS_INSUFFICIENT_RESOURCES
+ * @implemented
*/
BOOLEAN
STDCALL
FsRtlPrivateLock (
- IN PFILE_LOCK FileLock,
- IN PFILE_OBJECT FileObject,
- IN PLARGE_INTEGER FileOffset,
- IN PLARGE_INTEGER Length,
- IN PEPROCESS Process,
- IN ULONG Key,
- IN BOOLEAN FailImmediately,
- IN BOOLEAN ExclusiveLock,
- OUT PIO_STATUS_BLOCK IoStatus,
- IN PIRP Irp OPTIONAL,
- IN PVOID Context,
- IN BOOLEAN AlreadySynchronized
- )
+ IN PFILE_LOCK FileLock,
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN PLARGE_INTEGER Length,
+ IN PEPROCESS Process,
+ IN ULONG Key,
+ IN BOOLEAN FailImmediately, //seems meaningless for fast io
+ IN BOOLEAN ExclusiveLock,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PIRP Irp OPTIONAL,
+ IN PVOID Context,
+ IN BOOLEAN AlreadySynchronized
+ )
{
- return FALSE;
+ PFILE_LOCK_TOC LockToc;
+ KIRQL oldirql;
+
+ assert(FileLock);
+ if (FileLock->LockInformation == NULL)
+ {
+ ExAcquireFastMutex(&LockTocMutex);
+ //still NULL?
+ if (FileLock->LockInformation == NULL)
+ {
+ FileLock->LockInformation = ExAllocateFromNPagedLookasideList(&LockTocLookaside);
+ LockToc = FileLock->LockInformation;
+ KeInitializeSpinLock(&LockToc->SpinLock);
+ InitializeListHead(&LockToc->GrantedListHead);
+ InitializeListHead(&LockToc->PendingListHead);
+ InitializeListHead(&LockToc->CompletedListHead);
+ InitializeListHead(&LockToc->UnlockedListHead);
+ }
+ ExReleaseFastMutex(&LockTocMutex);
+ }
+
+ LockToc = FileLock->LockInformation;
+ KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
+
+ //try add new lock (while holding spin lock)
+ if (FsRtlpAddLock(LockToc,
+ FileObject,
+ FileOffset,
+ Length,
+ Process,
+ Key,
+ ExclusiveLock
+ ) )
+ {
+ IoStatus->Status = STATUS_SUCCESS;
+ }
+ else if (Irp && !FailImmediately)
+ { //failed + irp + no fail = mk. pending
+ //for our cancel routine
+ Irp->Tail.Overlay.DriverContext[0] = (PVOID)FileLock;
+ Irp->Tail.Overlay.DriverContext[1] = (PVOID)LockToc;
+ Irp->Tail.Overlay.DriverContext[2] = Context;
+
+ IoSetCancelRoutine(Irp, FsRtlpFileLockCancelRoutine);
+
+ if (Irp->Cancel)
+ {
+ //irp canceled even before we got to queue it
+ if (IoSetCancelRoutine(Irp, NULL))
+ { //Cancel routine will NOT be called: cancel it here
+ IoStatus->Status = STATUS_CANCELLED;
+ }
+ else
+ { //Cancel routine WILL be called. When we release the lock it will complete the irp
+ //Return pending since we are not completing the irp here
+ Irp->IoStatus.Status = IoStatus->Status = STATUS_PENDING;
+ Irp->IoStatus.Information = 0;
+ InitializeListHead(&Irp->Tail.Overlay.ListEntry);
+ }
+
+ }
+ else
+ { //not cancelled: queue irp
+ IoMarkIrpPending(Irp);
+ Irp->IoStatus.Status = IoStatus->Status = STATUS_PENDING;
+ Irp->IoStatus.Information = 0;
+ InsertHeadList(&LockToc->PendingListHead,&Irp->Tail.Overlay.ListEntry);
+ }
+
+ }
+ else
+ {
+ IoStatus->Status = STATUS_LOCK_NOT_GRANTED;
+ }
+
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql); //fires cancel routine
+
+ //never pending if no irp;-)
+ assert(!(IoStatus->Status == STATUS_PENDING && !Irp));
+
+ if (IoStatus->Status != STATUS_PENDING)
+ {
+ if (IoStatus->Status == STATUS_SUCCESS)
+ {
+ FsRtlAreThereCurrentFileLocks(FileLock) = TRUE;
+ }
+
+ if (Irp)
+ {
+ Irp->IoStatus.Status = IoStatus->Status;
+ Irp->IoStatus.Information = 0;
+
+ if (FileLock->CompleteLockIrpRoutine)
+ { //complete irp routine
+
+ if (!NT_SUCCESS(FileLock->CompleteLockIrpRoutine(Context,Irp)))
+ {
+ //CompleteLockIrpRoutine complain: revert changes
+ FsRtlpUnlockSingle( FileLock,
+ FileObject,
+ FileOffset,
+ Length,
+ Process,
+ Key,
+ Context,
+ AlreadySynchronized,
+ FALSE//CallUnlockRoutine
+ );
+ }
+ }
+ else
+ {//std irp completion
+ IofCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+ }
+ }
+
+ //NOTE: only fast io seems to care about this return value
+ return (IoStatus->Status == STATUS_SUCCESS || FailImmediately);
+
}
+
/**********************************************************************
* NAME EXPORTED
- * FsRtlProcessFileLock@12
- *
- * DESCRIPTION
- *
- * ARGUMENTS
- *
- * RETURN VALUE
- * -STATUS_INVALID_DEVICE_REQUEST
- * -STATUS_RANGE_NOT_LOCKED from unlock routines.
- * -STATUS_PENDING, STATUS_LOCK_NOT_GRANTED from FsRtlPrivateLock
- * (redirected IoStatus->Status).
- *
- * NOTE (Bo Branten)
- * -switch ( Irp->CurrentStackLocation->MinorFunction )
- * lock: return FsRtlPrivateLock;
- * unlocksingle: return FsRtlFastUnlockSingle;
- * unlockall: return FsRtlFastUnlockAll;
- * unlockallbykey: return FsRtlFastUnlockAllByKey;
- * default: IofCompleteRequest with STATUS_INVALID_DEVICE_REQUEST;
- * return STATUS_INVALID_DEVICE_REQUEST;
+ * FsRtlProcessFileLock
*
- * -'AllwaysZero' is passed thru as 'AllwaysZero' to lock / unlock routines.
- * -'Irp' is passet thru as 'Irp' to FsRtlPrivateLock.
+ * @implemented
*/
NTSTATUS
STDCALL
FsRtlProcessFileLock (
- IN PFILE_LOCK FileLock,
- IN PIRP Irp,
- IN PVOID Context OPTIONAL
- )
+ IN PFILE_LOCK FileLock,
+ IN PIRP Irp,
+ IN PVOID Context OPTIONAL
+ )
{
- return (STATUS_NOT_IMPLEMENTED);
+ PEXTENDED_IO_STACK_LOCATION Stack;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK LocalIoStatus;
+
+ assert(FileLock);
+ Stack = (PEXTENDED_IO_STACK_LOCATION) IoGetCurrentIrpStackLocation(Irp);
+ Irp->IoStatus.Information = 0;
+
+ switch(Stack->MinorFunction)
+ {
+ case IRP_MN_LOCK:
+ //ret: BOOLEAN
+ FsRtlPrivateLock( FileLock,
+ Stack->FileObject,
+ &Stack->Parameters.LockControl.ByteOffset, //not pointer!
+ Stack->Parameters.LockControl.Length,
+ IoGetRequestorProcess(Irp),
+ Stack->Parameters.LockControl.Key,
+ Stack->Flags & SL_FAIL_IMMEDIATELY,
+ Stack->Flags & SL_EXCLUSIVE_LOCK,
+ &LocalIoStatus,
+ Irp,
+ Context,
+ FALSE);
+
+ return LocalIoStatus.Status;
+
+ case IRP_MN_UNLOCK_SINGLE:
+ Status = FsRtlFastUnlockSingle ( FileLock,
+ Stack->FileObject,
+ &Stack->Parameters.LockControl.ByteOffset,
+ Stack->Parameters.LockControl.Length,
+ IoGetRequestorProcess(Irp),
+ Stack->Parameters.LockControl.Key,
+ Context,
+ FALSE);
+ break;
+
+ case IRP_MN_UNLOCK_ALL:
+ Status = FsRtlFastUnlockAll( FileLock,
+ Stack->FileObject,
+ IoGetRequestorProcess(Irp),
+ Context);
+ break;
+
+ case IRP_MN_UNLOCK_ALL_BY_KEY:
+ Status = FsRtlFastUnlockAllByKey ( FileLock,
+ Stack->FileObject,
+ IoGetRequestorProcess(Irp),
+ Stack->Parameters.LockControl.Key,
+ Context);
+
+ break;
+
+ default:
+ Irp->IoStatus.Status = Status = STATUS_INVALID_DEVICE_REQUEST;
+ IofCompleteRequest(Irp, IO_NO_INCREMENT);
+ return Status;
+ }
+
+ Irp->IoStatus.Status = Status;
+
+ if (FileLock->CompleteLockIrpRoutine )
+ {
+ FileLock->CompleteLockIrpRoutine(Context,Irp);
+ }
+ else
+ {
+ IofCompleteRequest(Irp,IO_NO_INCREMENT);
+ }
+
+ return Status;
}
/**********************************************************************
* NAME EXPORTED
- * FsRtlUninitializeFileLock@4
- *
- * DESCRIPTION
- *
- * ARGUMENTS
- *
- * RETURN VALUE
+ * FsRtlUninitializeFileLock
*
+ * @implemented
*/
VOID
STDCALL
FsRtlUninitializeFileLock (
- IN PFILE_LOCK FileLock
- )
+ IN PFILE_LOCK FileLock
+ )
{
+ PFILE_LOCK_TOC LockToc;
+ PIRP Irp;
+ PFILE_LOCK_GRANTED Granted;
+ PLIST_ENTRY EnumEntry;
+ KIRQL oldirql;
+
+ assert(FileLock);
+ if (FileLock->LockInformation == NULL)
+ {
+ return;
+ }
+
+ LockToc = FileLock->LockInformation;
+
+ KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
+
+ //remove and free granted locks
+ while (!IsListEmpty(&LockToc->GrantedListHead))
+ {
+ EnumEntry = RemoveTailList(&LockToc->GrantedListHead);
+ Granted = CONTAINING_RECORD(EnumEntry, FILE_LOCK_GRANTED, ListEntry);
+ ExFreeToNPagedLookasideList(&GrantedLookaside, Granted);
+ }
+
+ //remove, complete and free all pending locks
+ while (!IsListEmpty(&LockToc->PendingListHead))
+ {
+ EnumEntry = RemoveTailList(&LockToc->PendingListHead);
+ Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry);
+
+ if (!IoSetCancelRoutine(Irp, NULL))
+ {
+ //The cancel routine will be called. When we release the lock it will complete the irp.
+ InitializeListHead(&Irp->Tail.Overlay.ListEntry);
+ }
+ else
+ {
+ /*
+ Cancel routine will NOT be called, even though the irp might have been canceled.
+ Don't care since we'l complete it faster than the cancel routine would have.
+ */
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);//fires cancel routine
+
+ Irp->IoStatus.Status = STATUS_RANGE_NOT_LOCKED;
+
+ if (FileLock->CompleteLockIrpRoutine)
+ {
+ FileLock->CompleteLockIrpRoutine(Irp->Tail.Overlay.DriverContext[2], Irp);
+ }
+ else
+ {
+ IofCompleteRequest(Irp, IO_NO_INCREMENT);
+ }
+
+ KeAcquireSpinLock(&LockToc->SpinLock, &oldirql);
+ }
+ }
+
+ KeReleaseSpinLock(&LockToc->SpinLock, oldirql);
+
+ ExFreeToNPagedLookasideList(&LockTocLookaside, LockToc);
+
+ FsRtlAreThereCurrentFileLocks(FileLock) = FALSE;
+ FileLock->LockInformation = NULL;
+
}
/**********************************************************************
* NAME EXPORTED
- * FsRtlAllocateFileLock@8
+ * FsRtlAllocateFileLock
*
- * DESCRIPTION
+ * NOTE
* Only present in NT 5.0 or later.
- *
- * ARGUMENTS
- *
- * RETURN VALUE
+ * FCB FILE_LOCK struct should/is acording to DDK allocated from paged pool!
*
+ * @implemented
*/
PFILE_LOCK
STDCALL
-FsRtlAllocateFileLock (
- IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
- IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
- )
+FsRtlAllocateFileLock(
+ IN PCOMPLETE_LOCK_IRP_ROUTINE CompleteLockIrpRoutine OPTIONAL,
+ IN PUNLOCK_ROUTINE UnlockRoutine OPTIONAL
+ )
{
- return NULL;
+ PFILE_LOCK FileLock;
+
+ FileLock = ExAllocateFromPagedLookasideList(&LockLookaside);
+
+ FsRtlInitializeFileLock(FileLock,
+ CompleteLockIrpRoutine,
+ UnlockRoutine
+ );
+
+ return FileLock;
+}
+
+/**********************************************************************
+ * NAME EXPORTED
+ * FsRtlFreeFileLock
+ *
+ * NOTE
+ * Only present in NT 5.0 or later.
+ * FCB FILE_LOCK struct should/is acording to DDK allocated from paged pool!
+ *
+ * @implemented
+ */
+VOID
+STDCALL
+FsRtlFreeFileLock(
+ IN PFILE_LOCK FileLock
+ )
+{
+ assert(FileLock);
+
+ FsRtlUninitializeFileLock(FileLock);
+ ExFreeToPagedLookasideList(&LockLookaside, FileLock);
}
/* EOF */