Here is code I am providing to track files objects via the simple method. I
use this code in a production driver. If you find any problems or
enhancements let me know. Also if it doesn’t come out right in the list, I
will find some website to host on. The functions I don’t provide source
code to should be obvious, but if not let me know I will explain what they
do. It should be trivial to convert it to C source code (its in CPP)
fo_ref.h ---------------------------------------------
/*
fo_ref.h - Copyright (c) 2004 Columbia Data Products, Inc. All
Rights Reserved.
Maintains reference counts on file objects
see http://www.osronline.com/article.cfm?id=102 for more info on how
this works
Written by Rob Green/CDP (xxxxx@cdp.com) 3/22/04
Version 1.1 7/14/2004
You may use freely in as long as you give credit in the source code.
Found bugs? send them to me (xxxxx@cdp.com)
*/
/**
* Querys, adds or removes a object on the list
*
* This function uses FsContexts to determine a specific
* file that is open instead of FileObjects which identify
* an instance. IOW You can have 5 opens of the same file
* and you will have 5 file objects and 1 FsContext.
*
* see http://www.osronline.com/article.cfm?id=102 for more info
* on how this works
*/
class Generic_FileObject_RefCount: public DeletableObject
{
protected:
RTL_GENERIC_TABLE Table;
#define FO_REF_ACTION_QUERY 0
#define FO_REF_ACTION_ADD 1
#define FO_REF_ACTION_REMOVE 2
typedef struct sFO_Ref {
ULONG RefCount;
PUNICODE_STRING Name;
PFILE_OBJECT FileObject;
PVOID FsContext;
PVOID Data;
} tFO_Ref;
static RTL_GENERIC_COMPARE_RESULTS CompareElement (
struct _RTL_GENERIC_TABLE *Table,
PVOID FirstStruct,
PVOID SecondStruct
)
{
if(((tFO_Ref*)FirstStruct)->FsContext==((tFO_Ref*)SecondStruct)->FsContext)
return GenericEqual;
if(((tFO_Ref*)FirstStruct)->FsContext<((tFO_Ref*)SecondStruct)->FsContext)
return GenericLessThan;
return GenericGreaterThan;
}
static PVOID AllocElement (
struct _RTL_GENERIC_TABLE *Table,
CLONG ByteSize
)
{
return MemAllocatePoolWithTag(PagedPool,ByteSize,PSM_FO_TAG);
}
static VOID FreeElement (
struct _RTL_GENERIC_TABLE *Table,
PVOID Buffer
)
{
MemFreePool(Buffer);
return;
}
bool DoWork( PFILE_OBJECT FileObject, DWORD Action, PVOID *Data,
PUNICODE_STRING *Uni );
private:
FAST_MUTEX Mutex;
virtual NTSTATUS Init()
{
PAGED_CODE();
ExInitializeFastMutex(&Mutex);
RtlInitializeGenericTable(
&Table,
(PRTL_GENERIC_COMPARE_ROUTINE)CompareElement,
(PRTL_GENERIC_ALLOCATE_ROUTINE)AllocElement,
(PRTL_GENERIC_FREE_ROUTINE)FreeElement,
NULL
);
return STATUS_SUCCESS;
}
virtual VOID GetLock()
{
PAGED_CODE();
ExAcquireFastMutex( &Mutex );
}
virtual VOID ReleaseLock()
{
PAGED_CODE();
ExReleaseFastMutex(&Mutex);
}
virtual void Destroy()
{
// shouldnt get called
ASSERT(FALSE);
}
public:
Generic_FileObject_RefCount()
{
/*lint -e{534} ignore return value*/
Init();
}
virtual ~Generic_FileObject_RefCount()
{
Destroy();
}
/**
* Adds a file object to the list if it doesnt already
* exist, otherwise the reference count is incremented.
*
* This method must be called during the create completion
* handler or in the dispatch handler after the call has
* been sent down synchronously.
*
* @param FileObject FileObject to keep track of.
*
* @return true - File was already in list
* false - File was not in list
*/
bool Add( PFILE_OBJECT FileObject )
{
return DoWork(FileObject,FO_REF_ACTION_ADD,NULL,NULL);
}
bool Add( PFILE_OBJECT FileObject, PVOID Data )
{
return DoWork(FileObject,FO_REF_ACTION_ADD,&Data,NULL);
}
/**
* Returns if the file object is in the list
*
* @param FileObject FileObject to check for
*
* @return true - file is on list
* false - file is not on list
*/
bool Query( PFILE_OBJECT FileObject )
{
return DoWork(FileObject,FO_REF_ACTION_QUERY,NULL,NULL);
}
bool Query( PFILE_OBJECT FileObject, PVOID *Data )
{
return DoWork(FileObject,FO_REF_ACTION_QUERY,Data,NULL);
}
bool Query( PFILE_OBJECT FileObject, PVOID *Data, PUNICODE_STRING *Uni )
{
return DoWork(FileObject,FO_REF_ACTION_QUERY,Data,Uni);
}
/**
* Decrements a reference count on the file object.
* If the reference count is 0 the file object may be
* removed from the list.
*
* @param FileObject File object to remove
*
* @return true - file object was on list
* false - file object was not on list
*/
bool Remove( PFILE_OBJECT FileObject )
{
return DoWork(FileObject,FO_REF_ACTION_REMOVE,NULL,NULL);
}
bool Remove( PFILE_OBJECT FileObject, PVOID *Data )
{
return DoWork(FileObject,FO_REF_ACTION_REMOVE,Data,NULL);
}
};
class Generic_FileObject_RefCountNP: public Generic_FileObject_RefCount
{
private:
KSPIN_LOCK SpinLock;
KIRQL OldIrql;
static PVOID AllocElementNP (
struct _RTL_GENERIC_TABLE *Table,
CLONG ByteSize
)
{
return MemAllocatePoolWithTag(NonPagedPool,ByteSize,PSM_FO_TAG);
}
virtual NTSTATUS Init()
{
KeInitializeSpinLock(&SpinLock);
RtlInitializeGenericTable(
&Table,
(PRTL_GENERIC_COMPARE_ROUTINE)CompareElement,
(PRTL_GENERIC_ALLOCATE_ROUTINE)AllocElementNP,
(PRTL_GENERIC_FREE_ROUTINE)FreeElement,
NULL
);
return STATUS_SUCCESS;
}
virtual VOID GetLock()
{
pmAcquireSpinLock ( &SpinLock, &OldIrql );
}
virtual VOID ReleaseLock()
{
pmReleaseSpinLock(&SpinLock,OldIrql);
}
};
fo_ref.cpp ---------------------------------------------
/*
fo_ref.cpp - Copyright (c) 2004 Columbia Data Products, Inc. All
Rights Reserved.
Maintains reference counts on file objects
see http://www.osronline.com/article.cfm?id=102 for more info on how
this works
Written by Rob Green/CDP (xxxxx@cdp.com) 3/22/04
Version 1.1 7/14/2004
You may use freely in as long as you give credit in the source code.
Found bugs? send them to me (xxxxx@cdp.com)
*/
#include “precomp.h”
#ifdef RUN_WPP
extern “C” {
#include “fo_ref.tmh”
}
#endif
// Note: If a file is cached we may not see the close for it until sometime
later
// this could be immediately or hours or days. It depends on how busy the
system
// is
bool Generic_FileObject_RefCount::DoWork( PFILE_OBJECT FileObject, DWORD
Action, PVOID *Data, PUNICODE_STRING *Uni )
{
if(FileObject->FsContext == NULL) {
return false;
}
GetLock();
tFO_Ref _x;
_x.FsContext = FileObject->FsContext;
tFO_Ref *Found = (tFO_Ref *)RtlLookupElementGenericTable(&Table,&_x);
if ( (Action == FO_REF_ACTION_QUERY) && (Found) ) {
if(Data) {
*Data = Found->Data;
}
if(Uni) {
*Uni = Found->Name;
}
} else
if ( (Action == FO_REF_ACTION_REMOVE) && (Found)) {
// For each IRP_MJ_CLOSE the filter driver decrements the reference
// count on the per-file context structure if the FO_STREAM_FILE bit
// is not set for the file object.
if((!(FileObject->Flags & FO_STREAM_FILE)) && (Found->RefCount)) {
Found->RefCount–;
}
// If the reference count for the per-file context structure reaches
// zero and both the ImageSectionObject and DataSectionObject of the
// SectionObjectPointers field from the FILE_OBJECT is zero, the
// filter driver may then delete the per-file context data.
if ((Found->RefCount==0) &&
((!FileObject->SectionObjectPointer) ||
((!FileObject->SectionObjectPointer->ImageSectionObject) &&
(!FileObject->SectionObjectPointer->DataSectionObject)))) {
if(Found->Name) {
MemFreePool(Found->Name);
Found->Name = NULL;
}
if(Data) {
*Data=Found->Data;
}
RtlDeleteElementGenericTable(&Table,Found);
} else {
// only say found if we actually delete the entry
Found = NULL;
}
}
tFO_Ref *GetFileNameEntry=NULL;
if(Action == FO_REF_ACTION_ADD) {
// For each IRP_MJ_CREATE, the filter driver increments the
reference
// count on the per-file context structure.
if(!Found) {
tFO_Ref x;
BOOLEAN Ret;
x.FsContext = FileObject->FsContext;
x.RefCount=1;
x.Name = NULL;
x.FileObject = FileObject;
if(Data) {
x.Data = *Data;
} else {
x.Data = NULL;
}
Found =
(tFO_Ref*)RtlInsertElementGenericTable(&Table,&x,sizeof(tFO_Ref),&Ret);
// so we will get the name after the lock is released
GetFileNameEntry = Found;
} else {
Found->RefCount++;
}
} // if add
ReleaseLock();
if(GetFileNameEntry) {
GetFileNameEntry->Name =
File_QueryFileName(GetFileNameEntry->FileObject);
if(Uni) {
*Uni = GetFileNameEntry->Name;
}
}
return Found ? true : false;
}
/*
How to declare
Generic_FileObject_RefCount *StreamFiles=NULL;
StreamFiles = new Generic_FileObject_RefCount();
How to add
Note: You must call the add routine in the Create Completion
routine
or after you have already sent the request down in the
dispatch
routine
StreamFiles->Add(IrpSp->FileObject,PrivateData);
How to Query
if(StreamFiles->Query(IrpSp->FileObject,(PVOID*)&PrivateData,&Uni)) {
Debug(DEBUG_DICT,( “File = %wZ\n”,Uni));
}
*/
Thanks,
Rob
-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-181171-
xxxxx@lists.osr.com] On Behalf Of Tadashi Kimura
Sent: Wednesday, July 14, 2004 8:24 PM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] Tracking file
Dear whom it may concern,
I’m a device driver engineer from Japan, Recently, i have been writing
a FS Filter Driver, and I got a big probelm. I would really like somebady
helping me to deal this probelm.
In this driver, I need tracking a file, I refer NT Insider in the OSR
site,
(http://www.osronline.com/article.cfm?id=102) and try to use same method
to do it.
However, the file tracking doesn’t seem to work correctly.
The poblem is that the context cann’t be removed correctly.
Since DataSectionObject is not NULL when closing a certain file, a request
is completed without deleting a context.
Next, CLOSE of a stream file object with the same FCB comes.
At this time, since DataSectionObject is not NULL, a context is not
deleted, either.
CLOSE of a file with the same FCB is not notified to a driver after this.
Instead, the CREATE request whose FS returns the same FCB is notified.
But, the file name passed by this CREATE differs from upper CLOSE.
I am troubled very much by this problem.
Has somebaby bumped into the similar problem?
And, I am performing the tracking of a file,
in order to acquire a file name from the stream file object at the time
of delay writing.
If I could find the way to acquire a file from a stream file object, This
problem will be solved.
Does somebady know the method of acquiring a file name without the file
tracking?
If you have any useful information, Please e-mail me, I really appreciate
for you help.
Tadashi, Kimura
Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17
You are currently subscribed to ntfsd as: xxxxx@cdp.com
To unsubscribe send a blank email to xxxxx@lists.osr.com