[ros-dev] [ros-diffs] [pschweitzer] 62442: [NTOSKRNL] - Implement FsRtlNotifyFilterReportChange - Implement FsRtlNotifyUpdateBuffer - Implement FsRtlCancelNotify - Implement FsRtlNotifyGetLastPartOffset - Fix implementa...
Jérôme Gardou
jerome.gardou at reactos.org
Fri Mar 7 19:37:59 UTC 2014
Awesomeness, in its purest art.
Le 07.03.2014 20:33, pschweitzer at svn.reactos.org a écrit :
> 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=62442&r1=62441&r2=62442&view=diff
> ==============================================================================
> --- 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/fsrtl.h?rev=62442&r1=62441&r2=62442&view=diff
> ==============================================================================
> --- 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
>
>
>
More information about the Ros-dev
mailing list