Context Tracking and FO_STREAM_FILE

Greetings,
Awhile back there was a thread that described a technique for using
FileObjects created with IoCreateStreamFileObjectXxx and rolling create
irps. I didn’t pay much attention then but it seems that some unnamed
antivirus product is using this technique (or something very similar)
and it does not align well with the context tracking techniques
described in the OSR articles. Specifically, the technique described (I
included the code below) creates a FileObject with
IoCreateStreamFileObjectXxx, clears the FO_STREAM_FILE flag, rolls and
sends a create IRP, does whatever with the FileObject, rolls and sends a
cleanup, (this is the important part) sets the FO_STREAM_FILE flag and
dereferences the FileObject. The net effect in the context tracking is
that the context reference is incremented when the create irp is
processed (sans FO_STREAM_FILE) but is not decremented at close time
since the FO_STREAM_FILE flag is now set. The context reference never
goes to 0 and, in fact, is now ‘stale’.

I’ve included the code from the previous thread. The original post has
the line that cleared the FO_STREAM_FILE flag commented out but text
posted along with the code indicated that it should be included. Also,
just to be sure to give credit where credit is due, this was posted by
Ted Hess.

Any suggestions on how to distinguish a real FileObject from a stream
FileObject masquerading as a real FileObject? As near as I can tell,
unless that distinction can be made, the only way around this is to
always and only maintain a FileObject table for context tracking -
reference counting cannot be accurate. I guess another option would be
to decrement the reference count at close time regardless of the
FO_STREAM_FILE flag and re-create the context next time the FsContext is
detected. Neither method is nearly as elegant as the original described
in the OSR articles. If I’ve missed something obvious feel free to flog
as necessary.

Sorry for the long post but I wanted to include the original code.

John Moore

NTSTATUS
xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
NTSTATUS Status, RetStatus = STATUS_SUCCESS;
UNICODE_STRING fileName;
PUNICODE_STRING pFOName;
PFILE_OBJECT fileObject, cloneFileObject;

PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);

ACCESS_MASK desiredAccess =
irpSp->Parameters.Create.SecurityContext->DesiredAccess;
ACCESS_MASK desiredAccessCreate = desiredAccess;

ULONG createOptions = irpSp->Parameters.Create.Options &
FILE_VALID_OPTION_FLAGS;
ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24) &
0xFF;

// The subject of this excercise (save it)
fileObject = irpSp->FileObject;

// Make a copy of the callers buffer here 'cause we don’t know who’ll
free it
fileName.Length = fileObject->FileName.Length;
fileName.MaximumLength = fileObject->FileName.MaximumLength;
fileName.Buffer = ExAllocatePool(PagedPool, fileName.MaximumLength);

// Bag it if no memory available
if (!fileName.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Copy original contents into temp buffer
RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
fileName.MaximumLength);

// Allocate a new, temporary FO
#if defined(NT_40)
// Watch out for the gratuitous CLEANUP IRP!
cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL);
#endif

// (Re-)init newly created FO
// cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |
FO_CLEANUP_COMPLETE);
cloneFileObject->Flags = fileObject->Flags;
cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;

// Give it a name
pFOName = &cloneFileObject->FileName;
*pFOName = fileName;

// Initialize embedded event structures
KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent,
FALSE);
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent, FALSE);

// Make a copy of the create irp (preserve flags)
RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
nxtSp->Control = 0;
// Use temporary FO and underlying DO
nxtSp->FileObject = cloneFileObject;
nxtSp->DeviceObject = DeviceObject;

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Ensure that the create disposition requested
// indicates all of the appropriate “rights” necessary
// for the given operation.
//
if (createDisposition == FILE_SUPERSEDE) {
desiredAccessCreate |= DELETE;
}

if ((createDisposition == FILE_OVERWRITE) ||
(createDisposition == FILE_OVERWRITE_IF)) {
desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
FILE_WRITE_ATTRIBUTES);
}

// Setup our desired access
nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccessCreate & ~SYNCHRONIZE;
nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
// Specify open only (no create/supersede)
nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
(createOptions & (FILE_OPEN_NO_RECALL |
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
FILE_NO_INTERMEDIATE_BUFFERING;

//
// Chuck the request to the underlying driver
//
Status = IoCallDriver(DeviceObject, Irp);

if (Status == STATUS_PENDING) {
// Wait for event if pended
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
Status = Irp->IoStatus.Status;
}

// Now check for file open failures
if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE)) {
// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// Toss temp FO
ObDereferenceObject(cloneFileObject);

// Nothing has happened yet
Irp->PendingReturned = FALSE;

// OK to proceed if cannot open file
return STATUS_SUCCESS;
}

//
// *** OK - we now have an opened FileObject with a valid FsContext
pointer
//

// RetStatus = <<< *** Insert your desired actions here *** >>>

//
// *** Done with FileObject
//

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Need to light “handle created” flag
cloneFileObject->Flags |= FO_HANDLE_CREATED;

// Send a CLEANUP down to the FSD
nxtSp->MajorFunction = IRP_MJ_CLEANUP;
nxtSp->MinorFunction = 0;
nxtSp->Flags = 0;
nxtSp->Control = 0;
nxtSp->DeviceObject = DeviceObject;
nxtSp->FileObject = cloneFileObject;

// Init our completion event
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent, FALSE);

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Call the FSD to release any share access, cache refs, etc.
//
Status = IoCallDriver(DeviceObject, Irp);

//
// Wait for the cleanup to finish
//
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// We allocated the FO, now we must free it
ObDereferenceObject(cloneFileObject);

// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Nothing has happened yet
Irp->PendingReturned = FALSE;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

// return STATUS_SUCCESS if it is OK to go ahead and let the caller
open the file
return RetStatus;
}

With regards to my last post, the code snipped actually re-uses irps
rather than rolling new ones. Sorry for the confusion (and wasting
bandwidth).

John Moore

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-214547-
xxxxx@lists.osr.com] On Behalf Of John Moore
Sent: Wednesday, July 20, 2005 9:56 AM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] Context Tracking and FO_STREAM_FILE

Greetings,
Awhile back there was a thread that described a technique for using
FileObjects created with IoCreateStreamFileObjectXxx and rolling
create
irps. I didn’t pay much attention then but it seems that some unnamed
antivirus product is using this technique (or something very similar)
and it does not align well with the context tracking techniques
described in the OSR articles. Specifically, the technique described
(I
included the code below) creates a FileObject with
IoCreateStreamFileObjectXxx, clears the FO_STREAM_FILE flag, rolls and
sends a create IRP, does whatever with the FileObject, rolls and sends
a
cleanup, (this is the important part) sets the FO_STREAM_FILE flag and
dereferences the FileObject. The net effect in the context tracking
is
that the context reference is incremented when the create irp is
processed (sans FO_STREAM_FILE) but is not decremented at close time
since the FO_STREAM_FILE flag is now set. The context reference never
goes to 0 and, in fact, is now ‘stale’.

I’ve included the code from the previous thread. The original post
has
the line that cleared the FO_STREAM_FILE flag commented out but text
posted along with the code indicated that it should be included.
Also,
just to be sure to give credit where credit is due, this was posted by
Ted Hess.

Any suggestions on how to distinguish a real FileObject from a stream
FileObject masquerading as a real FileObject? As near as I can tell,
unless that distinction can be made, the only way around this is to
always and only maintain a FileObject table for context tracking -
reference counting cannot be accurate. I guess another option would
be
to decrement the reference count at close time regardless of the
FO_STREAM_FILE flag and re-create the context next time the FsContext
is
detected. Neither method is nearly as elegant as the original
described
in the OSR articles. If I’ve missed something obvious feel free to
flog
as necessary.

Sorry for the long post but I wanted to include the original code.

John Moore

NTSTATUS
xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
NTSTATUS Status, RetStatus = STATUS_SUCCESS;
UNICODE_STRING fileName;
PUNICODE_STRING pFOName;
PFILE_OBJECT fileObject, cloneFileObject;

PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);

ACCESS_MASK desiredAccess =
irpSp->Parameters.Create.SecurityContext->DesiredAccess;
ACCESS_MASK desiredAccessCreate = desiredAccess;

ULONG createOptions = irpSp->Parameters.Create.Options &
FILE_VALID_OPTION_FLAGS;
ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24)
&
0xFF;

// The subject of this excercise (save it)
fileObject = irpSp->FileObject;

// Make a copy of the callers buffer here 'cause we don’t know
who’ll
free it
fileName.Length = fileObject->FileName.Length;
fileName.MaximumLength = fileObject->FileName.MaximumLength;
fileName.Buffer = ExAllocatePool(PagedPool,
fileName.MaximumLength);

// Bag it if no memory available
if (!fileName.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Copy original contents into temp buffer
RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
fileName.MaximumLength);

// Allocate a new, temporary FO
#if defined(NT_40)
// Watch out for the gratuitous CLEANUP IRP!
cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL);
#endif

// (Re-)init newly created FO
// cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |
FO_CLEANUP_COMPLETE);
cloneFileObject->Flags = fileObject->Flags;
cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;

// Give it a name
pFOName = &cloneFileObject->FileName;
*pFOName = fileName;

// Initialize embedded event structures
KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent,
FALSE);
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);

// Make a copy of the create irp (preserve flags)
RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
nxtSp->Control = 0;
// Use temporary FO and underlying DO
nxtSp->FileObject = cloneFileObject;
nxtSp->DeviceObject = DeviceObject;

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Ensure that the create disposition requested
// indicates all of the appropriate “rights” necessary
// for the given operation.
//
if (createDisposition == FILE_SUPERSEDE) {
desiredAccessCreate |= DELETE;
}

if ((createDisposition == FILE_OVERWRITE) ||
(createDisposition == FILE_OVERWRITE_IF)) {
desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
FILE_WRITE_ATTRIBUTES);
}

// Setup our desired access
nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccessCreate & ~SYNCHRONIZE;
nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
// Specify open only (no create/supersede)
nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
(createOptions & (FILE_OPEN_NO_RECALL |
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
FILE_NO_INTERMEDIATE_BUFFERING;

//
// Chuck the request to the underlying driver
//
Status = IoCallDriver(DeviceObject, Irp);

if (Status == STATUS_PENDING) {
// Wait for event if pended
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
Status = Irp->IoStatus.Status;
}

// Now check for file open failures
if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE))
{
// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// Toss temp FO
ObDereferenceObject(cloneFileObject);

// Nothing has happened yet
Irp->PendingReturned = FALSE;

// OK to proceed if cannot open file
return STATUS_SUCCESS;
}

//
// *** OK - we now have an opened FileObject with a valid FsContext
pointer
//

// RetStatus = <<< *** Insert your desired actions here *** >>>

//
// *** Done with FileObject
//

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Need to light “handle created” flag
cloneFileObject->Flags |= FO_HANDLE_CREATED;

// Send a CLEANUP down to the FSD
nxtSp->MajorFunction = IRP_MJ_CLEANUP;
nxtSp->MinorFunction = 0;
nxtSp->Flags = 0;
nxtSp->Control = 0;
nxtSp->DeviceObject = DeviceObject;
nxtSp->FileObject = cloneFileObject;

// Init our completion event
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Call the FSD to release any share access, cache refs, etc.
//
Status = IoCallDriver(DeviceObject, Irp);

//
// Wait for the cleanup to finish
//
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// We allocated the FO, now we must free it
ObDereferenceObject(cloneFileObject);

// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Nothing has happened yet
Irp->PendingReturned = FALSE;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

// return STATUS_SUCCESS if it is OK to go ahead and let the caller
open the file
return RetStatus;
}


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: unknown lmsubst tag
argument: ‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com

A couple of comments on my orignal posting… Defending the indefensible :wink:

  1. There is a bug in the post-create freeing of the filename buffer. This
    code should be moved to after the CLEANUP has completed. Why? 'cause either
    NTFS or the CM may keep a copy of a pointer to this Unicode buffer. Freeing
    it prematurely caused some interesting BugChecks in the Verifier in NTFS.

  2. The line of code commented out (clearing FO_STREAM_FILE) was left as
    documentation. The line below it was copying the original flags from the
    FileObject which will not have any of these flags set (Optimization hack).

  3. Reseting the FO_STREAM_FILE flag assured the FileObject was handled
    correctly by the IO manager and drivers above you when calling
    ObDereferenceObject which in turn will eventually send the CLOSE IRP down
    the stack from the top.

  4. I’ve always maintained that if you want to correctly reference track
    FsContexts, you have to account for Stream FileObjects in all cases. Also,
    NTFS is also known to cache (not teardown) SCBs (FsContext pointers) even
    after the CLOSE has been processed. In this case, you can indeed see a
    re-open of a file (same FsContext) even though you haven’t seen your own
    fiter’s CLOSE completion for it yet. In these cases, you need have some sort
    of “re-open” logic in your post-CREATE handler for “known but closed”
    FsContexts. This is trick stuff if you are trying to be transparent whilst
    didling files.

  5. This essence of this code has existed for a couple of years now in a
    filter that runs on NT4.0 thru WS03. AFAIK, we don’t leak or crash or hang
    due to this bit of implementation chicanery.

OK, to answer your question directly - I think you need to reference count
FsContexts for StreamFileObjects when you see them and decrement the
reference on ALL CLOSEs regardless of FO type if the context is known to
you.

HTH, /ted

-----Original Message-----
From: John Moore [mailto:xxxxx@timespring.com]
Sent: Wednesday, July 20, 2005 11:56 AM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] Context Tracking and FO_STREAM_FILE

Greetings,
Awhile back there was a thread that described a technique for using
FileObjects created with IoCreateStreamFileObjectXxx and rolling create
irps. I didn’t pay much attention then but it seems that some unnamed
antivirus product is using this technique (or something very similar) and it
does not align well with the context tracking techniques described in the
OSR articles. Specifically, the technique described (I included the code
below) creates a FileObject with IoCreateStreamFileObjectXxx, clears the
FO_STREAM_FILE flag, rolls and sends a create IRP, does whatever with the
FileObject, rolls and sends a cleanup, (this is the important part) sets the
FO_STREAM_FILE flag and dereferences the FileObject. The net effect in the
context tracking is that the context reference is incremented when the
create irp is processed (sans FO_STREAM_FILE) but is not decremented at
close time since the FO_STREAM_FILE flag is now set. The context reference
never goes to 0 and, in fact, is now ‘stale’.

I’ve included the code from the previous thread. The original post has the
line that cleared the FO_STREAM_FILE flag commented out but text posted
along with the code indicated that it should be included. Also, just to be
sure to give credit where credit is due, this was posted by Ted Hess.

Any suggestions on how to distinguish a real FileObject from a stream
FileObject masquerading as a real FileObject? As near as I can tell, unless
that distinction can be made, the only way around this is to always and only
maintain a FileObject table for context tracking - reference counting cannot
be accurate. I guess another option would be to decrement the reference
count at close time regardless of the FO_STREAM_FILE flag and re-create the
context next time the FsContext is detected. Neither method is nearly as
elegant as the original described in the OSR articles. If I’ve missed
something obvious feel free to flog as necessary.

Sorry for the long post but I wanted to include the original code.

John Moore

NTSTATUS
xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
NTSTATUS Status, RetStatus = STATUS_SUCCESS;
UNICODE_STRING fileName;
PUNICODE_STRING pFOName;
PFILE_OBJECT fileObject, cloneFileObject;

PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);

ACCESS_MASK desiredAccess =
irpSp->Parameters.Create.SecurityContext->DesiredAccess;
ACCESS_MASK desiredAccessCreate = desiredAccess;

ULONG createOptions = irpSp->Parameters.Create.Options &
FILE_VALID_OPTION_FLAGS;
ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24) &
0xFF;

// The subject of this excercise (save it)
fileObject = irpSp->FileObject;

// Make a copy of the callers buffer here 'cause we don’t know who’ll
free it
fileName.Length = fileObject->FileName.Length;
fileName.MaximumLength = fileObject->FileName.MaximumLength;
fileName.Buffer = ExAllocatePool(PagedPool, fileName.MaximumLength);

// Bag it if no memory available
if (!fileName.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Copy original contents into temp buffer
RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
fileName.MaximumLength);

// Allocate a new, temporary FO
#if defined(NT_40)
// Watch out for the gratuitous CLEANUP IRP!
cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL); #endif

// (Re-)init newly created FO
// cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |
FO_CLEANUP_COMPLETE);
cloneFileObject->Flags = fileObject->Flags;
cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;

// Give it a name
pFOName = &cloneFileObject->FileName;
*pFOName = fileName;

// Initialize embedded event structures
KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent, FALSE);
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent, FALSE);

// Make a copy of the create irp (preserve flags)
RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
nxtSp->Control = 0;
// Use temporary FO and underlying DO
nxtSp->FileObject = cloneFileObject;
nxtSp->DeviceObject = DeviceObject;

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Ensure that the create disposition requested
// indicates all of the appropriate “rights” necessary
// for the given operation.
//
if (createDisposition == FILE_SUPERSEDE) {
desiredAccessCreate |= DELETE;
}

if ((createDisposition == FILE_OVERWRITE) ||
(createDisposition == FILE_OVERWRITE_IF)) {
desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
FILE_WRITE_ATTRIBUTES);
}

// Setup our desired access
nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccessCreate & ~SYNCHRONIZE;
nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
// Specify open only (no create/supersede)
nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
(createOptions & (FILE_OPEN_NO_RECALL |
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
FILE_NO_INTERMEDIATE_BUFFERING;

//
// Chuck the request to the underlying driver
//
Status = IoCallDriver(DeviceObject, Irp);

if (Status == STATUS_PENDING) {
// Wait for event if pended
KeWaitForSingleObject(&cloneFileObject->Event, Executive, KernelMode,
FALSE, 0);
Status = Irp->IoStatus.Status;
}

// Now check for file open failures
if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE)) {
// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// Toss temp FO
ObDereferenceObject(cloneFileObject);

// Nothing has happened yet
Irp->PendingReturned = FALSE;

// OK to proceed if cannot open file
return STATUS_SUCCESS;
}

//
// *** OK - we now have an opened FileObject with a valid FsContext
pointer
//

// RetStatus = <<< *** Insert your desired actions here *** >>>

//
// *** Done with FileObject
//

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Need to light “handle created” flag
cloneFileObject->Flags |= FO_HANDLE_CREATED;

// Send a CLEANUP down to the FSD
nxtSp->MajorFunction = IRP_MJ_CLEANUP;
nxtSp->MinorFunction = 0;
nxtSp->Flags = 0;
nxtSp->Control = 0;
nxtSp->DeviceObject = DeviceObject;
nxtSp->FileObject = cloneFileObject;

// Init our completion event
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent, FALSE);

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Call the FSD to release any share access, cache refs, etc.
//
Status = IoCallDriver(DeviceObject, Irp);

//
// Wait for the cleanup to finish
//
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&cloneFileObject->Event, Executive, KernelMode,
FALSE, 0);
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// We allocated the FO, now we must free it
ObDereferenceObject(cloneFileObject);

// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess = desiredAccess;

// Nothing has happened yet
Irp->PendingReturned = FALSE;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

// return STATUS_SUCCESS if it is OK to go ahead and let the caller open
the file
return RetStatus;
}


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: unknown lmsubst tag argument: ‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com

Hi Ted,

Thanks for the quick response, I know the original post was from a
couple of years ago.

First, regarding point 4 below. I agree that a filter should be able to
handle stream file objects in all cases. The problem I had is that it
had been my experience (and according to the articles, OSR’s too) that a
filter can safely assume that it won’t see ‘chameleon’ file objects that
change types midstream.

I think I would modify your conclusion a little. In this environment,
reference counting in per stream contexts is effectively useless. There
is no way to handle the various cases including the case where the only
irp you see for a FileObject is the close irp. You must maintain a per
stream context table of FileObjects so you know when the last FileObject
that you have seen, regareless of when you have seen it or its type, has
been closed. Of course, you could still maintain a reference count but
it would be redundant.

Thankfully, the Filter Manager or even the raw FsRtlXxxPerStreamContext
functionality should pretty much alleviate this problem.

John Moore

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-214583-
xxxxx@lists.osr.com] On Behalf Of Ted Hess
Sent: Wednesday, July 20, 2005 1:03 PM
To: Windows File Systems Devs Interest List
Subject: RE: [ntfsd] Context Tracking and FO_STREAM_FILE

A couple of comments on my orignal posting… Defending the
indefensible
:wink:

  1. There is a bug in the post-create freeing of the filename buffer.
    This
    code should be moved to after the CLEANUP has completed. Why? 'cause
    either
    NTFS or the CM may keep a copy of a pointer to this Unicode buffer.
    Freeing
    it prematurely caused some interesting BugChecks in the Verifier in
    NTFS.

  2. The line of code commented out (clearing FO_STREAM_FILE) was left
    as
    documentation. The line below it was copying the original flags from
    the
    FileObject which will not have any of these flags set (Optimization
    hack).

  3. Reseting the FO_STREAM_FILE flag assured the FileObject was handled
    correctly by the IO manager and drivers above you when calling
    ObDereferenceObject which in turn will eventually send the CLOSE IRP
    down
    the stack from the top.

  4. I’ve always maintained that if you want to correctly reference
    track
    FsContexts, you have to account for Stream FileObjects in all cases.
    Also,
    NTFS is also known to cache (not teardown) SCBs (FsContext pointers)
    even
    after the CLOSE has been processed. In this case, you can indeed see a
    re-open of a file (same FsContext) even though you haven’t seen your
    own
    fiter’s CLOSE completion for it yet. In these cases, you need have
    some
    sort
    of “re-open” logic in your post-CREATE handler for “known but closed”
    FsContexts. This is trick stuff if you are trying to be transparent
    whilst
    didling files.

  5. This essence of this code has existed for a couple of years now in
    a
    filter that runs on NT4.0 thru WS03. AFAIK, we don’t leak or crash or
    hang
    due to this bit of implementation chicanery.

OK, to answer your question directly - I think you need to reference
count
FsContexts for StreamFileObjects when you see them and decrement the
reference on ALL CLOSEs regardless of FO type if the context is known
to
you.

HTH, /ted

-----Original Message-----
From: John Moore [mailto:xxxxx@timespring.com]
Sent: Wednesday, July 20, 2005 11:56 AM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] Context Tracking and FO_STREAM_FILE

Greetings,
Awhile back there was a thread that described a technique for using
FileObjects created with IoCreateStreamFileObjectXxx and rolling
create
irps. I didn’t pay much attention then but it seems that some unnamed
antivirus product is using this technique (or something very similar)
and
it
does not align well with the context tracking techniques described in
the
OSR articles. Specifically, the technique described (I included the
code
below) creates a FileObject with IoCreateStreamFileObjectXxx, clears
the
FO_STREAM_FILE flag, rolls and sends a create IRP, does whatever with
the
FileObject, rolls and sends a cleanup, (this is the important part)
sets
the
FO_STREAM_FILE flag and dereferences the FileObject. The net effect
in
the
context tracking is that the context reference is incremented when the
create irp is processed (sans FO_STREAM_FILE) but is not decremented
at
close time since the FO_STREAM_FILE flag is now set. The context
reference
never goes to 0 and, in fact, is now ‘stale’.

I’ve included the code from the previous thread. The original post
has
the
line that cleared the FO_STREAM_FILE flag commented out but text
posted
along with the code indicated that it should be included. Also, just
to
be
sure to give credit where credit is due, this was posted by Ted Hess.

Any suggestions on how to distinguish a real FileObject from a stream
FileObject masquerading as a real FileObject? As near as I can tell,
unless
that distinction can be made, the only way around this is to always
and
only
maintain a FileObject table for context tracking - reference counting
cannot
be accurate. I guess another option would be to decrement the
reference
count at close time regardless of the FO_STREAM_FILE flag and
re-create
the
context next time the FsContext is detected. Neither method is nearly
as
elegant as the original described in the OSR articles. If I’ve missed
something obvious feel free to flog as necessary.

Sorry for the long post but I wanted to include the original code.

John Moore

NTSTATUS
xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
NTSTATUS Status, RetStatus = STATUS_SUCCESS;
UNICODE_STRING fileName;
PUNICODE_STRING pFOName;
PFILE_OBJECT fileObject, cloneFileObject;

PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);

ACCESS_MASK desiredAccess =
irpSp->Parameters.Create.SecurityContext->DesiredAccess;
ACCESS_MASK desiredAccessCreate = desiredAccess;

ULONG createOptions = irpSp->Parameters.Create.Options &
FILE_VALID_OPTION_FLAGS;
ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24)
&
0xFF;

// The subject of this excercise (save it)
fileObject = irpSp->FileObject;

// Make a copy of the callers buffer here 'cause we don’t know
who’ll
free it
fileName.Length = fileObject->FileName.Length;
fileName.MaximumLength = fileObject->FileName.MaximumLength;
fileName.Buffer = ExAllocatePool(PagedPool,
fileName.MaximumLength);

// Bag it if no memory available
if (!fileName.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Copy original contents into temp buffer
RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
fileName.MaximumLength);

// Allocate a new, temporary FO
#if defined(NT_40)
// Watch out for the gratuitous CLEANUP IRP!
cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL);
#endif

// (Re-)init newly created FO
// cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |
FO_CLEANUP_COMPLETE);
cloneFileObject->Flags = fileObject->Flags;
cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;

// Give it a name
pFOName = &cloneFileObject->FileName;
*pFOName = fileName;

// Initialize embedded event structures
KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent,
FALSE);
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);

// Make a copy of the create irp (preserve flags)
RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
nxtSp->Control = 0;
// Use temporary FO and underlying DO
nxtSp->FileObject = cloneFileObject;
nxtSp->DeviceObject = DeviceObject;

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Ensure that the create disposition requested
// indicates all of the appropriate “rights” necessary
// for the given operation.
//
if (createDisposition == FILE_SUPERSEDE) {
desiredAccessCreate |= DELETE;
}

if ((createDisposition == FILE_OVERWRITE) ||
(createDisposition == FILE_OVERWRITE_IF)) {
desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
FILE_WRITE_ATTRIBUTES);
}

// Setup our desired access
nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccessCreate & ~SYNCHRONIZE;
nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
// Specify open only (no create/supersede)
nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
(createOptions & (FILE_OPEN_NO_RECALL |
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
FILE_NO_INTERMEDIATE_BUFFERING;

//
// Chuck the request to the underlying driver
//
Status = IoCallDriver(DeviceObject, Irp);

if (Status == STATUS_PENDING) {
// Wait for event if pended
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode,
FALSE, 0);
Status = Irp->IoStatus.Status;
}

// Now check for file open failures
if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE))
{
// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// Toss temp FO
ObDereferenceObject(cloneFileObject);

// Nothing has happened yet
Irp->PendingReturned = FALSE;

// OK to proceed if cannot open file
return STATUS_SUCCESS;
}

//
// *** OK - we now have an opened FileObject with a valid FsContext
pointer
//

// RetStatus = <<< *** Insert your desired actions here *** >>>

//
// *** Done with FileObject
//

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Need to light “handle created” flag
cloneFileObject->Flags |= FO_HANDLE_CREATED;

// Send a CLEANUP down to the FSD
nxtSp->MajorFunction = IRP_MJ_CLEANUP;
nxtSp->MinorFunction = 0;
nxtSp->Flags = 0;
nxtSp->Control = 0;
nxtSp->DeviceObject = DeviceObject;
nxtSp->FileObject = cloneFileObject;

// Init our completion event
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Call the FSD to release any share access, cache refs, etc.
//
Status = IoCallDriver(DeviceObject, Irp);

//
// Wait for the cleanup to finish
//
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode,
FALSE, 0);
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// We allocated the FO, now we must free it
ObDereferenceObject(cloneFileObject);

// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Nothing has happened yet
Irp->PendingReturned = FALSE;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

// return STATUS_SUCCESS if it is OK to go ahead and let the caller
open
the file
return RetStatus;
}


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: unknown lmsubst tag
argument: ‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: xxxxx@timespring.com
To unsubscribe send a blank email to xxxxx@lists.osr.com

John,

I agree with your comments too.

In my world, I track both FileObject and FsContext pointer refs
(StreamHandleContext and StreamContext in Filter Manager speak). In both our
legacy and Filter Manager driver, I “ignore” CLEANUP & CLOSE on FileObjects
I’ve never seen and which are not being tracked. StreamFileObjects are
picked up for tracking in READ, WRITE & SET/QUERYINFORMATION calls. I also
ignore CLEANUPs on StreamFileObjects I am tracking as you only see them if
IoCreateStreamFile is called and then it is always the first IRP. This
method seems to handle all cases of FileObject (mis-)use.

Note: In the Filter Manager you get the StreamContext teardown callback only
when the underlying FSD “releases” the FCB/SCB. In the NTFS case, this is
not always when the ref count goes to 0 due to its caching for (I guess)
quick re-open. It turns out because of this, I need to maintain my own ref
count on the StreamContext for other reasons.

/ted

-----Original Message-----
From: John Moore [mailto:xxxxx@timespring.com]
Sent: Wednesday, July 20, 2005 3:30 PM
To: Windows File Systems Devs Interest List
Subject: RE: [ntfsd] Context Tracking and FO_STREAM_FILE

Hi Ted,

Thanks for the quick response, I know the original post was from a couple of
years ago.

First, regarding point 4 below. I agree that a filter should be able to
handle stream file objects in all cases. The problem I had is that it had
been my experience (and according to the articles, OSR’s too) that a filter
can safely assume that it won’t see ‘chameleon’ file objects that change
types midstream.

I think I would modify your conclusion a little. In this environment,
reference counting in per stream contexts is effectively useless. There is
no way to handle the various cases including the case where the only irp you
see for a FileObject is the close irp. You must maintain a per stream
context table of FileObjects so you know when the last FileObject that you
have seen, regareless of when you have seen it or its type, has been closed.
Of course, you could still maintain a reference count but it would be
redundant.

Thankfully, the Filter Manager or even the raw FsRtlXxxPerStreamContext
functionality should pretty much alleviate this problem.

John Moore

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-214583-
xxxxx@lists.osr.com] On Behalf Of Ted Hess
Sent: Wednesday, July 20, 2005 1:03 PM
To: Windows File Systems Devs Interest List
Subject: RE: [ntfsd] Context Tracking and FO_STREAM_FILE

A couple of comments on my orignal posting… Defending the
indefensible
:wink:

  1. There is a bug in the post-create freeing of the filename buffer.
    This
    code should be moved to after the CLEANUP has completed. Why? 'cause
    either NTFS or the CM may keep a copy of a pointer to this Unicode
    buffer. Freeing
    it prematurely caused some interesting BugChecks in the Verifier in
    NTFS.

  2. The line of code commented out (clearing FO_STREAM_FILE) was left
    as
    documentation. The line below it was copying the original flags from
    the
    FileObject which will not have any of these flags set (Optimization
    hack).

  3. Reseting the FO_STREAM_FILE flag assured the FileObject was handled
    correctly by the IO manager and drivers above you when calling
    ObDereferenceObject which in turn will eventually send the CLOSE IRP
    down
    the stack from the top.

  4. I’ve always maintained that if you want to correctly reference
    track
    FsContexts, you have to account for Stream FileObjects in all cases.
    Also,
    NTFS is also known to cache (not teardown) SCBs (FsContext pointers)
    even
    after the CLOSE has been processed. In this case, you can indeed see a
    re-open of a file (same FsContext) even though you haven’t seen your
    own
    fiter’s CLOSE completion for it yet. In these cases, you need have
    some
    sort
    of “re-open” logic in your post-CREATE handler for “known but closed”
    FsContexts. This is trick stuff if you are trying to be transparent
    whilst
    didling files.

  5. This essence of this code has existed for a couple of years now in
    a
    filter that runs on NT4.0 thru WS03. AFAIK, we don’t leak or crash or
    hang
    due to this bit of implementation chicanery.

OK, to answer your question directly - I think you need to reference
count
FsContexts for StreamFileObjects when you see them and decrement the
reference on ALL CLOSEs regardless of FO type if the context is known
to
you.

HTH, /ted

-----Original Message-----
From: John Moore [mailto:xxxxx@timespring.com]
Sent: Wednesday, July 20, 2005 11:56 AM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] Context Tracking and FO_STREAM_FILE

Greetings,
Awhile back there was a thread that described a technique for using
FileObjects created with IoCreateStreamFileObjectXxx and rolling
create
irps. I didn’t pay much attention then but it seems that some unnamed
antivirus product is using this technique (or something very similar)
and
it
does not align well with the context tracking techniques described in
the
OSR articles. Specifically, the technique described (I included the
code
below) creates a FileObject with IoCreateStreamFileObjectXxx, clears
the
FO_STREAM_FILE flag, rolls and sends a create IRP, does whatever with
the
FileObject, rolls and sends a cleanup, (this is the important part)
sets
the
FO_STREAM_FILE flag and dereferences the FileObject. The net effect
in
the
context tracking is that the context reference is incremented when the
create irp is processed (sans FO_STREAM_FILE) but is not decremented
at
close time since the FO_STREAM_FILE flag is now set. The context
reference never goes to 0 and, in fact, is now ‘stale’.

I’ve included the code from the previous thread. The original post
has
the
line that cleared the FO_STREAM_FILE flag commented out but text
posted
along with the code indicated that it should be included. Also, just
to
be
sure to give credit where credit is due, this was posted by Ted Hess.

Any suggestions on how to distinguish a real FileObject from a stream
FileObject masquerading as a real FileObject? As near as I can tell,
unless that distinction can be made, the only way around this is to
always
and
only
maintain a FileObject table for context tracking - reference counting
cannot be accurate. I guess another option would be to decrement the
reference
count at close time regardless of the FO_STREAM_FILE flag and
re-create
the
context next time the FsContext is detected. Neither method is nearly
as
elegant as the original described in the OSR articles. If I’ve missed
something obvious feel free to flog as necessary.

Sorry for the long post but I wanted to include the original code.

John Moore

NTSTATUS
xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
NTSTATUS Status, RetStatus = STATUS_SUCCESS;
UNICODE_STRING fileName;
PUNICODE_STRING pFOName;
PFILE_OBJECT fileObject, cloneFileObject;

PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);

ACCESS_MASK desiredAccess =
irpSp->Parameters.Create.SecurityContext->DesiredAccess;
ACCESS_MASK desiredAccessCreate = desiredAccess;

ULONG createOptions = irpSp->Parameters.Create.Options &
FILE_VALID_OPTION_FLAGS;
ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24)
&
0xFF;

// The subject of this excercise (save it)
fileObject = irpSp->FileObject;

// Make a copy of the callers buffer here 'cause we don’t know
who’ll
free it
fileName.Length = fileObject->FileName.Length;
fileName.MaximumLength = fileObject->FileName.MaximumLength;
fileName.Buffer = ExAllocatePool(PagedPool,
fileName.MaximumLength);

// Bag it if no memory available
if (!fileName.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Copy original contents into temp buffer
RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
fileName.MaximumLength);

// Allocate a new, temporary FO
#if defined(NT_40)
// Watch out for the gratuitous CLEANUP IRP!
cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL);
#endif

// (Re-)init newly created FO
// cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |
FO_CLEANUP_COMPLETE);
cloneFileObject->Flags = fileObject->Flags;
cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;

// Give it a name
pFOName = &cloneFileObject->FileName;
*pFOName = fileName;

// Initialize embedded event structures
KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent,
FALSE);
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);

// Make a copy of the create irp (preserve flags)
RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
nxtSp->Control = 0;
// Use temporary FO and underlying DO
nxtSp->FileObject = cloneFileObject;
nxtSp->DeviceObject = DeviceObject;

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Ensure that the create disposition requested
// indicates all of the appropriate “rights” necessary
// for the given operation.
//
if (createDisposition == FILE_SUPERSEDE) {
desiredAccessCreate |= DELETE;
}

if ((createDisposition == FILE_OVERWRITE) ||
(createDisposition == FILE_OVERWRITE_IF)) {
desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
FILE_WRITE_ATTRIBUTES);
}

// Setup our desired access
nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccessCreate & ~SYNCHRONIZE;
nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
// Specify open only (no create/supersede)
nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
(createOptions & (FILE_OPEN_NO_RECALL |
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
FILE_NO_INTERMEDIATE_BUFFERING;

//
// Chuck the request to the underlying driver
//
Status = IoCallDriver(DeviceObject, Irp);

if (Status == STATUS_PENDING) {
// Wait for event if pended
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
Status = Irp->IoStatus.Status;
}

// Now check for file open failures
if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE))
{
// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// Toss temp FO
ObDereferenceObject(cloneFileObject);

// Nothing has happened yet
Irp->PendingReturned = FALSE;

// OK to proceed if cannot open file
return STATUS_SUCCESS;
}

//
// *** OK - we now have an opened FileObject with a valid FsContext
pointer
//

// RetStatus = <<< *** Insert your desired actions here *** >>>

//
// *** Done with FileObject
//

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Need to light “handle created” flag
cloneFileObject->Flags |= FO_HANDLE_CREATED;

// Send a CLEANUP down to the FSD
nxtSp->MajorFunction = IRP_MJ_CLEANUP;
nxtSp->MinorFunction = 0;
nxtSp->Flags = 0;
nxtSp->Control = 0;
nxtSp->DeviceObject = DeviceObject;
nxtSp->FileObject = cloneFileObject;

// Init our completion event
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Call the FSD to release any share access, cache refs, etc.
//
Status = IoCallDriver(DeviceObject, Irp);

//
// Wait for the cleanup to finish
//
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// We allocated the FO, now we must free it
ObDereferenceObject(cloneFileObject);

// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Nothing has happened yet
Irp->PendingReturned = FALSE;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

// return STATUS_SUCCESS if it is OK to go ahead and let the caller
open the file
return RetStatus;
}


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: unknown lmsubst tag
argument: ‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: xxxxx@timespring.com To
unsubscribe send a blank email to xxxxx@lists.osr.com


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: unknown lmsubst tag argument: ‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com

Just to address the specific issue of not tearing down data structures
when the ref. count drops to zero: this is a HUGE performance gain in a
file system.

Applications routinely open/close, open/close files performing
operations all along the way. In addition, they open containing
directories, enumerate the directory contents, etc.

If you tear down aggressively, you incur the cost of rebuilding that
state a few milliseconds later when the file is reopened, the directory
rescanned, the path re-walked. By lazily deferring the cleanup - even
just a few seconds, you dramatically improve performance of the system,
not only because you don’t have the set-up/tear-down of the structures
but you also eliminate the contention on the tables and other structures
within the file system. Batching cleanup is extremely efficient if
combined with some sort of background, low priority non-aggressive
thread. For example, we’ve implemented asynchronous background cleanup
by doing non-blocking lock acquisitions. If we can’t grab the locks, we
just move on and try cleaning something different (note: the actual
algorithms here are a *tad bit* more complex, because we also bound the
amount of memory we will use).

Thus, while it may not make sense from a filter’s perspective, it makes
perfect sense from a file system’s perspective.

Regards,

Tony

Tony Mason
Consulting Partner
OSR Open Systems Resources, Inc.
http://www.osr.com

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Ted Hess
Sent: Wednesday, July 20, 2005 4:24 PM
To: ntfsd redirect
Subject: RE: [ntfsd] Context Tracking and FO_STREAM_FILE

John,

I agree with your comments too.

In my world, I track both FileObject and FsContext pointer refs
(StreamHandleContext and StreamContext in Filter Manager speak). In both
our
legacy and Filter Manager driver, I “ignore” CLEANUP & CLOSE on
FileObjects
I’ve never seen and which are not being tracked. StreamFileObjects are
picked up for tracking in READ, WRITE & SET/QUERYINFORMATION calls. I
also
ignore CLEANUPs on StreamFileObjects I am tracking as you only see them
if
IoCreateStreamFile is called and then it is always the first IRP. This
method seems to handle all cases of FileObject (mis-)use.

Note: In the Filter Manager you get the StreamContext teardown callback
only
when the underlying FSD “releases” the FCB/SCB. In the NTFS case, this
is
not always when the ref count goes to 0 due to its caching for (I guess)
quick re-open. It turns out because of this, I need to maintain my own
ref
count on the StreamContext for other reasons.

/ted

-----Original Message-----
From: John Moore [mailto:xxxxx@timespring.com]
Sent: Wednesday, July 20, 2005 3:30 PM
To: Windows File Systems Devs Interest List
Subject: RE: [ntfsd] Context Tracking and FO_STREAM_FILE

Hi Ted,

Thanks for the quick response, I know the original post was from a
couple of
years ago.

First, regarding point 4 below. I agree that a filter should be able to
handle stream file objects in all cases. The problem I had is that it
had
been my experience (and according to the articles, OSR’s too) that a
filter
can safely assume that it won’t see ‘chameleon’ file objects that change
types midstream.

I think I would modify your conclusion a little. In this environment,
reference counting in per stream contexts is effectively useless. There
is
no way to handle the various cases including the case where the only irp
you
see for a FileObject is the close irp. You must maintain a per stream
context table of FileObjects so you know when the last FileObject that
you
have seen, regareless of when you have seen it or its type, has been
closed.
Of course, you could still maintain a reference count but it would be
redundant.

Thankfully, the Filter Manager or even the raw FsRtlXxxPerStreamContext
functionality should pretty much alleviate this problem.

John Moore

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-214583-
xxxxx@lists.osr.com] On Behalf Of Ted Hess
Sent: Wednesday, July 20, 2005 1:03 PM
To: Windows File Systems Devs Interest List
Subject: RE: [ntfsd] Context Tracking and FO_STREAM_FILE

A couple of comments on my orignal posting… Defending the
indefensible
:wink:

  1. There is a bug in the post-create freeing of the filename buffer.
    This
    code should be moved to after the CLEANUP has completed. Why? 'cause
    either NTFS or the CM may keep a copy of a pointer to this Unicode
    buffer. Freeing
    it prematurely caused some interesting BugChecks in the Verifier in
    NTFS.

  2. The line of code commented out (clearing FO_STREAM_FILE) was left
    as
    documentation. The line below it was copying the original flags from
    the
    FileObject which will not have any of these flags set (Optimization
    hack).

  3. Reseting the FO_STREAM_FILE flag assured the FileObject was handled

correctly by the IO manager and drivers above you when calling
ObDereferenceObject which in turn will eventually send the CLOSE IRP
down
the stack from the top.

  1. I’ve always maintained that if you want to correctly reference
    track
    FsContexts, you have to account for Stream FileObjects in all cases.
    Also,
    NTFS is also known to cache (not teardown) SCBs (FsContext pointers)
    even
    after the CLOSE has been processed. In this case, you can indeed see a

re-open of a file (same FsContext) even though you haven’t seen your
own
fiter’s CLOSE completion for it yet. In these cases, you need have
some
sort
of “re-open” logic in your post-CREATE handler for “known but closed”
FsContexts. This is trick stuff if you are trying to be transparent
whilst
didling files.

  1. This essence of this code has existed for a couple of years now in
    a
    filter that runs on NT4.0 thru WS03. AFAIK, we don’t leak or crash or
    hang
    due to this bit of implementation chicanery.

OK, to answer your question directly - I think you need to reference
count
FsContexts for StreamFileObjects when you see them and decrement the
reference on ALL CLOSEs regardless of FO type if the context is known
to
you.

HTH, /ted

-----Original Message-----
From: John Moore [mailto:xxxxx@timespring.com]
Sent: Wednesday, July 20, 2005 11:56 AM
To: Windows File Systems Devs Interest List
Subject: [ntfsd] Context Tracking and FO_STREAM_FILE

Greetings,
Awhile back there was a thread that described a technique for using
FileObjects created with IoCreateStreamFileObjectXxx and rolling
create
irps. I didn’t pay much attention then but it seems that some unnamed

antivirus product is using this technique (or something very similar)
and
it
does not align well with the context tracking techniques described in
the
OSR articles. Specifically, the technique described (I included the
code
below) creates a FileObject with IoCreateStreamFileObjectXxx, clears
the
FO_STREAM_FILE flag, rolls and sends a create IRP, does whatever with
the
FileObject, rolls and sends a cleanup, (this is the important part)
sets
the
FO_STREAM_FILE flag and dereferences the FileObject. The net effect
in
the
context tracking is that the context reference is incremented when the

create irp is processed (sans FO_STREAM_FILE) but is not decremented
at
close time since the FO_STREAM_FILE flag is now set. The context
reference never goes to 0 and, in fact, is now ‘stale’.

I’ve included the code from the previous thread. The original post
has
the
line that cleared the FO_STREAM_FILE flag commented out but text
posted
along with the code indicated that it should be included. Also, just
to
be
sure to give credit where credit is due, this was posted by Ted Hess.

Any suggestions on how to distinguish a real FileObject from a stream
FileObject masquerading as a real FileObject? As near as I can tell,
unless that distinction can be made, the only way around this is to
always
and
only
maintain a FileObject table for context tracking - reference counting
cannot be accurate. I guess another option would be to decrement the
reference
count at close time regardless of the FO_STREAM_FILE flag and
re-create
the
context next time the FsContext is detected. Neither method is nearly
as
elegant as the original described in the OSR articles. If I’ve missed

something obvious feel free to flog as necessary.

Sorry for the long post but I wanted to include the original code.

John Moore

NTSTATUS
xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
NTSTATUS Status, RetStatus = STATUS_SUCCESS;
UNICODE_STRING fileName;
PUNICODE_STRING pFOName;
PFILE_OBJECT fileObject, cloneFileObject;

PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);

ACCESS_MASK desiredAccess =
irpSp->Parameters.Create.SecurityContext->DesiredAccess;
ACCESS_MASK desiredAccessCreate = desiredAccess;

ULONG createOptions = irpSp->Parameters.Create.Options &
FILE_VALID_OPTION_FLAGS;
ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24)
&
0xFF;

// The subject of this excercise (save it)
fileObject = irpSp->FileObject;

// Make a copy of the callers buffer here 'cause we don’t know
who’ll
free it
fileName.Length = fileObject->FileName.Length;
fileName.MaximumLength = fileObject->FileName.MaximumLength;
fileName.Buffer = ExAllocatePool(PagedPool,
fileName.MaximumLength);

// Bag it if no memory available
if (!fileName.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Copy original contents into temp buffer
RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
fileName.MaximumLength);

// Allocate a new, temporary FO
#if defined(NT_40)
// Watch out for the gratuitous CLEANUP IRP!
cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL);
#endif

// (Re-)init newly created FO
// cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |

FO_CLEANUP_COMPLETE);
cloneFileObject->Flags = fileObject->Flags;
cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;

// Give it a name
pFOName = &cloneFileObject->FileName;
*pFOName = fileName;

// Initialize embedded event structures
KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent,
FALSE);
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);

// Make a copy of the create irp (preserve flags)
RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
nxtSp->Control = 0;
// Use temporary FO and underlying DO
nxtSp->FileObject = cloneFileObject;
nxtSp->DeviceObject = DeviceObject;

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Ensure that the create disposition requested
// indicates all of the appropriate “rights” necessary
// for the given operation.
//
if (createDisposition == FILE_SUPERSEDE) {
desiredAccessCreate |= DELETE;
}

if ((createDisposition == FILE_OVERWRITE) ||
(createDisposition == FILE_OVERWRITE_IF)) {
desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
FILE_WRITE_ATTRIBUTES);
}

// Setup our desired access
nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccessCreate & ~SYNCHRONIZE;
nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
// Specify open only (no create/supersede)
nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
(createOptions & (FILE_OPEN_NO_RECALL |
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
FILE_NO_INTERMEDIATE_BUFFERING;

//
// Chuck the request to the underlying driver
//
Status = IoCallDriver(DeviceObject, Irp);

if (Status == STATUS_PENDING) {
// Wait for event if pended
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
Status = Irp->IoStatus.Status;
}

// Now check for file open failures
if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE))
{
// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// Toss temp FO
ObDereferenceObject(cloneFileObject);

// Nothing has happened yet
Irp->PendingReturned = FALSE;

// OK to proceed if cannot open file
return STATUS_SUCCESS;
}

//
// *** OK - we now have an opened FileObject with a valid FsContext

pointer
//

// RetStatus = <<< *** Insert your desired actions here *** >>>

//
// *** Done with FileObject
//

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Need to light “handle created” flag
cloneFileObject->Flags |= FO_HANDLE_CREATED;

// Send a CLEANUP down to the FSD
nxtSp->MajorFunction = IRP_MJ_CLEANUP;
nxtSp->MinorFunction = 0;
nxtSp->Flags = 0;
nxtSp->Control = 0;
nxtSp->DeviceObject = DeviceObject;
nxtSp->FileObject = cloneFileObject;

// Init our completion event
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Call the FSD to release any share access, cache refs, etc.
//
Status = IoCallDriver(DeviceObject, Irp);

//
// Wait for the cleanup to finish
//
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// We allocated the FO, now we must free it
ObDereferenceObject(cloneFileObject);

// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Nothing has happened yet
Irp->PendingReturned = FALSE;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

// return STATUS_SUCCESS if it is OK to go ahead and let the caller

open the file
return RetStatus;
}


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: unknown lmsubst tag
argument: ‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: xxxxx@timespring.com To
unsubscribe send a blank email to xxxxx@lists.osr.com


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: unknown lmsubst tag argument:
‘’
To unsubscribe send a blank email to xxxxx@lists.osr.com


Questions? First check the IFS FAQ at
https://www.osronline.com/article.cfm?id=17

You are currently subscribed to ntfsd as: xxxxx@osr.com
To unsubscribe send a blank email to xxxxx@lists.osr.com

John

In one legacy filter I started out reference counting file contexts
(FileObject->FsContext) as described in the OSR article. Long time ago now I
‘evolved’ the code so instead of reference counting I keep track of the
associated file objects seen for each file context. The condition I have
for discard of a file object related state is IRP_MJ_CLOSE; the condition I
have for discard of the file context related state is IRP_MJ_CLOSE with
“null” FileObject->SectionObjectPointers and the set of associated file
object related items is empty. It has been my experience that this scheme
has been more ‘robust’.

Ob question: Can anyone shed some light on the case of stream file objects
(which refer to files, not volumes, or directories) in IRP_MJ_CLOSE dispatch
(pre-operation if you prefer) where FileObject->FsContext is NULL?

Cheers
Lyndon

“John Moore” wrote in message
news:xxxxx@ntfsd…
Greetings,
Awhile back there was a thread that described a technique for using
FileObjects created with IoCreateStreamFileObjectXxx and rolling create
irps. I didn’t pay much attention then but it seems that some unnamed
antivirus product is using this technique (or something very similar)
and it does not align well with the context tracking techniques
described in the OSR articles. Specifically, the technique described (I
included the code below) creates a FileObject with
IoCreateStreamFileObjectXxx, clears the FO_STREAM_FILE flag, rolls and
sends a create IRP, does whatever with the FileObject, rolls and sends a
cleanup, (this is the important part) sets the FO_STREAM_FILE flag and
dereferences the FileObject. The net effect in the context tracking is
that the context reference is incremented when the create irp is
processed (sans FO_STREAM_FILE) but is not decremented at close time
since the FO_STREAM_FILE flag is now set. The context reference never
goes to 0 and, in fact, is now ‘stale’.

I’ve included the code from the previous thread. The original post has
the line that cleared the FO_STREAM_FILE flag commented out but text
posted along with the code indicated that it should be included. Also,
just to be sure to give credit where credit is due, this was posted by
Ted Hess.

Any suggestions on how to distinguish a real FileObject from a stream
FileObject masquerading as a real FileObject? As near as I can tell,
unless that distinction can be made, the only way around this is to
always and only maintain a FileObject table for context tracking -
reference counting cannot be accurate. I guess another option would be
to decrement the reference count at close time regardless of the
FO_STREAM_FILE flag and re-create the context next time the FsContext is
detected. Neither method is nearly as elegant as the original described
in the OSR articles. If I’ve missed something obvious feel free to flog
as necessary.

Sorry for the long post but I wanted to include the original code.

John Moore

NTSTATUS
xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
NTSTATUS Status, RetStatus = STATUS_SUCCESS;
UNICODE_STRING fileName;
PUNICODE_STRING pFOName;
PFILE_OBJECT fileObject, cloneFileObject;

PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);

ACCESS_MASK desiredAccess =
irpSp->Parameters.Create.SecurityContext->DesiredAccess;
ACCESS_MASK desiredAccessCreate = desiredAccess;

ULONG createOptions = irpSp->Parameters.Create.Options &
FILE_VALID_OPTION_FLAGS;
ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24) &
0xFF;

// The subject of this excercise (save it)
fileObject = irpSp->FileObject;

// Make a copy of the callers buffer here 'cause we don’t know who’ll
free it
fileName.Length = fileObject->FileName.Length;
fileName.MaximumLength = fileObject->FileName.MaximumLength;
fileName.Buffer = ExAllocatePool(PagedPool, fileName.MaximumLength);

// Bag it if no memory available
if (!fileName.Buffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
// Copy original contents into temp buffer
RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
fileName.MaximumLength);

// Allocate a new, temporary FO
#if defined(NT_40)
// Watch out for the gratuitous CLEANUP IRP!
cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL);
#endif

// (Re-)init newly created FO
// cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |
FO_CLEANUP_COMPLETE);
cloneFileObject->Flags = fileObject->Flags;
cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;

// Give it a name
pFOName = &cloneFileObject->FileName;
*pFOName = fileName;

// Initialize embedded event structures
KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent,
FALSE);
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent, FALSE);

// Make a copy of the create irp (preserve flags)
RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
nxtSp->Control = 0;
// Use temporary FO and underlying DO
nxtSp->FileObject = cloneFileObject;
nxtSp->DeviceObject = DeviceObject;

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Ensure that the create disposition requested
// indicates all of the appropriate “rights” necessary
// for the given operation.
//
if (createDisposition == FILE_SUPERSEDE) {
desiredAccessCreate |= DELETE;
}

if ((createDisposition == FILE_OVERWRITE) ||
(createDisposition == FILE_OVERWRITE_IF)) {
desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
FILE_WRITE_ATTRIBUTES);
}

// Setup our desired access
nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccessCreate & ~SYNCHRONIZE;
nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
// Specify open only (no create/supersede)
nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
(createOptions & (FILE_OPEN_NO_RECALL |
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
FILE_NO_INTERMEDIATE_BUFFERING;

//
// Chuck the request to the underlying driver
//
Status = IoCallDriver(DeviceObject, Irp);

if (Status == STATUS_PENDING) {
// Wait for event if pended
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
Status = Irp->IoStatus.Status;
}

// Now check for file open failures
if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE)) {
// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// Toss temp FO
ObDereferenceObject(cloneFileObject);

// Nothing has happened yet
Irp->PendingReturned = FALSE;

// OK to proceed if cannot open file
return STATUS_SUCCESS;
}

//
// OK - we now have an opened FileObject with a valid FsContext
pointer
//

// RetStatus = <<<
Insert your desired actions here >>>

//
//
Done with FileObject
//

// Free temp (or FSD) buffer if still allocated
if (pFOName->Buffer) {
ExFreePool(pFOName->Buffer);
pFOName->Buffer = NULL;
pFOName->Length = 0;
pFOName->MaximumLength = 0;
}

// Need to light “handle created” flag
cloneFileObject->Flags |= FO_HANDLE_CREATED;

// Send a CLEANUP down to the FSD
nxtSp->MajorFunction = IRP_MJ_CLEANUP;
nxtSp->MinorFunction = 0;
nxtSp->Flags = 0;
nxtSp->Control = 0;
nxtSp->DeviceObject = DeviceObject;
nxtSp->FileObject = cloneFileObject;

// Init our completion event
KeInitializeEvent(&cloneFileObject->Event, NotificationEvent, FALSE);

// Setup completion routine
IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
&cloneFileObject->Event, TRUE, TRUE, TRUE);

//
// Call the FSD to release any share access, cache refs, etc.
//
Status = IoCallDriver(DeviceObject, Irp);

//
// Wait for the cleanup to finish
//
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&cloneFileObject->Event, Executive,
KernelMode, FALSE, 0);
}

// Reset FO stuff
cloneFileObject->Flags |= FO_STREAM_FILE;
cloneFileObject->RelatedFileObject = NULL;

// We allocated the FO, now we must free it
ObDereferenceObject(cloneFileObject);

// Restore original IRP parameters
irpSp->Parameters.Create.SecurityContext->DesiredAccess =
desiredAccess;

// Nothing has happened yet
Irp->PendingReturned = FALSE;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

// return STATUS_SUCCESS if it is OK to go ahead and let the caller
open the file
return RetStatus;
}

Lyndon,

I’ve come to that conclusion myself. I think it might be worthwhile to
mention this in the FAQ or perhaps and addendum to the Context Tracking
articles.

I recall some comments awhile ago from Molly about the FsContext being
NULL. Though, as I remember it had to do with the IRP_MJ_CLEANUP and it
had to do with how the Object Manager cleaned up stream file objects as
part of the IoCreateStreamFileObject processing. In that case, the
cleanup irp was sent before the file system had a chance to fill in the
FsContext. Try a search of the archives.

John

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-214606-
xxxxx@lists.osr.com] On Behalf Of Lyndon J Clarke
Sent: Wednesday, July 20, 2005 3:17 PM
To: Windows File Systems Devs Interest List
Subject: Re:[ntfsd] Context Tracking and FO_STREAM_FILE

John

In one legacy filter I started out reference counting file contexts
(FileObject->FsContext) as described in the OSR article. Long time ago
now
I
‘evolved’ the code so instead of reference counting I keep track of
the
associated file objects seen for each file context. The condition I
have
for discard of a file object related state is IRP_MJ_CLOSE; the
condition
I
have for discard of the file context related state is IRP_MJ_CLOSE
with
“null” FileObject->SectionObjectPointers and the set of associated
file
object related items is empty. It has been my experience that this
scheme
has been more ‘robust’.

Ob question: Can anyone shed some light on the case of stream file
objects
(which refer to files, not volumes, or directories) in IRP_MJ_CLOSE
dispatch
(pre-operation if you prefer) where FileObject->FsContext is NULL?

Cheers
Lyndon

“John Moore” wrote in message
> news:xxxxx@ntfsd…
> Greetings,
> Awhile back there was a thread that described a technique for using
> FileObjects created with IoCreateStreamFileObjectXxx and rolling
create
> irps. I didn’t pay much attention then but it seems that some unnamed
> antivirus product is using this technique (or something very similar)
> and it does not align well with the context tracking techniques
> described in the OSR articles. Specifically, the technique described
(I
> included the code below) creates a FileObject with
> IoCreateStreamFileObjectXxx, clears the FO_STREAM_FILE flag, rolls and
> sends a create IRP, does whatever with the FileObject, rolls and sends
a
> cleanup, (this is the important part) sets the FO_STREAM_FILE flag and
> dereferences the FileObject. The net effect in the context tracking
is
> that the context reference is incremented when the create irp is
> processed (sans FO_STREAM_FILE) but is not decremented at close time
> since the FO_STREAM_FILE flag is now set. The context reference never
> goes to 0 and, in fact, is now ‘stale’.
>
> I’ve included the code from the previous thread. The original post
has
> the line that cleared the FO_STREAM_FILE flag commented out but text
> posted along with the code indicated that it should be included.
Also,
> just to be sure to give credit where credit is due, this was posted by
> Ted Hess.
>
> Any suggestions on how to distinguish a real FileObject from a stream
> FileObject masquerading as a real FileObject? As near as I can tell,
> unless that distinction can be made, the only way around this is to
> always and only maintain a FileObject table for context tracking -
> reference counting cannot be accurate. I guess another option would
be
> to decrement the reference count at close time regardless of the
> FO_STREAM_FILE flag and re-create the context next time the FsContext
is
> detected. Neither method is nearly as elegant as the original
described
> in the OSR articles. If I’ve missed something obvious feel free to
flog
> as necessary.
>
> Sorry for the long post but I wanted to include the original code.
>
> John Moore
>
> NTSTATUS
> xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
> NTSTATUS Status, RetStatus = STATUS_SUCCESS;
> UNICODE_STRING fileName;
> PUNICODE_STRING pFOName;
> PFILE_OBJECT fileObject, cloneFileObject;
>
> PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
> PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);
>
> ACCESS_MASK desiredAccess =
> irpSp->Parameters.Create.SecurityContext->DesiredAccess;
> ACCESS_MASK desiredAccessCreate = desiredAccess;
>
> ULONG createOptions = irpSp->Parameters.Create.Options &
> FILE_VALID_OPTION_FLAGS;
> ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24)
&
> 0xFF;
>
> // The subject of this excercise (save it)
> fileObject = irpSp->FileObject;
>
> // Make a copy of the callers buffer here 'cause we don’t know
who’ll
> free it
> fileName.Length = fileObject->FileName.Length;
> fileName.MaximumLength = fileObject->FileName.MaximumLength;
> fileName.Buffer = ExAllocatePool(PagedPool,
fileName.MaximumLength);
>
> // Bag it if no memory available
> if (!fileName.Buffer) {
> return STATUS_INSUFFICIENT_RESOURCES;
> }
> // Copy original contents into temp buffer
> RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
> fileName.MaximumLength);
>
> // Allocate a new, temporary FO
> #if defined(NT_40)
> // Watch out for the gratuitous CLEANUP IRP!
> cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
> cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL);
> #endif
>
> // (Re-)init newly created FO
> // cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |
> FO_CLEANUP_COMPLETE);
> cloneFileObject->Flags = fileObject->Flags;
> cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;
>
> // Give it a name
> pFOName = &cloneFileObject->FileName;
> *pFOName = fileName;
>
> // Initialize embedded event structures
> KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent,
> FALSE);
> KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);
>
> // Make a copy of the create irp (preserve flags)
> RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
> nxtSp->Control = 0;
> // Use temporary FO and underlying DO
> nxtSp->FileObject = cloneFileObject;
> nxtSp->DeviceObject = DeviceObject;
>
> // Setup completion routine
> IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
> &cloneFileObject->Event, TRUE, TRUE, TRUE);
>
> //
> // Ensure that the create disposition requested
> // indicates all of the appropriate “rights” necessary
> // for the given operation.
> //
> if (createDisposition == FILE_SUPERSEDE) {
> desiredAccessCreate |= DELETE;
> }
>
> if ((createDisposition == FILE_OVERWRITE) ||
> (createDisposition == FILE_OVERWRITE_IF)) {
> desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
> FILE_WRITE_ATTRIBUTES);
> }
>
> // Setup our desired access
> nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
> desiredAccessCreate & ~SYNCHRONIZE;
> nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
> // Specify open only (no create/supersede)
> nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
> (createOptions & (FILE_OPEN_NO_RECALL |
> FILE_OPEN_FOR_BACKUP_INTENT |
> FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
> FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
> FILE_NO_INTERMEDIATE_BUFFERING;
>
> //
> // Chuck the request to the underlying driver
> //
> Status = IoCallDriver(DeviceObject, Irp);
>
> if (Status == STATUS_PENDING) {
> // Wait for event if pended
> KeWaitForSingleObject(&cloneFileObject->Event, Executive,
> KernelMode, FALSE, 0);
> Status = Irp->IoStatus.Status;
> }
>
> // Now check for file open failures
> if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE))
{
> // Restore original IRP parameters
> irpSp->Parameters.Create.SecurityContext->DesiredAccess =
> desiredAccess;
>
> // Free temp (or FSD) buffer if still allocated
> if (pFOName->Buffer) {
> ExFreePool(pFOName->Buffer);
> pFOName->Buffer = NULL;
> pFOName->Length = 0;
> pFOName->MaximumLength = 0;
> }
>
> // Reset FO stuff
> cloneFileObject->Flags |= FO_STREAM_FILE;
> cloneFileObject->RelatedFileObject = NULL;
>
> // Toss temp FO
> ObDereferenceObject(cloneFileObject);
>
> // Nothing has happened yet
> Irp->PendingReturned = FALSE;
>
> // OK to proceed if cannot open file
> return STATUS_SUCCESS;
> }
>
> //
> // OK - we now have an opened FileObject with a valid FsContext
> pointer
> //
>
> // RetStatus = <<<
Insert your desired actions here >>>
>
> //
> //
Done with FileObject
> //
>
> // Free temp (or FSD) buffer if still allocated
> if (pFOName->Buffer) {
> ExFreePool(pFOName->Buffer);
> pFOName->Buffer = NULL;
> pFOName->Length = 0;
> pFOName->MaximumLength = 0;
> }
>
> // Need to light “handle created” flag
> cloneFileObject->Flags |= FO_HANDLE_CREATED;
>
> // Send a CLEANUP down to the FSD
> nxtSp->MajorFunction = IRP_MJ_CLEANUP;
> nxtSp->MinorFunction = 0;
> nxtSp->Flags = 0;
> nxtSp->Control = 0;
> nxtSp->DeviceObject = DeviceObject;
> nxtSp->FileObject = cloneFileObject;
>
> // Init our completion event
> KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);
>
> // Setup completion routine
> IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
> &cloneFileObject->Event, TRUE, TRUE, TRUE);
>
> //
> // Call the FSD to release any share access, cache refs, etc.
> //
> Status = IoCallDriver(DeviceObject, Irp);
>
> //
> // Wait for the cleanup to finish
> //
> if (Status == STATUS_PENDING) {
> KeWaitForSingleObject(&cloneFileObject->Event, Executive,
> KernelMode, FALSE, 0);
> }
>
> // Reset FO stuff
> cloneFileObject->Flags |= FO_STREAM_FILE;
> cloneFileObject->RelatedFileObject = NULL;
>
> // We allocated the FO, now we must free it
> ObDereferenceObject(cloneFileObject);
>
> // Restore original IRP parameters
> irpSp->Parameters.Create.SecurityContext->DesiredAccess =
> desiredAccess;
>
> // Nothing has happened yet
> Irp->PendingReturned = FALSE;
> Irp->IoStatus.Status = STATUS_SUCCESS;
> Irp->IoStatus.Information = 0;
>
> // return STATUS_SUCCESS if it is OK to go ahead and let the caller
> open the file
> return RetStatus;
> }
>
>
>
>
> —
> Questions? First check the IFS FAQ at
> https://www.osronline.com/article.cfm?id=17
>
> You are currently subscribed to ntfsd as: xxxxx@timespring.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com

Hi John

Thanks for the pointer; it was indeed IRP_MJ_CLEANUP that was being
discussed … I was the OP :slight_smile:

Cheers
Lydnon

“John Moore” wrote in message
news:xxxxx@ntfsd…
Lyndon,

I’ve come to that conclusion myself. I think it might be worthwhile to
mention this in the FAQ or perhaps and addendum to the Context Tracking
articles.

I recall some comments awhile ago from Molly about the FsContext being
NULL. Though, as I remember it had to do with the IRP_MJ_CLEANUP and it
had to do with how the Object Manager cleaned up stream file objects as
part of the IoCreateStreamFileObject processing. In that case, the
cleanup irp was sent before the file system had a chance to fill in the
FsContext. Try a search of the archives.

John

> -----Original Message-----
> From: xxxxx@lists.osr.com [mailto:bounce-214606-
> xxxxx@lists.osr.com] On Behalf Of Lyndon J Clarke
> Sent: Wednesday, July 20, 2005 3:17 PM
> To: Windows File Systems Devs Interest List
> Subject: Re:[ntfsd] Context Tracking and FO_STREAM_FILE
>
> John
>
> In one legacy filter I started out reference counting file contexts
> (FileObject->FsContext) as described in the OSR article. Long time ago
now
> I
> ‘evolved’ the code so instead of reference counting I keep track of
the
> associated file objects seen for each file context. The condition I
have
> for discard of a file object related state is IRP_MJ_CLOSE; the
condition
> I
> have for discard of the file context related state is IRP_MJ_CLOSE
with
> “null” FileObject->SectionObjectPointers and the set of associated
file
> object related items is empty. It has been my experience that this
scheme
> has been more ‘robust’.
>
> Ob question: Can anyone shed some light on the case of stream file
objects
> (which refer to files, not volumes, or directories) in IRP_MJ_CLOSE
> dispatch
> (pre-operation if you prefer) where FileObject->FsContext is NULL?
>
> Cheers
> Lyndon
>
> “John Moore” wrote in message
> news:xxxxx@ntfsd…
> Greetings,
> Awhile back there was a thread that described a technique for using
> FileObjects created with IoCreateStreamFileObjectXxx and rolling
create
> irps. I didn’t pay much attention then but it seems that some unnamed
> antivirus product is using this technique (or something very similar)
> and it does not align well with the context tracking techniques
> described in the OSR articles. Specifically, the technique described
(I
> included the code below) creates a FileObject with
> IoCreateStreamFileObjectXxx, clears the FO_STREAM_FILE flag, rolls and
> sends a create IRP, does whatever with the FileObject, rolls and sends
a
> cleanup, (this is the important part) sets the FO_STREAM_FILE flag and
> dereferences the FileObject. The net effect in the context tracking
is
> that the context reference is incremented when the create irp is
> processed (sans FO_STREAM_FILE) but is not decremented at close time
> since the FO_STREAM_FILE flag is now set. The context reference never
> goes to 0 and, in fact, is now ‘stale’.
>
> I’ve included the code from the previous thread. The original post
has
> the line that cleared the FO_STREAM_FILE flag commented out but text
> posted along with the code indicated that it should be included.
Also,
> just to be sure to give credit where credit is due, this was posted by
> Ted Hess.
>
> Any suggestions on how to distinguish a real FileObject from a stream
> FileObject masquerading as a real FileObject? As near as I can tell,
> unless that distinction can be made, the only way around this is to
> always and only maintain a FileObject table for context tracking -
> reference counting cannot be accurate. I guess another option would
be
> to decrement the reference count at close time regardless of the
> FO_STREAM_FILE flag and re-create the context next time the FsContext
is
> detected. Neither method is nearly as elegant as the original
described
> in the OSR articles. If I’ve missed something obvious feel free to
flog
> as necessary.
>
> Sorry for the long post but I wanted to include the original code.
>
> John Moore
>
> NTSTATUS
> xxxPreCreateCheck(PDEVICE_OBJECT targetDeviceObject, PIRP Irp) {
> NTSTATUS Status, RetStatus = STATUS_SUCCESS;
> UNICODE_STRING fileName;
> PUNICODE_STRING pFOName;
> PFILE_OBJECT fileObject, cloneFileObject;
>
> PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
> PIO_STACK_LOCATION nxtSp = IoGetNextIrpStackLocation(Irp);
>
> ACCESS_MASK desiredAccess =
> irpSp->Parameters.Create.SecurityContext->DesiredAccess;
> ACCESS_MASK desiredAccessCreate = desiredAccess;
>
> ULONG createOptions = irpSp->Parameters.Create.Options &
> FILE_VALID_OPTION_FLAGS;
> ULONG createDisposition = (irpSp->Parameters.Create.Options >> 24)
&
> 0xFF;
>
> // The subject of this excercise (save it)
> fileObject = irpSp->FileObject;
>
> // Make a copy of the callers buffer here 'cause we don’t know
who’ll
> free it
> fileName.Length = fileObject->FileName.Length;
> fileName.MaximumLength = fileObject->FileName.MaximumLength;
> fileName.Buffer = ExAllocatePool(PagedPool,
fileName.MaximumLength);
>
> // Bag it if no memory available
> if (!fileName.Buffer) {
> return STATUS_INSUFFICIENT_RESOURCES;
> }
> // Copy original contents into temp buffer
> RtlCopyMemory(fileName.Buffer, fileObject->FileName.Buffer,
> fileName.MaximumLength);
>
> // Allocate a new, temporary FO
> #if defined(NT_40)
> // Watch out for the gratuitous CLEANUP IRP!
> cloneFileObject = IoCreateStreamFileObject(fileObject, NULL); #else
> cloneFileObject = IoCreateStreamFileObjectLite(fileObject, NULL);
> #endif
>
> // (Re-)init newly created FO
> // cloneFileObject->Flags &= ~(FO_STREAM_FILE | FO_HANDLE_CREATED |
> FO_CLEANUP_COMPLETE);
> cloneFileObject->Flags = fileObject->Flags;
> cloneFileObject->RelatedFileObject = fileObject->RelatedFileObject;
>
> // Give it a name
> pFOName = &cloneFileObject->FileName;
> *pFOName = fileName;
>
> // Initialize embedded event structures
> KeInitializeEvent(&cloneFileObject->Lock, SynchronizationEvent,
> FALSE);
> KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);
>
> // Make a copy of the create irp (preserve flags)
> RtlCopyMemory(nxtSp, irpSp, sizeof(IO_STACK_LOCATION));
> nxtSp->Control = 0;
> // Use temporary FO and underlying DO
> nxtSp->FileObject = cloneFileObject;
> nxtSp->DeviceObject = DeviceObject;
>
> // Setup completion routine
> IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
> &cloneFileObject->Event, TRUE, TRUE, TRUE);
>
> //
> // Ensure that the create disposition requested
> // indicates all of the appropriate “rights” necessary
> // for the given operation.
> //
> if (createDisposition == FILE_SUPERSEDE) {
> desiredAccessCreate |= DELETE;
> }
>
> if ((createDisposition == FILE_OVERWRITE) ||
> (createDisposition == FILE_OVERWRITE_IF)) {
> desiredAccessCreate |= (FILE_WRITE_DATA | FILE_WRITE_EA |
> FILE_WRITE_ATTRIBUTES);
> }
>
> // Setup our desired access
> nxtSp->Parameters.Create.SecurityContext->DesiredAccess =
> desiredAccessCreate & ~SYNCHRONIZE;
> nxtSp->Parameters.Create.ShareAccess = FILE_SHARE_VALID_FLAGS;
> // Specify open only (no create/supersede)
> nxtSp->Parameters.Create.Options = (FILE_OPEN << 24) |
> (createOptions & (FILE_OPEN_NO_RECALL |
> FILE_OPEN_FOR_BACKUP_INTENT |
> FILE_COMPLETE_IF_OPLOCKED | FILE_NON_DIRECTORY_FILE |
> FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT)) |
> FILE_NO_INTERMEDIATE_BUFFERING;
>
> //
> // Chuck the request to the underlying driver
> //
> Status = IoCallDriver(DeviceObject, Irp);
>
> if (Status == STATUS_PENDING) {
> // Wait for event if pended
> KeWaitForSingleObject(&cloneFileObject->Event, Executive,
> KernelMode, FALSE, 0);
> Status = Irp->IoStatus.Status;
> }
>
> // Now check for file open failures
> if(!NT_SUCCESS(Status) || (Irp->IoStatus.Status == STATUS_REPARSE))
{
> // Restore original IRP parameters
> irpSp->Parameters.Create.SecurityContext->DesiredAccess =
> desiredAccess;
>
> // Free temp (or FSD) buffer if still allocated
> if (pFOName->Buffer) {
> ExFreePool(pFOName->Buffer);
> pFOName->Buffer = NULL;
> pFOName->Length = 0;
> pFOName->MaximumLength = 0;
> }
>
> // Reset FO stuff
> cloneFileObject->Flags |= FO_STREAM_FILE;
> cloneFileObject->RelatedFileObject = NULL;
>
> // Toss temp FO
> ObDereferenceObject(cloneFileObject);
>
> // Nothing has happened yet
> Irp->PendingReturned = FALSE;
>
> // OK to proceed if cannot open file
> return STATUS_SUCCESS;
> }
>
> //
> // OK - we now have an opened FileObject with a valid FsContext
> pointer
> //
>
> // RetStatus = <<<
Insert your desired actions here >>>
>
> //
> //
Done with FileObject
> //
>
> // Free temp (or FSD) buffer if still allocated
> if (pFOName->Buffer) {
> ExFreePool(pFOName->Buffer);
> pFOName->Buffer = NULL;
> pFOName->Length = 0;
> pFOName->MaximumLength = 0;
> }
>
> // Need to light “handle created” flag
> cloneFileObject->Flags |= FO_HANDLE_CREATED;
>
> // Send a CLEANUP down to the FSD
> nxtSp->MajorFunction = IRP_MJ_CLEANUP;
> nxtSp->MinorFunction = 0;
> nxtSp->Flags = 0;
> nxtSp->Control = 0;
> nxtSp->DeviceObject = DeviceObject;
> nxtSp->FileObject = cloneFileObject;
>
> // Init our completion event
> KeInitializeEvent(&cloneFileObject->Event, NotificationEvent,
FALSE);
>
> // Setup completion routine
> IoSetCompletionRoutine (Irp, xxxSimpleEventCompletion,
> &cloneFileObject->Event, TRUE, TRUE, TRUE);
>
> //
> // Call the FSD to release any share access, cache refs, etc.
> //
> Status = IoCallDriver(DeviceObject, Irp);
>
> //
> // Wait for the cleanup to finish
> //
> if (Status == STATUS_PENDING) {
> KeWaitForSingleObject(&cloneFileObject->Event, Executive,
> KernelMode, FALSE, 0);
> }
>
> // Reset FO stuff
> cloneFileObject->Flags |= FO_STREAM_FILE;
> cloneFileObject->RelatedFileObject = NULL;
>
> // We allocated the FO, now we must free it
> ObDereferenceObject(cloneFileObject);
>
> // Restore original IRP parameters
> irpSp->Parameters.Create.SecurityContext->DesiredAccess =
> desiredAccess;
>
> // Nothing has happened yet
> Irp->PendingReturned = FALSE;
> Irp->IoStatus.Status = STATUS_SUCCESS;
> Irp->IoStatus.Information = 0;
>
> // return STATUS_SUCCESS if it is OK to go ahead and let the caller
> open the file
> return RetStatus;
> }
>
>
>
>
> —
> Questions? First check the IFS FAQ at
> https://www.osronline.com/article.cfm?id=17
>
> You are currently subscribed to ntfsd as: xxxxx@timespring.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com