Author: pschweitzer Date: Fri Mar 7 19:33:38 2014 New Revision: 62442
URL: http://svn.reactos.org/svn/reactos?rev=62442&view=rev Log: [NTOSKRNL] - Implement FsRtlNotifyFilterReportChange - Implement FsRtlNotifyUpdateBuffer - Implement FsRtlCancelNotify - Implement FsRtlNotifyGetLastPartOffset - Fix implementation of FsRtlNotifyFilterChangeDirectory
This finishes the implementation of file system notifications inside the kernel. Data are properly returned to the caller on changes.
CORE-2582
Modified: trunk/reactos/ntoskrnl/fsrtl/notify.c trunk/reactos/ntoskrnl/include/internal/fsrtl.h
Modified: trunk/reactos/ntoskrnl/fsrtl/notify.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/fsrtl/notify.c?rev... ============================================================================== --- trunk/reactos/ntoskrnl/fsrtl/notify.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/fsrtl/notify.c [iso-8859-1] Fri Mar 7 19:33:38 2014 @@ -12,6 +12,55 @@ #define NDEBUG #include <debug.h>
+/* INLINED FUNCTIONS *********************************************************/ + +/* + * @implemented + */ +FORCEINLINE +VOID +FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync) +{ + ULONG_PTR CurrentThread = (ULONG_PTR)KeGetCurrentThread(); + + /* Only acquire fast mutex if it's not already acquired by the current thread */ + if (RealNotifySync->OwningThread != CurrentThread) + { + ExAcquireFastMutexUnsafe(&(RealNotifySync->FastMutex)); + RealNotifySync->OwningThread = CurrentThread; + } + /* Whatever the case, keep trace of the attempt to acquire fast mutex */ + RealNotifySync->OwnerCount++; +} + +/* + * @implemented + */ +FORCEINLINE +VOID +FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync) +{ + RealNotifySync->OwnerCount--; + /* Release the fast mutex only if no other instance needs it */ + if (!RealNotifySync->OwnerCount) + { + ExReleaseFastMutexUnsafe(&(RealNotifySync->FastMutex)); + RealNotifySync->OwningThread = (ULONG_PTR)0; + } +} + +#define FsRtlNotifyGetLastPartOffset(FullLen, TargLen, Type, Chr) \ + for (FullPosition = 0; FullPosition < FullLen; ++FullPosition) \ + if (((Type)NotifyChange->FullDirectoryName->Buffer)[FullPosition] == Chr) \ + ++FullNumberOfParts; \ + for (LastPartOffset = 0; LastPartOffset < TargLen; ++LastPartOffset) { \ + if ( ((Type)TargetDirectory.Buffer)[LastPartOffset] == Chr) { \ + ++TargetNumberOfParts; \ + if (TargetNumberOfParts == FullNumberOfParts) \ + break; \ + } \ + } + /* PRIVATE FUNCTIONS *********************************************************/
VOID @@ -22,13 +71,161 @@ FsRtlNotifySetCancelRoutine(IN PIRP Irp, IN PNOTIFY_CHANGE NotifyChange OPTIONAL);
+/* + * @implemented + */ VOID NTAPI FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { + PVOID Buffer; + PIRP NotifyIrp; + ULONG BufferLength; + PIO_STACK_LOCATION Stack; + PNOTIFY_CHANGE NotifyChange; + PREAL_NOTIFY_SYNC RealNotifySync; + PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL; + + /* Get the NOTIFY_CHANGE struct and reset it */ + NotifyChange = (PNOTIFY_CHANGE)Irp->IoStatus.Information; + Irp->IoStatus.Information = 0; + /* Reset the cancel routine */ + IoSetCancelRoutine(Irp, NULL); + /* And release lock */ IoReleaseCancelSpinLock(Irp->CancelIrql); - UNIMPLEMENTED; + /* Get REAL_NOTIFY_SYNC struct */ + RealNotifySync = NotifyChange->NotifySync; + + FsRtlNotifyAcquireFastMutex(RealNotifySync); + + _SEH2_TRY + { + /* Remove the IRP from the notifications list and mark it pending */ + RemoveEntryList(&(Irp->Tail.Overlay.ListEntry)); + IoMarkIrpPending(Irp); + + /* Now, the tricky part - let's find a buffer big enough to hold the return data */ + if (NotifyChange->Buffer && NotifyChange->AllocatedBuffer == NULL && + ((Irp->MdlAddress && MmGetSystemAddressForMdl(Irp->MdlAddress) == NotifyChange->Buffer) || + NotifyChange->Buffer == Irp->AssociatedIrp.SystemBuffer)) + { + /* Assume we didn't find any */ + Buffer = NULL; + BufferLength = 0; + + /* If we don't have IRPs, check if current buffer is big enough */ + if (IsListEmpty(&NotifyChange->NotifyIrps)) + { + if (NotifyChange->BufferLength >= NotifyChange->DataLength) + { + BufferLength = NotifyChange->BufferLength; + } + } + else + { + /* Otherwise, try to look at next IRP available */ + NotifyIrp = CONTAINING_RECORD(NotifyChange->NotifyIrps.Flink, IRP, Tail.Overlay.ListEntry); + Stack = IoGetCurrentIrpStackLocation(NotifyIrp); + + /* If its buffer is big enough, get it */ + if (Stack->Parameters.NotifyDirectory.Length >= NotifyChange->BufferLength) + { + /* Is it MDL? */ + if (NotifyIrp->AssociatedIrp.SystemBuffer == NULL) + { + if (NotifyIrp->MdlAddress != NULL) + { + Buffer = MmGetSystemAddressForMdl(NotifyIrp->MdlAddress); + } + } + else + { + Buffer = NotifyIrp->AssociatedIrp.MasterIrp; + } + + /* Backup our accepted buffer length */ + BufferLength = Stack->Parameters.NotifyDirectory.Length; + if (BufferLength > NotifyChange->BufferLength) + { + BufferLength = NotifyChange->BufferLength; + } + } + } + + /* At that point, we *may* have a buffer */ + + /* If it has null length, then note that we won't use it */ + if (BufferLength == 0) + { + NotifyChange->Flags |= NOTIFY_IMMEDIATELY; + } + else + { + /* If we have a buffer length, but no buffer then allocate one */ + if (Buffer == NULL) + { + PsChargePoolQuota(NotifyChange->OwningProcess, PagedPool, BufferLength); + Buffer = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, BufferLength, 'NrSF'); + NotifyChange->AllocatedBuffer = Buffer; + } + + /* Copy data in that buffer */ + RtlCopyMemory(Buffer, NotifyChange->Buffer, NotifyChange->DataLength); + NotifyChange->ThisBufferLength = BufferLength; + NotifyChange->Buffer = Buffer; + } + + /* If we don't have valide buffer, ensure everything is 0-ed out */ + if (NotifyChange->Flags & NOTIFY_IMMEDIATELY) + { + NotifyChange->Buffer = 0; + NotifyChange->AllocatedBuffer = 0; + NotifyChange->LastEntry = 0; + NotifyChange->DataLength = 0; + NotifyChange->ThisBufferLength = 0; + } + } + + /* It's now time to complete - data are ready */ + + /* Set appropriate status and complete */ + Irp->IoStatus.Status = STATUS_CANCELLED; + IofCompleteRequest(Irp, EVENT_INCREMENT); + + /* If that notification isn't referenced any longer, drop it */ + if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount))) + { + /* If it had an allocated buffer, delete */ + if (NotifyChange->AllocatedBuffer) + { + PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength); + ExFreePoolWithTag(NotifyChange->AllocatedBuffer, 'NrSF'); + } + + /* In case of full name, remember subject context for later deletion */ + if (NotifyChange->FullDirectoryName) + { + SubjectContext = NotifyChange->SubjectContext; + } + + /* We mustn't have ANY change left anymore */ + ASSERT(NotifyChange->NotifyList.Flink == NULL); + ExFreePoolWithTag(NotifyChange, 0); + } + } + _SEH2_FINALLY + { + FsRtlNotifyReleaseFastMutex(RealNotifySync); + + /* If the subject security context was captured, release and free it */ + if (SubjectContext) + { + SeReleaseSubjectContext(SubjectContext); + ExFreePool(SubjectContext); + } + } + _SEH2_END; }
/* @@ -58,6 +255,9 @@ } }
+/* + *@implemented + */ PNOTIFY_CHANGE FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList, IN PVOID FsContext) @@ -81,22 +281,6 @@ } } return NULL; -} - -FORCEINLINE -VOID -FsRtlNotifyAcquireFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync) -{ - ULONG_PTR CurrentThread = (ULONG_PTR)KeGetCurrentThread(); - - /* Only acquire fast mutex if it's not already acquired by the current thread */ - if (RealNotifySync->OwningThread != CurrentThread) - { - ExAcquireFastMutexUnsafe(&(RealNotifySync->FastMutex)); - RealNotifySync->OwningThread = CurrentThread; - } - /* Whatever the case, keep trace of the attempt to acquire fast mutex */ - RealNotifySync->OwnerCount++; }
/* @@ -216,7 +400,7 @@
DataLength = NotifyChange->DataLength;
- NotifyChange->Flags &= (INVALIDATE_BUFFERS | WATCH_TREE); + NotifyChange->Flags &= (NOTIFY_IMMEDIATELY | WATCH_TREE); NotifyChange->DataLength = 0; NotifyChange->LastEntry = 0;
@@ -233,19 +417,6 @@ } }
-FORCEINLINE -VOID -FsRtlNotifyReleaseFastMutex(IN PREAL_NOTIFY_SYNC RealNotifySync) -{ - RealNotifySync->OwnerCount--; - /* Release the fast mutex only if no other instance needs it */ - if (!RealNotifySync->OwnerCount) - { - ExReleaseFastMutexUnsafe(&(RealNotifySync->FastMutex)); - RealNotifySync->OwningThread = (ULONG_PTR)0; - } -} - /* * @implemented */ @@ -293,6 +464,91 @@
/* Return that we didn't removed cancel routine */ return FALSE; +} + +/* + * @implemented + */ +BOOLEAN +FsRtlNotifyUpdateBuffer(OUT PFILE_NOTIFY_INFORMATION OutputBuffer, + IN ULONG Action, + IN PSTRING ParentName, + IN PSTRING TargetName, + IN PSTRING StreamName, + IN BOOLEAN IsUnicode, + IN ULONG DataLength) +{ + /* Unless there's an issue with buffers, there's no reason to fail */ + BOOLEAN Succeed = TRUE; + ULONG AlreadyWritten = 0, ResultSize; + + PAGED_CODE(); + + /* Update user buffer with the change that occured + * First copy parent name if any + * Then copy target name, there's always one + * And finally, copy stream name if any + * If these names aren't unicode, then convert first + */ + _SEH2_TRY + { + OutputBuffer->NextEntryOffset = 0; + OutputBuffer->Action = Action; + OutputBuffer->FileNameLength = DataLength - sizeof(FILE_NOTIFY_INFORMATION); + if (IsUnicode) + { + if (ParentName->Length) + { + RtlCopyMemory(OutputBuffer->FileName, ParentName->Buffer, ParentName->Length); + OutputBuffer->FileName[ParentName->Length / sizeof(WCHAR)] = L'\'; + AlreadyWritten = ParentName->Length + sizeof(WCHAR); + } + RtlCopyMemory(OutputBuffer->FileName + AlreadyWritten, TargetName->Buffer, TargetName->Length); + if (StreamName) + { + AlreadyWritten += TargetName->Length; + OutputBuffer->FileName[AlreadyWritten / sizeof(WCHAR)] = L':'; + RtlCopyMemory(OutputBuffer->FileName + AlreadyWritten + sizeof(WCHAR), + StreamName->Buffer, StreamName->Length); + } + } + else + { + if (!ParentName->Length) + { + ASSERT(StreamName); + RtlCopyMemory(OutputBuffer->FileName, StreamName->Buffer, StreamName->Length); + } + else + { + RtlOemToUnicodeN(OutputBuffer->FileName, OutputBuffer->FileNameLength, + &ResultSize, ParentName->Buffer, + ParentName->Length); + OutputBuffer->FileName[ResultSize / sizeof(WCHAR)] = L'\'; + AlreadyWritten = ResultSize + sizeof(WCHAR); + + RtlOemToUnicodeN(OutputBuffer->FileName + AlreadyWritten, + OutputBuffer->FileNameLength, &ResultSize, + TargetName->Buffer, TargetName->Length); + + if (StreamName) + { + AlreadyWritten += ResultSize; + OutputBuffer->FileName[AlreadyWritten / sizeof(WCHAR)] = L':'; + RtlOemToUnicodeN(OutputBuffer->FileName + AlreadyWritten + sizeof(WCHAR), + OutputBuffer->FileNameLength, &ResultSize, + StreamName->Buffer, StreamName->Length); + } + } + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Succeed = FALSE; + } + _SEH2_END; + + return Succeed; }
/* PUBLIC FUNCTIONS **********************************************************/ @@ -445,7 +701,7 @@
/*++ * @name FsRtlNotifyFilterChangeDirectory - * @unimplemented + * @implemented * * FILLME * @@ -559,16 +815,16 @@ NotifyIrp->IoStatus.Status = STATUS_DELETE_PENDING; IoCompleteRequest(NotifyIrp, EVENT_INCREMENT); } - /* Complete if there is directory enumeration and no buffer available any more */ - if ((NotifyChange->Flags & INVALIDATE_BUFFERS) && (NotifyChange->Flags & ENUMERATE_DIR)) - { - NotifyChange->Flags &= ~INVALIDATE_BUFFERS; + /* Complete now if asked to (and not asked to notify later on) */ + if ((NotifyChange->Flags & NOTIFY_IMMEDIATELY) && !(NotifyChange->Flags & NOTIFY_LATER)) + { + NotifyChange->Flags &= ~NOTIFY_IMMEDIATELY; IoMarkIrpPending(NotifyIrp); NotifyIrp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR; IoCompleteRequest(NotifyIrp, EVENT_INCREMENT); } - /* If no data yet, or directory enumeration, handle */ - else if (NotifyChange->DataLength == 0 || (NotifyChange->Flags & ENUMERATE_DIR)) + /* If no data yet, or asked to notify later on, handle */ + else if (NotifyChange->DataLength == 0 || (NotifyChange->Flags & NOTIFY_LATER)) { goto HandleIRP; } @@ -612,20 +868,13 @@ else { /* If it can't contain WCHAR, it's ANSI */ - if (FullDirectoryName->Length < sizeof(WCHAR)) + if (FullDirectoryName->Length < sizeof(WCHAR) || ((CHAR*)FullDirectoryName->Buffer)[1] != 0) { NotifyChange->CharacterSize = sizeof(CHAR); } - /* First char is , so in unicode, right part is 0 - * whereas in ANSI it contains next char - */ - else if (((CHAR*)FullDirectoryName->Buffer)[1] == 0) + else { NotifyChange->CharacterSize = sizeof(WCHAR); - } - else - { - NotifyChange->CharacterSize = sizeof(CHAR); }
/* Now, check is user is willing to watch root */ @@ -668,7 +917,7 @@ FsRtlNotifyReleaseFastMutex(RealNotifySync);
/* If the subject security context was captured and there's no notify */ - if (SubjectContext && (!NotifyChange || FullDirectoryName)) + if (SubjectContext && (!NotifyChange || NotifyChange->FullDirectoryName)) { SeReleaseSubjectContext(SubjectContext); ExFreePool(SubjectContext); @@ -679,7 +928,7 @@
/*++ * @name FsRtlNotifyFilterReportChange - * @unimplemented + * @implemented * * FILLME * @@ -731,7 +980,437 @@ IN PVOID TargetContext, IN PVOID FilterContext) { - KeBugCheck(FILE_SYSTEM); + PIRP Irp; + PVOID OutputBuffer; + USHORT FullPosition; + PLIST_ENTRY NextEntry; + PIO_STACK_LOCATION Stack; + PNOTIFY_CHANGE NotifyChange; + PREAL_NOTIFY_SYNC RealNotifySync; + PFILE_NOTIFY_INFORMATION FileNotifyInfo; + BOOLEAN IsStream, IsParent, PoolQuotaCharged; + STRING TargetDirectory, TargetName, ParentName, IntNormalizedParentName; + ULONG NumberOfBytes, TargetNumberOfParts, FullNumberOfParts, LastPartOffset, ParentNameOffset, ParentNameLength; + ULONG DataLength, TargetNameLength, AlignedDataLength; + + TargetDirectory.Length = 0; + TargetDirectory.MaximumLength = 0; + TargetDirectory.Buffer = NULL; + TargetName.Length = 0; + TargetName.MaximumLength = 0; + TargetName.Buffer = NULL; + ParentName.Length = 0; + ParentName.MaximumLength = 0; + ParentName.Buffer = NULL; + IsStream = FALSE; + + PAGED_CODE(); + + DPRINT("FsRtlNotifyFilterReportChange(%p, %p, %p, %u, %p, %p, %p, %x, %x, %p, %p)\n", + NotifySync, NotifyList, FullTargetName, TargetNameOffset, StreamName, NormalizedParentName, + FilterMatch, Action, TargetContext, FilterContext); + + /* We need offset in name */ + if (!TargetNameOffset && FullTargetName) + { + return; + } + + /* Get real structure hidden behind the opaque pointer */ + RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync; + /* Acquire lock - will be released in finally block */ + FsRtlNotifyAcquireFastMutex(RealNotifySync); + _SEH2_TRY + { + /* Browse all the registered notifications we have */ + for (NextEntry = NotifyList->Flink; NextEntry != NotifyList; + NextEntry = NextEntry->Flink) + { + /* Try to find an entry matching our change */ + NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList); + if (FullTargetName != NULL) + { + ASSERT(NotifyChange->FullDirectoryName != NULL); + if (!NotifyChange->FullDirectoryName->Length) + { + continue; + } + + if (!(FilterMatch & NotifyChange->CompletionFilter)) + { + continue; + } + + /* If no normalized name provided, construct it from full target name */ + if (NormalizedParentName == NULL) + { + IntNormalizedParentName.Buffer = FullTargetName->Buffer; + if (TargetNameOffset != NotifyChange->CharacterSize) + { + IntNormalizedParentName.MaximumLength = + IntNormalizedParentName.Length = TargetNameOffset - NotifyChange->CharacterSize; + } + else + { + IntNormalizedParentName.MaximumLength = + IntNormalizedParentName.Length = TargetNameOffset; + } + NormalizedParentName = &IntNormalizedParentName; + } + + /* heh? Watched directory bigger than changed file? */ + if (NormalizedParentName->Length < NotifyChange->FullDirectoryName->Length) + { + continue; + } + + /* Same len => parent */ + if (NormalizedParentName->Length == NotifyChange->FullDirectoryName->Length) + { + IsParent = TRUE; + } + /* If not, then, we have to be watching the tree, otherwise we don't have to report such changes */ + else if (!(NotifyChange->Flags & WATCH_TREE)) + { + continue; + } + /* And finally, we've to check we're properly -terminated */ + else + { + if (!(NotifyChange->Flags & WATCH_ROOT)) + { + if (NotifyChange->CharacterSize == sizeof(CHAR)) + { + if (((PSTR)NormalizedParentName->Buffer)[NotifyChange->FullDirectoryName->Length] != '\') + { + continue; + } + } + else + { + if (((PWSTR)NormalizedParentName->Buffer)[NotifyChange->FullDirectoryName->Length / sizeof (WCHAR)] != L'\') + { + continue; + } + } + } + + IsParent = FALSE; + } + + /* If len matches, then check that both name are equal */ + if (!RtlEqualMemory(NormalizedParentName->Buffer, NotifyChange->FullDirectoryName->Buffer, + NotifyChange->FullDirectoryName->Length)) + { + continue; + } + + /* Call traverse callback (only if we have to traverse ;-)) */ + if (!IsParent + && NotifyChange->TraverseCallback != NULL + && !NotifyChange->TraverseCallback(NotifyChange->FsContext, + TargetContext, + NotifyChange->SubjectContext)) + { + continue; + } + + /* And then, filter callback if provided */ + if (NotifyChange->FilterCallback != NULL + && FilterContext != NULL + && !NotifyChange->FilterCallback(NotifyChange->FsContext, FilterContext)) + { + continue; + } + } + /* We have a stream! */ + else + { + ASSERT(NotifyChange->FullDirectoryName == NULL); + if (TargetContext != NotifyChange->SubjectContext) + { + continue; + } + + ParentName.Buffer = NULL; + ParentName.Length = 0; + IsStream = TRUE; + IsParent = FALSE; + } + + /* If we don't have to notify immediately, prepare for output */ + if (!(NotifyChange->Flags & NOTIFY_IMMEDIATELY)) + { + /* If we have something to output... */ + if (NotifyChange->BufferLength) + { + /* Get size of the output */ + NumberOfBytes = 0; + Irp = NULL; + if (!NotifyChange->ThisBufferLength) + { + if (IsListEmpty(&NotifyChange->NotifyIrps)) + { + NumberOfBytes = NotifyChange->BufferLength; + } + else + { + Irp = CONTAINING_RECORD(NotifyChange->NotifyIrps.Flink, IRP, Tail.Overlay.ListEntry); + Stack = IoGetCurrentIrpStackLocation(Irp); + NumberOfBytes = Stack->Parameters.NotifyDirectory.Length; + } + } + else + { + NumberOfBytes = NotifyChange->ThisBufferLength; + } + + /* If we're matching parent, we don't care about parent (redundant) */ + if (IsParent) + { + ParentName.Length = 0; + } + else + { + /* If we don't deal with streams, some more work is required */ + if (!IsStream) + { + if (NotifyChange->Flags & WATCH_ROOT || + (NormalizedParentName->Buffer != FullTargetName->Buffer)) + { + /* Construct TargetDirectory if we don't have it yet */ + if (TargetDirectory.Buffer == NULL) + { + TargetDirectory.Buffer = FullTargetName->Buffer; + TargetDirectory.Length = TargetNameOffset; + if (TargetNameOffset != NotifyChange->CharacterSize) + { + TargetDirectory.Length = TargetNameOffset - NotifyChange->CharacterSize; + } + TargetDirectory.MaximumLength = TargetDirectory.Length; + } + /* Now, we start looking for matching parts (unless we watch root) */ + TargetNumberOfParts = 0; + if (!(NotifyChange->Flags & WATCH_ROOT)) + { + FullNumberOfParts = 1; + if (NotifyChange->CharacterSize == sizeof(CHAR)) + { + FsRtlNotifyGetLastPartOffset(NotifyChange->FullDirectoryName->Length, + TargetDirectory.Length, PSTR, '\'); + } + else + { + FsRtlNotifyGetLastPartOffset(NotifyChange->FullDirectoryName->Length / sizeof(WCHAR), + TargetDirectory.Length / sizeof(WCHAR), PWSTR, L'\'); + LastPartOffset *= NotifyChange->CharacterSize; + } + } + + /* Then, we can construct proper parent name */ + ParentNameOffset = NotifyChange->CharacterSize + LastPartOffset; + ParentName.Buffer = &TargetDirectory.Buffer[ParentNameOffset]; + ParentNameLength = TargetDirectory.Length; + } + else + { + /* Construct parent name even for streams */ + ParentName.Buffer = &NormalizedParentName->Buffer[NotifyChange->FullDirectoryName->Length] + NotifyChange->CharacterSize; + ParentNameLength = NormalizedParentName->Length - NotifyChange->FullDirectoryName->Length; + ParentNameOffset = NotifyChange->CharacterSize; + } + ParentNameLength -= ParentNameOffset; + ParentName.Length = ParentNameLength; + ParentName.MaximumLength = ParentNameLength; + } + } + + /* Start to count amount of data to write, we've first the structure itself */ + DataLength = FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName); + + /* If stream, we'll just append stream name */ + if (IsStream) + { + ASSERT(StreamName != NULL); + DataLength += StreamName->Length; + } + else + { + /* If not parent, we've to append parent name */ + if (!IsParent) + { + if (NotifyChange->CharacterSize == sizeof(CHAR)) + { + DataLength += RtlOemStringToCountedUnicodeSize(&ParentName); + } + else + { + DataLength += ParentName.Length; + } + DataLength += sizeof(WCHAR); + } + + /* Look for target name & construct it, if required */ + if (TargetName.Buffer) + { + TargetNameLength = TargetName.Length; + } + else + { + TargetName.Buffer = &FullTargetName->Buffer[TargetNameOffset]; + TargetNameLength = FullTargetName->Length - TargetNameOffset; + TargetName.Length = TargetNameLength; + TargetName.MaximumLength = TargetNameLength; + } + + /* Then, we will append it as well */ + if (NotifyChange->CharacterSize == sizeof(CHAR)) + { + DataLength += RtlOemStringToCountedUnicodeSize(&TargetName); + } + else + { + DataLength += TargetName.Length; + } + + /* If we also had a stream name, then we can append it as well */ + if (StreamName != NULL) + { + if (NotifyChange->CharacterSize == sizeof(WCHAR)) + { + DataLength += StreamName->Length + sizeof(WCHAR); + } + else + { + DataLength = DataLength + RtlOemStringToCountedUnicodeSize(&TargetName) + sizeof(CHAR); + } + } + } + + /* Get the position where we can put our data (aligned!) */ + AlignedDataLength = ROUND_UP(NotifyChange->DataLength, sizeof(ULONG)); + /* If it's higher than buffer length, then, bail out without outputing */ + if (DataLength > NumberOfBytes || AlignedDataLength + DataLength > NumberOfBytes) + { + NotifyChange->Flags |= NOTIFY_IMMEDIATELY; + } + else + { + OutputBuffer = 0; + FileNotifyInfo = 0; + /* If we already had a buffer, update last entry position */ + if (NotifyChange->Buffer != NULL) + { + FileNotifyInfo = (PVOID)((ULONG_PTR)NotifyChange->Buffer + NotifyChange->LastEntry); + FileNotifyInfo->NextEntryOffset = AlignedDataLength - NotifyChange->LastEntry; + NotifyChange->LastEntry = AlignedDataLength; + /* And get our output buffer */ + OutputBuffer = (PVOID)((ULONG_PTR)NotifyChange->Buffer + AlignedDataLength); + } + /* If we hadn't buffer, try to find one */ + else if (Irp != NULL) + { + if (Irp->AssociatedIrp.SystemBuffer != NULL) + { + OutputBuffer = Irp->AssociatedIrp.SystemBuffer; + } + else if (Irp->MdlAddress != NULL) + { + OutputBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress); + } + + NotifyChange->Buffer = OutputBuffer; + NotifyChange->ThisBufferLength = NumberOfBytes; + } + + /* If we couldn't find one, then allocate one */ + if (NotifyChange->Buffer == NULL) + { + PoolQuotaCharged = FALSE; + _SEH2_TRY + { + PsChargePoolQuota(NotifyChange->OwningProcess, PagedPool, NumberOfBytes); + PoolQuotaCharged = TRUE; + OutputBuffer = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, + NumberOfBytes, 'NrSF'); + NotifyChange->Buffer = OutputBuffer; + NotifyChange->AllocatedBuffer = OutputBuffer; + } + /* If something went wrong during allocation, notify immediately instead of outputing */ + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + if (PoolQuotaCharged) + { + PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NumberOfBytes); + } + NotifyChange->Flags |= NOTIFY_IMMEDIATELY; + } + _SEH2_END; + } + + /* Finally, if we have a buffer, fill it in! */ + if (OutputBuffer != NULL) + { + if (FsRtlNotifyUpdateBuffer((FILE_NOTIFY_INFORMATION *)OutputBuffer, + Action, &ParentName, &TargetName, + StreamName, NotifyChange->CharacterSize == sizeof(WCHAR), + DataLength)) + { + NotifyChange->DataLength = DataLength + AlignedDataLength; + } + /* If it failed, notify immediately */ + else + { + NotifyChange->Flags |= NOTIFY_IMMEDIATELY; + } + } + } + + /* If we have to notify right now (something went wrong?) */ + if (NotifyChange->Flags & NOTIFY_IMMEDIATELY) + { + /* Ensure that all our buffers are NULL */ + if (NotifyChange->Buffer != NULL) + { + if (NotifyChange->AllocatedBuffer != NULL) + { + PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength); + ExFreePoolWithTag(NotifyChange->AllocatedBuffer, 'NrSF'); + } + + NotifyChange->Buffer = NULL; + NotifyChange->AllocatedBuffer = NULL; + NotifyChange->LastEntry = 0; + NotifyChange->DataLength = 0; + NotifyChange->ThisBufferLength = 0; + } + } + } + } + + /* If asking for old name in case of a rename, notify later on, + * so that we can wait for new name. + * http://msdn.microsoft.com/en-us/library/dn392331.aspx + */ + if (Action == FILE_ACTION_RENAMED_OLD_NAME) + { + NotifyChange->Flags |= NOTIFY_LATER; + } + else + { + NotifyChange->Flags &= ~NOTIFY_LATER; + if (!IsListEmpty(&NotifyChange->NotifyIrps)) + { + FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_SUCCESS); + } + } + } + } + _SEH2_FINALLY + { + FsRtlNotifyReleaseFastMutex(RealNotifySync); + } + _SEH2_END; }
/*++
Modified: trunk/reactos/ntoskrnl/include/internal/fsrtl.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/f... ============================================================================== --- trunk/reactos/ntoskrnl/include/internal/fsrtl.h [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/include/internal/fsrtl.h [iso-8859-1] Fri Mar 7 19:33:38 2014 @@ -53,9 +53,9 @@ // Notifications flags // #define WATCH_TREE 0x01 -#define INVALIDATE_BUFFERS 0x02 +#define NOTIFY_IMMEDIATELY 0x02 #define CLEANUP_IN_PROCESS 0x04 -#define ENUMERATE_DIR 0x08 +#define NOTIFY_LATER 0x08 #define WATCH_ROOT 0x10 #define DELETE_IN_PROCESS 0x20