3215 lines
95 KiB
C
3215 lines
95 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Notify.c
|
||
|
||
Abstract:
|
||
|
||
The Notify package provides support to filesystems which implement
|
||
NotifyChangeDirectory. This package will manage a queue of notify
|
||
blocks which are attached to some filesystem structure (i.e. Vcb
|
||
in Fat, HPFS). The filesystems will allocate a fast mutex to be used
|
||
by this package to synchronize access to the notify queue.
|
||
|
||
The following routines are provided by this package:
|
||
|
||
o FsRtlNotifyInitializeSync - Create and initializes the
|
||
synchronization object.
|
||
|
||
o FsrtlNotifyUninitializeSync - Deallocates the synchronization
|
||
object.
|
||
|
||
o FsRtlNotifyChangeDirectory - This routine is called whenever the
|
||
filesystems receive a NotifyChangeDirectoryFile call. This
|
||
routine allocates any neccessary structures and places the
|
||
Irp in the NotifyQueue (or possibly completes or cancels it
|
||
immediately).
|
||
|
||
o FsRtlNotifyFullChangeDirectory - This routine is called whenever the
|
||
filesystems receive a NotifyChangeDirectoryFile call. This differs
|
||
from the FsRtlNotifyChangeDirectory in that it expects to return
|
||
the notify information in the user's buffer.
|
||
|
||
o FsRtlNotifyFilterChangeDirectory - This routine is called whenever the
|
||
filesystems receive a NotifyChangeDirectoryFile call. This differs
|
||
from the FsRtlNotifyFullChangeDirectory in that it accepts a
|
||
FilterRoutine Callback.
|
||
|
||
o FsRtlNotifyReportChange - This routine is called by the
|
||
filesystems whenever they perform some operation that could
|
||
cause the completion of a notify operation. This routine will
|
||
walk through the notify queue to see if any Irps are affected
|
||
by the indicated operation.
|
||
|
||
o FsRtlNotifyFullReportChange - This routine is called by the
|
||
filesystems whenever they perform some operation that could
|
||
cause the completion of a notify operation. This routine differs
|
||
from the FsRtlNotifyReportChange call in that it returns more
|
||
detailed information in the caller's buffer if present.
|
||
|
||
o FsRtlNotifyFilterReportChange - This routine is called by the
|
||
filesystems whenever they perform some operation that could
|
||
cause the completion of a notify operation. This routine differs
|
||
from the FsRtlNotifyFullReportChange call in that it accepts a
|
||
FilterContext parameter for notifyees who specified a FilterRoutine.
|
||
|
||
o FsRtlNotifyCleanup - This routine is called to remove any
|
||
references to a particular FsContext structure from the notify
|
||
queue. If the matching FsContext structure is found in the
|
||
queue, then all associated Irps are completed.
|
||
|
||
Author:
|
||
|
||
Brian Andrew [BrianAn] 9-19-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "FsRtlP.h"
|
||
|
||
//
|
||
// Trace level for the module
|
||
//
|
||
|
||
#define Dbg (0x04000000)
|
||
|
||
//
|
||
// This is the synchronization object for the notify package. The caller
|
||
// given a pointer to this structure.
|
||
//
|
||
|
||
typedef struct _REAL_NOTIFY_SYNC {
|
||
|
||
FAST_MUTEX FastMutex;
|
||
ERESOURCE_THREAD OwningThread;
|
||
ULONG OwnerCount;
|
||
|
||
} REAL_NOTIFY_SYNC, *PREAL_NOTIFY_SYNC;
|
||
|
||
//
|
||
// A list of the following structures is used to store the NotifyChange
|
||
// requests. They are linked to a filesystem-defined list head.
|
||
//
|
||
|
||
typedef struct _NOTIFY_CHANGE {
|
||
|
||
//
|
||
// Fast Mutex. This fast mutex is used to access the list containing this
|
||
// structure.
|
||
//
|
||
|
||
PREAL_NOTIFY_SYNC NotifySync;
|
||
|
||
//
|
||
// FsContext. This value is given by the filesystems to uniquely
|
||
// identify this structure. The identification is on a
|
||
// per-user file object basis. The expected value is the Ccb address
|
||
// for this user file object.
|
||
//
|
||
|
||
PVOID FsContext;
|
||
|
||
//
|
||
// StreamID. This value matches the FsContext field in the file object for
|
||
// the directory being watched. This is used to identify the directory stream
|
||
// when the directory is being deleted.
|
||
//
|
||
|
||
PVOID StreamID;
|
||
|
||
//
|
||
// TraverseAccessCallback. This is the filesystem-supplied routine used
|
||
// to call back into the filesystem to check whether the caller has traverse
|
||
// access when watching a sub-directory. Only applies when watching a
|
||
// sub-directory.
|
||
//
|
||
|
||
PCHECK_FOR_TRAVERSE_ACCESS TraverseCallback;
|
||
|
||
//
|
||
// SubjectContext. If the caller specifies a traverse callback routine
|
||
// we will need to pass the Security Context from the thread which
|
||
// originated this call. The notify package will free this structure
|
||
// on tearing down the notify package. We don't expect to need this
|
||
// structure often.
|
||
//
|
||
|
||
PSECURITY_SUBJECT_CONTEXT SubjectContext;
|
||
|
||
//
|
||
// Full Directory Name. The following string is the full directory
|
||
// name of the directory being watched. It is used during watch tree
|
||
// operations to check whether this directory is an ancestor of
|
||
// the modified file. The string could be in ANSI or UNICODE form.
|
||
//
|
||
|
||
PSTRING FullDirectoryName;
|
||
|
||
//
|
||
// Notify List. The following field links the notify structures for
|
||
// a particular volume.
|
||
//
|
||
|
||
LIST_ENTRY NotifyList;
|
||
|
||
//
|
||
// Notify Irps. The following field links the Irps associated with
|
||
//
|
||
//
|
||
|
||
LIST_ENTRY NotifyIrps;
|
||
|
||
//
|
||
// FilterCallback. This is the filesystem-supplied routine used
|
||
// to call back into the filesystem to check whether a Notify block
|
||
// should see the change. (Initially added for TxfNtfs development
|
||
// as part of a strategy to control when, not if, transactions see
|
||
// changes from other transactions.)
|
||
//
|
||
|
||
PFILTER_REPORT_CHANGE FilterCallback;
|
||
|
||
//
|
||
// Flags. State of the notify for this volume.
|
||
//
|
||
|
||
USHORT Flags;
|
||
|
||
//
|
||
// Character size. Larger size indicates unicode characters.
|
||
// unicode names.
|
||
//
|
||
|
||
UCHAR CharacterSize;
|
||
|
||
//
|
||
// Completion Filter. This field is used to mask the modification
|
||
// actions to determine whether to complete the notify irp.
|
||
//
|
||
|
||
ULONG CompletionFilter;
|
||
|
||
//
|
||
// The following values are used to manage a buffer if there is no current
|
||
// Irp to complete. The fields have the following meaning:
|
||
//
|
||
// AllocatedBuffer - Buffer we need to allocate
|
||
// Buffer - Buffer to store data in
|
||
// BufferLength - Length of original user buffer
|
||
// ThisBufferLength - Length of the buffer we are using
|
||
// DataLength - Current length of the data in the buffer
|
||
// LastEntry - Offset of previous entry in the buffer
|
||
//
|
||
|
||
PVOID AllocatedBuffer;
|
||
PVOID Buffer;
|
||
ULONG BufferLength;
|
||
ULONG ThisBufferLength;
|
||
ULONG DataLength;
|
||
ULONG LastEntry;
|
||
|
||
//
|
||
// Reference count which keeps the notify structure around. Such references include
|
||
//
|
||
// - Lifetime reference. Count set to one initially and removed on cleanup
|
||
// - Cancel reference. Reference the notify struct when storing the cancel routine
|
||
// in the Irp. The routine which actually clears the routine will decrement
|
||
// this value.
|
||
//
|
||
|
||
ULONG ReferenceCount;
|
||
|
||
//
|
||
// This is the process on whose behalf the structure was allocated. We
|
||
// charge any quota to this process.
|
||
//
|
||
|
||
PEPROCESS OwningProcess;
|
||
|
||
} NOTIFY_CHANGE, *PNOTIFY_CHANGE;
|
||
|
||
#define NOTIFY_WATCH_TREE (0x0001)
|
||
#define NOTIFY_IMMEDIATE_NOTIFY (0x0002)
|
||
#define NOTIFY_CLEANUP_CALLED (0x0004)
|
||
#define NOTIFY_DEFER_NOTIFY (0x0008)
|
||
#define NOTIFY_DIR_IS_ROOT (0x0010)
|
||
#define NOTIFY_STREAM_IS_DELETED (0x0020)
|
||
|
||
//
|
||
// CAST
|
||
// Add2Ptr (
|
||
// IN PVOID Pointer,
|
||
// IN ULONG Increment
|
||
// IN (CAST)
|
||
// );
|
||
//
|
||
// ULONG
|
||
// PtrOffset (
|
||
// IN PVOID BasePtr,
|
||
// IN PVOID OffsetPtr
|
||
// );
|
||
//
|
||
|
||
#define Add2Ptr(PTR,INC,CAST) ((CAST)((PUCHAR)(PTR) + (INC)))
|
||
|
||
#define PtrOffset(BASE,OFFSET) ((ULONG)((PCHAR)(OFFSET) - (PCHAR)(BASE)))
|
||
|
||
//
|
||
// VOID
|
||
// SetFlag (
|
||
// IN ULONG Flags,
|
||
// IN ULONG SingleFlag
|
||
// );
|
||
//
|
||
// VOID
|
||
// ClearFlag (
|
||
// IN ULONG Flags,
|
||
// IN ULONG SingleFlag
|
||
// );
|
||
//
|
||
|
||
#define SetFlag(F,SF) { \
|
||
(F) |= (SF); \
|
||
}
|
||
|
||
#define ClearFlag(F,SF) { \
|
||
(F) &= ~(SF); \
|
||
}
|
||
|
||
//
|
||
// VOID
|
||
// AcquireNotifySync (
|
||
// IN PREAL_NOTIFY_SYNC NotifySync
|
||
// );
|
||
//
|
||
// VOID
|
||
// ReleaseNotifySync (
|
||
// IN PREAL_NOTIFY_SYNC NotifySync
|
||
// );
|
||
//
|
||
|
||
#define AcquireNotifySync(NS) { \
|
||
ERESOURCE_THREAD _CurrentThread; \
|
||
_CurrentThread = (ERESOURCE_THREAD) PsGetCurrentThread(); \
|
||
if (_CurrentThread != ((PREAL_NOTIFY_SYNC) (NS))->OwningThread) { \
|
||
ExAcquireFastMutexUnsafe( &((PREAL_NOTIFY_SYNC) (NS))->FastMutex ); \
|
||
((PREAL_NOTIFY_SYNC) (NS))->OwningThread = _CurrentThread; \
|
||
} \
|
||
((PREAL_NOTIFY_SYNC) (NS))->OwnerCount += 1; \
|
||
}
|
||
|
||
#define ReleaseNotifySync(NS) { \
|
||
((PREAL_NOTIFY_SYNC) (NS))->OwnerCount -= 1; \
|
||
if (((PREAL_NOTIFY_SYNC) (NS))->OwnerCount == 0) { \
|
||
((PREAL_NOTIFY_SYNC) (NS))->OwningThread = (ERESOURCE_THREAD) 0; \
|
||
ExReleaseFastMutexUnsafe(&((PREAL_NOTIFY_SYNC) (NS))->FastMutex); \
|
||
} \
|
||
}
|
||
|
||
//
|
||
// Define a tag for general pool allocations from this module
|
||
//
|
||
|
||
#undef MODULE_POOL_TAG
|
||
#define MODULE_POOL_TAG ('NrSF')
|
||
|
||
|
||
//
|
||
// Local support routines
|
||
//
|
||
|
||
PNOTIFY_CHANGE
|
||
FsRtlIsNotifyOnList (
|
||
IN PLIST_ENTRY NotifyListHead,
|
||
IN PVOID FsContext
|
||
);
|
||
|
||
VOID
|
||
FsRtlNotifyCompleteIrp (
|
||
IN PIRP NotifyIrp,
|
||
IN PNOTIFY_CHANGE Notify,
|
||
IN ULONG DataLength,
|
||
IN NTSTATUS Status,
|
||
IN ULONG CheckCancel
|
||
);
|
||
|
||
BOOLEAN
|
||
FsRtlNotifySetCancelRoutine (
|
||
IN PIRP NotifyIrp,
|
||
IN PNOTIFY_CHANGE Notify OPTIONAL
|
||
);
|
||
|
||
BOOLEAN
|
||
FsRtlNotifyUpdateBuffer (
|
||
IN PFILE_NOTIFY_INFORMATION NotifyInfo,
|
||
IN ULONG FileAction,
|
||
IN PSTRING ParentName,
|
||
IN PSTRING TargetName,
|
||
IN PSTRING StreamName OPTIONAL,
|
||
IN BOOLEAN UnicodeName,
|
||
IN ULONG SizeOfEntry
|
||
);
|
||
|
||
VOID
|
||
FsRtlNotifyCompleteIrpList (
|
||
IN PNOTIFY_CHANGE Notify,
|
||
IN NTSTATUS Status
|
||
);
|
||
|
||
VOID
|
||
FsRtlCancelNotify (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP ThisIrp
|
||
);
|
||
|
||
VOID
|
||
FsRtlCheckNotifyForDelete (
|
||
IN PLIST_ENTRY NotifyListHead,
|
||
IN PVOID FsContext
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, FsRtlNotifyInitializeSync)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyUninitializeSync)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyFullChangeDirectory)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyFullReportChange)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyFilterChangeDirectory)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyFilterReportChange)
|
||
#pragma alloc_text(PAGE, FsRtlIsNotifyOnList)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyChangeDirectory)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyCleanup)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyCompleteIrp)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyReportChange)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyUpdateBuffer)
|
||
#pragma alloc_text(PAGE, FsRtlCheckNotifyForDelete)
|
||
#pragma alloc_text(PAGE, FsRtlNotifyCompleteIrpList)
|
||
#endif
|
||
|
||
|
||
NTKERNELAPI
|
||
VOID
|
||
FsRtlNotifyInitializeSync (
|
||
IN PNOTIFY_SYNC *NotifySync
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to allocate and initialize the synchronization object
|
||
for this notify list.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the address to store the structure we allocate.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PREAL_NOTIFY_SYNC RealSync;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyInitializeSync: Entered\n", 0 );
|
||
|
||
//
|
||
// Clear the pointer and then attempt to allocate a non-paged
|
||
// structure.
|
||
//
|
||
|
||
*NotifySync = NULL;
|
||
|
||
RealSync = (PREAL_NOTIFY_SYNC) FsRtlpAllocatePool( NonPagedPool,
|
||
sizeof( REAL_NOTIFY_SYNC ));
|
||
|
||
//
|
||
// Initialize the structure.
|
||
//
|
||
|
||
ExInitializeFastMutex( &RealSync->FastMutex );
|
||
RealSync->OwningThread = (ERESOURCE_THREAD) 0;
|
||
RealSync->OwnerCount = 0;
|
||
|
||
*NotifySync = (PNOTIFY_SYNC) RealSync;
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyInitializeSync: Exit\n", 0 );
|
||
return;
|
||
}
|
||
|
||
|
||
NTKERNELAPI
|
||
VOID
|
||
FsRtlNotifyUninitializeSync (
|
||
IN PNOTIFY_SYNC *NotifySync
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to uninitialize the synchronization object
|
||
for this notify list.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the address containing the pointer to our synchronization
|
||
object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyUninitializeSync: Entered\n", 0 );
|
||
|
||
//
|
||
// Free the structure if present and clear the pointer.
|
||
//
|
||
|
||
if (*NotifySync != NULL) {
|
||
|
||
ExFreePool( *NotifySync );
|
||
*NotifySync = NULL;
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyUninitializeSync: Exit\n", 0 );
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FsRtlNotifyChangeDirectory (
|
||
IN PNOTIFY_SYNC NotifySync,
|
||
IN PVOID FsContext,
|
||
IN PSTRING FullDirectoryName,
|
||
IN PLIST_ENTRY NotifyList,
|
||
IN BOOLEAN WatchTree,
|
||
IN ULONG CompletionFilter,
|
||
IN PIRP NotifyIrp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by a file system which has received a NotifyChange
|
||
request. This routine checks if there is already a notify structure and
|
||
inserts one if not present. With a notify structure in hand, we check
|
||
whether we already have a pending notify and report it if so. If there
|
||
is no pending notify, we check if this Irp has already been cancelled and
|
||
completes it if so. Otherwise we add this to the list of Irps waiting
|
||
for notification.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the controlling fast mutex for this notify list.
|
||
It is stored here so that it can be found for an Irp which is being
|
||
cancelled.
|
||
|
||
FsContext - This is supplied by the file system so that this notify
|
||
structure can be uniquely identified.
|
||
|
||
FullDirectoryName - Points to the full name for the directory associated
|
||
with this notify structure.
|
||
|
||
NotifyList - This is the start of the notify list to add this
|
||
structure to.
|
||
|
||
WatchTree - This indicates whether all subdirectories for this directory
|
||
should be watched, or just the directory itself.
|
||
|
||
CompletionFilter - This provides the mask to determine which operations
|
||
will trigger the notify operations.
|
||
|
||
NotifyIrp - This is the Irp to complete on notify change.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyChangeDirectory: Entered\n", 0 );
|
||
|
||
//
|
||
// We will simply call the full notify routine to do the real work.
|
||
//
|
||
|
||
FsRtlNotifyFilterChangeDirectory( NotifySync,
|
||
NotifyList,
|
||
FsContext,
|
||
FullDirectoryName,
|
||
WatchTree,
|
||
TRUE,
|
||
CompletionFilter,
|
||
NotifyIrp,
|
||
NULL,
|
||
NULL,
|
||
NULL );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyChangeDirectory: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
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 PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by a file system which has received a NotifyChange
|
||
request. This routine checks if there is already a notify structure and
|
||
inserts one if not present. With a notify structure in hand, we check
|
||
whether we already have a pending notify and report it if so. If there
|
||
is no pending notify, we check if this Irp has already been cancelled and
|
||
completes it if so. Otherwise we add this to the list of Irps waiting
|
||
for notification.
|
||
|
||
This is the version of this routine which understands about the user's
|
||
buffer and will fill it in on a reported change.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the controlling fast mutex for this notify list.
|
||
It is stored here so that it can be found for an Irp which is being
|
||
cancelled.
|
||
|
||
NotifyList - This is the start of the notify list to add this
|
||
structure to.
|
||
|
||
FsContext - This is supplied by the file system so that this notify
|
||
structure can be uniquely identified. If the NotifyIrp is not specified
|
||
then this is used to identify the stream and it will match the FsContext
|
||
field in the file object of a stream being deleted.
|
||
|
||
FullDirectoryName - Points to the full name for the directory associated
|
||
with this notify structure. Ignored if the NotifyIrp is not specified.
|
||
|
||
WatchTree - This indicates whether all subdirectories for this directory
|
||
should be watched, or just the directory itself. Ignored if the
|
||
NotifyIrp is not specified.
|
||
|
||
IgnoreBuffer - Indicates whether we will always ignore any user buffer
|
||
and force the directory to be reenumerated. This will speed up the
|
||
operation. Ignored if the NotifyIrp is not specified.
|
||
|
||
CompletionFilter - This provides the mask to determine which operations
|
||
will trigger the notify operations. Ignored if the NotifyIrp is not
|
||
specified.
|
||
|
||
NotifyIrp - This is the Irp to complete on notify change. If this irp is
|
||
not specified it means that the stream represented by this file object
|
||
is being deleted.
|
||
|
||
TraverseCallback - If specified we must call this routine when a change
|
||
has occurred in a subdirectory being watched in a tree. This will
|
||
let the filesystem check if the watcher has traverse access to that
|
||
directory. Ignored if the NotifyIrp is not specified.
|
||
|
||
SubjectContext - If there is a traverse callback routine then we will
|
||
pass this subject context as a parameter to the call. We will release
|
||
the context and free the structure when done with it. Ignored if the
|
||
NotifyIrp is not specified, NULL in these cases.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyFullChangeDirectory: Entered\n", 0 );
|
||
|
||
//
|
||
// We will simply call the full notify routine to do the real work.
|
||
//
|
||
|
||
FsRtlNotifyFilterChangeDirectory( NotifySync,
|
||
NotifyList,
|
||
FsContext,
|
||
FullDirectoryName,
|
||
WatchTree,
|
||
IgnoreBuffer,
|
||
CompletionFilter,
|
||
NotifyIrp,
|
||
TraverseCallback,
|
||
SubjectContext,
|
||
NULL );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyFullChangeDirectory: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FsRtlNotifyFilterChangeDirectory (
|
||
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 PSECURITY_SUBJECT_CONTEXT SubjectContext OPTIONAL,
|
||
IN PFILTER_REPORT_CHANGE FilterCallback OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by a file system which has received a NotifyChange
|
||
request. This routine checks if there is already a notify structure and
|
||
inserts one if not present. With a notify structure in hand, we check
|
||
whether we already have a pending notify and report it if so. If there
|
||
is no pending notify, we check if this Irp has already been cancelled and
|
||
completes it if so. Otherwise we add this to the list of Irps waiting
|
||
for notification.
|
||
|
||
This is the version of this routine which understands about the user's
|
||
buffer and will fill it in on a reported change.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the controlling fast mutex for this notify list.
|
||
It is stored here so that it can be found for an Irp which is being
|
||
cancelled.
|
||
|
||
NotifyList - This is the start of the notify list to add this
|
||
structure to.
|
||
|
||
FsContext - This is supplied by the file system so that this notify
|
||
structure can be uniquely identified. If the NotifyIrp is not specified
|
||
then this is used to identify the stream and it will match the FsContext
|
||
field in the file object of a stream being deleted.
|
||
|
||
FullDirectoryName - Points to the full name for the directory associated
|
||
with this notify structure. Ignored if the NotifyIrp is not specified.
|
||
|
||
WatchTree - This indicates whether all subdirectories for this directory
|
||
should be watched, or just the directory itself. Ignored if the
|
||
NotifyIrp is not specified.
|
||
|
||
IgnoreBuffer - Indicates whether we will always ignore any user buffer
|
||
and force the directory to be reenumerated. This will speed up the
|
||
operation. Ignored if the NotifyIrp is not specified.
|
||
|
||
CompletionFilter - This provides the mask to determine which operations
|
||
will trigger the notify operations. Ignored if the NotifyIrp is not
|
||
specified.
|
||
|
||
NotifyIrp - This is the Irp to complete on notify change. If this irp is
|
||
not specified it means that the stream represented by this file object
|
||
is being deleted.
|
||
|
||
TraverseCallback - If specified we must call this routine when a change
|
||
has occurred in a subdirectory being watched in a tree. This will
|
||
let the filesystem check if the watcher has traverse access to that
|
||
directory. Ignored if the NotifyIrp is not specified.
|
||
|
||
SubjectContext - If there is a traverse callback routine then we will
|
||
pass this subject context as a parameter to the call. We will release
|
||
the context and free the structure when done with it. Ignored if the
|
||
NotifyIrp is not specified.
|
||
|
||
FilterCallback - This is the filesystem-supplied routine used
|
||
to call back into the filesystem to check whether this Notify block
|
||
should see the change. (Initially added for TxfNtfs development
|
||
as part of a strategy to control when, not if, transactions see
|
||
changes from other transactions.)
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNOTIFY_CHANGE Notify = NULL;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyFullChangeDirectory: Entered\n", 0 );
|
||
|
||
//
|
||
// Acquire exclusive access to the list by acquiring the mutex.
|
||
//
|
||
|
||
AcquireNotifySync( NotifySync );
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// If there is no Irp then find all of the pending Irps whose file objects
|
||
// refer to the same stream and complete them with STATUS_DELETE_PENDING.
|
||
//
|
||
|
||
if (NotifyIrp == NULL) {
|
||
|
||
FsRtlCheckNotifyForDelete( NotifyList, FsContext );
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// Get the current Stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( NotifyIrp );
|
||
|
||
//
|
||
// Clear the Iosb in the Irp.
|
||
//
|
||
|
||
NotifyIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
NotifyIrp->IoStatus.Information = 0;
|
||
|
||
//
|
||
// If the file object has already gone through cleanup, then complete
|
||
// the request immediately.
|
||
//
|
||
|
||
if (FlagOn( IrpSp->FileObject->Flags, FO_CLEANUP_COMPLETE )) {
|
||
|
||
//
|
||
// Always mark this Irp as pending returned.
|
||
//
|
||
|
||
IoMarkIrpPending( NotifyIrp );
|
||
|
||
FsRtlCompleteRequest( NotifyIrp, STATUS_NOTIFY_CLEANUP );
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// If the notify structure is not already in the list, add it
|
||
// now.
|
||
//
|
||
|
||
Notify = FsRtlIsNotifyOnList( NotifyList, FsContext );
|
||
|
||
if (Notify == NULL) {
|
||
|
||
//
|
||
// Allocate and initialize the structure.
|
||
//
|
||
|
||
Notify = FsRtlpAllocatePool( PagedPool, sizeof( NOTIFY_CHANGE ));
|
||
RtlZeroMemory( Notify, sizeof( NOTIFY_CHANGE ));
|
||
|
||
Notify->NotifySync = (PREAL_NOTIFY_SYNC) NotifySync;
|
||
Notify->FsContext = FsContext;
|
||
Notify->StreamID = IrpSp->FileObject->FsContext;
|
||
|
||
Notify->TraverseCallback = TraverseCallback;
|
||
Notify->SubjectContext = SubjectContext;
|
||
SubjectContext = NULL;
|
||
Notify->FilterCallback = FilterCallback;
|
||
|
||
Notify->FullDirectoryName = FullDirectoryName;
|
||
|
||
InitializeListHead( &Notify->NotifyIrps );
|
||
|
||
if (WatchTree) {
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_WATCH_TREE );
|
||
}
|
||
|
||
if (FullDirectoryName == NULL) {
|
||
|
||
//
|
||
// In the view index we aren't using this buffer to hold a
|
||
// unicode string.
|
||
//
|
||
|
||
Notify->CharacterSize = sizeof( CHAR );
|
||
|
||
} else {
|
||
|
||
//
|
||
// We look at the directory name to decide if we have a unicode
|
||
// name.
|
||
//
|
||
|
||
if (FullDirectoryName->Length >= 2
|
||
&& FullDirectoryName->Buffer[1] == '\0') {
|
||
|
||
Notify->CharacterSize = sizeof( WCHAR );
|
||
|
||
} else {
|
||
|
||
Notify->CharacterSize = sizeof( CHAR );
|
||
}
|
||
|
||
if (FullDirectoryName->Length == Notify->CharacterSize) {
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_DIR_IS_ROOT );
|
||
}
|
||
}
|
||
|
||
Notify->CompletionFilter = CompletionFilter;
|
||
|
||
//
|
||
// If we are to return data to the user then look for the length
|
||
// of the original buffer in the IrpSp.
|
||
//
|
||
|
||
if (!IgnoreBuffer) {
|
||
|
||
Notify->BufferLength = IrpSp->Parameters.NotifyDirectory.Length;
|
||
}
|
||
|
||
Notify->OwningProcess = THREAD_TO_PROCESS( NotifyIrp->Tail.Overlay.Thread );
|
||
InsertTailList( NotifyList, &Notify->NotifyList );
|
||
|
||
Notify->ReferenceCount = 1;
|
||
|
||
//
|
||
// If we have already been called with cleanup then complete
|
||
// the request immediately.
|
||
//
|
||
|
||
} else if (FlagOn( Notify->Flags, NOTIFY_CLEANUP_CALLED )) {
|
||
|
||
//
|
||
// Always mark this Irp as pending returned.
|
||
//
|
||
|
||
IoMarkIrpPending( NotifyIrp );
|
||
|
||
FsRtlCompleteRequest( NotifyIrp, STATUS_NOTIFY_CLEANUP );
|
||
try_return( NOTHING );
|
||
|
||
//
|
||
// If this file has been deleted then complete with STATUS_DELETE_PENDING.
|
||
//
|
||
|
||
} else if (FlagOn( Notify->Flags, NOTIFY_STREAM_IS_DELETED )) {
|
||
|
||
//
|
||
// Always mark this Irp as pending returned.
|
||
//
|
||
|
||
IoMarkIrpPending( NotifyIrp );
|
||
|
||
FsRtlCompleteRequest( NotifyIrp, STATUS_DELETE_PENDING );
|
||
try_return( NOTHING );
|
||
|
||
//
|
||
// If the notify pending flag is set or there is data in an internal buffer
|
||
// we complete this Irp immediately and exit.
|
||
//
|
||
|
||
} else if (FlagOn( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY )
|
||
&& !FlagOn( Notify->Flags, NOTIFY_DEFER_NOTIFY )) {
|
||
|
||
DebugTrace( 0, Dbg, "Notify has been pending\n", 0 );
|
||
|
||
//
|
||
// Clear the flag in our notify structure before completing the
|
||
// Irp. This will prevent a caller who reposts in his completion
|
||
// routine from looping in the completion routine.
|
||
//
|
||
|
||
ClearFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
|
||
|
||
//
|
||
// Always mark this Irp as pending returned.
|
||
//
|
||
|
||
IoMarkIrpPending( NotifyIrp );
|
||
|
||
FsRtlCompleteRequest( NotifyIrp, STATUS_NOTIFY_ENUM_DIR );
|
||
try_return( NOTHING );
|
||
|
||
} else if (Notify->DataLength != 0
|
||
&& !FlagOn( Notify->Flags, NOTIFY_DEFER_NOTIFY )) {
|
||
|
||
ULONG ThisDataLength = Notify->DataLength;
|
||
|
||
//
|
||
// Now set our buffer pointers back to indicate an empty buffer.
|
||
//
|
||
|
||
Notify->DataLength = 0;
|
||
Notify->LastEntry = 0;
|
||
|
||
FsRtlNotifyCompleteIrp( NotifyIrp,
|
||
Notify,
|
||
ThisDataLength,
|
||
STATUS_SUCCESS,
|
||
FALSE );
|
||
|
||
try_return( NOTHING );
|
||
}
|
||
|
||
//
|
||
// Add the Irp to the tail of the notify queue.
|
||
//
|
||
|
||
NotifyIrp->IoStatus.Information = (ULONG_PTR) Notify;
|
||
IoMarkIrpPending( NotifyIrp );
|
||
InsertTailList( &Notify->NotifyIrps, &NotifyIrp->Tail.Overlay.ListEntry );
|
||
|
||
//
|
||
// Increment the reference count to indicate that Irp might go through cancel.
|
||
//
|
||
|
||
InterlockedIncrement( &Notify->ReferenceCount );
|
||
|
||
//
|
||
// Call the routine to set the cancel routine.
|
||
//
|
||
|
||
FsRtlNotifySetCancelRoutine( NotifyIrp, NULL );
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
//
|
||
// Release the mutex.
|
||
//
|
||
|
||
ReleaseNotifySync( NotifySync );
|
||
|
||
//
|
||
// If there is still a subject context then release it and deallocate
|
||
// the structure. Remember that if FullDirectoryName is null, it means
|
||
// this is a view index, not a directory index, and the SubjectContext
|
||
// is really a piece of file system context information.
|
||
//
|
||
|
||
if ((SubjectContext != NULL) &&
|
||
((Notify == NULL) ||
|
||
(Notify->FullDirectoryName != NULL))) {
|
||
|
||
SeReleaseSubjectContext( SubjectContext );
|
||
ExFreePool( SubjectContext );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyFullChangeDirectory: Exit\n", 0 );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FsRtlNotifyReportChange (
|
||
IN PNOTIFY_SYNC NotifySync,
|
||
IN PLIST_ENTRY NotifyList,
|
||
IN PSTRING FullTargetName,
|
||
IN PSTRING TargetName,
|
||
IN ULONG FilterMatch
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by a file system when a file has been modified in
|
||
such a way that it will cause a notify change Irp to complete. We walk
|
||
through all the notify structures looking for those structures which
|
||
would be associated with an ancestor directory of the target file name.
|
||
|
||
We look for all the notify structures which have a filter match and
|
||
then check that the directory name in the notify structure is a
|
||
proper prefix of the full target name.
|
||
|
||
If we find a notify structure which matches the above conditions, we
|
||
complete all the Irps for the notify structure. If the structure has
|
||
no Irps, we mark the notify pending field.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the controlling fast mutex for this notify list.
|
||
It is stored here so that it can be found for an Irp which is being
|
||
cancelled.
|
||
|
||
NotifyList - This is the start of the notify list to add this
|
||
structure to.
|
||
|
||
FullTargetName - This is the full name of the file which has been
|
||
changed.
|
||
|
||
TargetName - This is the final component of the modified file.
|
||
|
||
FilterMatch - This flag field is compared with the completion filter
|
||
in the notify structure. If any of the corresponding
|
||
bits in the completion filter are set, then a notify
|
||
condition exists.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyReportChange: Entered\n", 0 );
|
||
|
||
//
|
||
// Call the full notify routine to do the actual work.
|
||
//
|
||
|
||
FsRtlNotifyFilterReportChange( NotifySync,
|
||
NotifyList,
|
||
FullTargetName,
|
||
(USHORT) (FullTargetName->Length - TargetName->Length),
|
||
NULL,
|
||
NULL,
|
||
FilterMatch,
|
||
0,
|
||
NULL,
|
||
NULL );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyReportChange: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FsRtlNotifyFullReportChange (
|
||
IN PNOTIFY_SYNC NotifySync,
|
||
IN PLIST_ENTRY NotifyList,
|
||
IN PSTRING FullTargetName,
|
||
IN USHORT TargetNameOffset,
|
||
IN PSTRING StreamName OPTIONAL,
|
||
IN PSTRING NormalizedParentName OPTIONAL,
|
||
IN ULONG FilterMatch,
|
||
IN ULONG Action,
|
||
IN PVOID TargetContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by a file system when a file has been modified in
|
||
such a way that it will cause a notify change Irp to complete. We walk
|
||
through all the notify structures looking for those structures which
|
||
would be associated with an ancestor directory of the target file name.
|
||
|
||
We look for all the notify structures which have a filter match and
|
||
then check that the directory name in the notify structure is a
|
||
proper prefix of the full target name.
|
||
|
||
If we find a notify structure which matches the above conditions, we
|
||
complete all the Irps for the notify structure. If the structure has
|
||
no Irps, we mark the notify pending field.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the controlling fast mutex for this notify list.
|
||
It is stored here so that it can be found for an Irp which is being
|
||
cancelled.
|
||
|
||
NotifyList - This is the start of the notify list to add this
|
||
structure to.
|
||
|
||
FullTargetName - This is the full name of the file from the root of the volume.
|
||
|
||
TargetNameOffset - This is the offset in the full name of the final component
|
||
of the name.
|
||
|
||
StreamName - If present then this is the stream name to store with
|
||
the filename.
|
||
|
||
NormalizedParentName - If present this is the same path as the parent name
|
||
but the DOS-ONLY names have been replaced with the associated long name.
|
||
|
||
FilterMatch - This flag field is compared with the completion filter
|
||
in the notify structure. If any of the corresponding bits in the
|
||
completion filter are set, then a notify condition exists.
|
||
|
||
Action - This is the action code to store in the user's buffer if
|
||
present.
|
||
|
||
TargetContext - This is one of the context pointers to pass to the file
|
||
system if performing a traverse check in the case of a tree being
|
||
watched.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyReportChange: Entered\n", 0 );
|
||
|
||
//
|
||
// Call the full notify routine to do the actual work.
|
||
//
|
||
|
||
FsRtlNotifyFilterReportChange( NotifySync,
|
||
NotifyList,
|
||
FullTargetName,
|
||
TargetNameOffset,
|
||
StreamName,
|
||
NormalizedParentName,
|
||
FilterMatch,
|
||
Action,
|
||
TargetContext,
|
||
NULL );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyReportChange: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FsRtlNotifyFilterReportChange (
|
||
IN PNOTIFY_SYNC NotifySync,
|
||
IN PLIST_ENTRY NotifyList,
|
||
IN PSTRING FullTargetName,
|
||
IN USHORT TargetNameOffset,
|
||
IN PSTRING StreamName OPTIONAL,
|
||
IN PSTRING NormalizedParentName OPTIONAL,
|
||
IN ULONG FilterMatch,
|
||
IN ULONG Action,
|
||
IN PVOID TargetContext,
|
||
IN PVOID FilterContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by a file system when a file has been modified in
|
||
such a way that it will cause a notify change Irp to complete. We walk
|
||
through all the notify structures looking for those structures which
|
||
would be associated with an ancestor directory of the target file name.
|
||
|
||
We look for all the notify structures which have a filter match and
|
||
then check that the directory name in the notify structure is a
|
||
proper prefix of the full target name.
|
||
|
||
If we find a notify structure which matches the above conditions, we
|
||
complete all the Irps for the notify structure. If the structure has
|
||
no Irps, we mark the notify pending field.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the controlling fast mutex for this notify list.
|
||
It is stored here so that it can be found for an Irp which is being
|
||
cancelled.
|
||
|
||
NotifyList - This is the start of the notify list to add this
|
||
structure to.
|
||
|
||
FullTargetName - This is the full name of the file from the root of the volume.
|
||
|
||
TargetNameOffset - This is the offset in the full name of the final component
|
||
of the name.
|
||
|
||
StreamName - If present then this is the stream name to store with
|
||
the filename.
|
||
|
||
NormalizedParentName - If present this is the same path as the parent name
|
||
but the DOS-ONLY names have been replaced with the associated long name.
|
||
|
||
FilterMatch - This flag field is compared with the completion filter
|
||
in the notify structure. If any of the corresponding bits in the
|
||
completion filter are set, then a notify condition exists.
|
||
|
||
Action - This is the action code to store in the user's buffer if
|
||
present.
|
||
|
||
TargetContext - This is one of the context pointers to pass to the file
|
||
system if performing a traverse check in the case of a tree being
|
||
watched.
|
||
|
||
FilterContext - This is the filesystem-supplied routine used
|
||
to call back into the filesystem to check whether each Notify block
|
||
should see the change. (Initially added for TxfNtfs development
|
||
as part of a strategy to control when, not if, transactions see
|
||
changes from other transactions.)
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY NotifyLinks;
|
||
|
||
STRING NormalizedParent;
|
||
STRING ParentName;
|
||
STRING TargetName;
|
||
|
||
PNOTIFY_CHANGE Notify;
|
||
STRING TargetParent;
|
||
PIRP NotifyIrp;
|
||
|
||
BOOLEAN NotifyIsParent;
|
||
BOOLEAN ViewIndex = FALSE;
|
||
UCHAR ComponentCount;
|
||
ULONG SizeOfEntry;
|
||
ULONG CurrentOffset;
|
||
ULONG NextEntryOffset;
|
||
ULONG ExceptionCode;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyFullReportChange: Entered\n", 0 );
|
||
|
||
//
|
||
// If this is a change to the root directory then return immediately.
|
||
//
|
||
|
||
if ((TargetNameOffset == 0) && (FullTargetName != NULL)) {
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyFullReportChange: Exit\n", 0 );
|
||
return;
|
||
}
|
||
|
||
ParentName.Buffer = NULL;
|
||
TargetName.Buffer = NULL;
|
||
|
||
//
|
||
// Acquire exclusive access to the list by acquiring the mutex.
|
||
//
|
||
|
||
AcquireNotifySync( NotifySync );
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Walk through all the notify blocks.
|
||
//
|
||
|
||
for (NotifyLinks = NotifyList->Flink;
|
||
NotifyLinks != NotifyList;
|
||
NotifyLinks = NotifyLinks->Flink) {
|
||
|
||
//
|
||
// Obtain the Notify structure from the list entry.
|
||
//
|
||
|
||
Notify = CONTAINING_RECORD( NotifyLinks, NOTIFY_CHANGE, NotifyList );
|
||
|
||
//
|
||
// The rules for deciding whether this notification applies are
|
||
// different for view indices versus file name indices (directories).
|
||
//
|
||
|
||
if (FullTargetName == NULL) {
|
||
|
||
ASSERTMSG( "Directory notify handle in view index notify list!", Notify->FullDirectoryName == NULL);
|
||
|
||
//
|
||
// Make sure this is the Fcb being watched.
|
||
//
|
||
|
||
if (TargetContext != Notify->SubjectContext) {
|
||
|
||
continue;
|
||
}
|
||
|
||
TargetParent.Buffer = NULL;
|
||
TargetParent.Length = 0;
|
||
|
||
ViewIndex = TRUE;
|
||
NotifyIsParent = FALSE;
|
||
|
||
//
|
||
// Handle the directory case.
|
||
//
|
||
|
||
} else {
|
||
|
||
ASSERTMSG( "View index notify handle in directory notify list!", Notify->FullDirectoryName != NULL);
|
||
|
||
//
|
||
// If the length of the name in the notify block is currently zero then
|
||
// someone is doing a rename and we can skip this block.
|
||
//
|
||
|
||
if (Notify->FullDirectoryName->Length == 0) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// If this filter match is not part of the completion filter then continue.
|
||
//
|
||
|
||
if (!(FilterMatch & Notify->CompletionFilter)) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// If there is no normalized name then set its value from the full
|
||
// file name.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT( NormalizedParentName )) {
|
||
NormalizedParent.Buffer = FullTargetName->Buffer;
|
||
NormalizedParent.Length = TargetNameOffset;
|
||
|
||
if (NormalizedParent.Length != Notify->CharacterSize) {
|
||
|
||
NormalizedParent.Length -= Notify->CharacterSize;
|
||
}
|
||
|
||
NormalizedParent.MaximumLength = NormalizedParent.Length;
|
||
|
||
NormalizedParentName = &NormalizedParent;
|
||
}
|
||
|
||
//
|
||
// If the length of the directory being watched is longer than the
|
||
// parent of the modified file then it can't be an ancestor of the
|
||
// modified file.
|
||
//
|
||
|
||
if (Notify->FullDirectoryName->Length > NormalizedParentName->Length) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// If the lengths match exactly then this can only be the parent of
|
||
// the modified file.
|
||
//
|
||
|
||
if (NormalizedParentName->Length == Notify->FullDirectoryName->Length) {
|
||
|
||
NotifyIsParent = TRUE;
|
||
|
||
//
|
||
// If we are not watching the subtree of this directory then continue.
|
||
//
|
||
|
||
} else if (!FlagOn( Notify->Flags, NOTIFY_WATCH_TREE )) {
|
||
|
||
continue;
|
||
|
||
//
|
||
// The watched directory can only be an ancestor of the modified
|
||
// file. Make sure that there is legal pathname separator immediately
|
||
// after the end of the watched directory name within the normalized name.
|
||
// If the watched directory is the root then we know this condition is TRUE.
|
||
//
|
||
|
||
} else {
|
||
|
||
if (!FlagOn( Notify->Flags, NOTIFY_DIR_IS_ROOT )) {
|
||
|
||
//
|
||
// Check for the character size.
|
||
//
|
||
|
||
if (Notify->CharacterSize == sizeof( CHAR )) {
|
||
|
||
if (*(Add2Ptr( NormalizedParentName->Buffer,
|
||
Notify->FullDirectoryName->Length,
|
||
PCHAR )) != '\\') {
|
||
|
||
continue;
|
||
}
|
||
|
||
} else if (*(Add2Ptr( NormalizedParentName->Buffer,
|
||
Notify->FullDirectoryName->Length,
|
||
PWCHAR )) != L'\\') {
|
||
|
||
continue;
|
||
}
|
||
}
|
||
|
||
NotifyIsParent = FALSE;
|
||
}
|
||
|
||
//
|
||
// We now have a correct match of the name lengths. Now verify that the
|
||
// characters match exactly.
|
||
//
|
||
|
||
if (!RtlEqualMemory( Notify->FullDirectoryName->Buffer,
|
||
NormalizedParentName->Buffer,
|
||
Notify->FullDirectoryName->Length )) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// The characters are correct. Now check in the case of a non-parent
|
||
// notify that we have traverse callback.
|
||
//
|
||
|
||
if (!NotifyIsParent &&
|
||
Notify->TraverseCallback != NULL &&
|
||
!Notify->TraverseCallback( Notify->FsContext,
|
||
TargetContext,
|
||
Notify->SubjectContext )) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Finally, if Notify block has a FilterRoutine *and* the caller specified
|
||
// a FilterContext, then we must finally call the filter routine.
|
||
//
|
||
|
||
if ((Notify->FilterCallback != NULL) &&
|
||
ARGUMENT_PRESENT( FilterContext ) &&
|
||
!Notify->FilterCallback( Notify->FsContext, FilterContext )) {
|
||
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this entry is going into a buffer then check that
|
||
// it will fit.
|
||
//
|
||
|
||
if (!FlagOn( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY )
|
||
&& Notify->BufferLength != 0) {
|
||
|
||
ULONG AllocationLength;
|
||
|
||
AllocationLength = 0;
|
||
NotifyIrp = NULL;
|
||
|
||
//
|
||
// If we don't already have a buffer then check to see
|
||
// if we have any Irps in the list and use the buffer
|
||
// length in the Irp.
|
||
//
|
||
|
||
if (Notify->ThisBufferLength == 0) {
|
||
|
||
//
|
||
// If there is an entry in the list then get the length.
|
||
//
|
||
|
||
if (!IsListEmpty( &Notify->NotifyIrps )) {
|
||
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
NotifyIrp = CONTAINING_RECORD( Notify->NotifyIrps.Flink,
|
||
IRP,
|
||
Tail.Overlay.ListEntry );
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( NotifyIrp );
|
||
|
||
AllocationLength = IrpSp->Parameters.NotifyDirectory.Length;
|
||
|
||
//
|
||
// Otherwise use the caller's last buffer size.
|
||
//
|
||
|
||
} else {
|
||
|
||
AllocationLength = Notify->BufferLength;
|
||
}
|
||
|
||
//
|
||
// Otherwise use the length of the current buffer.
|
||
//
|
||
|
||
} else {
|
||
|
||
AllocationLength = Notify->ThisBufferLength;
|
||
}
|
||
|
||
//
|
||
// Build the strings for the relative name. This includes
|
||
// the strings for the parent name, file name and stream
|
||
// name.
|
||
//
|
||
|
||
if (!NotifyIsParent) {
|
||
|
||
//
|
||
// We need to find the string for the ancestor of this
|
||
// file from the watched directory. If the normalized parent
|
||
// name is the same as the parent name then we can use
|
||
// the tail of the parent directly. Otherwise we need to
|
||
// count the matching name components and capture the
|
||
// final components.
|
||
//
|
||
|
||
if (!ViewIndex) {
|
||
|
||
//
|
||
// If the watched directory is the root then we just use the full
|
||
// parent name.
|
||
//
|
||
|
||
if (FlagOn( Notify->Flags, NOTIFY_DIR_IS_ROOT ) ||
|
||
NormalizedParentName->Buffer != FullTargetName->Buffer) {
|
||
|
||
//
|
||
// If we don't have a string for the parent then construct
|
||
// it now.
|
||
//
|
||
|
||
if (ParentName.Buffer == NULL) {
|
||
|
||
ParentName.Buffer = FullTargetName->Buffer;
|
||
ParentName.Length = TargetNameOffset;
|
||
|
||
if (ParentName.Length != Notify->CharacterSize) {
|
||
|
||
ParentName.Length -= Notify->CharacterSize;
|
||
}
|
||
|
||
ParentName.MaximumLength = ParentName.Length;
|
||
}
|
||
|
||
//
|
||
// Count through the components of the parent until we have
|
||
// swallowed the same number of name components as in the
|
||
// watched directory name. We have the unicode version and
|
||
// the Ansi version to watch for.
|
||
//
|
||
|
||
ComponentCount = 0;
|
||
CurrentOffset = 0;
|
||
|
||
//
|
||
// If this is the root then there is no more to do.
|
||
//
|
||
|
||
if (FlagOn( Notify->Flags, NOTIFY_DIR_IS_ROOT )) {
|
||
|
||
NOTHING;
|
||
|
||
} else {
|
||
|
||
ULONG ParentComponentCount;
|
||
ULONG ParentOffset;
|
||
|
||
ParentComponentCount = 1;
|
||
ParentOffset = 0;
|
||
|
||
if (Notify->CharacterSize == sizeof( CHAR )) {
|
||
|
||
//
|
||
// Find the number of components in the parent. We
|
||
// have to do this for each one because this name and
|
||
// the number of components could have changed.
|
||
//
|
||
|
||
while (ParentOffset < Notify->FullDirectoryName->Length) {
|
||
|
||
if (*((PCHAR) Notify->FullDirectoryName->Buffer + ParentOffset) == '\\') {
|
||
|
||
ParentComponentCount += 1;
|
||
}
|
||
|
||
ParentOffset += 1;
|
||
}
|
||
|
||
while (TRUE) {
|
||
|
||
if (*((PCHAR) ParentName.Buffer + CurrentOffset) == '\\') {
|
||
|
||
ComponentCount += 1;
|
||
|
||
if (ComponentCount == ParentComponentCount) {
|
||
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
CurrentOffset += 1;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Find the number of components in the parent. We
|
||
// have to do this for each one because this name and
|
||
// the number of components could have changed.
|
||
//
|
||
|
||
while (ParentOffset < Notify->FullDirectoryName->Length / sizeof( WCHAR )) {
|
||
|
||
if (*((PWCHAR) Notify->FullDirectoryName->Buffer + ParentOffset) == '\\') {
|
||
|
||
ParentComponentCount += 1;
|
||
}
|
||
|
||
ParentOffset += 1;
|
||
}
|
||
|
||
while (TRUE) {
|
||
|
||
if (*((PWCHAR) ParentName.Buffer + CurrentOffset) == L'\\') {
|
||
|
||
ComponentCount += 1;
|
||
|
||
if (ComponentCount == ParentComponentCount) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
CurrentOffset += 1;
|
||
}
|
||
|
||
//
|
||
// Convert characters to bytes.
|
||
//
|
||
|
||
CurrentOffset *= Notify->CharacterSize;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We now know the offset into the parent name of the separator
|
||
// immediately preceding the relative parent name. Construct the
|
||
// target parent name for the buffer.
|
||
//
|
||
|
||
CurrentOffset += Notify->CharacterSize;
|
||
|
||
TargetParent.Buffer = Add2Ptr( ParentName.Buffer,
|
||
CurrentOffset,
|
||
PCHAR );
|
||
TargetParent.MaximumLength =
|
||
TargetParent.Length = ParentName.Length - (USHORT) CurrentOffset;
|
||
|
||
//
|
||
// If the normalized is the same as the parent name use the portion
|
||
// after the match with the watched directory.
|
||
//
|
||
|
||
} else {
|
||
|
||
TargetParent.Buffer = Add2Ptr( NormalizedParentName->Buffer,
|
||
(Notify->FullDirectoryName->Length +
|
||
Notify->CharacterSize),
|
||
PCHAR );
|
||
|
||
TargetParent.MaximumLength =
|
||
TargetParent.Length = NormalizedParentName->Length -
|
||
Notify->FullDirectoryName->Length -
|
||
Notify->CharacterSize;
|
||
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// The length of the target parent is zero.
|
||
//
|
||
|
||
TargetParent.Length = 0;
|
||
}
|
||
|
||
//
|
||
// Compute how much buffer space this report will take.
|
||
//
|
||
|
||
SizeOfEntry = FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName );
|
||
|
||
if (ViewIndex) {
|
||
|
||
//
|
||
// In the view index case, the information to copy to the
|
||
// buffer comes to us in the stream name, and that is all
|
||
// the room we need to worry about having.
|
||
//
|
||
|
||
ASSERT(ARGUMENT_PRESENT( StreamName ));
|
||
|
||
SizeOfEntry += StreamName->Length;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If there is a parent to report, find the size and include a separator
|
||
// character.
|
||
//
|
||
|
||
if (!NotifyIsParent) {
|
||
|
||
if (Notify->CharacterSize == sizeof( CHAR )) {
|
||
|
||
SizeOfEntry += RtlOemStringToCountedUnicodeSize( &TargetParent );
|
||
|
||
} else {
|
||
|
||
SizeOfEntry += TargetParent.Length;
|
||
}
|
||
|
||
//
|
||
// Include the separator. This is always a unicode character.
|
||
//
|
||
|
||
SizeOfEntry += sizeof( WCHAR );
|
||
}
|
||
|
||
//
|
||
// If we don't have the string for the target then construct it now.
|
||
//
|
||
|
||
if (TargetName.Buffer == NULL) {
|
||
|
||
TargetName.Buffer = Add2Ptr( FullTargetName->Buffer, TargetNameOffset, PCHAR );
|
||
TargetName.MaximumLength =
|
||
TargetName.Length = FullTargetName->Length - TargetNameOffset;
|
||
}
|
||
|
||
if (Notify->CharacterSize == sizeof( CHAR )) {
|
||
|
||
SizeOfEntry += RtlOemStringToCountedUnicodeSize( &TargetName );
|
||
|
||
} else {
|
||
|
||
SizeOfEntry += TargetName.Length;
|
||
}
|
||
|
||
//
|
||
// If there is a stream name then add the bytes needed
|
||
// for that.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( StreamName )) {
|
||
|
||
//
|
||
// Add the space needed for the ':' separator.
|
||
//
|
||
|
||
if (Notify->CharacterSize == sizeof( WCHAR )) {
|
||
|
||
SizeOfEntry += (StreamName->Length + sizeof( WCHAR ));
|
||
|
||
} else {
|
||
|
||
SizeOfEntry += (RtlOemStringToCountedUnicodeSize( StreamName )
|
||
+ sizeof( CHAR ));
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Remember if this report would overflow the buffer.
|
||
//
|
||
|
||
NextEntryOffset = (ULONG)LongAlign( Notify->DataLength );
|
||
|
||
if (SizeOfEntry <= AllocationLength
|
||
&& (NextEntryOffset + SizeOfEntry) <= AllocationLength) {
|
||
|
||
PFILE_NOTIFY_INFORMATION NotifyInfo = NULL;
|
||
|
||
//
|
||
// If there is already a notify buffer, we append this
|
||
// data to it.
|
||
//
|
||
|
||
if (Notify->Buffer != NULL) {
|
||
|
||
NotifyInfo = Add2Ptr( Notify->Buffer,
|
||
Notify->LastEntry,
|
||
PFILE_NOTIFY_INFORMATION );
|
||
|
||
NotifyInfo->NextEntryOffset = NextEntryOffset - Notify->LastEntry;
|
||
|
||
Notify->LastEntry = NextEntryOffset;
|
||
|
||
NotifyInfo = Add2Ptr( Notify->Buffer,
|
||
Notify->LastEntry,
|
||
PFILE_NOTIFY_INFORMATION );
|
||
|
||
//
|
||
// If there is an Irp list we check whether we will need
|
||
// to allocate a new buffer.
|
||
//
|
||
|
||
} else if (NotifyIrp != NULL) {
|
||
|
||
if (NotifyIrp->AssociatedIrp.SystemBuffer != NULL) {
|
||
|
||
Notify->Buffer =
|
||
NotifyInfo = NotifyIrp->AssociatedIrp.SystemBuffer;
|
||
|
||
Notify->ThisBufferLength = AllocationLength;
|
||
|
||
} else if (NotifyIrp->MdlAddress != NULL) {
|
||
|
||
Notify->Buffer =
|
||
NotifyInfo = MmGetSystemAddressForMdl( NotifyIrp->MdlAddress );
|
||
|
||
Notify->ThisBufferLength = AllocationLength;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we need to allocate a buffer, we will charge quota
|
||
// to the original process and allocate paged pool.
|
||
//
|
||
|
||
if (Notify->Buffer == NULL) {
|
||
|
||
BOOLEAN ChargedQuota = FALSE;
|
||
|
||
try {
|
||
|
||
PsChargePoolQuota( Notify->OwningProcess,
|
||
PagedPool,
|
||
AllocationLength );
|
||
|
||
ChargedQuota = TRUE;
|
||
|
||
Notify->AllocatedBuffer =
|
||
Notify->Buffer = FsRtlpAllocatePool( PagedPool,
|
||
AllocationLength );
|
||
|
||
Notify->ThisBufferLength = AllocationLength;
|
||
|
||
NotifyInfo = Notify->Buffer;
|
||
|
||
} except(( ExceptionCode = GetExceptionCode(), FsRtlIsNtstatusExpected(ExceptionCode))
|
||
? EXCEPTION_EXECUTE_HANDLER
|
||
: EXCEPTION_CONTINUE_SEARCH ) {
|
||
|
||
|
||
ASSERT( (ExceptionCode == STATUS_INSUFFICIENT_RESOURCES) ||
|
||
(ExceptionCode == STATUS_QUOTA_EXCEEDED) );
|
||
|
||
//
|
||
// Return quota if we allocated the buffer.
|
||
//
|
||
|
||
if (ChargedQuota) {
|
||
|
||
PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
|
||
AllocationLength );
|
||
|
||
}
|
||
|
||
//
|
||
// Forget any current buffer and resort to immediate
|
||
// notify.
|
||
//
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we have a buffer then fill in the results
|
||
// from this operation. Otherwise we remember
|
||
// to simply alert the caller.
|
||
//
|
||
|
||
if (NotifyInfo != NULL) {
|
||
|
||
//
|
||
// Update the buffer with the current data.
|
||
//
|
||
|
||
if (FsRtlNotifyUpdateBuffer( NotifyInfo,
|
||
Action,
|
||
&TargetParent,
|
||
&TargetName,
|
||
StreamName,
|
||
(BOOLEAN) (Notify->CharacterSize == sizeof( WCHAR )),
|
||
SizeOfEntry )) {
|
||
|
||
//
|
||
// Update the buffer data length.
|
||
//
|
||
|
||
Notify->DataLength = NextEntryOffset + SizeOfEntry;
|
||
|
||
//
|
||
// We couldn't copy the data into the buffer. Just
|
||
// notify without any additional information.
|
||
//
|
||
|
||
} else {
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
|
||
}
|
||
|
||
//
|
||
// If we have a buffer but can't use it then clear all of the
|
||
// buffer related fields. Also deallocate any buffer allocated
|
||
// by us.
|
||
//
|
||
|
||
if (FlagOn( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY )
|
||
&& Notify->Buffer != NULL) {
|
||
|
||
if (Notify->AllocatedBuffer != NULL) {
|
||
|
||
PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
|
||
Notify->ThisBufferLength );
|
||
|
||
ExFreePool( Notify->AllocatedBuffer );
|
||
}
|
||
|
||
Notify->AllocatedBuffer = Notify->Buffer = NULL;
|
||
|
||
Notify->ThisBufferLength = Notify->DataLength = Notify->LastEntry = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Complete the next entry on the list if we aren't holding
|
||
// up notification.
|
||
//
|
||
|
||
if (Action == FILE_ACTION_RENAMED_OLD_NAME) {
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_DEFER_NOTIFY );
|
||
|
||
} else {
|
||
|
||
ClearFlag( Notify->Flags, NOTIFY_DEFER_NOTIFY );
|
||
|
||
if (!IsListEmpty( &Notify->NotifyIrps )) {
|
||
|
||
FsRtlNotifyCompleteIrpList( Notify, STATUS_SUCCESS );
|
||
}
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
ReleaseNotifySync( NotifySync );
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyFullReportChange: Exit\n", 0 );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FsRtlNotifyCleanup (
|
||
IN PNOTIFY_SYNC NotifySync,
|
||
IN PLIST_ENTRY NotifyList,
|
||
IN PVOID FsContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called for a cleanup of a user directory handle. We
|
||
walk through our notify structures looking for a matching context field.
|
||
We complete all the pending notify Irps for this Notify structure, remove
|
||
the notify structure and deallocate it.
|
||
|
||
Arguments:
|
||
|
||
NotifySync - This is the controlling fast mutex for this notify list.
|
||
It is stored here so that it can be found for an Irp which is being
|
||
cancelled.
|
||
|
||
NotifyList - This is the start of the notify list to add this
|
||
structure to.
|
||
|
||
FsContext - This is a unique value assigned by the file system to
|
||
identify a particular notify structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNOTIFY_CHANGE Notify;
|
||
PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyCleanup: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Mutex -> %08lx\n", Mutex );
|
||
DebugTrace( 0, Dbg, "Notify List -> %08lx\n", NotifyList );
|
||
DebugTrace( 0, Dbg, "FsContext -> %08lx\n", FsContext );
|
||
|
||
//
|
||
// Acquire exclusive access to the list by acquiring the mutex.
|
||
//
|
||
|
||
AcquireNotifySync( NotifySync );
|
||
|
||
//
|
||
// Use a try-finally to facilitate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Search for a match on the list.
|
||
//
|
||
|
||
Notify = FsRtlIsNotifyOnList( NotifyList, FsContext );
|
||
|
||
//
|
||
// If found, then complete all the Irps with STATUS_NOTIFY_CLEANUP
|
||
//
|
||
|
||
if (Notify != NULL) {
|
||
|
||
//
|
||
// Set the flag to indicate that we have been called with cleanup.
|
||
//
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_CLEANUP_CALLED );
|
||
|
||
if (!IsListEmpty( &Notify->NotifyIrps )) {
|
||
|
||
FsRtlNotifyCompleteIrpList( Notify, STATUS_NOTIFY_CLEANUP );
|
||
}
|
||
|
||
//
|
||
// Cleanup can only occur once, and will always remove the notify from
|
||
// the list. Cancel will defer this work to cleanup, and cannot free
|
||
// a notify which has not gone through cleanup since we have a ref
|
||
// to ensure it.
|
||
//
|
||
|
||
RemoveEntryList( &Notify->NotifyList );
|
||
|
||
//
|
||
// We assert this in cancel routine.
|
||
//
|
||
|
||
#if DBG
|
||
Notify->NotifyList.Flink = NULL;
|
||
#endif
|
||
|
||
InterlockedDecrement( &Notify->ReferenceCount );
|
||
|
||
if (Notify->ReferenceCount == 0) {
|
||
|
||
if (Notify->AllocatedBuffer != NULL) {
|
||
|
||
PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
|
||
Notify->ThisBufferLength );
|
||
|
||
ExFreePool( Notify->AllocatedBuffer );
|
||
}
|
||
|
||
if (Notify->FullDirectoryName != NULL) {
|
||
|
||
SubjectContext = Notify->SubjectContext;
|
||
}
|
||
|
||
ExFreePool( Notify );
|
||
}
|
||
}
|
||
|
||
} finally {
|
||
|
||
ReleaseNotifySync( NotifySync );
|
||
|
||
if (SubjectContext != NULL) {
|
||
|
||
SeReleaseSubjectContext( SubjectContext );
|
||
ExFreePool( SubjectContext );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyCleanup: Exit\n", 0 );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
PNOTIFY_CHANGE
|
||
FsRtlIsNotifyOnList (
|
||
IN PLIST_ENTRY NotifyListHead,
|
||
IN PVOID FsContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to walk through a notify list searching for
|
||
a member associated with the FsContext value.
|
||
|
||
Arguments:
|
||
|
||
NotifyListHead - This is the start of the notify list.
|
||
|
||
FsContext - This is supplied by the file system so that this notify
|
||
structure can be uniquely identified.
|
||
|
||
Return Value:
|
||
|
||
PNOTIFY_CHANGE - A pointer to the matching structure is returned. NULL
|
||
is returned if the structure isn't present.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY Link;
|
||
|
||
PNOTIFY_CHANGE ThisNotify;
|
||
PNOTIFY_CHANGE Notify;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlIsNotifyOnList: Entered\n", 0 );
|
||
|
||
//
|
||
// Assume we won't have a match.
|
||
//
|
||
|
||
Notify = NULL;
|
||
|
||
//
|
||
// Walk through all the entries on the list looking for a match.
|
||
//
|
||
|
||
for (Link = NotifyListHead->Flink;
|
||
Link != NotifyListHead;
|
||
Link = Link->Flink) {
|
||
|
||
//
|
||
// Obtain the notify structure from the link.
|
||
//
|
||
|
||
ThisNotify = CONTAINING_RECORD( Link, NOTIFY_CHANGE, NotifyList );
|
||
|
||
//
|
||
// If the context field matches, remember this structure and
|
||
// exit.
|
||
//
|
||
|
||
if (ThisNotify->FsContext == FsContext) {
|
||
|
||
Notify = ThisNotify;
|
||
break;
|
||
}
|
||
}
|
||
|
||
DebugTrace( 0, Dbg, "Notify Structure -> %08lx\n", Notify );
|
||
DebugTrace( -1, Dbg, "FsRtlIsNotifyOnList: Exit\n", 0 );
|
||
|
||
return Notify;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
FsRtlNotifyCompleteIrp (
|
||
IN PIRP NotifyIrp,
|
||
IN PNOTIFY_CHANGE Notify,
|
||
IN ULONG DataLength,
|
||
IN NTSTATUS Status,
|
||
IN ULONG CheckCancel
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to complete an Irp with the data in the NOTIFY_CHANGE
|
||
structure.
|
||
|
||
Arguments:
|
||
|
||
NotifyIrp - Irp to complete.
|
||
|
||
Notify - Notify structure which contains the data.
|
||
|
||
DataLength - This is the length of the data in the buffer in the notify
|
||
structure. A value of zero indicates that we should complete the
|
||
request with STATUS_NOTIFY_ENUM_DIR.
|
||
|
||
Status - Indicates the status to complete the Irp with.
|
||
|
||
CheckCancel - Indicates if we should only complete the irp if we clear the cancel routine
|
||
ourselves.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlIsNotifyCompleteIrp: Entered\n", 0 );
|
||
|
||
//
|
||
// Attempt to clear the cancel routine. If this routine owns the cancel
|
||
// routine then it can complete the irp. Otherwise there is a cancel underway
|
||
// on this.
|
||
//
|
||
|
||
if (FsRtlNotifySetCancelRoutine( NotifyIrp, Notify ) || !CheckCancel) {
|
||
|
||
//
|
||
// We only process the buffer if the status is STATUS_SUCCESS.
|
||
//
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
//
|
||
// Get the current Stack location
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( NotifyIrp );
|
||
|
||
//
|
||
// If the data won't fit in the user's buffer or there was already a
|
||
// buffer overflow then return the alternate status code. If the data
|
||
// was already stored in the Irp buffer then we know that we won't
|
||
// take this path. Otherwise we wouldn't be cleaning up the Irp
|
||
// correctly.
|
||
//
|
||
|
||
if (DataLength == 0
|
||
|| IrpSp->Parameters.NotifyDirectory.Length < DataLength) {
|
||
|
||
Status = STATUS_NOTIFY_ENUM_DIR;
|
||
|
||
//
|
||
// We have to carefully return the buffer to the user and handle all
|
||
// of the different buffer cases. If there is no allocated buffer
|
||
// in the notify structure it means that we have already used the
|
||
// caller's buffer.
|
||
//
|
||
// 1 - If the system allocated an associated system buffer we
|
||
// can simply fill that in.
|
||
//
|
||
// 2 - If there is an Mdl then we get a system address for the Mdl
|
||
// and copy the data into it.
|
||
//
|
||
// 3 - If there is only a user's buffer and pending has not been
|
||
// returned, we can fill the user's buffer in directly.
|
||
//
|
||
// 4 - If there is only a user's buffer and pending has been returned
|
||
// then we are not in the user's address space. We dress up
|
||
// the Irp with our system buffer and let the Io system
|
||
// copy the data in.
|
||
//
|
||
|
||
} else {
|
||
|
||
if (Notify->AllocatedBuffer != NULL) {
|
||
|
||
//
|
||
// Protect the copy with a try-except and ignore the buffer
|
||
// if we have some error in copying it to the buffer.
|
||
//
|
||
|
||
try {
|
||
|
||
if (NotifyIrp->AssociatedIrp.SystemBuffer != NULL) {
|
||
|
||
RtlCopyMemory( NotifyIrp->AssociatedIrp.SystemBuffer,
|
||
Notify->AllocatedBuffer,
|
||
DataLength );
|
||
|
||
} else if (NotifyIrp->MdlAddress != NULL) {
|
||
|
||
RtlCopyMemory( MmGetSystemAddressForMdl( NotifyIrp->MdlAddress ),
|
||
Notify->AllocatedBuffer,
|
||
DataLength );
|
||
|
||
} else if (!FlagOn( IrpSp->Control, SL_PENDING_RETURNED )) {
|
||
|
||
RtlCopyMemory( NotifyIrp->UserBuffer,
|
||
Notify->AllocatedBuffer,
|
||
DataLength );
|
||
|
||
} else {
|
||
|
||
NotifyIrp->Flags |= (IRP_BUFFERED_IO | IRP_INPUT_OPERATION | IRP_DEALLOCATE_BUFFER);
|
||
NotifyIrp->AssociatedIrp.SystemBuffer = Notify->AllocatedBuffer;
|
||
|
||
}
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
Status = STATUS_NOTIFY_ENUM_DIR;
|
||
DataLength = 0;
|
||
}
|
||
|
||
//
|
||
// Return the quota and deallocate the buffer if we didn't pass it
|
||
// back via the irp.
|
||
//
|
||
|
||
PsReturnProcessPagedPoolQuota( Notify->OwningProcess, Notify->ThisBufferLength );
|
||
|
||
if (Notify->AllocatedBuffer != NotifyIrp->AssociatedIrp.SystemBuffer
|
||
&& Notify->AllocatedBuffer != NULL) {
|
||
|
||
ExFreePool( Notify->AllocatedBuffer );
|
||
}
|
||
|
||
Notify->AllocatedBuffer = NULL;
|
||
Notify->ThisBufferLength = 0;
|
||
}
|
||
|
||
//
|
||
// Update the data length in the Irp.
|
||
//
|
||
|
||
NotifyIrp->IoStatus.Information = DataLength;
|
||
|
||
//
|
||
// Show that there is no buffer in the notify package
|
||
// anymore.
|
||
//
|
||
|
||
Notify->Buffer = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make sure the Irp is marked as pending returned.
|
||
//
|
||
|
||
IoMarkIrpPending( NotifyIrp );
|
||
|
||
//
|
||
// Now complete the request.
|
||
//
|
||
|
||
FsRtlCompleteRequest( NotifyIrp, Status );
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlIsNotifyCompleteIrp: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
BOOLEAN
|
||
FsRtlNotifySetCancelRoutine (
|
||
IN PIRP NotifyIrp,
|
||
IN PNOTIFY_CHANGE Notify OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a separate routine because it cannot be paged.
|
||
|
||
Arguments:
|
||
|
||
NotifyIrp - Set the cancel routine in this Irp.
|
||
|
||
Notify - If NULL then we are setting the cancel routine. If not-NULL then we
|
||
are clearing the cancel routine. If the cancel routine is not-null then
|
||
we need to decrement the reference count on this Notify structure
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - Only meaningfull if Notify is specified. It indicates if this
|
||
routine cleared the cancel routine. FALSE indicates that the cancel
|
||
routine is processing the Irp.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN ClearedCancel = FALSE;
|
||
PDRIVER_CANCEL CurrentCancel;
|
||
|
||
//
|
||
// Grab the cancel spinlock and set our cancel routine in the Irp.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock( &NotifyIrp->CancelIrql );
|
||
|
||
//
|
||
// If we are completing an Irp then clear the cancel routine and
|
||
// the information field.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( Notify )) {
|
||
|
||
CurrentCancel = IoSetCancelRoutine( NotifyIrp, NULL );
|
||
NotifyIrp->IoStatus.Information = 0;
|
||
|
||
IoReleaseCancelSpinLock( NotifyIrp->CancelIrql );
|
||
|
||
//
|
||
// If the current cancel routine is non-NULL then decrement the reference count
|
||
// in the Notify.
|
||
//
|
||
|
||
if (CurrentCancel != NULL) {
|
||
|
||
InterlockedDecrement( &Notify->ReferenceCount );
|
||
ClearedCancel = TRUE;
|
||
}
|
||
|
||
//
|
||
// If the cancel flag is set, we complete the Irp with cancelled
|
||
// status and exit.
|
||
//
|
||
|
||
} else if (NotifyIrp->Cancel) {
|
||
|
||
DebugTrace( 0, Dbg, "Irp has been cancelled\n", 0 );
|
||
|
||
FsRtlCancelNotify( NULL, NotifyIrp );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set our cancel routine in the Irp.
|
||
//
|
||
|
||
IoSetCancelRoutine( NotifyIrp, FsRtlCancelNotify );
|
||
|
||
IoReleaseCancelSpinLock( NotifyIrp->CancelIrql );
|
||
}
|
||
|
||
return ClearedCancel;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
BOOLEAN
|
||
FsRtlNotifyUpdateBuffer (
|
||
IN PFILE_NOTIFY_INFORMATION NotifyInfo,
|
||
IN ULONG FileAction,
|
||
IN PSTRING ParentName,
|
||
IN PSTRING TargetName,
|
||
IN PSTRING StreamName OPTIONAL,
|
||
IN BOOLEAN UnicodeName,
|
||
IN ULONG SizeOfEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to fill in a FILE_NOTIFY_INFORMATION structure for a
|
||
notify change event. The main work is in converting an OEM string to Unicode.
|
||
|
||
Arguments:
|
||
|
||
NotifyInfo - Information structure to complete.
|
||
|
||
FileAction - Action which triggered the notification event.
|
||
|
||
ParentName - Relative path to the parent of the changed file from the
|
||
directory being watched. The length for this will be zero if the modified
|
||
file is in the watched directory.
|
||
|
||
TargetName - This is the name of the modified file.
|
||
|
||
StreamName - If present there is a stream name to append to the filename.
|
||
|
||
UnicodeName - Indicates if the above name is Unicode or Oem.
|
||
|
||
SizeOfEntry - Indicates the number of bytes to be used in the buffer.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if we were able to update the buffer, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN CopiedToBuffer;
|
||
ULONG BufferOffset = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyUpdateBuffer: Entered\n", 0 );
|
||
|
||
//
|
||
// Protect the entire call with a try-except. If we had an error
|
||
// we will assume that we have a bad buffer and we won't return
|
||
// the data in the buffer.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Update the common fields in the notify information.
|
||
//
|
||
|
||
NotifyInfo->NextEntryOffset = 0;
|
||
NotifyInfo->Action = FileAction;
|
||
|
||
NotifyInfo->FileNameLength = SizeOfEntry - FIELD_OFFSET( FILE_NOTIFY_INFORMATION, FileName );
|
||
|
||
//
|
||
// If we have a unicode name, then copy the data directly into the output buffer.
|
||
//
|
||
|
||
if (UnicodeName) {
|
||
|
||
if (ParentName->Length != 0) {
|
||
|
||
RtlCopyMemory( NotifyInfo->FileName,
|
||
ParentName->Buffer,
|
||
ParentName->Length );
|
||
|
||
*(Add2Ptr( NotifyInfo->FileName, ParentName->Length, PWCHAR )) = L'\\';
|
||
BufferOffset = ParentName->Length + sizeof( WCHAR );
|
||
}
|
||
|
||
RtlCopyMemory( Add2Ptr( NotifyInfo->FileName,
|
||
BufferOffset,
|
||
PVOID ),
|
||
TargetName->Buffer,
|
||
TargetName->Length );
|
||
|
||
if (ARGUMENT_PRESENT( StreamName )) {
|
||
|
||
BufferOffset += TargetName->Length;
|
||
|
||
*(Add2Ptr( NotifyInfo->FileName, BufferOffset, PWCHAR )) = L':';
|
||
|
||
RtlCopyMemory( Add2Ptr( NotifyInfo->FileName,
|
||
BufferOffset + sizeof( WCHAR ),
|
||
PVOID ),
|
||
StreamName->Buffer,
|
||
StreamName->Length );
|
||
}
|
||
|
||
//
|
||
// For a non-unicode name, use the conversion routines.
|
||
//
|
||
|
||
} else {
|
||
|
||
ULONG BufferLength;
|
||
|
||
if (ParentName->Length != 0) {
|
||
|
||
RtlOemToUnicodeN( NotifyInfo->FileName,
|
||
NotifyInfo->FileNameLength,
|
||
&BufferLength,
|
||
ParentName->Buffer,
|
||
ParentName->Length );
|
||
|
||
*(Add2Ptr( NotifyInfo->FileName, BufferLength, PWCHAR )) = L'\\';
|
||
|
||
BufferOffset = BufferLength + sizeof( WCHAR );
|
||
}
|
||
|
||
//
|
||
// For view indices, we do not have a parent name.
|
||
//
|
||
|
||
if (ParentName->Length == 0) {
|
||
|
||
ASSERT(ARGUMENT_PRESENT( StreamName ));
|
||
|
||
RtlCopyMemory( Add2Ptr( NotifyInfo->FileName,
|
||
BufferOffset,
|
||
PCHAR ),
|
||
StreamName->Buffer,
|
||
StreamName->Length );
|
||
|
||
} else {
|
||
|
||
RtlOemToUnicodeN( Add2Ptr( NotifyInfo->FileName,
|
||
BufferOffset,
|
||
PWCHAR ),
|
||
NotifyInfo->FileNameLength,
|
||
&BufferLength,
|
||
TargetName->Buffer,
|
||
TargetName->Length );
|
||
|
||
if (ARGUMENT_PRESENT( StreamName )) {
|
||
|
||
BufferOffset += BufferLength;
|
||
|
||
*(Add2Ptr( NotifyInfo->FileName, BufferOffset, PWCHAR )) = L':';
|
||
|
||
RtlOemToUnicodeN( Add2Ptr( NotifyInfo->FileName,
|
||
BufferOffset + sizeof( WCHAR ),
|
||
PWCHAR ),
|
||
NotifyInfo->FileNameLength,
|
||
&BufferLength,
|
||
StreamName->Buffer,
|
||
StreamName->Length );
|
||
}
|
||
}
|
||
}
|
||
|
||
CopiedToBuffer = TRUE;
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
CopiedToBuffer = FALSE;
|
||
}
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyUpdateBuffer: Exit\n", 0 );
|
||
|
||
return CopiedToBuffer;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
FsRtlNotifyCompleteIrpList (
|
||
IN OUT PNOTIFY_CHANGE Notify,
|
||
IN NTSTATUS Status
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine walks through the Irps for a particular notify structure
|
||
and completes the Irps with the indicated status. If the status is
|
||
STATUS_SUCCESS then we are completing an Irp because of a notification event.
|
||
In that case we look at the notify structure to decide if we can return the
|
||
data to the user.
|
||
|
||
Arguments:
|
||
|
||
Notify - This is the notify change structure.
|
||
|
||
Status - Indicates the status used to complete the request. If this status
|
||
is STATUS_SUCCESS then we only want to complete one Irp. Otherwise we
|
||
want complete all the Irps in the list.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP Irp;
|
||
ULONG DataLength;
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlNotifyCompleteIrpList: Entered\n", 0 );
|
||
|
||
DataLength = Notify->DataLength;
|
||
|
||
//
|
||
// Clear the fields to indicate that there is no more data to return.
|
||
//
|
||
|
||
ClearFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
|
||
Notify->DataLength = 0;
|
||
Notify->LastEntry = 0;
|
||
|
||
//
|
||
// Walk through all the Irps in the list. We are never called unless
|
||
// there is at least one irp.
|
||
//
|
||
|
||
do {
|
||
|
||
Irp = CONTAINING_RECORD( Notify->NotifyIrps.Flink, IRP, Tail.Overlay.ListEntry );
|
||
|
||
RemoveHeadList( &Notify->NotifyIrps );
|
||
|
||
//
|
||
// Call our completion routine to complete the request.
|
||
//
|
||
|
||
FsRtlNotifyCompleteIrp( Irp,
|
||
Notify,
|
||
DataLength,
|
||
Status,
|
||
TRUE );
|
||
|
||
//
|
||
// If we were only to complete one Irp then break now.
|
||
//
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
break;
|
||
}
|
||
|
||
} while (!IsListEmpty( &Notify->NotifyIrps ));
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlNotifyCompleteIrpList: Exit\n", 0 );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
FsRtlCancelNotify (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP ThisIrp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is for an Irp which is being cancelled. We Null the cancel
|
||
routine and then walk through the Irps for this notify structure and
|
||
complete all cancelled Irps. It is possible there is pending notify
|
||
stored in the buffer for this Irp. In this case we want to copy the
|
||
data to a system buffer if possible.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Ignored.
|
||
|
||
ThisIrp - This is the Irp to cancel.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
|
||
|
||
PNOTIFY_CHANGE Notify;
|
||
PNOTIFY_SYNC NotifySync;
|
||
LONG ExceptionCode;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
DebugTrace( +1, Dbg, "FsRtlCancelNotify: Entered\n", 0 );
|
||
DebugTrace( 0, Dbg, "Irp -> %08lx\n", Irp );
|
||
|
||
//
|
||
// Capture the notify structure.
|
||
//
|
||
|
||
Notify = (PNOTIFY_CHANGE) ThisIrp->IoStatus.Information;
|
||
|
||
//
|
||
// Void the cancel routine and release the cancel spinlock.
|
||
//
|
||
|
||
IoSetCancelRoutine( ThisIrp, NULL );
|
||
ThisIrp->IoStatus.Information = 0;
|
||
IoReleaseCancelSpinLock( ThisIrp->CancelIrql );
|
||
|
||
FsRtlEnterFileSystem();
|
||
|
||
//
|
||
// Grab the mutex for this structure.
|
||
//
|
||
|
||
NotifySync = Notify->NotifySync;
|
||
AcquireNotifySync( NotifySync );
|
||
|
||
//
|
||
// Use a try finally to faciltate cleanup.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Remove the Irp from the queue.
|
||
//
|
||
|
||
RemoveEntryList( &ThisIrp->Tail.Overlay.ListEntry );
|
||
|
||
IoMarkIrpPending( ThisIrp );
|
||
|
||
//
|
||
// We now have the Irp. Check to see if there is data stored
|
||
// in the buffer for this Irp.
|
||
//
|
||
|
||
if (Notify->Buffer != NULL
|
||
&& Notify->AllocatedBuffer == NULL
|
||
|
||
&& ((ThisIrp->MdlAddress != NULL
|
||
&& MmGetSystemAddressForMdl( ThisIrp->MdlAddress ) == Notify->Buffer)
|
||
|
||
|| (Notify->Buffer == ThisIrp->AssociatedIrp.SystemBuffer))) {
|
||
|
||
PIRP NextIrp;
|
||
PVOID NewBuffer;
|
||
ULONG NewBufferLength;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
//
|
||
// Initialize the above values.
|
||
//
|
||
|
||
NewBuffer = NULL;
|
||
NewBufferLength = 0;
|
||
|
||
//
|
||
// Remember the next Irp on the list. Find the length of any
|
||
// buffer it might have. Also keep a pointer to the buffer
|
||
// if present.
|
||
//
|
||
|
||
if (!IsListEmpty( &Notify->NotifyIrps )) {
|
||
|
||
NextIrp = CONTAINING_RECORD( Notify->NotifyIrps.Flink,
|
||
IRP,
|
||
Tail.Overlay.ListEntry );
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( NextIrp );
|
||
|
||
//
|
||
// If the buffer here is large enough to hold the data we
|
||
// can use that buffer.
|
||
//
|
||
|
||
if (IrpSp->Parameters.NotifyDirectory.Length >= Notify->DataLength) {
|
||
|
||
//
|
||
// If there is a system buffer or Mdl then get a new
|
||
// buffer there.
|
||
//
|
||
|
||
if (NextIrp->AssociatedIrp.SystemBuffer != NULL) {
|
||
|
||
NewBuffer = NextIrp->AssociatedIrp.SystemBuffer;
|
||
|
||
} else if (NextIrp->MdlAddress != NULL) {
|
||
|
||
NewBuffer = MmGetSystemAddressForMdl( NextIrp->MdlAddress );
|
||
}
|
||
|
||
NewBufferLength = IrpSp->Parameters.NotifyDirectory.Length;
|
||
|
||
if (NewBufferLength > Notify->BufferLength) {
|
||
|
||
NewBufferLength = Notify->BufferLength;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Otherwise check if the user's original buffer is larger than
|
||
// the current buffer.
|
||
//
|
||
|
||
} else if (Notify->BufferLength >= Notify->DataLength) {
|
||
|
||
NewBufferLength = Notify->BufferLength;
|
||
}
|
||
|
||
//
|
||
// If we have a new buffer length then we either have a new
|
||
// buffer or need to allocate one. We will do this under
|
||
// the protection of a try-except in order to continue in the
|
||
// event of a failure.
|
||
//
|
||
|
||
if (NewBufferLength != 0) {
|
||
|
||
BOOLEAN ChargedQuota;
|
||
|
||
try {
|
||
|
||
ChargedQuota = FALSE;
|
||
|
||
if (NewBuffer == NULL) {
|
||
|
||
PsChargePoolQuota( Notify->OwningProcess,
|
||
PagedPool,
|
||
NewBufferLength );
|
||
|
||
ChargedQuota = TRUE;
|
||
|
||
//
|
||
// If we didn't get an error then attempt to
|
||
// allocate the pool. If there is an error
|
||
// don't forget to release the quota.
|
||
//
|
||
|
||
NewBuffer = FsRtlpAllocatePool( PagedPool,
|
||
NewBufferLength );
|
||
|
||
Notify->AllocatedBuffer = NewBuffer;
|
||
}
|
||
|
||
//
|
||
// Now copy the data over to the new buffer.
|
||
//
|
||
|
||
RtlCopyMemory( NewBuffer,
|
||
Notify->Buffer,
|
||
Notify->DataLength );
|
||
|
||
//
|
||
// It is possible that the buffer size changed.
|
||
//
|
||
|
||
Notify->ThisBufferLength = NewBufferLength;
|
||
Notify->Buffer = NewBuffer;
|
||
|
||
} except( FsRtlIsNtstatusExpected( ExceptionCode = GetExceptionCode()) ?
|
||
EXCEPTION_EXECUTE_HANDLER :
|
||
EXCEPTION_CONTINUE_SEARCH ) {
|
||
|
||
ASSERT( (ExceptionCode == STATUS_INSUFFICIENT_RESOURCES) ||
|
||
(ExceptionCode == STATUS_QUOTA_EXCEEDED) );
|
||
|
||
//
|
||
// Return quota if we allocated the buffer.
|
||
//
|
||
|
||
if (ChargedQuota) {
|
||
|
||
PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
|
||
NewBufferLength );
|
||
}
|
||
|
||
//
|
||
// Forget any current buffer and resort to immediate
|
||
// notify.
|
||
//
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
|
||
}
|
||
|
||
//
|
||
// Otherwise set the immediate notify flag.
|
||
//
|
||
|
||
} else {
|
||
|
||
SetFlag( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY );
|
||
}
|
||
|
||
//
|
||
// If the immediate notify flag is set then clear the other
|
||
// values in the notify structures.
|
||
//
|
||
|
||
if (FlagOn( Notify->Flags, NOTIFY_IMMEDIATE_NOTIFY )) {
|
||
|
||
//
|
||
// Forget any current buffer and resort to immediate
|
||
// notify.
|
||
//
|
||
|
||
Notify->AllocatedBuffer = Notify->Buffer = NULL;
|
||
|
||
Notify->ThisBufferLength =
|
||
Notify->DataLength = Notify->LastEntry = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Complete the Irp with status cancelled.
|
||
//
|
||
|
||
FsRtlCompleteRequest( ThisIrp, STATUS_CANCELLED );
|
||
|
||
//
|
||
// Decrement the count of Irps that might go through the cancel path.
|
||
//
|
||
|
||
InterlockedDecrement( &Notify->ReferenceCount );
|
||
|
||
if (Notify->ReferenceCount == 0) {
|
||
|
||
if (Notify->AllocatedBuffer != NULL) {
|
||
|
||
PsReturnProcessPagedPoolQuota( Notify->OwningProcess,
|
||
Notify->ThisBufferLength );
|
||
|
||
ExFreePool( Notify->AllocatedBuffer );
|
||
}
|
||
|
||
if (Notify->FullDirectoryName != NULL) {
|
||
|
||
SubjectContext = Notify->SubjectContext;
|
||
}
|
||
|
||
//
|
||
// Cleanup owned one of the refcounts, and must have removed this from
|
||
// the notify list. If not, we are freeing pool with live links.
|
||
//
|
||
|
||
ASSERT( Notify->NotifyList.Flink == NULL );
|
||
|
||
ExFreePool( Notify );
|
||
Notify = NULL;
|
||
}
|
||
|
||
} finally {
|
||
|
||
//
|
||
// No matter how we exit, we release the mutex.
|
||
//
|
||
|
||
ReleaseNotifySync( NotifySync );
|
||
|
||
if (SubjectContext != NULL) {
|
||
|
||
SeReleaseSubjectContext( SubjectContext );
|
||
ExFreePool( SubjectContext );
|
||
}
|
||
|
||
FsRtlExitFileSystem();
|
||
|
||
DebugTrace( -1, Dbg, "FsRtlCancelNotify: Exit\n", 0 );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
VOID
|
||
FsRtlCheckNotifyForDelete (
|
||
IN PLIST_ENTRY NotifyListHead,
|
||
IN PVOID StreamID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a stream is being marked for delete. We will
|
||
walk through the notify structures looking for an Irp for the same stream.
|
||
We will complete these Irps with STATUS_DELETE_PENDING.
|
||
|
||
Arguments:
|
||
|
||
NotifyListHead - This is the start of the notify list.
|
||
|
||
StreamID - This is the Context ID used to identify the stream.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY Link;
|
||
|
||
PNOTIFY_CHANGE ThisNotify;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Walk through all the entries on the list looking for a match.
|
||
//
|
||
|
||
for (Link = NotifyListHead->Flink;
|
||
Link != NotifyListHead;
|
||
Link = Link->Flink) {
|
||
|
||
//
|
||
// Obtain the notify structure from the link.
|
||
//
|
||
|
||
ThisNotify = CONTAINING_RECORD( Link, NOTIFY_CHANGE, NotifyList );
|
||
|
||
//
|
||
// If the context field matches, then complete any waiting Irps.
|
||
//
|
||
|
||
if (ThisNotify->StreamID == StreamID) {
|
||
|
||
//
|
||
// Start by marking the notify structure as file deleted.
|
||
//
|
||
|
||
SetFlag( ThisNotify->Flags, NOTIFY_STREAM_IS_DELETED );
|
||
|
||
//
|
||
// Now complete all of the Irps on this list.
|
||
//
|
||
|
||
if (!IsListEmpty( &ThisNotify->NotifyIrps )) {
|
||
|
||
FsRtlNotifyCompleteIrpList( ThisNotify, STATUS_DELETE_PENDING );
|
||
}
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|