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