mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 16:33:02 -05:00
sys: create: sharing violation oplock checks
This commit is contained in:
parent
a9b4fd4634
commit
74de84aaab
204
src/sys/create.c
204
src/sys/create.c
@ -35,13 +35,9 @@ static VOID FspFsvolCreatePostClose(FSP_FILE_DESC *FileDesc);
|
||||
static FSP_IOP_REQUEST_FINI FspFsvolCreateRequestFini;
|
||||
static FSP_IOP_REQUEST_FINI FspFsvolCreateTryOpenRequestFini;
|
||||
static FSP_IOP_REQUEST_FINI FspFsvolCreateOverwriteRequestFini;
|
||||
static NTSTATUS FspFsvolCreateSharingViolation(
|
||||
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response, FSP_FILE_NODE *FileNode);
|
||||
static NTSTATUS FspFsvolCreateSharingViolationWork(
|
||||
static NTSTATUS FspFsvolCreateSharingViolationCheckOplock(
|
||||
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||
BOOLEAN CanWait);
|
||||
static VOID FspFsvolCreateSharingViolationOplockComplete(
|
||||
PVOID Context, PIRP Irp);
|
||||
static BOOLEAN FspFsvolCreateCheckOplock(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response,
|
||||
PNTSTATUS PResult);
|
||||
static VOID FspFsvolCreateCheckOplockPrepare(
|
||||
@ -62,9 +58,7 @@ FSP_DRIVER_DISPATCH FspCreate;
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateRequestFini)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateTryOpenRequestFini)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateOverwriteRequestFini)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateSharingViolation)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateSharingViolationWork)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateSharingViolationOplockComplete)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateSharingViolationCheckOplock)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateCheckOplock)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateCheckOplockPrepare)
|
||||
#pragma alloc_text(PAGE, FspFsvolCreateCheckOplockComplete)
|
||||
@ -848,15 +842,13 @@ NTSTATUS FspFsvolCreateComplete(
|
||||
{
|
||||
if (STATUS_SHARING_VIOLATION == Result)
|
||||
{
|
||||
Result = FspFsvolCreateSharingViolation(Irp, Response, OpenedFileNode);
|
||||
if (STATUS_PENDING != Result)
|
||||
{
|
||||
FspFileNodeClose(OpenedFileNode, 0, TRUE);
|
||||
FspFileNodeDereference(OpenedFileNode);
|
||||
Result = STATUS_SHARING_VIOLATION;
|
||||
}
|
||||
FspIopSetIrpResponse(Irp, Response);
|
||||
FspIopRequestContext(Request, FspIopRequestExtraContext) = FileNode;
|
||||
|
||||
Irp->IoStatus.Information = 0;
|
||||
Result = FspFsvolCreateSharingViolationCheckOplock(
|
||||
FsvolDeviceObject, Irp, IrpSp, FALSE);
|
||||
|
||||
FSP_RETURN();
|
||||
}
|
||||
|
||||
@ -1115,6 +1107,13 @@ static VOID FspFsvolCreateRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Con
|
||||
FSP_FILE_DESC *FileDesc = Context[RequestFileDesc];
|
||||
HANDLE AccessToken = Context[RequestAccessToken];
|
||||
PEPROCESS Process = Context[RequestProcess];
|
||||
FSP_FILE_NODE *ExtraFileNode = FspIopRequestContext(Request, FspIopRequestExtraContext);
|
||||
|
||||
if (0 != ExtraFileNode)
|
||||
{
|
||||
FspFileNodeClose(ExtraFileNode, 0, TRUE);
|
||||
FspFileNodeDereference(ExtraFileNode);
|
||||
}
|
||||
|
||||
if (0 != FileDesc)
|
||||
{
|
||||
@ -1199,129 +1198,120 @@ static VOID FspFsvolCreateOverwriteRequestFini(FSP_FSCTL_TRANSACT_REQ *Request,
|
||||
FspFsvolDeviceFileRenameReleaseOwner(FsvolDeviceObject, Request);
|
||||
}
|
||||
|
||||
static NTSTATUS FspFsvolCreateSharingViolation(
|
||||
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response, FSP_FILE_NODE *FileNode)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
NTSTATUS Result;
|
||||
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
|
||||
|
||||
FspIopSetIrpResponse(Irp, Response);
|
||||
FspIopRequestContext(Request, FspIopRequestExtraContext) = FileNode;
|
||||
|
||||
Result = FspWqRepostIrpWorkItem(Irp,
|
||||
FspFsvolCreateSharingViolationWork, FspFsvolCreateRequestFini);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
FspIopRequestContext(Request, FspIopRequestExtraContext) = 0;
|
||||
return STATUS_SHARING_VIOLATION;
|
||||
}
|
||||
|
||||
return STATUS_PENDING;
|
||||
}
|
||||
|
||||
static NTSTATUS FspFsvolCreateSharingViolationWork(
|
||||
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||
static NTSTATUS FspFsvolCreateSharingViolationCheckOplock(
|
||||
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||
BOOLEAN CanWait)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
/*
|
||||
* Perform oplock checks in the case that we received STATUS_SHARING_VIOLATION.
|
||||
* The logic below is quite tortured, because it tries to mimic what FastFat
|
||||
* does in Win7. Here is an explanation.
|
||||
* The logic below is rather tortured, because it tries to mimic what FastFat
|
||||
* does in Win7 when a sharing violation happens. Here is an explanation.
|
||||
*
|
||||
* First notice that this is run in a work thread. So it is assumed that we can
|
||||
* block here. We cannot block in the completion thread, because it belongs to
|
||||
* the user mode file system that may have a limited number of threads to service
|
||||
* file system requests.
|
||||
* When this routine first gets called the CanWait parameter is FALSE. This is
|
||||
* because it gets called in the context of a completion thread, which belongs
|
||||
* to the user mode file system. This means that we cannot block this thread
|
||||
* because the user mode file system may have a limited number of threads to
|
||||
* service file system requests.
|
||||
*
|
||||
* Second we want a way to differentiate the case between an oplock broken or
|
||||
* underway and no oplock needed. Unfortunately this only seems possible when a
|
||||
* completion routine is set in FsRtlCheckOplock and FsRtlOplockBreakH. In this
|
||||
* case we get STATUS_PENDING when there is an oplock to be broken and
|
||||
* STATUS_SUCCESS when there is not one.
|
||||
* When CanWait is FALSE the routine checks whether there are any oplocks on
|
||||
* this file. If there are none it simply returns STATUS_SHARING_VIOLATION.
|
||||
* Otherwise it posts a work item so that the routine can be executed in the
|
||||
* context of a worker thread. When executed in the context of a worker thread
|
||||
* the CanWait parameter is TRUE.
|
||||
*
|
||||
* Third notice that by default we must return STATUS_SHARING_VIOLATION, because
|
||||
* the share access check failed. The only case that we will completely retry is
|
||||
* when FspRtlOplockBreakH returns STATUS_PENDING.
|
||||
* When CanWait is TRUE the routine is free to wait for any oplocks breaks. We
|
||||
* try to break both batch and CACHE_HANDLE_LEVEL oplocks and return the
|
||||
* appropriate status code.
|
||||
*
|
||||
* So in a nutshell, this routine emulates what happens with FastFat during
|
||||
* STATUS_SHARING_VIOLATION processing. However FastFat tries to break batch
|
||||
* oplocks before the share access check. Unfortunately this is hard to do in
|
||||
* this FSD, so we have to emulate this behavior.
|
||||
* We acquire the FileNode shared so allow oplock breaks to happen concurrently.
|
||||
* See https://www.osronline.com/showthread.cfm?link=248984
|
||||
*/
|
||||
|
||||
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
|
||||
FSP_FSCTL_TRANSACT_RSP *Response = FspIopIrpResponse(Irp);
|
||||
FSP_FSCTL_TRANSACT_RSP *Response;
|
||||
FSP_FILE_NODE *FileNode = FspIopRequestContext(Request, FspIopRequestExtraContext);
|
||||
BOOLEAN StatusSharingViolation = TRUE, OpbatchBreakUnderway = FALSE;
|
||||
KEVENT Event;
|
||||
BOOLEAN OpbatchBreakUnderway = FALSE;
|
||||
NTSTATUS Result;
|
||||
BOOLEAN Success;
|
||||
|
||||
FspFileNodeAcquireShared(FileNode, Main);
|
||||
Success = DEBUGTEST(90) &&
|
||||
FspFileNodeTryAcquireSharedF(FileNode, FspFileNodeAcquireMain, CanWait);
|
||||
if (!Success)
|
||||
return FspWqRepostIrpWorkItem(Irp,
|
||||
FspFsvolCreateSharingViolationCheckOplock, FspFsvolCreateRequestFini);
|
||||
|
||||
Result = FspCheckOplockEx(FspFileNodeAddrOfOplock(FileNode), Irp,
|
||||
OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY, 0, 0, 0);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||||
|
||||
if (FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(FileNode)))
|
||||
if (!CanWait)
|
||||
{
|
||||
Result = FspCheckOplock(FspFileNodeAddrOfOplock(FileNode), Irp,
|
||||
&Event, FspFsvolCreateSharingViolationOplockComplete, 0);
|
||||
if (STATUS_PENDING == Result)
|
||||
{
|
||||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0);
|
||||
if (STATUS_SUCCESS == Irp->IoStatus.Status)
|
||||
OpbatchBreakUnderway = TRUE;
|
||||
/*
|
||||
* If there is no batch or CACHE_HANDLE_LEVEL oplock we are done;
|
||||
* else retry in a worker thread to break the oplocks.
|
||||
*/
|
||||
Success = !FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(FileNode)) &&
|
||||
(FlagOn(IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED) ||
|
||||
!FsRtlCurrentOplockH(FspFileNodeAddrOfOplock(FileNode)));
|
||||
|
||||
KeResetEvent(&Event);
|
||||
}
|
||||
}
|
||||
|
||||
if (!FlagOn(IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED))
|
||||
{
|
||||
Result = FspOplockBreakH(FspFileNodeAddrOfOplock(FileNode), Irp, 0,
|
||||
&Event, FspFsvolCreateSharingViolationOplockComplete, 0);
|
||||
if (STATUS_PENDING == Result)
|
||||
{
|
||||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0);
|
||||
if (STATUS_SUCCESS == Irp->IoStatus.Status)
|
||||
StatusSharingViolation = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
FspFileNodeRelease(FileNode, Main);
|
||||
|
||||
FspFileNodeClose(FileNode, 0, TRUE);
|
||||
FspFileNodeDereference(FileNode);
|
||||
FspIopRequestContext(Request, FspIopRequestExtraContext) = 0;
|
||||
if (!Success)
|
||||
return FspWqRepostIrpWorkItem(Irp,
|
||||
FspFsvolCreateSharingViolationCheckOplock, FspFsvolCreateRequestFini);
|
||||
|
||||
if (StatusSharingViolation)
|
||||
{
|
||||
Irp->IoStatus.Information = OpbatchBreakUnderway ? FILE_OPBATCH_BREAK_UNDERWAY : 0;
|
||||
return STATUS_SHARING_VIOLATION;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if 0
|
||||
/* ???: is this needed in this case? */
|
||||
Result = FspCheckOplockEx(FspFileNodeAddrOfOplock(FileNode), Irp,
|
||||
OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY, 0, 0, 0);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
Result = STATUS_SHARING_VIOLATION;
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(FileNode)))
|
||||
{
|
||||
/* wait for batch oplock break to complete */
|
||||
Result = FspCheckOplock(FspFileNodeAddrOfOplock(FileNode), Irp,
|
||||
0, 0, 0);
|
||||
if (STATUS_SUCCESS == Result)
|
||||
OpbatchBreakUnderway = TRUE;
|
||||
}
|
||||
|
||||
Result = STATUS_SHARING_VIOLATION;
|
||||
|
||||
if (!FlagOn(IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED) &&
|
||||
FsRtlCurrentOplockH(FspFileNodeAddrOfOplock(FileNode)))
|
||||
{
|
||||
/* wait for CACHE_HANDLE_LEVEL oplock break to complete */
|
||||
Result = FspOplockBreakH(FspFileNodeAddrOfOplock(FileNode), Irp,
|
||||
0, 0, 0, 0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
exit:
|
||||
#endif
|
||||
FspFileNodeRelease(FileNode, Main);
|
||||
|
||||
Response = FspIopIrpResponse(Irp);
|
||||
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
Response->IoStatus.Status = (UINT32)Result;
|
||||
Response->IoStatus.Information =
|
||||
STATUS_SHARING_VIOLATION == Result && OpbatchBreakUnderway ?
|
||||
FILE_OPBATCH_BREAK_UNDERWAY : 0;
|
||||
}
|
||||
|
||||
FspIopRetryCompleteIrp(Irp, Response, &Result);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
static VOID FspFsvolCreateSharingViolationOplockComplete(
|
||||
PVOID Context, PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
KeSetEvent((PKEVENT)Context, FSP_IO_INCREMENT, FALSE);
|
||||
}
|
||||
|
||||
static BOOLEAN FspFsvolCreateCheckOplock(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response,
|
||||
PNTSTATUS PResult)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user