Directory Change Notification is the functionality provided by file system, which allows a file system user application to keep a watch on a directory residing on that file system. With this functionality, the file system user application will be notified whenever there is a change in the directory, which is being watched for. Applications can put a watch on a directory for certain changes such as File addition, deletion, or attributes changes like last modify time changes etc, and gets notified by file system, whenever any of these change occur.
Although this functionality is not mandatory for a file system to provide, yet there are certain applications which rely on Directory Change Notification support in a file system either completely or partially. Few of them are ASP.net, Internet Information Server (IIS), and few components of Microsoft Exchange (SMTP Service), Apache Web Server etc. Windows explorer also relies on this functionality to refresh its view whenever there is a change in directory, without this functionality, when changes to files occur by other processes in a directory, the changes will not be automatically reflected until a manual refresh is done or the file folder is reopened.
Whenever any application registers itself for directory change notification, file system receives IRP_MJ_DIRECTORY_CONTROL request with MinorFunction = IRP_MN_NOTIFY_CHANGE_DIRECTORY.
There are also few file system specific helper routines provided with Windows, which can be used to implement this functionality in a file system, few of them are: FsRtlNotifyFullChangeDirectory, FsRtlNotifyFullReportChange, FsRtlNotifyCleanup
This article explains how file system driver (FSD) can provide implementation for directory change notification feature and usage of this feature by user mode application.
Before proceeding with directory notification implementation for file systems, we will take a look at important data structures: FSD needs to maintain 2 important members per File System Volume:
This list is initialized to NULL and NotifySync object is initialized by calling FsRtlNotifyInitializeSync() function.
IrpSp->Parameters.NotifyDirectory.Length: Length in bytes of the buffer pointed to by Irp->UserBuffer.
IrpSp->Flags: If IrpSp->Flags is set to SL_WATCH_TREE, it means that all subdirectories of this directory also need to be watched. This value is later on utilized while calling FsRtlNotifyFullChangeDirectory()
Pseudo code for IRP_MN_NOTIFY_CHANGE_DIRECTORY handler looks as follows:< /P>
BOOLEAN queuedSuccessfully = FALSE;
completionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter;
watchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE );
// We will be handling this request only for directories
if (NotDirectory(IrpSp->FileObject))
{
// Complete request with STATUS_INVALID_PARAMETER
...
}
// Do all necessary validation
// Call FsRtlNotifyFullChangeDirectory
try
{
FsRtlNotifyFullChangeDirectory(
VolumeContext->NotifySync,
&VolumeContext->DirNotifyList,
IrpSp->FileObject->FsContext2,
(PSTRING)&FileObjectContext->FullFileName,
WatchTree,
FALSE,
CompletionFilter,
Irp,
NULL,
NULL );
Status = STATUS_PENDING;
queuedSuccessfully = TRUE;
} finally {
// If something failed in between then complete the
// request with unsuccessful status
if (!queuedSuccessfully) {
...
}
}
return Status;
In addition to this, file system driver needs to call FsRtlNotifyFullChangeDirectory with NotifyIRP = NULL, while handling close request for a directory, where a directory is created with FILE_DELETE_ON_CLOSE create option. Whenever NotifyIRP is supplied as NULL, FsRtlNotifyFullChangeDirectory checks for all pending notify IRPs related to this file object and completes them with STATUS_DELETE_PENDING.
When changes occurs to any directory, file system should invoke FsRtlNotifyFullReportChange() function to inform the library about the changes. The library routine is then responsible for scanning all pending notify requests and processing them pertaining to particular event.
For e.g.: Let's consider a scenario where new subdirectory is added to a directory. Then after successful completion of subdirectory, file system needs to call FsRtlNotifyFullReportChange as follows:
FsRtlNotifyFullReportChange(
VolumeContext->NotifySync,
& VolumeContext->NotifyIrpList,
(PSTRING)&FileObjectContext->FullNameDCN,
(USHORT)( FileObjectContext->FullNameDCN.Length � NameForDCN.Length),
(PSTRING)NULL,
(PSTRING)NULL,
(ULONG)FILE_NOTIFY_CHANGE_DIR_NAME,
(ULONG)FILE_ACTION_ADDED,
(PVOID)NULL );
[Please refer table from section "Different scenarios where FsRtl� is called" for possible values of FilterMatch and Action parameter and appropriate scenarios where they are used]
Whenever Cleanup is performed on a particular file object (indicating all user handles for given file objects are closed), File system should notify the FSRTL using FsRtlNotifyCleanup()function. FSRTL library then dequeues and completes all Notify IRPs related to this file object.
Below is example of FsRtlNotifyCleanup usage inside IRP_MJ_CLEANUP handler:
if(!NotDirectory(IrpSp->FileObject))
{
FsRtlNotifyCleanup(VolumeContext->NotifySync,
&VolumeContext->NotifyIrpList,
IrpSp->FileObject->FsContext2);
}
Below is the table which summarizes different directory change scenarios where FSD need to call FsRtlFullReportChange and related value for "FilterMatch" and "Action" parameters:
|
No. |
Scenario |
File System IRP(s) |
FilterMatch |
Action |
|
1 |
A subdirectory is deleted |
IRP_MJ_CLEANUP |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_ACTION_REMOVED |
|
2 |
File is deleted |
IRP_MJ_CLEANUP |
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_ACTION_REMOVED |
|
3 |
New subdirectory is created |
IRP_MJ_CREATE |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_ACTION_ADDED |
|
4 |
New File is created |
IRP_MJ_CREATE |
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_ACTION_ADDED |
|
5 |
File / Directory attributes are modified |
IRP_MJ_SET_INFORMATION, |
FILE_NOTIFY_CHANGE_ATTRIBUTES FILE_NOTIFY_CHANGE_SIZE FILE_NOTIFY_CHANGE_LAST_WRITE FILE_NOTIFY_CHANGE_LAST_ACCESS FILE_NOTIFY_CHANGE_CREATION |
FILE_ACTION_MODIFIED |
|
6 |
File size changed during write operation |
IRP_MJ_WRITE |
FILE_NOTIFY_CHANGE_SIZE |
FILE_ACTION_MODIFIED |
|
7 |
End-Of-File for a file is modified |
IRP_MJ_SET_EA |
FILE_NOTIFY_CHANGE_EA |
FILE_ACTION_MODIFIED |
|
8 |
Security attributes for a file are modified |
IRP_MJ_SET_SECURITY |
FILE_NOTIFY_CHANGE_SECURITY |
FILE_ACTION_MODIFIED |
Rename operation needs special handling. In this case there are 2 notifications events needs to set:
First event when source directory is deleted for object being renamed:
If rename is performed within a same directory and target of rename operation does not exists then FSD should report FILE_ACTION_RENAMED_OLD_FILE else the FSD should report FILE_ACTION_REMOVED.
Second event related to target object of rename operation:
There can 3 possible scenarios:
An application can register for directory change notification by calling FindFirstChangeNotification() function.
- FindFirstChangeNotification() function takes "dwNotifyFilter" as an argument so that application can wait for particular directory changes. Also, "bWatchSubtree" argument decides whether or not to monitor the directory sub tree.
- After calling "FindFirstChangeNotification", application waits for change notification, this is done by calling WaitForMultipleObjects or WaitForSingleObject function.
- Once first wait is satisfied, FindNextChangeNotification can be used to provide a notification handle to wait on subsequent changes.
- The FindCloseChangeNotification function closes the notification handle.
- Similar to FindFirstChangeNotification, wait functions are called after the FindNextChangeNotification function to wait for directory change event.
Below is sample source code which waits for directory notification for "C:\" -
#include <windows.h>
#include <stdio.h>
int main()
{
DWORD dwWaitStatus;
HANDLE dwChangeHandles[1];
dwChangeHandles[0] = FindFirstChangeNotification(
"C:", // directory to watch
FALSE, // do not watch the subtree
FILE_NOTIFY_CHANGE_DIR_NAME);
// watch Dir name changes
if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)
exit(GetLastError());
printf("Watching C:\ for directory changes\n");
// Change notification is set. Now wait on both notifications
// handles and refresh accordingly.
while (TRUE) {
dwWaitStatus = WaitForMultipleObjects(1, dwChangeHandles,
FALSE, INFINITE);
printf(".")
switch (dwWaitStatus) {
case WAIT_OBJECT_0:
if (FindNextChangeNotification( dwChangeHandles[0])
== FALSE )
exit(GetLastError());
default:
printf( "Notify message failure\n");
exit(0);
break;
}
}
return 0;
}
Providing support for Directory Change Notification in a distributed file systems becomes a little bit more challenging because of the global name space nature of the file system, which gets accessed by multiple nodes, so if a directory has been modified by one node, then it should notify all other nodes, about the change and nature of change.
So it is required to have following functionality support in a distributed file system protocol to provide Directory Change Notification functionality:
Notify all nodes regarding change, this notification message will contain Notification structure describing exact change
- As soon as a node receives such change message, it calls FsRtlNotifyFullReportChange by extracting appropriate parameters from notification structure sent with message.
Even after placing the notification mechanism in place, it some times become almost impossible to accurately implement Directory Change Notification functionality in Distributed file systems, because of the inherent network delay of notification message exchange.
So one has to make a choice between their needs and based on that makes the decision up to what extent, one wants to provide Directory Change Notification support in Distributed File System.
Installable File System Design Guide
http://msdn2.microsoft.com/en-us/library/ms793583.aspx
Windows NT File System Internals � Design Guide
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||