I writing a Windows Minifilter Driver which needs to read the entire file (only files with size up to a specific threshold) on IRP_MJ_CLEANUP. As FltReadFile may not be called from the preop callback I queued the job to a work queue and did it there. When I finish reading the file I call FltCompletePendedPreOperation and invoke the post-cleanup callback which also handles the post operation as deferred work. Here are snippets of my code:
static NTSTATUS HandlePreCleanup(_In_ PFLT_CALLBACK_DATA Data,
_Out_ PVOID *Context)
{
NTSTATUS Status = STATUS_SUCCESS;
PFLT_INSTANCE Instance;
PFILE_OBJECT FileObject;
PVOID Buffer = NULL;
LARGE_INTEGER FileOffset;
FileObject = Data->Iopb->TargetFileObject;
Instance = Data->Iopb->TargetInstance;
Buffer = ExAllocatePoolWithTag(PagedPool,
(ULONG) FILE_CHUNK_SIZE,
PPFILTER_FILE_POOLTAG);
if (Buffer == NULL) {
PPERROR("Failed allocating file chunk\n");
Status = STATUS_MEMORY_NOT_ALLOCATED;
goto out;
}
FileOffset.QuadPart = 0;
for (;;) {
ULONG BytesRead;
Status = FltReadFile(
Instance, FileObject, &FileOffset,
(ULONG) FILE_CHUNK_SIZE, Buffer,
FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET,
&BytesRead, NULL, NULL
);
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_END_OF_FILE) {
Status = STATUS_SUCCESS;
break;
}
PPERROR("Failed reading from file %wZ: error %d\n",
&FileObject->FileName, Status);
goto out;
}
FileOffset.QuadPart += BytesRead;
}
out:
if (Buffer != NULL) {
ExFreePoolWithTag(Buffer, PPFILTER_FILE_POOLTAG);
}
return Status;
}
static VOID DeferredPreCallback(_In_ PFLT_DEFERRED_IO_WORKITEM WorkItem,
_In_ PFLT_CALLBACK_DATA Data,
_In_opt_ PVOID Context)
{
NTSTATUS Status = STATUS_SUCCESS;
PVOID PostContext = NULL;
UNREFERENCED_PARAMETER(Context);
switch (Data->Iopb->MajorFunction) {
case IRP_MJ_CLEANUP:
Status = HandlePreCleanup(Data, &PostContext);
break;
default:
NT_ASSERTMSG("Unexpected deferred pre callback operation",
FALSE);
break;
}
FltCompletePendedPreOperation(Data,
FLT_PREOP_SUCCESS_WITH_CALLBACK,
PostContext);
FltFreeDeferredIoWorkItem(WorkItem);
}
static NTSTATUS QueueWork(_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PFLT_DEFERRED_IO_WORKITEM_ROUTINE WorkRoutine,
_In_ PVOID Context)
{
NTSTATUS Status = STATUS_SUCCESS;
PFLT_DEFERRED_IO_WORKITEM WorkItem = NULL;
WorkItem = FltAllocateDeferredIoWorkItem();
if (WorkItem == NULL) {
Status = STATUS_MEMORY_NOT_ALLOCATED;
PPERROR("Failed allocating work item\n");
goto failed;
}
Status = FltQueueDeferredIoWorkItem(WorkItem, Data, WorkRoutine,
CriticalWorkQueue, Context);
if (!NT_SUCCESS(Status)) {
PPERROR("Failed queuing work item to queue: error %d\n",
Status);
goto failed;
}
return STATUS_SUCCESS;
failed:
if (WorkItem != NULL) {
FltFreeDeferredIoWorkItem(WorkItem);
}
return Status;
}
static FLT_PREOP_CALLBACK_STATUS DeferPreCallback(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_Out_ PVOID *CompletionContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
Status = QueueWork(Data, DeferredPreCallback, NULL);
if (!NT_SUCCESS(Status)) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
return FLT_PREOP_PENDING;
}
CONST FLT_OPERATION_REGISTRATION OperationRegistrations[] = {
{
IRP_MJ_CLEANUP,
0,
DeferPreCallback,
DeferPostCallback,
NULL
},
{ IRP_MJ_OPERATION_END },
};
This proves to work for a while but the system seems to hang (deadlock?) after a while. The problem seems to be with the call to FltReadFile since the hang does not occur when removing this call. Any ideas on why this might happen or how to debug it further?