[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / fsrtl / notify.c
index a20889d..3fdb49e 100644 (file)
 #define NDEBUG
 #include <debug.h>
 
+/* PRIVATE FUNCTIONS *********************************************************/
+
+VOID
+FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
+                           IN NTSTATUS Status);
+
+BOOLEAN
+FsRtlNotifySetCancelRoutine(IN PIRP Irp,
+                            IN PNOTIFY_CHANGE NotifyChange OPTIONAL);
+
+VOID
+NTAPI
+FsRtlCancelNotify(IN PDEVICE_OBJECT DeviceObject,
+                  IN PIRP Irp)
+{
+    IoReleaseCancelSpinLock(Irp->CancelIrql);
+    UNIMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+VOID
+FsRtlCheckNotifyForDelete(IN PLIST_ENTRY NotifyList,
+                          IN PVOID FsContext)
+{
+    PLIST_ENTRY NextEntry;
+    PNOTIFY_CHANGE NotifyChange;
+
+    if (!IsListEmpty(NotifyList))
+    {
+        /* Browse the notifications list to find the matching entry */
+        for (NextEntry = NotifyList->Flink;
+             NextEntry != NotifyList;
+             NextEntry = NextEntry->Flink)
+        {
+            NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
+            /* If the current record matches with the given context, it's the good one */
+            if (NotifyChange->FsContext == FsContext && !IsListEmpty(&(NotifyChange->NotifyIrps)))
+            {
+                FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_DELETE_PENDING);
+            }
+        }
+    }
+}
+
+PNOTIFY_CHANGE
+FsRtlIsNotifyOnList(IN PLIST_ENTRY NotifyList,
+                    IN PVOID FsContext)
+{
+    PLIST_ENTRY NextEntry;
+    PNOTIFY_CHANGE NotifyChange;
+
+    if (!IsListEmpty(NotifyList))
+    {
+        /* Browse the notifications list to find the matching entry */
+        for (NextEntry = NotifyList->Flink;
+             NextEntry != NotifyList;
+             NextEntry = NextEntry->Flink)
+        {
+            NotifyChange = CONTAINING_RECORD(NextEntry, NOTIFY_CHANGE, NotifyList);
+            /* If the current record matches with the given context, it's the good one */
+            if (NotifyChange->FsContext == FsContext)
+            {
+                return NotifyChange;
+            }
+        }
+    }
+    return NULL;
+}
+
+VOID
+FORCEINLINE
+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
+ */
+VOID
+FsRtlNotifyCompleteIrp(IN PIRP Irp,
+                       IN PNOTIFY_CHANGE NotifyChange,
+                       IN ULONG DataLength,
+                       IN NTSTATUS Status,
+                       IN BOOLEAN SkipCompletion)
+{
+    PVOID Buffer;
+    PIO_STACK_LOCATION Stack;
+
+    PAGED_CODE();
+
+    /* Check if we need to complete */
+    if (!FsRtlNotifySetCancelRoutine(Irp, NotifyChange) && SkipCompletion)
+    {
+        return;
+    }
+
+    /* No succes => no data to return just complete */
+    if (Status != STATUS_SUCCESS)
+    {
+        goto Completion;
+    }
+
+    /* Ensure there's something to return */
+    Stack = IoGetCurrentIrpStackLocation(Irp);
+    if (!DataLength || Stack->Parameters.NotifyDirectory.Length < DataLength)
+    {
+        Status = STATUS_NOTIFY_ENUM_DIR;
+        goto Completion;
+    }
+
+    /* Ensture there's a buffer where to find data */
+    if (!NotifyChange->AllocatedBuffer)
+    {
+        Irp->IoStatus.Information = DataLength;
+        NotifyChange->Buffer = NULL;
+        goto Completion;
+    }
+
+    /* Now, browse all the way to return data
+     * and find the one that will work. We will
+     * return data whatever happens
+     */
+    if (Irp->AssociatedIrp.SystemBuffer)
+    {
+        Buffer = Irp->AssociatedIrp.SystemBuffer;
+        goto CopyAndComplete;
+    }
+
+    if (Irp->MdlAddress)
+    {
+        Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
+        goto CopyAndComplete;
+    }
+
+    if (!(Stack->Control & SL_PENDING_RETURNED))
+    {
+        Buffer = Irp->UserBuffer;
+        goto CopyAndComplete;
+    }
+
+    Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_SYNCHRONOUS_PAGING_IO);
+    Irp->AssociatedIrp.SystemBuffer = NotifyChange->AllocatedBuffer;
+    /* Nothing to copy */
+    goto ReleaseAndComplete;
+
+CopyAndComplete:
+    _SEH2_TRY
+    {
+        RtlCopyMemory(Buffer, NotifyChange->AllocatedBuffer, DataLength);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Do nothing */
+    }
+    _SEH2_END;
+
+ReleaseAndComplete:
+    PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
+
+    /* Release buffer UNLESS it's used */
+    if (NotifyChange->AllocatedBuffer != Irp->AssociatedIrp.SystemBuffer &&
+        NotifyChange->AllocatedBuffer)
+    {
+        ExFreePoolWithTag(NotifyChange->AllocatedBuffer, 0);
+    }
+
+    /* Prepare for return */
+    NotifyChange->AllocatedBuffer = 0;
+    NotifyChange->ThisBufferLength = 0;
+    Irp->IoStatus.Information = DataLength;
+    NotifyChange->Buffer = NULL;
+
+    /* Finally complete */
+Completion:
+    IoMarkIrpPending(Irp);
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, EVENT_INCREMENT);
+}
+
+/*
+ * @implemented
+ */
+VOID
+FsRtlNotifyCompleteIrpList(IN PNOTIFY_CHANGE NotifyChange,
+                           IN NTSTATUS Status)
+{
+    PIRP Irp;
+    ULONG DataLength;
+    PLIST_ENTRY NextEntry;
+
+    DataLength = NotifyChange->DataLength;
+
+    NotifyChange->Flags &= (INVALIDATE_BUFFERS | WATCH_TREE);
+    NotifyChange->DataLength = 0;
+    NotifyChange->LastEntry = 0;
+
+    while (!IsListEmpty(&(NotifyChange->NotifyIrps)))
+    {
+        /* We take the first entry */
+        NextEntry = RemoveHeadList(&(NotifyChange->NotifyIrps));
+        Irp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
+        /* We complete it */
+        FsRtlNotifyCompleteIrp(Irp, NotifyChange, DataLength, Status, TRUE);
+        /* If we're notifying success, just notify first one */
+        if (Status == STATUS_SUCCESS)
+            break;
+    }
+}
+
+VOID
+FORCEINLINE
+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
+ */
+BOOLEAN
+FsRtlNotifySetCancelRoutine(IN PIRP Irp,
+                            IN PNOTIFY_CHANGE NotifyChange OPTIONAL)
+{
+    PDRIVER_CANCEL CancelRoutine;
+
+    /* Acquire cancel lock */
+    IoAcquireCancelSpinLock(&Irp->CancelIrql);
+
+    /* If NotifyChange was given */
+    if (NotifyChange)
+    {
+        /* First get cancel routine */
+        CancelRoutine = IoSetCancelRoutine(Irp, NULL);
+        Irp->IoStatus.Information = 0;
+        /* Release cancel lock */
+        IoReleaseCancelSpinLock(Irp->CancelIrql);
+        /* If there was a cancel routine */
+        if (CancelRoutine)
+        {
+            /* Decrease reference count */
+            InterlockedDecrement((PLONG)&NotifyChange->ReferenceCount);
+            /* Notify that we removed cancel routine */
+            return TRUE;
+        }
+    }
+    else
+    {
+        /* If IRP is cancel, call FsRtl cancel routine */
+        if (Irp->Cancel)
+        {
+             FsRtlCancelNotify(NULL, Irp);
+        }
+        else
+        {
+            /* Otherwise, define FsRtl cancel routine as IRP cancel routine */
+            IoSetCancelRoutine(Irp, FsRtlCancelNotify);
+            /* Release lock */
+            IoReleaseCancelSpinLock(Irp->CancelIrql);
+        }
+    }
+
+    /* Return that we didn't removed cancel routine */
+    return FALSE;
+}
+
 /* PUBLIC FUNCTIONS **********************************************************/
 
 /*++
  *
  * @return None
  *
- * @remarks This function only redirects to FsRtlNotifyFullChangeDirectory.
- * So, it's better to call the entire function.  
+ * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
  *
  *--*/
 VOID
@@ -58,32 +340,33 @@ FsRtlNotifyChangeDirectory(IN PNOTIFY_SYNC NotifySync,
                            IN ULONG CompletionFilter,
                            IN PIRP NotifyIrp)
 {
-    FsRtlNotifyFullChangeDirectory(NotifySync,
-                                   NotifyList,
-                                   FsContext,
-                                   FullDirectoryName,
-                                   WatchTree,
-                                   TRUE,
-                                   CompletionFilter,
-                                   NotifyIrp,
-                                   NULL,
-                                   NULL);
+    FsRtlNotifyFilterChangeDirectory(NotifySync,
+                                     NotifyList,
+                                     FsContext,
+                                     FullDirectoryName,
+                                     WatchTree,
+                                     TRUE,
+                                     CompletionFilter,
+                                     NotifyIrp,
+                                     NULL,
+                                     NULL,
+                                     NULL);
 }
 
 /*++
  * @name FsRtlNotifyCleanup
- * @unimplemented
+ * @implemented
  *
  * Called by FSD when all handles to FileObject (identified by FsContext) are closed
  *
  * @param NotifySync
- *        FILLME
+ *        Synchronization object pointer
  *
  * @param NotifyList
- *        FILLME
+ *        Notify list pointer (to head) 
  *
  * @param FsContext
- *        FILLME
+ *        Used to identify the notify structure
  *
  * @return None
  *
@@ -96,7 +379,68 @@ FsRtlNotifyCleanup(IN PNOTIFY_SYNC NotifySync,
                    IN PLIST_ENTRY NotifyList,
                    IN PVOID FsContext)
 {
-    KeBugCheck(FILE_SYSTEM);
+    PNOTIFY_CHANGE NotifyChange;
+    PREAL_NOTIFY_SYNC RealNotifySync;
+    PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
+
+    /* Get real structure hidden behind the opaque pointer */
+    RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
+
+    /* Acquire the fast mutex */
+    FsRtlNotifyAcquireFastMutex(RealNotifySync);
+
+    _SEH2_TRY
+    {
+        /* Find if there's a matching notification with the FsContext */
+        NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
+        if (NotifyChange)
+        {
+            /* Mark it as to know that cleanup is in process */
+            NotifyChange->Flags |= CLEANUP_IN_PROCESS;
+
+            /* If there are pending IRPs, complete them using the STATUS_NOTIFY_CLEANUP status */
+            if (!IsListEmpty(&NotifyChange->NotifyIrps))
+            {
+                FsRtlNotifyCompleteIrpList(NotifyChange, STATUS_NOTIFY_CLEANUP);
+            }
+
+            /* Decrease reference number and if 0 is reached, it's time to do complete cleanup */
+            if (!InterlockedDecrement((PLONG)&(NotifyChange->ReferenceCount)))
+            {
+                /* Remove it from the notifications list */
+                RemoveEntryList(&NotifyChange->NotifyList);
+
+                /* In case there was an allocated buffer, free it */
+                if (NotifyChange->AllocatedBuffer)
+                {
+                    PsReturnProcessPagedPoolQuota(NotifyChange->OwningProcess, NotifyChange->ThisBufferLength);
+                    ExFreePool(NotifyChange->AllocatedBuffer);
+                }
+
+                /* In case there the string was set, get the captured subject security context */
+                if (NotifyChange->FullDirectoryName)
+                {
+                    SubjectContext = NotifyChange->SubjectContext;
+                }
+
+                /* Finally, free the notification, as it's not needed anymore */
+                ExFreePool(NotifyChange);
+            }
+        }
+    }
+    _SEH2_FINALLY
+    {
+        /* Release fast mutex */
+        FsRtlNotifyReleaseFastMutex(RealNotifySync);
+
+        /* If the subject security context was captured, release and free it */
+        if (SubjectContext)
+        {
+            SeReleaseSubjectContext(SubjectContext);
+            ExFreePool(SubjectContext);
+        }
+    }
+    _SEH2_END;
 }
 
 /*++
@@ -157,7 +501,180 @@ FsRtlNotifyFilterChangeDirectory(IN PNOTIFY_SYNC NotifySync,
                                  IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
                                  IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL)
 {
-    KeBugCheck(FILE_SYSTEM);
+    ULONG SavedLength;
+    PIO_STACK_LOCATION Stack;
+    PNOTIFY_CHANGE NotifyChange;
+    PREAL_NOTIFY_SYNC RealNotifySync;
+
+    PAGED_CODE();
+
+    DPRINT("FsRtlNotifyFilterChangeDirectory(): %p, %p, %p, %wZ, %d, %d, %u, %p, %p, %p, %p\n",
+    NotifySync, NotifyList, FsContext, FullDirectoryName, WatchTree, IgnoreBuffer, CompletionFilter, NotifyIrp,
+    TraverseCallback, SubjectContext, FilterCallback);
+
+    /* Get real structure hidden behind the opaque pointer */
+    RealNotifySync = (PREAL_NOTIFY_SYNC)NotifySync;
+
+    /* Acquire the fast mutex */
+    FsRtlNotifyAcquireFastMutex(RealNotifySync);
+
+    _SEH2_TRY
+    {
+        /* If we have no IRP, FSD is performing a cleanup */
+        if (!NotifyIrp)
+        {
+            /* So, we delete */
+            FsRtlCheckNotifyForDelete(NotifyList, FsContext);
+            _SEH2_LEAVE;
+        }
+
+        NotifyIrp->IoStatus.Status = STATUS_SUCCESS;
+        NotifyIrp->IoStatus.Information = (ULONG_PTR)NULL;
+
+        Stack = IoGetCurrentIrpStackLocation(NotifyIrp);
+        /* If FileObject's been cleaned up, just return */
+        if (Stack->FileObject->Flags & FO_CLEANUP_COMPLETE)
+        {
+            IoMarkIrpPending(NotifyIrp);
+            NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
+            IoCompleteRequest(NotifyIrp, EVENT_INCREMENT);
+            _SEH2_LEAVE;
+        }
+
+        /* Try to find a matching notification has been already registered */
+        NotifyChange = FsRtlIsNotifyOnList(NotifyList, FsContext);
+        if (NotifyChange)
+        {
+            /* If it's been found, and is cleaned up, immediatly complete */
+            if (NotifyChange->Flags & CLEANUP_IN_PROCESS)
+            {
+                IoMarkIrpPending(NotifyIrp);
+                NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP;
+                IoCompleteRequest(NotifyIrp, EVENT_INCREMENT);
+            }
+            /* Or if it's about to be deleted, complete */
+            else if (NotifyChange->Flags & DELETE_IN_PROCESS)
+            {
+                IoMarkIrpPending(NotifyIrp);
+                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;
+                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))
+            {
+                goto HandleIRP;
+            }
+            /* Else, just complete with we have */
+            else
+            {
+                SavedLength = NotifyChange->DataLength;
+                NotifyChange->DataLength = 0;
+                FsRtlNotifyCompleteIrp(NotifyIrp, NotifyChange, SavedLength, STATUS_SUCCESS, FALSE);
+            }
+
+            _SEH2_LEAVE;
+        }
+
+        /* Allocate new notification */
+        NotifyChange = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
+                                             sizeof(NOTIFY_CHANGE), 'FSrN');
+        RtlZeroMemory(NotifyChange, sizeof(NOTIFY_CHANGE));
+
+        /* Set basic information */
+        NotifyChange->NotifySync = NotifySync;
+        NotifyChange->FsContext = FsContext;
+        NotifyChange->StreamID = Stack->FileObject->FsContext;
+        NotifyChange->TraverseCallback = TraverseCallback;
+        NotifyChange->SubjectContext = SubjectContext;
+        NotifyChange->FullDirectoryName = FullDirectoryName;
+        NotifyChange->FilterCallback = FilterCallback;
+        InitializeListHead(&(NotifyChange->NotifyIrps));
+
+        /* Keep trace of WatchTree */
+        if (WatchTree)
+        {
+            NotifyChange->Flags |= WATCH_TREE;
+        }
+
+        /* If string is empty, faulty to ANSI */
+        if (FullDirectoryName->Length == 0)
+        {
+            NotifyChange->CharacterSize = sizeof(CHAR);
+        }
+        else
+        {
+            /* If it can't contain WCHAR, it's ANSI */
+            if (FullDirectoryName->Length < sizeof(WCHAR))
+            {
+                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)
+            {
+                NotifyChange->CharacterSize = sizeof(WCHAR);
+            }
+            else
+            {
+                NotifyChange->CharacterSize = sizeof(CHAR);
+            }
+
+            /* Now, check is user is willing to watch root */
+            if (FullDirectoryName->Length == NotifyChange->CharacterSize)
+            {
+                NotifyChange->Flags |= WATCH_ROOT;
+            }
+        }
+
+        NotifyChange->CompletionFilter = CompletionFilter;
+
+        /* In case we have not to ignore buffer , keep its length */
+        if (!IgnoreBuffer)
+        {
+            NotifyChange->BufferLength = Stack->Parameters.NotifyDirectory.Length;
+        }
+
+        NotifyChange->OwningProcess = NotifyIrp->Tail.Overlay.Thread->ThreadsProcess;
+
+        /* Insert the notification into the notification list */
+        InsertTailList(NotifyList, &(NotifyChange->NotifyList));
+
+        NotifyChange->ReferenceCount = 1;
+
+HandleIRP:
+        /* Associate the notification to the IRP */
+        NotifyIrp->IoStatus.Information = (ULONG_PTR)NotifyChange;
+        /* The IRP is pending */
+        IoMarkIrpPending(NotifyIrp);
+        /* Insert the IRP in the IRP list */
+        InsertTailList(&(NotifyChange->NotifyIrps), &(NotifyIrp->Tail.Overlay.ListEntry));
+        /* Increment reference count */
+        InterlockedIncrement((PLONG)&(NotifyChange->ReferenceCount));
+        /* Set cancel routine to FsRtl one */
+        FsRtlNotifySetCancelRoutine(NotifyIrp, NULL);
+    }
+    _SEH2_FINALLY
+    {
+        /* Release fast mutex */
+        FsRtlNotifyReleaseFastMutex(RealNotifySync);
+
+        /* If the subject security context was captured and there's no notify */
+        if (SubjectContext && (!NotifyChange || FullDirectoryName))
+        {
+            SeReleaseSubjectContext(SubjectContext);
+            ExFreePool(SubjectContext);
+        }
+    }
+    _SEH2_END;
 }
 
 /*++
@@ -219,43 +736,46 @@ FsRtlNotifyFilterReportChange(IN PNOTIFY_SYNC NotifySync,
 
 /*++
  * @name FsRtlNotifyFullChangeDirectory
- * @unimplemented
+ * @implemented
  *
- * FILLME
+ * Lets FSD know if changes occures in the specified directory. 
  *
  * @param NotifySync
- *        FILLME
+ *        Synchronization object pointer
  *
  * @param NotifyList
- *        FILLME
+ *        Notify list pointer (to head) 
  *
  * @param FsContext
- *        FILLME
+ *        Used to identify the notify structure
  *
  * @param FullDirectoryName
- *        FILLME
+ *        String (A or W) containing the full directory name 
  *
  * @param WatchTree
- *        FILLME
+ *        True to notify changes in subdirectories too
  *
  * @param IgnoreBuffer
- *        FILLME
+ *        True to reenumerate directory. It's ignored it NotifyIrp is null
  *
  * @param CompletionFilter
- *        FILLME
+ *        Used to define types of changes to notify
  *
- * @param Irp
- *        FILLME
+ * @param NotifyIrp
+ *        IRP pointer to complete notify operation. It can be null
  *
  * @param TraverseCallback
- *        FILLME
+ *        Pointer to a callback function. It's called each time a change is
+ *        done in a subdirectory of the main directory. It's ignored it NotifyIrp
+ *        is null
  *
  * @param SubjectContext
- *        FILLME
+ *        Pointer to pass to SubjectContext member of TraverseCallback.
+ *        It's freed after use. It's ignored it NotifyIrp is null
  *
  * @return None
  *
- * @remarks None
+ * @remarks This function only redirects to FsRtlNotifyFilterChangeDirectory.
  *
  *--*/
 VOID
@@ -267,49 +787,60 @@ FsRtlNotifyFullChangeDirectory(IN PNOTIFY_SYNC NotifySync,
                                IN BOOLEAN WatchTree,
                                IN BOOLEAN IgnoreBuffer,
                                IN ULONG CompletionFilter,
-                               IN PIRP Irp,
+                               IN PIRP NotifyIrp,
                                IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL,
                                IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL)
 {
-    KeBugCheck(FILE_SYSTEM);
+    FsRtlNotifyFilterChangeDirectory(NotifySync,
+                                     NotifyList,
+                                     FsContext,
+                                     FullDirectoryName,
+                                     WatchTree,
+                                     IgnoreBuffer,
+                                     CompletionFilter,
+                                     NotifyIrp,
+                                     TraverseCallback,
+                                     SubjectContext,
+                                     NULL);
 }
 
 /*++
  * @name FsRtlNotifyFullReportChange
- * @unimplemented
+ * @implemented
  *
- * FILLME
+ * Complets the pending notify IRPs.
  *
  * @param NotifySync
- *        FILLME
+ *        Synchronization object pointer
  *
  * @param NotifyList
- *        FILLME
+ *        Notify list pointer (to head) 
  *
  * @param FullTargetName
- *        FILLME
+ *        String (A or W) containing the full directory name that changed
  *
  * @param TargetNameOffset
- *        FILLME
+ *        Offset, in FullTargetName, of the final component that is in the changed directory 
  *
  * @param StreamName
- *        FILLME
+ *        String (A or W) containing a stream name
  *
  * @param NormalizedParentName
- *        FILLME
+ *        String (A or W) containing the full directory name that changed with long names
  *
  * @param FilterMatch
- *        FILLME
+ *        Flags that will be compared to the completion filter
  *
  * @param Action
- *        FILLME
+ *        Action code to store in user's buffer
  *
  * @param TargetContext
- *        FILLME
+ *        Pointer to a callback function. It's called each time a change is
+ *        done in a subdirectory of the main directory.
  *
  * @return None
  *
- * @remarks None
+ * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
  *
  *--*/
 VOID
@@ -324,54 +855,73 @@ FsRtlNotifyFullReportChange(IN PNOTIFY_SYNC NotifySync,
                             IN ULONG Action,
                             IN PVOID TargetContext)
 {
-    KeBugCheck(FILE_SYSTEM);
+    FsRtlNotifyFilterReportChange(NotifySync,
+                                  NotifyList,
+                                  FullTargetName,
+                                  TargetNameOffset,
+                                  StreamName,
+                                  NormalizedParentName,
+                                  FilterMatch,
+                                  Action,
+                                  TargetContext,
+                                  NULL);
 }
 
 /*++
  * @name FsRtlNotifyInitializeSync
- * @unimplemented
+ * @implemented
  *
- * FILLME
+ * Allocates the internal structure associated with notifications.
  *
  * @param NotifySync
- *        FILLME
+ *        Opaque pointer. It will receive the address of the allocated internal structure.
  *
  * @return None
  *
- * @remarks None
+ * @remarks This function raise an exception in case of a failure.
  *
  *--*/
 VOID
 NTAPI
 FsRtlNotifyInitializeSync(IN PNOTIFY_SYNC *NotifySync)
 {
-    KeBugCheck(FILE_SYSTEM);
+    PREAL_NOTIFY_SYNC RealNotifySync;
+
+    *NotifySync = NULL;
+    
+    RealNotifySync = ExAllocatePoolWithTag(NonPagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
+                                           sizeof(REAL_NOTIFY_SYNC), 'FSNS');
+    ExInitializeFastMutex(&(RealNotifySync->FastMutex));
+    RealNotifySync->OwningThread = 0;
+    RealNotifySync->OwnerCount = 0;
+
+    *NotifySync = RealNotifySync;
 }
 
 /*++
  * @name FsRtlNotifyReportChange
- * @unimplemented
+ * @implemented
  *
- * FILLME
+ * Complets the pending notify IRPs.
  *
  * @param NotifySync
- *        FILLME
+ *        Synchronization object pointer
  *
  * @param NotifyList
- *        FILLME
+ *        Notify list pointer (to head) 
  *
  * @param FullTargetName
- *        FILLME
+ *        String (A or W) containing the full directory name that changed
  *
  * @param FileNamePartLength
- *        FILLME
+ *        Length of the final component that is in the changed directory
  *
  * @param FilterMatch
- *        FILLME
+ *        Flags that will be compared to the completion filter
  *
  * @return None
  *
- * @remarks None
+ * @remarks This function only redirects to FsRtlNotifyFilterReportChange.
  *
  *--*/
 VOID
@@ -382,11 +932,20 @@ FsRtlNotifyReportChange(IN PNOTIFY_SYNC NotifySync,
                         IN PUSHORT FileNamePartLength,
                         IN ULONG FilterMatch)
 {
-    KeBugCheck(FILE_SYSTEM);
+      FsRtlNotifyFilterReportChange(NotifySync,
+                                    NotifyList,
+                                    FullTargetName,
+                                    FullTargetName->Length - *FileNamePartLength,
+                                    NULL,
+                                    NULL,
+                                    FilterMatch,
+                                    0,
+                                    NULL,
+                                    NULL); 
 }
 
 /*++
- * @name FsRtlCurrentBatchOplock
+ * @name FsRtlNotifyUninitializeSync
  * @implemented
  *
  * Uninitialize a NOTIFY_SYNC object
@@ -404,6 +963,10 @@ VOID
 NTAPI
 FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync)
 {
-    KeBugCheck(FILE_SYSTEM);
+    if (*NotifySync)
+    {
+        ExFreePoolWithTag(*NotifySync, 'FSNS');
+        *NotifySync = NULL;
+    }
 }