sys: create: sharing violation oplock checks

This commit is contained in:
Bill Zissimopoulos 2016-11-11 16:52:21 -08:00
parent a9b4fd4634
commit 74de84aaab

View File

@ -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)
{ {