From 8395b22ddc2795786510151704aa82e5f59db5f3 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 7 Nov 2016 20:42:32 -0800 Subject: [PATCH] sys: oplocks: WIP --- src/sys/driver.h | 17 ++++ src/sys/file.c | 2 + src/sys/fsctl.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++ src/sys/util.c | 42 ++++++++++ 4 files changed, 275 insertions(+) diff --git a/src/sys/driver.h b/src/sys/driver.h index d86f51c7..b4027313 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -514,6 +514,11 @@ NTSTATUS FspNotifyFullReportChange( ULONG FilterMatch, ULONG Action, PVOID TargetContext); +NTSTATUS FspOplockFsctrlEx( + POPLOCK Oplock, + PIRP Irp, + ULONG OpenCount, + BOOLEAN Create); #define FspNotifyUninitializeSync(NS)\ FsRtlNotifyUninitializeSync(NS) #define FspNotifyCleanupAll(NS, NL)\ @@ -526,6 +531,10 @@ NTSTATUS FspNotifyFullReportChange( FspNotifyFullChangeDirectory(NS, NL, FC, 0, 0, FALSE, 0, 0, 0, 0) #define FspNotifyReportChange(NS, NL, FN, FO, NP, F, A)\ FspNotifyFullReportChange(NS, NL, (PSTRING)(FN), FO, 0, (PSTRING)(NP), F, A, 0) +#define FspOplockFsctrlCreate(OL, I, OC)\ + FspOplockFsctrlEx(OL, I, OC, TRUE) +#define FspOplockFsctrl(OL, I, OC)\ + FspOplockFsctrlEx(OL, I, OC, FALSE) /* utility: synchronous work queue */ typedef struct @@ -983,6 +992,9 @@ typedef struct FSP_FILE_NODE ULONG StreamInfoChangeNumber; BOOLEAN TruncateOnClose; FILE_LOCK FileLock; +#if (NTDDI_VERSION < NTDDI_WIN8) + OPLOCK Oplock; +#endif struct { PVOID LazyWriteThread; @@ -1174,6 +1186,11 @@ BOOLEAN FspMainFileOpenCheck(PIRP Irp) #define FspFileNodeDereferenceDirInfo(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeDereferenceStreamInfo(P) FspMetaCacheDereferenceItemBuffer(P) #define FspFileNodeUnlockAll(N,F,P) FsRtlFastUnlockAll(&(N)->FileLock, F, P, N) +#if (NTDDI_VERSION < NTDDI_WIN8) +#define FspFileNodeAddrOfOplock(N) (&(N)->Oplock) +#else +#define FspFileNodeAddrOfOplock(N) (&(N)->Header.Oplock) +#endif /* multiversion support */ typedef diff --git a/src/sys/file.c b/src/sys/file.c index 4b252b42..614b0ca2 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -218,6 +218,7 @@ NTSTATUS FspFileNodeCreate(PDEVICE_OBJECT DeviceObject, RtlInitEmptyUnicodeString(&FileNode->FileName, FileNode->FileNameBuf, (USHORT)ExtraSize); FsRtlInitializeFileLock(&FileNode->FileLock, FspFileNodeCompleteLockIrp, 0); + FsRtlInitializeOplock(FspFileNodeAddrOfOplock(FileNode)); *PFileNode = FileNode; @@ -231,6 +232,7 @@ VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode) FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject); + FsRtlUninitializeOplock(FspFileNodeAddrOfOplock(FileNode)); FsRtlUninitializeFileLock(&FileNode->FileLock); FsRtlTeardownPerStreamContexts(&FileNode->Header); diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c index c5eecfc9..d12499a4 100644 --- a/src/sys/fsctl.c +++ b/src/sys/fsctl.c @@ -25,6 +25,10 @@ static NTSTATUS FspFsvolFileSystemControlReparsePoint( static NTSTATUS FspFsvolFileSystemControlReparsePointComplete( PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response, BOOLEAN IsWrite); +static NTSTATUS FspFsvolFileSystemControlOplock( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +static IO_COMPLETION_ROUTINE FspFsvolFileSystemControlOplockCompletion; +static WORKER_THREAD_ROUTINE FspFsvolFileSystemControlOplockCompletionWork; static NTSTATUS FspFsvolFileSystemControl( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); FSP_IOCMPL_DISPATCH FspFsvolFileSystemControlComplete; @@ -35,6 +39,9 @@ FSP_DRIVER_DISPATCH FspFileSystemControl; #pragma alloc_text(PAGE, FspFsctlFileSystemControl) #pragma alloc_text(PAGE, FspFsvolFileSystemControlReparsePoint) #pragma alloc_text(PAGE, FspFsvolFileSystemControlReparsePointComplete) +#pragma alloc_text(PAGE, FspFsvolFileSystemControlOplock) +// !#pragma alloc_text(PAGE, FspFsvolFileSystemControlOplockCompletion) +#pragma alloc_text(PAGE, FspFsvolFileSystemControlOplockCompletionWork) #pragma alloc_text(PAGE, FspFsvolFileSystemControl) #pragma alloc_text(PAGE, FspFsvolFileSystemControlComplete) #pragma alloc_text(PAGE, FspFsvolFileSystemControlRequestFini) @@ -296,6 +303,202 @@ static NTSTATUS FspFsvolFileSystemControlReparsePointComplete( return STATUS_SUCCESS; } +typedef struct +{ + PDEVICE_OBJECT FsvolDeviceObject; + WORK_QUEUE_ITEM WorkItem; +} FSP_FSVOL_FILESYSTEM_CONTROL_OPLOCK_COMPLETION_CONTEXT; + +static NTSTATUS FspFsvolFileSystemControlOplock( + PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) +{ + PAGED_CODE(); + + PFILE_OBJECT FileObject = IrpSp->FileObject; + + /* is this a valid FileObject? */ + if (!FspFileNodeIsValid(FileObject->FsContext)) + return STATUS_INVALID_DEVICE_REQUEST; + + NTSTATUS Result; + FSP_FILE_NODE *FileNode = FileObject->FsContext; + ULONG FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer; + ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + ULONG OplockCount; + FSP_FSVOL_FILESYSTEM_CONTROL_OPLOCK_COMPLETION_CONTEXT *CompletionContext; + + if (FSCTL_REQUEST_OPLOCK == FsControlCode) + { + if (sizeof(REQUEST_OPLOCK_INPUT_BUFFER) > InputBufferLength || + sizeof(REQUEST_OPLOCK_OUTPUT_BUFFER) > OutputBufferLength) + return STATUS_BUFFER_TOO_SMALL; + + if (FileNode->IsDirectory && !FsRtlOplockIsSharedRequest(Irp)) + return STATUS_INVALID_PARAMETER; + } + else + { + if (FileNode->IsDirectory) + return STATUS_INVALID_PARAMETER; + } + + /* + * As per FastFat: + * + * We grab the Fcb exclusively for oplock requests, shared for oplock + * break acknowledgement. + */ + + switch (FsControlCode) + { + case FSCTL_REQUEST_OPLOCK_LEVEL_1: + case FSCTL_REQUEST_OPLOCK_LEVEL_2: + case FSCTL_REQUEST_BATCH_OPLOCK: + case FSCTL_REQUEST_FILTER_OPLOCK: + exclusive: + FspFileNodeAcquireExclusive(FileNode, Main); + if (!FsRtlOplockIsSharedRequest(Irp)) + { + FspFsvolDeviceLockContextTable(FsvolDeviceObject); + OplockCount = FileNode->HandleCount; + FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); + } + else + { + if (!FileNode->IsDirectory) + /* ???: Win8 and FsRtlCheckLockForOplockRequest? see FastFat */ + OplockCount = FsRtlAreThereCurrentOrInProgressFileLocks(&FileNode->FileLock); + else + OplockCount = 0; + } + if (FSCTL_REQUEST_FILTER_OPLOCK == FsControlCode || + FSCTL_REQUEST_BATCH_OPLOCK == FsControlCode || + (FSCTL_REQUEST_OPLOCK == FsControlCode && + FlagOn(((PREQUEST_OPLOCK_INPUT_BUFFER)InputBuffer)->RequestedOplockLevel, + OPLOCK_LEVEL_CACHE_HANDLE))) + { + BOOLEAN DeletePending; + + DeletePending = 0 != FileNode->DeletePending; + MemoryBarrier(); + if (DeletePending) + { + FspFileNodeRelease(FileNode, Main); + return STATUS_DELETE_PENDING; + } + } + break; + + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: + case FSCTL_OPBATCH_ACK_CLOSE_PENDING: + case FSCTL_OPLOCK_BREAK_NOTIFY: + case FSCTL_OPLOCK_BREAK_ACK_NO_2: + shared: + FspFileNodeAcquireShared(FileNode, Main); + OplockCount = 0; + break; + + case FSCTL_REQUEST_OPLOCK: + if (FlagOn(((PREQUEST_OPLOCK_INPUT_BUFFER)InputBuffer)->Flags, + REQUEST_OPLOCK_INPUT_FLAG_REQUEST)) + goto exclusive; + if (FlagOn(((PREQUEST_OPLOCK_INPUT_BUFFER)InputBuffer)->Flags, + REQUEST_OPLOCK_INPUT_FLAG_ACK)) + goto shared; + + /* one of REQUEST_OPLOCK_INPUT_FLAG_REQUEST or REQUEST_OPLOCK_INPUT_FLAG_ACK required */ + return STATUS_INVALID_PARAMETER; + + default: + ASSERT(0); + return STATUS_INVALID_DEVICE_REQUEST; + } + + /* + * The FileNode is acquired exclusive or shared. + * Make sure to release it before exiting! + */ + + /* + * This IRP will be completed by the FSRTL package and therefore + * we will have no chance to do our normal IRP completion processing. + * Hook the IRP completion and perform the IRP completion processing + * there. + */ + + CompletionContext = FspAllocNonPaged(sizeof *CompletionContext); + if (0 == CompletionContext) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto unlock_exit; + } + CompletionContext->FsvolDeviceObject = FsvolDeviceObject; + ExInitializeWorkItem(&CompletionContext->WorkItem, + FspFsvolFileSystemControlOplockCompletionWork, CompletionContext); + + Result = FspIrpHook(Irp, FspFsvolFileSystemControlOplockCompletion, CompletionContext); + if (!NT_SUCCESS(Result)) + { + FspFree(CompletionContext); + goto unlock_exit; + } + + /* + * It is possible for FspOplockFsctrl to complete the IRP immediately. + * In this case trying to access the IRP (to get its IrpFlags) in FspFileNodeRelease + * can lead to a bugcheck. For this reason we set the TopLevelIrp to NULL here. + * + * FspFsvolFileSystemControlOplock does not need the TopLevelIrp functionality, + * because it cannot be used recursively (I believe -- famous last words). + */ + PIRP TopLevelIrp = IoGetTopLevelIrp(); + IoSetTopLevelIrp(0); + + Result = FspOplockFsctrl(FspFileNodeAddrOfOplock(FileNode), Irp, OplockCount); + + FspFileNodeRelease(FileNode, Main); + + if (!NT_SUCCESS(Result)) + { + /* set back the top level IRP just in case! */ + IoSetTopLevelIrp(TopLevelIrp); + + FspIrpHookReset(Irp); + FspFree(CompletionContext); + return Result; + } + + return STATUS_PENDING; + +unlock_exit: + FspFileNodeRelease(FileNode, Main); + + return Result; +} + +static NTSTATUS FspFsvolFileSystemControlOplockCompletion( + PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) +{ + // !PAGED_CODE(); + + FSP_FSVOL_FILESYSTEM_CONTROL_OPLOCK_COMPLETION_CONTEXT *CompletionContext = + FspIrpHookContext(Context); + ExQueueWorkItem(&CompletionContext->WorkItem, DelayedWorkQueue); + + return FspIrpHookNext(DeviceObject, Irp, Context); +} + +static VOID FspFsvolFileSystemControlOplockCompletionWork(PVOID Context) +{ + PAGED_CODE(); + + FSP_FSVOL_FILESYSTEM_CONTROL_OPLOCK_COMPLETION_CONTEXT *CompletionContext = Context; + FspDeviceDereference(CompletionContext->FsvolDeviceObject); + FspFree(CompletionContext); +} + static NTSTATUS FspFsvolFileSystemControl( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp) { @@ -318,6 +521,17 @@ static NTSTATUS FspFsvolFileSystemControl( case FSCTL_DELETE_REPARSE_POINT: Result = FspFsvolFileSystemControlReparsePoint(FsvolDeviceObject, Irp, IrpSp, TRUE); break; + case FSCTL_REQUEST_OPLOCK_LEVEL_1: + case FSCTL_REQUEST_OPLOCK_LEVEL_2: + case FSCTL_REQUEST_BATCH_OPLOCK: + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: + case FSCTL_OPBATCH_ACK_CLOSE_PENDING: + case FSCTL_OPLOCK_BREAK_NOTIFY: + case FSCTL_OPLOCK_BREAK_ACK_NO_2: + case FSCTL_REQUEST_FILTER_OPLOCK: + case FSCTL_REQUEST_OPLOCK: + Result = FspFsvolFileSystemControlOplock(FsvolDeviceObject, Irp, IrpSp); + break; } break; } diff --git a/src/sys/util.c b/src/sys/util.c index a5b56234..4a889ea5 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -67,6 +67,11 @@ NTSTATUS FspNotifyFullReportChange( ULONG FilterMatch, ULONG Action, PVOID TargetContext); +NTSTATUS FspOplockFsctrlEx( + POPLOCK Oplock, + PIRP Irp, + ULONG OpenCount, + BOOLEAN Create); VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context); VOID FspExecuteSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem); @@ -104,6 +109,7 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); #pragma alloc_text(PAGE, FspNotifyInitializeSync) #pragma alloc_text(PAGE, FspNotifyFullChangeDirectory) #pragma alloc_text(PAGE, FspNotifyFullReportChange) +#pragma alloc_text(PAGE, FspOplockFsctrlEx) #pragma alloc_text(PAGE, FspInitializeSynchronousWorkItem) #pragma alloc_text(PAGE, FspExecuteSynchronousWorkItem) #pragma alloc_text(PAGE, FspExecuteSynchronousWorkItemRoutine) @@ -640,6 +646,42 @@ NTSTATUS FspNotifyFullReportChange( return Result; } +NTSTATUS FspOplockFsctrlEx( + POPLOCK Oplock, + PIRP Irp, + ULONG OpenCount, + BOOLEAN Create) +{ + PAGED_CODE(); + + NTSTATUS Result; + + try + { + ASSERT( + (Create && IRP_MJ_CREATE == IoGetCurrentIrpStackLocation(Irp)->MajorFunction) || + (!Create && IRP_MJ_FILE_SYSTEM_CONTROL == IoGetCurrentIrpStackLocation(Irp)->MajorFunction)); + + Result = FsRtlOplockFsctrl( + Oplock, + Irp, + OpenCount); + + /* + * When the IRP is IRP_MJ_FILE_SYSTEM_CONTROL, FsRtlOplockFsctrl always completes the IRP + * (unless it raises). So return STATUS_SUCCESS in that case. + */ + if (!Create) + Result = STATUS_SUCCESS; + } + except (EXCEPTION_EXECUTE_HANDLER) + { + Result = GetExceptionCode(); + } + + return Result; +} + VOID FspInitializeSynchronousWorkItem(FSP_SYNCHRONOUS_WORK_ITEM *SynchronousWorkItem, PWORKER_THREAD_ROUTINE Routine, PVOID Context) {