diff --git a/src/sys/close.c b/src/sys/close.c index 596999bc..d7b4821d 100644 --- a/src/sys/close.c +++ b/src/sys/close.c @@ -74,7 +74,7 @@ static NTSTATUS FspFsvolClose( Request->Req.Close.UserContext = FileNode->UserContext; Request->Req.Close.UserContext2 = FileDesc->UserContext2; - FspFileNodeClose(FileNode, FileObject, FALSE); + FspFileNodeClose(FileNode, FileObject, FALSE, FALSE); /* delete the FileDesc and deref the FileNode; order is important (FileDesc has FileNode ref) */ FspFileDescDelete(FileDesc); /* this will also close the MainFileObject if any */ diff --git a/src/sys/create.c b/src/sys/create.c index e5abeea4..9198fec9 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -28,6 +28,13 @@ static NTSTATUS FspFsvolCreateNoLock( BOOLEAN MainFileOpen); FSP_IOPREP_DISPATCH FspFsvolCreatePrepare; FSP_IOCMPL_DISPATCH FspFsvolCreateComplete; +static NTSTATUS FspFsvolCreateSharingViolation( + PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response, FSP_FILE_NODE *FileNode); +static NTSTATUS FspFsvolCreateSharingViolationWork( + PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, + BOOLEAN CanWait); +static VOID FspFsvolCreateSharingViolationOplockComplete( + PVOID Context, PIRP Irp); static NTSTATUS FspFsvolCreateTryOpen(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response, FSP_FILE_NODE *FileNode, FSP_FILE_DESC *FileDesc, PFILE_OBJECT FileObject, BOOLEAN FlushImage); @@ -45,6 +52,9 @@ FSP_DRIVER_DISPATCH FspCreate; #pragma alloc_text(PAGE, FspFsvolCreatePrepare) #pragma alloc_text(PAGE, FspFsvolCreateComplete) #pragma alloc_text(PAGE, FspFsvolCreateTryOpen) +#pragma alloc_text(PAGE, FspFsvolCreateSharingViolation) +#pragma alloc_text(PAGE, FspFsvolCreateSharingViolationWork) +#pragma alloc_text(PAGE, FspFsvolCreateSharingViolationOplockComplete) #pragma alloc_text(PAGE, FspFsvolCreatePostClose) #pragma alloc_text(PAGE, FspFsvolCreateRequestFini) #pragma alloc_text(PAGE, FspFsvolCreateTryOpenRequestFini) @@ -813,11 +823,25 @@ NTSTATUS FspFsvolCreateComplete( FileDesc->DeleteOnClose = BooleanFlagOn(IrpSp->Parameters.Create.Options, FILE_DELETE_ON_CLOSE); /* open the FileNode */ - OpenedFileNode = FspFileNodeOpen(FileNode, FileObject, + Result = FspFileNodeOpen(FileNode, FileObject, Response->Rsp.Create.Opened.GrantedAccess, IrpSp->Parameters.Create.ShareAccess, - &Result); - if (0 == OpenedFileNode) + &OpenedFileNode); + if (!NT_SUCCESS(Result)) { + if (STATUS_SHARING_VIOLATION == Result) + { + Result = FspFsvolCreateSharingViolation(Irp, Response, OpenedFileNode); + if (STATUS_PENDING != Result) + { + FspFileNodeClose(OpenedFileNode, FileObject, FALSE, TRUE); + FspFileNodeDereference(OpenedFileNode); + Result = STATUS_SHARING_VIOLATION; + } + + Irp->IoStatus.Information = 0; + FSP_RETURN(); + } + /* unable to open the FileNode; post a Close request */ FspFsvolCreatePostClose(FileDesc); @@ -955,6 +979,118 @@ NTSTATUS FspFsvolCreateComplete( IrpSp->FileObject, IrpSp->FileObject->RelatedFileObject, IrpSp->FileObject->FileName); } +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, + 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. + * + * 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. + * + * Second we want a way to differentiate the case between an oplock break being + * 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 where there is not one. + * + * 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. + * + * 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. + */ + + FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); + FSP_FSCTL_TRANSACT_RSP *Response = FspIopIrpResponse(Irp); + FSP_FILE_NODE *FileNode = FspIopRequestContext(Request, FspIopRequestExtraContext); + BOOLEAN StatusSharingViolation = TRUE, OpbatchBreakUnderway = FALSE; + KEVENT Event; + NTSTATUS Result; + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + FspFileNodeAcquireShared(FileNode, Main); + + if (FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(FileNode))) + { + 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; + } + } + + KeResetEvent(&Event); + 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; + } + + FspFileNodeRelease(FileNode, Main); + + FspFileNodeClose(FileNode, IrpSp->FileObject, FALSE, TRUE); + FspFileNodeDereference(FileNode); + FspIopRequestContext(Request, FspIopRequestExtraContext) = 0; + + if (StatusSharingViolation) + { + Response->IoStatus.Status = (UINT32)STATUS_SHARING_VIOLATION; + Response->IoStatus.Information = 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 NTSTATUS FspFsvolCreateTryOpen(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response, FSP_FILE_NODE *FileNode, FSP_FILE_DESC *FileDesc, PFILE_OBJECT FileObject, BOOLEAN FlushImage) @@ -1009,7 +1145,7 @@ static NTSTATUS FspFsvolCreateTryOpen(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Re if (0 == Request) { FspFsvolCreatePostClose(FileDesc); - FspFileNodeClose(FileNode, FileObject, TRUE); + FspFileNodeClose(FileNode, FileObject, TRUE, TRUE); } return DeleteOnClose ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION; @@ -1115,7 +1251,7 @@ static VOID FspFsvolCreateTryOpenRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PV ASSERT(0 != FileObject); FspFsvolCreatePostClose(FileDesc); - FspFileNodeClose(FileDesc->FileNode, FileObject, TRUE); + FspFileNodeClose(FileDesc->FileNode, FileObject, TRUE, TRUE); FspFileNodeDereference(FileDesc->FileNode); FspFileDescDelete(FileDesc); } @@ -1142,7 +1278,7 @@ static VOID FspFsvolCreateOverwriteRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, else if (RequestProcessing == State) FspFileNodeReleaseOwner(FileDesc->FileNode, Full, Request); - FspFileNodeClose(FileDesc->FileNode, FileObject, TRUE); + FspFileNodeClose(FileDesc->FileNode, FileObject, TRUE, TRUE); FspFileNodeDereference(FileDesc->FileNode); FspFileDescDelete(FileDesc); } diff --git a/src/sys/driver.h b/src/sys/driver.h index c73b30cd..7bf2bfc4 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -712,6 +712,10 @@ enum FspIopCreateRequestNonPagedFlag = 0x02, FspIopCreateRequestWorkItemFlag = 0x04, }; +enum +{ + FspIopRequestExtraContext = 4, +}; typedef VOID FSP_IOP_REQUEST_FINI(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]); typedef NTSTATUS FSP_IOP_REQUEST_WORK( PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp, @@ -724,7 +728,7 @@ typedef struct typedef struct { FSP_IOP_REQUEST_FINI *RequestFini; - PVOID Context[4]; + PVOID Context[4 + 1/*FspIopRequestExtraContext*/]; FSP_FSCTL_TRANSACT_RSP *Response; FSP_FSCTL_TRANSACT_REQ_WORK_ITEM *WorkItem; __declspec(align(FSP_FSCTL_TRANSACT_REQ_ALIGNMENT)) UINT8 RequestBuf[]; @@ -1098,13 +1102,13 @@ VOID FspFileNodeReleaseForeign(FSP_FILE_NODE *FileNode) FileNode = FileNode->MainFileNode; ExReleaseResourceLite(FileNode->Header.Resource); } -FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - UINT32 GrantedAccess, UINT32 ShareAccess, NTSTATUS *PResult); +NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, + UINT32 GrantedAccess, UINT32 ShareAccess, FSP_FILE_NODE **POpenedFileNode); VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PBOOLEAN PDeletePending); VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - BOOLEAN AlsoCleanup); + BOOLEAN RemoveShareAccess, BOOLEAN HandleCleanup); NTSTATUS FspFileNodeFlushAndPurgeCache(FSP_FILE_NODE *FileNode, UINT64 FlushOffset64, ULONG FlushLength, BOOLEAN FlushAndPurge); VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName); diff --git a/src/sys/file.c b/src/sys/file.c index 614b0ca2..3721b542 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -31,13 +31,13 @@ VOID FspFileNodeConvertExclusiveToSharedF(FSP_FILE_NODE *FileNode, ULONG Flags); VOID FspFileNodeSetOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner); VOID FspFileNodeReleaseF(FSP_FILE_NODE *FileNode, ULONG Flags); VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner); -FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - UINT32 GrantedAccess, UINT32 ShareAccess, NTSTATUS *PResult); +NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, + UINT32 GrantedAccess, UINT32 ShareAccess, FSP_FILE_NODE **POpenedFileNode); VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PBOOLEAN PDeletePending); VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - BOOLEAN AlsoCleanup); + BOOLEAN RemoveShareAccess, BOOLEAN HandleCleanup); NTSTATUS FspFileNodeFlushAndPurgeCache(FSP_FILE_NODE *FileNode, UINT64 FlushOffset64, ULONG FlushLength, BOOLEAN FlushAndPurge); VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName); @@ -447,8 +447,8 @@ VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner) FSP_FILE_NODE_CLR_FLAGS(); } -FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - UINT32 GrantedAccess, UINT32 ShareAccess, NTSTATUS *PResult) +NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, + UINT32 GrantedAccess, UINT32 ShareAccess, FSP_FILE_NODE **POpenedFileNode) { /* * Attempt to insert our FileNode into the volume device's generic table. @@ -596,13 +596,8 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, Result = STATUS_SUCCESS; exit: - if (!NT_SUCCESS(Result)) - { - if (0 != PResult) - *PResult = Result; - + if (!NT_SUCCESS(Result) && STATUS_SHARING_VIOLATION != Result) OpenedFileNode = 0; - } if (0 != OpenedFileNode) { @@ -613,7 +608,9 @@ exit: FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); - return OpenedFileNode; + *POpenedFileNode = OpenedFileNode; + + return Result; } VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, @@ -724,7 +721,7 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject } VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - BOOLEAN AlsoCleanup) + BOOLEAN RemoveShareAccess, BOOLEAN HandleCleanup) { /* * Close the FileNode. If the OpenCount becomes zero remove it @@ -740,7 +737,7 @@ VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, FspFsvolDeviceLockContextTable(FsvolDeviceObject); - if (AlsoCleanup) + if (RemoveShareAccess) { /* * Sharing violations between main file and streams were determined @@ -759,10 +756,11 @@ VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, } IoRemoveShareAccess(FileObject, &FileNode->ShareAccess); - - FileNode->HandleCount--; } + if (HandleCleanup) + FileNode->HandleCount--; + if (0 < FileNode->OpenCount && 0 == --FileNode->OpenCount) { FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName, diff --git a/src/sys/iop.c b/src/sys/iop.c index d2d97f81..742be42c 100644 --- a/src/sys/iop.c +++ b/src/sys/iop.c @@ -321,7 +321,10 @@ VOID FspIopCompleteIrpEx(PIRP Irp, NTSTATUS Result, BOOLEAN DeviceDereference) ClearFlag(Irp->Flags, IRP_INPUT_OPERATION | IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER); } - if (STATUS_SUCCESS != Result && STATUS_REPARSE != Result && STATUS_BUFFER_OVERFLOW != Result) + if (STATUS_SUCCESS != Result && + STATUS_REPARSE != Result && + STATUS_BUFFER_OVERFLOW != Result && + STATUS_SHARING_VIOLATION != Result) Irp->IoStatus.Information = 0; Irp->IoStatus.Status = Result; IoCompleteRequest(Irp, FSP_IO_INCREMENT);