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?re…
==============================================================================
--- 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/…
==============================================================================
--- 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