#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
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
*
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 */
+ ExFreePoolWithTag(NotifyChange, 'FSrN');
+ }
+ }
+ }
+ _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;
}
/*++
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;
}
/*++
/*++
* @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
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
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
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
NTAPI
FsRtlNotifyUninitializeSync(IN PNOTIFY_SYNC *NotifySync)
{
- KeBugCheck(FILE_SYSTEM);
+ if (*NotifySync)
+ {
+ ExFreePoolWithTag(*NotifySync, 'FSNS');
+ *NotifySync = NULL;
+ }
}