diff --git a/src/sys/create.c b/src/sys/create.c index 87fe4060..b22542f5 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -521,6 +521,10 @@ static NTSTATUS FspFsvolCreateNoLock( FspFileNameSuffix(&FileNode->FileName, &FileNode->FileName, &Suffix); } + /* zero Irp->IoStatus, because we now use it to maintain state in FspFsvolCreateComplete */ + Irp->IoStatus.Status = 0; + Irp->IoStatus.Information = 0; + return FSP_STATUS_IOQ_POST; } @@ -640,6 +644,7 @@ NTSTATUS FspFsvolCreateComplete( FSP_FILE_DESC *FileDesc = FspIopRequestContext(Request, RequestFileDesc); FSP_FILE_NODE *FileNode = FileDesc->FileNode; FSP_FILE_NODE *OpenedFileNode; + ULONG SharingViolationReason; UNICODE_STRING NormalizedName; PREPARSE_DATA_BUFFER ReparseData; UNICODE_STRING ReparseTargetPrefix0, ReparseTargetPrefix1, ReparseTargetPath; @@ -838,21 +843,38 @@ NTSTATUS FspFsvolCreateComplete( /* open the FileNode */ Result = FspFileNodeOpen(FileNode, FileObject, Response->Rsp.Create.Opened.GrantedAccess, IrpSp->Parameters.Create.ShareAccess, - &OpenedFileNode); + &OpenedFileNode, &SharingViolationReason); if (!NT_SUCCESS(Result)) { if (STATUS_SHARING_VIOLATION == Result) { ASSERT(0 != OpenedFileNode); - FspIopSetIrpResponse(Irp, Response); - FspIopRequestContext(Request, FspIopRequestExtraContext) = OpenedFileNode; + if (STATUS_SHARING_VIOLATION != Irp->IoStatus.Status) + { + ASSERT(0 == Irp->IoStatus.Information || + FILE_OPBATCH_BREAK_UNDERWAY == Irp->IoStatus.Information); - Result = FspFsvolCreateSharingViolationOplock( - FsvolDeviceObject, Irp, IrpSp, FALSE); - if (STATUS_PENDING == Result) - FSP_RETURN(); + FspIopSetIrpResponse(Irp, Response); + FspIopRequestContext(Request, FspIopRequestExtraContext) = OpenedFileNode; + + /* HACK: We have run out of Contexts. Store the sharing violation reason in the IRP. */ + Irp->IoStatus.Status = STATUS_SHARING_VIOLATION; + Irp->IoStatus.Information = SharingViolationReason; + + Result = FspFsvolCreateSharingViolationOplock( + FsvolDeviceObject, Irp, IrpSp, FALSE); + if (STATUS_PENDING == Result) + FSP_RETURN(); + } + else + { + FspFileNodeClose(OpenedFileNode, 0, TRUE); + FspFileNodeDereference(OpenedFileNode); + } } + else + ASSERT(0 == OpenedFileNode); /* unable to open the FileNode; post a Close request */ FspFsvolCreatePostClose(FileDesc); @@ -1245,18 +1267,22 @@ static NTSTATUS FspFsvolCreateSharingViolationOplock( * try to break both Batch and Handle oplocks and return the appropriate status * code. * - * We acquire the FileNode shared so allow oplock breaks to happen concurrently. + * We acquire the FileNode shared to 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; FSP_FILE_NODE *ExtraFileNode = FspIopRequestContext(Request, FspIopRequestExtraContext); - BOOLEAN OpbatchBreakUnderway = FALSE; + ULONG SharingViolationReason = (ULONG)Irp->IoStatus.Information; NTSTATUS Result; BOOLEAN Success; ASSERT(FspFsctlTransactCreateKind == Request->Kind); + ASSERT( + FspFileNodeSharingViolationGeneral == SharingViolationReason || + FspFileNodeSharingViolationMainFile == SharingViolationReason || + FspFileNodeSharingViolationStream == SharingViolationReason); Success = DEBUGTEST(90) && FspFileNodeTryAcquireSharedF(ExtraFileNode, FspFileNodeAcquireMain, CanWait); @@ -1271,7 +1297,9 @@ static NTSTATUS FspFsvolCreateSharingViolationOplock( * worker thread to break the oplocks. */ Success = DEBUGTEST(90) && - !FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(ExtraFileNode)) && + !(FspFileNodeSharingViolationMainFile == SharingViolationReason) && + !(FspFileNodeSharingViolationStream == SharingViolationReason) && + !(FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(ExtraFileNode))) && !(!FlagOn(IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED) && FsRtlCurrentOplockH(FspFileNodeAddrOfOplock(ExtraFileNode))); @@ -1286,16 +1314,55 @@ static NTSTATUS FspFsvolCreateSharingViolationOplock( } else { + Irp->IoStatus.Information = 0; + + Result = STATUS_SHARING_VIOLATION; + if (FspFileNodeSharingViolationMainFile == SharingViolationReason) + { + /* + * If a SUPERSEDE, OVERWRITE or OVERWRITE_IF operation is performed + * on an alternate data stream and FILE_SHARE_DELETE is not specified + * and there is a Batch or Filter oplock on the primary data stream, + * request a break of the Batch or Filter oplock on the primary data + * stream. + */ + + FSP_FILE_DESC *FileDesc = FspIopRequestContext(Request, RequestFileDesc); + FSP_FILE_NODE *FileNode = FileDesc->FileNode; + + /* break Batch oplocks on the main file and this stream */ + Result = FspFileNodeCheckBatchOplocksOnAllStreams(FsvolDeviceObject, Irp, + ExtraFileNode, &FileNode->FileName); + if (STATUS_SUCCESS != Result) + Result = STATUS_SHARING_VIOLATION; + } + else + if (FspFileNodeSharingViolationStream == SharingViolationReason) + { + /* + * If a SUPERSEDE, OVERWRITE or OVERWRITE_IF operation is performed + * on the primary data stream and DELETE access has been requested + * and there are Batch or Filter oplocks on any alternate data stream, + * request a break of the Batch or Filter oplocks on all alternate data + * streams that have them. + */ + + /* break Batch oplocks on the main file and all our streams */ + Result = FspFileNodeCheckBatchOplocksOnAllStreams(FsvolDeviceObject, Irp, + ExtraFileNode, 0); + if (STATUS_SUCCESS != Result) + Result = STATUS_SHARING_VIOLATION; + } + else if (FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(ExtraFileNode))) { /* wait for Batch oplock break to complete */ Result = FspCheckOplock(FspFileNodeAddrOfOplock(ExtraFileNode), Irp, 0, 0, 0); - if (STATUS_SUCCESS == Result) - OpbatchBreakUnderway = TRUE; - - /* if a Batch oplock is broken, we still return STATUS_SHARING_VIOLATION */ - Result = STATUS_SHARING_VIOLATION; + if (STATUS_SUCCESS != Result) + Result = STATUS_SHARING_VIOLATION; + else + Irp->IoStatus.Information = FILE_OPBATCH_BREAK_UNDERWAY; } else if (!FlagOn(IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED) && @@ -1305,11 +1372,9 @@ static NTSTATUS FspFsvolCreateSharingViolationOplock( Result = FspOplockBreakH(FspFileNodeAddrOfOplock(ExtraFileNode), Irp, 0, 0, 0, 0); ASSERT(STATUS_OPLOCK_BREAK_IN_PROGRESS != Result); - - /* if a Handle oplock is broken, we can actually retry the sharing check */ + if (STATUS_SUCCESS != Result) + Result = STATUS_SHARING_VIOLATION; } - else - Result = STATUS_SHARING_VIOLATION; FspFileNodeRelease(ExtraFileNode, Main); @@ -1324,9 +1389,7 @@ static NTSTATUS FspFsvolCreateSharingViolationOplock( else { Response->IoStatus.Status = (UINT32)Result; - Response->IoStatus.Information = - STATUS_SHARING_VIOLATION == Result && OpbatchBreakUnderway ? - FILE_OPBATCH_BREAK_UNDERWAY : 0; + Response->IoStatus.Information = (UINT32)Irp->IoStatus.Information; } FspIopRetryCompleteIrp(Irp, Response, &Result); diff --git a/src/sys/device.c b/src/sys/device.c index 0a26cdb4..f71c9678 100644 --- a/src/sys/device.c +++ b/src/sys/device.c @@ -634,9 +634,9 @@ PVOID FspFsvolDeviceEnumerateContextByName(PDEVICE_OBJECT DeviceObject, PUNICODE if (0 != Result && FspFileNameIsPrefix(FileName, Result->FileName, CaseInsensitive, 0) && - FileName->Length < Result->FileName->Length && - (L'\\' == Result->FileName->Buffer[FileName->Length / sizeof(WCHAR)] || - L':' == Result->FileName->Buffer[FileName->Length / sizeof(WCHAR)])) + (FileName->Length == Result->FileName->Length || + (L'\\' == Result->FileName->Buffer[FileName->Length / sizeof(WCHAR)] || + L':' == Result->FileName->Buffer[FileName->Length / sizeof(WCHAR)]))) return Result->Context; else return 0; diff --git a/src/sys/driver.h b/src/sys/driver.h index 4e6f3ec2..7291c515 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -976,6 +976,12 @@ enum FspFileNodeFileKind = 'BZ', }; enum +{ + FspFileNodeSharingViolationGeneral = 'G', + FspFileNodeSharingViolationMainFile = 'M', + FspFileNodeSharingViolationStream = 'S', +}; +enum { FspFileNodeAcquireMain = 1, FspFileNodeAcquirePgio = 2, @@ -1108,7 +1114,8 @@ VOID FspFileNodeReleaseForeign(FSP_FILE_NODE *FileNode) ExReleaseResourceLite(FileNode->Header.Resource); } NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - UINT32 GrantedAccess, UINT32 ShareAccess, FSP_FILE_NODE **POpenedFileNode); + UINT32 GrantedAccess, UINT32 ShareAccess, + FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason); VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PBOOLEAN PDeletePending); VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); @@ -1117,6 +1124,11 @@ VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, BOOLEAN HandleCleanup); /* TRUE to decrement handle count */ NTSTATUS FspFileNodeFlushAndPurgeCache(FSP_FILE_NODE *FileNode, UINT64 FlushOffset64, ULONG FlushLength, BOOLEAN FlushAndPurge); +NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams( + PDEVICE_OBJECT FsvolDeviceObject, + PIRP OplockIrp, + FSP_FILE_NODE *FileNode, + PUNICODE_STRING StreamFileName); BOOLEAN FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp, FSP_FILE_NODE *FileNode, PUNICODE_STRING FileName); VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName); diff --git a/src/sys/file.c b/src/sys/file.c index b6245ce4..87c9b5e1 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -32,7 +32,8 @@ 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); NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - UINT32 GrantedAccess, UINT32 ShareAccess, FSP_FILE_NODE **POpenedFileNode); + UINT32 GrantedAccess, UINT32 ShareAccess, + FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason); VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PBOOLEAN PDeletePending); VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); @@ -41,6 +42,11 @@ VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, BOOLEAN HandleCleanup); /* TRUE to decrement handle count */ NTSTATUS FspFileNodeFlushAndPurgeCache(FSP_FILE_NODE *FileNode, UINT64 FlushOffset64, ULONG FlushLength, BOOLEAN FlushAndPurge); +NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams( + PDEVICE_OBJECT FsvolDeviceObject, + PIRP OplockIrp, + FSP_FILE_NODE *FileNode, + PUNICODE_STRING StreamFileName); BOOLEAN FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp, FSP_FILE_NODE *FileNode, PUNICODE_STRING FileName); VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName); @@ -103,6 +109,7 @@ NTSTATUS FspMainFileClose( #pragma alloc_text(PAGE, FspFileNodeCleanupComplete) #pragma alloc_text(PAGE, FspFileNodeClose) #pragma alloc_text(PAGE, FspFileNodeFlushAndPurgeCache) +#pragma alloc_text(PAGE, FspFileNodeCheckBatchOplocksOnAllStreams) #pragma alloc_text(PAGE, FspFileNodeRenameCheck) #pragma alloc_text(PAGE, FspFileNodeRename) #pragma alloc_text(PAGE, FspFileNodeGetFileInfo) @@ -449,7 +456,8 @@ VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner) } NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, - UINT32 GrantedAccess, UINT32 ShareAccess, FSP_FILE_NODE **POpenedFileNode) + UINT32 GrantedAccess, UINT32 ShareAccess, + FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason) { /* * Attempt to insert our FileNode into the volume device's generic table. @@ -466,6 +474,8 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, BOOLEAN Inserted, DeletePending; NTSTATUS Result; + *PSharingViolationReason = FspFileNodeSharingViolationGeneral; + FspFsvolDeviceLockContextTable(FsvolDeviceObject); /* @@ -494,6 +504,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, FILE_EXECUTE | FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE)) { OpenedFileNode = FileNode->MainFileNode; + *PSharingViolationReason = FspFileNodeSharingViolationMainFile; Result = STATUS_SHARING_VIOLATION; goto exit; } @@ -544,6 +555,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, if (FlagOn(GrantedAccess, DELETE)) { + *PSharingViolationReason = FspFileNodeSharingViolationStream; Result = STATUS_SHARING_VIOLATION; goto exit; } @@ -834,6 +846,149 @@ NTSTATUS FspFileNodeFlushAndPurgeCache(FSP_FILE_NODE *FileNode, return IoStatus.Status; } +NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams( + PDEVICE_OBJECT FsvolDeviceObject, + PIRP OplockIrp, + FSP_FILE_NODE *FileNode, + PUNICODE_STRING StreamFileName) +{ + /* + * Called during Create processing. The device rename resource has been acquired shared. + * No concurrent renames are allowed. + */ + + PAGED_CODE(); + + ASSERT(0 == FileNode->MainFileNode); + + FSP_FILE_NODE *DescendantFileNode; + FSP_FILE_NODE *DescendantFileNodeArray[16], **DescendantFileNodes; + ULONG DescendantFileNodeCount, DescendantFileNodeIndex; + FSP_DEVICE_CONTEXT_BY_NAME_TABLE_RESTART_KEY RestartKey; + USHORT FileNameLength = FileNode->FileName.Length; + BOOLEAN CaseInsensitive = !FspFsvolDeviceExtension(FsvolDeviceObject)-> + VolumeParams.CaseSensitiveSearch; + NTSTATUS Result; + + DescendantFileNodes = DescendantFileNodeArray; + DescendantFileNodeCount = 0; + + FspFsvolDeviceLockContextTable(FsvolDeviceObject); + + /* count descendant file nodes and try to gather them in a local array if possible */ + memset(&RestartKey, 0, sizeof RestartKey); + for (;;) + { + DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject, + &FileNode->FileName, FALSE, &RestartKey); + if (0 == DescendantFileNode) + break; + if (DescendantFileNode->FileName.Length > FileNameLength && + L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)]) + break; + + if (1 >= DescendantFileNode->HandleCount) + continue; + + if (0 != StreamFileName) + { + if (DescendantFileNode != FileNode && + 0 != FspFileNameCompare(&DescendantFileNode->FileName, StreamFileName, + CaseInsensitive, 0)) + continue; + } + + /* keep a reference to the FileNode in case it goes away in later processing */ + FspFileNodeReference(DescendantFileNode); + + if (ARRAYSIZE(DescendantFileNodeArray) > DescendantFileNodeCount) + DescendantFileNodes[DescendantFileNodeCount] = DescendantFileNode; + DescendantFileNodeCount++; + } + + ASSERT(0 != StreamFileName || DescendantFileNodeCount <= 2); + + /* if the local array is out of space, gather descendant file nodes in the pool */ + if (ARRAYSIZE(DescendantFileNodeArray) < DescendantFileNodeCount) + { + ASSERT(0 == StreamFileName); + + DescendantFileNodes = FspAllocMustSucceed(DescendantFileNodeCount * sizeof(FSP_FILE_NODE *)); + DescendantFileNodeIndex = 0; + memset(&RestartKey, 0, sizeof RestartKey); + for (;;) + { + DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject, + &FileNode->FileName, FALSE, &RestartKey); + if (0 == DescendantFileNode) + break; + if (DescendantFileNode->FileName.Length > FileNameLength && + L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)]) + break; + + if (1 >= DescendantFileNode->HandleCount) + continue; + + DescendantFileNodes[DescendantFileNodeIndex] = DescendantFileNode; + DescendantFileNodeIndex++; + ASSERT(DescendantFileNodeCount >= DescendantFileNodeIndex); + } + + ASSERT(DescendantFileNodeCount == DescendantFileNodeIndex); + } + + FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); + + /* + * At this point all descendant FileNode's are enumerated and referenced. + */ + + /* break any Batch or Handle oplocks on descendants */ + Result = STATUS_SUCCESS; + for ( + DescendantFileNodeIndex = 0; + DescendantFileNodeCount > DescendantFileNodeIndex; + DescendantFileNodeIndex++) + { + DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex]; + + if (FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(DescendantFileNode))) + { + NTSTATUS Result0 = FspCheckOplock(FspFileNodeAddrOfOplock(DescendantFileNode), OplockIrp, + 0, 0, 0); + if (STATUS_SUCCESS != Result0) + Result = STATUS_SHARING_VIOLATION; + else + OplockIrp->IoStatus.Information = FILE_OPBATCH_BREAK_UNDERWAY; + } + else + if (FsRtlCurrentOplockH(FspFileNodeAddrOfOplock(DescendantFileNode))) + { + NTSTATUS Result0 = FspOplockBreakH(FspFileNodeAddrOfOplock(DescendantFileNode), OplockIrp, + 0, 0, 0, 0); + ASSERT(STATUS_OPLOCK_BREAK_IN_PROGRESS != Result0); + if (STATUS_SUCCESS != Result0) + Result = STATUS_SHARING_VIOLATION; + } + } + + /* dereference all FileNode's referenced during initial enumeration */ + for ( + DescendantFileNodeIndex = 0; + DescendantFileNodeCount > DescendantFileNodeIndex; + DescendantFileNodeIndex++) + { + DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex]; + + FspFileNodeDereference(DescendantFileNode); + } + + if (DescendantFileNodeArray != DescendantFileNodes) + FspFree(DescendantFileNodes); + + return Result; +} + BOOLEAN FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp, FSP_FILE_NODE *FileNode, PUNICODE_STRING FileName) { @@ -952,7 +1107,7 @@ BOOLEAN FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp, } } - /* break any batch oplocks on descendants */ + /* break any Batch or Handle oplocks on descendants */ for ( DescendantFileNodeIndex = 0; DescendantFileNodeCount > DescendantFileNodeIndex; @@ -963,7 +1118,9 @@ BOOLEAN FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp, DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~1); if (HasOpenHandles) - FspCheckOplock(FspFileNodeAddrOfOplock(DescendantFileNode), OplockIrp, 0, 0, 0); + if (FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(DescendantFileNode)) || + FsRtlCurrentOplockH(FspFileNodeAddrOfOplock(DescendantFileNode))) + FspCheckOplock(FspFileNodeAddrOfOplock(DescendantFileNode), OplockIrp, 0, 0, 0); } FspFsvolDeviceLockContextTable(FsvolDeviceObject); diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index 9fafd26f..eca05360 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -1283,7 +1283,8 @@ retry: */ Result = STATUS_SUCCESS; if (!FspFileNodeRenameCheck(FsvolDeviceObject, Irp, FileNode, &FileNode->FileName) || - !FspFileNodeRenameCheck(FsvolDeviceObject, Irp, 0, &NewFileName)) + (0 != FspFileNameCompare(&FileNode->FileName, &NewFileName, !FileDesc->CaseSensitive, 0) && + !FspFileNodeRenameCheck(FsvolDeviceObject, Irp, 0, &NewFileName))) Result = STATUS_ACCESS_DENIED; if (!NT_SUCCESS(Result)) return Result;