init. work on a notify-directory impl.

svn path=/trunk/; revision=13790
This commit is contained in:
Gunnar Dalsnes 2005-03-01 22:13:40 +00:00
parent 2093306969
commit 926067bd87
3 changed files with 292 additions and 111 deletions

View file

@ -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

View file

@ -9,36 +9,36 @@
*/
#include <ntoskrnl.h>
#define NDEBUG
//#define NDEBUG
#include <internal/debug.h>
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);
}

View file

@ -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);