From: Gunnar Dalsnes Date: Mon, 28 Feb 2005 21:00:59 +0000 (+0000) Subject: init. work on a notify-directory impl. X-Git-Tag: backups/alex_devel_branch@15093~18^2~9 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=799674bef89f0103c6961b71c781fa23388d527e init. work on a notify-directory impl. svn path=/trunk/; revision=13782 --- diff --git a/reactos/ntoskrnl/fs/notify.c b/reactos/ntoskrnl/fs/notify.c index 401dce9252d..548b4c0da95 100644 --- a/reactos/ntoskrnl/fs/notify.c +++ b/reactos/ntoskrnl/fs/notify.c @@ -5,13 +5,125 @@ * FILE: ntoskrnl/fs/notify.c * PURPOSE: No purpose listed. * - * PROGRAMMERS: No programmer listed. + * PROGRAMMERS: Gunnar Dalsnes */ #include #define NDEBUG #include + +PAGED_LOOKASIDE_LIST NotifyEntryLookaside; + +typedef struct _NOTIFY_ENTRY +{ + LIST_ENTRY ListEntry; + PSTRING FullDirectoryName; + BOOLEAN WatchTree; + BOOLEAN IgnoreBuffer; + ULONG CompletionFilter; + LIST_ENTRY IrpQueue; + PVOID Fcb; + PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback; + PSECURITY_SUBJECT_CONTEXT SubjectContext; + PVOID FsContext; + LIST_ENTRY BufferedChangesList; +} NOTIFY_ENTRY, *PNOTIFY_ENTRY; + +typedef struct _BUFFERED_CHANGE +{ + LIST_ENTRY ListEntry; + ULONG Action; + USHORT NameLen; + WCHAR RelativeName[1]; + +} BUFFERED_CHANGE, *PBUFFERED_CHANGE; + + +/********************************************************************** + * NAME PRIVATE + * FsRtlpInitNotifyImplementation + * + */ +VOID +STDCALL INIT_FUNCTION +FsRtlpInitNotifyImplementation(VOID) +{ + ExInitializePagedLookasideList( &NotifyEntryLookaside, + NULL, + NULL, + 0, + sizeof(NOTIFY_ENTRY), + 0 /* FSRTL_NOTIFY_TAG*/, + 0 + ); + + +} + + + + + +/********************************************************************** + * NAME PRIVATE + * FsRtlpNotifyCancelRoutine + * + */ +static +VOID +STDCALL +FsRtlpNotifyCancelRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) +{ + PFAST_MUTEX Lock; + + //don't need this since we have our own sync. protecting irp cancellation + IoReleaseCancelSpinLock(Irp->CancelIrql); + + Lock = (PFAST_MUTEX)Irp->Tail.Overlay.DriverContext[3]; + + ExAcquireFastMutex(Lock ); + + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); + + ExReleaseFastMutex(Lock); + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + +} + + + +static +PNOTIFY_ENTRY +FASTCALL +FsRtlpFindNotifyEntry( + PLIST_ENTRY NotifyList, + PVOID FsContext + ) +{ + PLIST_ENTRY EnumEntry; + PNOTIFY_ENTRY NotifyEntry; + + LIST_FOR_EACH(EnumEntry, NotifyList) + { + NotifyEntry = CONTAINING_RECORD(EnumEntry, NOTIFY_ENTRY, ListEntry); + + if (NotifyEntry->FsContext == FsContext) + { + return NotifyEntry; + } + } + + return NULL; +} + /********************************************************************** * NAME EXPORTED * FsRtlNotifyChangeDirectory@28 @@ -57,7 +169,8 @@ FsRtlNotifyChangeDirectory ( * FsRtlNotifyCleanup@12 * * DESCRIPTION - * + * Called by FSD when all handles to FileObject (identified by FsContext) are closed + * * ARGUMENTS * * RETURN VALUE @@ -72,6 +185,64 @@ FsRtlNotifyCleanup ( IN PVOID FsContext ) { + PNOTIFY_ENTRY NotifyEntry; + LIST_ENTRY CompletedListHead; + PLIST_ENTRY TmpEntry; + PBUFFERED_CHANGE BufferedChange; + PIRP Irp; + + InitializeListHead(&CompletedListHead); + + ExAcquireFastMutex((PFAST_MUTEX)NotifySync); + + NotifyEntry = FsRtlpFindNotifyEntry(NotifyList, FsContext); + + if (NotifyEntry) + { + /* free buffered changes */ + while (!IsListEmpty(&NotifyEntry->BufferedChangesList)) + { + TmpEntry = RemoveHeadList(&NotifyEntry->BufferedChangesList); + BufferedChange = CONTAINING_RECORD(TmpEntry , BUFFERED_CHANGE, ListEntry); + ExFreePool(BufferedChange); + } + + /* cancel(?) pending irps */ + while (!IsListEmpty(&NotifyEntry->IrpQueue)) + { + TmpEntry = RemoveHeadList(&NotifyEntry->IrpQueue); + Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry); + + /* irp cancelation bolilerplate */ + if (!IoSetCancelRoutine(Irp, NULL)) + { + //The cancel routine will be called. When we release the lock it will complete the irp. + InitializeListHead(&Irp->Tail.Overlay.ListEntry); + continue; + } + + Irp->IoStatus.Status = STATUS_NOTIFY_CLEANUP; /* FIXME: correct status? */ + Irp->IoStatus.Information = 0; + + /* avoid holding lock while completing irp */ + InsertTailList(&CompletedListHead, &Irp->Tail.Overlay.ListEntry); + } + + /* Unlink and free the NotifyStruct */ + RemoveEntryList(&NotifyEntry->ListEntry); + ExFreeToPagedLookasideList(&NotifyEntryLookaside, NotifyEntry); + } + + ExReleaseFastMutex((PFAST_MUTEX)NotifySync); + + /* complete defered irps */ + while (!IsListEmpty(&CompletedListHead)) + { + TmpEntry = RemoveHeadList(&CompletedListHead); + Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + } @@ -119,6 +290,75 @@ FsRtlNotifyFilterReportChange ( } + +static +VOID +FASTCALL +FsRtlpWatchedDirectoryWasDeleted( + IN PNOTIFY_SYNC NotifySync, + IN PLIST_ENTRY NotifyList, + IN PVOID Fcb + ) +{ + LIST_ENTRY CompletedListHead; + PLIST_ENTRY EnumEntry, TmpEntry; + PNOTIFY_ENTRY NotifyEntry; + PBUFFERED_CHANGE BufferedChange; + PIRP Irp; + + InitializeListHead(&CompletedListHead); + + ExAcquireFastMutex((PFAST_MUTEX)NotifySync); + + LIST_FOR_EACH_SAFE(EnumEntry, NotifyList, NotifyEntry, NOTIFY_ENTRY, ListEntry ) + { + if (NotifyEntry->Fcb == Fcb) + { + RemoveEntryList(&NotifyEntry->ListEntry); + + /* free buffered changes */ + while (!IsListEmpty(&NotifyEntry->BufferedChangesList)) + { + TmpEntry = RemoveHeadList(&NotifyEntry->BufferedChangesList); + BufferedChange = CONTAINING_RECORD(TmpEntry , BUFFERED_CHANGE, ListEntry); + ExFreePool(BufferedChange); + } + + /* cancel(?) pending irps */ + while (!IsListEmpty(&NotifyEntry->IrpQueue)) + { + TmpEntry = RemoveHeadList(&NotifyEntry->IrpQueue); + Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry); + + /* irp cancelation bolilerplate */ + if (!IoSetCancelRoutine(Irp, NULL)) + { + //The cancel routine will be called. When we release the lock it will complete the irp. + InitializeListHead(&Irp->Tail.Overlay.ListEntry); + continue; + } + + Irp->IoStatus.Status = STATUS_NOTIFY_CLEANUP; /* FIXME: correct status? */ + Irp->IoStatus.Information = 0; + + /* avoid holding lock while completing irp */ + InsertTailList(&CompletedListHead, &Irp->Tail.Overlay.ListEntry); + } + } + } + + ExReleaseFastMutex((PFAST_MUTEX)NotifySync); + + /* complete defered irps */ + while (!IsListEmpty(&CompletedListHead)) + { + TmpEntry = RemoveHeadList(&CompletedListHead); + Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry); + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + +} + /********************************************************************** * NAME EXPORTED * FsRtlNotifyFullChangeDirectory@40 @@ -137,21 +377,183 @@ FsRtlNotifyFullChangeDirectory ( IN PNOTIFY_SYNC NotifySync, IN PLIST_ENTRY NotifyList, IN PVOID FsContext, - IN PSTRING FullDirectoryName, - IN BOOLEAN WatchTree, - IN BOOLEAN IgnoreBuffer, - IN ULONG CompletionFilter, - IN PIRP NotifyIrp, - IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL, + IN PSTRING FullDirectoryName, + IN BOOLEAN WatchTree, + IN BOOLEAN IgnoreBuffer, + IN ULONG CompletionFilter, + IN PIRP NotifyIrp, + IN PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback OPTIONAL, IN PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL ) { -#if defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1300) - DbgPrint("%s()\n", __FUNCTION__); -#else - DbgPrint("FsRtlNotifyFullChangeDirectory()\n"); -#endif + PIO_STACK_LOCATION IrpStack; + PNOTIFY_ENTRY NotifyEntry; + PBUFFERED_CHANGE BufferedChange; + PLIST_ENTRY TmpEntry; + + if (!NotifyIrp) + { + /* all other params are ignored if NotifyIrp == NULL */ + FsRtlpWatchedDirectoryWasDeleted(NotifySync, NotifyList, FsContext); + return; + } + + ExAcquireFastMutex((PFAST_MUTEX)NotifySync); + + IrpStack = IoGetCurrentIrpStackLocation(NotifyIrp); + if (IrpStack->FileObject->Flags & FO_CLEANUP_COMPLETE) + { + ExReleaseFastMutex((PFAST_MUTEX)NotifySync); + + NotifyIrp->IoStatus.Information = 0; + NotifyIrp->IoStatus.Status = STATUS_NOTIFY_CLEANUP; + IoCompleteRequest(NotifyIrp, IO_NO_INCREMENT); + return; + } + + NotifyEntry = FsRtlpFindNotifyEntry(NotifyList, FsContext); + + if (!NotifyEntry) + { + /* No NotifyStruct for this FileObject existed */ + + NotifyEntry = ExAllocateFromPagedLookasideList(&NotifyEntryLookaside); + + NotifyEntry->FsContext = FsContext; + NotifyEntry->FullDirectoryName = FullDirectoryName; + NotifyEntry->WatchTree = WatchTree; + NotifyEntry->IgnoreBuffer = IgnoreBuffer; + NotifyEntry->CompletionFilter = CompletionFilter; + NotifyEntry->TraverseCallback = TraverseCallback; + NotifyEntry->SubjectContext = SubjectContext; + NotifyEntry->Fcb = IrpStack->FileObject->FsContext; + InitializeListHead(&NotifyEntry->IrpQueue); + InitializeListHead(&NotifyEntry->BufferedChangesList); + + InsertTailList(NotifyList, &NotifyEntry->ListEntry); + } + + /* + * FIXME: this NotifyStruct allready have values for WatchTree, CompletionFilter etc. + * What if the WatchTree, CompletionFilter etc. params are different from + * those in the NotifyStruct? Should the params be ignored or should the params overwrite + * the "old" values in the NotifyStruct?? + * STATUS: Currently we ignore these params for subsequesnt request. + * + * -Gunnar + */ + + if (IsListEmpty(&NotifyEntry->BufferedChangesList)) + { + /* No changes are pending. Queue the irp */ + + /* Irp cancelation boilerplate */ + IoSetCancelRoutine(NotifyIrp, FsRtlpNotifyCancelRoutine); + if (NotifyIrp->Cancel && IoSetCancelRoutine(NotifyIrp, NULL)) + { + //irp was canceled + ExReleaseFastMutex((PFAST_MUTEX)NotifySync); + + NotifyIrp->IoStatus.Status = STATUS_CANCELLED; + NotifyIrp->IoStatus.Information = 0; + + IoCompleteRequest(NotifyIrp, IO_NO_INCREMENT); + return; + } + + IoMarkIrpPending(NotifyIrp); + + //FIXME: any point in setting irp status/information before queueing? + + /* save NotifySych for use in the cancel routine */ + NotifyIrp->Tail.Overlay.DriverContext[3] = NotifySync; + InsertTailList(&NotifyEntry->IrpQueue, &NotifyIrp->Tail.Overlay.ListEntry); + + ExReleaseFastMutex((PFAST_MUTEX)NotifySync); + return; + } + + + + + /* + typedef struct _FILE_NOTIFY_INFORMATION { + ULONG NextEntryOffset; + ULONG Action; + ULONG NameLength; + WCHAR Name[1]; + } FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION; + */ + + /* Buffered changes exist */ + + + /* Copy as much buffered data as available/the buffer can hold */ + while (!IsListEmpty(&NotifyEntry->BufferedChangesList)) + { + TmpEntry = RemoveHeadList(&NotifyEntry->BufferedChangesList); + BufferedChange = CONTAINING_RECORD(TmpEntry, BUFFERED_CHANGE, ListEntry); + + /* FIXME: + Fill user-buffer with recorded events until full. If user buffer is too small to hold even + a single record or can only hold some of the events, what should we do???????????? + */ + + /* FIXME: implement this (copy data to user) */ + +// BufferedChange->Action = Action; +// RecordedChange->Name +// RecordedChange->NameLength + + ExFreePool(BufferedChange); + + + } + + ExReleaseFastMutex((PFAST_MUTEX)NotifySync); + + NotifyIrp->IoStatus.Information = 0; //<- FIXME + NotifyIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(NotifyIrp, IO_NO_INCREMENT); +} + + + +static +PIRP +FASTCALL +FsRtlpGetNextIrp(PNOTIFY_ENTRY NotifyEntry) +{ + PIRP Irp; + PLIST_ENTRY TmpEntry; + + /* Loop to get a non-canceled irp */ + while (!IsListEmpty(&NotifyEntry->IrpQueue)) + { + /* If we have queued irp(s) we can't possibly have buffered changes too */ + ASSERT(IsListEmpty(&NotifyEntry->BufferedChangesList)); + + TmpEntry = RemoveHeadList(&NotifyEntry->IrpQueue); + Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry); + + /* irp cancelation bolilerplate */ + if (!IoSetCancelRoutine(Irp, NULL)) + { + //The cancel routine will be called. When we release the lock it will complete the irp. + InitializeListHead(&Irp->Tail.Overlay.ListEntry); + continue; + } + + /* Finally we got a non-canceled irp */ + return Irp; + } + + return NULL; } + + + + /********************************************************************** @@ -172,7 +574,7 @@ FsRtlNotifyFullReportChange ( IN PNOTIFY_SYNC NotifySync, IN PLIST_ENTRY NotifyList, IN PSTRING FullTargetName, - IN USHORT TargetNameOffset, + IN USHORT TargetNameOffset, /* in bytes */ IN PSTRING StreamName OPTIONAL, IN PSTRING NormalizedParentName OPTIONAL, IN ULONG FilterMatch, @@ -180,6 +582,80 @@ FsRtlNotifyFullReportChange ( IN PVOID TargetContext ) { + UNICODE_STRING FullDirName; + UNICODE_STRING TargetName; + PLIST_ENTRY EnumEntry; + PNOTIFY_ENTRY NotifyEntry; + PBUFFERED_CHANGE BufferedChange; + PIRP Irp; + LIST_ENTRY CompletedListHead; + + InitializeListHead(&CompletedListHead); + + FullDirName.Buffer = (WCHAR*)FullTargetName->Buffer; + FullDirName.MaximumLength = FullDirName.Length = TargetNameOffset - sizeof(WCHAR); + + TargetName.Buffer = (WCHAR*)(FullTargetName->Buffer + TargetNameOffset); + TargetName.MaximumLength = TargetName.Length = FullTargetName->Length - TargetNameOffset; + + + ExAcquireFastMutex((PFAST_MUTEX)NotifySync); + + LIST_FOR_EACH_SAFE(EnumEntry, NotifyList, NotifyEntry, NOTIFY_ENTRY, ListEntry ) + { + /* rule out some easy cases */ + /* FIXME: short vs. long names??? */ + if (FilterMatch != NotifyEntry->CompletionFilter) continue; + + if (FullDirName.Length < NotifyEntry->FullDirectoryName->Length) continue; + + if (!NotifyEntry->WatchTree && FullDirName.Length != NotifyEntry->FullDirectoryName->Length) continue; + + if (wcsncmp((WCHAR*)NotifyEntry->FullDirectoryName->Buffer, + FullDirName.Buffer, + NotifyEntry->FullDirectoryName->Length/sizeof(WCHAR)) != 0) continue; + + /* Found a valid change */ + + if ((Irp = FsRtlpGetNextIrp(NotifyEntry))) + { + //FIXME: copy data to user + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + /* avoid holding lock while completing irp */ + InsertTailList(&CompletedListHead, &Irp->Tail.Overlay.ListEntry); + } + else + { + /* No irp in queue. Buffer changes */ + /* FIXME: how much stuff should we buffer? + -Should we alloc with quotas? + -Should we use a hardcoded limit? + -Should we use a time-out? (drop changes if they are not retrieved in x seconds? + */ + BufferedChange = ExAllocatePool(PagedPool, FIELD_OFFSET(BUFFERED_CHANGE, RelativeName) + TargetName.Length); + + BufferedChange->Action = Action; + BufferedChange->NameLen = TargetName.Length; + memcpy(BufferedChange->RelativeName, TargetName.Buffer, TargetName.Length); + + InsertTailList(&NotifyEntry->BufferedChangesList, &BufferedChange->ListEntry); + } + } + + ExReleaseFastMutex((PFAST_MUTEX)NotifySync); + + /* complete defered irps */ + while (!IsListEmpty(&CompletedListHead)) + { + EnumEntry = RemoveHeadList(&CompletedListHead); + Irp = CONTAINING_RECORD(EnumEntry, IRP, Tail.Overlay.ListEntry); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + } + } @@ -198,25 +674,11 @@ FsRtlNotifyFullReportChange ( VOID STDCALL FsRtlNotifyInitializeSync ( - IN PNOTIFY_SYNC NotifySync + IN PNOTIFY_SYNC *NotifySync ) { -#if 0 - *NotifySync = NULL; - *NotifySync = ExAllocatePoolWithTag ( - 0x10, // PoolType??? - sizeof (NOTIFY_SYNC), // NumberOfBytes = 0x28 - FSRTL_TAG - ); - - *NotifySync->Unknown0 = 1; - *NotifySync->Unknown2 = 0; - *NotifySync->Unknown3 = 1; - *NotifySync->Unknown4 = 4; - *NotifySync->Unknown5 = 0; - *NotifySync->Unknown9 = 0; - *NotifySync->Unknown10 = 0; -#endif + *NotifySync = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), 0/*FSRTL_NOTIFY_TAG*/ ); + ExInitializeFastMutex((PFAST_MUTEX)*NotifySync); } @@ -246,11 +708,11 @@ FsRtlNotifyReportChange ( NotifySync, NotifyList, FullTargetName, - (FullTargetName->Length - *FileNamePartLength), /*?*/ + (FullTargetName->Length - *FileNamePartLength), NULL, NULL, FilterMatch, - 0, /* Action ? */ + 0, NULL ); } @@ -276,16 +738,10 @@ FsRtlNotifyReportChange ( VOID STDCALL FsRtlNotifyUninitializeSync ( - IN OUT PNOTIFY_SYNC NotifySync + IN PNOTIFY_SYNC NotifySync ) { -#if 0 - if (NULL != *NotifySync) - { - ExFreePool (*NotifySync); - *NotifySync = NULL; - } -#endif + ExFreePool (NotifySync); } /**********************************************************************