From 926067bd876be647fa7364ed4eb01bf8b94e2125 Mon Sep 17 00:00:00 2001 From: Gunnar Dalsnes Date: Tue, 1 Mar 2005 22:13:40 +0000 Subject: [PATCH] init. work on a notify-directory impl. svn path=/trunk/; revision=13790 --- reactos/include/ntos/zwtypes.h | 9 +- reactos/ntoskrnl/fs/notify.c | 389 +++++++++++++++++------- reactos/ntoskrnl/include/internal/ifs.h | 5 + 3 files changed, 292 insertions(+), 111 deletions(-) diff --git a/reactos/include/ntos/zwtypes.h b/reactos/include/ntos/zwtypes.h index 37ba0b14f6a..1483c5778b6 100755 --- a/reactos/include/ntos/zwtypes.h +++ b/reactos/include/ntos/zwtypes.h @@ -1102,10 +1102,11 @@ typedef struct _FILE_BOTH_DIRECTORY_INFORMATION { */ typedef struct _FILE_NOTIFY_INFORMATION { - ULONG Action; - ULONG FileNameLength; - WCHAR FileName[0]; -} FILE_NOTIFY_INFORMATION; + ULONG NextEntryOffset; + ULONG Action; + ULONG NameLength; + WCHAR Name[1]; +} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION; #define FSCTL_GET_VOLUME_BITMAP 0x9006F #define FSCTL_GET_RETRIEVAL_POINTERS 0x90073 diff --git a/reactos/ntoskrnl/fs/notify.c b/reactos/ntoskrnl/fs/notify.c index 548b4c0da95..837ed5a6c24 100644 --- a/reactos/ntoskrnl/fs/notify.c +++ b/reactos/ntoskrnl/fs/notify.c @@ -9,36 +9,36 @@ */ #include -#define NDEBUG + +//#define NDEBUG #include PAGED_LOOKASIDE_LIST NotifyEntryLookaside; +#define FSRTL_NOTIFY_TAG TAG('N','O','T','I') + typedef struct _NOTIFY_ENTRY { LIST_ENTRY ListEntry; PSTRING FullDirectoryName; BOOLEAN WatchTree; BOOLEAN IgnoreBuffer; + BOOLEAN PendingChanges; ULONG CompletionFilter; LIST_ENTRY IrpQueue; PVOID Fcb; PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback; PSECURITY_SUBJECT_CONTEXT SubjectContext; PVOID FsContext; - LIST_ENTRY BufferedChangesList; + BOOLEAN Unicode; + BOOLEAN BufferExhausted; + PVOID Buffer; /* Buffer == NULL equals IgnoreBuffer == TRUE */ + ULONG BufferSize; + ULONG NextEntryOffset; + PFILE_NOTIFY_INFORMATION PrevEntry; } NOTIFY_ENTRY, *PNOTIFY_ENTRY; -typedef struct _BUFFERED_CHANGE -{ - LIST_ENTRY ListEntry; - ULONG Action; - USHORT NameLen; - WCHAR RelativeName[1]; - -} BUFFERED_CHANGE, *PBUFFERED_CHANGE; - /********************************************************************** * NAME PRIVATE @@ -54,7 +54,7 @@ FsRtlpInitNotifyImplementation(VOID) NULL, 0, sizeof(NOTIFY_ENTRY), - 0 /* FSRTL_NOTIFY_TAG*/, + FSRTL_NOTIFY_TAG, 0 ); @@ -63,6 +63,21 @@ FsRtlpInitNotifyImplementation(VOID) +static +inline +BOOLEAN +FsRtlpIsUnicodePath( + PSTRING Path + ) +{ + ASSERT(Path->Length); + + if (Path->Length == 1) return FALSE; + + if (*(WCHAR*)Path->Buffer == '\\') return TRUE; + + return FALSE; +} /********************************************************************** @@ -188,7 +203,6 @@ FsRtlNotifyCleanup ( PNOTIFY_ENTRY NotifyEntry; LIST_ENTRY CompletedListHead; PLIST_ENTRY TmpEntry; - PBUFFERED_CHANGE BufferedChange; PIRP Irp; InitializeListHead(&CompletedListHead); @@ -200,11 +214,9 @@ FsRtlNotifyCleanup ( if (NotifyEntry) { /* free buffered changes */ - while (!IsListEmpty(&NotifyEntry->BufferedChangesList)) + if (NotifyEntry->Buffer) { - TmpEntry = RemoveHeadList(&NotifyEntry->BufferedChangesList); - BufferedChange = CONTAINING_RECORD(TmpEntry , BUFFERED_CHANGE, ListEntry); - ExFreePool(BufferedChange); + ExFreePool(NotifyEntry->Buffer); } /* cancel(?) pending irps */ @@ -303,7 +315,6 @@ FsRtlpWatchedDirectoryWasDeleted( LIST_ENTRY CompletedListHead; PLIST_ENTRY EnumEntry, TmpEntry; PNOTIFY_ENTRY NotifyEntry; - PBUFFERED_CHANGE BufferedChange; PIRP Irp; InitializeListHead(&CompletedListHead); @@ -317,14 +328,12 @@ FsRtlpWatchedDirectoryWasDeleted( RemoveEntryList(&NotifyEntry->ListEntry); /* free buffered changes */ - while (!IsListEmpty(&NotifyEntry->BufferedChangesList)) + if (NotifyEntry->Buffer) { - TmpEntry = RemoveHeadList(&NotifyEntry->BufferedChangesList); - BufferedChange = CONTAINING_RECORD(TmpEntry , BUFFERED_CHANGE, ListEntry); - ExFreePool(BufferedChange); + ExFreePool(NotifyEntry->Buffer); } - /* cancel(?) pending irps */ + /* cleanup pending irps */ while (!IsListEmpty(&NotifyEntry->IrpQueue)) { TmpEntry = RemoveHeadList(&NotifyEntry->IrpQueue); @@ -338,7 +347,7 @@ FsRtlpWatchedDirectoryWasDeleted( continue; } - Irp->IoStatus.Status = STATUS_NOTIFY_CLEANUP; /* FIXME: correct status? */ + Irp->IoStatus.Status = STATUS_DELETE_PENDING; Irp->IoStatus.Information = 0; /* avoid holding lock while completing irp */ @@ -359,6 +368,11 @@ FsRtlpWatchedDirectoryWasDeleted( } + + + + + /********************************************************************** * NAME EXPORTED * FsRtlNotifyFullChangeDirectory@40 @@ -388,8 +402,7 @@ FsRtlNotifyFullChangeDirectory ( { PIO_STACK_LOCATION IrpStack; PNOTIFY_ENTRY NotifyEntry; - PBUFFERED_CHANGE BufferedChange; - PLIST_ENTRY TmpEntry; + ULONG IrpBuffLen; if (!NotifyIrp) { @@ -398,6 +411,8 @@ FsRtlNotifyFullChangeDirectory ( return; } + DPRINT("FullDirectoryName: %wZ\n", FullDirectoryName); + ExAcquireFastMutex((PFAST_MUTEX)NotifySync); IrpStack = IoGetCurrentIrpStackLocation(NotifyIrp); @@ -411,39 +426,61 @@ FsRtlNotifyFullChangeDirectory ( return; } + IrpBuffLen = IrpStack->Parameters.NotifyDirectory.Length; + NotifyEntry = FsRtlpFindNotifyEntry(NotifyList, FsContext); if (!NotifyEntry) { /* No NotifyStruct for this FileObject existed */ + /* The first request for this FileObject set the standards. + * For subsequent requests, these params will be ignored. + * Ref: Windows NT File System Internals page 516 + */ + NotifyEntry = ExAllocateFromPagedLookasideList(&NotifyEntryLookaside); + RtlZeroMemory(NotifyEntry, sizeof(NOTIFY_ENTRY)); + 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; + NotifyEntry->Unicode = FsRtlpIsUnicodePath(FullDirectoryName); + + /* Init. buffer */ + if (IrpBuffLen && !IgnoreBuffer) + { + _SEH_TRY + { + NotifyEntry->Buffer = ExAllocatePoolWithQuotaTag( + PagedPool, + IrpBuffLen, + FSRTL_NOTIFY_TAG + ); + + NotifyEntry->PrevEntry = NotifyEntry->Buffer; + NotifyEntry->BufferSize = IrpBuffLen; + } + _SEH_HANDLE + { + /* ExAllocatePoolWithQuotaTag raised exception */ + } + _SEH_END; + } + 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)) + + + if (!NotifyEntry->PendingChanges) { /* No changes are pending. Queue the irp */ @@ -472,48 +509,46 @@ FsRtlNotifyFullChangeDirectory ( 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 */ + /* Pending changes exist */ - - /* Copy as much buffered data as available/the buffer can hold */ - while (!IsListEmpty(&NotifyEntry->BufferedChangesList)) + if (NotifyEntry->Buffer == NULL || + NotifyEntry->BufferExhausted || + IrpBuffLen < NotifyEntry->NextEntryOffset) { - 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); - + /* + Can't return detailed changes to user cause: + -No buffer exist, OR + -Buffer were overflowed, OR + -Current irp buff was not large enough + */ + + NotifyIrp->IoStatus.Information = 0; + NotifyIrp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR; } + else + { + /* terminate last entry */ + NotifyEntry->PrevEntry->NextEntryOffset = 0; + + //FIXME: copy data correctly to user + memcpy(NotifyIrp->UserBuffer, NotifyEntry->Buffer, NotifyEntry->NextEntryOffset); + + NotifyIrp->IoStatus.Information = NotifyEntry->NextEntryOffset; + NotifyIrp->IoStatus.Status = STATUS_SUCCESS; + } + + /* reset buffer */ + NotifyEntry->PrevEntry = NotifyEntry->Buffer; + NotifyEntry->NextEntryOffset = 0; + NotifyEntry->BufferExhausted = FALSE; + + NotifyEntry->PendingChanges = FALSE; ExReleaseFastMutex((PFAST_MUTEX)NotifySync); - NotifyIrp->IoStatus.Information = 0; //<- FIXME - NotifyIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(NotifyIrp, IO_NO_INCREMENT); } @@ -530,8 +565,8 @@ FsRtlpGetNextIrp(PNOTIFY_ENTRY NotifyEntry) /* 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)); + /* If we have queued irp(s) we can't possibly have pending changes too */ + ASSERT(!NotifyEntry->PendingChanges); TmpEntry = RemoveHeadList(&NotifyEntry->IrpQueue); Irp = CONTAINING_RECORD(TmpEntry , IRP, Tail.Overlay.ListEntry); @@ -552,8 +587,35 @@ FsRtlpGetNextIrp(PNOTIFY_ENTRY NotifyEntry) } - - +static +inline +VOID +FsRtlpCopyName( + PFILE_NOTIFY_INFORMATION CurrentEntry, + BOOLEAN Unicode, + PSTRING RelativeName, + PSTRING StreamName + ) +{ + /* Buffer size is allready probed, so just copy the data */ + + if (Unicode) + { + memcpy(CurrentEntry->Name, RelativeName->Buffer, RelativeName->Length); + if (StreamName) + { + CurrentEntry->Name[RelativeName->Length/sizeof(WCHAR)] = ':'; + memcpy(&CurrentEntry ->Name[(RelativeName->Length/sizeof(WCHAR))+1], + StreamName->Buffer, + StreamName->Length); + } + } + else + { + //FIXME: convert to unicode etc. + DPRINT1("FIXME: ansi strings in notify impl. not supported yet\n"); + } +} /********************************************************************** @@ -573,75 +635,188 @@ STDCALL FsRtlNotifyFullReportChange ( IN PNOTIFY_SYNC NotifySync, IN PLIST_ENTRY NotifyList, - IN PSTRING FullTargetName, + IN PSTRING FullTargetName, /* can include short names! */ IN USHORT TargetNameOffset, /* in bytes */ IN PSTRING StreamName OPTIONAL, - IN PSTRING NormalizedParentName OPTIONAL, + IN PSTRING NormalizedParentName OPTIONAL, /* same as FullTargetName, but with long names */ IN ULONG FilterMatch, IN ULONG Action, IN PVOID TargetContext ) { - UNICODE_STRING FullDirName; - UNICODE_STRING TargetName; + USHORT FullDirLen; + STRING RelativeName; PLIST_ENTRY EnumEntry; PNOTIFY_ENTRY NotifyEntry; - PBUFFERED_CHANGE BufferedChange; PIRP Irp; LIST_ENTRY CompletedListHead; + USHORT NameLenU; + ULONG RecordLen; + PFILE_NOTIFY_INFORMATION CurrentEntry; InitializeListHead(&CompletedListHead); + + DPRINT("FullTargetName: %wZ\n", FullTargetName); + + /* + I think FullTargetName can include/be a short file name! What the heck do i do with this? + Dont think this apply to FsRtlNotifyFullChangeDirectory's FullDirectoryName. + */ - 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 ) { + ASSERT(NotifyEntry->Unicode == FsRtlpIsUnicodePath(FullTargetName)); + /* rule out some easy cases */ /* FIXME: short vs. long names??? */ - if (FilterMatch != NotifyEntry->CompletionFilter) continue; + if (!(FilterMatch & NotifyEntry->CompletionFilter)) continue; - if (FullDirName.Length < NotifyEntry->FullDirectoryName->Length) continue; + FullDirLen = TargetNameOffset - (NotifyEntry->Unicode?sizeof(WCHAR):sizeof(char)); - if (!NotifyEntry->WatchTree && FullDirName.Length != NotifyEntry->FullDirectoryName->Length) continue; + + if (FullDirLen < NotifyEntry->FullDirectoryName->Length) continue; + + if (!NotifyEntry->WatchTree && FullDirLen != NotifyEntry->FullDirectoryName->Length) continue; - if (wcsncmp((WCHAR*)NotifyEntry->FullDirectoryName->Buffer, - FullDirName.Buffer, - NotifyEntry->FullDirectoryName->Length/sizeof(WCHAR)) != 0) continue; + DPRINT("NotifyEntry->FullDirectoryName: %wZ\n", NotifyEntry->FullDirectoryName); + + if (memcmp(NotifyEntry->FullDirectoryName->Buffer, + FullTargetName->Buffer, + NotifyEntry->FullDirectoryName->Length) != 0) continue; + + + if (NotifyEntry->WatchTree && + NotifyEntry->TraverseCallback && + FullDirLen != NotifyEntry->FullDirectoryName->Length) + { + /* change happend in a subdir. ask caller if we are allowed in here */ + NTSTATUS Status = NotifyEntry->TraverseCallback(NotifyEntry->FsContext, + TargetContext, + NotifyEntry->SubjectContext); + + if (!NT_SUCCESS(Status)) continue; + + /* + FIXME: notify-dir impl. should release and free the SubjectContext + */ + } + + DPRINT("Found match\n"); /* Found a valid change */ + + RelativeName.Buffer = FullTargetName->Buffer + TargetNameOffset; + RelativeName.MaximumLength = + RelativeName.Length = + FullTargetName->Length - TargetNameOffset; + + DPRINT("RelativeName: %wZ\n",&RelativeName); + + /* calculate unicode bytes of relative-name + stream-name */ + if (NotifyEntry->Unicode) + { + NameLenU = RelativeName.Length + (StreamName ? (StreamName->Length + sizeof(WCHAR)) : 0); + } + else + { + NameLenU = RelativeName.Length * sizeof(WCHAR) + + (StreamName ? ((StreamName->Length * sizeof(WCHAR)) + sizeof(WCHAR)) : 0); + } + + RecordLen = FIELD_OFFSET(FILE_NOTIFY_INFORMATION, Name) + NameLenU; if ((Irp = FsRtlpGetNextIrp(NotifyEntry))) { - //FIXME: copy data to user + PIO_STACK_LOCATION IrpStack; + ULONG IrpBuffLen; - Irp->IoStatus.Status = STATUS_SUCCESS; - Irp->IoStatus.Information = 0; + IrpStack = IoGetCurrentIrpStackLocation(Irp); + IrpBuffLen = IrpStack->Parameters.NotifyDirectory.Length; + + DPRINT("Got pending irp\n"); + + ASSERT(!NotifyEntry->PendingChanges); + + if (NotifyEntry->Buffer == NULL || /* aka. IgnoreBuffer */ + RecordLen > IrpBuffLen) + { + /* ignore buffer / buffer not large enough */ + Irp->IoStatus.Status = STATUS_NOTIFY_ENUM_DIR; + Irp->IoStatus.Information = 0; + } + else + { + //FIXME: copy data to user correctly + CurrentEntry = (PFILE_NOTIFY_INFORMATION)Irp->UserBuffer; + + CurrentEntry->Action = Action; + CurrentEntry->NameLength = NameLenU; + CurrentEntry->NextEntryOffset = 0; + + FsRtlpCopyName( + CurrentEntry, + NotifyEntry->Unicode, + &RelativeName, + StreamName + ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = RecordLen; + } /* 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); + DPRINT("No irp\n"); - BufferedChange->Action = Action; - BufferedChange->NameLen = TargetName.Length; - memcpy(BufferedChange->RelativeName, TargetName.Buffer, TargetName.Length); + NotifyEntry->PendingChanges = TRUE; + + if (NotifyEntry->Buffer == NULL || NotifyEntry->BufferExhausted) continue; - InsertTailList(&NotifyEntry->BufferedChangesList, &BufferedChange->ListEntry); + if (RecordLen > NotifyEntry->BufferSize - NotifyEntry->NextEntryOffset) + { + /* overflow. drop these changes and stop buffering any other changes too */ + NotifyEntry->BufferExhausted = TRUE; + continue; + } + + /* The buffer has enough room for the changes. + * Copy data to buffer. + */ + + CurrentEntry = (PFILE_NOTIFY_INFORMATION)NotifyEntry->Buffer; + + CurrentEntry->Action = Action; + CurrentEntry->NameLength = NameLenU; + CurrentEntry->NextEntryOffset = 0; + + FsRtlpCopyName(CurrentEntry, + NotifyEntry->Unicode, + &RelativeName, + StreamName + ); + + /* adjust buffer */ + NotifyEntry->PrevEntry->NextEntryOffset = (char*)CurrentEntry - (char*)NotifyEntry->PrevEntry; + NotifyEntry->PrevEntry = CurrentEntry; + NotifyEntry->NextEntryOffset += RecordLen; + + +// { +// UNICODE_STRING TmpStr; +// TmpStr.Buffer = BufferedChange->RelativeName; +// TmpStr.MaximumLength = TmpStr.Length = BufferedChange->NameLen; +// DPRINT("BufferedChange->RelativeName: %wZ\n", &TmpStr); +// } + + } } @@ -677,7 +852,7 @@ FsRtlNotifyInitializeSync ( IN PNOTIFY_SYNC *NotifySync ) { - *NotifySync = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), 0/*FSRTL_NOTIFY_TAG*/ ); + *NotifySync = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), FSRTL_NOTIFY_TAG ); ExInitializeFastMutex((PFAST_MUTEX)*NotifySync); } diff --git a/reactos/ntoskrnl/include/internal/ifs.h b/reactos/ntoskrnl/include/internal/ifs.h index 812d37f86ab..18543b41226 100644 --- a/reactos/ntoskrnl/include/internal/ifs.h +++ b/reactos/ntoskrnl/include/internal/ifs.h @@ -8,6 +8,11 @@ /* Look for "FSrt" in mem view */ #define IFS_POOL_TAG 0x74725346 +VOID +STDCALL INIT_FUNCTION +FsRtlpInitNotifyImplementation(VOID); + + VOID STDCALL FsRtlpInitFileLockingImplementation(VOID);